Skip to main content

Codemod that migrates MCP Python SDK v1 code to v2 (spec 2026-07-28).

Project description

mcp-codemod

Migrates MCP Python SDK v1 code to v2 (the 2026-07-28 spec revision).

The TypeScript and Go SDKs ship a codemod for this migration. Python does not. Python developers get a 2,879-line migration guide and a lot of manual find-and-replace. This tool fills that gap.

Installation

pip install mcp-codemod

Usage

mcp-codemod path/to/your/server           # dry run, prints a diff
mcp-codemod path/to/your/server --write   # applies the changes

Dry run is the default. Review the diff before writing.

Why not find-and-replace

The v2 models still accept camelCase at construction time. Only attribute access changed:

tool = Tool(name="forecast", inputSchema={"type": "object"})   # valid in v2
print(tool.inputSchema)                                        # broken in v2

Running sed -i 's/inputSchema/input_schema/g' breaks the first line while fixing the second. This tool handles both correctly because it operates on the concrete syntax tree, where a keyword argument is a Name node and attribute access is an Attribute node. Visiting only Attribute nodes cannot reach the keyword argument.

 tool = Tool(name="forecast", inputSchema={"type": "object"})
-print(tool.inputSchema, tool.outputSchema)
+print(tool.input_schema, tool.output_schema)

The implementation uses LibCST rather than the standard library ast module. A round trip through ast discards formatting: it reflows the file, strips comments, and normalises quote style. LibCST preserves every byte it does not deliberately change, so the resulting diff contains only the migration.

Changes applied automatically

Change Example
FastMCP to MCPServer mcp = FastMCP("x") becomes mcp = MCPServer("x")
mcp.server.fastmcp.* to mcp.server.mcpserver.* includes all submodules
McpError to MCPError
camelCase to snake_case attribute access 11 fields including inputSchema, isError, nextCursor, mimeType
ctx.fastmcp to ctx.mcp_server only on parameters annotated Context
Content to ContentBlock
ResourceReference to ResourceTemplateReference
ClientRequestType to ClientRequest and the five other *Type unions
streamablehttp_client to streamable_http_client
mcp.shared.version to mcp.types.version module was removed in v2
timedelta timeouts to float seconds read_timeout_seconds=timedelta(minutes=2) becomes =120

Changes reported for manual review

Some v2 changes depend on runtime types, or on intent that the source code does not express. Applying those automatically would risk introducing bugs, so they are reported and the source is left unmodified.

Code Description
F001 model_dump() without by_alias=True
F002 .root access on a union that is no longer a RootModel
F003 httpx or httpx-sse imported, SDK moved to httpx2
F004 Name removed in v2 with no drop-in replacement (Cursor, AnyFunction, others)
F005 RequestParams.Meta is now a TypedDict, attribute access becomes .get()
F006 Deprecated name (SUPPORTED_PROTOCOL_VERSIONS)
F007 timedelta timeout too dynamic to convert safely
F008 Lowlevel @server.list_tools() decorator, now an on_list_tools= constructor parameter

F001 deserves particular attention. In v1, model_dump() emitted camelCase because the model fields themselves were camelCase. In v2 the same call emits snake_case, which other MCP implementations will not recognise. The migration guide describes the consequence directly:

No error is raised; the output is silently in the wrong shape.

This is not corrected automatically because the receiver cannot be statically proven to be an MCP protocol type. Adding by_alias=True to an unrelated Pydantic model would corrupt that model's output instead.

Imports that are intentionally left alone

The mcp.types module moved to a standalone mcp-types distribution, but this is a no-op for projects that depend on mcp. The migration guide is explicit:

mcp.types is a permanent alias that mirrors mcp_types exactly. Keep importing through mcp, the package you actually depend on, rather than writing import mcp_types, which would reach past your declared dependency into a transitive one.

Rewriting those imports would introduce an undeclared dependency, so mcp-codemod never does. There is a test covering this.

Relationship to mcp-migration

mcp-migration detects behavioural hazards that a codemod cannot rewrite, such as in-memory state mutated inside tool handlers, session-id dependencies, and live server readiness. It is not a codemod. This tool is not a hazard detector. The two are complementary:

mcp-codemod .          # apply the mechanical changes
mcp-migration scan .   # then check for behavioural hazards

Limitations

  • Coverage is the migration guide's "changes almost every project hits" table plus the removed-alias set. The full guide is 2,879 lines and this tool does not implement all of it.
  • Rewrites are skipped in files that do not import mcp. A module that handles MCP types without importing from mcp will not be processed.
  • Symbol renames are name-based. A local variable named Content in a file that also imports mcp would be renamed. Review the diff.
  • Import order is left untouched. Use isort if you need it.

Development

pip install -e ".[dev]"
pytest

Author

Sanjay Keerthan (@8crsk)

License

MIT. See LICENSE.

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_codemod-0.1.0.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

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

mcp_codemod-0.1.0-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for mcp_codemod-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7dd6e90700d926a7c814525b83458e8e3c23ee33789ca427470264c60a38511b
MD5 0e1019aba63f9b3be723f5958dfa8ae0
BLAKE2b-256 4150912c85684670768e42584ec48120054291001b1dbffc3d42cf74e1b97d39

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on 8crsk/mcp-codemod

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_codemod-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_codemod-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.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_codemod-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ad296ed57589e21346e796eae293a09d1163b3039e8d4836a2988a14de7142d5
MD5 8a4bde896e0fe56dab40e32fc5564cfc
BLAKE2b-256 e3f765f83778db99fa672daf8f152ac5f28e1582dc4451ca6c970a137b5fa20f

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on 8crsk/mcp-codemod

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