Metadata-Version: 2.5
Name: claude-archive-mcp
Version: 1.14.0
Summary: Local stdio MCP server exposing unified Claude chat history (claude.ai export + Claude Code sessions) as ranked, scoped, structured search.
Project-URL: Homepage, https://github.com/manderse-dispatch/claude-archive-mcp
Project-URL: Repository, https://github.com/manderse-dispatch/claude-archive-mcp
Project-URL: Issues, https://github.com/manderse-dispatch/claude-archive-mcp/issues
Project-URL: Changelog, https://github.com/manderse-dispatch/claude-archive-mcp/blob/main/CHANGELOG.md
Author-email: Mike Andersen <manderse21@gmail.com>
License-Expression: PolyForm-Noncommercial-1.0.0
License-File: LICENSE
License-File: LICENSES/MIT-through-v1.13.0.txt
Keywords: archive,chat-history,claude,fts5,mcp,search,sqlite
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: anyio<5,>=4.0
Requires-Dist: ijson<4,>=3.2
Requires-Dist: mcp<3,>=2
Requires-Dist: numpy<3,>=1.26
Requires-Dist: pydantic<3,>=2.6
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: embeddings
Requires-Dist: sentence-transformers>=3.0; extra == 'embeddings'
Provides-Extra: http
Requires-Dist: starlette>=0.40; extra == 'http'
Requires-Dist: uvicorn>=0.30; extra == 'http'
Provides-Extra: otel
Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.45.1; extra == 'otel'
Requires-Dist: opentelemetry-sdk==1.45.1; extra == 'otel'
Description-Content-Type: text/markdown

# claude-archive-mcp

> Local stdio MCP server that exposes your unified Claude chat history
> (claude.ai export + Claude Code sessions) to Claude Code as ranked, scoped,
> structured search.

**Status:** v1.14.0 &middot; **Author:** Mike Andersen &middot; **License:** PolyForm Noncommercial 1.0.0

When you reference past work — *"did we settle this in nortam_dev last month?"*,
*"find the TAP routing analysis"*, *"what did Claude Code commit on April 14?"* —
Claude Code reaches for this server instead of asking you to summarize.

## Two sources, one index

| Source | `source` | Origin |
|---|---|---|
| claude.ai export | `claude_ai` | `~/.claude/Exported_Chats/conversations.json` + `projects/` |
| Claude Code sessions | `claude_code` | `~/.claude/projects/**/*.jsonl` |

Both feed one `conversations` table; the same tools search across both.

## Tools

`search_chats` &middot; `get_chat` &middot; `list_projects` &middot; `list_recent_chats` &middot;
`search_within_chat` &middot; `archive_stats` &middot; `timeline` &middot; `find_related_chats` &middot;
`get_digest` &middot; `recall` &middot; `get_session_lineage` — eleven read-only, source-aware tools.
See [`docs/TOOLS.md`](docs/TOOLS.md). The server also exposes a per-project **resource**
(`claude-archive://project/{source}/{identifier}`) — a dynamic markdown project overview.

**Digest:** `get_digest` (tool) or `python scripts/digest.py --weekly` (also `/digest`) — a
markdown activity recap over a rolling daily/weekly window; both share one aggregator.

**Semantic search (optional):** `find_related_chats` ranks conversations by embedding
cosine. It needs the optional extra — `pip install -e .[embeddings]`, then
`python scripts/embed.py --backfill` (also `/embed`). The server itself stays inference-free.

**Comprehension sidecars (v1.4.0):** `search_chats` and `find_related_chats` each carry an additive
semantic **sidecar tier** — matches against per-conversation, LLM-authored comprehension sidecars
(an abstract plus entities/identifiers), searched by FTS and typed separately from the lexical and
embedding rows. It is graceful under partial coverage (an un-covered conversation simply cannot
appear), so it never regresses the existing results. Keeping the sidecar store current is an
attended, Mike-armed task — see [`docs/SIDECAR_REFRESH.md`](docs/SIDECAR_REFRESH.md).

**Briefing packs (v1.10.0):** `claude-archive-brief --project <slug>` writes a deterministic,
read-only markdown pack answering what happened here recently, what is still open, what was
decided, and what to open next -- every line carrying a locator you can open. Same library behind
`python scripts\brief.py`, the viewer's `/brief` page, and the plugin's `/archive:brief`. It is an
out-of-process surface, **not** a twelfth tool. See [`docs/BRIEFING.md`](docs/BRIEFING.md).

**Curated memory (v1.10.0, opt-in):** an append-only store of Decision / Entity / Thread objects in
a **separate** `memory.db` beside the archive. Curation is out of process -- `claude-archive-curate`
carries six verbs (`correct`, `supersede`, `dispute`, `pin`, `merge`, `forget`), each an append, so
no wording is ever rewritten; `claude-archive-memory` (the console entry point, v1.12.0),
`python scripts\memory.py` and the viewer's `/memory` page read it.
When the store exists, `search_chats` and `find_related_chats` rows gain a `curated_status` label
(`current` / `superseded` / `disputed` / null) -- a **label only**, changing no count, no ordering
and no selection. With no `memory.db` the key is absent, so responses are byte-identical to an
install that never curated anything. No archive migration and no new MCP tool.

**Curated notes (v1.11.0, opt-in):** `python scripts\curated_notes.py --out <vault> --force` renders
those same memory objects as plain markdown notes with flat YAML frontmatter, into a `curated\`
folder **beside** -- never inside -- the conversation tree the markdown exporter writes. Generated
Maps of Content per kind and per project, a prune pass so a forgotten object's note leaves the
vault, and byte-identical re-export when nothing changed. With no `memory.db` it is a clean no-op,
not an error. Read-only against both stores; no plugin and no new dependency is required to read
any of it. See [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md).

**Context planner (v1.11.0, prompt-side):** the `recall-past-work` skill that ships with the
`archive` plugin carries a four-rung disclosure ladder over these same eleven tools -- candidates,
then curated memory objects, then the specific passage, then the whole conversation -- climbing only
when the current rung has not answered, inside a 24,000-character budget across the first three
rungs, and disclosing where it stopped and why. It is prompt-side text with **no server component**:
no twelfth tool, no hook, and no change to any tool response.

**Continuity Capsule (v1.12.0, opt-in):** `claude-archive-brief --capsule [--json]
[--select startup|resume|compact|fork] [--since <ISO>]` emits one typed continuity object per
(subject, window) -- ten closed fields of rows, each row carrying its own evidence locator,
provenance class, confidence tier and `recorded_at`. It is assembled purely from the collections
the briefing pack already gathers, so it adds no database read. Opt-in throughout: without
`--capsule` the entry point emits the briefing pack byte-for-byte as before. Out-of-process, like
the pack it is projected from: **no twelfth tool**, no schema change. The bounded, opt-in,
**default-off** `SessionStart` integration that consumes it **is built** and registered in
`plugin/hooks/hooks.json`; it injects nothing unless `CLAUDE_ARCHIVE_SESSIONSTART` is on. See
`docs/design/session_start.md`, and `docs/PROGRAM.md` section 18 for its provenance.

**Runs in a hardened container (v1.14.0, optional):** `docker compose build`, then `docker compose run --rm worker reindex`, a first-run `claude_archive_mcp.memory.populate`, `docker compose run --rm worker publish` and `docker compose run --rm serve`. Non-root, **no network interface at all**, read-only root filesystem, dropped capabilities. The container serves a **published generation** -- an identified, hash-verified copy of the corpus that `claude-archive-index publish` mints; serve mounts it read-only and nothing else, verifies every hash before opening anything, refuses with one line and exit 2 if a byte moved, and rolls back by rewriting one pointer. It has no path to the live working store at all. Native and containerized runs over the same fixture produce the same application result for all eleven tools, compared against the **published-generation topology the deployment files select** rather than only the legacy flat mount -- a test, not a promise (`tests/test_container_equivalence.py`). Nine of the eleven are compared over payloads carrying content; `find_related_chats` and `get_session_lineage` agree as structurally empty answers this fixture cannot make answer, so the measured floor is nine. Setup, the Gateway's control gaps, the legacy flat mode and the evidence: `docs/INSTALL.md` section 15, `reports/container_runtime_1.md` and `reports/snapshot_publisher_measurements_1.md`.

**Release truth is machine-generated (v1.14.0):** `claude-archive-doctor capabilities [--json]`
prints what a build actually ships -- package, archive-schema and memory-schema versions, the F20
baseline hash, the eleven tool names **and their reviewed MCP annotations**, the console entry
points, the plugin manifest version, the registered hooks, the transports and the MCP revision the
installed SDK implements, the serve plane's read-only declaration, the maintenance entry point, the
registered sources with their capability flags, the migration list, and the default of every
`CLAUDE_ARCHIVE_*` control. Read-only and offline: it opens no database and binds no socket, and
resolves defaults with the environment cleared so the manifest reports what an unconfigured
install does. `--json` is byte-stable (sorted keys, LF, trailing newline).

## Install

See [`docs/INSTALL.md`](docs/INSTALL.md). In short (Windows PowerShell 5.1 -- these commands are
5.1-clean and run unchanged under PowerShell 7):

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e .[dev]
python scripts\reindex.py        # build the index from both sources
python scripts\install_mcp.py    # register the server in ~/.claude.json
```

Then start a new Claude Code session and ask away (see INSTALL.md for details).

## Web viewer (read-only, localhost)

A plain, server-rendered web UI for browsing the same index in a browser — a second,
read-only *consumer* of the query layer (it adds no MCP tool and changes no contract).

```powershell
python -m claude_archive_mcp.webui                 # opens http://127.0.0.1:8766/
python -m claude_archive_mcp.webui --port 9000     # same, on a different port
```

- **Pages (eleven):** Home (recent conversations), Search (full-text, ranked), Conversation
  (with within-chat search, and a **link** to its Related page -- related is no longer
  rendered inline), Related (one conversation's neighbours across the structural, embedding
  and sidecar tiers, as its own actively-visited page), Timeline (a topic's activity over
  time, filterable by source/project), Digest (rolling daily/weekly activity digest), Stats
  (archive statistics with near-duplicate project names collapsed for reading),
  Observability (read-only adoption panels: archive tool adoption, web-UI engagement, and
  sidecar coverage), Brief (a briefing pack for one project or query), Cockpit (nine
  continuity health surfaces on one page), and Memory (the curated-memory objects,
  read-only). The query logic is reused from the same handlers the MCP tools wrap -- no
  duplicate logic, no MCP transport.
- **Posture (all enforced in code, proven by tests):** binds `127.0.0.1` **only** (no
  config to widen it); opens the SQLite DB `mode=ro`, so it can never write or migrate;
  GET-only; no JavaScript; stdlib `http.server` (no extra dependencies).
- **Port:** defaults to `8766`; override with `CLAUDE_ARCHIVE_WEBUI_PORT` or the `--port`
  flag (the flag wins). The **host is a hard rail** — `--port` changes the port only,
  never the loopback bind. Index first (`python scripts\reindex.py`) — the viewer reads an
  existing DB and will not create one.
- **Freshness:** the long-running viewer records a running-code stamp when it starts;
  `python scripts\check_webui_stale.py` reports whether the live process is serving stale
  code. Restarting the live process is a deliberate, attended action.

## Documentation

**Plan of record:**

- [`ROADMAP.md`](ROADMAP.md) -- the canonical product roadmap (north star, principles, horizons,
  release sequence, do-not-reopen register)
- [`docs/PROGRAM.md`](docs/PROGRAM.md) -- the operational truth layer (per-horizon state, evidence
  gates, closed rulings, dated baselines, recorded policies)
- [`docs/ROADMAP-archive-mcp.md`](docs/ROADMAP-archive-mcp.md) -- **FROZEN-HISTORICAL** as of
  2026-08-13; prior-cycle evidence, not a plan

**Reference:**

- [`docs/CONTRACT.md`](docs/CONTRACT.md) — the frozen 1.0 surface + the 1.x semver policy
- [`docs/INSTALL.md`](docs/INSTALL.md) — Windows install walkthrough
- [`docs/TOOLS.md`](docs/TOOLS.md) — per-tool reference + examples
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — design + rationale
- [`docs/BRIEFING.md`](docs/BRIEFING.md) -- the briefing pack: running it, reading it, freshness
- [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md) -- curated notes in Obsidian: running the export,
  reading a note, what the vault layout guarantees
- [`docs/SOURCES.md`](docs/SOURCES.md) — how each source is parsed, edge cases
- [`docs/SIDECAR_REFRESH.md`](docs/SIDECAR_REFRESH.md) — keeping the comprehension-sidecar store current (attended)
- [`docs/STATE_RECOVERY.md`](docs/STATE_RECOVERY.md) — recover every regenerable local-state surface
- [`SECURITY.md`](SECURITY.md) -- posture, the loopback-only invariant, and artifact integrity
- [`CHANGELOG.md`](CHANGELOG.md)

## Development

```powershell
pip install -e .[dev]
ruff check .
ruff format --check .
mypy --strict src/
pytest
```

Conventions live in [`CLAUDE.md`](CLAUDE.md). The project is built in gated
phases; do not chain past a phase gate.

## Export tools (`tools/`)

Standalone scripts for payloads the archive does not index: the claude.ai parser stores each tool
call's placeholder line, not its input. They open the export's `conversations.json` read-only,
stream it with `ijson`, never touch `archive.db`, and are not part of the installed package.

- `tools/extract_tool_inputs.py` -- one JSONL line per `tool_use` block for the named tools
  (`--tool`, repeatable), a census of every tool name (`--list-tools`), and markdown dumps of
  listed conversations (`--prose-candidates`).
- `tools/recipes_to_box.py` -- converts `recipe_display_v0` calls to Recipe Box schema v1
  documents (`tools/recipe_box_schema_v1.json`) and validates them.

Both take `--check`, which re-derives the counts and outputs and exits non-zero on any
difference. Run them with the project venv's interpreter, which carries `ijson` and `jsonschema`.

## License

> Beginning with v1.14.0, Claude Archive MCP is licensed under PolyForm Noncommercial 1.0.0.
> Personal and other qualifying noncommercial use, modification and redistribution remain free.
> Any commercial use requires a separate commercial license from the copyright holder. Releases
> through v1.13.0 remain available under the MIT terms under which they were originally
> released.

The last MIT release is tagged
[`v1.13.0`](https://github.com/manderse21/claude-archive-mcp/tree/v1.13.0); its license text is
preserved at [`LICENSES/MIT-through-v1.13.0.txt`](LICENSES/MIT-through-v1.13.0.txt). The current
license text is [`LICENSE`](LICENSE).
