{
  "markdown": "# khwan-mcp\n\n**Durable memory that survives the session.** An [MCP](https://modelcontextprotocol.io)\nserver that plugs [Khwan](https://khwan.ai) — a pure AI-memory layer — into\nClaude Code, Claude Desktop, or any MCP client.\n\nKhwan never runs *your* model. **The client is the model.** Its job is to persist\nand distil what matters into a brain you can **recall in a later session or seed a\nsubagent with** — a compact, bounded set of facts instead of a replayed\ntranscript. One account can hold many isolated **cores** (brains), and — on paid\nplans — an isolated sub-brain per end-user.\n\nNothing on the read or write path costs you an inference call: `khwan_recall` and\n`khwan_remember` make none. Khwan does run one model of its own, on a schedule —\na nightly pass that distils stored turns into standing lessons. A real production\nrun was 8 brains and 245 stored turns distilled into 11 lessons, for **$0.0094**.\nThat pass is Khwan's cost, not yours, and it is the whole difference from memory\nlayers that compress, graph, or consolidate on every turn.\n\n<!-- The MCP Registry verifies ownership of a PyPI package by finding this\n     name in the package README, which is what PyPI shows as the description.\n     It must match `name` in server.json, and it only reaches PyPI on the next\n     release — so do not rename one without re-releasing. -->\n<!-- mcp-name: ai.khwan/khwan-mcp -->\n\n## How it saves tokens (and where it doesn't)\n\nBe honest about the mechanism — an MCP **adds** to a host's context, it cannot\n**replace** the transcript the host already sends. So:\n\n- **Within one hot session, it does not save tokens.** Claude Code caches its\n  growing history (cache reads ≈ 0.1×), so re-injecting memory every turn only\n  adds. Don't do that here.\n- **Across sessions and subagents, it does.** A cache dies in minutes; a session\n  ends. Khwan persists distilled facts so the *next* run recalls them cheaply —\n  no cold-replay of an old transcript, and facts that already scrolled out of\n  context are retrievable again.\n\nThe token-smart pattern: **seed once, remember durable facts** (below), rather\nthan running the full loop on every turn of a caching host. The full\n`prepare → record` loop still shines in a **custom agent on a non-caching host**,\nwhere replacing history with distilled memory bounds per-turn cost directly.\n\n## Install\n\n```bash\npip install khwan-mcp          # or: uvx khwan-mcp\n```\n\n## Connect to Claude Code\n\n```bash\nclaude mcp add khwan --scope project \\\n  -e KHWAN_CORE=default \\\n  -- khwan-mcp\n```\n\n`--scope project` writes `.mcp.json` into the repo, so the setting travels with\nthe project. Note what is **not** in that command: the key.\n\n### Keeping the key out of the repo\n\n`claude mcp add -e KHWAN_API_KEY=…` writes the literal value into `.mcp.json` —\na file whose whole point is being committed. Two ways to avoid that, and the\nsecond is the one that works everywhere:\n\n**Shell environment.** Leave `KHWAN_API_KEY` out of the config entirely and\nexport it in the shell that launches `claude`. The server inherits it.\n\n```bash\nexport KHWAN_API_KEY=kwk_live_xxx\n```\n\n**A launcher (works in the desktop app too).** A desktop app is started from a\ndock or menu, not a login shell, so it inherits none of your shell exports and\nthe approach above silently yields no key. Read it from a file instead:\n\n```bash\nmkdir -p ~/.khwan && chmod 700 ~/.khwan\nprintf 'KHWAN_API_KEY=kwk_live_xxx\\n' > ~/.khwan/env && chmod 600 ~/.khwan/env\n\ncat > ~/.khwan/khwan-mcp <<'SH'\n#!/bin/sh\nset -a\n[ -f \"$HOME/.khwan/env\" ] && . \"$HOME/.khwan/env\"\nset +a\nexec khwan-mcp \"$@\"\nSH\nchmod 700 ~/.khwan/khwan-mcp\n```\n\nThen point the config at the launcher and keep only non-secret settings inline:\n\n```bash\nclaude mcp add khwan --scope project \\\n  -e KHWAN_CORE=acme -e KHWAN_USER=Web \\\n  -- ~/.khwan/khwan-mcp\n```\n\n`.mcp.json` is now safe to commit, and every new repo costs two lines instead of\na pasted key. Anyone else on the team writes their own `~/.khwan/env`.\n\n## One brain per project\n\nMemory is only useful if the right project's memory comes back. Two axes, and\nboth give **complete** isolation:\n\n| | selected by | free | paid |\n|---|---|---|---|\n| **core** | `KHWAN_CORE` | 1 — `default` only | 5 (starter) → 25 (pro) |\n| **sub-brain** | `KHWAN_USER` | **3** | unlimited |\n\nA sub-brain is a full separate brain, not a filter: `account::@web` shares\nnothing with `account::@api`. So the two axes multiply, and **a free account\nalready holds four isolated brains** — the core on its own, plus three\nsub-brains:\n\n```\naccount              default core, no KHWAN_USER      brain 1\naccount::@web        KHWAN_USER=web                   brain 2\naccount::@api        KHWAN_USER=api                   brain 3\naccount::@docs       KHWAN_USER=docs                  brain 4\n```\n\nWhich means one-brain-per-project works on the free plan, for up to four\nprojects — **and it needs no `KHWAN_CORE` at all**:\n\n```bash\n# in ~/code/web\nclaude mcp add khwan --scope project -e KHWAN_USER=web -- ~/.khwan/khwan-mcp\n# in ~/code/api\nclaude mcp add khwan --scope project -e KHWAN_USER=api -- ~/.khwan/khwan-mcp\n```\n\nNamed cores are the paid axis. Reach for one when four brains stop being\nenough, or when you want them grouped per client rather than per repository:\n\n```bash\n# in ~/code/acme-web\nclaude mcp add khwan --scope project -e KHWAN_CORE=acme -e KHWAN_USER=Web -- ~/.khwan/khwan-mcp\n# in ~/code/acme-api\nclaude mcp add khwan --scope project -e KHWAN_CORE=acme -e KHWAN_USER=Api -- ~/.khwan/khwan-mcp\n```\n\nTwo things to know before you point `KHWAN_CORE` anywhere. **A core must exist\nfirst** — an unknown slug answers `404`, not \"created it for you\" — and they are\ncreated in the dashboard. **On the free plan there is nothing to point at**: the\ncap of one is spent on `default`, so creating a named core answers `402`. Leave\n`KHWAN_CORE` unset there and use `KHWAN_USER`. Sub-brains, by contrast, are\ncreated on first write.\n\n### Recommended pattern (token-smart)\n\nOn a caching host like Claude Code, prefer **seed + remember** over the per-turn\nloop:\n\n1. **Seed** at the start of a session or subagent:\n   > \"Call `khwan_recall(query=\"<the task>\")` and use the returned `seed_text` as\n   > context.\"\n2. **Remember** durable facts as they emerge:\n   > \"That's a standing decision — call `khwan_remember(fact=\"…\")`.\"\n\nReinforce it in your project's `CLAUDE.md`, e.g.:\n\n```md\n- At the start of a task, call `khwan_recall` to seed relevant memory.\n- When a durable decision/preference/fact emerges, call `khwan_remember`.\n- Don't call prepare/record every turn — it adds tokens without saving them here.\n```\n\n**Seeding a subagent** is where the win is clearest — hand it a bounded brief\ninstead of the whole transcript:\n\n> \"Recall deploy memory with `khwan_recall(query=\"deploy runbook\")`, then spawn a\n> subagent whose brief is that `seed_text` plus the task.\"\n\n## Connect to Claude Desktop\n\nClaude Desktop and Claude Code keep **separate** MCP configuration — a server\nadded to one is invisible to the other, and `claude mcp add` does not touch this\nfile. Add to `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"khwan\": {\n      \"command\": \"/Users/you/.khwan/khwan-mcp\",\n      \"env\": {\n        \"KHWAN_CORE\": \"acme\",\n        \"KHWAN_USER\": \"Web\"\n      }\n    }\n  }\n}\n```\n\nUse an absolute path: a desktop app does not get your shell's `PATH` either, so\na bare `khwan-mcp` may not resolve. One core is selected for the whole app —\nthere is no per-project switch here, so choose a broad one.\n\n## Configuration (environment)\n\n| Var              | Required | Purpose                                                              |\n| ---------------- | -------- | ------------------------------------------------------------------- |\n| `KHWAN_API_KEY`  | yes      | Your key from the Khwan dashboard (`kwk_live_…`).                    |\n| `KHWAN_CORE`     | no       | Select a named core. Paid plans only — free has just `default`.     |\n| `KHWAN_USER`     | no       | A separate brain inside the core — 3 on free, unlimited on paid.    |\n| `KHWAN_BASE_URL` | no       | Override the API base — e.g. `http://127.0.0.1:8010` for a local engine. |\n\n## Tools\n\n| Tool                              | When                                                              |\n| --------------------------------- | ---------------------------------------------------------------- |\n| `khwan_recall(query, limit=3)`    | **seed** a session/subagent — synthesised `lessons` + up to 3 relevant facts, as `seed_text`. |\n| `khwan_remember(fact)`            | **persist** a durable fact/preference for future sessions.        |\n| `khwan_prepare(input)`            | full loop, **before** answering — memory context + a `turn_token`. |\n| `khwan_record(turn_token, answer)`| full loop, **after** answering — persists the turn so Khwan learns. |\n| `khwan_memory(limit=20)`          | inspect what the brain currently remembers.                       |\n| `khwan_cores()`                   | list the isolated cores on the account.                           |\n\n`khwan_recall` / `khwan_remember` are the token-smart pair for a caching host;\n`khwan_prepare` / `khwan_record` are the full loop for custom agents (pass the\nexact `turn_token` from prepare back into record).\n\n### What comes back, and what an empty answer means\n\n`khwan_recall` returns at most **three** facts — that ceiling is the server's,\nso `limit` can lower it but not raise it — plus any `lessons` synthesis has\ndistilled from many past turns. Lessons lead the `seed_text`: a rule earned over\nmonths outranks a single turn that happens to sit nearby in the index.\n\nRetrieval applies a relevance floor, so **an empty `facts` is an answer**: the\nbrain has nothing close to this question. Read it as \"not known here\" rather than\nas a failure, and do not fill the gap by leaning on whichever fact was nearest.\n\nThe floor is deliberately loose, because a memory wrongly dropped is invisible\nwhile a memory wrongly kept is not. Expect a returned fact to be *plausibly*\nrelated, not certainly relevant — read it before relying on it.\n\n## Seeding a brain from work you have already done\n\nA new brain knows nothing, so its first weeks of recall are thin — while the\nanswers are often already sitting in the host's own transcripts, unread.\n[`examples/backfill/`](examples/backfill/) replays Claude Code transcripts into a\nbrain: deterministic, no model calls, dry-run by default.\n\n```bash\npython3 examples/backfill/backfill_claude_code.py --map cores.json\n```\n\n## Always-on memory (Claude Code hooks)\n\nThe tools above are called *when Claude decides to*. For **deterministic** memory\n— no reliance on the model — use the hook preset in\n[`examples/claude-code-hooks/`](examples/claude-code-hooks/): a `UserPromptSubmit`\nhook injects memory on every prompt and a `Stop` hook records every answer.\n\n> ⚠️ On a caching host this is the *thorough* option, not the *cheap* one — it\n> adds per-turn tokens. Prefer it when recall reliability matters more than token\n> cost (or on a non-caching client); otherwise use `khwan_recall` at session\n> start.\n\n## Source\n\n[github.com/khwanlabs/khwan-mcp](https://github.com/khwanlabs/khwan-mcp) — this\nserver runs on your machine, with your key, reading what you type. Read it before\nyou install it.\n\n## License\n\nMIT — © Khwan Labs. See [LICENSE](LICENSE).\n",
  "bytes": 11335,
  "sha": "d5dde8713ceb09de9cfe6bbe7aad4569086c5e25549cd12192345eff4fbd92a9",
  "repo_slug": "khwanlabs/khwan-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_ai_khwan_khwan_mcp_4209e0b9/readme"
}