From ce3e9acc725220bf13f725819d5ef8ff166723b0 Mon Sep 17 00:00:00 2001 From: Christian Wallen Date: Sun, 9 Aug 2026 18:17:10 +0200 Subject: [PATCH] fix(mcp): adapt to mcp 2.0.0 SDK API changes The mcp>=1.0.0 dependency resolves to 2.0.0, a breaking release that renamed FastMCP->MCPServer (mcp.server.fastmcp->mcp.server.mcpserver), moved transport_security from the constructor to streamable_http_app(), renamed several Tool/CallToolResult fields to snake_case (inputSchema->input_schema, structuredContent->structured_content), and dropped the unused session-id callback from streamable_http_client's return tuple. Co-Authored-By: Claude Sonnet 5 --- src/openharness/mcp/client.py | 8 ++++---- tests/fixtures/fake_mcp_server.py | 4 ++-- tests/test_mcp/test_client_errors.py | 2 +- tests/test_mcp/test_http_flow.py | 11 +++++------ 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/openharness/mcp/client.py b/src/openharness/mcp/client.py index 8d1f1649a..4a8586c50 100644 --- a/src/openharness/mcp/client.py +++ b/src/openharness/mcp/client.py @@ -147,8 +147,8 @@ async def call_tool(self, server_name: str, tool_name: str, arguments: dict[str, parts.append(getattr(item, "text", "")) else: parts.append(item.model_dump_json()) - if result.structuredContent and not parts: - parts.append(str(result.structuredContent)) + if result.structured_content and not parts: + parts.append(str(result.structured_content)) if not parts: parts.append("(no output)") return "\n".join(parts).strip() @@ -221,7 +221,7 @@ async def _connect_http(self, name: str, config: McpHttpServerConfig) -> None: http_client = await stack.enter_async_context( httpx.AsyncClient(headers=config.headers or None) ) - read_stream, write_stream, _get_session_id = await stack.enter_async_context( + read_stream, write_stream = await stack.enter_async_context( streamable_http_client(config.url, http_client=http_client) ) await self._register_connected_session( @@ -273,7 +273,7 @@ async def _register_connected_session( server_name=name, name=tool.name, description=tool.description or "", - input_schema=dict(tool.inputSchema or {"type": "object", "properties": {}}), + input_schema=dict(tool.input_schema or {"type": "object", "properties": {}}), ) for tool in tool_result.tools ] diff --git a/tests/fixtures/fake_mcp_server.py b/tests/fixtures/fake_mcp_server.py index 5f191f54c..0dcc64f04 100644 --- a/tests/fixtures/fake_mcp_server.py +++ b/tests/fixtures/fake_mcp_server.py @@ -2,9 +2,9 @@ from __future__ import annotations -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer -server = FastMCP("fixture-demo") +server = MCPServer("fixture-demo") @server.tool() diff --git a/tests/test_mcp/test_client_errors.py b/tests/test_mcp/test_client_errors.py index 4f5fce767..7ca074b2b 100644 --- a/tests/test_mcp/test_client_errors.py +++ b/tests/test_mcp/test_client_errors.py @@ -181,7 +181,7 @@ async def test_connect_all_marks_http_server_failed_when_initialize_is_cancelled monkeypatch.setattr( client_module, "streamable_http_client", - lambda *args, **kwargs: _AsyncContextManager((object(), object(), AsyncMock())), + lambda *args, **kwargs: _AsyncContextManager((object(), object())), ) manager._register_connected_session = AsyncMock( side_effect=asyncio.CancelledError("simulated cancellation") diff --git a/tests/test_mcp/test_http_flow.py b/tests/test_mcp/test_http_flow.py index 33eb088b1..ac9e735a7 100644 --- a/tests/test_mcp/test_http_flow.py +++ b/tests/test_mcp/test_http_flow.py @@ -6,7 +6,7 @@ import httpx import pytest -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer from mcp.server.transport_security import TransportSecuritySettings import openharness.mcp.client as client_module @@ -18,10 +18,7 @@ @pytest.mark.asyncio async def test_http_mcp_manager_connects_and_executes_in_process_server(monkeypatch): - server = FastMCP( - "demo-http", - transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False), - ) + server = MCPServer("demo-http") @server.tool() def hello(name: str) -> str: @@ -31,7 +28,9 @@ def hello(name: str) -> str: def readme() -> str: return "http fixture resource contents" - app = server.streamable_http_app() + app = server.streamable_http_app( + transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False) + ) transport = httpx.ASGITransport(app=app) original_async_client = client_module.httpx.AsyncClient seen_headers: list[dict[str, str] | None] = []