The runtime remains 2026-only by default. Applications can opt into
Snodo.Protocol.V2025_11_25 and Snodo.Protocol.V2025_06_18 alongside
Snodo.Protocol.V2026_07_28, in that preference order after the latest dialect.
Merely loading a module does not enable it.
HTTP
initialize selects the exact requested enabled legacy version, or the first
configured initialize-capable dialect for an unsupported proposal. Malformed
initialization and conflicting version headers fail. Subsequent requests use
MCP-Protocol-Version; missing headers are rejected because the March 2025
fallback dialect is not implemented. Initialization does not require that header.
notifications/initialized returns an empty HTTP 202 response. Ping is supported.
GET and DELETE return 405. JSON-RPC errors from executing a request, such as an
unknown tool or method, are returned with HTTP 200, because clients of these
revisions treat a non-2xx answer as a transport failure and 404 as an expired
session. Admission failures (media type, Origin, a missing or unsupported
MCP-Protocol-Version) keep their 4xx statuses. There is no HTTP session ID, session process, retained
client capability state, or lifecycle registry. Authentication remains application
owned and is evaluated for each request; session or client metadata never becomes
an identity. Applications must supply the existing cancellation scope for peers
that need separate request-id namespaces.
Stdio
Over stdio the version is negotiated once per connection. initialize selects
a version as it does over HTTP, and Snodo.Transport.Stdio records the version
from the successful result. Every later message on the connection is handled
as if it carried MCP-Protocol-Version with that version, so dialect
selection, validation, and result shaping are the same as over HTTP.
notifications/initialized gets no reply, and a repeated initialize
negotiates again. A request sent before initialize carries no version and is
not handled by a legacy dialect. A 2026-07-28 client never sends initialize,
so its messages are unaffected. Progress and notifications/cancelled work as
they do for 2026-07-28 clients on the same transport.
Shared behavior
The shared router handles tools, prompts, resource lists/templates/reads,
completion and pagination. Legacy wire shaping omits 2026 resultType, cache and
server metadata fields. Legacy structured output and output schemas require an
object. A tool whose input or output schema is not an object is left out of
tools/list on these dialects, and a call to it returns -32602 before it runs;
Snodo.Server.Runtime.new/1 logs a warning naming such tools when a legacy
dialect is enabled. The 2026-07-28 dialect still lists and calls them.
Execution errors retain readable content with isError; protocol errors
remain JSON-RPC errors. Request-bound progress can use SSE; cancellation retains
the executor's authenticated isolation. Unsupported Tasks, continuation inputs,
subscriptions and server requests are not advertised. No Tasks or
session-manager behavior is added.
Configured capabilities must be valid for every enabled profile. For a mixed runtime, use the implemented base tools/prompts/resources/completions capabilities; listChanged and subscribe remain unavailable on this legacy slice. Applications that need latest-only subscriptions can expose a separate 2026-only runtime.
Official conformance
The official runner's frozen 2025-11-25 requirement set runs in CI against a
fixture with these dialects enabled: 21 of 30 required scenarios pass. The
2025-06-18 run has no frozen set; 21 of its 27 scenarios pass. The failures are
logging/setLevel, server-initiated sampling and elicitation, and
resources/subscribe, which this slice does not implement, plus a warning that
no session ID is issued. See the
conformance lanes.
Snodo.Client as an initialize-era client
Snodo.Client speaks these two versions as well as 2026-07-28, so it can call
a server built on an older SDK, or a snodo server configured with only these
dialects. By default connect/2 probes with server/discover and falls back
to initialize with 2025-11-25, then 2025-06-18; protocol: pins a version.
The negotiated session, the Mcp-Session-Id handling, DELETE on close, and
the routing of the server's own requests (elicitation/create,
sampling/createMessage, roots/list) to the client's input handlers are
described in the client guide.
Against a snodo server with these dialects, a direct, stdio, or HTTP client
negotiates without any application code: the runtime's registry (direct) or
the probe (stdio and HTTP) picks the version.
Native client evidence
HTTP loopback checks on 2026-09-21 against a real application server:
- Claude Code 2.1.273
mcp listinitializes with 2025-11-25, connects, and lists tools. - Codex 0.149.0 initializes with 2025-06-18, lists tools, resources, and resource templates, calls a tool, and reads both a fixed resource and a templated one.
- A restricted Claude print session uses 2026-07-28
server/discoverand calls the same tool.
Stdio checks on 2026-09-26 against a probe server with these dialects enabled:
- Codex 0.157.1 initializes with 2025-06-18, sends
notifications/initialized, lists tools, and calls a tool. - Claude Code 2.1.283 sends a 2026-07-28
server/discoverprobe, lists tools, and calls the same tool. It does not use these dialects.
These checks show that the implemented workflows work with those clients. They are not general SDK conformance. The test suite covers literal legacy requests, shared routing, pagination, rejection cases, mixed-version routing, HTTP progress, cross-principal cancellation, and stdio negotiation.