Skip to content

feat: migrate MCP client and AgentOS server to mcp 2.x - #9269

Merged
sannya-singal merged 16 commits into
agno-agi:mainfrom
SiAgentBuild:fix/mcp-upper-bound
Sep 2, 2026
Merged

sannya-singal merged 16 commits into
agno-agi:mainfrom
SiAgentBuild:fix/mcp-upper-bound

Conversation

@SiAgentBuild

@SiAgentBuild SiAgentBuild commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

mcp 2.0.0 (released 2026-07-28) shipped breaking changes across the client and server surface: it renamed McpError to MCPError, removed streamablehttp_client, changed ClientSession timeouts to float seconds, renamed mcp.types fields to snake_case, and dropped the timeout/sse_read_timeout/auth kwargs from streamable_http_client. Because the mcp extra declared an unbounded mcp>=1.9.2, a fresh pip install agno[mcp] resolved to the incompatible 2.0.0 and crashed on import (see #9267).

The blocker that made a quick "cap it" the only option has since cleared: fastmcp 4.0.0b1 now supports mcp>=2.0.0,<3.0.0 (PrefectHQ/fastmcp #4655). So this PR does the full migration rather than a temporary pin.

Changes:

  • Dependency floors (pyproject.toml): mcp = ["mcp>=2.0.0,<3.0.0", "fastmcp>=4.0.0b1,<5"] (pre-release fastmcp until stable 4.x ships). Added httpx[http2]>=0.28.1 floor — pre-release resolvers otherwise pull httpx 1.0 dev builds that drop HTTPStatusError and crash fastmcp.server.server on import.
  • MCP client (utils/mcp.py, tools/mcp/mcp.py, tools/mcp/multi_mcp.py, tools/mcp/params.py): McpError→MCPError; streamablehttp_client→streamable_http_client (now yields a 2-tuple); read_timeout_seconds as float; snake_case attribute access (is_error, structured_content, mime_type, input_schema); model_dump_json(by_alias=True) for wire output; StreamableHTTPClientParams timeouts as float seconds. A shared helper _streamable_http_connection() maps the removed timeout/sse_read_timeout kwargs onto an httpx2.Timeout via the SDK's own create_mcp_http_client (the same pattern fastmcp 4 uses).
  • AgentOS auth (os/mcp_auth_builtin.py): adapted to the v2 OAuthClientInformationFull — client_id is now a required str (removed dead None guards), token_endpoint_auth_method/redirect_uris are Optional (handled), model_dump_json(by_alias=True) for stored client metadata.
  • Web MCP wrappers (context/web/exa_mcp.py, context/web/parallel_mcp.py): StreamableHTTPClientParams(timeout=...) as float seconds.
  • Tests: updated MCP and AgentOS MCP tests to the v2 surface (patch targets, MCPError, snake_case CallToolResult, 2-tuple transports).

Fixes #9267

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Improvement
  • Model update
  • Other:

Checklist

  • Code complies with style guidelines
  • Ran format/validation scripts (./scripts/format.sh and ./scripts/validate.sh)
  • Self-review completed
  • Documentation updated (comments, docstrings)
  • Examples and guides: Relevant cookbook examples have been included or updated (if applicable)
  • Tested in clean environment
  • Tests added/updated (if applicable)

Duplicate and AI-Generated PR Check

  • I have searched existing open pull requests and confirmed that no other PR already addresses this issue
  • If a similar PR exists, I have explained below why this PR is a better approach
  • Check if this PR was entirely AI-generated (by Copilot, Claude Code, Cursor, etc.)

Additional Notes

Verified in a clean environment against mcp 2.0.0 + fastmcp 4.0.0b1 + mcp-types 2.0.0:

  • mypy: all 950 libs/agno source files clean under the mcp 2.0 environment.
  • ruff: format and check clean (1998 files).
  • Client tests tests/unit/tools/test_mcp.py: 76 passed.
  • AgentOS MCP tests (test_mcp_auth_builtin, test_mcp_oauth, test_mcp_run_results, test_mcp_server, test_mcp_resolution, test_mcp_surface_v2, test_mcp_auth_tier2, test_registry_mcp_tools): 230 passed.
  • End-to-end: MCPTools over stdio against a fastmcp 4.0.0b1 / mcp 2.0.0 server completes connect → list_tools → call_tool successfully.

Two things worth a maintainer's eye:

  1. fastmcp is a pre-release pin (>=4.0.0b1,<5). The intent is to move to the stable fastmcp 4.x as soon as it ships; happy to gate that here or track it separately.
  2. fastmcp 4 API move: FastMCP/Context are no longer top-level exports; AgentOS already imports them from fastmcp.server.server / fastmcp.server.context, which is compatible.

"No one pins the same dependency twice, for it is not the same dependency and they are not the same code. The protocol flows on; we flow with it."

— after Heraclitus, "No man ever steps in the same river twice"


Status: do not merge yet. This pins fastmcp to the 4.0.0b1 pre-release. I'll update the pin to the stable fastmcp 4.x once it ships and re-verify — the PR should only be merged after that.

@SiAgentBuild
SiAgentBuild requested a review from a team as a code owner July 30, 2026 09:24
@github-actions

Copy link
Copy Markdown
Contributor

PR Triage

Missing tests: This PR modifies source code but does not include any test changes. Please add or update tests to cover your changes.

@SiAgentBuild SiAgentBuild changed the title fix: cap mcp<2.0.0 and report incompatible mcp versions accurately feat: migrate MCP client and AgentOS server to mcp 2.x Jul 30, 2026
@sannya-singal

Copy link
Copy Markdown
Contributor

hey @ProgrammerPlus1998 can you resolve the conflicts?

@SiAgentBuild
SiAgentBuild force-pushed the fix/mcp-upper-bound branch from 67027f2 to c335ede Compare July 31, 2026 01:29
@sannya-singal sannya-singal self-assigned this Jul 31, 2026
mcp 2.0.0 renamed McpError to MCPError, removed streamablehttp_client,
and changed ClientSession timeouts and mcp.types field names. The
unbounded `mcp>=1.9.2` constraint lets fresh installs resolve to the
incompatible 2.0.0, and the surrounding try/except then re-raised a
misleading "`mcp` not installed" error.

- Constrain the mcp extra to `mcp>=1.9.2,<2.0.0`
- Distinguish "mcp not installed" (ModuleNotFoundError) from an
  incompatible-but-installed mcp (ImportError), and report the real
  cause plus the supported range in the latter case

Refs agno-agi#9267
mcp 2.0.0 renamed McpError to MCPError, removed streamablehttp_client,
changed ClientSession timeouts to float seconds, renamed mcp.types
fields to snake_case, and dropped timeout/sse_read_timeout kwargs from
streamable_http_client. This migrates agno's MCP client (MCPTools,
MultiMCPTools, utils) and the AgentOS MCP server/auth onto mcp 2.x.

- Raise the mcp extra to mcp>=2.0.0,<3.0.0 and fastmcp>=4.0.0b1,<5
  (pre-release, until stable 4.x ships).
- Map streamable-http timeouts onto an httpx2.Timeout via
  create_mcp_http_client, following the SDK's own v2 pattern.
- Adapt AgentOS auth to the v2 OAuthClientInformationFull (required
  client_id, Optional token_endpoint_auth_method/redirect_uris).
- Floor httpx[http2]>=0.28.1: pre-release resolvers otherwise pick
  httpx 1.0 dev builds that drop HTTPStatusError and break fastmcp 4.
- Update MCP and AgentOS tests to the v2 API surface.

Fixes agno-agi#9267
httpx2.Timeout(read=None) means no read limit at all. A raw
StreamableHTTPClientParams-style dict that set `timeout` without
`sse_read_timeout` (or vice versa) therefore produced a Timeout with an
unbounded read side, where the mcp 1.x client would have applied the
SDK's own default (300s stream read / 30s operations). Default the
missing knob instead of passing None through.

Also cover _streamable_http_connection directly: both timeouts set,
each knob missing, legacy timedelta values, and the no-custom-client
path were previously only exercised through mocks.
Nothing in agno calls a 2.1-only API, but 2.1.0 carries client-side
interop fixes MCPTools benefits from against newer servers: cache-hint
fields from later revisions are ignored instead of failing list_tools()
(agno-agi#3223), boolean sub-schemas are accepted in tool schema properties
(agno-agi#3353), and recursive return types get an object-rooted outputSchema
pre-2026 clients accept (agno-agi#3337). The full unit suite is validated
against 2.1.0.
@SiAgentBuild

SiAgentBuild commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Rebased onto the post-v3.0.0 main and reworked the PR for the final release. Summary of what changed in this update:

Rebase adjustments

Fix found while rebasing

  • In _streamable_http_connection (the v2 streamable-http shim), a params dict that set timeout without sse_read_timeout (or vice versa) produced an httpx2.Timeout(read=None) — i.e. an unbounded read timeout, where the mcp 1.x client applied the SDK default (300s). The missing knob now falls back to the SDK defaults (30s ops / 300s read) instead of passing None through.

Tests

  • Added direct unit tests for _streamable_http_connection (both timeouts set, each knob missing, legacy timedelta values, no-custom-client path) plus a terminate_on_close passthrough test. These paths were previously only exercised through mocks.

Dependency floor

Validation

  • All 9 MCP-related unit test files pass against mcp==2.1.0 + fastmcp==4.0.0b3: 307 passed.
  • ruff check / ruff format clean; mypy reports zero errors on the touched files.

@SiAgentBuild

SiAgentBuild commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Status update — this is now rebased onto v3.0.0 and raises the mcp floor to 2.1.0.

One thing to flag for review: it currently pulls fastmcp>=4.0.0b1 (pre-release) for mcp 2.x support — happy to pin to the stable fastmcp 4.x release once it ships.

Given v3 deleted MultiMCPTools and reworked the AgentOS MCP surface, landing this sooner avoids more code accumulating on the mcp 1.x client. Ready for review, thanks!

@SiAgentBuild

Copy link
Copy Markdown
Contributor Author

Hi @kausmeows @ysolanky — good news: FastMCP 4.0.0 stable shipped today, which was the last blocker on this PR.

I've updated the pin from fastmcp>=4.0.0b1 to fastmcp>=4.0.0,<5 and verified the full MCP unit suite (184 tests) passes against mcp 2.1.1 + fastmcp 4.0.0 stable — no code changes were needed between the beta and stable releases.

This is ready to merge whenever you have a moment. Thanks!

@kausmeows

Copy link
Copy Markdown
Contributor

thanks @ProgrammerPlus1998 we'll take a look on this. cc @sannya-singal

The merge from main brought back mcp 1.x call sites in
context/web/parallel_mcp.py and streamable-http header tests written
against the v1 streamablehttp_client signature. Update them to the v2
convention: headers/timeouts ride on an httpx2 client built by
create_mcp_http_client, streamable_http_client yields a 2-tuple,
ClientSession read timeout is float seconds, and CallToolResult uses
is_error.
ToolAnnotations in mcp 2.x renames its Python attributes to snake_case
while keeping the protocol's camelCase wire names as aliases, so the
allowlist guard now compares against aliases (the names the protocol
actually defines; agno's public annotation keys are unchanged).

FastMCP 4 derives a display title from the tool name when a tool
publishes none, so the duck-typing guard asserts the look-alike
attribute is ignored rather than asserting title stays unset.
@SiAgentBuild

Copy link
Copy Markdown
Contributor Author

Update: after the latest merge from main, CI flagged a few call sites that came in from main still using the mcp 1.x API. Fixed in the last two commits:

  • context/web/parallel_mcp.py ported to the v2 transport (streamable_http_client + create_mcp_http_client, 2-tuple streams, float read timeout, is_error)
  • streamable-http header tests and the tool-metadata guards aligned with mcp 2.x / FastMCP 4 (snake_case model fields with camelCase wire aliases; FastMCP 4's name-derived display titles)

CI is now fully green on the merge with main (all style checks + all 5 test shards). Ready for review.

@sannya-singal
sannya-singal merged commit 1216ee5 into agno-agi:main Sep 2, 2026
11 checks passed
@SiAgentBuild
SiAgentBuild deleted the fix/mcp-upper-bound branch September 7, 2026 06:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] MCPTools ImportError on mcp 2.0.0: McpError renamed to MCPError (dependency has no upper bound)

3 participants