
Handoff
io.github.kirill-sviridov由 GitHub 帳號 kirill-sviridov 發布v0.4.0更新於 Oct 24, 2026
Cross-session memory for Claude over MCP — prioritised briefs, supersession, cross-project recall.
概覽
為助理提供跨工作階段的持久記憶:記錄目標、決策、死路與下一步,並在下次工作階段開始時回傳依優先順序排序的簡報。
- 可以做什麼
- Handoff 將事件筆記與持久實體筆記存放在本機 markdown 儲存庫中,並以 SQLite FTS5 建立索引,用於排序與全文檢索。工具包含 get_brief(依專案與 token 預算產生交接簡報)、log_event(記錄目標、決策、死路、檔案、問題與下一步,可用 supersedes 撤回舊事件)、search_memory(跨專案的關鍵字或選用語意檢索)、note_entity、checkpoint、consolidate 與 sync。新決策可撤回舊決策,被撤回的內容不會再出現在簡報中,簡報由確定性排序器產生而非大型語言模型。
- 適用情境
- 適合工作橫跨多次工作階段、希望助理恢復時已知目標、目前決策、死路與下一步的情況。也適合需要回想其他專案中做過的決定,或從既有 git 提交紀錄、Claude Code 工作階段紀錄初始化記憶的情況。
- 執行需求
- 透過 stdio 在本機執行,從 PyPI 安裝為 handoff-mcp,通常以 uvx 啟動,因此 PATH 中需要有 uv 或 Python。需設定 HANDOFF_VAULT(共用 markdown 儲存庫路徑),可選設定 HANDOFF_PROJECT;核心功能不需要帳號或 API 金鑰。選用的語意檢索與整併需要額外相依套件,使用 openai 後端時還需要 HANDOFF_EMBED_API_KEY 或相容 OpenAI 的金鑰;選用的多裝置同步需要私有 git 遠端儲存庫與可用的 git 驗證。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Handoff,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
handoff-mcp
[CI] [Python] [Checked with mypy] [Ruff] [License: MIT]
Your agent's memory between sessions. Persistent, cross-project hand-off for Claude over the Model Context Protocol — it remembers the goal, the decisions, the dead-ends, and what's next, so the next session never starts from zero.
Claude forgets everything between sessions. handoff-mcp gives it a memory that
survives the context window: it tracks the progress of your work — goals,
decisions and their rationale, dead-ends, open questions, the next step — and at
the boundary of a session produces a prioritised, token-budgeted brief so the
next session resumes already knowing where you left off.
It is not a context dump. Two things make it different from similarity-based memory stores (mem0 / OpenMemory style):
- Temporal supersession. A new decision retracts an old one; retracted decisions never appear in a future brief. Memory reflects the current state of the world, not a flat pile of contradictory facts.
- Deterministic brief. The brief is assembled by an explainable ranker (recency + importance + supersession), not by an LLM — so it is reproducible and stays within a token budget (a soft cap on event content; see Limitations).
- Your store, multi-device. The vault is your own private git repo, not a hosted service — so memory can follow you across machines (pull-on-start, push-on-checkpoint, conflict-free entity merges) with zero vendor lock. See Multi-device sync.
Status: v0.4, early. The core (vault, brief, supersession, keyword search, cross-project recall) is tested and stable; the semantic, consolidation, importer, and multi-device sync layers are optional and newer. It leans on the agent calling the tools at the right moments — see Limitations for the honest edges.
It is also cross-project: one vault, namespaced per project. The brief is
project-scoped (where did I leave off here), but search_memory recalls across
all projects — so when you say "in one of my projects we did X", Claude can
find and pull that decision out of another project.
Contents
- Demo: two sessions
- Benchmarks
- How it works
- MCP tools
- Cost & API keys
- Semantic recall (optional)
- Memory consolidation (optional)
- Import existing history
- Quickstart
- Connect your MCP client — Claude, Cursor, Codex, Kilo Code, …
- Where your memory lives
- Multi-device sync (optional)
- Agent integration
- Limitations
- Development
- License
Demo: two sessions
python examples/two_sessions_demo.py — session 1 works and stops; session 2 is
a fresh process that resumes from the brief alone.
Session 1 logs its progress (and changes its mind once):
Session 2 calls get_brief() and gets back only the current state — the
retracted JSON decision is gone:
The retracted "flat JSON file" decision never appears. The graph-linked
[[Architecture]]note is pulled in automatically. The brief is ~170 tokens.
And cross-project recall — search_memory("timeout chunking", scope="all") pulls
a decision out of a different project:
Benchmarks
Two offline, deterministic benchmarks — no LLM, no network — so they regenerate
identically anywhere and are pinned by tests/test_benchmark.py. Full numbers,
methodology, and how to reproduce: benchmarks/RESULTS.md.
1. Supersession in isolation (benchmarks/supersession_benchmark.py). A
project's decisions evolve across 8 statements over 4 topics; each has one decision
a later one retracts (JSON→SQLite, cookies→JWT, …). Retrieved by decision (so
every stale fact is reachable), the flat log vs the active view:
Supersession removes exactly the retracted decisions while keeping every current one — and scoring current kept too means an empty answer can't pass as a win. A similarity store with no notion of one fact retiring another behaves like the OFF row.
2. Brief vs naive dump (benchmarks/brief_reconstruction.py). What a resuming
session actually reads — the budgeted, supersession-aware brief vs pasting back the
whole log. As history grows the dump balloons and keeps carrying every retraction;
the brief stays bounded (a soft cap) and contradiction-free while retaining all
key items (e.g. at 228 events: 3063→217 tokens, 14×, 3 contradictions → 0).
These measure the mechanism honestly rather than staging a head-to-head against another store — a fair cross-system run needs both under identical retrieval plus an LLM endpoint we can't reproduce in CI (see ADR-0008).
"Tokens" here and elsewhere in this README are estimated as
len(text) / 4(model-agnostic), not counted with a real tokenizer.
How it works
The markdown vault is the source of truth — human-readable, openable in Obsidian, your data on your disk. The SQLite + FTS5 index is derived from the vault and can be rebuilt at any time; it powers ranking, the token budget, and cross-project full-text search.
See docs/architecture.md and the
ADRs for the design rationale.
MCP tools
Also exposed: an MCP resource session://brief and a prompt resume for
auto-loading the brief at the top of a session.
log_event types: goal, decision, deadend, file, question, next_step.
Cost & API keys
No subscription. No required API keys. The core is free and fully local — your memory is plain files on your disk, and handoff-mcp never phones home (no hosted service, no telemetry).
So most of the value costs nothing and needs no key. The optional layers either run locally or use your own provider — you're never locked into ours.
Semantic recall (optional)
Keyword search (FTS5/bm25) is the default and needs nothing extra. You can enable a semantic layer that fuses keyword and embedding similarity with Reciprocal Rank Fusion:
The backend you pick decides whether this actually understands paraphrases.
The default hashing backend is a lexical baseline — it hashes tokens, so it
adds fuzzy lexical matching (and demonstrates the hybrid pipeline) but does not
recall on meaning when the words differ. For genuine paraphrase-tolerant recall,
choose local or openai, which use learned embeddings.
Pluggable embedding backends, selected with HANDOFF_EMBEDDER — one server,
no forks:
Design (see ADR-0004):
- Pluggable embedder behind an
Embedderprotocol — thehashingdefault is deterministic and dependency-free;openai/localplug in for real semantic quality without changing anything else. - sqlite-vec is an accelerator, not a requirement (
.[semantic]) — vectors persist as BLOBs and search works with an exact cosine scan; ifsqlite-vecis installed and loadable, avec0table provides fast KNN with the same top-ranked results. - The deterministic brief never consults embeddings — semantics only affect
search_memory. - Reranking — recall results (any mode) are reordered by a deterministic
blend of relevance + recency (time-decay) + importance, so fresh, high-priority
memories surface first. No LLM; pass
rerank=Falseto get raw relevance order. - Incremental embedding — events are immutable, so startup only embeds new events (cached vectors are reused); the cache self-invalidates if the embedding model's dimension changes. This keeps heavier local models practical.
To use a lighter/faster local model instead, set HANDOFF_EMBED_MODEL (e.g.
Alibaba-NLP/gte-multilingual-base, 305M/768-dim — also set
HANDOFF_EMBED_TRUST_REMOTE_CODE=1 as that model requires it).
Memory consolidation (optional)
Over a long-lived project the episodic log grows without bound — a volume problem, not just an indexing one. Consolidation ("sleep") folds it down:
consolidate(project?, older_than_days?) distils old finished sessions' active
decisions into the durable entity notes (Architecture, Decisions, Dead-ends, …),
then archives the originals to <project>/archive/ and drops them from the
active index. So the vault shrinks but the lasting knowledge — which the brief
already surfaces — is kept.
- It is the only step that calls an LLM, and it's off unless
HANDOFF_LLM_MODELis set (OpenAI-compatible; reuses the embedder's endpoint settings). The brief, search, and supersession stay deterministic. - Only active events are distilled — a retracted decision is never immortalised. Dead-ends are kept as cautionary facts.
- Originals are archived, not deleted (auditable, reversible).
See ADR-0006.
Import existing history
Bootstrap a project's memory from data you already have, so it's useful from minute one instead of empty:
- git turns each commit into a decision (the subject) plus a files-touched note, timestamped at the commit date — fully deterministic, no LLM.
- claude pulls the first prompt as a goal and file edits as file events from a Claude Code transcript (best-effort).
- Import is idempotent (ids derive from the source), so re-running only adds
what's new. Writes into the same shared vault (
HANDOFF_VAULT).
Quickstart
Connect your MCP client
Claude Code — one-minute install (recommended):
That bundles the server (auto-installed from PyPI via uvx — needs uv on
PATH), a session-start hook that loads the memory protocol, and the
session-handoff / session-planning skills. Manual setup below is for other
clients (or if you prefer explicit config).
handoff-mcp speaks standard MCP over stdio, so it works with any MCP client —
Claude, Cursor, Codex, Kilo Code, Windsurf, Cline, VS Code, Zed, … The config is
essentially the same everywhere; only the file location (and, for Codex, the
format) differs.
Canonical config — the mcpServers JSON block used by Claude, Cursor, Kilo
Code, Windsurf, Cline, and most others:
Codex CLI uses TOML in ~/.codex/config.toml (or run codex mcp add):
VS Code uses "servers" and an explicit type:
Notes:
handoff-mcpmust be onPATH— install it as a tool (uv tool install handoff-mcp; or from source,uv tool install git+https://github.com/kirill-sviridov/handoff-mcp) or, from a checkout, use"command": "python", "args": ["-m", "handoff_mcp.server"].- Set
HANDOFF_PROJECTper agent/repo; keepHANDOFF_VAULTpointed at the same shared vault across all of them (see below). Since 0.4.0, whenHANDOFF_PROJECTis unset the project defaults to the git-root/cwd basename, giving per-repo namespaces with zero config; setting it explicitly still wins. Under Claude Code the directory isCLAUDE_PROJECT_DIR(the session's project) rather than the server's cwd (since 0.4.1). - Running from a checkout with uv? Use
uv run --project /path/to/handoff-mcp handoff-mcp, not--directory:--directorymoves the server's cwd into the checkout, so clients that don't setCLAUDE_PROJECT_DIRwould file every project underhandoff-mcp.
Where your memory lives
One central vault, with a folder per project inside it:
- One vault, not one-per-repo. Cross-project recall (
search_memory) only works because every project lives in a single store. So point every agent'sHANDOFF_VAULTat the same directory and just varyHANDOFF_PROJECT. - It lives outside your code repos, so it never gets committed into your work projects by accident — your project repos stay clean.
- Want backup / multi-machine sync? The vault is plain markdown, so version it
on its own (a private git repo, Obsidian Sync, Dropbox…). For built-in git sync
across devices — pull-on-start, push-on-checkpoint, conflict-free entity merges —
see Multi-device sync;
handoff-sync --setupconfigures the remote and gitignores the derived index for you. - Prefer memory that travels with one repo? Point
HANDOFF_VAULTinside that repo (e.g../.handoff) — but then search sees only that project. The shared vault is recommended.
Multi-device sync (optional)
Your vault is just a private git repo, so memory can follow you across machines. Sync is strictly opt-in — with no git configured, memory works fully on one device.
Entity notes merge cleanly across machines via git's built-in union driver
(*/entities/*.md merge=union in the vault's .gitattributes, written by setup).
In a shell-less client (e.g. Cursor), just ask the agent to sync — the sync
tool needs no terminal. Setup needs working git auth (gh auth login or an SSH
key); if a push fails, the command tells you exactly what to fix.
Agent integration
Knowing when to use it. The server never pushes anything to the model; the model decides when to call the tools. Four layers make that reliable, from most portable to most capable:
-
Tool descriptions (built in) — every tool says when to call it. Works in any MCP client.
-
Server
instructions(built in) — a short ritual the server sends on connect (load the brief at start, log as you work, checkpoint at the end). Portable across Claude Desktop, Cursor, and other MCP clients. -
Claude Code skills (
skills/) — encode the workflow and, crucially, trigger on colloquial cues:session-handoff— when you say "го в следующую сессию" / "that's it for today", the agent knows tocheckpointon its own. It also bootstraps the project's instruction file on first use (CLAUDE.md, orAGENTS.md/.cursor/rules/handoff.mdc/.windsurfrulesper client).session-planning— optional companion: breaks a big task into session-sized chunks and persists the plan in memory.
Install by copying into your skills dir:
(Skills are a Claude Code / claude.ai feature; other agents rely on layers 1–2.)
For non-Claude clients, drop the memory block into the project's instruction file with the
handoff-initCLI (idempotent): -
Claude Code plugin — layers 1-3 in one install; see Connect your MCP client.
For Claude Code specifically, also add this to your CLAUDE.md so the brief loads
automatically even without the skill:
At the start of a session call
get_brief. Record decisions, dead-ends and the next step withlog_eventas you work — one atomic item per call (1-2 sentences with the why), not a whole-session summary; reference durable notes as[[Entity]].checkpointbefore you stop. When I mention past work or another project, callsearch_memory.
Limitations
Honest edges of v0.4, so you know what you're adopting:
- Supersession is explicit, not inferred. handoff-mcp never decides on its own
that one memory retires another — the agent must say so, via
supersedes(by id) orsupersedes_query(by best match). That is deliberate (it's what keeps the brief deterministic and auditable), but it means the quality of the memory depends on the agent actually logging retractions. It won't silently de-duplicate contradictions the way an LLM-extraction store attempts to. Exception:next_step— a newly logged next step auto-retires prior sessions' active next steps (rule-based, recorded on the event; ADR-0009). If a retired step was still valid, re-log it. - It depends on the agent's discipline. The server never pushes anything; value
comes from the model calling
log_event/get_brief/checkpointat the right moments. The tool descriptions and the skill nudge this, but a client that never calls the tools gets an empty vault. Cross-session memory is only as good as what got logged. - The default semantic backend (
hashing) is lexical, not paraphrase-aware. Real paraphrase recall needslocaloropenai(see Semantic recall). The deterministic brief itself never uses embeddings. - The token budget is soft. It bounds event content; section headings and the "Related knowledge" block are chrome on top, so the rendered brief can sit a little above the number. It keeps the brief bounded and flat as history grows — it is not a hard byte cap.
- Single-process freshness. One vault can be shared across processes/agents
(SQLite WAL + a busy timeout let writers coexist), but a running process refreshes
its view of the vault at startup (
_sync_index) — it picks up its own writes live, but another process's new events only on the next launch (async— manual or theHANDOFF_AUTO_SYNCpull-on-brief — re-indexes mid-session when it pulls new events). For concurrent threads inside one process, access to the shared index is serialised by a lock. - Personal/team scale. Ranking loads a project's events into memory; this is fine for thousands of sessions, not tuned for millions (see ADR-0005 for the localized fixes if that day comes).
Development
License
MIT — see LICENSE.
來源:README.md,提交 dd4c511
工具
0版本歷史
1- v0.4.0最新Sep 16, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分