Instructions for AI coding agents working on this repo. Keep changes surgical: touch only what the task requires, match existing style, preserve behavior in refactors.
MARM is a local-first MCP memory server: Python FastAPI in marm-mcp-server/, package marm_mcp_server/.
- 14 public MCP tools: 7 core memory, 5 code graph, 2 concept graph. HTTP and STDIO must stay in exact parity.
- HTTP transport:
marm_mcp_server/server.py; tools are whitelisted inMCP_TOOL_OPERATIONS. A tool not in that list does not exist over HTTP. - STDIO transport:
marm_mcp_server/server_stdio.pyowns theFastMCPapp and seven core@mcp.tool()wrappers. Graph/concept bodies live inservices/stdio_graph_tools.pyand are explicitly registered after the core tools sotools/listorder stays stable. Never fork behavior between transports. - Endpoint logic lives in
marm_mcp_server/endpoints/split by surface (memory, logging, notebook, session, compaction, graph, concepts, system). Shared helpers stay incore/. - Storage: SQLite WAL at
~/.marm/marm_memory.db(connection pool, FTS5 external-content indexmemories_fts,memory_chunksfor long-memory chunking). The concept graph uses its own database~/.marm/index/marm_index.dbwith its own pool. Never share connections between the two. - Write path: all memory writes go through the serialized async write queue (one worker). Do not add write paths that bypass it.
marm_log_entrydual-writes: alog_entriesrow plus a semantic memory inmemories(via the queue); a semantic-store failure must never fail the log write. - Code graph: a pinned external binary (codebase-memory-mcp) supervised as a child process over newline-delimited JSON-RPC (
core/graph_supervisor.py,core/graph_client.py). It starts lazily and runs degraded on failure. Graph or concept failures must never break the 7 core memory tools. - Embeddings: one fastembed
jinaai/jina-embeddings-v2-small-enencoder (512 dimensions), lazy-loaded and serialized behind a lock. Writes must succeed even when the encoder is unavailable. Existing data requiresmarm-mcp-server --migrate-embeddingsbefore restart when upgrading from MiniLM.
When adding or removing an MCP tool, update ALL of the following:
marm_mcp_server/endpoints/<surface>.py- the implementationmarm_mcp_server/server.py- route +MCP_TOOL_OPERATIONSwhitelistmarm_mcp_server/server_stdio.py- STDIO bootstrap/registration and matching wrapper or service pathmarm-mcp-server/server.json- tools arrayscripts/find-tools.py-CANONICAL_TOOLSlist- Docs with full tool lists:
README.md,docs/PROTOCOL.md,docs/PROTOCOL-LITE.md, and theirmarm-mcp-server/marm-docs/copies, plus tool counts in FAQ - Tests covering both transports
Then run python scripts/find-tools.py; every surface must report OK.
README mirrors are generated, never hand-edited:
- Root
README.mdis the single source of truth. marm-mcp-server/README.mdis the PyPI variant (adds themcp-name:header and two image divs).marm-mcp-server/marm-docs/README.mdis the text-only agent-facing subset (badges, demo, and footer sections stripped).
When bumping the version, update ALL of the following (audit with python scripts/find-versions.py):
marm-mcp-server/pyproject.tomlmarm-mcp-server/server.json(three occurrences, including the Docker identifier)marm-mcp-server/marm_mcp_server/__init__.py(__version__and docstring)marm-mcp-server/marm_mcp_server/config/settings.py(SERVER_VERSION)marm-mcp-server/marm_mcp_server/server.pydocstringmarm-mcp-server/Dockerfileversion label anddocker-compose.yml- Root
README.mdh1 (then regenerate mirrors) and the version headers indocs/INSTALL-*.md
Semver: MAJOR = breaking (schema renames, parameter removals), MINOR = new tools/parameters/features, PATCH = fixes and doc updates.
- New tool flow: implement in
endpoints/, register the HTTP route, whitelist it, add the STDIO wrapper, then walk the consistency checklist above. - Prefer the smallest change that solves the problem. No speculative abstractions, no config flags nobody asked for.
- Comments are minimal and only explain non-obvious "why". Never add comments that narrate what the next line does.
- Keep orchestration in the current owner file; extract modules only at real boundaries (see existing
endpoints/split for the pattern).
- Tests live in
marm-mcp-server/tests/; run withpytestfrommarm-mcp-server/. - Hit real FastAPI endpoints and real SQLite. Mock only when it meaningfully speeds the test AND matches real behavior with at least 95% fidelity.
- Every new MARM Console API route needs at least one happy-path FastAPI response-contract test with the MCP adapter stubbed. This verifies the actual response model without requiring a live graph backend.
- No existence-check or coded-to-pass tests. Deep tests that exercise real paths beat broad shallow coverage.
pytest.mark.skiponly for genuinely unavailable dependencies (no embedding model, no spaCy[concepts]extra), never for effort.
- Never commit without an explicit user request. The user reviews all changes first.
- Dev setup:
cd marm-mcp-server && pip install -e ".[dev,concepts]" && python -m spacy download en_core_web_sm - Benchmarks live in
scripts/benchmarking/:preformance/bench_hotpath.pyfor hot-path performance,accuracy/locomo/run_eval.pyfor LoCoMo retrieval accuracy. Do not publish performance claims neither script can back.
- 14 MCP tools over HTTP + STDIO
- 2 isolated SQLite databases (memory + concept graph)
- Hybrid recall: FTS5 BM25 exact lane + bounded semantic rerank
- Optional extras:
[concepts](spaCy extraction), Docker image with the graph engine baked in