feat(python): support MCP protocol 2026-07-28 via mcp 2.x, dual-major - #643
Conversation
MCPToolProvider now auto-detects the installed mcp major. On 2.x every server gets its own mcp.Client entered as a context manager with mode="auto": the in-band server/discover probe reaches 2026-07-28 servers and falls back to the legacy initialize handshake for older ones (verified live against a real 1.29 FastMCP stdio server). On 1.x the code path is verbatim the old one — the existing test suite, pinned to it, is the regression proof. A _field helper reads both attribute spellings since 2.x types are strictly snake_case; this also fixes the silent isError/structuredContent masking that broke under 2.x. v2 read_resource takes a plain str, list_tools pagination is followed on v2 only, and streamable-http headers ride an owned httpx2 client. The mcp<2 cap from #631 is lifted: both extras accept mcp>=1.0.0 again. Closes #634 Code written by Claude (Fable 5), architected and approved by @cornelcroi.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 196db808c6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| tools_result = await session.list_tools() | ||
| for mcp_tool in tools_result.tools: | ||
| tools = await self._list_all_tools(session) |
There was a problem hiding this comment.
Roll back v2 clients when tool discovery fails
When using mcp 2.x, _connect_v2 has already entered and stored the client before this discovery call. If list_tools() raises—for example because the server disconnects or returns an invalid response—create() propagates the exception without returning the provider, so the caller cannot invoke disconnect() and the entered client, transport, and any stdio subprocess remain live. Roll back all sessions and owned HTTP clients opened during connection when initialization or discovery fails.
Useful? React with 👍 / 👎.
Issue Link
Closes #634
Summary
Python counterpart of #633.
MCPToolProviderauto-detects the installedmcpmajor: on 2.x every server gets its ownmcp.Clientwithmode="auto"(in-bandserver/discoverprobe,initializefallback — no configuration needed); on 1.x the code path is verbatim the old one. This also lifts the<2cap from #631, soagent-squad[mcp]accepts both majors again.Changes
mcp.Clientexists only in 2.x (detection),streamable_http_client+ httpx client factory for the renamed v2 transport._ensure_connectedsplit into_connect_v1(old code verbatim) /_connect_v2(Client as context manager, stored where sessions live); shared_transport_cm._field()helper reads both attribute spellings — mcp 2.x types are strictly snake_case with no alias access (tool.inputSchemaraisesAttributeErrorthere). Replaces the five camelCase read sites, which also fixes the silentisError/structuredContentmasking that 2.x would have caused.read_resourcegets a plainstr(2.x rejectsAnyUrl); v2list_toolsfollowsnext_cursorpagination (v1 keeps its exact single-call behavior); v2 streamable-http headers ride an owned httpx2 client whose lifecycle we manage (the SDK doesn't close caller-provided clients).setup.cfg: both extras back tomcp>=1.0.0._V2Client=Nonepatch — zero other edits, that's the regression proof. 8 new v2 tests (connection, headers lifecycle, pagination, snake_case results/schema, strread_resource, disconnect lifecycle,_fieldunit). 37/37 passing under both real mcp 1.29.0 and 2.0.0 installs, ruff clean.User experience
Same
MCPToolProvider.create([...])code; protocol era negotiated per server automatically. Users who pinnedmcp<2themselves stay on the unchanged v1 path.python-expert review: two REQUIRED items (comment accuracy, v2 disconnect test) — both applied; approved to proceed.
Release note: this should ship as a minor version (fresh installs change SDK major), together with #631's context.
Checklist
Code written by Claude (Fable 5), architected and approved by @cornelcroi.