The goal is that migrated v1 code runs on v2 on its legacy paths, not
that it adopts v2 idioms. Applying that bar:
- Leave e.error.code / .message / .data chains alone: v2's MCPError
keeps a typed .error ErrorData, so the v1 spelling runs and
type-checks unchanged. The except-binding tracking goes with it.
- Rewrite one-argument McpError(...) calls to MCPError.from_error_data(...)
instead of flattening the inline ErrorData: the user's expression is
kept as written and the non-inline form no longer needs a marker.
- Convert v1 positional arguments on the lowlevel Server constructor to
keywords (v2 is keyword-only after name but kept v1's names and order),
pinned against the installed signature by a new ratchet test.
- Reword every marker message that pointed at replaced internals or at
the successor of the removed experimental tasks API; state removals
plainly instead of steering users onto new surfaces.
- Teach the batch harness that a reportArgumentType error naming a
detonating argument type (timedelta, AnyUrl) is a real break, never
v2 strictness drift, and ignore stale work/ directories.
Three additions to mcp-codemod, closing the gaps a comparison with the
TypeScript codemod surfaced:
Imports of module namespaces v2 deleted outright (the experimental tasks
namespaces, the WebSocket transports, `mcp.shared.progress`) are now
marked with replacement guidance. A new ratchet test freezes the 107
public modules v1 shipped and asserts every one imports on v2, is
renamed, or is in the removed table, so the whole v1 module namespace is
provably accounted for.
The codemod now also updates the `mcp` requirement in `pyproject.toml`
(PEP 621 tables and dependency groups) and `requirements*.txt` to
`>=2,<3` -- only where the current constraint cannot accept any v2
release, and only the version specifier: name, extras, environment
marker, and spacing keep the user's spelling. Poetry tables and the
removed `ws` extra are marked instead of guessed at, under the same
`# mcp-codemod:` contract as source markers.
`scripts/codemod-batch-test/` runs the codemod against pinned real
repositories and audits the marker contract end to end: it type-checks
the pristine clone against the latest v1 and the migrated copy against
this workspace's v2 with identical pyright settings, then requires every
error that exists only on the migrated side to sit next to a marker.
Across the four repos in the manifest every migration-surface error is
covered, and the audit caught two real bugs now fixed here: `Context`
imported from the old `.server` submodule is rehomed to the package (the
submodule holds the name at runtime, but a type checker treats a
non-re-exported name as private), and `request_context` on a receiver
the pre-pass proved holds a lowlevel `Server` is flagged again --
receiver-matched, so the live `ctx.request_context` idiom stays
untouched.