MCP Integration
The mempill-mcp package is a FastMCP server that
exposes the mempill engine over the MCP stdio transport. It lets any MCP-compatible agent
(Claude Desktop, Claude Code, custom) read and write mempill claims without embedding the
Python wheel directly.
What mempill-mcp exposes
Section titled “What mempill-mcp exposes”The server registers four tools, implemented in
mempill-mcp/src/mempill_mcp/tools.py:
| Tool | Signature | Description |
|---|---|---|
ingest_claim |
(subject, predicate, value, provenance, ...) |
Write a belief claim |
query_memory |
(subject, predicate, as_of_tx_time?, valid_at?) |
Read the canonical belief |
reconcile |
(subject_lines) |
Trigger conflict reconciliation |
audit |
(limit?, claim_ref?, from_tx_time?) |
Read the immutable audit ledger |
These map directly to engine.ingest_claim, engine.query_memory, engine.reconcile,
and engine.query_audit. The agent_id is injected from the environment — the MCP
client never needs to pass it.
query_subject (subject-scoped enumeration of every predicate known about a subject,
available in the Rust core and Python wheel as of 0.3.0) is not yet exposed as an MCP
tool. Use Python (Advanced) if
you need it today.
ingest_claim parameters
Section titled “ingest_claim parameters”subject str Entity (e.g. "user:alice")predicate str Property (e.g. "city")value any JSON-serialisable valueprovenance str | dict "External:UserAsserted" | "External:ExternalFirstHand" | "RecallReEntry" | "ModelDerived" | raw wire dictcardinality str = "Functional" "Functional" | "SetValued" | "Unknown"confidence_value float = 0.9 Value confidence [0, 1]confidence_valid_time float = 0.9 Temporal confidence [0, 1]criticality str = "Low" "Low" | "Medium" | "High" | "Critical"valid_time dict | None {"start"?: ISO-8601, "end"?: ISO-8601, "valid_time_confidence": float, # required if valid_time is set "start_granularity"?: "year"|"month"|"day"|"instant", "end_granularity"?: "year"|"month"|"day"|"instant"}derived_from list[str] | None Source claim UUIDsWhen disposition is non-committed (Contested, QueuedForAdjudication, etc.), the response
includes a human-readable status_reason field explaining why.
query_memory parameters
Section titled “query_memory parameters”subject strpredicate stras_of_tx_time str | None ISO-8601 UTC — rewinds the transaction-time axis ("what did the engine know as of this timestamp?")valid_at str | None ISO-8601 UTC — filters by the valid-time axis ("what was true in the real world at this point in time?")as_of_tx_time and valid_at are independent. Use valid_at to ask “who was CEO on
2021-06-01?” regardless of when that claim was recorded. Use as_of_tx_time to rewind
to what the engine knew at an earlier transaction timestamp.
Returns {"belief": {...}}. Contested beliefs also include status_reason.
When a belief has a primary claim, the response includes pre-rendered display strings:
{ "belief": { "status": "Resolved", "primary": { "fact": {"value": "Alice"}, "valid_from_display": "2020-03", "valid_until_display": null, "valid_time": { "start": "2020-03-01T00:00:00Z", "end": null, "valid_time_confidence": 0.9, "start_granularity": "month", "end_granularity": null } } }}valid_from_display / valid_until_display (on the belief slot) reflect the recorded
precision (no fabricated day or month components). Use these strings when surfacing valid-time
bounds to users or downstream tools. valid_time.start_granularity / valid_time.end_granularity
carry the machine-readable label (lowercase: "year", "month", "day", "instant", or
null for legacy claims).
reconcile parameters
Section titled “reconcile parameters”subject_lines list[list[str]] Each inner list is [subject, predicate]Example: [["user:alice", "city"], ["user:bob", "location"]]
audit parameters
Section titled “audit parameters”limit int = 50claim_ref str | None UUID of a specific claim to filterfrom_tx_time str | None ISO-8601 UTC lower boundEnvironment variables
Section titled “Environment variables”| Variable | Required | Default | Meaning |
|---|---|---|---|
MEMPILL_AGENT_ID |
Yes | — | Agent identity for all writes in this session. Must be set before launch — the server fails fast with a clear error if missing. |
MEMPILL_DB_DIR |
No | in-memory | Base directory for SQLite storage. The database file is derived automatically as MEMPILL_DB_DIR/agent_{MEMPILL_AGENT_ID}.db. If omitted the server uses an ephemeral in-memory engine (data lost on exit). |
MEMPILL_AGENT_ID is the single-writer key: all claims written in a session belong to this
agent. Use a stable, meaningful identifier (e.g. "claude-desktop-alice").
Installation
Section titled “Installation”mempill-mcp depends on the 0.4.0 open_for_agent entry point (see the breaking-change note
above), which is not yet in the published PyPI wheel (latest published: 0.3.0). Until 0.4.0 is
published, build both packages from source into the same venv rather than mixing a PyPI wheel
with the main-branch MCP adapter — pip install mempill (0.3.0) + mempill-mcp from main
will fail at startup because the wheel lacks the entry point the adapter calls:
# 1. Build and install the mempill wheel from source (0.4.0, main branch)cd mempill/mempill-pythonmaturin develop --release
# 2. Install the MCP adapter from source into the same virtual environmentcd ../mempill-mcppip install .
# 3. Test the serverMEMPILL_AGENT_ID=test python -m mempill_mcp# Should start and wait for MCP stdio input; Ctrl-C to exit.Once 0.4.0 is published to PyPI, step 1 collapses back to pip install mempill.
The easiest approach is to use the mempill-demo repository, which ships a pre-built venv
with all dependencies. See Examples for the demo.
Claude Desktop configuration
Section titled “Claude Desktop configuration”Claude Desktop reads mcpServers from its config file. Copy the block below, replace the
absolute paths, and restart Claude Desktop (full quit — menu bar → Quit, not just close):
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{ "mcpServers": { "mempill": { "command": "/ABSOLUTE/PATH/TO/mempill-demo/.venv/bin/python", "args": ["-m", "mempill_mcp"], "env": { "MEMPILL_AGENT_ID": "my-agent", "MEMPILL_DB_DIR": "/ABSOLUTE/PATH/TO/mempill-demo/data" } } }}Important notes:
- Use the
pythonbinary from the project venv that hasmempillandmempill-mcpinstalled. Launchingmempill-mcpas a bare command requires both packages to be on the system Python, which is unlikely. The venv python is the reliable path. - Claude Desktop does not expand
$HOMEor shell variables in this file — use literal absolute paths. - The
MEMPILL_DB_DIRdirectory is shared across all Claude Desktop sessions for this agent; the actual database file is derived per-agent asMEMPILL_DB_DIR/agent_{MEMPILL_AGENT_ID}.db. Use distinctMEMPILL_AGENT_IDvalues (or distinctMEMPILL_DB_DIRdirectories) if you need per-agent isolation.
This config is sourced from mempill-demo/mcp/claude_desktop_config.json.example.
Claude Code (.mcp.json) configuration
Section titled “Claude Code (.mcp.json) configuration”Claude Code reads .mcp.json from the project root. This file is gitignored in mempill-demo.
Claude Code supports ${VAR:-default} expansion in the env block:
{ "mcpServers": { "mempill": { "type": "stdio", "command": "/ABSOLUTE/PATH/TO/mempill-demo/.venv/bin/python", "args": ["-m", "mempill_mcp"], "env": { "MEMPILL_AGENT_ID": "${MEMPILL_AGENT_ID:-demo-agent}", "MEMPILL_DB_DIR": "${MEMPILL_DB_DIR}" } } }}If MEMPILL_DB_DIR is unset in the shell environment, the server opens an in-memory
(ephemeral) engine.
This config is sourced from mempill-demo/mcp/.mcp.json.example.
How the server lifecycle works
Section titled “How the server lifecycle works”The server opens the mempill engine once in a FastMCP lifespan context manager and
yields {"engine": engine, "agent_id": agent_id} to all tool functions. This means:
- Engine startup cost (migrations, WAL setup) happens once per server process, not per call.
- All four tools share the same engine instance and agent identity.
- SQLite WAL mode is active — concurrent reads from other processes do not block.
- When
MEMPILL_DB_DIRis set, the database file persists after the server exits.
Testing the server without an MCP host
Section titled “Testing the server without an MCP host”mempill-demo/mcp/verify_stdio.py is a small script that sends a tool-list request over
stdio and validates the response. Run it against the installed server:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \ | MEMPILL_AGENT_ID=test .venv/bin/python -m mempill_mcpYou should see a JSON response listing all four tools.
Contested beliefs in MCP context
Section titled “Contested beliefs in MCP context”When query_memory returns a Contested belief, the response includes:
{ "belief": { "status": "Contested", "primary": null, "alternatives": [ {"fact": {"value": "Berlin"}}, {"fact": {"value": "Munich"}} ] }, "status_reason": "This claim conflicts with one or more existing beliefs..."}A well-behaved MCP agent should surface this to the user rather than silently picking one value. See Concepts: Contested and Dispositions.
Related guides
Section titled “Related guides”- Writing an Oracle — adding HITL resolution to MCP sessions
- Python (Advanced) — embedding the wheel directly instead of MCP
- Examples — the demo repo with a working MCP config