Skip to main content

Find and fix what the MCP 2026-07-28 spec revision breaks in your server.

Project description

mcp-migrate

Scattered, unsorted source blocks on the left resolving through a scan into an ordered, graded grid on the right

mcp-migrate finds and fixes what the MCP 2026-07-28 spec revision breaks in your server: protocol sessions and Mcp-Session-Id removed, initialize / notifications/initialized replaced by server/discover, ping and logging/setLevel gone, resources/subscribe replaced by subscriptions/listen, required resultType and cache metadata on results, server-initiated Sampling/Roots/Elicitation replaced by Multi Round-Trip Requests, and more. The official Python SDK ships no codemod for any of this -- only a manual migration guide. The TypeScript codemod only handles the v1→v2 package rename, not the protocol changes. mcp-migrate fix is the only thing that edits your server's code for you.

uvx mcp-migrate check .
uvx mcp-migrate fix . --write

mcp-migrate check

$ uvx mcp-migrate check tests/fixtures/fixer_roundtrip

mcp-migrate v0.1.0  ->  fixer_roundtrip
2 Python files, 21 rules, spec 2026-07-28

            rule    where         what
breaking    R001    server.py:28  Mcp-Session-Id was removed from the Streamable HTTP transport.
breaking    R001    server.py:29  Mcp-Session-Id was removed from the Streamable HTTP transport.
breaking    R017    errors.py:8   -32002 for resource-not-found is the old code; 2026-07-28 uses -32602.
deprecated  R006    server.py:15  HTTP+SSE transport is deprecated.
deprecated  R006    server.py:24  HTTP+SSE transport is deprecated.
advisory    R004    server.py:32  Tools are returned without an explicit sort.
advisory    R005    server.py:16  Capabilities are declared but `extensions` is absent.
advisory    R010    (project)     This project registers MCP request handlers (tools/resources/prompts)
                                  but has no server/discover implementation anywhere in the project.
advisory    R015    server.py:32  This file implements a result-returning MCP handler but `resultType`
                                  never appears in it.
advisory    R016    server.py:32  This file implements a list/read handler but neither `ttlMs` nor
                                  `cacheScope` appears in it.

  R001  Uses Mcp-Session-Id, which no longer exists
  SEP-2567 https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2567
  Sessions are gone from the transport. Mint an explicit handle server-side and take it as an
  ordinary tool argument instead.

  [... one block like this per rule that fired ...]

Grade F (23/100)  3 breaking, 2 deprecated, 5 advisory

Add your server to the board:  mcp-migrate entry --repo owner/name

Zero findings prints Grade A and a ready-to-paste badge instead. check exits 1 if anything breaking was found, 0 otherwise, so it drops straight into CI. Add --json for machine-readable, uncapped output (the terminal table caps each rule at 5 rows with a "+N more" line so it stays readable; JSON always has every finding).

By default, check skips test code: anything under a tests/, test/, testing/, fixtures/, examples/, or docs/ directory, plus test_*.py, *_test.py, and conftest.py files. A backward-compat test that deliberately exercises a deprecated transport, or an integration test that posts a literal {"method": "tools/list"} payload, is evidence your project is well tested -- not evidence the server itself is broken. Pass --include-tests to scan those paths too.

mcp-migrate fix

$ uvx mcp-migrate fix tests/fixtures/fixer_roundtrip

errors.py
--- a/errors.py
+++ b/errors.py
@@ -5,7 +5,7 @@
 
 def read_resource(handle: str) -> dict:
     if not _exists(handle):
-        return {"code": -32002, "message": "resource not found"}
+        return {"code": -32602, "message": "resource not found"}
     return {"contents": _load(handle)}
 
 
  [R017/safe] line 8: resource-not-found error code -32002 -> -32602

server.py
--- a/server.py
+++ b/server.py
@@ -12,26 +12,29 @@
 from __future__ import annotations
 
 from mcp.server import Server
-from mcp.server.sse import SseServerTransport
+from mcp.server.streamable_http import StreamableHTTPServerTransport
 from mcp.types import ServerCapabilities, Tool, ToolsCapability
 
 server = Server("fixture-server")
 
 capabilities = ServerCapabilities(
     tools=ToolsCapability(list_changed=True),
+    extensions={},
 )
 
-transport = SseServerTransport("/messages")
+# TODO(mcp-migrate): verify no constructor args were lost moving off SSE, see https://modelcontextprotocol.io/specification/draft/changelog
+transport = StreamableHTTPServerTransport()
 
 
 def _session_for(request):
-    mcp_session_id = request.headers.get("Mcp-Session-Id")
+    # TODO(mcp-migrate): replaced by an explicit handle argument, see https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2567
+    # mcp_session_id = request.headers.get("Mcp-Session-Id")
     return mcp_session_id
 
 
 @server.list_tools()
 async def list_tools() -> list[Tool]:
-    return [
+    return sorted([
         Tool(name="zeta", description="Last alphabetically."),
         Tool(name="alpha", description="First alphabetically."),
-    ]
+    ], key=lambda t: t.name)
  [R001/review] line 28: commented out Mcp-Session-Id header access, added TODO
  [R004/safe] line 35: wrapped returned tool list in sorted(key=lambda t: t.name)
  [R005/safe] line 20: added extensions={} to ServerCapabilities(...)
  [R006/review] line 15: import Streamable HTTP transport instead of SSE
  [R006/review] line 25: SseServerTransport(...) -> StreamableHTTPServerTransport(), flagged for review

2 file(s), 6 change(s): 3 safe, 3 flagged for human review
4 finding(s) still need a human after this fix -- run `mcp-migrate check tests/fixtures/fixer_roundtrip` for details.
Dry run -- nothing was written. Re-run with --write to apply.

fix runs in dry-run mode by default (that's what --dry-run names explicitly; it's implied when you pass neither flag) -- it prints the exact unified diff for every file that would change and writes nothing. Pass --write to apply it. --dry-run and --write are mutually exclusive.

Every change is tagged safe or review in the diff output:

  • safe -- the fixer is certain the transformation can't change behavior (e.g. wrapping an already-unordered list literal in sorted(), or adding extensions={} where absent already meant "no extensions"). Apply these with confidence.
  • review -- the fixer did the mechanical part it can be sure of (renaming an import, commenting out a now-nonexistent header read) and left a # TODO(mcp-migrate): ... exactly where a human has to finish the job, because the rest genuinely requires a judgment call this tool can't make for you (what do you name the new handle argument? what constructor args did the old transport need that the new one doesn't take?).

Pass --safe-only to apply only safe fixers, --rule R006 to restrict to one rule, --include-tests to also fix test/fixture paths (skipped by default, same rule as check). Only 5 of the 21 rules ship a fixer at all -- see the table below and mcp-migrate fixers. Fixers are deliberately conservative: when a fixer can't be sure a transformation is correct, it leaves the source untouched rather than guess. A wrong fix that silently corrupts your server is worse than reporting the finding and doing nothing.

Other commands

mcp-migrate rules                     # list every rule this version ships, with severity and spec ref
mcp-migrate fixers                    # list every fixer, with confidence (safe/review)
mcp-migrate entry --repo owner/name   # print a registry/servers/*.yaml entry for the board

Every rule

Rule Severity What breaks Fixer
R001 breaking Mcp-Session-Id is gone from the Streamable HTTP transport (SEP-2567). yes (review)
R002 breaking Servers are required to be stateless (SEP-2567); a module-level dict keyed by connection breaks behind a load balancer or a restart. no
R003 advisory Hand-rolled HTTP clients that skip the new required Mcp-Method/Mcp-Name routing headers get rejected by anything enforcing the new transport. no
R004 advisory tools/list order is not guaranteed; non-deterministic ordering defeats caching. yes (safe)
R005 advisory Optional capabilities negotiate through an extensions map that isn't declared. yes (safe)
R006 deprecated HTTP+SSE is deprecated in favor of Streamable HTTP; stays in the spec 12+ months, then leaves. yes (review)
R007 deprecated Roots, Sampling and Logging are deprecated as core capabilities. no
R008 advisory Trace context (traceparent, tracestate, baggage) now travels in _meta (SEP-414); OpenTelemetry breaks at your server if it's never read. no
R009 breaking The initialize/notifications/initialized handshake (SEP-2575) is gone; a server still implementing it never becomes usable to a 2026-07-28 client. no
R010 advisory Servers must implement server/discover (SEP-2575); registering handlers without it leaves clients with no way to learn what you support. Downgraded from breaking: this checks for something the new spec introduced, so it fires on ~100% of pre-migration servers and has no discriminating power. no
R011 breaking ping (SEP-2575) is removed from the protocol; liveness rides on the transport now. no
R012 breaking logging/setLevel (SEP-2575) is removed; log level is per-request via _meta now. no
R013 breaking resources/subscribe/resources/unsubscribe (SEP-2575) are replaced by subscriptions/listen. no
R014 breaking SSE resumability via Last-Event-ID (SEP-2575) is removed; a dropped connection is just dropped now. no
R015 advisory Every result now requires resultType (SEP-2322); results returned without it are malformed. Downgraded from breaking for the same reason as R010 -- it fires on ~100% of servers today. no
R016 advisory List/read results require ttlMs/cacheScope (SEP-2549). Downgraded from breaking for the same reason as R010 -- it fires on ~100% of servers today. no
R017 breaking The resource-not-found error code changed from -32002 to -32602. yes (safe)
R018 breaking Server-initiated Roots/Sampling/Elicitation (SEP-2322) are replaced by Multi Round-Trip Requests (InputRequiredResult + inputResponses). no
R019 breaking tasks/list is removed and blocking tasks/result (SEP-2663) is replaced by polling; Tasks moves to an extension. no
R020 deprecated RFC 7591 Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents. no
R021 advisory Implementations must support at least JSON Schema 2020-12 (SEP-2106) for inputSchema/outputSchema. no

Run mcp-migrate rules to see this list for the exact version you have installed, and mcp-migrate fixers for the fixer confidence table. Full spec changelog: https://modelcontextprotocol.io/specification/draft/changelog.

Missed something and want to fill in a fixer, or just document the manual fix? See Contribute below and cookbook/ for every change that doesn't have a recipe yet.

The grade

Every finding costs points, but no single rule can sink your grade by itself: each rule's total contribution is capped, no matter how many times it fires. Real evidence for why this matters: mcp-atlassian's R003 once hit 19 times on the same false-positive pattern, for 475 raw penalty points -- over 4x what one rule is now allowed to cost.

Severity Cost per finding Cap per rule Meaning
breaking -25 -25 Your server stops working under 2026-07-28.
deprecated -8 -12 Still works today, on a 12+ month clock.
advisory -3 -6 Best practice, not a compatibility risk.

Score starts at 100 and floors at 0. The letter grade comes from the score:

Score Grade Badge color
95-100 A brightgreen
80-94 B green
60-79 C yellow
40-59 D orange
0-39 F red

Get your badge

Run the check, fix what's breaking, then generate your entry and badge in one shot:

uvx mcp-migrate entry --repo owner/name > registry/servers/name.yaml

That prints something like:

# registry/servers/name.yaml
name: name
repo: owner/name
language: python
grade: A
score: 100
checked_with: mcp-migrate 0.1.0
spec: "2026-07-28"
status: ready
notes: >-
  Replace this line with one sentence about what your server does.

Edit the notes: line to one sentence about what your server does, then PR it into this repo (steps in CONTRIBUTING.md). CI validates the YAML against registry/schema.yaml and regenerates the board below on merge. Listing is automated and unopinionated: schema passes and the repo exists, it merges -- no maintainer reviews the server itself or argues about your grade.

Drop this in your own README once you know your grade (swap the letter and color using the table above):

[![MCP 2026-07-28](https://img.shields.io/badge/MCP%202026--07--28-A-brightgreen)](https://github.com/owner/name)

The board

14 servers checked (6x A, 6x B, 1x C, 1x D)

server grade status language what it does
cloudwatch-mcp-server A ready python AWS Labs MCP server for CloudWatch that gives troubleshooting agents alarm, metric, and log data for root cause analysis.
mcp-server-qdrant A ready python Official MCP server for Qdrant that acts as a semantic memory layer for keeping and retrieving memories in the vector search engine.
aws-documentation-mcp-server A ready python AWS Labs MCP server that fetches, searches, and recommends AWS documentation pages, converted to markdown.
duckduckgo-mcp-server A ready python MCP server that provides web search through DuckDuckGo, with additional content fetching and parsing features.
dynamodb-mcp-server A ready python Official AWS DynamoDB MCP server providing expert data modeling guidance, validation, and cost analysis tools.
mcp-server-motherduck A ready python Local MCP server connecting AI assistants to DuckDB and MotherDuck for SQL analytics and data engineering.
mcp-server-tree-sitter B ready python MCP server providing tree-sitter code analysis so AI assistants get structure-aware access to codebases in many languages.
mcp-neo4j-cypher B ready python MCP server for Neo4j that runs Cypher graph queries and supports Text2Cypher workflows over graph data.
mcp-server-fetch B ready python Reference MCP server that fetches web pages and converts HTML to markdown so LLMs can read them in chunks.
mcp-server-time B ready python Reference MCP server giving LLMs current time and timezone conversion using IANA timezone names.
mcp-server-sentry B ready python Archived reference MCP server for retrieving and analyzing issues, stacktraces, and debugging info from Sentry.io.
mcp-server-sqlite B ready python Archived reference MCP server for SQLite that runs SQL queries and auto-generates business insight memos.
mcp-atlassian C migrating python MCP server for Atlassian products (Confluence and Jira), supporting both Cloud and Server/Data Center deployments.
mcp-server-git D ready python Reference MCP server for Git repository interaction, giving LLMs tools to read, search, and manipulate repos.

Contribute

Three ways in, cheapest first:

  • A cookbook recipe (~5 minutes). Pick an open slot in cookbook/ -- markdown, no Python, no tests. See CONTRIBUTING.md.
  • A rule (~15 minutes). A Rule subclass -- a check(project) method, a regex or an AST walk, a Finding for every hit. Most rules are under 25 lines. See CONTRIBUTING.md.
  • A fixer (~45 minutes). A Fixer subclass that turns one rule's finding into a text-level edit, tagged safe or review. See CONTRIBUTING.md.

The standing principle behind all three: a false positive is worse than a missed finding. When a rule can't be made precise, it ships advisory, or it doesn't ship. Reviews happen within 48 hours; one passing test is enough to merge.

Listing your own server on the board is separate from all three and takes about 60 seconds -- see CONTRIBUTING.md.

Working principles

For the project's non-negotiable principles, current state, and priorities, see HANDOFF.md -- it's the brief a new maintainer gets, and it's kept accurate on purpose.

License

Apache-2.0. See LICENSE.

Listing on the board is automated and unopinionated: if registry/servers/*.yaml passes schema validation and the repo it points at exists, it gets merged. No maintainer reviews the server itself.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_migrate-0.1.0.tar.gz (278.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_migrate-0.1.0-py3-none-any.whl (62.7 kB view details)

Uploaded Python 3

File details

Details for the file mcp_migrate-0.1.0.tar.gz.

File metadata

  • Download URL: mcp_migrate-0.1.0.tar.gz
  • Upload date:
  • Size: 278.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_migrate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5cd66a5b76c0404423e612839ad4340abad86b28c56a8ae3472bb54f810ca4a6
MD5 6674e2c2b7f4eae273d02851524c4c9e
BLAKE2b-256 618a8c6e5c67883943a16ef71a93d192d8a248bf28f4f1e9ac9c9dc18db4b962

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_migrate-0.1.0.tar.gz:

Publisher: release.yml on dheerajjha/mcp-migrate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_migrate-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_migrate-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 62.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_migrate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c0f19c23d63d348daef5b70a5d7c9c22993abe5954e8898ee6d9c6a90d410753
MD5 467696c21171a60c57d6b27cf3656e53
BLAKE2b-256 755d75347a3bcf17397053c145df4628f30f5984ba2d8a0bdb90e7c662ac52ad

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_migrate-0.1.0-py3-none-any.whl:

Publisher: release.yml on dheerajjha/mcp-migrate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page