feat(mcp): migrate to the mcp 2.x SDK
mcp 2.0.0 implements MCP revision 2026-07-28 and makes three breaking changes Hermes sits on top of: `mcp.server.fastmcp` is gone, every model field is renamed to snake_case (camelCase survives only as a serialization alias, which pydantic does not expose to attribute access), and the SDK's own HTTP stack moved from `httpx` to `httpx2`. Bump the pin across the dev/mcp/computer-use extras and port the tree: - `mcp_serve.py` and `agent/transports/hermes_tools_mcp_server.py` move from `FastMCP` to `mcp.server.MCPServer`, which has the same decorator/add_tool surface. The hermes-tools server already synthesised `__signature__` from Hermes' JSON Schema, which is exactly what 2.0's `add_tool` reads. - SDK model reads go through `mcp_field(obj, snake, camel)`, which reads both spellings. A single-spelling read fails *silently* on the other generation — empty tool schemas, dropped structured content, tool results vanishing from sampling conversations — and `mcp` is an optional extra users install at their own version. - `sdk_httpx()` resolves the httpx flavour from the SDK's own transport module, so objects handed to `streamable_http_client`, the `sse_client` factory, and the OAuth metadata helpers come from the module the installed SDK actually imports. - HTTP support is gated on either streamable-HTTP entry point, not just the deprecated alias 2.0 removed. - OAuth: `OAuthClientProvider` lost its `timeout` argument (the configured `oauth.timeout` now bounds the callback waiter's own poll loop, where the browser round-trip was always awaited), and `callback_handler` must return `AuthorizationCodeResult` rather than a tuple. 2.0 also validates the RFC 9207 `iss` parameter, so the callback handler and paste fallback capture it. `mcp`/`mcp-types` 2.0.0 are inside the 14-day `exclude-newer` window, so two narrow `exclude-newer-package` entries unblock `uv lock`, annotated for removal on or after 2026-08-11. `httpx2` needs no exemption: 2.7.0 is already outside the window and satisfies mcp's floor. Refs #69931 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+7
-4
@@ -47,13 +47,16 @@ logger = logging.getLogger("hermes.mcp_serve")
|
||||
# Lazy MCP SDK import
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# mcp 2.0 removed `mcp.server.fastmcp`; its decorator-driven server is now
|
||||
# `mcp.server.MCPServer` with the same `@server.tool()` / `run_stdio_async()`
|
||||
# surface (docstring -> tool description, signature -> input schema).
|
||||
_MCP_SERVER_AVAILABLE = False
|
||||
try:
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from mcp.server import MCPServer
|
||||
|
||||
_MCP_SERVER_AVAILABLE = True
|
||||
except ImportError:
|
||||
FastMCP = None # type: ignore[assignment,misc]
|
||||
MCPServer = None # type: ignore[assignment,misc]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -617,7 +620,7 @@ class EventBridge:
|
||||
# MCP Server
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def create_mcp_server(event_bridge: Optional[EventBridge] = None) -> "FastMCP":
|
||||
def create_mcp_server(event_bridge: Optional[EventBridge] = None) -> "MCPServer":
|
||||
"""Create and return the Hermes MCP server with all tools registered."""
|
||||
if not _MCP_SERVER_AVAILABLE:
|
||||
raise ImportError(
|
||||
@@ -625,7 +628,7 @@ def create_mcp_server(event_bridge: Optional[EventBridge] = None) -> "FastMCP":
|
||||
f"Install with: {sys.executable} -m pip install 'mcp'"
|
||||
)
|
||||
|
||||
mcp = FastMCP(
|
||||
mcp = MCPServer(
|
||||
"hermes",
|
||||
instructions=(
|
||||
"Hermes Agent messaging bridge. Use these tools to interact with "
|
||||
|
||||
Reference in New Issue
Block a user