LM

lyellr88/marm-memory

Developer tools
427 stars 95 forks Quality 93 Trend 41Apache-2.0

marm-memory v2.60.1 - Give your AI Agents a permanent memory in 60 seconds

Overview

marm-memory v2.60.1 - Give your AI Agents a permanent memory in 60 seconds

README

IMPORTANT

Project Status: Brief Personal Pause

I took a short personal break from active development to focus on matters outside the project. This project is still active, and I plan to return soon to keep building and refining it.

Thank you for your patience, support, and continued interest. I’ll resume work soon and share updates as progress continues.

Table of Contents

Quick Start

  1. Install and initialize with your preferred agent profiles:
pip install marm-mcp-server
marm-memory init --g-claude --g-codex --g-antigravity

Also available: --g-cursor, --g-grok, --g-hermes, --g-opencode, --g-devin, --g-cline, --g-qwen, --g-kiro and --g-zed. Run without flags to install into your current project folder instead of home

  1. Hand off to your AI companion. Tell your agent:

“Use the marm-init skill to set up MARM.”

  1. Interact: Your agent will handle the entire setup (Python/Docker, HTTP/STDIO, keys, and client configs) interactively right inside your chat.

Manual setup

Prefer to wire it up yourself:

Replace “agent” with your client’s CLI command (for example, claude, agy, or qwen). For Codex, use codex mcp add marm-memory --url http://localhost:8001/mcp instead.

If you are… Start the server Connect your MCP client
Solo developer / researcher marm-memory start "agent" mcp add --transport http marm-memory http://localhost:8001/mcp
Private local STDIO user marm-mcp-stdio "agent" mcp add --transport stdio marm-memory-stdio marm-mcp-stdio
Multiple agents sharing memory marm-memory start --profile swarm "agent" mcp add --transport http marm-memory http://localhost:8001/mcp
Private high-throughput swarm marm-memory start --profile swarm-max "agent" mcp add --transport http marm-memory http://localhost:8001/mcp
Trusted private lab/server marm-memory start --profile trusted "agent" mcp add --transport http marm-memory http://localhost:8001/mcp
  • ⚡ Fastest HTTP Startup: Run marm-memory fast-start-http to spin up the local runtime, launch the console, and open it in your browser immediately.
  • 🖥️ Web Console: Run marm-memory console to view the local UI app instantly (no Node.js required).
  • ⚙️ Lifecycle Management: Manage the background daemon using status, logs --follow, restart, and stop.
  • 💡 Quick Flags: Use --no-console or --no-browser to restrict startups. Run marm-memory --help for full command lists.

Why MARM Memory

Your AI forgets everything. MARM Memory doesn’t.

marm-memory gives your agents a private, shared memory for the context that normally gets lost between chats: decisions, research, fixes, notes, and project history. Switch from Claude Code to Codex or Gemini without losing the context already gathered.

It brings three things together:

  • 🧠 Core Memory (8 tools) stores conversations, notes, notebook entries, and summaries so they stay searchable.
  • 💻 Code Graph (6 tools) maps your repository so agents can find symbols, follow code paths, and understand the project without rereading it all. Point it at a repo once and it keeps itself current as you work.
  • 🧩 Concept Graph (2 tools) connects people, decisions, errors, and ideas from your stored memories, with links back to relevant code when available. It builds itself as you store memories.

All 16 tools work over HTTP and STDIO. Your agents share the same local memory across sessions instead of starting from scratch each time. The bundled Console App provides a local control plane for memory, graphs, code context, distillation, runtime controls, and the integrated terminal. Indexing a repository creates its independent Code Graph, which you can explore from Knowledge Graph → Code Explorer even before storing any memories.

How It Works

Layer What it does Why it matters
Memory model Sessions, structured logs, notebooks, summaries, and semantic memories Keeps project history searchable instead of trapped in one chat
Scale layer SQLite WAL mode, connection pooling, serialized write queue, and HTTP rate-limit presets Lets one server support solo use, multi-agent work, and swarm-style bursts
Intelligence layer FTS filter, semantic re-rank, bounded semantic fallback, auto-classification, write-time consolidation, and compaction candidates Keeps recall useful as memory grows instead of letting duplicates pile up
Code graph layer Repo indexing, symbol lookup, call tracing, architecture overview, and change-impact analysis Gives agents project structure without rereading the whole codebase
Concept graph layer Entity and relationship extraction from stored memories, with links back into the code graph Connects decisions, errors, tools, and people across sessions instead of leaving them as flat text
Token layer Lightweight 8-tool core surface (16 total with bundled graph tools), semantic re-rank before retrieval, and write-time deduplication Reduces tokens sent to the model on every recall and cost stays predictable as memory scales
Deployment layer Pip, Docker, STDIO, HTTP, and managed swarm, swarm-max, and trusted profiles Lets you run private local memory or shared multi-agent memory with the same MCP surface

See Performance & Scaling Benchmarks for retrieval latency, concurrency, and write-cost numbers, and Architecture & Internals for the mechanisms behind each layer.

MARM Console: Your Local Control Plane

Run marm-memory console to open the bundled web app at http://127.0.0.1:8002. It ships with MARM, needs no Node.js installation, stays on your machine, and works with the same local stores and MCP runtime your agents use.

Workspace What it gives you
Memories and Knowledge Graph Browse, filter, edit, and clean up memory, logs, notebooks, extracted concepts, duplicates, concept builds, and code links.
Indexed Projects and Project Explorer Index local repositories, inspect architecture, impact, coverage, decisions, runtime traces, symbol search, and code-graph topology.
Code Context Build one bounded view of task-ranked symbols, source, and related memory. An optional local model can answer from that same context, with its citations checked.
Distill Turn transcripts into durable-memory proposals. Review, apply, or discard them with duplicate evidence visible.
System and Terminal Manage runtime health, indexing, local-model settings, backups, diagnostics, and maintenance. The docked terminal provides a real local shell with persistent sessions and a searchable MARM command menu.
Connections Set up MARM end to end in one place. Setup connects Claude Code, Claude Desktop, Cursor, VS Code, Codex CLI, Grok Build, Hermes Agent, OpenCode, Cline, Antigravity, Qwen Code, Devin, Kiro, and Zed over HTTP, STDIO, or Docker STDIO, tests each connection, and saves server settings to ~/.marm/settings.json. Docker pulls, starts, and stops the MARM container and writes a compose file. Manual gives copy-ready config for each client and OS, every CLI command, and every HTTP endpoint.

Runtime CLI Commands

marm-memory is the local runtime manager installed with the Python package. These are the normal operational commands; use marm-memory --help for flags and command-specific examples.

Daily runtime work

marm-memory fast-start-http                # start HTTP, Console, and open the browser
marm-memory fast-start-http --client cursor # also connect one client (add --transport stdio for STDIO)
marm-memory start                          # start or reuse the managed HTTP runtime
marm-memory start --profile swarm          # shared multi-agent preset
marm-memory stop                           # stop the managed runtime safely
marm-memory restart                        # restart the managed runtime
marm-memory status                         # inspect runtime, database, queue, and graph status
marm-memory logs --follow                  # follow bounded runtime logs
marm-memory console                        # start or reuse the bundled local Console

Transports and setup

marm-memory http                           # run HTTP in the foreground
marm-memory stdio                          # run the strict local MCP STDIO transport
marm-memory init                           # install the MARM skill into detected agents (project scan)
marm-memory init --g-claude                # install the skill into the home-folder claude directory
marm-memory doctor                         # diagnose the local install
marm-memory key init                       # create or reuse ~/.marm/.env without displaying the key
marm-memory key path                       # print the managed key-file path
marm-memory key reveal                     # explicitly display the managed key
marm-memory console --import-key           # open an authenticated local Console session
marm-memory upgrade --check                # compare the installed package with PyPI
marm-memory uninstall                      # preview package removal; always preserves ~/.marm

Knowledge, projects, and maintenance

marm-memory knowledge status               # Indexers, models, and how far behind automatic indexing is
marm-memory knowledge build --all          # Rebuild the whole concept graph (new memories index themselves)
marm-memory knowledge auto off             # Stop indexing memories automatically (on, off, status)
marm-memory projects list                  # List all tracked workspaces
marm-memory projects index           # Add a repo to the code graph (kept current after that)
marm-memory projects status                # Inspect target repo graph readiness
marm-memory projects auto off              # Stop re-indexing repos automatically (on, off, status)
marm-memory maintenance status             # Check internal database optimization state
marm-memory maintenance embeddings migrate # Upgrade old 384-dim vectors to 512-dim
marm-memory maintenance chunks rechunk     # Recalibrate long memory text splits

Docker commands are documented separately below because they require explicit data mounts, network exposure, and key-handling choices.

Performance & Scaling Benchmarks

MARM is tuned for fast recall first, even as memory grows and long memories are chunked behind the scenes.

These measurements use the fastembed-backed jinaai/jina-embeddings-v2-small-en encoder and a throwaway local SQLite database. Every timed path calls the shipped MARMMemory code, not a benchmark-local reimplementation. Sections 1-4 are timings from a single run of scripts/benchmarking/performance/bench_hotpath.py on local hardware; absolute milliseconds vary by machine, so treat the scaling shape as the signal. Section 5 is a separate accuracy benchmark (run_eval.py) measuring retrieval rather than speed, and its latest row is a controlled before and after, explained there.

1. Retrieval Latency Scaling

End-to-end recall_similar latency (includes query encoding).

Session Size ($N$) Min Latency Median Latency p95 Latency
N = 100 7.4 ms 7.9 ms 9.4 ms
N = 250 11.9 ms 13.5 ms 15.4 ms
N = 500 10.9 ms 11.8 ms 13.4 ms
N = 1,000 13.3 ms 13.5 ms 15.6 ms
N = 2,000 17.5 ms 18.2 ms 19.6 ms
N = 4,000 23.8 ms 25.9 ms 30.9 ms

Run-to-run variance at small $N$ is larger than the gap between adjacent sizes, which is why N = 250 reads slower than N = 500 here. Treat the trend from N = 1,000 upward as the real signal.

2. Encoder + Concurrency

  • Cold model load: 893ms
  • Warm encode: median 3.8ms, p95 4.3ms
  • Concurrent recall: 10 gathered recalls completed in 151.5ms vs 176.0ms serial (gather/serial = 0.86). Do not read that as parallelism: repeated runs of this same benchmark land anywhere from 0.63 to 0.86, so the ratio is not stable enough to claim a speedup. The path is serialized around shared encoder and SQLite work by design, and any apparent gain is measurement noise.

3. Write-Time Ingestion Cost

  • Consolidation off: median 6.5ms, p95 7.6ms
  • Consolidation on: median 58.1ms, p95 106.5ms
  • Tradeoff: write-time dedupe/clustering adds 9.0x median cost so recall stays fast and the store stays cleaner over time. Consolidation is off by default.

4. Recall Scaling: Full Scan vs Production Hybrid

Why recall stays flat as memory grows: Instead of scanning every vector, production recall uses an FTS keyword pre-filter to narrow the candidate pool, then re-ranks using a blended semantic + BM25 + temporal score. Both benchmark columns represent authentic asynchronous code paths timed with precomputed vectors to isolate retrieval speed from raw encoding overhead. Tests alternate execution to ensure completely unbiased cache conditions.

Session Size ($N$) Full Semantic Scan Production Hybrid Speedup FTS candidates
N = 100 3.3 ms 6.6 ms 0.5x 85 / 200
N = 500 16.3 ms 11.6 ms 1.4x 200 / 200
N = 1,000 31.1 ms 14.7 ms 2.1x 200 / 200
N = 2,000 63.5 ms 19.0 ms 3.3x 200 / 200
N = 4,000 127.2 ms 29.1 ms 4.4x 200 / 200
N = 10,000 316.7 ms 53.8 ms 5.9x 200 / 200

The full scan grows roughly linearly with $N$ while hybrid recall grows far more slowly, so the advantage still widens with session size. At very small $N$ the pre-filter is not worth its overhead and hybrid is slower.

5. LoCoMo Retrieval Accuracy

All 10 LoCoMo conversations are ingested through marm_log_entry (5,882 memories), then top-5 marm_smart_recall results are scored against 1,977 evidence-annotated questions. No answer-generation model or LLM judge is involved, so this measures whether the right memory is retrieved, not whether an agent answers correctly with it.

Configuration Any evidence hit All evidence hit Mean evidence recall
MiniLM baseline 37.5% 29.5% not published
Jina v2 Small (v2.29.0) 53.0% 43.4% 47.6%
v2.33.1 through v2.44.3 62.9 - 63.5% 53.1 - 53.5% 57.4 - 57.9%
v2.44.4 (log lane fix) 69.1 - 69.6% 58.2 - 58.6% 63.0 - 63.5%

The last row is a controlled comparison, same build and data with the log lane as the only variable. That lane previously substring-matched the whole query against log topics and summaries, so a natural-language question never matched and it scored 0.0% on all 1,977 questions. It now tokenizes the query and reaches 53.3% on its own. Ranges rather than single figures because the semantic lane varies about half a point between runs, so a sub-point difference is not a result.

Multi-hop remains the weakest category at 44.9%, and single-hop evidence recall is 36.6% against a 66.2% any-hit rate, so the lane often surfaces some of a question’s evidence rather than all of it. Reproduce with run_eval.py. It also reports MRR and nDCG@k for the ranked semantic lane; set MARM_RECALL_REFERENCE_TIME on the server so two runs rank near-ties identically.

6. vs Competitors: Architecture

MARM targets a specific niche: local-first memory for MCP-connected coding agents, not general personalization memory or a full agent runtime. Here’s how it differs architecturally from established names in AI agent memory:

MARM Mem0 Letta (MemGPT) Zep / Graphiti agentmemory
Type Memory engine, MCP-native Memory layer API Full agent runtime Temporal knowledge graph Memory engine, MCP-native
Required infrastructure No separate data service (embedded SQLite) Vector DB (Qdrant/pgvector) Postgres + vector DB Neo4j Separate iii-engine runtime
Deployment Local-first by default; Docker for shared/remote Cloud API or self-hosted Self-hosted or cloud Cloud or self-hosted Local-first
Retrieval model Hybrid: FTS5 BM25 exact lane + semantic rerank Vector + graph + key-value Vector archival store + agent-managed core memory Temporal knowledge graph (fact validity windows) BM25 + vector + graph (RRF fusion)
Write capture Explicit tool calls from the connected agent Explicit add() calls (some integrations auto-extract) Agent self-edits its own memory Explicit API calls Hook-based, automatic (no explicit calls needed)
Code structure awareness Bundled code graph + concept graph, fused with memory Not built in Not built in Not built in Not built in (pairs with a separate project)
Framework lock-in None (any MCP client) None High (must run within Letta) None None (any MCP client)

Disclaimers & Accuracy: Competitor landscapes evolve rapidly. The matrix above reflects core architectural traits as of Q3 2026, based on public documentation and READMEs, not internal testing of each system. If any data point regarding an alternative framework has changed or is misrepresented, please open an issue or submit a Pull Request to update the table. We actively welcome corrections from peer maintainers.

MCP Client Setup for HTTP & STDIO

Manual pip install

pip install marm-mcp-server

Use this quick rule of thumb to choose your setup

  • Local HTTP/STDIO = fastest single-machine setup.
  • Docker HTTP = shared/always-on server (key required).
  • Docker STDIO = private containerized local use (no HTTP key).

Swarm / multi-agent note: The write queue is enabled by default to serialize memory writes through one worker. For shared HTTP deployments, use marm-memory start --profile swarm (200 RPM) or --profile swarm-max (600 RPM). --profile trusted disables rate limiting entirely for private deployments. STDIO is still best for private single-agent/local use. See Swarm & multi-agent presets for the full table.



Connect your client

Start the server (python -m marm_mcp_server), then wire up your client below. Every block assumes the default local install (no key). For Docker or exposed servers, add the Authorization: Bearer header shown in each client’s collapsible.

Full platform walkthroughs, key setup, and OS-specific notes: Windows · macOS · Linux · Docker/key mode · Other platforms

Using a client that isn’t listed? Open an issue and let us know; client adapters are a first-class feature request.

Complete MCP Tool Suite (16 Tools)

💡 Pro Tip: You don’t need to manually call these tools! Just tell your AI agent what you want in natural language:

  • “Claude, log this session as ‘Project Alpha’ and add this conversation as ‘database design discussion’”
  • “Remember this code snippet in your notebook for later”
  • “Search for what we discussed about authentication yesterday”

The AI agent will automatically use the appropriate tools. Manual tool access is available for power users who want direct control.

🧠 Core Memory (8 tools)

Tool What it does Key parameters
marm_smart_recall Hybrid memory recall with an additive, bounded concept/code graph sidecar when a compatible graph exists query, limit, session_name, search_all, detail=1/2/3, project, platform, exact_mode
marm_log_entry Add structured session log entries; each entry is also embedded into semantic memory so marm_smart_recall can find it entry, session_name, project (optional; defaults to the server’s detected project)
marm_log_show Display all entries and sessions, with filtering session_name
marm_delete Delete a log session, log entry, or notebook entry type, target, session_name, project, platform
marm_summary Cached, paste-ready session summaries with intelligent truncation session_name
marm_notebook Session-scoped scratch pad plus promotion to a permanent, graph-linked doc action="add"|"use"|"show"|"status"|"clear"|"save", name, data, session_name, project, platform
marm_compaction Agent-assisted memory cleanup with a reviewable audit trail action="status"|"candidates"|"review"|"stage"|"apply"|"discard"
marm_distill Turn raw conversation into memory proposals, each resolved against the store as new, duplicate, or near. Selects sentences verbatim by default; with use_llm=true and local generation enabled, writes self-contained facts with a local model instead, keeping the verbatim span each came from. Staged for review; applied unattended only under review_mode="guardrails" with the operator’s auto-apply switch on, and then only proposals that pass every deterministic check action="propose"|"review"|"apply"|"discard", text, session_name, proposal_id, use_llm, review_mode (manual|guardrails)

🕸️ Code Graph (6 tools)

Tool What it does Key parameters
marm_graph_index Index a repo into the code-structure graph, check status, list projects, or turn automatic re-indexing on and off repo_path, project, action
marm_code_lookup Find symbols, text patterns, or a symbol’s source; use instead of grep/glob kind="auto"|"symbol"|"text"|"snippet"
marm_code_context Composed context for a task in one call: symbols ranked by personalised PageRank, their source read from disk, and what memory records about them task, project, cwd, budget, detail (0-3; 0 uses MARM_CODE_CONTEXT_DETAIL, the server default), include_graph (default false), answer (default false), analyst_mode (read_only|manual_review|guardrails)
marm_graph_trace Trace call paths and data flow from a function direction, mode
marm_graph_architecture Architecture overview: modules, node/edge breakdown, schema project
marm_graph_impact Blast radius of code changes: git diff → affected symbols + risk since, base_branch, depth

🧩 Concept Graph (2 tools)

Tool What it does Key parameters
marm_concept_build Rebuild the graph, or index memories stored before automatic indexing. New memories are indexed on their own session_name, project, or search_all=True (one required)
marm_concept_recall Explicitly query entities, relationships, and linked code symbols query, depth (1-5), direction, project, platform

All 16 tools are available on both HTTP and STDIO. Behind the tool surface, the server handles lifecycle setup, protocol refresh, docs indexing, date context, summary-cache maintenance, write queue handling, concept indexing, code re-indexing as repos change, project/platform attribution, and health checks automatically; none of those consume the agent’s attention or tokens. The two graph engines start lazily on first use and never block the 8 core memory tools if they fail to start. See Architecture & Internals for the mechanisms.

Using MARM: Talk, Don’t Call Tools

MARM handles lifecycle work internally. Docs and session state initialize on the first real tool call, and packaged docs are indexed into the marm_system memory namespace with source-file hash tracking, so your agent can answer MARM usage questions from memory itself.

Example Workflow: Cross-AI Research Project

A realistic workflow showing MARM in action. Scenario: you’re researching authentication patterns for a new project using multiple AI clients.

Phase 1: Route Session (Claude)

You: "Claude, create a MARM session called 'auth-research-2025-01'"
Claude calls: marm_log_entry(entry="Session: auth-research")
Result: Session routed to auth-research-[today]. MARM lifecycle/docs initialize automatically.

Phase 2: Capture Research (Claude)

You: "Summarize OAuth2 vs JWT for API authentication and save it"
Claude calls: marm_log_entry(entry="Research: OAuth2 is token-based with refresh cycles, better for delegated access. JWT is stateless, good for microservices...", session_name="auth-research-2025-01")
Result: Research captured in the active session log and marked for summary-cache refresh

Phase 3: Add Reusable Reference (Claude)

You: "Save a JWT validation code snippet to my notebooks as 'jwt-validation-pattern'"
Claude calls: marm_notebook(action="add", name="jwt-validation-pattern", data="def verify_jwt(token):\n  # validation logic...")
Result: Reusable snippet stored for future projects

Phase 4: Recall Context (Gemini)

You: "Gemini, what authentication approaches did we research? Activate the JWT pattern."
Gemini calls: marm_smart_recall("authentication patterns", search_all=True)
Gemini calls: marm_notebook(action="use", names="jwt-validation-pattern")
Result: Gemini sees previous research + has JWT code available as context

Phase 5: Synthesis & Summary (Qwen)

You: "Qwen, pull everything from the auth research and create a summary"
Qwen calls: marm_smart_recall("authentication", session_name="auth-research-2025-01", limit=20)
Qwen calls: marm_summary(session_name="auth-research-2025-01")
Result: Qwen generates an implementation guide from all captured research

Phase 6: End Session (Claude)

You: "Log final decision - we're using JWT for APIs, and OAuth2 for user auth"
Claude calls: marm_log_entry(entry="DECISION: JWT for API auth, OAuth2 for user flows. Rationale: stateless APIs + delegated user access", session_name="auth-research-2025-01")
Result: Decision logged and searchable by all future AI clients

Result: Three different AI clients collaboratively researched a topic, shared insights, and documented decisions. All without re-explaining the project to each new AI.

Advanced patterns

Understanding MARM Memory

Two searches, two very different problems, one tool:

User: "I discussed machine learning algorithms yesterday"
MARM Search: Finds related memories about "ML models", "neural networks", "AI training"

User: "What was the COMPACTION_TRIGGER_COUNT setting?"
MARM Search: Finds the exact config memory even if the rest of the text differs

The first query is about meaning, so MARM reranks candidates with local vector embeddings — RAG-style semantic search without a hosted vector database. The second is syntax-shaped (a config key), so MARM detects that automatically and routes it through deterministic exact matching instead. This exact-retrieval lane is the difference between a memory system that works in demos and one that answers the questions developers actually ask: config keys, CLI flags, file paths, API names, error strings. Pure-semantic memory systems fail at exactly those queries.

Memory types & classification

  1. Context Logs - Auto-classified conversation memories
  2. Manual Entries - Explicitly saved important information
  3. Notebook Entries - Reusable instructions and knowledge
  4. Session Summaries - Compressed conversation history

MARM automatically categorizes content on write: Code (programming snippets and technical discussions), Project (work conversations and planning), Book (literature, learning materials, research), and General (everything else).

Project & platform attribution

MARM stores nullable project and platform columns on memories, log entries, and notebook entries. The project is detected from the working directory and the platform from the connecting client (Claude Code, VS Code, Cursor, …); MARM_PROJECT and MARM_PLATFORM override detection. marm_smart_recall(project=..., platform=...) scopes recall without changing the default unfiltered behavior, so one shared server can hold several projects without cross-contamination.

Distilling a conversation into memory

Storing a memory is an explicit act, so the things worth keeping are the ones somebody remembered to keep. marm_distill works the other way round: hand it a stretch of raw conversation and it proposes the durable facts in it.

marm_distill(action="propose", session_name="release-notes", text="")
marm_distill(action="review")                       → the staged proposals and their verdicts
marm_distill(action="apply", proposal_id="...")     → writes that one as a memory
marm_distill(action="discard", proposal_id="...")   → rejects it, permanently

Every proposal is resolved against what is already stored and carries a verdict: new, duplicate, or near. near is the one that needs a person: it is close enough to a stored memory to be related, and a similarity score cannot say whether it refines that memory, contradicts it, or is simply adjacent. The proposal is shown beside the memory it resembles so a reviewer can decide which.

By default it proposes and never writes. propose stages into a review queue and only apply creates a memory; the one opt-in exception is review_mode="guardrails" with the operator’s auto-apply switch on, which applies a proposal only when every deterministic check passes. That is deliberately the same shape as marm_compaction, and for the same reason: a similarity score is not evidence enough to write memory unattended, and anything that does so on such a score fills a store with near-misses faster than it fills it with facts. A discarded proposal is never proposed again, enforced by a unique constraint rather than by convention — re-offering something a reviewer already rejected is how a review queue stops being read. It also makes re-running propose over the same text a no-op, which is what makes it safe to call at the end of every session.

How the text is written. With no local model reachable, this selects sentences that already read like durable facts and normalises them, rather than composing new ones — so a fact spread across three turns, or implied but never stated, will not be proposed. It finds what was said plainly, not what was meant. That is a real limitation, and also a reasonable fit: a MARM memory is a headline, and a headline is usually a sentence someone already typed. Asked to (use_llm=true) with local generation enabled and a model reachable, it writes the fact instead and keeps the verbatim span it came from; a proposal’s mode reports which of the two happened. See Optional local generation.

While proposals sit unreviewed, MARM can attach a review request to a tool response rather than waiting to be asked. The request names one proposal and asks for a decision on it — apply or discard — and only one is attached per cooldown window, server-wide, so a batch of proposals cannot put a request on every response. MARM_DISTILL_NUDGE=0 turns that off; the cooldown and budget are tunable in the configuration reference.

Knowledge Graphs: Code & Concepts

MARM ships two graph systems that complement the memory store: a code graph that understands your repository’s structure, and a concept graph that understands what your stored memories are about. When both are indexed for the same project, concept entities cross-link to code symbols.

Code Graph: repo indexing and code lookup

marm-graph is bundled into both transports. It indexes a repository once, then lets agents ask code-structure questions without repeatedly scanning files:

Use marm_graph_index to index this repository.
Then use marm_code_context when the question is how something works or what a change would touch.
Use marm_code_lookup when you need a specific symbol, file, or source snippet.
Use marm_graph_trace for call paths, marm_graph_architecture for an overview, and marm_graph_impact for change-risk checks.

The recommended agent workflow: index once, then marm_code_context for a task-shaped question, marm_code_lookup before broad file reads, marm_graph_trace when callers/callees or data-flow context matters, marm_graph_architecture for orientation, and marm_graph_impact before risky refactors. One graph query replaces dozens of grep/read cycles, which is where the token savings come from.

Composed context: marm_code_context

The other five tools answer one question each, and an agent typically chains them: search for a name, trace its callers, read each file, then look for anything memory recorded about them. marm_code_context runs that chain server-side and returns the result as one payload:

marm_code_context(task="how does the write queue serialise concurrent writes?")

Six steps: seed with lexical and semantic search, expand along callers and callees, rank that subgraph with personalised PageRank, read the winners’ source off disk, recall the memories and memory→symbol links attached to them, and budget the result down to a character limit with the decisive material first.

Step three is the one plain search cannot do. Lexical search answers “which symbols mention these words”, which is not the question an agent is asking; a symbol nothing calls and nothing references ranks below one sitting at the centre of the relevant neighbourhood, even when both mention the terms equally. Step five is the one a pure code index structurally cannot do — it carries why the code is the way it is, not only what it says.

detail trades size for structure. 1 (the default) returns the markdown and notes: what an agent needs, and nothing twice. 2 adds symbol and memory metadata — names, files, lines, scores, and the provenance that records whether a symbol was seeded from the task or pulled in along a call edge — without repeating the source already in the markdown. 3 adds source and memory text as structured fields too, which is what a renderer wants and what the Console asks for. MARM_CODE_CONTEXT_DETAIL moves that default for every agent at once. include_graph is a separate switch on a different axis, off by default: it returns the ranked edge list for visualisation, which nothing else reads.

One constraint worth knowing: traces resolve by qualified name. A bare name matching two symbols comes back as status: "ambiguous" with no edges rather than a guess, which leaves ranking with nothing to work on and collapses the result to plain search order.

answer=true goes one step further and answers the task in prose from the composed context alone — see Optional local generation.

Once a repository is indexed, MARM keeps it current on its own. A filesystem watcher notices a save, a commit, a branch switch, or a merge and re-indexes shortly after, debounced so a burst of changes becomes one pass rather than one per file. A periodic reconciliation pass catches anything a watcher event missed and is the only trigger for a directory that is not a git repo. To index only on request instead:

marm-mcp-server projects auto off

An agent can do the same with marm_graph_index(action="auto_off"), and action="auto_status" reports what is being watched and when each project was last indexed. The switch persists across restarts and beats the GRAPH_AUTO_INDEX environment variable.

Under the hood, the engine is codebase-memory-mcp (MIT), a zero-dependency static binary that parses 158 languages through tree-sitter with Hybrid LSP type resolution for the major ones, indexes an average repository in seconds, and answers symbol search and call tracing in well under a second. Measured on a 149,107-node graph over the persistent connection MARM holds: symbol search 146ms, call tracing 67ms, and the full architecture overview 1.23s, which is the one query that is not sub-second. MARM pins a specific release, verifies its tool schema on startup, and routes the upstream tool set through 6 focused MCP tools so the model surface stays small. The graph backend starts lazily on first graph-tool use, so memory, logging, notebook, and summary tools still start fast. In Docker, the engine binary is baked into the image; local pip installs fetch it on first graph use (~269MB, one time).

Degraded mode: if the graph engine fails to start (no network for the first-run download, disk full, schema drift) or GRAPH_ENABLED=false is set, graph tools return {"status": "error", "message": "graph backend unavailable"} while the other 10 tools keep working normally. Graph failures can never take down memory.

Concept Graph: what your memories are about

MARM extracts a knowledge graph from the memories you store, producing typed entities (concepts, decisions, patterns, errors, tools, people, organizations) connected by typed relationships (fixes, implements, depends_on, uses, causes, replaces, extends). This happens on its own: storing a memory queues it, and a background worker adds it to the graph roughly 30 seconds later. marm_concept_build is still there for a full or scoped rebuild. Once there is a graph, marm_smart_recall adds bounded related entities, relationships, and linked code as a graph_context sidecar without changing primary memory ranking. marm_concept_recall remains available for explicit graph exploration:

marm_concept_recall(query="write queue")            → the entity, its relationships, linked code symbols
marm_concept_recall(query="related to SQLite", depth=3) → multi-hop traversal of everything connected

How to use it:

  • Automatic by default: new memories reach the graph without a tool call. Set CONCEPT_AUTO_INDEX=false to go back to manual builds only, which stops the worker but keeps recording queue rows, so turning it back on picks up everything written while it was off; CONCEPT_INDEX_DEBOUNCE_SECONDS (30) and CONCEPT_INDEX_BATCH_SIZE (20) control the pace.
  • Safe on both transports at once: a leased lock in the memory database keeps a rebuild in one process from dropping graph tables while another process is writing to them. A build that finds the graph busy says so instead of colliding.
  • Failure never reaches your memories: indexing runs on a durable queue outside the write path. Extraction problems retry, a memory that fails repeatedly is parked with its error, and the memory itself stores and recalls normally throughout.
  • Linked code says when it changed: each linked symbol carries a freshness of changed once its code differs from when MARM first linked it, so a memory about it may be out of date; unchanged; or unknown when no safe fingerprint could be taken. The fingerprint ignores whitespace, blank lines and re-indentation, so it is a practical source-change signal rather than semantic equivalence, and changed means the code may have moved on, not that the memory is wrong. A link is fingerprinted only after a completed re-index, and only when its file has not been saved since that index began; changed stays set once seen.
  • Clearing a backlog costs some recall speed: entity extraction is CPU-bound, so while the worker is working through a queue, measured recall goes from ~8ms to ~16ms median on a real 768-memory corpus. Writes are unaffected. It only applies while a backlog is draining, which for most people is once, after the upgrade rebuild. Reproduce it with scripts/benchmarking/performance/bench_concept_worker.py --from-live.
  • Build for the backlog: marm_concept_build scoped to a session_name, project, or search_all=True indexes memories stored before automatic indexing existed, and rebuilds after an upgrade that requires one.
  • Upgrade twice so far: graphs built before platform attribution, or before compaction sources replaced summaries as the indexed rows, require marm_concept_build(search_all=True). A full build backs up and resets only the derived concept database; targeted builds refuse to guess platform ownership.
  • Older names are flagged, not rewritten: graphs built before extraction stopped keeping markdown can still hold names such as **apply**. The Concept Graph Manager counts them with a read-only check and offers Rebuild graph, which runs the usual reset and then a global build. Nothing is renamed in place.
  • Whole scope, paged: builds read every memory in scope. CONCEPT_BUILD_ROW_CAP (default 500) is the page size, so lowering it makes a build read more, smaller pages rather than skipping the rest.
  • Compacted sessions: the original memories are indexed and the generated summary is not, so concepts stay attributed to where they were actually stated.
  • Recall fails open: a missing, empty, incompatible, or unavailable concept graph never blocks normal memory recall. The response reports graph status separately.
  • Code cross-linking: when the code graph has indexed the same project, concept entities that match code symbols get linked, connecting “what we decided” to “where it lives in the code.”
  • Bundled extraction runtime: the spaCy runtime and English extraction model ship with MARM but load only on the first extraction, which now happens on its own shortly after the first memory is stored rather than when you run a build. If a damaged or partial installation makes them unavailable, both concept tools degrade cleanly while core memory remains available; run marm-memory knowledge status, then reinstall MARM if needed.
  • Isolated storage: the concept graph lives in its own SQLite database (~/.marm/index/marm_index.db) with its own connection pool, so concept-graph writes can never block or corrupt the production memory database.
  • Console atlas: the Knowledge Graph opens in a compact, deterministic connected sample for fast navigation. Choose Render all N nodes when you need the complete atlas. Full-atlas mode keeps background relationship lines hidden until you hover or select a node, so its direct connections remain readable without drawing the whole spiderweb at once.

This fills the cross-session structure gap that flat memory search leaves open: sessions organize memories, but the concept graph connects them, so “what depends on the write queue?” is answerable even when the answer spans five sessions from three different agents.

Architecture & Internals

Everything above runs on a small number of deliberate mechanisms. This section is the full map, so you (or your agent) never have to guess what the server is doing.

Storage engine

  • SQLite in WAL mode at ~/.marm/marm_memory.db with a connection pool (5 connections). WAL keeps readers unblocked during writes, which matters when several agents recall while one writes.
  • FTS5 full-text index (memories_fts) is maintained as an external-content table over the memories table and powers both the exact lane (BM25) and the filter stage of hybrid recall.
  • Chunk storage: memories past ~180 words are split into overlapping 150-token chunks (50-token overlap) in a memory_chunks table, each with its own embedding. Recall scores chunks and collapses to the parent memory.
  • Embeddings come from the fastembed-backed jinaai/jina-embeddings-v2-small-en encoder: 33M parameters, 512 dimensions, an 8,192-token context window, and an Apache-2.0 license. It does not require separate query/document text prefixes. The encoder is lazily loaded on first semantic use and serialized behind a lock so concurrent encodes can’t corrupt each other. If it is unavailable, writes still succeed; memories are stored without embeddings until it loads. Semantic scoring runs as a single NumPy batch (matrix cosine) rather than a Python loop.
  • The concept graph gets its own database (~/.marm/index/marm_index.db) and its own pool, reusing the same pool implementation but never sharing connections with the memory store. Deliberate isolation: an experimental graph build must not be able to stall the production WAL. The one exception is the indexing queue, which lives in the memory database on purpose so a memory and its indexing task commit together; the graph itself stays derived and disposable.

Write path

  • Serialized write queue (enabled by default): all memory writes flow through one internal async worker, eliminating SQLite writer contention under multi-agent load. The queue is generic; compaction applies go through the same worker, so there is exactly one writer no matter which subsystem is writing. MAX_QUEUE_SIZE bounds it.
  • Write-time consolidation (opt-in, CONSOLIDATION_ENABLED=1) runs two layers before a memory lands:
    • Layer 1, exact dedup: a SHA-256 hash of normalized content is checked within the session; hash hits are verified against the actual content before deduplicating, so a hash collision stores a new row instead of silently merging different content.
    • Layer 2, semantic merge: near-duplicates above CONSOLIDATION_THRESHOLD cosine similarity are merged rather than accumulated. This never blocks a write; if the encoder isn’t available, the write proceeds unconsolidated.
    • The tradeoff is measured and published: roughly 9x median write cost (58ms vs 6.5ms) in exchange for a store that stays clean, because reads dominate memory workloads. See section 3 of the benchmarks above.
  • Concept indexing is a durable outbox: a write records an indexing task in the same transaction as the memory, so a memory cannot exist without one. A background worker drains that queue and writes the concept graph. Nothing on the write path waits for extraction, and a process killed mid-extraction loses no work because the task is a row rather than an in-memory job. Both transports run a worker, so the two coordinate through a leased lock in the memory database rather than an in-process lock, which would not span them.
  • Compaction (opt-in, COMPACTION_ENABLED=1) is Layer 3: after enough writes in a session, a background pass detects clusters of memories that restate each other: every pair must clear the cosine-similarity threshold and share most of its words, gated by minimum cluster size, minimum age, and an active-session grace period so it never compacts work in flight. MARM then injects a bounded request asking the connected agent to summarize each cluster: candidates → stage → review → apply or discard. Source memory IDs are preserved on apply, so compacted summaries stay traceable to their originals. Staged summaries expire (COMPACTION_STAGING_TTL_HOURS), nudges are capped and cooldown-limited, and the injection has a byte budget. The design is honest about what LLMs are for: MARM detects, the agent summarizes, and a human-reviewable stage/apply/discard loop gates the destructive step.

Recall path

Covered in Understanding MARM Memory: exact lane (FTS5 BM25 + LIKE fallback), filter→rerank (bounded FTS candidates → batch semantic rerank → temporal blend), bounded semantic fallback with an explicit truncation flag, and chunk-collapse scoring. Recall depth (detail=1/2/3) controls how much of each memory is returned, and every MCP response passes through a 1MB response limiter that truncates content intelligently instead of breaking the protocol.

Code graph subprocess protocol

The bundled graph engine runs as a supervised child process, not an import:

  • Transport: newline-delimited JSON-RPC 2.0 over the child’s stdio, with a verified handshake (initialize → capture server version → initialized notification).
  • Envelope care: responses are scanned for the first JSON-parseable content item rather than assuming index 0, because the upstream binary can prepend an update notice. Tool errors arrive as result.isError, not JSON-RPC errors, and are converted to clean {"status": "error"} dicts with the upstream’s own remediation hint attached.
  • Serialization: one lock guards each write+read round trip on the single stdin pipe; async callers go through asyncio.to_thread so the event loop never blocks on subprocess IO.
  • Crash recovery: stderr is drained on a background thread, child EOF/crash is detected, and the process is transparently respawned on the next call. Timeouts are deliberately not treated as crashes; a long index run may still be working, and killing it would destroy in-flight work.
  • Supervision: a lazy singleton supervisor owns the client for the process lifetime. Startup is triggered by the first graph-tool call or by the auto-index worker if the engine binary is already downloaded, never raises into the MCP layer, and verifies the pinned binary’s tool schema so upstream drift is caught at startup instead of mid-call.
  • Auto re-indexing is filesystem watched, with a git content signature and a reconciliation fallback: a bundled watcher wakes the worker on a save, commit, branch switch, or merge; matching events are debounced so a burst becomes one re-index. For a git repository, the trigger is confirmed by hashing the diff against HEAD plus a fingerprint of non-ignored untracked files, computed outside the engine so an idle check costs no engine lock and two different edits to the same already-modified file are told apart instead of read as identical. A periodic reconciliation pass catches a missed watcher event, covers a filesystem that cannot be watched, and is the only trigger for a directory that is not a git repo. Git runs with core.fsmonitor disabled and a scrubbed environment, since that setting names a program git would otherwise execute from a watched repository.
  • One gate for every store mutation: manual indexes on all three surfaces, the auto-index worker, and project deletion all pass through a single leased row in the memory database. HTTP and STDIO are separate processes with separate engine children over one shared engine store, so an in-process lock cannot span them. The lease is released when the engine call actually returns rather than when its caller stops waiting: a cancelled request cannot hand the store to another process while the engine is still writing to it.

Optional local generation

MARM has never shipped a generative model. Concept extraction is spaCy and search is a sentence encoder, both local, which is why marm_distill selects sentences rather than writing them. This does not change that default — generation is used only once the operator switches it on (the Console’s System → Controls toggle, or MARM_LLM_ENABLED=1) and a local server is reachable. Discovering a running model is never enough on its own, and everything keeps working without one.

Two features use it, both only when asked per call, and both degrade rather than fail:

  • marm_distill(use_llm=true) writes self-contained facts instead of lifting sentences, and keeps the verbatim span each one came from. The proposal’s mode says which happened: generated means a model answered, selected means it did not.
  • marm_code_context(answer=true) closes the loop and answers the task in prose, grounded only in the context it just composed — so the ranking decides what the answer is allowed to be about. The Console streams that answer over an internal route, so text appears while the rest is still being written. That stream is the only request: its first event, context, is the composition itself (the payload marm_code_context returns, or its no_project/unavailable status), and the answer is written from exactly that composition, so what the Console displays and what the answer is grounded in are one retrieval. Each call is held to the operator’s profile: a reply the model does not finish within its token cap or time limit, or a stream that drops, is never retried wider; done carries truncated, and a truncated answer is never verified.

Loopback is enforced, not documented. A non-loopback host is refused outright rather than warned about, because a configuration mistake pointing this at a hosted endpoint would ship transcripts and source off the machine quietly, with no other symptom. The override exists, requires stating the intent in full (MARM_LLM_ALLOW_REMOTE=i-understand-this-leaves-my-machine), and is named in the refusal.

Every failure is a None, never an exception. A cold model, a busy GPU, a stopped container and a malformed reply all degrade to “no model answer this time”. Callers branch on the None; they do not catch. A memory tool must not stop working because an unrelated container was restarted.

Local evidence analyst

marm_code_context(answer=true) is a bounded, read-only analyst. MARM retrieves; the local model reads only what MARM retrieved; deterministic code judges every result before it is returned. The model never searches, fetches more context, writes memory or calls a tool.

  • One evidence packet. The composition is frozen into a packet with citable handles, S1… for symbols and M1… for memories, and a content hash, packet_id. The profile caps what the packet holds, whatever budget the caller asked for; answer_packet reports its size and what the cap left out.

  • Profiles, chosen by the operator. general writes one free-form answer, as before. small runs five narrow operations, one per call: a summary, supported facts with a verbatim quote, call and memory relations, missing evidence, and low-risk next steps (read, compare, verify or ask). large runs the same five in one call. Each operation returns JSON in a fixed schema, requested from the server as json_schema where it is supported and validated either way; an entry that does not fit is dropped and counted, never repaired. The profile is set in System > Controls or with MARM_ANALYST_PROFILE, and is never inferred from the model.

  • Hard limits. Every call asks for the profile’s cap, answer tokens plus any reasoning allowance, and is never retried wider. No call starts after the time limit, and none may run past it. answer_model_info reports the cap, the number of calls, the elapsed time and whether the run stopped early; a cut-off answer is never verified.

  • Every result verified on its own. A cited handle the packet does not contain rejects. A fact is supported only when its quote is verbatim in an item it cites, a call relation only when the packet has that edge, a memory relation only when the memory is bound to or names the symbol, and every claim only when the words it asserts appear in the evidence it cites. A claim with nothing a check could fail is not verified. An answer is verified only when every substantive result is, and every operation completed; otherwise it is uncertain, reported in answer_status as ok, unverified or rejected. answer_items carries each result with its state and what supported it.

  • Code-memory disagreement. A memory that states a call the packet’s graph contradicts, or does not show, is listed in answer_disagreements without asking the model.

  • Three modes (analyst_mode), each owning exactly one thing. read_only, the default, answers from the packet and nothing else: no retrieval, no staging, no writes. manual_review stages the verified answer’s own results as marm_distill proposals with origin: "analyst", for a person to apply or discard. guardrails stages them the same way and lets MARM apply the ones it can prove mechanically, only when the operator has switched auto-apply on. The model never applies anything, and review never asks it for anything: only an answer whose verification is verified stages at all, so an uncertain answer cannot become verified by being restated.

Only mechanically provable claims are applied automatically. Each staged result carries its claim kind. MARM applies a call_edge (written by MARM from a packet call edge), an identifier_in_range (MARM’s own statement of where a cited symbol is defined), a quoted_span or a verbatim_statement (a whole sentence or line of the cited source or memory, never a fragment, since a fragment can drop the negation that governs it), and re-checks the kind against what was stored before deciding. Anything else, including any paraphrase however well cited, returns review_required. The other checks: the operator’s auto-apply switch is on; the proposal is re-resolved when the decision is made and is still new; one line of 12 to 300 characters; nothing that looks like a credential, whether labelled (api_key=) or in a known token format (provider keys, a bearer value, a JWT, a URL carrying a password). review_mode="guardrails" on marm_distill makes the same decision for distilled proposals, whose span must be verbatim in the source text. Every decision, applied or not, is recorded on the proposal before any write, and becomes apply_failed with the error if the write then does not happen. An applied proposal is reported as applied and no longer counts as awaiting review.

The JSON response and the stream share one implementation and return the same verification. A reader who closes the stream stops the generation. scripts/eval-analyst.py runs the evaluation matrix in tests/fixtures/analyst_eval.json against a live local model under any profile.

That is the contract for marm_code_context(answer=true), which returns "answer": null when a model is unreachable or answers with nothing. The Console’s streaming route cannot use it, because a stream has already started by the time generation fails: it emits an SSE error event whose JSON data carries a message, plus a hint when no model is reachable at all. Same outcome either way – the ranked context stands and only the answer is missing – but a client reading the stream branches on the event, not on a null.

The endpoint is resolved in order: a runtime choice saved from the Console, then MARM_LLM_URL, then discovery, then the built-in default. Discovery scans loopback for the ports the common local runtimes use and reports what answered rather than what a port usually belongs to, so MARM follows whichever server is actually serving without a restart or a config change. A stated endpoint always wins, so an address you set and that is dead surfaces as dead instead of being silently replaced.

The Console’s System → Controls tab surfaces all of it: which server answered and how it was chosen, the models it is serving, the model files found in the usual local roots, and a picker that pins a choice as a durable runtime flag.

Security & rate limiting

  • Two-mode auth gate: keyless on loopback (127.0.0.1), MARM_API_KEY (Bearer) mandatory the moment the server is network-exposed (SERVER_HOST=0.0.0.0, Docker). --generate-key produces one. Safe by default, zero setup friction locally.
  • IP-based rate limiting with sliding windows and temporary blocks, tuned through CLI presets rather than a config maze (table below).
  • Local-first: everything lives under ~/.marm/; no cloud sync, no telemetry, no external storage.
  • Graceful shutdown: SIGTERM/SIGINT handlers drain and close the connection pool cleanly, and an internal event system runs automation callbacks with per-callback error isolation and timeouts so one bad hook can’t wedge the server.

Swarm & multi-agent presets

Flag Rate Limit Write Queue Use When
(none) 80 RPM enabled Normal local use and small 3-5 agent setups
--swarm 200 RPM enabled Shared HTTP server, roughly 15-30 agents depending on write style
--swarm-max 600 RPM enabled Heavier local/private swarm, roughly 50-100 agents depending on write style
--trusted disabled enabled Private/trusted deployments only
--rate-limit-rpm N N RPM unchanged Custom override; 0 disables limiting

The write queue serializes memory writes regardless of preset; swarm flags tune the HTTP rate limit on top of that. The queue controls write ordering; consolidation and compaction are separate memory-maintenance layers. This stack (WAL + pooling + one serialized writer + RPM presets) is intentionally scoped to “SQLite, many agents, one machine”; distributed multi-node memory is out of scope for the current design.

Self-maintaining documentation

Packaged docs are indexed into the marm_system memory namespace on startup and refreshed every 50 tool calls, with source-file hash tracking so unchanged docs are skipped and changed or deleted rows are re-indexed. Connected agents can answer MARM usage questions with marm_smart_recall instead of you pasting docs at them.

Configuration reference

Troubleshooting

For memory behavior, transports, supported clients, compaction, and backup questions, see the FAQ.

Star History

Contributing

MARM welcomes contributors at every level. Code helps, but so do docs, setup notes, client testing, bug reports, benchmarks, and real workflow feedback from people using AI tools every day.

Good places to help:

  • Test MARM with more MCP clients, IDE agents, and operating systems
  • Improve docs, screenshots, examples, and platform-specific setup notes
  • Report bugs or confusing install steps with clear reproduction details
  • Share memory workflows, agent habits, and tool ideas from real use
  • Check out open issues

💡 Want to get your name on this list? Check out our CONTRIBUTING.md guide to get started!

Join the MARM Community

Help build the future of AI memory - no coding required!

Connect: MARM Discord | GitHub Discussions

License & Usage Notice

Copyright © 2026 Ryan A. Lyell. MARM is released under the Apache 2.0 License (see NOTICE for the copyright statement), and forks, experiments, and integrations are welcome. MARM also wraps third-party open-source components such as codebase-memory-mcp under MIT; see THIRD_PARTY_NOTICES.md for attribution. If you build on it, please make unofficial versions easy to distinguish from releases published by the official MARM repository so users know what they are installing.

Project Documentation

Usage Guides

  • README.md - This file: complete usage guide, tool reference, workflows, and architecture
  • PROTOCOL.md - MCP operating protocol
  • FAQ.md - Answers to common questions about using MARM

MCP Server Installation

Project Information

View the full README on GitHub

Install

docker run --rm lyellr88/marm-mcp-server:latest --generate-key
New to MCP? Start with the mcpskill.org guide →

Configuration

{ "mcpServers": { "marm-memory-local": { "type": "http", "url": "http://localhost:8001/mcp" }, "marm-memory-docker": { "type": "http", "url": "http://localhost:8001/mcp", "headers": { "Authorization": "Bearer ${env:MARM_API_KEY}" } } } }