Facthouse
Facthouse is an MCP server that gives AI assistants a local, consolidated memory: it copies or records conversation events, extracts durable facts, integrates them into an entity knowledge graph, and lets you search and retrieve that knowledge across sessions.
Log conversation events (
log_event) — record raw messages, tool calls, results, and artifacts, either manually or automatically via hooks.Retrieve event history (
get_events) — recall what happened earlier in a session or a previous session, useful after context compaction.Capture durable facts (
capture_fact) — store stable knowledge (preferences, relationships, decisions) immediately; duplicates and contradictions are reconciled later.Get a session briefing (
get_session_contextormemory://briefingresource) — loads the working profile, recent learnings, and open threads at conversation start.Search integrated knowledge (
search_knowledge) — hybrid keyword/semantic search over deduplicated, reconciled facts, with source attribution and confidence.Explore a topic deeply (
get_context) — combines search with entity-graph traversal to get the complete picture around a subject.Inspect a named entity (
get_entity) — everything known about a person, project, place, or product, and how it connects to other things.List knowledge domains (
get_schemas) — see the domains and subdomains this store actually uses.View knowledge-base stats (
get_stats) — fact/entity counts, domain distribution, extract backlog, embedding coverage, and intelligence spend.Consolidate pending knowledge (
consolidate) — run the copy → extract → integrate pipeline to turn raw events into the knowledge graph; supports backfill caps and full-backlog mode.Manage stores via CLI —
facthouse init,record,consolidate,search,notify,stats,inspect,prune, andsettingsfor configuration, hooks, and storage (SQLite or Postgres).Optional embeddings and local/remote intelligence — enable semantic search via Ollama/Voyage, and route extraction/summarisation to an OpenAI-compatible HTTP host or the Claude CLI.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Facthouseremember that my favorite color is blue"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Facthouse
Facthouse is a local memory engine for AI tools. Most “memory” products index chat logs. Facthouse takes agent activity - messages, tool use, and other MCP traffic - and applies neuroscience-inspired consolidation so it moves through Data (what happened in the session) → Information (extracted facts) → Knowledge (integrated beliefs on an entity graph). During this process, Facthouse links entities, drops duplicates, reconciles conflicts, and supersedes what is out of date. Vector embeddings add optional semantic search on top of that graph. The store is a SQLite file on your disk.
Quick Start
Needs Node 22.5 or 24+.
npm install -g @facthouse/mcp@0.31.0
facthouse initIf npm install -g fails because a command named mcp already exists, remove that leftover command and retry.
facthouse init --web is the same setup as a browser form — it prints a 127.0.0.1 URL and does not open a browser.
Press Enter to accept each default (copy = Claude Code or Cursor session logs on disk; type record if the assistant should save facts). If you picked copy, init asks whether to copy existing logs, then whether to extract and integrate. Init prints an MCP snippet as soon as the store is written — add it to the client's MCP config while copy/extract run. Restart the client when init finishes.
In the client, state something durable in ordinary conversation — there is no remember command.
Ask it back in the next session, or facthouse search. That is the store.
Copy from Claude Code or Cursor logs, or record from any MCP client: How conversations get in. Replay: facthouse.dev/demo.html. CLI: below.
Related MCP server: HKC Memory Server
What you get
Local SQLite. Optional Postgres. Isolation is the directory, not a column.
Entity graph. People, organisations, projects, places, products — extracted, typed and linked.
Hybrid search. BM25 + structured domain + entity-graph paths, merged via Reciprocal Rank Fusion. An embedding provider adds meaning as a fourth list; off by default.
In-session memory.
get_session_contextis the same briefing asmemory://briefing. Tools-only clients should call it at session start.Immutable history. Facts are never deleted, only superseded.
How it works
One SQLite database. Three tables in it, not three databases: Data (what happened in the session) → Information (extracted facts) → Knowledge (integrated beliefs).
D (
session_events) — what was said (copied transcripts, or what the assistant records)I (
session_facts) — what was just extracted, orcapture_factK (
facts) — integrated knowledge
FTS5 (words) and optional embeddings (meaning) are indexes of K. They are not a second store. Semantic search is off unless you turn it on: search "shellfish" finds a shellfish fact, search "food" does not, until you choose an embedding model — a model is an opinion about what “similar” means.
Two speeds. Copy tails named transcripts into Data. Extract turns new transcript lines into self-contained facts (D→I). Integrate fits them into what the store already knows: domains, entities, duplicates, contradictions, the graph (I→K). consolidate is the umbrella: copy, extract, and integrate together. Extract is capped at 50 lines per run, so a first backfill is never spent on the lot; when extract runs, it takes the oldest 50 lines. Consolidation does not invent a sentence nobody said.
A hook cannot call MCP tools — those exist only on the assistant’s connection — and it must not wait for a model pass. So it does not invoke consolidate. It runs facthouse notify …, which tells the already-running server that a moment happened and returns at once. consolidate is the pipeline verb (MCP tool or CLI); the caller waits. notify compaction is that verb asked of the live server, asynchronously. notify threshold is a different policy (extract only, if due).
Automatic
When | Copy | Extract (D→I) | Integrate (I→K) |
Facthouse MCP server starts | yes | yes (cap 50) | yes |
A Facthouse tool or resource is called | yes, if sources named and JSONL grew | no | no |
Facthouse MCP process exits | no | no | yes |
Callable
Call | From | Copy | Extract (D→I) | Integrate (I→K) |
| MCP tool or CLI. Caller waits. | yes | yes (cap 50) | yes |
| Other process (recommended PreCompact; we do not install). Does not wait. | yes | yes (cap 50) | yes |
| Other process. Does not wait. Not a copy-store hook. | no | yes, if due (cap 50) | no |
On a default copy store with no extra hooks, only the automatic table runs: Facthouse starts (all three), copy on each Facthouse tool or resource call if named sources grew, and integrate on a clean process exit. Closing a chat window may skip the exit row; the next start still consolidates. Due on threshold means at least 10 unexamined lines and two minutes since the last gated run. consolidate --all lifts the extract cap. Compaction is recommended PreCompact (notify compaction): same three steps as consolidate, on the running server, without waiting. We do not install the hook. Not a Stop hook. record wakes threshold extract on a record store — do not install record hooks on a copy store.
Storage needs Node. Intelligence needs a language model. By default that is the Claude Code CLI on your existing subscription. Without it, consolidation falls back to a built-in heuristic that does not extract facts from transcripts. capture_fact still stores facts, with no entities and no domain routing.
How conversations get in
Two ways. Pick one per store.
Copy from transcripts | The assistant records | |
Who | Claude Code or Cursor (session logs on disk, under the client home) | Any MCP client (Grok, Desktop, …) |
How | Name a source; Facthouse copies new lines from those logs into the store | Empty |
First run | TTY walk-through, pick copy, set cwd; init asks whether to copy existing logs, then whether to extract and integrate | TTY walk-through, pick record |
On a copy store, capture_fact is a correction for every MCP client, not only the one that writes JSONL. Grok has no transcript adapter — do not put Claude Code on copy and Grok on the same store expecting Grok to record.
facthouse initPick copy, set cwd. Init asks whether to copy existing logs, then whether to extract and integrate (Enter = all copied lines; a selection of 500 or more asks you to type the choice again). Decline extract to do that later with facthouse consolidate (--all takes remaining unexamined lines, not ones skipped as outside a 7-day or 30-day window). After that, the server copies new lines when it handles a Facthouse call. Extract and integrate follow the table in How it works.
Compact (recommended): facthouse notify compaction — we do not install the hook. Not a turn-end Stop hook.
MCP
Works with any MCP-compatible tool. Default store: ~/.facthouse. A different path is "env": { "FACTHOUSE_DATA": "/absolute/path" } on the MCP snippet. JSON accepts forward slashes on Windows.
Cursor consumes tools but not resources until a later adapter exists — search_knowledge and get_entity still work there; call get_session_context at session start.
Resources are context the client loads automatically — no tool call. Tools only help if the assistant remembers to reach for them; resources are simply present.
memory://briefing— Everything worth knowing right now: profile, what was learned in the last consolidation, open threads, and recent knowledge. Markdown, kept to roughly a screenful.memory://profile— Core identity facts, most important first.
Both are read-only views over the same database the tools query. Clients that never load resources (Cursor, Windsurf, Grok) get the same briefing by calling get_session_context at the start of a conversation. No second profile schema.
Tools
Session
log_event— Log conversation events (messages, artifacts).get_events— Retrieve events from current or previous session.get_session_context— Working briefing (the same markdown asmemory://briefing) plus facts captured in this session. Call at the start of every conversation if the client does not load resources.
Reading
get_entity— Everything known about any named subject — person, organisation, project, place, product — and how it connects. When several rows share the name under different types, facts from all of them come back. Hyphens, underscores, and stray punctuation count as the same letters only when that does not join two names already stored as separate rows. If there is no entity by that name, facts that mention the wording still come back rather than an empty miss.get_context— Everything relevant to a topic (search + entity traversal)search_knowledge— Hybrid search across integrated knowledge
Writing
capture_fact— Store a fact. On a copy store this is a correction for something extraction missed; on a store with emptysourcesit is how facts get in. The description the assistant sees is generated from that same rule.consolidate— Integrate pending facts into long-term knowledge. Extracts entities, resolves duplicates, detects contradictions, builds the knowledge graph.Inference tools — Opt-in, off by default (
inferences.enabledin config.json). A hypothesis cites existing fact ids and stays pending until confirmed. Those tools are not registered until you turn the gate on. Consolidate never invents a sentence nobody said.
Meta
get_schemas— Available domains and structureget_stats— Fact count, entity count, domain distribution, extract backlog, intelligence spend
CLI
The MCP JSON starts the server via npx and does not need a global install. npm install -g puts facthouse on PATH for init, settings, stats, and inspect. The same CLI without PATH is npx -y -p "@facthouse/mcp" -- facthouse — pin the version; quote the package so PowerShell does not splat. -p and -- stop an older global binary winning. npx -y @facthouse/mcp with no -p / facthouse is the server; do not run it as a shell command for init, settings, or stats. The MCP paste starts the server. It does not put facthouse on PATH. To inspect the file from a terminal, see CLI below.
These CLI commands work in bash, zsh, and PowerShell. Quote @facthouse/mcp in PowerShell. Git Bash /c/... paths are not PowerShell; use C:/... and pass --data instead of cd or export. In Git Bash, quote a backslash path or write C:/... — unquoted \ is an escape. ~/ is expanded on every platform. WSL uses /mnt/c/.... FACTHOUSE_DATA on an MCP snippet applies only to that server process. A terminal facthouse command needs --data, FACTHOUSE_DATA in the environment that shell inherits, or a .facthouse store in this project. Hooks do not see mcp.json env.
npm install -g @facthouse/mcp@0.31.0
facthouse init --yesnpx -y -p "@facthouse/mcp@0.31.0" -- facthouse init --yes
npx -y -p "@facthouse/mcp@0.31.0" -- facthouse settings --json
npx -y -p "@facthouse/mcp@0.31.0" -- facthouse stats
npx -y -p "@facthouse/mcp@0.31.0" -- facthouse inspectJob | Use |
MCP server (what the client starts) | The JSON snippet: |
|
|
Change extra knobs later |
|
One CLI command, no PATH |
|
facthouse init [dir]
The walk-through is how a human first-run writes config.json. Skip it and the server still creates the directory on first MCP boot.
On a terminal, init asks data directory, copy transcripts vs assistant records (default copy), semantic search, and More settings. --yes never prompts and leaves sources empty. --web prints a 127.0.0.1 URL and does not open a browser; --yes refuses --web. On a terminal, --force still asks those questions, then replaces the whole file; --yes --force is the silent reset. --force does not merge with the previous file.
facthouse init --yesfacthouse init --yes ~/my-memoryfacthouse init --yes --forceThe generated config.json is where you change consolidation behaviour — most notably intelligence.provider (cli by default; heuristic for a zero-dependency regex fallback, or FACTHOUSE_PROVIDER=heuristic at runtime). Init does not ask that field.
facthouse settings
Change extra knobs on an existing config.json (CLI model, timeout, optional local extract). Does not reset the rest of the file. Refuses if there is no config.json (this command does not create a store). --json / not a terminal prints the current knobs and does not write. --web is the same knobs on a local page (print URL, no auto-open).
facthouse settingsfacthouse settings --data ~/my-memoryfacthouse record
Inserts events directly into the database (no running server needed). Supported for demos and for stores that have no named source. Not the Claude Code or Cursor default — that is sources plus facthouse consolidate.
# From a hook (reads JSON payload from stdin):
echo '{"hook_event_name":"UserPromptSubmit","prompt":"hello"}' | facthouse record --role user
# With explicit content:
facthouse record --role user --event-type message --content "hello world"
# Options:
# --role user | assistant | system | tool (default: user)
# --event-type message | tool_call | tool_result | artifact (default: message)
# --content-type text | json | image | audio | binary (default: text)
# --content Event content (or pipe via stdin)
# --speaker Named participant when the transcript has one
# --session-id Target session (default: most recent)
# --data Data directory (default: FACTHOUSE_DATA, a .facthouse store in this project, or ~/.facthouse)facthouse consolidate
Copy new lines from config.sources, extract candidate facts from them, and integrate the pending facts into knowledge. The one command that spends model calls:
facthouse consolidate
facthouse consolidate --copy # copy only; spends nothing
facthouse consolidate --integrate # pending facts to knowledge; no extract pass
facthouse consolidate --all # extract the whole backlog now
facthouse consolidate --limit 200 # extract the oldest 200
# Steps — named steps run, in order; none named means all three:
# -c, --copy copy new transcript lines into the store
# -e, --extract turn new lines into candidate facts (the model call)
# -i, --integrate classify, link, dedupe, supersede, embed
# Extract is capped at 50 lines per run so a first backfill is never spent on
# the lot; the run says how many remain. --all lifts the cap, --limit N sets it.
# --json print the result object instead of the summary
# --data Data directory (default: FACTHOUSE_DATA, a .facthouse store in this project, or ~/.facthouse)Honours the configured provider (by default claude -p). Empty sources makes the copy step a no-op. Set cwd on the source unless you intend to copy every project group. Do not also run record hooks on a store with named sources.
facthouse notify <moment>
Tell the running MCP server that a moment happened. The server decides what to run and does it in the background, so a hook returns at once:
facthouse notify compaction # client about to compact: copy new JSONL, extract, integrate (does not wait)
facthouse notify threshold # events arrived: extract if the threshold is due
# Options:
# --data Data directory (default: FACTHOUSE_DATA, a .facthouse store in this project, or ~/.facthouse)No server listening is not an error: the command says so and exits 0, and the next session start covers it. This is what the PreCompact hook calls.
facthouse search <query>
facthouse search "coffee"
facthouse search "coffee" --domain preferences
facthouse search "coffee" --json
# Options:
# --domain Prioritise a domain. Biases ranking; does not filter
# --limit Maximum results (default: 20)
# --json Emit the raw search payload
# --data Data directory (default: FACTHOUSE_DATA, a .facthouse store in this project, or ~/.facthouse)--domain biases ranking rather than filtering. A hard filter would hide a fact filed under a near-synonym.
facthouse stats
facthouse stats
facthouse stats --jsonFacts are immutable — superseded facts are kept — so the current count and the total legitimately differ once anything has been superseded. --json includes the answering binary's package version. Intelligence spend is calls, tokens, and elapsed time for extract / classify / entities / reconcile / supersede / summarise, with provider and model per stage. Embeddings are not that number.
facthouse inspect
Sample D, I, K, entities, and the graph. Writes a local HTML file under the data directory (not the cwd). Prints the path. Does not open a browser. The file is a memory export — treat it like stats --json. The same page also shows intelligence spend (Graph / Spend).
facthouse inspect
facthouse inspect --graph
facthouse inspect --layer k
facthouse inspect --json
facthouse inspect --entity Helios --limit 20 --output ~/inspect.html--layer health|d|i|k|entities|graph|all prints terminal tables (newest-first, capped). --graph (the default when no --layer / --json) writes inspect.html. --limit is 10 for tables and 50 for the canvas. --all draws every node — a hairball, explicit. Search and type filter in the page can still reach a node that was outside the cap.
Advanced
Another store
The store is this directory. Clients share it by using the same path. A second store is a second directory, not a second install. The default MCP server name is facthouse. Splitting is not a filter on which client wrote the row. Work and personal is one reason to split, not a required setup.
A non-default data directory prints a distinct MCP server name so two stores can share one mcp.json. Init against each extra directory prints that snippet. Example:
{
"mcpServers": {
"facthouse-personal": {
"command": "npx",
"args": ["-y", "@facthouse/mcp@0.31.0"],
"env": { "FACTHOUSE_DATA": "C:\\Users\\alex\\.facthouse-personal" }
},
"facthouse-work": {
"command": "npx",
"args": ["-y", "@facthouse/mcp@0.31.0"],
"env": { "FACTHOUSE_DATA": "C:\\Users\\alex\\.facthouse-work" }
}
}
}Point each store's sources.cwd (or hook --data) at that store only. Two directories do not isolate anything if both copy the same home.
Postgres (optional)
SQLite is the default and needs no extra software. To use Postgres instead, set storage.provider to "postgres" in that store's config.json, or FACTHOUSE_STORAGE=postgres on the MCP entry, and set FACTHOUSE_POSTGRES_URL to a postgres:// (or postgresql://) URL. The password belongs in the environment, not in config.json. If the URL is missing or the server cannot be reached, Facthouse stops; it does not create a SQLite file.
The data directory is still the memory: config.json and the scheduler socket live there. Tables live at the URL. Two memories need two directories and two databases.
Init does not ask which engine to use. facthouse init --yes still writes sqlite.
Example — placeholders only; do not put a real password in a committed file:
{
"mcpServers": {
"facthouse": {
"command": "npx",
"args": ["-y", "@facthouse/mcp@0.31.0"],
"env": {
"FACTHOUSE_DATA": "C:\\Users\\alex\\.facthouse-work",
"FACTHOUSE_STORAGE": "postgres",
"FACTHOUSE_POSTGRES_URL": "postgres://USER:PASSWORD@localhost:5432/facthouse"
}
}
}
}Copy versus record
Choose one mechanism per store.
Recommended — copy. Name a claude-code or cursor source (set cwd) and run facthouse consolidate from the CLI first. The MCP server also copies at session start and when it handles a call. Grok and Codex are later adapters. Unknown kind values are rejected.
{
"sources": [
{
"kind": "claude-code",
"home": "~/.claude",
"cwd": "C:\\dev\\app"
}
]
}home is the client config dir (~/.claude or ~/.cursor — path examples, not extra discovery). Cursor is "kind": "cursor" and home/projects/*/agent-transcripts/**/*.jsonl only — not Composer SQLite. Cursor encodes C:\\dev\\app as c-dev-app (Claude Code uses C--dev-app). A first backfill of more than 50 lines takes several runs, or one facthouse consolidate --all.
Alternative — record, no sources. Leave sources empty. Pipe a client hook payload into facthouse record if you have one. MCP log_event / capture_fact keep working.
Do not install record hooks on this store — both write the same rows. Facthouse does not detect or rewrite existing hook configs.
MCP-only record mode
To skip the wizard (record only — no transcript copy), paste this. The server creates ~/.facthouse on first boot; you are not asked those questions.
{
"mcpServers": {
"facthouse": {
"command": "npx",
"args": ["-y", "@facthouse/mcp@0.31.0"]
}
}
}Hooks
PreCompact notify compaction is the useful one: when the client is about to compact, a short-lived hook tells the running server, and the hook returns at once. The server copies new JSONL lines, then extracts and integrates — compaction does not delete the transcript, and the hook does not photocopy the live window. Init prints this JSON with --data filled in. Paste into Claude Code .claude/settings.json (user or project). We do not install it. Do not install a Stop hook. Do not install record hooks on this store — both write the same rows.
mcp.json env is not visible to hooks. Pass the same --data (or set FACTHOUSE_DATA in the environment the client itself inherits). The command must invoke the CLI (facthouse), never the server binary. npx -y @facthouse/mcp with no -p / facthouse starts the MCP server and hangs a hook. Pin the package version, quote it if the hook runs PowerShell, and put -- before facthouse so a globally installed older binary on PATH cannot win.
{
"hooks": {
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "npx -y -p @facthouse/mcp@0.31.0 -- facthouse notify compaction --data /absolute/path/to/the-same-store"
}
]
}
]
}
}PreCompact notify compaction asks the running server to consolidate: copy the newest JSONL lines, extract, integrate. The hook returns at once; the server does the work. We do not install a turn-end Stop hook. On Windows the --data path is the same absolute directory you put in FACTHOUSE_DATA (for example C:\\Users\\alex\\AppData\\Local\\Temp\\facthouse-try).
Frequent incremental copying interleaves conversations on the global sequence: a long chat kept open is sliced between other chats. Extract progress is per conversation, so a timeout in one chat does not discard another. Shrinking extraction.batch_size means more extract calls (more chances of a timeout), not a store-wide hold-all. facthouse stats reports unextracted events against that extract watermark.
If the MCP server does not start, or lists no tools, check the package version the client actually spawned. A global facthouse on PATH can be years behind the pin in this README. Diagnose with facthouse stats --data <dir> (the CLI prints whether the scheduler is listening) and by inspecting serverInfo.version from initialize plus tools/list over stdio. 0.2.x answers initialize then throws on tools/list.
Embeddings, model, timeout, bitemporal
Set embedding.provider in config.json to "ollama" (local, no API key) or "voyage" (hosted), run facthouse consolidate, and search "food" starts returning the allergy. Facts are embedded when they are consolidated. Voyage applies a 3 requests/minute rate limit until a payment method is on the account.
Meaning-search is an exact scan of stored vectors when the set is small. When that set is large (default 32 MiB of the current model), an HNSW index of those vectors is used instead: in-process on SQLite, or a Postgres vector sidecar when the extension is enabled. Small stores stay exact. A missing engine keeps exact search and prints a warning; Facthouse does not install a native addon. embedding.ann is null (auto), false (never), or true (force when the engine allows). This does not turn embeddings on.
intelligence.cli.model and intelligence.cli.timeout_ms are extra knobs. First-run More settings (Y) can write them; later, facthouse settings. Init does not ask intelligence.provider; FACTHOUSE_PROVIDER=heuristic is the kill-switch. The heuristic fallback does not extract facts from transcripts.
Unnamed user-channel speech is attributed to the store's owner; a display name still does not create a person. Extra backing (assent, a tool observation, a different speaker restating) is recorded, not scored, unless the store sets interlocutor ranking weights in config.json. The engine ships none. Weight keys match the speaker string as stored, so two people with the same name share a key.
Set temporal.mode to bitemporal to record when the system retracted a belief, so search can answer what the store believed at an instant.
Intelligence spend
facthouse stats and get_stats report billed consolidation calls: tokens, elapsed time, and the provider plus model on each stage (extract, classify, entities, reconcile, supersede, summarise). A run that did not report tokens omits those fields rather than showing zero. Embeddings are a different API and are not this number.
Optional intelligence.token_budget caps billed extract per provider on rolling windows. Unset is unlimited. Over the cap, consolidate skips extract, holds the watermark, and does not fall back to the heuristic. Stats and inspect Spend show used and remaining on each cap, and when oldest usage in that window ages out (resets).
"intelligence": {
"token_budget": {
"cli": { "week": "10M" }
}
}hour, day, week, and month are rolling. Omit a scale to leave it unlimited. Remaining room is on facthouse stats, get_stats, and inspect Spend. Set the cap in this store's config.json — there is no budget command.
Optional local intelligence is a different switch from embeddings. Add intelligence.http on an OpenAI-compatible host. The protocol is POST /v1/chat/completions; only the port changes:
Host | Typical URL |
Ollama |
|
LM Studio |
|
vLLM |
|
llama.cpp |
|
The model string is whatever that host lists. GET {base_url}/models prints the names. nomic-embed-text is embed-only and will not extract. If the host is up and serves exactly one chat model, Facthouse uses it for this run and tells you to pin intelligence.http.model. If several chat models are listed, set that field; extract will not guess.
Extract and summarise then use that host; reconcile and supersede stay on the CLI unless you list intelligence.stages. Each stage can set on-fail to cli, http, or none (see the JSON below). HTTP extract defaults to retrying on the CLI (counts against the CLI token budget). Contradiction defaults to none — no provider switch. none holds the extract watermark — it does not fall through to the heuristic. First-run More settings (Y, after the recommended path) can set the host, model, and extract on-fail. Later, facthouse settings merges those knobs into an existing file without resetting it. facthouse inspect Spend shows the same knobs and copies JSON; it does not save config.json.
The live script npm run test:http-intelligence has passed on qwen2.5vl:7b.
"intelligence": {
"http": {
"base_url": "http://localhost:11434/v1",
"model": "qwen2.5vl:7b"
},
"stages": {
"extract": { "provider": "http", "on_fail": "cli" },
"summarise": { "provider": "http", "on_fail": "cli" },
"reconcile": { "provider": "cli", "on_fail": "none" },
"supersede": { "provider": "cli", "on_fail": "none" }
}
}CLI demo (no transcript source)
Throwaway store, not the capture path for a real Claude Code or Cursor home. These three lines are typed in.
export FACTHOUSE_DATA=/tmp/facthouse-demo
om() { npx -y -p "@facthouse/mcp@0.31.0" -- facthouse "$@"; }
om init --yes
om record --role user --content "I prefer dark mode in every editor, and I never want telemetry enabled."
om record --role user --content "I am allergic to shellfish, so avoid seafood restaurants when booking anything."
om record --role user --content "My colleague Robin at Acme is leading the Atlas migration project this quarter."
om consolidate
om search "Atlas"
om stats$env:FACTHOUSE_DATA = Join-Path $env:TEMP "facthouse-demo"
function om { npx -y -p "@facthouse/mcp@0.31.0" -- facthouse @args }
om init --yes
om record --role user --content "I prefer dark mode in every editor, and I never want telemetry enabled."
om record --role user --content "I am allergic to shellfish, so avoid seafood restaurants when booking anything."
om record --role user --content "My colleague Robin at Acme is leading the Atlas migration project this quarter."
om consolidate
om search "Atlas"
om statsallergies is not a domain Facthouse ships. The engine has no built-in vocabulary — it read the conversation and decided that fact needed a home. A domain biases ranking rather than filtering. Clean up: rm -rf /tmp/facthouse-demo (Git Bash / macOS / Linux) or Remove-Item -Recurse -Force $env:TEMP\facthouse-demo (PowerShell).
Integration
Facthouse's tool descriptions tell assistants when to search and when a correction is worth staging. They are not how Claude Code conversations enter the store — that is copy from a named source.
Without configuration
Claude Code or Cursor: name a sources entry (set cwd) and run facthouse consolidate from the CLI first. MCP session start also copies. capture_fact is there if the assistant needs to correct or add something copy-plus-extraction will not produce.
Clients with no copy adapter still rely on log_event / capture_fact until their adapter exists.
Hook points
Hook point | When | What to call | Why |
Session start | Conversation begins |
| The assistant knows who you are from message one |
Correction | A durable fact is missing from the store |
| Optional; Claude Code conversations are already in |
Pre-response search | Before generating a reply |
| Responses informed by stored knowledge |
Pre-compaction | Before context window compression |
| The server copies new lines, extracts, integrates |
Natural breakpoints | Topic change, task completion |
| Keeps the knowledge graph current |
On pre-compaction: facthouse notify compaction asks the server to consolidate. It is not a record hook.
Claude Code
Create .claude/rules/facthouse.md in your project (or ~/.claude/rules/facthouse.md globally):
# Facthouse
- Conversations are copied from the named Claude Code source (first backfill: `facthouse consolidate` on the CLI)
- Do not install record hooks on this store — both write the same rows.
- Identity context loads automatically from the `memory://profile` resource — no tool call needed
- Before answering questions this store might already know, call `search_knowledge`
- Call `capture_fact` only to correct or add something that is not in the transcript
- When the conversation is getting long, call `consolidate` (or rely on PreCompact `facthouse notify compaction`)
- At natural breakpoints (topic change, task completion), call `consolidate` to keep the knowledge graph currentTo allow Facthouse tools without per-call approval prompts, add to the permissions.allow array in .claude/settings.json:
{
"permissions": {
"allow": [
"mcp__facthouse__*"
]
}
}Cursor / Windsurf
On Cursor Directory, Add to Cursor is per component: click it on the MCP server and again on the rule. That boots a record store at ~/.facthouse — no facthouse init. The Hooks tab is Copy into ~/.cursor/hooks.json, not Add to Cursor. To copy Agent transcripts, run facthouse init, pick copy, kind cursor.
Add to .cursorrules (Cursor) or .windsurfrules (Windsurf) in your project root:
When the facthouse MCP server is available:
- At the start of every conversation, before answering, call get_session_context unless you already loaded the memory://briefing resource. That call returns the same working briefing the resource would have injected. Tools-only clients never fetch resources.
- Before answering questions this store might already know, call search_knowledge
- To find out everything known about a particular person, project, or thing, call get_entity
- Call capture_fact only to correct or add something copy or extraction missedCursor and Windsurf consume tools but not resources, so memory://profile will not load on its own there. Cursor conversations themselves are copied with kind: "cursor" (JSONL under ~/.cursor/projects/, not the SQLite composer store).
Claude Desktop / other MCP clients
No copy adapter yet. Tool descriptions handle search and optional capture_fact; conversations are not tailed until a later adapter exists.
Reclaiming space
Facthouse logs raw conversation and tool output to session_events. On a store wired into an agentic client this becomes almost all of the database. A store measured in daily use held 47,000 events and 493 MB against 21 integrated facts.
facthouse stats reports the raw layer alongside the knowledge, including how much is reclaimable. To reclaim it:
facthouse prune # report only — nothing is deleted
facthouse prune --apply --vacuum # delete, then rebuild the fileSet retention.disk_budget in config.json to a size such as "2GB" to cap memory.db. Unset is unlimited; init does not write a cap. When a cap is set and the file is full, unreachable raw events are pruned automatically so new logs can reuse that space; if nothing unused remains, more raw events are refused. Facts are never deleted to meet the number. Compacting (--vacuum) is still a human step — it copies the whole file so the operating system sees the smaller size.
If most of that volume is tool output you judge to be noise, extraction.event_types and extraction.roles restrict what is examined, and extraction.min_content_length skips trivial events. Measure before you do. Volume and value are not the same axis.
The rule is reachability, not age. An event is removed only when all three hold:
Extraction has already read it. Anything ahead of the consolidation watermark is still input.
No fact's provenance cites it.
It has fallen outside its own session's most recent
extraction.working_memory_sizeevents — a spare so consolidation can still glance at recent raw notes. That window is evidence of the current topic, not a pronoun dictionary.
No fact, entity, embedding or search result is affected. Deleting rows does not shrink the file on its own — that is --vacuum. Without a cap, nothing prunes automatically.
Development
git clone https://github.com/gordonkjlee/facthouse
cd facthouse
npm install
npm run build
npm testnpm test always runs hermetic pipelines (fixture JSONL → copy → extract →
search) with a recording extractor, and skips live evals that need a real
model:
Semantic recall needs Ollama with
nomic-embed-text. Start it, thennpm run test:semantic.The live first-fact eval needs the
claudeCLI. Runnpm run test:first-fact.The live coding-store eval (warehouse-shaped Cursor transcripts) also needs the
claudeCLI. Runnpm run test:coding-store.Local HTTP extract needs a chat model on an OpenAI-compatible host and
FACTHOUSE_HTTP_MODEL(verified onqwen2.5vl:7b). Runnpm run test:http-intelligence.
Each of those scripts fails rather than skips when its dependency is missing, so a green run means the claim was actually verified rather than quietly stepped over.
Contribute
Issues and pull requests are welcome. Open an issue first if the change is more than a typo.
Questions: GitHub Discussions
How to build and test: CONTRIBUTING.md
License
MIT
Available Tools
10 toolscapture_factA
Store a durable fact worth remembering across sessions. A durable fact is a stable piece of knowledge about whatever this store is used for: its subjects, their attributes, their relationships, decisions, and context. Ignore ephemeral statements (current tasks, transient mood). Call this proactively whenever you learn something this store should keep.
Capture is fast — the server stores the fact immediately. Entity extraction, domain classification, and cross-session reconciliation run in batch when you call consolidate. Capture frequently without slowing the conversation.
Exact same-session duplicates are dropped immediately. Cross-session exact duplicates are also rejected during the next consolidation run — safe to capture the same fact from multiple conversations without polluting the knowledge graph.
| Name | Required | Description | Default |
|---|---|---|---|
| content | Yes | The fact to capture | |
| confidence | No | How confident (0.0–1.0) | |
| importance | No | How important (0.0–1.0). High for medical/safety, low for casual preferences | |
| domain_hint | No | Suggested domain. Domains are whatever this store uses, not a fixed list — call get_schemas to see them, reuse an existing one where it fits, and propose a new short lowercase noun when none does. Omit it and the server will classify. | |
| capture_context | No | What the conversation is about right now | |
| source_event_id | No | ID of the event that prompted this capture |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the transparency burden. It discloses immediate server-side storage, batch processing during consolidation, and detailed deduplication behavior (same-session immediate drop, cross-session rejection at consolidation). It adequately conveys side effects and timing without hiding any obvious operational details.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description spans three paragraphs and repeats deduplication information across the second and third paragraphs. While structured and readable, it could be tightened by merging overlapping statements without losing key guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of an output schema, the description appropriately focuses on input semantics, storage behavior, and deduplication. It covers the asynchronous consolidation aspect and references get_schemas for domain exploration, making the tool's behavior fully understandable in context. Minor gaps like error handling or return behavior are absent but not critical here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful extra guidance beyond the schema, such as explaining that domain_hint is not a fixed list (referencing get_schemas) and that importance is high for medical/safety versus low for casual preferences, which clarifies parameter usage beyond the short schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Store a durable fact worth remembering across sessions' with a specific verb and resource, and explicitly distinguishes from ephemeral statements. It effectively differentiates this write tool from sibling query and event tools by focusing on stable knowledge.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance ('Call this proactively whenever you learn something this store should keep') and when-not-to-use ('Ignore ephemeral statements'). It also reassures frequent use with latency and deduplication notes, though it does not directly compare to alternative sibling tools like log_event.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
consolidateA
Copy, extract, and integrate. Copies new lines from named sources, extracts candidate facts from them, and integrates pending facts: domains, entities, duplicates, contradictions, the knowledge graph.
Call this after capturing several facts, at a topic change, or before the conversation ends.
Extract is capped at 50 of the oldest unexamined lines per call; events_remaining in the result says how many wait. Pass all: true to take the whole backlog in one call, or limit: N for the oldest N.
When meaning search is on, integrate also embeds currently-true facts that have no vector for the working model. A result with facts_integrated: 0 can still have written vectors — read embedding (embedded, missing, error). Call get_stats for store-wide coverage.
| Name | Required | Description | Default |
|---|---|---|---|
| all | No | Extract the whole backlog this call instead of the capped oldest batch | |
| limit | No | Extract at most this many of the oldest unexamined lines |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations at all, the description carries the full disclosure burden and does so richly: it explains the 50-line cap, the events_remaining counter, whole-backlog vs. limited extraction, meaning-search embedding behavior, and the subtle case where facts_integrated: 0 can still produce written vectors. This is far beyond a generic 'consolidate' statement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well organized: a one-line summary, a when-to-call sentence, extraction mechanics, and embedding behavior. Every sentence contributes operational information, and the most important usage guidance is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema and no annotations, the description covers the key result fields an agent needs (events_remaining, facts_integrated, embedding) and refers to get_stats for store-wide coverage. It adequately equips the agent to call the tool correctly and interpret its results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is already 100%, the description adds real semantics to both parameters: all: true means 'take the whole backlog in one call' and limit: N means 'the oldest N' unexamined lines. It also ties them to the extraction cap, which is not evident from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a concrete verb phrase, 'Copy, extract, and integrate,' then specifies exactly what the tool does: copies lines from named sources, extracts candidate facts, and integrates pending fact types (domains, entities, duplicates, contradictions, knowledge graph). This is specific enough to distinguish it from siblings like capture_fact and get_events.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit trigger conditions: 'Call this after capturing several facts, at a topic change, or before the conversation ends.' It also explains how to vary behavior with all/limit and points to get_stats for broader coverage, giving the agent clear directives on when and how to invoke it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_contextA
Get everything known about a topic, combining search with entity relationship traversal. More comprehensive than search_knowledge — it follows entity connections outward: from a named subject to the things it relates to, and the facts about those in turn.
Call this when you need the COMPLETE picture of a topic, subject, or domain rather than a specific fact — planning something involving a person, project or system, catching up on a subject, or answering an open-ended question about any of them.
Prefer search_knowledge when you want one fact fast; prefer this when missing a connection would make your answer wrong.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | Yes | Topic, person, project, or domain to explore |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It implies a read-only operation through 'Get everything known' and 'traversal,' but does not explicitly state the absence of side effects, auth requirements, or rate limits. Given the tool name and context, a slight inference is needed, but it is acceptable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured into two paragraphs: the first explains functionality, the second provides usage guidance. Every sentence contributes necessary information, and there is no fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema is present, so the description need not specify return format. It adequately describes the scope of results (facts and connections) and provides enough context for an agent to decide when to invoke the tool. Slight ambiguity remains about the exact structure of the returned data, but it is not critical.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema description for 'topic' is present and clear ('Topic, person, project, or domain to explore'), providing 100% coverage. The tool description further elaborates on the parameter by relating it to the tool's purpose, adding moderate value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Get everything known about a topic, combining search with entity relationship traversal.' It also explicitly differentiates from search_knowledge, making the tool's unique value obvious.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Call this when you need the COMPLETE picture...' and 'Prefer search_knowledge when you want one fact fast; prefer this when missing a connection would make your answer wrong.' This leaves no ambiguity about the appropriate scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_entityA
Get everything known about a named thing — who or what it is, the facts about it, and how it connects to other things.
A "thing" is any subject this store holds knowledge about: a person, an organisation, a project, a place, a product, a system — whatever the store is used for. This is the "tell me about X" tool.
Call this WHENEVER a named thing is mentioned or alluded to and knowing it would improve your answer — including indirect references like "my manager", "the Helsinki office", "the payments service". Call it before advising on anything involving that thing, and before asking who or what something is — you may already know.
Facts come back most relevant first, each flagged with is_subject. True means the fact is ABOUT this thing; false means it only mentions it. Treat the difference as real when you answer: "Alex's transfer was approved by Robin" is worth knowing when asked about Robin, but it is a fact about Alex, and reporting it as something you know about Robin would be wrong. Other relationship values are the same kind of role — this entity's part in this fact, free text, not a directed graph edge. Do not infer who did what to whom from the wording.
If several entity rows share that name under different types (the extractor labelled one thing two ways), facts from all of them come back. Hyphens, underscores, and stray punctuation count as the same letters only when that does not join two names already stored as separate rows. If this store has no entity by that name, facts that mention the wording still come back (is_subject false) rather than an empty miss. found is whether an entity row exists, not whether anything is known.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | The thing's name. Resolve an indirect reference to a name first if you can (e.g. via get_context or a prior fact). | |
| type | No | Optional type filter, only for disambiguation when one name refers to two different things (a person and a project both called 'Mercury'). Types are whatever this store uses — omit it to match any type, which is almost always what you want. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses edge cases: multiple types sharing a name return combined facts, punctuation normalization rules, missing names returning mention facts with is_subject false, and the definition of 'found'. This gives the agent a precise behavioral model.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
While long, every sentence adds essential information about behavior, edge cases, or parameter interaction. The main purpose is front-loaded, and subsequent details are logically organized, earning their place without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description explains return semantics (facts, is_subject, found) sufficiently. It also addresses ambiguity, missing entities, and type variance, making the tool's behavior fully predictable for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema descriptions cover both parameters thoroughly (name resolution guidance, type as a disambiguation filter). The description reinforces type behavior ('facts from all of them come back') and adds context on resolving indirect references, slightly exceeding the high coverage baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get') and resource ('everything known about a named thing'), and clearly distinguishes itself as the 'tell me about X' tool. It contrasts implicitly with sibling tools by focusing on entity retrieval with fuzzy matching and is_subject semantics.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'Call this WHENEVER a named thing is mentioned or alluded to' and 'Call it before advising on anything involving that thing, and before asking who or what something is.' It also explains behavior for missing entities and ambiguous names, reducing guesswork.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_eventsA
Retrieve events from the current or a previous session. Returns the raw episodic record — messages, tool calls, tool results, and artifacts in sequence order. Use this to recall what happened earlier in a conversation (especially after context compaction), or to review a previous session.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum events to return (default 50). | |
| session_id | No | Session to query (matches MCP or client session ID). Omit for the current session. | |
| after_sequence | No | Only return events after this sequence number (for pagination). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It transparently describes what the tool returns (raw episodic record in sequence order) and the kind of content included. It does not mention side effects, but this is a read-only retrieval operation and the description makes that implicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no redundant information. The first sentence states purpose and return content; the second provides usage guidance. Every word contributes value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 3 simple parameters and no output schema, the description is complete: it explains what is returned, when to use it, and differentiates from sibling tools. No additional context is needed for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and each parameter already has a clear description (limit, session_id, after_sequence). The tool description itself adds no extra parameter semantics beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Retrieve events') and resource ('events from the current or a previous session'). Clearly differentiates from sibling tools by describing the raw episodic record (messages, tool calls, tool results, artifacts) rather than context or knowledge.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells when to use the tool: 'Use this to recall what happened earlier in a conversation (especially after context compaction), or to review a previous session.' This gives clear situational guidance without ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_schemasA
List the knowledge domains this user's memory actually uses, and their subdomains. The set is not fixed — beyond the core domains it grows to fit the user, so it is worth asking rather than assuming.
Call this before filtering a search by domain, before choosing a domain_hint for capture_fact, or when you want to know how this user's knowledge is organised. Rarely needed mid-conversation — search_knowledge and get_context work without it.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It does this well by revealing that the domain set is not fixed, grows to fit the user, and reflects domains actually used — non-obvious behavior that matters for an agent deciding whether to cache or assume domains. It does not mention return format or explicit read-only status, but the verb 'List' makes the basic behavior clear for a zero-parameter tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded. The first sentence states exactly what the tool does, the second explains why the result may vary, and the third gives concrete use cases and alternatives. Every sentence earns its place without repetition or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple, zero-parameter listing tool with no output schema and no annotations, this description provides everything an agent needs: what is returned, why the result is not static, when to call it, when not to call it, and which sibling tools to use instead. There are no significant gaps for correct selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters and the schema description coverage is 100%, so the schema fully documents the parameter surface. The baseline for a no-parameter tool is 4, and the description appropriately adds no unnecessary parameter-level detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: it explicitly says the tool will 'List the knowledge domains this user's memory actually uses, and their subdomains.' This clearly distinguishes it from sibling retrieval tools like search_knowledge and get_context, which retrieve content rather than enumerate the domain vocabulary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit guidance on when to call this tool: before filtering a search by domain, before choosing a domain_hint for capture_fact, or when wanting to understand how the user's knowledge is organized. It also explicitly states when it is not needed — rarely needed mid-conversation — and names alternatives that work without it: search_knowledge and get_context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_session_contextA
At the start of every conversation, before answering, call get_session_context unless you already loaded the memory://briefing resource. That call returns the same working briefing the resource would have injected. Tools-only clients never fetch resources.
Also returns facts captured in this session that have not been consolidated yet. Call it before re-capturing a fact you may have already stored this session.
| Name | Required | Description | Default |
|---|---|---|---|
| session_id | No | Session to query. Omit for the current session. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so description carries full burden. It discloses the two return payloads (briefing and unconsolidated facts), which is important context. However, it doesn't state side-effect freedom or any limitations (e.g., staleness, performance), and the read-only nature is only implied by the name.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tight but not terse: first paragraph states the mandatory use case and equivalence to memory://briefing; second paragraph adds the fact-dedup rule. Every sentence carries operational meaning, and the most important usage instruction is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-optional-param getter with no output scheema, it covers what, when, and why, plus a dup-avoidance nuance. It stops short of describing the response format of the briefing or facts, but an agent aware of memory://briefing has enough to proceed. No side-effect or error info is necessary for this simple call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers 100% of the single parameter with 'Session to query. Omit for the current session.' The description adds no extra param detail beyond referencing 'this session'; thus baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description uses specific verb 'returns' and names the resource: session context / working briefing. It clearly explains the tool returns the same briefing as memory://briefing plus unconsolidated session facts. It doesn't explicitly differentiate from sibling get_context, so not a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit when: at the start of every conversation before answering, unless memory://briefing already loaded. It also gives a specific trigger for the facts portion: call before re-capturing a fact that may already be stored. This is direct, actionable guidance with an exclusion condition.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_statsA
Get knowledge base statistics — how many facts are currently true, how many are held in total including superseded history, entity and domain counts, how facts are distributed across domains, and how much raw log can be reclaimed.
Call this when the user asks what you know or remember about them, how much you have stored, or whether their memory is working. This answers "how much do you know", not "what do you know" — use search_knowledge, get_entity or get_context for actual recall.
semantic is present when this store is configured for meaning search. semantic.stored is how many currently-true facts already have a vector for the working model (and dimension, when known). embeddings lists every vector group the store holds, including leftovers from a previous model — leftover rows do not serve meaning search. No semantic object means keyword-only, which is the default, even if embeddings still lists leftover rows. When semantic is present and semantic.stored is well below facts.active_latest, facts are findable by wording but not yet by meaning — call consolidate. Search does not wait for full coverage: unembedded facts still match on words.
extract.unextracted_events is how many transcript lines extract has not examined. pending_facts is I not yet integrated. A large unextracted count with a healthy fact count means capture is writing D that extract has not examined.
intelligence is billed consolidation spend (calls, tokens, elapsed), broken down by stage and provider for the last 24 hours, all time, and the last few runs. Embeddings are not this number — they are a separate API. Token fields are omitted when the provider did not report them, not shown as zero.
token_budget is remaining room under optional intelligence.token_budget caps (per billed provider, rolling hour / day / week / month). Unset means unlimited. Over the cap, consolidate skips extract, holds the watermark, and does not fall back to the heuristic.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description reveals the core behavior — it returns all calls, unpaginated, with no filtering — which is valuable given the readOnlyHint annotation. However, it does not disclose full output shape, potential size limits, or whether the endpoint accepts optional filters. The safety profile is partially covered by the annotation, but pagination and response structure are left unspecified.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler, and the critical scoping constraint ('no user/workspace filtering – use the other tool') is front-loaded. The warning about timezone is a single short clause. Very efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists for this tool, and the description does not describe the response shape, pagination, or failure modes. The tool's main behavioral constraint, unbounded date range, is stated, but an agent cannot fully anticipate what comes back. Since annotations are absent too, the description must carry more weight than it does.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Both parameters are trivially explained by their names and types in the schema (date/time strings), and the description does not add much beyond restating them. The one genuinely useful addition is the timezone expectation, which the schema does not convey. This raises the score slightly from a baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and object: 'List ALL calls in date range – no user/workspace filtering.' It not only states what the tool does but immediately distinguishes it from the filtering alternative, search_calls_extensive. The name alone would be ambiguous; this description removes all doubt.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent when this tool is appropriate ('no user/workspace filtering') and when it is not, pointing directly to the sibling tool that does filter. This is clear routing guidance that an agent can act on without opening any schema.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
log_eventA
Record a raw exchange — a user message, your response, a tool call, a tool result, an artifact — as the episodic record consolidation later extracts facts from. Logging both sides gives that extraction the context to know what a reply refers to.
Where the client has hooks configured, every event is logged for you automatically and you do not need to call this at all. Call it when there are no hooks, or when an exchange matters enough to preserve verbatim — a decision, a correction, a specification — and you want it recorded whether or not hooks are running. Duplicates are reconciled at consolidation, so logging something twice is safe.
To store a fact you already know, use capture_fact instead: this tool records what was said, not what it means.
| Name | Required | Description | Default |
|---|---|---|---|
| role | Yes | Channel that produced this event (user, assistant, system, tool). For a named person on a group transcript, keep role as user and pass speaker. | |
| content | Yes | Text content of the event | |
| speaker | No | Named participant when the transcript has one (e.g. Alex). Role stays the channel — do not invent a person role. | |
| metadata | No | Arbitrary metadata | |
| event_type | Yes | Type of event | |
| content_ref | No | URI or path for non-text content | |
| content_type | No | How to interpret the content. Defaults to 'text' for messages. Use 'json' for structured data, 'image' for screenshots or generated images, 'audio' for voice or audio clips, 'binary' for anything else non-text. | text |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses that it records raw exchanges, that duplicates are reconciled at consolidation, and that it is for episodic memory. It also clarifies the distinction from fact storage, giving a clear behavioral picture despite lacking annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, using three short sentences that sequentially cover purpose, usage conditions, and alternative tool. It is well-structured and free of unnecessary detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description gives sufficient context for an agent to decide when to invoke the tool, what parameters to fill, and how it differs from related tools. It even mentions automatic logging when hooks exist, which is important operational context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
All 7 parameters have descriptive names and schema-level explanations. The tool description adds practical guidance for ambiguous cases, such as using the 'speaker' parameter for named participants and defaulting content_type to 'text'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool records a raw exchange for episodic memory, using the verb 'record' and specifying the resource (raw exchange). It also differentiates from capture_fact and explains its role in consolidation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is provided: call when no hooks are configured or when an exchange matters enough to preserve verbatim; otherwise, it is automatic. It also notes duplicates are safe and directs to capture_fact for storing facts instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_knowledgeA
Search the knowledge base. Call this BEFORE answering questions that might benefit from what this store knows. If you have not called get_session_context (or loaded memory://briefing) this conversation, do that first — otherwise you start without this store's context. Returns facts ranked by relevance with source attribution and confidence scores.
Three fields come back. results is integrated knowledge: deduplicated, reconciled against everything else known, entities resolved. Each result carries speaker_role when the primary event is known (user, assistant, system, or tool) and speaker when the transcript named the person — who uttered it, not who it is about. pending is what was captured recently and not yet consolidated — real, and usually the most recent thing you were told, but not yet checked against existing knowledge, so it may duplicate or contradict a fact in results. Trust results first; use pending to avoid forgetting something you were told minutes ago. episodes is filled only when results are empty: a short raw-log window around a keyword hit in the copied transcript, not yet extracted. It is not knowledge of the same standing — do not report it as an integrated fact.
When semantic search is enabled, results also matches on meaning, so a query can surface a fact that shares none of its words. pending and episodes never do — they are keyword-only. A just-captured fact is findable by its own words but not yet by a paraphrase of them.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | What to search for | |
| domain | No | Prioritise a domain. Domains are whatever this store uses — they are not a fixed list, so call get_schemas to see them rather than guessing. This biases ranking rather than filtering: facts in the domain are surfaced and rank higher, but a strong match elsewhere still appears. Domains are assigned by a classifier and are approximate, so a hard filter would hide a fact filed under a near-synonym. Omit it to search everything. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries full behavioral disclosure, and it delivers: it explains the three return fields, the reliability difference between results and pending, the conditions under which episodes is filled, and the semantic-vs-keyword matching behavior. It also cautions that episodes should not be reported as integrated facts. This is exemplary transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place: purpose is front-loaded, usage guidance follows, then return semantics are explained in a structured, scannable way. The prose moves from what to do first, to what comes back, to how to interpret edge cases. No filler is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must explain what the tool returns — and it does so thoroughly, covering results, pending, episodes, and the semantic-search distinction. It also addresses the prerequisite relationship with get_session_context, making the tool safe and effective to invoke in context. Given the tool's complexity, this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful nuance beyond the schema: it explains that query may be a paraphrase when semantic search is enabled, and that domain biases ranking rather than filtering, reinforcing the schema's own explanation. This helps an agent form better queries even though the schema already documents both parameters well.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb-resource pair: 'Search the knowledge base.' It then distinguishes its purpose from related operations by detailing what it returns (integrated knowledge vs pending vs episodes), which sets it apart from siblings like get_context and get_session_context. The role of the tool as the pre-answer retrieval step is explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool ('Call this BEFORE answering questions that might benefit from what this store knows') and gives a concrete prerequisite: call get_session_context or load memory://briefing first. It also explains how to interpret results when deciding whether to rely on pending vs results, giving clear operational guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.30.1- First observed
capture_fact - First observed
consolidate - First observed
get_context - First observed
get_entity - First observed
get_events - First observed
get_schemas - First observed
get_session_context - First observed
get_stats - First observed
log_event - First observed
search_knowledge
TDQS
Scored across 10 tools
Each tool maps to a distinct operation: raw event logging, durable fact capture, consolidation, statistics, session context, and retrieval/search. The three retrieval tools have clearly described boundaries, with get_entity for named things, get_context for broader relationship traversal, and search_knowledge for ranked fact lookup.
The naming is mostly consistent: get_* for reads and verb_noun combinations for writes like capture_fact and log_event. Minor deviations are search_knowledge, which uses search_ instead of get_, and consolidate, which lacks a noun object.
10 tools is well-scoped for a persistent knowledge and memory server. Capture, logging, consolidation, retrieval, stats, schemas, and session context each serve a distinct purpose without feeling padded or redundant.
The core memory lifecycle is covered: capture/log events, consolidate and reconcile facts, search and retrieve knowledge, inspect stats and schemas, and load session context. The main gap is the lack of an explicit forget/delete or manual fact-editing tool; corrections appear to rely on capturing contradictions and letting consolidation handle them.
Maintenance
Related MCP Connectors
Persistent, portable memory for AI assistants — your private memory graph, from any MCP client.
Your memory, everywhere AI goes. Build knowledge once, access it via MCP anywhere.
An MCP memory server. One memory your agents share — across models, devices and apps.
Persistent personal memory for AI assistants — save, search, and recall across every MCP client.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceOpen-source MCP memory server providing persistent, cross-platform context for AI tools via a knowledge graph with encrypted storage.21 npm13AGPL 3.0
- FlicenseNot gradedqualityDmaintenanceAn MCP server for managing persistent AI memory using hybrid search (keyword + semantic vector) with SQLite storage and offline-first local embeddings.-
- AlicenseNot gradedqualityDmaintenanceA portable MCP server providing a shared intelligent memory system for any MCP-compatible AI tool, enabling storage, retrieval, extraction, and governance of memories across sessions.18 npmMIT
- AlicenseNot gradedqualityBmaintenanceA self-hosted MCP server that provides a personal semantic memory layer for AI tools. It enables storing, searching, and managing memories using hybrid vector and keyword search, allowing AI assistants to recall information by meaning.MIT