docs / agent-memory / getting startedgithub.com/Fareground/agent-memory ↗

Getting started

Install

One line from PyPI. fg-agent-id is the only runtime dependency — everything else is stdlib — and it resolves automatically:

pip install fg-agent-memory

# with the optional MCP stdio server
pip install "fg-agent-memory[mcp]"

Working from source:

git clone https://github.com/Fareground/agent-memory.git && cd agent-memory
python -m venv .venv
.venv/bin/pip install -e ".[dev]"

Requires Python ≥ 3.11. The mcp extra pulls mcp>=1.0,<2 (mcp 2.0 removed the server API it uses); only the MCP server imports it — the core never does.

Everything runs locally — no network and no model required for the core.

Two calls

from fg_agent_memory import Memory

memory = Memory("./memory")          # readable directory you can commit to git
memory.remember("John's favorite editor is Zed.")
print(memory.recall("what editor does John use?").as_prompt_block())

That's the whole porcelain: remember writes, recall reads a budgeted, prompt-ready block. Remembering the same content twice deduplicates automatically — the repeat reinforces the record and merges tags instead of duplicating it.

Want every record and every export signed by a persistent agent-id identity? Pass a keyfile path — it is created on first run and reloaded ever after (an existing KeyPair works too):

memory = Memory("./memory", identity="agent.key")

The ghost-memory demo

The problem: you learn X, later learn not-X, and stale X haunts retrieval forever. Watch what happens here instead:

from fg_agent_memory import Memory

memory = Memory("./memory")
a = memory.remember("The staging database is Postgres.")
b = memory.remember("The staging database is not Postgres.")

memory.consolidate()                 # detects the contradiction; neither side dropped
print(memory.recall("staging database").as_prompt_block())
# Relevant memories (query: staging database):
# - The staging database is Postgres. [disputed: one side negates the other]
# - The staging database is not Postgres. [disputed: one side negates the other]

Both sides are surfaced, explicitly marked as disputed. The dispute stays visible until you resolve it, with a reason:

memory.resolve(a.record_id, b.record_id,
               "checked infra: staging moved off Postgres in March")

print(memory.recall("staging database").as_prompt_block())
# Relevant memories (query: staging database):
# - The staging database is not Postgres.

The loser isn't deleted — it is superseded and rides the history trail:

print(memory.recall("staging database", include_history=True).as_prompt_block())
# Relevant memories (query: staging database):
# - The staging database is not Postgres.
# - The staging database is Postgres. [superseded — kept for history]

Under the hood the loser's full version trail is preserved (active → transitional → superseded), with superseded_by pointing at the winner and your reason recorded as state_reason.

What just happened

MCP server

With the [mcp] extra installed, expose a memory directory over stdio:

fg-agent-memory-mcp --path ~/agent-memory

Client configuration:

{ "mcpServers": { "memory": { "command": "fg-agent-memory-mcp",
                              "args": ["--path", "/home/me/agent-memory"] } } }

The server exposes five tools: remember, recall, consolidate, redact, status. There is deliberately no resolve tool — settling a contradiction requires the Python API, keeping dispute resolution a deliberate act. Memory holds one lock around every public operation, so sharing an instance across threads or MCP worker pools is safe.

Where to go next