On July 28, 2026 the Model Context Protocol locks its 2026-07-28 specification — the biggest breaking change the protocol has shipped. The handshake and sessions are gone; the protocol goes stateless. If you run an MCP server, several things you rely on today stop working on that date.
I built mcp-vet, a zero-config CLI that scans your MCP server source (TypeScript, JavaScript, Python) for the exact patterns that break — and tells you what to change.
npx @booyaka/mcp-vet .
No account, no API key, no network calls. It parses your code locally (ts-morph for TS/JS, a bundled Python ast script for .py) and exits non-zero on anything BREAKING, so it drops straight into CI.
What changes on 2026-07-28
Breaking — these stop working:
-
Mcp-Session-Idheader and protocol-level sessions are removed. Client info and capabilities now travel in per-request_meta. -
initialize/notifications/initializedhandshake is removed. Protocol version, client info, and capabilities now arrive on every request. -
Error code
-32002(missing resource) becomes the JSON-RPC standard-32602Invalid Params. -
Legacy Tasks methods —
tasks/get·tasks/update·tasks/cancelargument shapes change, andtasks/listandtasks/resultare removed entirely (poll withtasks/get).
Deprecated — 12-month grace period: the roots, sampling, and logging capabilities.
It matches how servers are actually written
The trick to a useful migration linter is catching real code, not just raw method strings. mcp-vet matches:
-
literal method strings —
'tasks/list','sampling/createMessage','logging/setLevel', … -
SDK schema-constant registration —
server.setRequestHandler(InitializeRequestSchema, …)(how the official SDKs register handlers) mapsInitializeRequestSchema,ListRootsRequestSchema,CreateMessageRequestSchema,ListTasksRequestSchema, … to the right rule. -
SDK capability constructors — the Python SDK's
ClientCapabilities(roots=RootsCapability())is recognized structurally. -
sessionIdGenerator— flagged only when it's a real generator, not the migratedsessionIdGenerator: undefined.
Every finding carries a confidence (high / medium / low) so you can tune the signal with --min-confidence.
Validated on real servers — 0 false positives
Pointed at the official MCP TypeScript SDK's own example servers, it finds 6 real issues — including the sessionIdGenerator session usage that servers set but rarely spell out as a literal header — and stays quiet on the Mcp-Session-Id and initialize mentions that live only in comments:
legacy-routing.ts:36 BREAKING MCP_SESSION_ID req.headers['mcp-session-id']
legacy-routing.ts:41 BREAKING MCP_SESSION_ID sessionIdGenerator: () => randomUUID()
sse-polling.ts:34 DEPRECATED LOGGING_CAP capabilities: { logging: {} }
6 finding(s): 5 BREAKING, 1 DEPRECATED
Run across a broad corpus of the official reference servers (TypeScript and Python), it produced 0 false positives — precision from structural AST checks, not text matching.
Fix the mechanical one automatically
The -32002 → -32602 swap is purely mechanical, so it's auto-fixable — with a preview first:
npx @booyaka/mcp-vet . --fix --dry-run # show the rewrites, change nothing
npx @booyaka/mcp-vet . --fix # apply them
Drop it into CI
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npx @booyaka/mcp-vet . --github-annotations
It also emits SARIF 2.1.0 for GitHub code scanning, plus Markdown/JSON reports, --json for piping, and inline suppression comments. And it's a typed library, not just a CLI:
import { scan, applyFixes } from '@booyaka/mcp-vet';
const { findings } = scan(['./src'], { /* ... */ });
What it can't tell you (on purpose)
A few 2026-07-28 changes have no reliable static signal — the removed long-lived SSE push channel, the new required Mcp-Method / Mcp-Name headers, auth hardening, and JSON-Schema-2020-12 tool schemas. mcp-vet documents these and prints a reminder after every scan, so a clean result never pretends to mean "fully migrated."
The spec locks in days — better to find out now than when a client stops talking to your server.
Repo: https://github.com/Booyaka101/mcp-vet
Install: npx @booyaka/mcp-vet .

Top comments (4)
Useful static layer, especially the distinction between executable AST patterns and comments. One migration nuance I would make explicit: July 28 is the specification release date, not a switch that remotely disables existing deployments. Breakage appears when a client and server negotiate or require the new revision, so the production test matrix needs both
2025-11-25and2026-07-28paths during rollout.I’d pair the linter with protocol-level fixtures:
server/discover, per-request protocol/client metadata, required HTTP routing headers, stateless auth context, explicit state-handle creation/resume, duplicate requests, retries on another server instance, and cache invalidation fortools/list. Also verify downgrade/refusal behavior rather than silently accepting a request under the wrong semantics.“0 false positives” is encouraging but incomplete without corpus size, commit SHAs, labeled negatives, and recall. A regression suite of intentionally obfuscated/dynamic usages—aliased imports, wrappers, computed properties, generated registration, and framework adapters—would show what the AST scanner misses. Static analysis can prove known patterns are absent; the conformance and dual-version tests prove the server still speaks the intended wire contract.
This is one of the most useful pieces of feedback I've had — almost all of v0.4.0 came out of it. Point by point:
mcp-vet fixturesnow emits nine runtime scenarios that track your list almost 1:1:server/discover, per-request_meta, routing headers, stateless auth, task-handle create/resume, duplicate requests, retry on another instance,tools/listcache invalidation, and downgrade/refusal — plus a CHECKLIST to run against a live server.caught/+missed/regression suite that documents what the AST scanner catches vs. can't (wrappers, computed properties, generated registration) so the claims can't quietly rot.All in v0.4.0 (
npx @booyaka/mcp-vet .). Thank you — you raised the bar on the tool.Stateless MCP is the kind of change that sounds simple in the spec and turns messy in the glue code around auth, retries, and session assumptions. I like that this checks source patterns locally and fails CI, because most breakage will sit in wrappers and samples people forgot they copied six months ago. The part I'd add to migration plans is exercising client-side assumptions too: a lot of tool reliability bugs only show up when the server is stateless but the client still behaves as if it owns a session.
This comment was the nudge for the headline feature in v0.4.0 — thank you. mcp-vet now flags client-side session ownership too: a client transport built with a real
sessionId/session_id, or a read oftransport.sessionId, gets flagged even when the server scans clean — andsessionId: undefinedis recognized as the migrated form so it doesn't re-flag already-fixed code. That's exactly your "server is stateless but the client still behaves as if it owns a session" case. Appreciate you pointing at the glue code; that's where the copied-six-months-ago wrappers bite. Shipped in v0.4.0.