handoff-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@handoff-mcpload the previous session context"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
handoff-mcp
An MCP server that gives AI coding agents persistent memory across sessions.
When you close a Claude Code session and start a new one, the new session has no idea what the previous one was doing. handoff-mcp solves this by saving session context — tasks, decisions, blockers, and file pointers — to a local .handoff/ directory that the next session can load automatically.
The Problem
AI coding sessions are stateless. Every new session starts from zero:
"What was I working on?" — the agent doesn't know
"What decisions were made?" — lost with the previous context window
"What's left to do?" — you have to re-explain everything
This gets painful fast on multi-session projects.
Related MCP server: SloplessCode
How It Works
Session 1 Session 2
┌──────────────┐ ┌──────────────┐
│ Working... │ .handoff/ │ load_context │
│ │──────────────────>│ ↓ guidance │
│ save_context │ tasks/ │ save_context │
│ - close │ sessions/ │ (active) │
│ - summary │ config.toml │ ↓ work... │
│ - decisions │ │ save_context │
│ - blockers │ │ (close) │
└──────────────┘ └──────────────┘At session start, the agent calls handoff_load_context to pick up where things left off. If no active session exists, the response includes session_guidance prompting the agent to establish one via handoff_save_context with session_status: "active" — this creates a persistent .active.json that survives interruptions. At session end, the agent calls handoff_save_context (defaulting to session_status: "closed") to close the session.
Installation
Claude Code Plugin (recommended)
The easiest way to install handoff-mcp is as a Claude Code plugin.
# 1. Install the binary (required — the plugin calls it)
npm install -g handoff-mcp-server
# or: cargo install handoff-mcp
# 2. Add the marketplace (GitHub repo)
/plugin marketplace add alphaelements/handoff-mcp
# 3. Install the plugin (MCP server + skills)
/plugin install handoff-mcp@handoff-mcp-marketplace
# 4. Apply the change
/reload-pluginsThis registers the MCP server and all skills automatically — no manual
.mcp.json or skill file setup needed. The handoff-mcp plugin is enabled
on install, so no separate /plugin enable is needed. Run /reload-plugins
to pick up the change mid-session (a Claude Code restart also applies it).
Naming:
handoff-mcpis the plugin name;handoff-mcp-marketplaceis the marketplace name (thenamefield in.claude-plugin/marketplace.json). Install commands always use<plugin>@<marketplace>.
Platform support
The npm package ships prebuilt binaries — no Rust toolchain, no compiler, and no install scripts are needed. npm downloads only the binary matching your platform.
Platform | x64 | arm64 |
Linux (glibc 2.35+) | ✅ | ✅ |
macOS | ✅ | ✅ |
Windows | ✅ | ✅ |
WSL works as a plain Linux install. For anything else — musl/Alpine, FreeBSD,
32-bit, or glibc older than 2.35 — install with cargo install handoff-mcp
instead, which builds from source.
If you already have a binary you built yourself, point the npm wrapper at it
with HANDOFF_MCP_BINARY_PATH=/path/to/handoff-mcp.
Note: installing with
--omit=optionalskips the prebuilt binary and leaves the CLI unable to start. Reinstall without that flag.Alpine / musl: the Linux binaries are glibc-linked. npm may install one anyway, and it fails to exec with a confusing "not found" error even though the file is present. Use
cargo install handoff-mcpon musl systems.
Optional: task loop (automated TDD + research workflows)
/plugin install handoff-task-loop@handoff-mcp-marketplace
/plugin enable handoff-task-loop@handoff-mcp-marketplace
/reload-pluginsAdds /session-loop (parallel TDD implementation, adversarial testing, Opus
review) and /research-loop (multi-agent investigation, verification, spec
drafting). See plugin-task-loop/README.md.
Optional: memory auto-injection hooks
/plugin install handoff-mcp-hooks@handoff-mcp-marketplace
/plugin enable handoff-mcp-hooks@handoff-mcp-marketplace
/reload-pluginsThis adds hooks that inject relevant project memories on every prompt and file
edit. Disable anytime with /plugin disable handoff-mcp-hooks@handoff-mcp-marketplace —
the MCP server and skills remain active.
Important: The hooks require a
handoffMCP server entry in the project's.mcp.json. Runhandoff-mcp setup --mcp-jsonin the project directory to add it automatically, or add it manually:{ "mcpServers": { "handoff": { "type": "stdio", "command": "handoff-mcp", "args": [], "env": {} } } }Without this entry, the hooks will show "not connected" errors on every prompt. The plugin's built-in MCP server is accessible to tools but not to hooks — this is a Claude Code limitation.
The
handoff-task-loopandhandoff-mcp-hooksplugins ship withdefaultEnabled: false, so they need an explicit/plugin enablestep after install. The mainhandoff-mcpplugin isdefaultEnabled: trueand skips it.
Installing the local development version instead
If you are hacking on handoff-mcp and want Claude Code to load your local
checkout rather than the published GitHub version, register the repository root
(the directory containing .claude-plugin/marketplace.json) as a local
marketplace:
# 1. Build the binary and sync skills + plugin caches
./scripts/install-local.sh
# 2. Register the repo root as a local marketplace (first time only).
# Use the local path here — not the alphaelements/handoff-mcp shorthand.
/plugin marketplace add /absolute/path/to/handoff-mcp
# 3. Install and apply
/plugin install handoff-mcp@handoff-mcp-marketplace
/reload-pluginsAfter the first setup, re-run ./scripts/install-local.sh whenever you change
the code, then restart Claude Code (or /reload-plugins) to load the rebuilt
version. Note that install-local.sh only rebuilds the binary and refreshes
the plugin cache — it does not register the marketplace or enable the plugin,
so steps 2 and 3 are a one-time bootstrap.
Updating
If you installed handoff-mcp as a plugin, it updates along two independent
paths, and you need both. The plugin does not bundle the binary:
plugin.json registers the MCP server as command: "handoff-mcp", which
Claude Code resolves on your PATH. So the marketplace ships the skills and
the plugin manifest, while npm or cargo ships the executable that actually
implements the MCP tools.
(Installed without the plugin, via cargo install or npm install -g alone?
Then only step 1 and the restart apply.)
Updating only the plugin leaves you on the old MCP tools. Updating only the binary leaves you on the old skills.
# 1. Binary — this is what implements the MCP tools
npm install -g handoff-mcp-server@latest
# or: cargo install handoff-mcp --force
# 2. Marketplace catalog — fetch the new version list
/plugin marketplace update handoff-mcp-marketplace
# 3. Plugin — skills and manifest
/plugin update handoff-mcp@handoff-mcp-marketplace
# 4. Restart Claude CodeUpdate the optional plugins the same way if you installed them:
/plugin update handoff-task-loop@handoff-mcp-marketplace
/plugin update handoff-mcp-hooks@handoff-mcp-marketplaceSteps 1 and 3 update different things, and neither substitutes for the other.
/plugin update never touches the binary — it swaps the cached plugin directory,
which contains no executable at all — so no amount of restarting will give you
new MCP tools if you skipped step 1. Conversely, step 1 rewrites the file on disk
but the MCP server Claude Code already spawned keeps running the old image, so
you get the new tools only after step 4.
Step 4 is therefore not optional, and /reload-plugins is not a substitute: it
refreshes skills, not the MCP server process.
Do not expect step 1 to take effect on its own. An installer that overwrites a
running binary in place fails on Linux with Text file busy; installers that
replace the file instead (unlink, then create — what install-local.sh does)
succeed, and leave the already-running server executing the now-deleted old
image until it restarts. Either way, the new tools appear only after step 4.
Verify the update landed:
which handoff-mcp # the binary Claude Code will actually run
handoff-mcp --version
claude plugin list # plugin version, per marketplaceclaude plugin list reports the version you actually have installed. Note that
claude plugin details reads the marketplace source instead, so it shows the
version on offer whether or not you have updated to it — don't use it to
confirm an update.
Local development checkout: ./scripts/install-local.sh does both halves at
once (rebuilds the binary into ~/.local/bin and refreshes the plugin cache).
Restart Claude Code afterwards.
Troubleshooting
Plugin or skills don't show up — run
/reload-plugins, or restart Claude Code. As a last resort,rm -rf ~/.claude/plugins/cacheand reinstall.plugin not found— refresh the catalog with/plugin marketplace update handoff-mcp-marketplace, then reinstall.MCP server won't start — open
/plugin→ Errors tab, and confirm the binary is on yourPATH(which handoff-mcp).You updated, but the MCP tools still behave like the old version — you almost certainly updated the plugin without updating the binary, or you updated the binary but did not restart Claude Code. Work through the four steps above in order.
An older
handoff-mcpearlier on yourPATHshadows the new one. This is the most common cause of "I updated and nothing changed", becausenpm install -gandcargo installwrite to different directories. List every copy and see which one wins:type -a handoff-mcp # every match, in resolution order for d in ${PATH//:/ }; do [ -x "$d/handoff-mcp" ] && echo "$d -> $("$d/handoff-mcp" --version)" doneThe first line is the one Claude Code runs. Remove the stale copies (e.g.
cargo uninstall handoff-mcp, or delete the old file), or put the directory holding the current binary earlier on yourPATH.--versionsays the right number but the behavior is old. A version string only changes when a release bumps it, so two builds of the same version — a stalecargo installand a fresh one — report identically. Compare the file itself rather than the version:ls -l "$(which handoff-mcp)" # check the mtimeWhen in doubt, reinstall the binary and restart Claude Code.
Old versions pile up in the plugin cache. Claude Code keeps each installed version in its own directory under
~/.claude/plugins/cache/handoff-mcp-marketplace/<plugin>/<version>/. This is harmless — the active version is recorded in~/.claude/plugins/installed_plugins.json— but you can reclaim the space by deleting the directories for versions you no longer use.
cargo
cargo install handoff-mcpnpm
npm install -g handoff-mcp-serverBoth install the same binary, by different routes. cargo install fetches the
crate from crates.io and compiles it,
so it needs a Rust toolchain and works on any target Rust supports.
npm install downloads a prebuilt binary for your platform and compiles
nothing — see Platform support for the list.
Build from source
git clone https://github.com/alphaelements/handoff-mcp.git
cd handoff-mcp
cargo build --releaseSetup (non-plugin)
If you installed via cargo/npm (without the plugin), register handoff-mcp as an MCP server in Claude Code manually:
Option A — CLI (recommended):
claude mcp add -s user handoff -- handoff-mcpThe -s user flag registers it globally (available in all projects). Verify with claude mcp get handoff.
Option B — Manual edit of ~/.claude.json:
{
"mcpServers": {
"handoff": {
"type": "stdio",
"command": "handoff-mcp",
"args": []
}
}
}Enable automatic memory injection (optional)
If you installed via the plugin, use handoff-mcp-hooks instead (see above).
For non-plugin installs, run:
handoff-mcp setupThis installs Claude Code hooks into ~/.claude/settings.json and adds a
handoff server entry to the project's .mcp.json (required for hooks to
connect). The command is interactive by default — use -y to skip prompts:
handoff-mcp setup -y # non-interactive (auto-approve everything)
handoff-mcp setup --mcp-json # only add .mcp.json entry (skip hooks)
handoff-mcp setup --check # check if hooks and .mcp.json are configuredRestart Claude Code after running setup.
You can check the current status or remove the hooks:
handoff-mcp setup --check # Show hook status
handoff-mcp setup --uninstall # Remove handoff hooksThe hooks fire on every prompt and file edit, which adds a small overhead per
interaction. If you want to stop automatic injection, run
handoff-mcp setup --uninstall — the memory tools themselves remain available
for manual use, only the automatic hooks are removed.
See Automatic injection via hooks for the manual configuration alternative.
Quick Start
Initialize a project:
The agent calls
handoff_initwith your project name. This creates a.handoff/directory:.handoff/ ├── config.toml # Project settings ├── sessions/ # Session history (TOML files) └── tasks/ # Task tree (directories + TOML files)Load context at session start — the agent calls
handoff_load_context. Ifsession_guidanceis returned, the agent establishes an active session viahandoff_save_contextwithsession_status: "active"before starting work.Work normally — create tasks, track progress, make decisions. The active session persists on disk, so progress survives interruptions.
Save context at session end — the agent calls
handoff_save_contextto close the active session with handoff data (summary, decisions, blockers, references).
Add
.handoff/to your.gitignore— it contains local working state, not code.
Tools
Core Session Management
Tool | Purpose |
| Initialize |
| Load session context, tasks, and git state at session start |
| Save session state — establish an active session or close it with handoff data |
| Incrementally update active session (toggle checklist, add decisions/notes/pointers) |
| List all sessions (open/active/paused/closed) with summary info |
| Get full detail of a specific session by ID |
| Fork a new session from an existing one with context inheritance |
| Merge multiple sessions into one with conflict detection |
Task Management
Tool | Purpose |
| List tasks with filters (status, assignee, milestone, priority, label) |
| Get full task details (notes, done_criteria, schedule, etc.) |
| Create, update, or move tasks; supports |
| Toggle a single done_criteria item by index |
| Log hours worked — adds to |
| Update multiple tasks in one call (status, schedule, assignee, priority) |
Metrics & Scheduling
Tool | Purpose |
| Project metrics: completion %, effort, overdue, budget, milestones |
| Work capacity for a date range, respecting calendar and assignee config |
| Auto-schedule tasks based on dependencies, estimates, and capacity |
Configuration & Team
Tool | Purpose |
| Read project configuration (full TOML as JSON) |
| Update config: settings, calendar, assignees, effort budget, gantt view |
| List team members with task counts and effort stats |
| Add a team member ( |
| Update a team member's fields (partial; null clears a field) |
| Remove a team member and unassign them from every task |
| List milestones ( |
| Add a milestone (date, color, description) |
| Update a milestone (partial) |
| Remove a milestone |
| Patch the project |
| Set the project-level label vocabulary |
| Set |
These CRUD tools and the VSCode extension write the same config.toml, so the
GUI and the MCP server stay in full parity. All writes are atomic (temp-file +
rename) so a concurrent reader never sees a partially-written file.
Cross-Project
Tool | Purpose |
| Overview of all handoff-enabled projects |
| Bulk import tasks and session data from documents |
| Send a cross-project referral (bug, improvement, request) |
| List incoming referrals from other projects (summaries only) |
| Fetch one incoming referral in full — details, suggested tasks, done_criteria, context |
| Update referral status (open → acknowledged → resolved) |
Timer Coordination
Tool | Purpose |
| Start tracking time for a task — delegates to VSCode extension if alive, otherwise starts MCP fallback timer |
| Stop the timer and log elapsed hours to |
| Get current timer state (elapsed, authority, projected total) without stopping |
Project Memory
Tool | Purpose |
| Save a durable project memory (lesson/rule/convention/gotcha); detects exact and near-duplicate memories and hands near-duplicates back for AI-driven merge |
| Return the memories most relevant to the current prompt/file (BM25 + scope-path boost); with a |
| Delete a memory by id (full id or unique prefix) |
| Manual/CLI housekeeping: silently merge exact duplicates, return near-duplicate/stale recommendations, gc old injection sidecars |
For usage best practices (granularity, scope_paths, conflict handling, cleanup), see skills/handoff-memory/SKILL.md.
See Project Memory below for what it is and how to wire automatic injection.
Document Management
Tool | Purpose |
| Create or update a document (auto-splits into sections) |
| Read a document — full, meta, or single section |
| List/search documents with BM25 and filters |
| Delete a document; unlinks from tasks |
| Reconstruct original Markdown from sections, with drift detection |
| Replace a single section by seq (optimistic locking) |
| Walk family tree (ancestors/descendants/related) |
| Visualize inter-document relationships with optional verification status |
| Trace a document's lineage or dependency chain |
| Context injection — staged full/outline, hook-driven |
| Verification matrix: generate, check, check_all, skip, sync, set_refs |
| Verification progress summary with optional per-section details |
| Read-only heuristic scan (import step 1) |
| Atomic bulk write after analysis (import step 3) |
Documents live in .handoff/docs/ as single _doc.<slug>.md files (YAML
frontmatter + body). Large Markdown is split into sections on save;
handoff_doc_reassemble reconstructs the original with drift detection, and
handoff_doc_query feeds staged (outline-first, then full-text) context to
the agent — the same mechanism that powers the hook-driven injection described
below. handoff_doc_verify provides a verification matrix for tracking
per-section review status, implementation/test references, and staleness
detection after spec changes.
Task Data Model
Tasks are stored as a directory tree with status encoded in filenames:
tasks/
├── t1-implement-auth/
│ ├── _task.done.json
│ ├── t1.1-design-schema/
│ │ └── _task.done.json
│ └── t1.2-write-handlers/
│ └── _task.in_progress.json
└── t2-deploy-staging/
└── _task.blocked.jsonStatuses: todo | in_progress | review | done | blocked | skipped
Each task can have:
Assignee — team member key (matches
[assignees.<key>]in config.toml)Priority —
low/medium/highLabels — free-form tags
Done criteria — checklist items (all must be checked before
donetransition)Links — URLs to issues, MRs, or docs
Notes — markdown description
Schedule —
start_date,due_date,estimate_hours,actual_hours,remaining_hours,milestone,pinnedDependencies — task IDs this task depends on (circular deps rejected)
Session Context
When saving context, the agent can record:
Summary — one-line description of what happened
Decisions — what was decided and why, with confidence levels (
confirmed/estimated/unverified)Blockers — what's preventing progress
Checklist — items for the next session
Handoff notes — categorized as
caution,context, orsuggestionReferences — links to files, issues, MRs, wiki pages, or URLs
Context pointers — specific files and line ranges the next session should look at
Git state — current branch, recent commits, and dirty files (captured automatically)
Dashboard
handoff_dashboard scans directories for projects with .handoff/ and shows a summary:
## my-project (3 tasks)
- [in_progress] Implement auth (high)
- [todo] Add tests (medium)
- [blocked] Deploy staging (medium)
## other-project (1 task)
- [review] Update README (low)Configuration
.handoff/config.toml:
[project]
name = "my-project"
description = "Project description"
[settings]
history_limit = 20 # Max closed sessions to keep
done_task_limit = 10 # Max completed tasks to show
auto_git_summary = true # Capture git state automatically
require_estimate_hours = true # Require estimate_hours on leaf tasks (default true)
multi_session = true # Allow multiple active sessions (default true for new projects)
ai_estimate_multiplier = 0.2 # Multiplier turning human estimates into AI-effort hours
timer_provider = "auto" # "auto" | "vscode" | "mcp" | "off"
timer_authority_ttl_secs = 30 # Heartbeat freshness TTL for authority.json
timer_idle_timeout_minutes = 10 # Idle pause threshold for MCP fallback timer
[dashboard]
scan_dirs = ["~/pro/"] # Directories to scan for dashboard
[calendar]
work_hours_per_day = 8
closed_weekdays = ["sat", "sun"]
closed_dates = ["2026-12-25"]
open_dates = []
schedule_mode = "auto" # "auto" or "manual"
overwork_limit_percent = 150
[calendar.day_hours]
fri = 4 # Per-weekday hour overrides
[effort_budget]
total_hours = 500 # Total project effort cap
[assignees.alice]
display_name = "Alice Chen"
color = "#4A90D9"
work_hours_per_day = 8
closed_weekdays = [1, 2] # Per-assignee overrides
[assignees.bob]
display_name = "Bob Martinez"
color = "#E74C3C"
work_hours_per_day = 6
[gantt_view]
sort = "start" # start, id, id-desc, status
zoom = "week" # day, week, month
mode = "compare" # plan, actual, compareAll configuration sections can be updated via handoff_update_config with dot-notation keys (e.g., "calendar.work_hours_per_day": 7).
Estimates and AI effort
handoff-mcp distinguishes the raw human-effort estimate you record on a task from the AI-effort hours used in scheduling and metrics:
require_estimate_hours(defaulttrue) —handoff_update_taskrejects creating or updating a leaf task inin_progress/review/donewithoutschedule.estimate_hours > 0. Parent tasks (with children) and tasks intodo/blocked/skippedare exempt, and an estimate already on the task satisfies the requirement. Set tofalseto opt out.ai_estimate_multiplier(default0.2) — the factor applied to raw estimates to model how long the work takes when an AI agent does it. Always record the raw human-effort estimate; the multiplier is applied at aggregation time byhandoff_get_metrics(total_adjusted_estimate_hoursand per-milestoneadjusted_estimate_hours) andhandoff_get_capacity. Raw values are never overwritten.
Project Memory
Sessions answer "what was I doing last time?". Memory answers a longer-lived question: "what should every session in this project always know?" — durable lessons, rules, conventions, and gotchas that outlive any one session.
Note: The memory tools (
handoff_memory_save,handoff_memory_query, etc.) can always be called directly by the agent. For automatic injection — where relevant memories are surfaced on every prompt without the agent asking — you need to configure Claude Code hooks. See Automatic injection via hooks.
Memories live in .handoff/memory/ (one JSON file per memory, plus per-session
injected/ sidecars). A built-in multilingual similarity engine (Japanese /
English, dictionary-free) ranks relevance and detects duplicates, all in-memory
and sub-millisecond.
Using it directly
The agent can call the memory tools at any time:
handoff_memory_save— record a memory. An exact duplicate is reported (not rewritten); a near-duplicate comes back as aconflictwith both bodies so the agent can merge them (merge_into=<id>,absorb_ids=[…]) or save separately withforce=true. handoff-mcp never merges for you — it surfaces both bodies and lets the agent decide.handoff_memory_query— fetch the memories most relevant to some text and/or files.handoff_memory_delete/handoff_memory_cleanup— prune and de-duplicate the store.
Automatic injection via hooks
MCP is request/response — the server cannot push a memory into the agent's
context on its own. Claude Code hooks close that gap: they fire regardless of
what the agent intends, call handoff_memory_query, and inject the matching memories as
additionalContext. A per-session diff (keyed on the hook session_id) ensures
the same memory is not injected twice in one session — and an edited memory
(new content hash) is re-injected.
The same hooks also call handoff_doc_query, so relevant documents (saved via
handoff_doc_save) are staged into context alongside memories — outline first,
then full text as relevance/budget allows.
Event | Calls | Effect |
|
| Inject memories and documents relevant to the prompt |
|
| Inject memories and documents scoped to the file being edited |
handoff_memory_cleanup (merge exact duplicates, gc old sidecars) is not wired
to a hook — call it manually or from a CLI/cron job when you want housekeeping.
The bundled
plugin-hooks/hooks/hooks.json(used by the "Handoff MCP — Memory & Document Hooks" plugin) already wires bothhandoff_memory_queryandhandoff_doc_queryonUserPromptSubmitandPreToolUse; the JSON examples below show the equivalent hand-written config for non-plugin setups.
Wire hooks in your user/global settings, not in the repo. Hooks are a personal workflow choice; the handoff-mcp repo does not ship a
.claude/hooks config, and you should not commit one into a shared project. Put the config in~/.claude/settings.json(global) or your own.claude/settings.local.json(git-ignored).
Native mcp_tool hook (preferred). Recent Claude Code versions can call an
MCP tool from a hook directly, with no wrapper script. In
~/.claude/settings.json:
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [ {
"type": "mcp_tool", "server": "handoff", "tool": "handoff_memory_query",
"input": { "project_dir": "${cwd}",
"session_id": "${session_id}", "text": "${prompt}" }
} ] }
],
"PreToolUse": [
{ "matcher": "Edit|Write|MultiEdit", "hooks": [ {
"type": "mcp_tool", "server": "handoff", "tool": "handoff_memory_query",
"input": { "project_dir": "${cwd}",
"session_id": "${session_id}", "tool_name": "${tool_name}",
"text": "${tool_input.file_path}",
"file_paths": ["${tool_input.file_path}"] }
} ] }
]
}
}(server must match the name you registered handoff-mcp under — handoff in the
Setup examples.)
Wrapper script fallback. If your Claude Code version doesn't support the
mcp_tool hook type, use the bundled command wrapper
scripts/handoff-memory-hook.py. It reads the
hook JSON on stdin, calls the server over JSON-RPC, and emits
additionalContext — the memory tools return their payload as a JSON string so
both paths parse it identically. Point all three hooks at it:
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [ { "type": "command",
"command": "/path/to/handoff-mcp/scripts/handoff-memory-hook.py" } ] }
],
"PreToolUse": [
{ "matcher": "Edit|Write|MultiEdit", "hooks": [ { "type": "command",
"command": "/path/to/handoff-mcp/scripts/handoff-memory-hook.py" } ] }
]
}
}The script resolves the handoff-mcp binary from PATH (override with
HANDOFF_MCP_BIN) and fails safe: on any error it prints nothing and exits
0, so a memory miss is silent and never blocks your prompt.
Upgrading from a version with a SessionStart cleanup hook
Versions before this fix had handoff-mcp setup (and the
handoff-mcp-hooks plugin) install a synchronous SessionStart hook
that ran handoff_memory_cleanup on every session start. Under many
parallel sub-agents (e.g. /research-loop), that hook could pile up heavy
cleanup calls on the single-threaded server and hang your editor.
The SessionStart cleanup hook has been removed entirely — memory_cleanup
is still available, but only via manual/CLI invocation, never auto-fired.
If you already ran handoff-mcp setup before this change, migrate with
one of the following:
Re-run setup (recommended):
handoff-mcp setup. It now detects and automatically strips the legacySessionStartcleanup hook while leaving your other handoff hooks untouched. Usehandoff-mcp setup --checkfirst if you want to confirm whether the legacy hook is present before touching anything.Manual edit: open
~/.claude/settings.jsonand delete thehooks.SessionStartentry whosetoolishandoff_memory_cleanup(remove the wholeSessionStartkey if that was its only entry).Plugin users:
/plugin update handoff-mcp-hooks@handoff-mcp-marketplaceto pick up the newhooks.json, then restart Claude Code. This one ships inside the plugin, so no binary update is needed.
Restart Claude Code after any of the above for the change to take effect.
Memory settings
All under [settings] in .handoff/config.toml, all with safe defaults
(existing projects need no change), all settable via handoff_update_config:
Key | Default | Meaning |
|
| Master switch. When |
|
| Jaccard similarity at/above which a save is a near-duplicate conflict and cleanup groups a cluster |
|
| BM25 relevance floor for |
|
| Max memories returned per query |
|
| Days without a reference before a memory is flagged stale |
|
| Age at which per-session injection sidecars are garbage-collected |
CLI API
Since v0.15.0, every MCP tool is also callable directly from the shell:
handoff-mcp <group> <action> [--key value ...]All output is JSON on stdout, suitable for scripting and programmatic use
(e.g. child_process.execFile from a VSCode extension).
Examples:
# Memory operations
handoff-mcp memory save --text "Always use atomic_write" --kind lesson --tags safety,io
handoff-mcp memory query --text "atomic" --limit 5
handoff-mcp memory delete --id m-20260630-...
# Task management
handoff-mcp task list --status-filter todo
handoff-mcp task update --id t1 --title "New task" --status todo --estimate-hours 2
handoff-mcp task log-time --task-id t1 --hours 0.5
# Session and metrics
handoff-mcp session load
handoff-mcp metrics
handoff-mcp dashboardAvailable groups: init, task, session, config, memory,
referral, assignee, milestone, calendar, labels, project,
metrics, capacity, schedule, dashboard, timer.
Run handoff-mcp --help to see all groups, or handoff-mcp <group> --help
for actions within a group. See the
CLI API Reference
on the wiki for the full command list.
MCP Resources
URI | Description |
| Active session data (JSON) |
| Project configuration (TOML) |
Recommended CLAUDE.md Setup
Add the following to your project's CLAUDE.md so the agent uses handoff consistently:
## Session Handoff
This project uses handoff-mcp for session continuity.
- **Session start**: Call `handoff_load_context` to load previous session state.
If not initialized, call `handoff_init` with the project name.
If `session_guidance` is present, immediately call `handoff_save_context`
with `session_status: "active"` to establish a persistent session before
starting work. Include inherited context from the previous session.
- **Session end**: Call `handoff_save_context` with a summary, decisions, and blockers.
- **During work**: Use `handoff_update_task` to track progress.
Mark tasks `in_progress` when starting, `done` when complete.
- **Decisions**: Record decisions with confidence levels as they are made,
not just at session end. Use `confirmed` for verified facts, `estimated`
for reasonable assumptions, `unverified` for unknowns.
- **Timer**: Use `handoff_timer_start` / `handoff_timer_stop` to track task time.
When the VSCode extension is running, the timer delegates to it automatically.
When the extension is absent, MCP runs a fallback timer and logs hours on stop.
Use `handoff_timer_get_time` to check elapsed time without stopping.
- **Spec registration**: if the task has a spec or design document,
register it via `handoff_doc_save(task_ids=[...])` so it survives across
sessions and links bidirectionally to the task. Generate a verification
matrix with `handoff_doc_verify(action="generate")` for review tracking.
- **Project memory**: Use `handoff_memory_save` to record durable lessons, rules,
conventions, and gotchas that every future session should know. Use
`handoff_memory_query` to retrieve relevant memories. Near-duplicate memories are
surfaced as conflicts for you to merge or force-save — never merged silently.
Save **as you learn them** during work, not at session end.Skills
This repository includes skill files that make handoff behavior automatic in Claude Code:
Skill | Purpose |
| Core session lifecycle, task management, metrics, scheduling |
| Quick session-start procedure |
| Document management — save, search, verify, import, family tree |
| Memory CRUD, conflict handling, cleanup |
| Cross-project referrals |
| Bulk import from documents |
Plugin users: all skills are included automatically.
Manual setup: copy the skills to your user skills directory:
cp -r skills/* ~/.claude/skills/Compatibility
Claude Code — fully supported (stdio transport)
Other MCP clients — any client supporting the MCP stdio transport
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityBmaintenanceProvides durable project context for coding agents, including project maps, session history, and explicit memories, all stored locally.Last updated2676MIT
- Alicense-qualityCmaintenanceProvides operational continuity for AI coding agents, preserving task state, decisions, checkpoints, and project context across sessions and model switches via MCP.Last updated1Apache 2.0
- Alicense-qualityDmaintenanceProvides persistent memory for AI coding agents through the Model Context Protocol, enabling them to store and retrieve project knowledge across sessions.Last updated89MIT
- Alicense-qualityDmaintenanceProvides persistent, searchable memory and knowledge capture for AI-assisted development, enabling agents to retain decisions, bugs, and patterns across sessions and projects.Last updatedMIT
Related MCP Connectors
Persistent memory for AI agents. Search, store, and recall across sessions.
Persistent memory layer that saves and recalls your project context and preferences.
Universal memory for AI agents and tools. Save, organize and search context anywhere.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/alphaelements/handoff-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server