diff --git a/docs/MCP.md b/docs/MCP.md index dbe3431..deddac8 100644 --- a/docs/MCP.md +++ b/docs/MCP.md @@ -1,6 +1,6 @@ # RightMemory MCP -RightMemory exposes a local MCP stdio server for ordinary agent work: +RightMemory exposes a local MCP stdio server for ordinary agent work using the official MCP Python SDK v2: ```bash rightmemory mcp diff --git a/pyproject.toml b/pyproject.toml index dbc1aea..05976b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,8 +29,8 @@ classifiers = [ ] dependencies = [ "fastapi>=0.115.0", - "mcp>=1.28,<2", - "pydantic-ai>=1.0.0", + "mcp>=2,<3", + "pydantic-ai-slim[anthropic,openai]>=1.0.0", "uvicorn>=0.30.0", ] diff --git a/rightmemory/mcp.py b/rightmemory/mcp.py index 952c403..23be36e 100644 --- a/rightmemory/mcp.py +++ b/rightmemory/mcp.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Annotated, Protocol -from mcp.server.fastmcp import FastMCP +from mcp.server import MCPServer from mcp.types import CallToolResult, TextContent from pydantic import Field @@ -176,11 +176,11 @@ def create_mcp_server( memory_root: Path, *, backend: McpBackend | None = None, -) -> FastMCP: +) -> MCPServer: selected_backend = backend or DefaultMcpBackend( Path(memory_root).expanduser().resolve() ) - server = FastMCP(name="RightMemory", log_level="WARNING") + server = MCPServer("RightMemory", log_level="WARNING") @server.tool( name="rightmemory_retrieve", diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 0144f3d..0026ba7 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -9,6 +9,7 @@ from types import SimpleNamespace from unittest.mock import patch +from mcp import Client from mcp.types import CallToolResult from rightmemory import entrypoint @@ -48,20 +49,30 @@ def actionable_warning(self) -> str | None: return self.warning +async def _call_tool(server, name: str, arguments: dict[str, str]) -> CallToolResult: + async with Client(server, raise_exceptions=True) as client: + return await client.call_tool(name, arguments) + + def call_tool(server, name: str, arguments: dict[str, str]) -> CallToolResult: - result = asyncio.run(server.call_tool(name, arguments)) + result = asyncio.run(_call_tool(server, name, arguments)) if not isinstance(result, CallToolResult): raise AssertionError(f"expected CallToolResult, got {type(result).__name__}") return result +async def _list_tools(server): + async with Client(server, raise_exceptions=True) as client: + return (await client.list_tools()).tools + + class McpToolTests(unittest.TestCase): def setUp(self) -> None: self.backend = FakeBackend() self.server = create_mcp_server(Path("/unused"), backend=self.backend) def test_server_exposes_only_the_three_ordinary_agent_tools(self): - tools = asyncio.run(self.server.list_tools()) + tools = asyncio.run(_list_tools(self.server)) self.assertEqual( {tool.name for tool in tools}, {