CI / Build & Test (push) Failing after 4m57s
Streamable HTTP transport (JSON-RPC 2.0 over POST), one-class-per-tool/resource/prompt API mirroring RequestHandler, boot-time-precompiled schema/list payloads for a zero-alloc hot path, and optional OAuth2 protection built on flash-ext-oidc (lazy-loaded, RFC 8707 audience binding, RFC 9728 Protected Resource Metadata). Registers the module in the root and flash-extensions POMs and adds the ext-mcp commit scope to AGENTS.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
52 lines
3.4 KiB
Markdown
52 lines
3.4 KiB
Markdown
# 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-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-mcp` never requires installing `flash-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-jackson` installed for its own REST routes, that
|
|
`ObjectMapper`'s configuration (custom modules, date formatting, naming strategy, etc.) is
|
|
**not** consulted by `flash-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.
|