The MCP Python SDK 2.0 broke the wrapper libraries. Here's how to spot it and pin back The official Python MCP SDK's 2.0.0 release, published July 28, 2026, removed and renamed public APIs, breaking wrapper libraries that depended on `mcp` without an upper version bound. A developer reproduced the failures in isolated virtual environments against versions 1.29.1 and 2.2.0, documenting the renamed `streamable_http_client` import, the context manager's shift from a 3-tuple to a 2-tuple, and Pydantic model attributes moving from camelCase to snake_case. The writeup notes that autogen-ext 0.7.5 remains broken against current `mcp`, while llama-index-tools-mcp was fixed in 0.5.1, and recommends pinning `mcp>=1.28,<2` or upgrading the wrapper. If your MCP tools started failing a few weeks ago and nothing in your own code changed, you're not imagining it. The official Python mcp package shipped 2.0.0 on 2026-07-28. It's a real major version, with public APIs removed and renamed. Latest on PyPI right now is 2.2.0, and a plain pip install mcp resolves to 2.x. Any library that depends on mcp without an upper bound quietly floated forward and broke at install time. I spent a while reproducing this in throwaway venvs with 1.29.1 and 2.2.0 side by side. Here's what actually changed, what the errors look like, and how to get moving again. Two SDKs share the name. Python mcp is the one on PyPI. The Rust one is rmcp on crates.io, which is what Goose and some other Rust agents use, and it's on 3.x now. If you're debugging a Rust agent, the Python 2.x story is not your story. I nearly chased the wrong version myself...... glad I checked the crate name first. In 1.x: python from mcp.client.streamable http import streamablehttp client async with streamablehttp client url, timeout=30, sse read timeout=300 as read, write, get session id : ... In 2.x that import raises ImportError. The replacement is: python from mcp.client.streamable http import streamable http client async with streamable http client url as read, write : ... Two changes there. The name has underscores now, and the context manager yields a 2-tuple. The 3rd element, get session id , is gone. That 2nd part is the one that hurts, because it doesn't fail at import. The wrapper imports fine, then blows up at runtime, and you get the error a lot of people are probably googling right now: ValueError: not enough values to unpack expected 3, got 2 I reproduced that against a live streamable-HTTP server on 2.2.0. In the installed package it's mcp/client/streamable http.py , around line 753: yield read stream, write stream . StreamableHTTPTransport. init in 2.x is just self, url . The timeout , sse read timeout , headers and auth keywords aren't accepted there anymore; set them on the httpx2 AsyncClient instead. Pass them and you get TypeError: unexpected keyword argument 'timeout' . Honesty note in the other direction: sse client still takes timeout and sse read timeout . So the "all the transport kwargs got dropped" version of this story is wrong. It's the streamable-HTTP path that changed. This is the quiet one. The Pydantic protocol models renamed their attributes: tool.inputSchema goes to tool.input schema result.isError goes to result.is error structuredContent goes to structured content nextCursor goes to next cursor The JSON on the wire is still camelCase the aliases keep that , so servers and clients still agree on the protocol. It's your Python that breaks, with an AttributeError. Bonus trap: model dump without by alias=True now hands you snake case dicts without complaint, which can poison anything downstream that expects the protocol shape. Two examples I verified by downloading the packages rather than trusting a changelog. autogen-ext 0.7.5 declares mcp =1.11.0 with no upper bound, imports streamablehttp client , unpacks the 3-tuple, and reads inputSchema / isError , so its mcp extra is broken against current mcp , and as of mid-September that's still the case on main. llama-index-tools-mcp 0.5.0 is the same failure with a twist: it declared mcp =2.0.0 but still unpacked the 3-tuple, so it was broken against its own dependency floor. Fixed in 0.5.1 in late August; 0.6.0 is current. If you don't need 2.x features, pin back. The official 2.0.0 release notes tell library authors to keep a <2 upper bound, and the 1.x line is still getting security fixes: pip install "mcp =1.28,<2" resolves to 1.30.0 today uv add "mcp<2" If the wrapper already fixed it llama-index-tools-mcp = 0.5.1, for example , upgrade the wrapper instead of downgrading mcp . Read the release notes before you assume which side is fixed. Before guessing, check what you're actually running: python python -c "import importlib.metadata as m, inspect; print m.version 'mcp' ; import mcp.client.streamable http as sh; print hasattr sh,'streamablehttp client' ; print inspect.signature sh.streamable http client " And check the wrapper's pyproject.toml for an unbounded mcp dependency. That missing upper bound is the whole bug, and it'll happen again at the next major. I saw a couple of claims circulating while digging that can cost you hours if you chase them. An empty list from a tool producing zero content blocks is not a 2.x regression. I measured the same numbers on both 1.29.1 and 2.2.0: gives 0 blocks, '' gives 1, json.dumps gives 1, 'a','b' gives 2. It's long-standing FastMCP behavior in convert to content , which flattens a list item by item, and an empty list flattens to nothing. structured content is still correct. If your tool returns a list, return something wrapper-friendly instead of a bare so the model gets one text block to read. That one matters if an agent keeps re-running your search instead of concluding "no match". Separately, 2.x removed automatic return-value wrapping in the low-level Server , and mcp.types moved out into a mcp-types package. Both are in the migration guide. A dependency major plus an unbounded requirement in the wrapper equals silent breakage at install time, with a stack trace pointing at your code instead of the dependency. Pin it, then skim the migration guide at py.sdk.modelcontextprotocol.io/migration. It's a long read, but the naming section alone saves the afternoon. I run a pile of MCP servers locally for agent work, and pinning is the boring habit that keeps it quiet. If you want to see how I aggregate and hot-swap those servers, that's in my Smart-MCP-Proxy repo, but the pin is the only part you need today. And I could be wrong about your specific stack, so test the pin in a throwaway venv first. That's what I did before writing any of this down.