Skip to main content
Glama

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.

npm CI License: MIT

Quick Start

Needs Node 22.5 or 24+.

npm install -g @facthouse/mcp@0.30.0
facthouse init

If 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_context is the same briefing as memory://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, or capture_fact

  • K (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)

consolidate

MCP tool or CLI. Caller waits.

yes

yes (cap 50)

yes

facthouse notify compaction

Other process (recommended PreCompact; we do not install). Does not wait.

yes

yes (cap 50)

yes

facthouse notify threshold

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 sources; the assistant calls capture_fact

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 init

Pick 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 as memory://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 empty sources it 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.enabled in 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 structure

  • get_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.30.0
facthouse init --yes
npx -y -p "@facthouse/mcp@0.30.0" -- facthouse init --yes
npx -y -p "@facthouse/mcp@0.30.0" -- facthouse settings --json
npx -y -p "@facthouse/mcp@0.30.0" -- facthouse stats
npx -y -p "@facthouse/mcp@0.30.0" -- facthouse inspect

Job

Use

MCP server (what the client starts)

The JSON snippet: npx with args -y and a pinned @facthouse/mcp@…. No global install.

facthouse on PATH

npm install -g @facthouse/mcp@… (same pin). Update it when you bump the snippet.

Change extra knobs later

facthouse settings (or settings --data <dir>). Does not reset the file.

One CLI command, no PATH

npx -y -p "@facthouse/mcp@…" -- facthouse …

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 --yes
facthouse init --yes ~/my-memory
facthouse init --yes --force

The 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 settings
facthouse settings --data ~/my-memory

facthouse 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 --json

Facts 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.30.0"],
      "env": { "FACTHOUSE_DATA": "C:\\Users\\alex\\.facthouse-personal" }
    },
    "facthouse-work": {
      "command": "npx",
      "args": ["-y", "@facthouse/mcp@0.30.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.30.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.30.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.30.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

http://localhost:11434/v1 (the default if you omit the URL)

LM Studio

http://localhost:1234/v1

vLLM

http://localhost:8000/v1

llama.cpp

http://localhost:8080/v1

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.30.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.30.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 stats

allergies 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

memory://profile (automatic), search_knowledge

The assistant knows who you are from message one

Correction

A durable fact is missing from the store

capture_fact

Optional; Claude Code conversations are already in session_events via copy

Pre-response search

Before generating a reply

search_knowledge, get_context

Responses informed by stored knowledge

Pre-compaction

Before context window compression

facthouse notify compaction

The server copies new lines, extracts, integrates

Natural breakpoints

Topic change, task completion

consolidate (optional)

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 current

To allow Facthouse tools without per-call approval prompts, add to the permissions.allow array in .claude/settings.json:

{
  "permissions": {
    "allow": [
      "mcp__facthouse__*"
    ]
  }
}

Cursor / Windsurf

Add to .cursorrules (Cursor) or .windsurfrules (Windsurf) in your project root:

When the facthouse MCP server is available:
- 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 missed
- When context is getting long, call consolidate

Cursor 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 file

Set 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:

  1. Extraction has already read it. Anything ahead of the consolidation watermark is still input.

  2. No fact's provenance cites it.

  3. It has fallen outside its own session's most recent extraction.working_memory_size events — 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 test

npm 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, then npm run test:semantic.

  • The live first-fact eval needs the claude CLI. Run npm run test:first-fact.

  • The live coding-store eval (warehouse-shaped Cursor transcripts) also needs the claude CLI. Run npm run test:coding-store.

  • Local HTTP extract needs a chat model on an OpenAI-compatible host and FACTHOUSE_HTTP_MODEL (verified on qwen2.5vl:7b). Run npm 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.

License

MIT

Available Tools

10 tools
capture_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.

ParametersJSON Schema
NameRequiredDescriptionDefault
contentYesThe fact to capture
confidenceNoHow confident (0.0–1.0)
importanceNoHow important (0.0–1.0). High for medical/safety, low for casual preferences
domain_hintNoSuggested 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_contextNoWhat the conversation is about right now
source_event_idNoID of the event that prompted this capture

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness3/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
allNoExtract the whole backlog this call instead of the capped oldest batch
limitNoExtract at most this many of the oldest unexamined lines

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does substantial work: it discloses the 50-line extraction cap, the 'oldest unexamined lines' ordering, the events_remaining result field, and the all/limit overrides. However, it does not explain whether integrating duplicates and contradictions mutates or destructively merges knowledge-graph data, which is a meaningful gap for a tool that 'integrates' content.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The structure is sensible: purpose, then when to call, then cap behavior and parameters. The opening fragment 'Copy, extract, and integrate.' is slightly redundant with the fuller sentence that follows, but every other sentence earns its place and the critical batch-limit detail is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations and no output schema, the description compensates well by mentioning events_remaining and explaining partial-processing semantics. Remaining gaps are the undefined 'named sources' and no clarification of how this relates to capture_fact as an alternative ingestion path, but the core operational picture is complete enough for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

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 value beyond the schema by explaining all: true means taking the 'whole backlog in one call' and limit: N means 'the oldest N,' tying both parameters to the 50-item cap behavior described in prose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a concrete pipeline with specific verbs and resources: copies new lines from named sources, extracts candidate facts, and integrates pending facts into domains, entities, duplicates, contradictions, and the knowledge graph. This clearly distinguishes it from siblings like log_event, capture_fact, and get_events, though the vague phrase 'named sources' leaves some ambiguity about where inputs come from.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit trigger conditions are given: 'after capturing several facts, at a topic change, or before the conversation ends.' This gives clear when-to-use context, though it does not name alternatives or state when not to use the tool, which would fully earn a 5.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
topicYesTopic, person, project, or domain to explore

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesThe thing's name. Resolve an indirect reference to a name first if you can (e.g. via get_context or a prior fact).
typeNoOptional 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

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum events to return (default 50).
session_idNoSession to query (matches MCP or client session ID). Omit for the current session.
after_sequenceNoOnly return events after this sequence number (for pagination).

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
session_idNoSession to query. Omit for the current session.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines5/5

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.

embeddings reports semantic-search coverage per model. An empty list means this store searches by keyword only, which is the default. A count well below the current fact count means some facts are findable by wording but not by meaning — worth mentioning if the user asks why something was not recalled.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral transparency, but it never explicitly states that this tool is read-only and has no side effects. It clarifies the meaning of fields and that token fields are omitted rather than zero, but it stops short of confirming that calling get_stats does not modify data or trigger background processes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than strictly necessary, but the extra detail is purposeful and well structured with inline-code field names and separate paragraphs for each metric. It front-loads the main purpose and then handles potential confusions like embeddings versus intelligence, which justifies the length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Since there is no output schema, the description provides a thorough explanation of the statistics returned, including edge cases such as omitted token fields, unlimited token budgets, and behavior over the cap. This gives enough context for an agent to understand what the results mean without needing a formal output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the schema is complete, so there are no parameter semantics left unexplained. The baseline for zero parameters is 4, and the description correctly adds no irrelevant parameter information.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb and resource: 'Get knowledge base statistics' and enumerates exactly which statistics are returned. It also differentiates the tool from siblings like search_knowledge, get_entity, and get_context, making its purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says when to call the tool: when the user asks what is known, how much is stored, or whether memory is working. It also names alternatives for actual recall, saying to use search_knowledge, get_entity, or get_context, which gives clear when-not-to-use guidance.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
roleYesChannel that produced this event (user, assistant, system, tool). For a named person on a group transcript, keep role as user and pass speaker.
contentYesText content of the event
speakerNoNamed participant when the transcript has one (e.g. Alex). Role stays the channel — do not invent a person role.
metadataNoArbitrary metadata
event_typeYesType of event
content_refNoURI or path for non-text content
content_typeNoHow 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

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesWhat to search for
domainNoPrioritise 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

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 10 tool updatesv0.30.1
    • First observedcapture_fact
    • First observedconsolidate
    • First observedget_context
    • First observedget_entity
    • First observedget_events
    • First observedget_schemas
    • First observedget_session_context
    • First observedget_stats
    • First observedlog_event
    • First observedsearch_knowledge

TDQS

A4.4/5.0

Scored across 10 tools

Disambiguation5/5

Each tool has a clearly distinct purpose, and the descriptions actively cross-reference each other to eliminate overlap — e.g., capture_fact vs log_event (semantic vs episodic), search_knowledge vs get_context vs get_entity (specific fact vs comprehensive topic vs entity-centric). No two tools could be confused.

Naming Consistency4/5

The naming follows a strong verb_noun pattern (log_event, capture_fact, get_events, get_session, get_entity, get_context, get_schemas, get_stats, search_knowledge) with a single deviation: 'consolidate' is a bare verb lacking a noun object. Otherwise the pattern is highly consistent.

Tool Count5/5

Ten tools is squarely within the ideal 3–15 range and each one earns its place: two capture paths, one batch-consolidation step, six retrieval variants, and two introspection/metadata tools. Nothing feels redundant or superfluous.

Completeness4/5

The memory lifecycle is well covered: capture (capture_fact, log_event), integration (consolidate), rich retrieval (search_knowledge, get_context, get_entity, get_events, get_session), and metadata (get_schemas, get_stats). The only gap is the absence of a direct fact update/delete tool, though supersession and deduplication are handled implicitly by consolidation.

Maintenance

ActivityMaintained
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Open-source MCP memory server providing persistent, cross-platform context for AI tools via a knowledge graph with encrypted storage.
    9
    13
    AGPL 3.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    A 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