Context
PR #21 merged 2026-07-28-shaped code that still speaks 2025 on the wire:
| Spec |
Code today |
ping removed |
build_ping_response, pipeline fast path, MODERN_METHOD_CONTRACTS includes ping with requires_method_header=False |
-32021 Missing Required Client Capability |
JsonRpcErrorCode has -32020 and -32022 only (nitrostack/protocol/errors.py) |
serverInfo / version / caps on result _meta |
build_discover_result puts serverInfo on the body; _meta is only a cache hint |
resultType on 2026 results |
Set on discover, task create helper, MRTR — not on normal tools/call / list / read results |
SEP-2243 MUST MCP-Protocol-Version + Mcp-Method (and Mcp-Name on name-scoped methods) |
tools/call has requires_method_header=False; comments say header is not required on modern in the sidecar; validate_header_body_method no-ops if header absent |
| Retired methods |
Table still lists tasks/result, tasks/list, logging/setLevel, resources/subscribe as modern contracts; rejected only when era is modern, so auto still serves them |
Sidecar handle_post currently intercepts discover/initialize/ping. WP0 must remove that. This issue is the handler-side wire: core/app.py _setup_handlers, protocol/discovery.py, protocol/errors.py, protocol/method_contract.py.
Handoff: #21 (comment)
Spec references (judge wire, not official SDK DX):
NitroStack may keep modules/DI/@tool. Do not copy official SDK internals.
Goal
After WP0, mcp 2.x owns /mcp. NitroStack handlers stamp 2026 fields. No ping. Modern POST fails closed on missing MUST headers. Discover _meta includes serverInfo.
Probe
rg -n "ping|build_ping_response|-32021|32021|serverInfo|resultType|requires_method_header" nitrostack/protocol nitrostack/core/app.py nitrostack/transports/dispatch.py
Read:
nitrostack/protocol/errors.py
nitrostack/protocol/method_contract.py — entire MODERN_METHOD_CONTRACTS and DEPRECATED_MODERN_METHODS
nitrostack/protocol/discovery.py — build_discover_result, build_sessionless_initialize_result
nitrostack/protocol/jsonrpc.py — header validators; parse_jsonrpc_request treats missing id as a request
nitrostack/core/app.py — handle_server_discover, _setup_handlers, _to_call_tool_result
nitrostack/runtime/stateless.py — comment that protocol version header is not required on modern
Confirm era auto still allows 2025 handshake via mcp 2.x, not via a NitroStack JSON-RPC middleware. Delete build_sessionless_initialize_result from any sidecar path. If mcp needs a NitroStack initialize payload, use NitroStackMcpServer.initialize_handler on the Server object only.
Implementation
- Errors. Add
-32021 to JsonRpcErrorCode + default message. Prefer mcp 2.x owning -32020/-32022 if it already emits them. If you keep a thin header check, it must not become a second method router.
- Ping. Remove from contracts, production helpers, and public exports.
ping → -32601.
- Discover. One builder used by
handle_server_discover and server.add_request_handler("server/discover", ...). Put serverInfo (name, version) on result _meta. Set resultType on discover. If era auto also exposes top-level serverInfo for old clients, modern must still satisfy 2026 _meta rules. Advertise io.modelcontextprotocol/tasks only if the tasks issue implements 2026 tasks (default: do not advertise).
- Headers (modern). POST
/mcp JSON-RPC MUST send MCP-Protocol-Version: 2026-07-28 and Mcp-Method matching body. tools/call, resources/read, prompts/get MUST send Mcp-Name matching name/uri. One validator: missing/mismatch → -32020. Delete the dual optional-vs-required helpers. Era auto: do not require 2026 headers on 2025 handshake; do require them on 2026 methods if mcp does.
resultType. Stamp in NitroStack handlers that already build mcp types: list tools/resources/prompts, tools/call, resources/read, prompts/get — not only discover/MRTR/task.
- Retired methods on modern. Do not register (or register not-found):
tasks/result, tasks/list, resources/subscribe, logging/setLevel, ping, initialize. Do not rely on a sidecar that return None so mcp still runs them. auto handshake is mcp’s job (WP0).
- Method table. If it exists only for tests, delete it and test via HTTP. It must not list
ping / tasks/result / tasks/list as live modern methods.
Do not implement tasks/update here (tasks issue). Do not add era aliases.
Test / verify
Prefer Starlette TestClient on McpApplication.get_combined_app() (same fixture style as tests/test_transport_http.py / lifecycle HTTP tests). Era via NITRO_MCP_PROTOCOL_VERSION / ServerConfig.protocol_era.
| Case |
Era |
Expect |
POST /mcp method: ping |
modern, auto |
-32601 |
POST /mcp server/discover without MUST headers |
modern |
-32020 or mcp equivalent, not 200 |
POST /mcp discover with headers |
modern, auto |
200; serverInfo on result _meta; no Mcp-Session-Id |
POST /mcp initialize |
modern |
-32601 |
POST /mcp tools/call without Mcp-Method |
modern |
-32020 |
-32021 is defined and used or re-exported |
— |
enum exists; at least one test references it (task capability or document “unused until tasks issue”) |
Do not restore header-only ping tests.
pytest on the new tests + tests/test_transport_http.py tests/test_lifecycle_http.py. Expect tests/test_mcp20_stateless_http.py to fail until the test-rewrite issue; do not expand that file.
Review
- Wire changes live in handlers / mcp registration, not a new ASGI JSON-RPC server.
- Discover
_meta.serverInfo present on modern.
rg ping nitrostack/ has no live RPC.
Success
Related
Depends on WP0. Align subscribe flag with WP1. Tasks advertising is WP3.
Context
PR #21 merged 2026-07-28-shaped code that still speaks 2025 on the wire:
pingremovedbuild_ping_response, pipeline fast path,MODERN_METHOD_CONTRACTSincludespingwithrequires_method_header=False-32021Missing Required Client CapabilityJsonRpcErrorCodehas-32020and-32022only (nitrostack/protocol/errors.py)serverInfo/ version / caps on result_metabuild_discover_resultputsserverInfoon the body;_metais only a cache hintresultTypeon 2026 resultstools/call/ list / read resultsMCP-Protocol-Version+Mcp-Method(andMcp-Nameon name-scoped methods)tools/callhasrequires_method_header=False; comments say header is not required on modern in the sidecar;validate_header_body_methodno-ops if header absenttasks/result,tasks/list,logging/setLevel,resources/subscribeas modern contracts; rejected only when era ismodern, soautostill serves themSidecar
handle_postcurrently intercepts discover/initialize/ping. WP0 must remove that. This issue is the handler-side wire:core/app.py_setup_handlers,protocol/discovery.py,protocol/errors.py,protocol/method_contract.py.Handoff: #21 (comment)
Spec references (judge wire, not official SDK DX):
NitroStack may keep modules/DI/
@tool. Do not copy official SDK internals.Goal
After WP0, mcp 2.x owns
/mcp. NitroStack handlers stamp 2026 fields. No ping. Modern POST fails closed on missing MUST headers. Discover_metaincludesserverInfo.Probe
rg -n "ping|build_ping_response|-32021|32021|serverInfo|resultType|requires_method_header" nitrostack/protocol nitrostack/core/app.py nitrostack/transports/dispatch.pyRead:
nitrostack/protocol/errors.pynitrostack/protocol/method_contract.py— entireMODERN_METHOD_CONTRACTSandDEPRECATED_MODERN_METHODSnitrostack/protocol/discovery.py—build_discover_result,build_sessionless_initialize_resultnitrostack/protocol/jsonrpc.py— header validators;parse_jsonrpc_requesttreats missingidas a requestnitrostack/core/app.py—handle_server_discover,_setup_handlers,_to_call_tool_resultnitrostack/runtime/stateless.py— comment that protocol version header is not required on modernConfirm era
autostill allows 2025 handshake via mcp 2.x, not via a NitroStack JSON-RPC middleware. Deletebuild_sessionless_initialize_resultfrom any sidecar path. If mcp needs a NitroStack initialize payload, useNitroStackMcpServer.initialize_handleron the Server object only.Implementation
-32021toJsonRpcErrorCode+ default message. Prefer mcp 2.x owning-32020/-32022if it already emits them. If you keep a thin header check, it must not become a second method router.ping→-32601.handle_server_discoverandserver.add_request_handler("server/discover", ...). PutserverInfo(name, version) on result_meta. SetresultTypeon discover. If eraautoalso exposes top-levelserverInfofor old clients,modernmust still satisfy 2026_metarules. Advertiseio.modelcontextprotocol/tasksonly if the tasks issue implements 2026 tasks (default: do not advertise)./mcpJSON-RPC MUST sendMCP-Protocol-Version: 2026-07-28andMcp-Methodmatching body.tools/call,resources/read,prompts/getMUST sendMcp-Namematchingname/uri. One validator: missing/mismatch →-32020. Delete the dual optional-vs-required helpers. Eraauto: do not require 2026 headers on 2025 handshake; do require them on 2026 methods if mcp does.resultType. Stamp in NitroStack handlers that already build mcp types: list tools/resources/prompts,tools/call,resources/read,prompts/get— not only discover/MRTR/task.tasks/result,tasks/list,resources/subscribe,logging/setLevel,ping,initialize. Do not rely on a sidecar thatreturn Noneso mcp still runs them.autohandshake is mcp’s job (WP0).ping/tasks/result/tasks/listas live modern methods.Do not implement
tasks/updatehere (tasks issue). Do not add era aliases.Test / verify
Prefer Starlette
TestClientonMcpApplication.get_combined_app()(same fixture style astests/test_transport_http.py/ lifecycle HTTP tests). Era viaNITRO_MCP_PROTOCOL_VERSION/ServerConfig.protocol_era.POST /mcpmethod: ping-32601POST /mcpserver/discoverwithout MUST headers-32020or mcp equivalent, not 200POST /mcpdiscover with headersserverInfoon result_meta; noMcp-Session-IdPOST /mcpinitialize-32601POST /mcptools/callwithoutMcp-Method-32020-32021is defined and used or re-exportedDo not restore header-only ping tests.
pyteston the new tests +tests/test_transport_http.pytests/test_lifecycle_http.py. Expecttests/test_mcp20_stateless_http.pyto fail until the test-rewrite issue; do not expand that file.Review
_meta.serverInfopresent on modern.rg ping nitrostack/has no live RPC.Success
-32021exists onJsonRpcErrorCode_metacontainsserverInforesultTypenot limited to discover/MRTRRelated
Depends on WP0. Align subscribe flag with WP1. Tasks advertising is WP3.