Skip to content

Stateless wire parity gaps: schema validation, stdio, and the adaptive client's fallback #1637

Description

@panyam

agni ran its MCP tests on both wires (panyam/agni#1172) against mcpkit v0.8.12, a ModeDual HTTP server, and found three places the stateless wire or the adaptive client behaves differently from the session wire. All three are in mcpkit, and agni asserts each one today so the release that fixes it shows up as a failing test there.

1. The stateless tools/call skips input-schema validation

Dispatcher.handleToolsCall validates arguments against the tool's InputSchema (dispatch.go, the entry.schema.Validate block) and answers a tool error. statelessBackend.callToolForStateless (stateless_backend.go:424) never does; it runs file-input validation and the requestState check, then calls the handler. So a tool whose schema says "additionalProperties": false refuses a misspelled argument on the session wire and answers it on the stateless one.

Repro: register a tool with {"type":"object","properties":{"design":{"type":"string"}},"additionalProperties":false}, then call it with {"design":"x","desing":"y"} from a ClientModeStateless client. The session wire answers isError: true; the stateless wire runs the handler.

2. stdio serves no stateless wire

stdio_transport.go has no stateless mode, so a ClientModeStateless client over WithIOTransport gets RPC error -32600: server not initialized on its first call. A host that prefers the stateless wire can't use a stdio server at all.

3. An adaptive client can't fall back from a session-only server

With the HTTP server forced to ModeLegacyOnly (MCPKIT_STATELESS_MODE=legacy), a ClientModeAdaptive client's Connect fails with adaptive probe failed: HTTP 400: missing Mcp-Session-Id header. ClientModeAdaptive falls back to initialize on -32601, and the legacy server answers the server/discover probe with a 400 before it reaches method dispatch. Either the legacy server should answer -32601 for server/discover without a session, or the adaptive client should treat that 400 as "no stateless wire here".

Done when

Each case behaves as on the session wire: (1) a stray argument is a tool error on both wires, (2) a stateless client works over stdio, (3) an adaptive client against a legacy-only server lands on a session and answers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Low priority — periodic reviewbugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions