The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Conversation Handoff MCP listing page.
MCP server for transferring conversation context between AI chats or different projects within the same AI.
handoff_load returns stored content inside untrusted-content markers — a warning banner, a one-time boundary token that content cannot forge, and code fences widened past any backtick run in the bodyfrom_project / spawner_dispatch_id / sender_agent_id let an orchestrator record who sent a handoff, without tying the server to any one ecosystemproject:foo, issue:176) and discover them with handoff_search — multi-criteria filtering by tags, text, project, AI, status, and date rangehandoff_list with message count, size, status, and next action — decide on work resumption without loading full conversations--audit flag)handoff_saveWorks with Claude Desktop, Claude Code, Codex CLI, Gemini CLI, and other MCP clients.
| Client | Config File |
|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Claude Code | ~/.claude/settings.json |
| Codex CLI | ~/.codex/config.toml |
| Gemini CLI | ~/.gemini/settings.json |
| Cursor | ~/.cursor/mcp.json |
| ChatGPT Desktop | In-app settings (Developer Mode) |
No pre-installation required - runs via npx.
For global installation:
MCP configuration:
Note: Codex CLI uses TOML format. See Codex MCP documentation for details.
Save conversation context. Key and title are auto-generated if omitted (v0.4.0+). The conversation field stores the complete verbatim content — AI is instructed not to summarize or abbreviate messages (v0.6.1+).
Three optional fields let an orchestrator record who sent a handoff, so the receiving agent can trace it back without the operator typing it in by hand:
| Field | Meaning |
|---|---|
from_project | Source project / repository scope |
spawner_dispatch_id | Dispatch ID of the sending orchestrator, when it runs under one |
sender_agent_id | Stable identifier of the sending orchestrator / agent |
All of them are optional and opaque to this server — no format is enforced, and nothing downstream requires them. Clients that omit them behave exactly as before. They are returned by both handoff_list and handoff_load, and are left out of the output entirely when unset.
Orchestrator integration. This is the pattern tmai uses to auto-populate sender context when it wraps the save path — but nothing here is tmai-specific. Any orchestrator can adopt the same fields, and an ecosystem with no "dispatch" concept can simply leave them out.
On merge. handoff_merge keeps spawner_dispatch_id / sender_agent_id only when every source handoff agrees on them. Unlike from_ai / from_project, which merge into a readable "a, b" list, these are opaque identifiers — a joined string would no longer resolve to anything, so an ambiguous merge drops the field instead.
Get list of saved handoffs (summaries only).
Load full content of a specific handoff.
Delete handoffs.
Merge multiple related handoffs into one. Useful for combining discussions from separate sessions.
| Parameter | Required | Default | Description |
|---|---|---|---|
keys | Yes | - | Array of handoff keys to merge (min 2) |
new_key | No | auto | Key for merged handoff |
new_title | No | auto | Title for merged handoff |
new_summary | No | auto | Summary for merged handoff |
delete_sources | No | false | Delete source handoffs after merge |
strategy | No | "chronological" | "chronological" (by creation time) or "sequential" (array order) |
Check storage usage and limits.
For MCP Apps-compatible clients, handoff_list automatically opens an interactive UI. Non-compatible clients receive the standard JSON response.
Note: According to the MCP Apps specification,
sendMessageshould add messages directly to the conversation and trigger a model response. However, Claude Desktop's current implementation inserts the message into the chat input field instead, requiring the user to press Enter. When you click "Load", the handoff content will be inserted into the input field - press Enter to send it to Claude. This behavior is expected to improve in future Claude Desktop updates.
Starting with v0.4.0, the server automatically starts in the background when an MCP client connects. No manual setup required!
| Mode | When | Behavior |
|---|---|---|
| Auto-Connect (default) | No HANDOFF_SERVER set | Discovers or auto-starts server |
| Explicit Server | HANDOFF_SERVER=http://... | Connects to specified URL |
| Standalone | HANDOFF_SERVER=none | No server, in-memory only |
Handoff data is stored in memory only:
When the shared server goes down during operation:
HANDOFF_RETRY_COUNT (default: 30)The server automatically shuts down after a period of inactivity:
HANDOFF_SERVER_TTL environment variable0 to disable auto-shutdownStandard configuration (recommended) - Just works with auto-connect:
Specify custom server:
Force standalone mode (no server):
For Claude Desktop only. Claude Desktop cannot transfer conversations between projects by default, but since it shares memory space as a single app, this MCP server enables handoffs between projects. Claude Code and CLI tools run as separate processes per tab/session, so handoffs don't work in this mode.
If you prefer manual control:
| Method | Path | Description |
|---|---|---|
| POST | /handoff | Save a handoff |
| POST | /handoff/merge | Merge multiple handoffs |
| GET | /handoff | List all handoffs |
| GET | /handoff/:key | Load a specific handoff |
| DELETE | /handoff/:key | Delete a specific handoff |
| DELETE | /handoff | Delete all handoffs |
| GET | /stats | Get storage statistics |
| GET | / | Health check |
Scenario: Design discussion in Claude Desktop → Implementation in Claude Code
In Claude Desktop - Have a design discussion:
Save the conversation - When ready to hand off:
In Claude Code - Load and continue:
Key Points:
Note: The server automatically starts in the background when the first MCP client connects. No manual startup required.
Customize behavior via environment variables.
| Variable | Default | Description |
|---|---|---|
HANDOFF_SERVER | (auto) | none for standalone, or explicit server URL |
HANDOFF_PORT_RANGE | 1099-1200 | Port range for auto-discovery |
HANDOFF_RETRY_COUNT | 30 | Auto-reconnect retry count |
HANDOFF_RETRY_INTERVAL | 10000 | Auto-reconnect interval (ms) |
HANDOFF_SERVER_TTL | 86400000 (24h) | Server auto-shutdown after inactivity (0 = disabled) |
HANDOFF_AUDIT | (disabled) | true or 1 to enable audit logging (same as --audit) |
| Variable | Default | Description |
|---|---|---|
HANDOFF_MAX_COUNT | 100 | Maximum number of handoffs |
HANDOFF_MAX_CONVERSATION_BYTES | 1048576 (1MB) | Maximum conversation size |
HANDOFF_MAX_SUMMARY_BYTES | 10240 (10KB) | Maximum summary size |
HANDOFF_MAX_TITLE_LENGTH | 200 | Maximum title length |
HANDOFF_MAX_KEY_LENGTH | 100 | Maximum key length |
HANDOFF_MAX_SENDER_METADATA_LENGTH | 200 | Maximum length of spawner_dispatch_id / sender_agent_id |
Everything a handoff stores was written by someone else, so handoff_load returns it inside explicit untrusted-content markers:
from_ai and the rest sit inside the block as well; they are sender-written just like the bodyThe structuredContent field of the same response stays raw and unwrapped, for programmatic consumers that handle escaping themselves.
This is a mitigation, not a guarantee. It makes stored content unambiguously identifiable as data, but a model can still be swayed by sufficiently convincing text — treat handoffs from untrusted senders accordingly.
MIT
trust-delta