MCP 2026-07-28 one lesson per page
23 lessons
Fails quietlyInteractions · FundamentalsTrack 2 · 10 / 14

Small removals with a long reach

Several utilities that depended on session state are gone, and one familiar error code moved.

Listen to this lessonAudio overview in Gemini Notebook · about 15–25 min · opens in a new tab

3 a.m., one failing call

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.

  • ping is removed.
  • 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.

Same job, two eras
Compare

This is how it works in 2026-07-28. Switch to Legacy only to see what it replaced.

ClientServertools/call _meta{logLevel: debug}notifications/message (this stream)resulttools/call (no logLevel)result, with zero log messages
Solid arrows are requests, dashed are responses or notifications. Red ✕ is gone; green is new.

Turning on debug logs

2026-07-28
{"jsonrpc": "2.0", "id": 9, "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"   } }}// log lines arrive on THIS request's response stream only{"jsonrpc": "2.0", "method": "notifications/message", "params": { "level": "debug", "logger": "report", "data": "fetched 120 rows" }}
What it used to look like (legacy, for comparison only)
Legacybefore
{"jsonrpc": "2.0", "id": 4, "method": "logging/setLevel", "params": { "level": "debug" }}// from now on the server logs at debug, for the whole session

Resource not found

2026-07-28
{"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.

  • Health checks built on ping break. Use server/discover, or a plain HTTP health endpoint.
  • Admin tools that call setLevel break, and servers that log by default now break the "MUST NOT" rule.
  • Clients matching -32002 stop recognising not-found errors from modern servers.
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' 

Answer all 2 correctly and this lesson is marked as learned.

Q1A request arrives with no io.modelcontextprotocol/logLevel. May the server send notifications/message for it?

Q2Your container health check used MCP ping. After upgrading to 2026-07-28 it fails. What is the best replacement?

Feedback or a correction? Email diego [at] diegozuluaga [dot] dev or open an issue on GitHub.

Content CC BY 4.0 · Code MIT

Tip: ← and → move between lessons. Hover any heading and press # to copy a link to it.