Skip to content

test(server): hold MCP answers to McpJson under any application Json - #62

Merged
youndie merged 1 commit into
mainfrom
test/mcp-wire-format
Oct 2, 2026
Merged

youndie merged 1 commit into
mainfrom
test/mcp-wire-format

Conversation

@youndie

@youndie youndie commented Oct 2, 2026

Copy link
Copy Markdown
Owner

metrik logs WARN ContentNegotiation is already installed. MCP requires json(McpJson)… at startup. This explains it and holds the explanation with a test (M-117).

Why the warning is printed

SDK 0.15.0 prints it whenever it finds an application ContentNegotiation: Ktor's public API does not expose the installed Json, so the SDK cannot check it. metrik installs ContentNegotiation on MetrikJson before installKoreMcp on purpose; the other order makes the SDK install its own on the whole application and metrik's install throws DuplicatePluginException.

Why it does no harm

In the stateless transport exactly one thing can pass through the application's ContentNegotiation: the JSON-RPC answer to a POST (call.respond(payload)). Requests are read by the SDK from the raw body and decoded with McpJson; refusals are written by the SDK as text. kore-mcp encodes that one answer with McpJson on the transport's route, before ContentNegotiation sees it.

The test

McpWireFormatTest sends ten exchanges — initialize with the latest and an older protocol version, ping with a key the protocol does not define, tools/list, a call without optional fields, a call ending in a tool error, an unknown tool, an unknown method, a batch, an unparseable body — and checks that each raw body is byte for byte what McpJson writes for the message it carries.

  • the assembled module: clean;
  • kore-mcp under a Json that omits defaults (kotlinx defaults, unknown keys refused) and under json() with no argument: clean;
  • control — the bare SDK under the same two Json: mismatches. Omitting defaults drops protocolVersion from the latest-version initialize answer and the whole result from ping; json() adds null for every unset field;
  • the ping with an unknown key is refused by the strict Json (asserted), so its 200 shows requests are not parsed by the application's Json.

Mutations run locally: json() instead of json(MetrikJson) in module keeps the guard green (the application Json never reaches the answers); a byte comparison forced to "always equal" turns the null control red.

Verified: :server:jvmTest and :server:linuxX64Test for this class, green; the SDK's warning is reproduced in the native run, once per application.

The MCP SDK logs a warning at startup because metrik installs its own
ContentNegotiation before the endpoint. The test shows the warning names
no harm: requests are parsed by the SDK from the raw body, and the one
answer that passes through ContentNegotiation is encoded with McpJson by
kore-mcp first. A bare SDK under the same harmful Json is the control.
@youndie
youndie merged commit b006d39 into main Oct 2, 2026
1 check passed
@youndie
youndie deleted the test/mcp-wire-format branch October 2, 2026 08:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant