# Claude Code Integration (MCP)
Co-Engram integrates with Claude Code via the **Model Context Protocol** (MCP) — Anthropic's open standard for connecting AI applications to external tools.
## How It Works
```mermaid
flowchart LR
CC["Claude Code
(MCP client)"] -.stdio.-> MCP["co-engram-mcp
(MCP server)"]
MCP --> Core["@co-engram/core"]
Core --> Data[("~/team-memory/
Git repo")]
```
- Claude Code spawns the `co-engram-mcp` binary as a child process
- Communication is over stdio (JSON-RPC 2.0)
- The MCP server loads `@co-engram/core`, wires up tools, optionally starts the maintenance engine
- Tools are exposed as `mcp__co-engram__` in the Claude Code session
- At MCP initialize, a deterministic **team skill catalog** is injected into the system prompt (each skill's skillId + native SKILL.md description, ordered by utility, max 10 entries); skills whose `retentionStage` is `forgotten` are excluded — the list evolves automatically as the maintenance engine recomputes decay
> **Single-daemon mode (default since 2026-07).** Each `co-engram-mcp` spawn thin-launches to a shared long-lived **daemon** — one `ToolContext` reused across every Claude Code session on the same data root — so the second session onward skips cold boot. The daemon auto-exits after 30 min idle. Set `CO_ENGRAM_DAEMON=0` to fall back to one-process-per-session; any daemon spawn/connect failure also falls back transparently to the in-process path. Claude Code-only — OpenClaw is unaffected. See [Environment Variables](#environment-variables).
## Installation
### Option A: Global install (recommended)
```bash
npm install -g @co-engram/claude-code
```
Pros: fast startup (package already on disk), `co-engram-mcp` on PATH.
Cons: manual updates (`npm update -g @co-engram/claude-code`).
### Option B: Zero-install via npx
No install step. Each cold start fetches the package.
```bash
# When wiring with claude mcp add:
... -- npx -y @co-engram/claude-code
```
Pros: always latest, no disk footprint.
Cons: ~2s penalty on first run of each session.
## Wiring
Run `claude mcp add` once. This stores the config in your Claude Code user config.
### Minimal (no maintenance engine)
```bash
claude mcp add co-engram \
-e CO_ENGRAM_DATA_ROOT=$HOME/team-memory \
--scope user \
-- co-engram-mcp
```
### Full (with auto-maintenance)
```bash
claude mcp add co-engram \
-e CO_ENGRAM_DATA_ROOT=$HOME/team-memory \
-e CO_ENGRAM_DEFAULT_CREATED_BY=$USER \
-e CO_ENGRAM_MAINTENANCE=1 \
-e CO_ENGRAM_MAINTENANCE_ENABLED_STAGES=light,deep,rem \
--scope user \
-- co-engram-mcp
```
### npx variant
Replace `-- co-engram-mcp` with `-- npx -y @co-engram/claude-code`.
## Scope
The `--scope` flag controls where the config lives:
| Scope | File | Visibility |
| ----------------------------- | --------------------------- | --------------------------------- |
| `user` (default for this doc) | `~/.claude.json` | All your Claude Code sessions |
| `project` | `.mcp.json` in project root | Anyone cloning the repo |
| `local` | project-local | Only this project on your machine |
For team-shared config, use `project` and commit `.mcp.json`.
## Verifying
```bash
# From shell — check connection
claude mcp list
# From Claude Code session — check tools loaded
/mcp
```
Expected:
```
co-engram: ✓ Connected
Tools: 19 # standard profile (default); use CO_ENGRAM_TOOLS_PROFILE=minimal|full to change
```
## Environment Variables
All optional. See the [Configuration section of the main README](../README.md#configuration) for the full table.
Key ones:
- `CO_ENGRAM_DATA_ROOT` — **must be absolute path** to the data Git repo
- `CO_ENGRAM_DEFAULT_CREATED_BY` — fallback for `createdBy` (2026-07: `engram_create` / `synapse_create` / `engram_accept_proposal` etc. now **ignore the LLM-passed `createdBy`** to prevent self-filling host identifiers like `"claude-code"`). Resolution chain: **local git identity (`user.name` → `user.email`)** > `team-memory.json`'s `defaultCreatedBy` field (written by `co-engram init`) > this env var > final fallback `'unknown'`. Git is the authority; this env is an escape hatch when git is unavailable.
- `CO_ENGRAM_MAINTENANCE=1` — enable the maintenance engine
- `CO_ENGRAM_MAINTENANCE_LEARNING_RATE` — RPE learning rate (default 0.1)
- `CO_ENGRAM_PROPOSALS_ENABLED=1` — enable implicit memory proposals
- `ANTHROPIC_API_KEY` — Claude API key used by the proposal engine's Layer 2 necessity evaluator. Usually already set in the Claude Code environment; the adapter auto-detects it. When unset, Layer 2 falls back to the rule-based evaluator (zero LLM cost). See [observability two-layer filtering](./observability.md#proposal-engine).
- `CO_ENGRAM_VIEWER_ENABLED=1` — start the web viewer at `http://127.0.0.1:18899`
- `CO_ENGRAM_LANGUAGE` — language for tool descriptions / viewer / prompts (`en` | `zh`; default `en` or persisted team-memory config)
- `CO_ENGRAM_TOOLS_PROFILE` — tool surface for the LLM: `minimal` (12 tools — 8 core read/write + 3 proposal triage + `engram_sync`, so the maintenance engine's auto-generated candidates can always be closed-loop handled), `standard` (19, default — adds learning loop, contradiction, self-healing, progressive disclosure, LLM synthesis, audit query), `full` (29, includes admin + internal management tools). Counts are derived from `PROFILE_TOOL_COUNTS` in source via `.size`, so they cannot silently drift. Invalid values warn and fall back to `standard`.
- `CO_ENGRAM_DAEMON` — single-daemon mode (default `1`): each Claude Code session connects to a shared long-lived daemon (one `ToolContext` reused across sessions on the same data root). Set to `0` to fall back to one-process-per-session; daemon spawn/connect failure also falls back transparently. Claude Code-only; OpenClaw ignores it.
- `CO_ENGRAM_DAEMON_IDLE_TIMEOUT_MS` — daemon auto-shutdown when no clients are connected for this long (default `1800000` = 30 min).
- `CO_ENGRAM_DAEMON_SOCKET_DIR` — override directory for the daemon's unix socket files (default `/co-engram`).
## Web Viewer
The viewer is a loopback-only HTTP server that lets you browse the data repo in a browser. Off by default. Enable with:
```bash
claude mcp add co-engram \
-e CO_ENGRAM_DATA_ROOT=$HOME/team-memory \
-e CO_ENGRAM_VIEWER_ENABLED=1 \
-e CO_ENGRAM_VIEWER_TOKEN=mysecret \
--scope user \
-- co-engram-mcp
```
Then open `http://127.0.0.1:18899` in your browser. If you set a token, the browser will prompt for it.
> **`viewer.port` persisted config is deprecated.** If `port` is set in `~/team-memory/.co-engram/config.json`'s `viewer` block, the server prints a one-line warning at startup because the two hosts (Claude Code + OpenClaw) share that persisted config and would otherwise collide. Prefer the env var `CO_ENGRAM_VIEWER_PORT`. The persisted value still works as a fallback for this release.
Endpoints: see [observability.md](./observability.md#viewer).
### Memory Proposals
Proposals are implicit candidates — topics the engine noticed recurring in conversation but haven't been recorded yet. Off by default. Enable with `CO_ENGRAM_PROPOSALS_ENABLED=1`.
When enabled, the engine applies **two-layer filtering** to suppress mechanical noise and evaluate necessity (see [observability two-layer filtering](./observability.md#proposal-engine)):
- **Layer 1**: prefilter at `observe()` entry rejects system/empty/short/trivial/low-density messages
- **Layer 2**: before cluster promotion, rule-based evaluator (5 rules) + optional LLM semantic judgment
The LLM evaluator uses Anthropic Messages API and auto-detects env `ANTHROPIC_API_KEY` with default model `claude-haiku-4-5-20251001`. You can also configure `necessityLlm` explicitly in `~/.co-engram/config.json` (supports custom endpoint / model / apiKey / headers). LLM call failures (network / timeout / parse error) automatically fall back to the rule-based evaluator, with reason prefixed `[llm-unavailable, rule-fallback] ...`.
When enabled and a session starts with pending proposals, the MCP server emits a log message via `notifications/message`:
```
[co-engram] 3 memory candidates pending (topics seen ≥3 times but not recorded).
Use `engram_list_proposals` to view, `engram_accept_proposal` to record,
or `engram_dismiss_proposal` to ignore.
```
Claude Code surfaces this in the session banner. The LLM can then triage with the three proposal tools.
### Auto-Memory Sync (Claude Code → Co-Engram Proposals)
Claude Code maintains its own auto-memory under `~/.claude/projects//memory/*.md` (typed: `user` / `feedback` / `project` / `reference` / `pattern`). Once co-engram is loaded, the LLM-facing prompts (`## Exclusive memory store` in system instructions + the `engram_create` description) **direct agents to call `engram_create` directly** — auto-memory is a fallback for agents that don't adopt co-engram's native tools. Whatever the capture entry point, co-engram watches the directory and **mirrors every file into the team repo as a pending proposal** — not directly as an engram. The user (or LLM) then triages via `engram_list_proposals` / `engram_accept_proposal` / `engram_dismiss_proposal`, exactly the same path as conversation-cluster proposals. This keeps auto-memory subject to the same review gate as every other capture path.
**On by default** (low-friction). Disable with:
```bash
claude mcp add co-engram \
-e CO_ENGRAM_AUTO_MEMORY_SYNC=0 \
-- co-engram-mcp
```
Or in `~/.co-engram/config.json`:
```json
{ "autoMemorySync": { "enabled": false } }
```
How it works:
- On startup, scans every project's `memory/` directory under `~/.claude/projects/` and bulk-syncs existing files
- A `fs.watch` watcher picks up new/updated `.md` files in real time (debounced 500ms)
- Each memory becomes a pending proposal (entityId `am:`) with payload pre-populated:
- `domainTag` `claude-code-auto-memory` (filterable in `engram_search` after accept)
- `encodingContext` `claude-code-auto-memory:` (idempotency key)
- `source` `auto-memory` (distinguishes from `conversation` proposals)
- Type mapping: `pattern` → `pattern`, `feedback` / `user` → `observation`, `project` / `reference` → `fact`
- Accepting a proposal via `engram_accept_proposal({ entityId: "am:" })` creates the engram (no need to re-type title/content — the proposal carries them)
- Editing a memory in Claude Code → the proposal's payload is updated (replaces the previous pending proposal); an already-accepted proposal is not reopened
- `MEMORY.md` (the index file) is intentionally skipped
Watch for this log line at MCP startup to confirm it's running:
```
[co-engram] auto-memory sync: watching /home/you/.claude/projects (initial: 12 files, 5 proposed, 0 updated)
```
OpenClaw doesn't have an equivalent auto-memory writer, so this subsystem is **claude-code-mcp only** — the openclaw-plugin does not start it.
### External-Markdown Proposals (dataRoot `.md` → Co-Engram Proposals)
Beyond Claude Code's auto-memory directory, the watcher also observes the data root itself. **Any `.md` file you drop into `CO_ENGRAM_DATA_ROOT`** — hand-authored notes, exported docs, or files synced from another machine — is picked up and turned into a pending proposal, never silently ignored.
- File already has valid engram frontmatter (`title` + `kind`) → proposal is created directly from the frontmatter.
- **Bare `.md`** (no frontmatter, or frontmatter missing `title` / `kind`) → the engine auto-extracts the missing fields before proposing:
- **`ANTHROPIC_API_KEY` available** (default in Claude Code): an LLM pass extracts `title` / `kind` / `domainTags` / `summary` from the body.
- **LLM unavailable or fails**: rule-based fallback — first H1 or filename → `title`, `kind = observation`, `domainTags = ["imported"]`.
The proposal carries `source: "external-markdown"` (distinguishable from `conversation` and `auto-memory`) and a pre-filled payload, so you can triage it via `engram_list_proposals` / `engram_accept_proposal`, or bulk-accept with `engram_accept_proposals_by_source({ source: "external-markdown" })`. Extraction runs asynchronously (fire-and-forget), never blocking the watcher.
Unlike auto-memory sync, this subsystem lives in `@co-engram/core` and is **shared by both hosts** (Claude Code and OpenClaw). See [observability two-layer filtering](./observability.md#proposal-engine) for extraction details.
## Automatic Sync Lifecycle
The data root is a Git repo, and the Claude Code host keeps it in sync with the remote automatically — routine cross-machine updates do not require invoking `engram_sync`.
**On startup** (when the MCP server — or the shared daemon, in daemon mode — boots):
- `git pull --no-edit` (30 s timeout) pulls teammate changes from the remote.
- The merge driver resolves YAML conflicts automatically; nothing for you to do.
- Silently skipped when there is no remote, no network, or the repo is already up to date (no log noise).
**On shutdown** (when the session ends, or the daemon idle-times-out after committing):
1. `git commit` (auto) — commits this session's memory writes, if anything changed.
2. `git push` (30 s timeout) — pushes the new commit to the remote.
3. Push failure only logs a warning and never blocks exit — the next startup's `git pull` will sync the missed changes.
For on-demand control (custom commit message, `dryRun`, conflict review, Gerrit review fallback), invoke `engram_sync` explicitly — see [README → Save and sync to remote](../README.md#save-and-sync-to-remote-engram_sync). The two paths are complementary: the auto lifecycle handles routine traffic; `engram_sync` is the manual override.
## Project-Local Config (`.mcp.json`)
For team-shared Co-Engram setups, drop a `.mcp.json` in the project root:
```json
{
"mcpServers": {
"co-engram": {
"command": "npx",
"args": ["-y", "@co-engram/claude-code"],
"env": {
"CO_ENGRAM_DATA_ROOT": "${workspaceFolder}/.team-memory",
"CO_ENGRAM_MAINTENANCE": "1"
}
}
}
}
```
Commit it to share with teammates.
## Restarting
MCP servers are loaded at Claude Code startup. If you change env vars:
```bash
claude mcp remove co-engram -s user
# re-add with new env
claude mcp add co-engram -e NEW=VALUE ... -- co-engram-mcp
```
Or just edit `~/.claude.json` directly and restart Claude Code.
## Troubleshooting
See [quickstart.md → Troubleshooting](./quickstart.md#troubleshooting) for common issues.