Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/MCP.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
6 changes: 3 additions & 3 deletions rightmemory/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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",
Expand Down
15 changes: 13 additions & 2 deletions tests/test_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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},
{
Expand Down