Skip to main content
Glama
README.md
# handoff-mcp-server

An MCP server that lets an AI coding agent save the state of a task and pick it up again in a later session. The saved state is a small validated JSON document, not a transcript. It loads back as a short briefing: the objective, where the work stands, the files that matter and which lines in them, the decisions already made and why, and the next steps in priority order.

## Why

Long coding sessions end before the task does. The context window fills, the session times out, the laptop closes. The next session starts from zero, or from a transcript that is mostly noise. What actually carries a task across sessions is small: what I was doing, where in the code, what I decided and why, and what is left. This server stores exactly that and nothing else.

I wrote it for my own multi-day tasks in Claude Code. A saved handoff is usually one to two thousand tokens when loaded, against tens of thousands for the session it came from.

## Tools

The server exposes three tools over stdio.

| Tool | What it does |
|---|---|
| `save_handoff` | Validates the state against a Zod schema and writes it to `~/.handoffs/<session_id>.json`. Returns the path and a token estimate. |
| `load_handoff` | Reads and re-validates a saved state and returns it formatted as a briefing (see below). |
| `list_handoffs` | Lists every saved handoff with its objective, status and last update. |

`save_handoff` takes:

- `session_id`: a short slug, used as the filename (`auth-bug-fix`, `payment-feature`).
- `task_objective`, `task_status` (`in_progress`, `blocked`, `needs_review`), `progress_summary`.
- `files`: a list of `{ path, relevance, focus_ranges? }`. Each focus range is `{ start, end, note }`, so the next session can open the right lines instead of the whole file.
- `decisions`: a list of `{ decision, rationale }`. The server stamps each with the save time.
- `next_steps`: a list of `{ action, priority }` with priority `high`, `medium` or `low`.

## The briefing format

`load_handoff` returns Markdown shaped like this. The example is fictional.

```markdown
# Session Restoration

## Task: Make invoice PDF generation idempotent
**Status:** in_progress
**Progress:** Root cause found: the job enqueues twice on retry. Fix written, not yet tested.

## Relevant Files

### app/jobs/invoice_pdf_job.rb
Where the duplicate enqueue happens

Focus areas:
- Lines 14-31: perform method, the retry path re-enqueues instead of re-running
- Lines 40-52: new guard using the invoice id as the lock key

### spec/jobs/invoice_pdf_job_spec.rb
Has the failing case to extend

## Key Decisions

**Decision:** Lock on invoice id rather than job id
**Rationale:** Job ids change on retry; invoice id is stable and already indexed

## Next Steps

- [HIGH] Add a spec for the retry path
- [MEDIUM] Check the two other jobs that use the same retry helper
- [LOW] Remove the debug logging once the spec is green
```

Next steps are sorted by priority. Decisions are only printed when there are any.

## Install

Requires Node 18 or later.

```bash
git clone https://github.com/Abdelwahab313/handoff-mcp-server.git
cd handoff-mcp-server
npm install
npm run build
```

`npm install` runs the build, so `build/index.js` exists once it finishes.

### Claude Code

```bash
claude mcp add handoff-protocol -- node /absolute/path/to/handoff-mcp-server/build/index.js
```

### Claude Desktop or any other MCP client

Add to the client's MCP configuration:

```json
{
  "mcpServers": {
    "handoff-protocol": {
      "command": "node",
      "args": ["/absolute/path/to/handoff-mcp-server/build/index.js"]
    }
  }
}
```

## How I use it

At the end of a session, or when the context is getting full, I ask the agent to save a handoff with a named session id. At the start of the next session I ask it to load that id before anything else. The briefing is short enough to read in full, so I can correct it before the agent acts on it.

## Limits

- Storage is a flat directory in the home folder. Saving with an existing `session_id` overwrites it. There is no history.
- `session_id` becomes the filename unchanged. Keep it to letters, digits, dots and dashes.
- The token estimate is characters divided by four. It is a rough guide, not a count.
- No test suite yet. The schema validation on both save and load is the only guard.

## License

MIT