MCP 2026-07-28 conformance¶
neon-mcp targets the 2026-07-28 revision of the Model Context Protocol, the
stateless core1, on the Python mcp SDK 2.x (2.2.0 at release) through
its low-level Server API3. The SDK serves both eras on stdio, so clients
on earlier revisions (2024-11-05 … 2025-11-25) still work.
What the server does¶
| Requirement | neon-mcp |
|---|---|
No initialize handshake, no sessions |
Streamable HTTP runs with stateless_http=True: no Mcp-Session-Id, any instance answers any request |
server/discover |
answered by the SDK with supported versions, capabilities (tools, resources, prompts) and the server instructions |
Per-request _meta envelope |
io.modelcontextprotocol/clientCapabilities decides whether MRTR elicitation may be used |
| Cacheable list results | tools/list 5 min, resources/list and resources/templates/list 1 h, prompts/list 24 h, server/discover 5 min, resources/read 1 h (24 h for static guides); cacheScope is always public because nothing depends on the caller |
resultType |
complete on every result; input_required only from neon_get_sample |
Result _meta |
the SDK's io.modelcontextprotocol/serverInfo plus io.neon-mcp/upstream (NEON requests, cache hits, rate-limit headroom, identity) |
| JSON Schema 2020-12 | every inputSchema and outputSchema declares the dialect; outputs match structuredContent |
Tool annotations and _meta |
all four hints on every tool; io.neon-mcp/surface, requiresToken, endpoints, stdioOnly |
| Header routing | Mcp-Method / Mcp-Name must match the body (400, -32020); an unsupported version is -32022 |
| Deprecated features | none: no roots, sampling, logging/setLevel, SSE transport or sessions (a CI job greps for them) |
MRTR and requestState¶
Only neon_get_sample asks the user a question, and only when the client declares
elicitation support: a sample tag that belongs to several classes produces an
InputRequiredResult with a form listing the candidates. The continuation
(requestState) is base64url JSON of at most 16 KiB holding the question and
candidates; it never contains a token, a path or an authorization decision, it expires
after an hour, and the answer is re-validated against a fresh lookup on resume. It is
unsigned by design: tampering can only change the caller's own question.
Measured sizes¶
With all 20 tools (stdio), tools/list is 107,734 bytes of compact JSON: input
schemas 25,347 B, output schemas 67,661 B, descriptions 6,581 characters. HTTP lists 19
tools in 102,342 B. Pydantic's generated title keys are dropped from both schemas and
descriptions from output schemas (the tool reference keeps
them). The conformance bound is 135,000 B (measured × 1.25).
SDK behaviour worth knowing¶
- A request whose
MCP-Protocol-Versionheader names an older revision (e.g.2025-11-25) is served in the legacy era even if its body carries a 2026-07-28 envelope; a modern header with a different envelope version is rejected with-32020. resources/readandprompts/getrequireMcp-Name(the URI or prompt name).
Conformance tests¶
tests/test_spec_conformance_2026_07_28.py and tests/transport/ run in their own CI
job: schema dialect and validity, annotations and _meta, sorted and stable
tools/list within its size bound, cache hints on every list and read, discovery,
stateless requests in both eras, result _meta, header routing, protocol-version
errors, host allow-lists, resources and prompts over HTTP, an MRTR round trip with a
tampered continuation, and a real stdio subprocess speaking both protocol eras.
-
MCP 2026-07-28 release notes. https://blog.modelcontextprotocol.io/posts/2026-07-28/ ↩
-
Model Context Protocol specification, 2026-07-28. https://modelcontextprotocol.io/specification/2026-07-28 ↩
-
MCP Python SDK. https://github.com/modelcontextprotocol/python-sdk ↩