AuthMiddleware.install(ctx, config, source) now owns the annotation processor and the flash.auth.policy key, so a second credential source gets annotation-driven authorization without copying the wiring. The key is public: an extension that contributes middleware can order itself around authentication. OidcSession becomes Session in auth-core, carrying claims, an expiry and an opaque attribute map. OpenID Connect keeps its access, id and refresh tokens in that map under its own keys, so renewal stays its business and core has no OAuth2 vocabulary in it. isAccessTokenExpired() becomes isExpired(), with the 30s eager-renewal window it always had and now a test for it. flash-ext-oidc is renamed flash-ext-auth-oidc, matching cache-core/cache-caffeine and data-core/data-hibernate.
3.4 KiB
Why no flash-ext-jackson interop (yet)
The decision
flash-ext-mcp does not depend on, or integrate with, flash-ext-jackson. It brings its own
JSON handling (jackson-databind/jackson-core as a plain library dependency, wrapped by the
internal McpJson utility) and never touches flash-ext-jackson's Json/JacksonMiddleware/
shared ObjectMapper, even if the host app has flash-ext-jackson installed. This was a
deliberate choice, discussed and made explicitly — not an oversight — and is written down here
so it isn't accidentally "fixed" later without re-litigating the trade-off.
Why
flash-ext-jackson's Json class is built around full databinding:
mapper.readValue(bytes, SomeDto.class) / mapper.writeValueAsBytes(obj) — reflection-driven
property matching in both directions. The MCP JSON-RPC envelope has a fixed, known shape
({jsonrpc, id, method, params} in, {jsonrpc, id, result|error} out) defined by a spec, not by
application DTOs. Given that, hand-writing it with JsonGenerator directly is both simpler and
strictly cheaper than round-tripping through databinding: no property-name matching, no
reflection, no intermediate POJO graph for the parts of the response this extension controls
(the envelope itself, tools/list/resources/list/prompts/list — precompiled once at boot,
see tools-resources-prompts.md — and the fixed TextContent/TextResourceContents/
PromptMessage shapes). ToolArguments/PromptArguments read the incoming arguments object
as a JsonNode tree, not as a databound class, for the same reason — a JSON-RPC tool call's
arguments aren't a DTO with getters/setters, they're a dynamic, per-tool-defined bag of values.
This mirrors how flash-ext-auth-oidc already handles its own internal JSON needs (json-smart for
token-endpoint responses) independently of flash-ext-jackson — extensions with protocol-level
JSON needs that are shaped by a spec, not by user code, own that JSON handling themselves rather
than routing it through the app's general-purpose JSON extension.
What this means practically
- Installing
flash-ext-mcpnever requires installingflash-ext-jackson. A pure MCP server with no other JSON REST routes has zero unrelated dependencies to configure. - If the host app does have
flash-ext-jacksoninstalled for its own REST routes, thatObjectMapper's configuration (custom modules, date formatting, naming strategy, etc.) is not consulted byflash-ext-mcp— the two JSON paths are entirely independent today.
What a future opt-in reuse could look like
Nothing here rules out a later, additive convenience layer: McpExtension.routes() could check
ctx.find(ObjectMapper.class) (populated by JacksonExtension.provide()) and, if present, use
that shared mapper as the backing for an escape hatch such as ToolArguments.as(Class<T>) or
for a tool that wants to ToolResponse.success(someRecord) and have it serialized with the
app's own conventions — falling back to a locally-constructed default ObjectMapper when
flash-ext-jackson isn't installed, the same "prefer shared, degrade to sane default" shape
already used for McpSecurity.AUTO. That would be purely additive on top of the
JsonGenerator-based envelope/content writing described above, not a replacement for it — the
fixed-shape protocol plumbing has no reason to ever go through databinding, regardless of what
convenience layer gets added around it.