|

MCP 2.0 Migration: The Hands-On QA Checklist for SDK v2.0.0

MCP 2.0 migration QA checklist featured image

The Model Context Protocol Python SDK shipped v2.0.0 on 28 July 2026, and this is not a quiet point release. pip install mcp now resolves 2.x by default, FastMCP is gone, and any v1 tool code throws import errors the moment a fresh CI environment pulls the latest version. I spent a week running this MCP 2.0 migration on our test-reporting and browser-automation servers, and I made every mistake so you do not have to. Here is the hands-on checklist.

Table of Contents

Contents

What Actually Changed in MCP 2.0

The Python SDK jumped straight from 1.29.0 to 2.0.0, with no 1.x stepping stone in between. The npm side tells a different story: @modelcontextprotocol/sdk sits at 1.30.0 while the new server packages such as @modelcontextprotocol/server, server-legacy, node, hono, and fastify all landed at 2.0.0 on 27 July 2026. If you only watch the npm feed, you miss the breaking change entirely. That version split is a trap in itself, and it is exactly why I wrote this checklist.

The v2.0.0 release notes are explicit about what happened:

  • One SDK, two protocol eras. v2 speaks the 2026-07-28 revision of the Model Context Protocol (stateless requests, no handshake, server/discover, subscriptions/listen, multi-round-trip requests) and still serves every 2025-era client from the same MCPServer object over Streamable HTTP and stdio, with nothing to configure.
  • FastMCP is now MCPServer, and there is a first-class Client that replaces the old transport plus ClientSession plus initialize() layering.
  • v1.x is in maintenance mode. It now receives security fixes only. If you are not ready to move, pin mcp>=1.28,<2.
  • New capabilities ship on by default: OpenTelemetry tracing, pluggable extension APIs, and a standalone types package called mcp-types (imported as mcp_types).

What the 2026-07-28 revision changes for test agents

Under the hood, the protocol itself changed, and that matters more than the SDK renames. At the 2026-07-28 revision, the server can no longer call the client. In practice that means a tool no longer fires a follow-up question at the model mid-request. Instead it returns the question, and a Resolve(fn) parameter is filled by your function invisibly to the model. One tool body now serves both the old and new protocol eras, which is why the migration is mechanical rather than a redesign.

For QA, the practical effect is that multi-round-trip requests and subscriptions/listen change how a test agent asks for data and how you assert on those requests. If your tests mock a server that pushed events to a client, those mocks need a second look, because the direction of the conversation shifted.

The adoption numbers tell you this is not a niche. The Python SDK repo shows 24,060 GitHub stars, and the TypeScript SDK shows 13,205. On the package side, @modelcontextprotocol/sdk pulled 202.8 million npm downloads in the last month, and the mcp package pulled 357.9 million PyPI downloads in the same window. When a dependency moves this fast, your QA tooling moves with it or it rots.

Why QA Engineers Should Care About MCP 2.0 Migration

MCP servers are now part of the QA stack, not a side experiment. Playwright MCP exposes the browser as a set of tools an AI agent can call. Test-reporting MCP servers feed pass/fail results back to those agents. Internal MCP tools let an LLM test agent query Jira, your test-case database, or CI state directly instead of guessing.

When the SDK breaks under you, the failure is not a clean traceback. It is an AI agent that reaches for a dead tool and either times out or reports a hallucinated result. That is a QA problem dressed up as an infrastructure problem, and it lands on your desk either way.

Here is the concrete risk I see on real teams:

  • Fresh environments break first. Your local venv stays on 1.x because it is cached. CI builds a clean image, resolves mcp==2.0.0, and the import fails in front of everyone.
  • Silent behavior changes. MCP_* environment variables are no longer read, and Streamable HTTP request bodies over 4 MiB now return HTTP 413. A large test-report payload that worked yesterday starts bouncing today.
  • Your test agents degrade. A tool-calling agent that loses a tool does not tell you it lost the tool. It fills the gap with a plausible answer, and your coverage gap hides behind a green dashboard.

Here is a concrete failure I watched unfold: a team’s browser-automation server ran on v1 under a pinned lock file for months. The moment a new teammate ran pip install -r requirements.txt with an unpinned mcp, CI pulled 2.x, the import died, and the AI regression agent silently fell back to a cached “no failures” response for two full runs. Nobody saw a red build; the coverage gap showed up in a customer report. That is the real cost of treating this migration as optional.

If you already maintain an MCP server for QA, this migration is mandatory maintenance. If you are about to build one, start on v2 and skip the pain. Either way, the breaking changes below are your map.

The Breaking Changes That Break Test Code

The full list lives in the official migration guide, which walks every change with before-and-after code. These are the four that hit QA code hardest.

FastMCP is now MCPServer

The decorator API is unchanged, so @mcp.tool() still works. What changes is the import and the class name. Every from mcp.server.fastmcp import FastMCP in your repo becomes from mcp.server import MCPServer. The default server name also shifts from FastMCP to mcp-server, which matters if anything in your test suite asserts on the server name.

camelCase is gone, snake_case is in

Field names on MCP protocol types changed from camelCase to snake_case. If your test code reads fields off tool results, request metadata, or capability objects, check every attribute access. Extra fields on MCP types are also no longer preserved, so code that relied on loose passthrough of unknown keys now drops them.

mcp.types moved to mcp-types

Every protocol type is now its own package. from mcp.types import TextContent becomes import mcp_types or from mcp_types import TextContent. The mcp.types alias still exists as a permanent pointer in v2, but the standalone package is the new home, published in lock-step with mcp.

MCP_* env vars, McpError, and other removals

A few removals trip people up because they are not import errors. MCP_* environment variables and .env files are no longer read; configuration moves to pydantic-settings. McpError is renamed MCPError. FileResource(is_binary=) becomes FileResource(encoding=). The mcp dev and mcp install commands now pin the spawned environment to your SDK version, which changes how you set up local dev tooling.

MCP 2.0 Migration: The 7-Step QA Checklist

I have run this migration twice now, once on a small reporting server and once on a browser-automation server with a dozen tools. This order kept both moves boring, which is the goal.

  1. Pin your current version first. Commit the working 1.x state, tag it, and lock the exact version in requirements.txt or pyproject.toml. You need a known-good rollback point before you touch anything.
  2. Inventory every MCP touchpoint. Grep for FastMCP, mcp.types, McpError, ClientSession, and MCP_. List every import and every place you read a protocol type field.
  3. Write a compatibility test suite first. Cover each tool call, each resource read, and the server name. These tests run against v1 today and prove v2 tomorrow.
  4. Bump to mcp==2.0.0 in a branch. Do not do this on main. Let the import errors fire in one place.
  5. Apply the renames. FastMCP to MCPServer, mcp.types to mcp_types, McpError to MCPError, camelCase fields to snake_case.
  6. Fix transports and config. Replace removed MCP_* env vars with pydantic-settings, and audit any payload that could exceed the 4 MiB Streamable HTTP body limit.
  7. Run the compat suite, then a live agent smoke test, then merge. The unit tests prove the tools work. The smoke test proves the agent can still find and call them end to end.

This is not a one-hour job on a real codebase. Budget a day for a small server and two to three days for a server with ten or more tools, mostly in the compatibility tests and the camelCase audit.

Before/After: Migrating a QA MCP Server

Here is a minimal test-result reporter in v1, the kind of server a QA team runs to let an AI agent query the latest run status.

A v1 FastMCP server

# v1 (mcp 1.x)
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("test-result-reporter")

@mcp.tool()
def get_last_run(project: str) -> dict:
    return {"project": project, "status": "passed", "duration_sec": 94.2}

@mcp.resource("run://latest/{project}")
def latest_run(project: str) -> str:
    return f"latest run for {project}: passed"

The v2 MCPServer equivalent

# v2 (mcp 2.x)
from mcp.server import MCPServer

mcp = MCPServer("test-result-reporter")

@mcp.tool()
def get_last_run(project: str) -> dict:
    return {"project": project, "status": "passed", "duration_sec": 94.2}

@mcp.resource("run://latest/{project}")
def latest_run(project: str) -> str:
    return f"latest run for {project}: passed"

The tool bodies do not change. The import and the class name do. The smaller diffs are where the bugs hide. Here are the three renames you will paste most often:

# Error class
from mcp import McpError      # v1
from mcp import MCPError      # v2

# Types package
from mcp.types import TextContent   # v1
import mcp_types                    # v2

For the exact call signature of the new Client, the What’s new in v2 page and the migration guide are the source of truth. Do not copy a v2 client example from a six-month-old blog post; the API moved faster than the tutorials did.

The New QASkills Skill for MCP 2.0 Migration

This is the product update part. I shipped a new skill on QASkills.sh that automates the mechanical half of this migration, because the mechanical half is exactly the part a checklist gets wrong at 6pm on a Friday.

The skill does three things when you run it against a repo:

  • Scans for breaking imports. It greps for FastMCP, mcp.types, McpError, ClientSession, and MCP_ and lists every file and line that will break.
  • Maps the renames. It produces the exact before-and-after diff for each hit, so the camelCase to snake_case changes are not left to memory.
  • Flags the config traps. It calls out removed MCP_* env vars and any Streamable HTTP body that risks the 4 MiB limit.

Install it the same way as any QASkills skill:

npx @qaskills/cli add mcp-2-0-migration

The skill does not do the migration for you, and that is deliberate. It handles the rote scanning and mapping so you spend your time on the two things that need a human: the compatibility tests and the live agent smoke test. If you want the full reasoning behind every breaking change, I already published the deep-dive companion pieces: MCP 2.0 Breaking Changes Every QA Engineer Must Know and MCP 2.0 Migration: The Breaking Changes Every SDET Will Hit.

Testing Your Migrated MCP Server

The migration is not done until you have tested it. v2 gives you one capability that makes this dramatically easier than v1: the Client can connect straight to a server object in memory, so your tests run without spinning up a socket.

from mcp import Client
from mcp.server import MCPServer

mcp = MCPServer("test-result-reporter")

# ... register tools ...

async def test_get_last_run():
    client = Client(mcp)  # in-memory, no network, no subprocess
    result = await client.call_tool("get_last_run", {"project": "checkout"})
    assert result is not None

This in-memory client is the single biggest QA win in v2. v1 forced you to stand up a transport and run initialize() before you could assert anything. Now a unit test for a tool is a few lines and runs in milliseconds.

Pair it with two more checks:

  • An end-to-end smoke test that launches the server over Streamable HTTP (or stdio) and has a real agent call one tool. This is the test that catches the config traps, because it exercises the transport, not just the handler.
  • A payload-size test that sends a response near and above 4 MiB to confirm you get the expected behavior instead of a silent truncation.

One more thing worth knowing: OpenTelemetry tracing now ships on by default in v2. That is a gift for QA, not a chore. You get traces of every tool call through your MCP server for free, which means the next time a test agent produces a wrong result, you can follow the tool call end to end instead of reconstructing it from logs. Wire those traces into your existing observability stack during the migration, while you are already inside the server code.

For the broader question of how to test an MCP server as a product, not just a migration, I covered it in MCP Server Test Plan for Tool-Calling Agents and MCP Server Testing: Compatibility Checks for AI QA.

India Context: MCP Is Now an SDET Job Requirement

I watch SDET job descriptions closely, because my audience lives in the gap between a manual testing job and an automation role. MCP has crossed from “nice to have” to a named requirement in a growing share of AI-adjacent QA postings in Bengaluru and Hyderabad.

Here is what I see shifting:

  • AI test-agent roles now list MCP. Postings for “AI QA Engineer” and “SDET, AI Platform” increasingly mention building or maintaining MCP servers so agents can reach test infrastructure. Six months ago that line was rare; today it is common on product-company postings.
  • MCP skills separate the AI-tool user from the AI-tool builder. Anyone can prompt an agent. The candidate who can migrate a FastMCP server to MCPServer, write an in-memory client test, and keep the tools healthy is the one who justifies the senior band.
  • The pay gap follows the skill gap. In my experience, AI-platform SDET roles in product companies land in the ₹25 to 40 LPA band, well above the automation roles that stop at Selenium plus a bit of Playwright. MCP fluency is one of the fastest ways to move up that ladder.

If you are a manual tester or a Selenium-focused automation engineer and MCP still sounds abstract, start with the two compatibility-check posts I linked above, then do this migration on a throwaway server. The fastest way to understand a protocol is to break and fix a tool that speaks it.

Common Migration Traps to Avoid

Every one of these bit me or a teammate during the two migrations, so I am writing them down instead of pretending they did not happen.

  • Assuming npm and PyPI move together. They do not. Python went to 2.0.0, the npm core SDK stayed at 1.30.0, and the npm server subpackages went to 2.0.0. Check the package you actually import, not the ecosystem headline.
  • Skipping the compatibility tests. You will rename FastMCP to MCPServer, see the import resolve, and call it done. Then a camelCase field access fails at runtime inside an agent call and you do not find it until production.
  • Forgetting the 4 MiB body limit. Large test reports and big result payloads that worked on v1 can now return HTTP 413 on Streamable HTTP. This one is invisible in local unit tests because you never send a big payload there.
  • Leaving MCP_* env vars in your deploy config. v2 ignores them. Your server silently falls back to defaults and you spend an afternoon wondering why the transport changed.
  • Copying v2 client code from stale tutorials. The API changed fast. The only trustworthy reference is the official docs, and even then, check the date.

Key Takeaways

MCP 2.0 migration is not a refactor you can defer forever, and it is not a rewrite. It is a bounded, mechanical change wrapped around a few genuine decisions. The short version:

  • Python SDK v2.0.0 shipped 28 July 2026; pip install mcp now pulls 2.x, and v1 is security-fixes-only maintenance mode.
  • The renames are FastMCP to MCPServer, mcp.types to mcp_types, McpError to MCPError, and camelCase to snake_case.
  • The traps are the MCP_* env var removal and the 4 MiB Streamable HTTP body limit, not the imports.
  • v2’s in-memory Client(server) makes tool tests a few lines instead of a transport setup.
  • The new QASkills skill (npx @qaskills/cli add mcp-2-0-migration) automates the scan and rename mapping so you focus on tests and the live smoke check.

FAQ

Is the npm SDK also 2.0.0?

No, and that is the confusing part. The core @modelcontextprotocol/sdk package is at 1.30.0, while the new server packages such as @modelcontextprotocol/server and @modelcontextprotocol/node are at 2.0.0. Check the specific package you import rather than assuming the whole ecosystem moved together.

Do I have to migrate right now?

Not if your dependency is pinned. v1.x is in maintenance mode and still gets security fixes, so pin mcp>=1.28,<2 and schedule the migration on your own timeline. What you should not do is leave an unpinned mcp requirement that silently resolves 2.x in CI.

What is the single biggest breaking change?

The FastMCP to MCPServer rename breaks the most imports, but the camelCase to snake_case field rename causes the most subtle runtime bugs because it fails inside a tool call, not at import time.

How do I test a migrated MCP server?

Use v2’s in-memory client: Client(mcp) connects to a server object with no network or subprocess. Pair that with a live smoke test over your real transport and a payload-size check near the 4 MiB limit.

Where do I find the authoritative breaking-change list?

The official migration guide lists every breaking change with before-and-after code. The v2.0.0 GitHub release is the summary. Bookmark both before you start.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.