{
  "markdown": "# Tacitus\n\n**Long-term memory for your AI agents — local-first, with provenance.**\n\n[![npm](https://img.shields.io/npm/v/@dashiro/tacitus-mcp-server)](https://www.npmjs.com/package/@dashiro/tacitus-mcp-server)\n[![CI](https://github.com/ionasrobert/tacitus-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/ionasrobert/tacitus-mcp-server/actions/workflows/ci.yml)\n[![license](https://img.shields.io/npm/l/@dashiro/tacitus-mcp-server)](./LICENSE)\n\nTacitus is an [MCP](https://modelcontextprotocol.io) server that turns any folder\nof Markdown notes into an **agent-native knowledge base**. It gives AI agents\n(Claude Code, Claude Desktop, and any MCP client) three things they actually need:\n\n1. **Memory with provenance** — typed, queryable long-term memory. Every fact\n   carries its source and is returned within a token budget; contradictions are\n   surfaced, not silently resolved.\n2. **Retrieval that fits the context window** — search returns ranked snippets\n   (never whole notes) under a token budget; `get_note` discloses progressively\n   (outline → frontmatter → full); the wikilink graph is a queryable API.\n   Hybrid lexical + semantic search, with an optional neural embedder.\n3. **Safe write-back** — propose a changeset, preview the diff, commit\n   atomically, and revert by version. Read-only scope forbids mutations; every\n   write is audited.\n\nNotes stay as plain `.md` files in your folder. No cloud, no lock-in.\n\n![An agent remembering a fact with its source, recalling it under a token budget, proposing a diff, committing it, and reverting it](./site/demo/tacitus-memory.gif)\n\n*Recorded from a real session — the memory id, changeset id, version id and audit\nline above are what the binary actually returned ([how](./site/demo/README.md)).*\n\n## Quick start\n\n```bash\nnpx -y @dashiro/tacitus-mcp-server /path/to/your/vault\n```\n\n### Claude Code\n\n```bash\nclaude mcp add tacitus -- npx -y @dashiro/tacitus-mcp-server /path/to/your/vault\n```\n\n### Claude Desktop (`claude_desktop_config.json`)\n\n```json\n{\n  \"mcpServers\": {\n    \"tacitus\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@dashiro/tacitus-mcp-server\", \"/path/to/your/vault\"]\n    }\n  }\n}\n```\n\n### Native binary (no Node)\n\nPrefer a single, zero-dependency binary? The Rust server ships prebuilt for\nmacOS, Linux, and Windows on every release.\n\n```bash\n# macOS / Linux — installs `tacitus-mcp` into your Cargo bin dir\ncurl --proto '=https' --tlsv1.2 -LsSf \\\n  https://github.com/ionasrobert/tacitus-mcp-server/releases/latest/download/tacitus-mcp-installer.sh | sh\n```\n\n```powershell\n# Windows (PowerShell)\nirm https://github.com/ionasrobert/tacitus-mcp-server/releases/latest/download/tacitus-mcp-installer.ps1 | iex\n```\n\nOr grab a `.tar.xz` / `.zip` for your platform from the\n[latest release](https://github.com/ionasrobert/tacitus-mcp-server/releases/latest).\nThen point any MCP client at the binary instead of `npx`:\n\n```bash\nclaude mcp add tacitus -- tacitus-mcp /path/to/your/vault\n```\n\nThe native binary is the flagship server (25 tools; the npm server has the\ncore 16 — see the table below). Both share the same on-disk formats, so a\nvault works with either. Set `TACITUS_SCOPE=read-only` to run the native\nserver without write permissions.\n\n## Tools\n\n| Group | Tools |\n|---|---|\n| **Memory** | `remember`, `recall`, `forget` |\n| **Retrieval** | `search`, `get_note`, `graph_query`, `list_notes`, `properties_query`* |\n| **Write-back** | `propose_changes`, `commit_changes`, `revert`, `rename_note`*, `delete_note`*, `get_version`* |\n| **Convenience** | `create_note`, `update_note`, `link`, `tag`, `audit_log` |\n| **Templates** | `list_templates`*, `create_from_template`* |\n| **Tasks** | `list_tasks`*, `toggle_task`* |\n| **Meta** | `capabilities` |\n\n\\* Native-Rust-server first (the npm server will catch up):\n`properties_query` — Bases-like structured queries over YAML frontmatter\n(filters `eq|ne|contains|exists|not_exists|gt|lt|gte|lte`, sort, select,\ntoken_budget). Templates — Markdown files in `.tacitus/templates/` whose\n`{{var}}` placeholders form a schema; substitution happens before YAML\nparsing so numeric vars stay typed, `{{date}}`/`{{time}}`/`{{datetime}}`\nauto-fill, and creation is versioned + audited like any agent write.\nTasks — every checklist line (`- [ ]`) as a typed entity (done, due from\n`due:YYYY-MM-DD` or `📅`, #tags), queryable and toggleable; toggling takes\nthe task text as a concurrency guard so a stale caller gets a CONFLICT\ninstead of flipping the wrong task. `rename_note` retargets every wikilink\nthat resolves to the note (alias/heading kept) in one atomic changeset —\na single revert undoes the whole rename; `delete_note` is versioned too.\n\nEvery tool validates input with a schema and returns structured, actionable\nerrors (`{ code, reason, suggestion }`) rather than stack traces.\n\n## For developers: plugins & integrations\n\n**In Tacitus, a plugin is an MCP client** — the tool contract above is the\npublic API, with permission scoping, versioning, and audit built in.\n\n- [docs/PLUGINS.md](./docs/PLUGINS.md) — integration guide: connect an agent,\n  write a plugin in Python/TypeScript, embed the Rust engine, plugin patterns\n- **TypeScript SDK** — [`@dashiro/tacitus-sdk`](./packages/sdk): every tool as\n  a typed method, `{code, reason, suggestion}` errors thrown as\n  `TacitusToolError`:\n  ```ts\n  const tacitus = await TacitusClient.spawn({ vault: '/path/to/vault' });\n  const hits = await tacitus.search({ query: 'client X', token_budget: 500 });\n  ```\n- **Sandboxed WASM plugins (experimental)** — crate `tacitus-plugins` runs\n  guest wasm under Wasmtime with manifest-declared permissions (tool allowlist\n  + scope), fuel and memory limits, no WASI: `tacitus.call` *is* `tools/call`.\n  The native binary embeds the runtime: `tacitus-mcp plugin list|run` for cron\n  agents and scripts.\n  See [docs/PLUGINS.md §5](./docs/PLUGINS.md#5-sandboxed-wasm-plugins-experimental)\n- [docs/MCP_API.md](./docs/MCP_API.md) — full reference for all 25 tools\n  (params, returns, error codes)\n- Neural search (opt-in): `TACITUS_EMBEDDER=ollama` uses a local Ollama daemon\n  for embeddings (`TACITUS_OLLAMA_EMBED_MODEL`, default `nomic-embed-text`;\n  needs an Ollama with embedding support). Vectors cached in `.tacitus/vectors/`;\n  falls back to the deterministic hashing embedder when unavailable.\n- [docs/SYNC.md](./docs/SYNC.md) — **Sync (beta)**: E2E-encrypted CRDT sync\n  between devices (`tacitus-mcp sync init`; `sync status` shows how much of\n  the relay quota a vault uses)\n- [docs/DATA_FORMAT.md](./docs/DATA_FORMAT.md) — the on-disk format\n  (`.tacitus/` internals, stable ids, note conventions)\n- [examples/](./examples/) — three complete plugins (Python read-only\n  analyzer, Node daily-note cron agent, sandboxed WASM guest), tested against\n  the binary\n\n### What the sync relay can see\n\n![Two devices exchanging a note through a relay: plain Markdown on the devices, only base64 ciphertext in the relay's log](./site/demo/tacitus-sync.gif)\n\nLeft: two devices, plain Markdown. Right: everything the relay stores for that\nvault. Those blobs were read out of a real `log.jsonl` after the recording —\nno note title, no path, no plaintext. The vault code is the key and never\nleaves your devices; lose it and the relay's copy is undecryptable forever.\n\n## Semantic search (optional neural embeddings)\n\n`search` defaults to **hybrid** mode (lexical + a deterministic, offline\nembedder that catches morphological variants). For synonym/paraphrase matching,\nopt into a neural embedder:\n\n```bash\nnpm i @huggingface/transformers\nTACITUS_EMBEDDER=transformers npx @dashiro/tacitus-mcp-server /path/to/vault\n```\n\nVectors are cached under `.tacitus/vectors/`. Falls back to the deterministic\nembedder if the optional dependency or model isn't available.\n\n## How it stores things\n\n```\nyour-vault/\n├── notes...             ← your Markdown files (untouched format)\n└── .tacitus/\n    ├── memory/*.md      ← agent memories (Markdown + YAML frontmatter)\n    ├── vectors/*.json   ← cached embeddings\n    ├── history/*.json   ← version snapshots (for revert)\n    └── audit.log        ← JSONL log of every agent write\n```\n\n## Development\n\nPolyglot monorepo. The reference server (shipped on npm) is TypeScript in\n`packages/mcp-server`. A **native Rust server** in `crates/` provides a\nsingle-binary, zero-runtime-deps build (`crates/tacitus-core` engine +\n`crates/tacitus-mcp` rmcp server) — a superset of the TS server (25 vs 16\ntools). Its `stable_id` matches the TS engine byte-for-byte, so memory ids are\nidentical across both engines.\n\n```bash\n# TypeScript server\nnpm ci\nnpm test          # vitest\nnpm run typecheck\nnpm run lint\nnpm run build     # tsup → packages/mcp-server/dist\nnpm run eval      # retrieval quality report\n\n# Rust server (native, single binary)\ncargo test\ncargo clippy --all-targets -- -D warnings\ncargo fmt --check\ncargo run -p tacitus-mcp -- /path/to/vault   # runs the MCP server on stdio\ncargo build --release                         # → target/release/tacitus-mcp\n```\n\nCross-platform release binaries are built and published to GitHub Releases by\n[cargo-dist](https://opensource.axo.dev/cargo-dist/) (`dist-workspace.toml` +\n`.github/workflows/release.yml`) on every `v*` tag.\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n",
  "bytes": 9281,
  "sha": "565a2f06fe73d4d96724a83a3cdcc5aa3f368f8f3704d56518abdc6e9712e1b9",
  "repo_slug": "ionasrobert/tacitus-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ionasrobert_tacitus_mcp_server_a13a60e1/readme"
}