It's 3 a.m. One customer's build_report call fails; everyone else is fine.
Legacy: you send logging/setLevel: debug, and every user on that server now floods your log pipeline. The bill spikes and the one line you need is buried.
Now: you replay just that customer's call with "io.modelcontextprotocol/logLevel": "debug". Debug lines come back on that one response stream and nowhere else.
Meanwhile your Kubernetes liveness probe, which used ping, starts getting 404s and restarting healthy pods. Switch it to a plain /healthz endpoint, or to server/discover.
logging/setLevel is removed. The level is now set per request with io.modelcontextprotocol/logLevel in _meta. If a request doesn't carry it, the server MUST NOT send any notifications/message for that request.
notifications/roots/list_changed is removed.
notifications/elicitation/complete and the elicitationId on URL-mode elicitation (both added in 2025-11-25) are removed. Under MRTR, the client learns the outcome by retrying, and the server tracks its own ID inside requestState.
"Resource not found" changed from -32002 to -32602. Don't emit the old code, but clients should still accept it from older servers. -32042 (URL elicitation required) is retired too.
Each of these assumed a session: a global log level, a liveness check on a connection, a server-pushed completion signal. In a stateless protocol they either have nothing to attach to or are covered by something else.
{"jsonrpc":"2.0", "id":5,"error": { "code":-32602, "message":"Resource not found" }}
What it used to look like (legacy, for comparison only)
Legacybefore
{"jsonrpc":"2.0", "id":5,"error": { "code":-32002, "message":"Resource not found" }}
-32602 is the standard JSON-RPC "Invalid params" code. The spec's new allocation rule: -32000 to -32019 is legacy territory, and -32020 to -32099 is reserved for MCP.
First time here? Set up the test helper (once per terminal)
ShellSetup
# 1. In a SECOND terminal, start the reference server (Node 18+, no dependencies)curl -sO https://www.diegozuluaga.dev/mcpa/reference-server.mjsnode reference-server.mjs # http://localhost:3000/mcp, logs appear here# 2. In THIS terminal, define the helper every test uses (bash or zsh)export MCP=http://localhost:3000/mcpMETA='"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}'mcp() { # usage: mcp <method> '<json body>' [extra curl args...] curl -sS -N "$MCP" \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'MCP-Protocol-Version: 2026-07-28' \ -H "Authorization: Bearer ${MCP_USER:-alice}" \ -H "Mcp-Method: $1" "${@:3}" -d "$2" \ -w '\nHTTP %{http_code}\n'}# Demo auth: the reference server treats the bearer token as the user's name.# Prefix a command with MCP_USER=bob to act as someone else.
ShellTests
# 1. Without logLevel: zero notifications/message (takes 10 s)mcp tools/call '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"build_report","arguments":{"month":"2026-09"},'"$META"'}}' -H 'Mcp-Name: build_report' | grep -c notifications/message# expect: 0# 2. With logLevel: debug lines arrive on THIS request's stream onlymcp tools/call '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"build_report","arguments":{"month":"2026-09"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{},"io.modelcontextprotocol/logLevel":"debug"}}}' -H 'Mcp-Name: build_report'# 3. ping is gone: expect 404 and -32601. Health checks use a plain endpoint instead:mcp ping '{"jsonrpc":"2.0","id":3,"method":"ping","params":{'"$META"'}}'curl -sS http://localhost:3000/healthz; echo# 4. Missing resource: expect -32602, not -32002mcp resources/read '{"jsonrpc":"2.0","id":4,"method":"resources/read","params":{"uri":"file:///nope",'"$META"'}}' -H 'Mcp-Name: file:///nope'