MCP server dies at startup after the mcp package resolves to 2.0.0
Symptom
Two stdio MCP servers that had worked for weeks stopped appearing in the client. No tools, no error dialog — the server process simply exits during startup, and the client shows it as failed to connect. Running it by hand shows why:
AttributeError: 'Server' object has no attribute 'list_tools'
Nothing in the project changed. That is the tell.
Cause: an unpinned SDK and a fresh resolution
The server’s dependency spec was permissive:
mcp>=0.9.0
uv re-resolves on each run when there is no lock file in play, and the newest release satisfying that
range became 2.0.0, which removed the decorator-style Server.list_tools() API the server was written
against. The code is fine; it is simply talking to a different major version than the one it was built for.
Fix: pin the SDK
mcp==1.19.0
I verified both servers work with 1.19.0. When launching without a project environment, pin it on the command line too, so a stray resolution cannot bite again:
uv run --no-project --with 'mcp==1.19.0' python -m my_mcp.server --url= --token=
If your server lives outside the current project tree, be explicit about where the code is instead of relying on the working directory:
PYTHONPATH=/path/to/server/src uv run --no-project --with 'mcp==1.19.0' \
python -m my_mcp.server
After changing the client configuration (for example mcpServers in your CLI’s config), restart the
client — a running client keeps the old process definition.
Two adjacent traps from the same debugging session
Non-interactive runs need the tools allow-listed. With Claude Code, claude -p in non-interactive mode
does not expose MCP tools unless you name them:
claude -p "…" --allowedTools "mcp__my-server__tool_one,mcp__my-server__tool_two"
Without it the model simply behaves as if the server were not there, which looks exactly like a broken server.
Serialisation errors are not version errors. In the same setup two tool calls kept failing while create/update/list worked:
- one crashed on a
fixedRatefield the server did not expect to be null, - another failed pydantic validation with
int_typebecause the upstream API returned objects where the model declared integers (project, activity and user came back expanded).
Both are bugs in the server’s models against the live API, not SDK incompatibilities. The workaround was
to use the endpoints that do work (a filtered list instead of the broken get) and treat the rest as
known-broken until the models are fixed.
The generalisable rule
For anything launched by an editor or agent runtime — MCP servers, language servers, hooks — pin the protocol SDK exactly. These processes are started for you, their stderr is usually swallowed, and a silent major-version bump presents as “the integration disappeared” rather than as a stack trace. A pin costs nothing; the debugging session costs an evening.