Skip to content

feat(integrations): add MCP server exposing MemU as Claude Code tools#422

Open
Meur3ault wants to merge 1 commit into
NevaMind-AI:mainfrom
Meur3ault:feat/mcp-server-integration
Open

feat(integrations): add MCP server exposing MemU as Claude Code tools#422
Meur3ault wants to merge 1 commit into
NevaMind-AI:mainfrom
Meur3ault:feat/mcp-server-integration

Conversation

@Meur3ault
Copy link
Copy Markdown

📝 Pull Request Summary

Resolves #387. Registers MemU as a Model Context Protocol server so the existing memory primitives reach LLM agents through the standard MCP transport rather than via library-specific bindings or the private claude-agent-sdk wrapper currently shown in examples/proactive/.


✅ What does this PR do?

Adds two stdio MCP servers that share one async tool layer (_MemUTools) and expose the same five tools:

Tool Maps to
memu_memorize(content, user_id, modality="conversation") MemoryService.memorize
memu_retrieve(query, user_id, limit=5) MemoryService.retrieve
memu_list_items(user_id) MemoryService.list_memory_items
memu_list_categories(user_id) MemoryService.list_memory_categories
memu_clear_memory(user_id) MemoryService.clear_memory
  • src/memu/integrations/mcp_server.py — FastMCP entry point. Tools registered with @mcp.tool decorators. pip install memu-py[mcp], then python -m memu.integrations.mcp_server.
  • src/memu/integrations/mcp_server_lowlevel.py — same five tools through mcp.server.lowlevel.Server for users who prefer the official SDK directly. pip install memu-py[mcp-lowlevel], then python -m memu.integrations.mcp_server_lowlevel.

memu_memorize writes the content to a temp file before calling MemoryService.memorize (which requires a resource_url), mirroring the pattern in src/memu/integrations/langgraph.py.

The CLI entry reads MEMU_API_KEY (falling back to OPENAI_API_KEY) and optional MEMU_BASE_URL / MEMU_CHAT_MODEL / MEMU_EMBED_MODEL, so it works against any OpenAI-compatible provider (OpenAI, DeepSeek, Qwen DashScope, OpenRouter, Together, local Ollama, …). Users who need separate chat and embedding endpoints can build a MemoryService programmatically and pass it to build_server; the example file shows this.

Register with Claude Code:

pip install "memu-py[mcp]"
claude mcp add memu -e OPENAI_API_KEY=$OPENAI_API_KEY -- python -m memu.integrations.mcp_server

🤔 Why is this change needed?

Issue #387 asks how to use MemU as a Claude Code plugin. Today examples/proactive/memory/local/tools.py uses claude_agent_sdk.create_sdk_mcp_server, which is Anthropic's proprietary wrapper rather than the standard MCP transport every MCP client (Claude Code, Cursor, Continue, Zed, …) speaks. That example also only exposes two tools and is not part of the src/memu/integrations public surface.

This PR adds a first-class MCP integration in src/memu/integrations/ that:

  1. Speaks standard MCP over stdio (works with any MCP client, not only Claude Code)
  2. Exposes the full read/write surface of MemoryService, not just retrieve + todos
  3. Ships through optional extras and a module-level __main__ so users register a single command

🔍 Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactor / cleanup
  • Other

✅ PR Quality Checklist

  • PR title follows conventional format (feat(integrations):)
  • Changes are limited in scope (one feature, single commit, ~280 lines source + ~330 lines tests)
  • Documentation updated where applicable (config description in pyproject.toml, docstrings on build_server / _service_from_env, full registration example in examples/mcp_server_example.py)
  • No breaking changes — MemULangGraphTools and the existing examples/proactive server are untouched; both new modules are optional via separate extras
  • Related issue linked ([FEATURE] How do I use it as a Claude code plugin? #387)

📌 Optional

  • Edge cases considered:
    • OPENAI_API_KEY unset → server fails with a clear RuntimeError rather than a downstream provider error (covered by test_raises_when_no_key_set)
    • Missing fastmcp / mcp package → build_server raises an ImportError pointing at the correct extra to install
    • LLM/network failure inside memorize / retrieve → tool returns a "Failed to ..." string so the MCP client still gets a response instead of the request hanging
  • Follow-up tasks left for separate PRs:
    • First-class split chat/embedding profile support in the CLI entry (currently requires a small launcher script)
    • Updating examples/proactive/ to point at this integration once it lands

Tests

14 tests in tests/integrations/test_mcp.py, all hermetic (no API key needed):

  • 8 unit tests covering _MemUTools against AsyncMock(spec=MemoryService)
  • 4 env-var tests for _service_from_env (preference order, fallback, overrides, missing-key error)
  • 2 build tests for build_server on each SDK
  • 2 end-to-end protocol roundtrip tests that connect a real MCP client to each server through the in-memory transport and assert list_tools + call_tool memu_retrieve returns the expected TextContent
$ make test
======================== 93 passed, 1 skipped in 3.26s =========================

$ make check
🚀 Linting code: Running pre-commit  ............ Passed
🚀 Static type checking: Running mypy ............ Success: no issues found in 133 source files
🚀 Checking for obsolete dependencies: Running deptry  ........ Success

Additionally validated locally end-to-end through Claude Code: claude mcp add memu-local -- uv run … python -m memu.integrations.mcp_server, then in a fresh chat session called memu_memorize + memu_list_items against gpt-4o-mini + text-embedding-3-small. Both items came back as expected with the LLM-extracted profile/event memory types.

1 2 3 4 5

Implements issue NevaMind-AI#387: register MemU as a Model Context Protocol server so
the same memory primitives reach LLM agents through the standard MCP
transport rather than via library-specific bindings.

Two adapters share a common async tool layer (_MemUTools):

  - mcp_server.py runs through FastMCP (`memu-py[mcp]` extra). Five tools
    are registered with the `@mcp.tool` decorator; stdio transport is used
    when invoked as a module.

  - mcp_server_lowlevel.py binds the same tool implementations to a
    low-level mcp.server.lowlevel.Server (`memu-py[mcp-lowlevel]` extra)
    with explicit JSON Schemas, for users who prefer the official SDK.

Both expose memu_memorize / memu_retrieve / memu_list_items /
memu_list_categories / memu_clear_memory, all scoped by user_id.
memu_memorize writes content to a temp file before delegating to
MemoryService.memorize (which requires a resource_url), mirroring the
pattern already used by the LangGraph integration.

Register the server in Claude Code:

    pip install "memu-py[mcp]"
    claude mcp add memu -- python -m memu.integrations.mcp_server
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.

[FEATURE] How do I use it as a Claude code plugin?

1 participant