==========================================================================
 claude-handoff-manager  —  Complete Guide  (v1.1.1)
==========================================================================

claude-handoff-manager is an MCP (Model Context Protocol) server for Claude
Code that automates the conversation "handoff" workflow. When a chat hits the
message cap, Claude saves a structured summary to disk; the next session
detects it and restores the context automatically — no manual copy-paste.

Key facts:
  - PyPI package name : claude-handoff-manager
  - MCP server name   : handoff-manager
  - Version           : 1.1.1
  - Python            : >= 3.10
  - License           : MIT
  - Transport         : stdio
  - Dependency        : mcp >= 1.0.0
  - Storage           : one JSON file per handoff at ~/.claude/handoffs/<id>.json


--------------------------------------------------------------------------
 1. HOW IT WORKS (the mental model)
--------------------------------------------------------------------------

A "handoff" is a JSON record on disk capturing everything the next session
needs to continue your work. Each record looks like this:

  {
    "id": "20260616_125242_home-cd-Documents-myproject",
    "created_at": "2026-06-16T12:52:42.384762+00:00",
    "working_dir": "/home/cd/Documents/myproject",
    "project_name": "myproject",
    "message": "Continuing from previous session. Goal: ...",
    "pending": true
  }

Field meanings:
  - id          : "<UTC timestamp>_<project slug>". Timestamp is
                  YYYYMMDD_HHMMSS. Slug = working dir with leading/trailing "/"
                  stripped, "/" -> "-", space -> "_".
                  e.g. /home/cd/Documents/myproject -> home-cd-Documents-myproject
  - pending     : true right after saving (not yet picked up). Flipped to false
                  once consumed/auto-loaded so it won't reappear.
  - message     : the full handoff summary text Claude wrote.

All listings are sorted NEWEST-FIRST by file modification time, so "most
recent" everywhere means "most recently written/touched."

The two lifecycle moments:

  A. SESSION END (save). When Claude is 2-3 messages from the cap (or you ask
     to "wrap up" / "save a handoff"), Claude drafts a summary and calls
     save_handoff(...). The record is written with pending=true and the message
     is also copied to your clipboard.

  B. SESSION START (restore). Two restore paths:
     1. Automatic (the SessionStart hook). The script session_hook.py runs on
        every new session, finds the most recent PENDING handoff, prints it
        into Claude's context, and immediately marks it consumed. Hands-free.
     2. Interactive (MCP tools). On your first message Claude calls
        list_handoffs(working_dir=<cwd>), shows the list, and lets you PICK
        which one to continue. On pick it calls load_handoff(<id>) then
        consume_handoff(<id>).

  Note: both can be installed. The hook fires first and consumes the newest
  pending one; the tools let you browse/choose any handoff explicitly.


--------------------------------------------------------------------------
 2. HOW TO INSTALL
--------------------------------------------------------------------------

2.1 Quick install (recommended — from PyPI via uvx)

  claude mcp add handoff-manager -- uvx claude-handoff-manager

  Then RESTART Claude Code. Verify:

  claude mcp list

  You should see "handoff-manager" listed, exposing the six tools
  (save_handoff, load_handoff, list_handoffs, consume_handoff,
  copy_handoff_to_clipboard, delete_handoff).


2.2 Alternative: pip / pipx, then register

  pipx install claude-handoff-manager        (isolated)
    -- or --
  pip install claude-handoff-manager

  Both expose the console script "claude-handoff-manager". Register it:

  claude mcp add handoff-manager -- claude-handoff-manager


2.3 Alternative: run from local source checkout

  claude mcp add handoff-manager -- uv run --directory ~/.claude/mcp-servers/handoff-manager claude-handoff-manager

  or, with a Python env that has mcp>=1.0.0 installed:

  claude mcp add handoff-manager -- python -m handoff_manager.server


2.4 Manual MCP config (JSON instead of the CLI)

  {
    "mcpServers": {
      "handoff-manager": {
        "command": "uvx",
        "args": ["claude-handoff-manager"]
      }
    }
  }

  (Swap command/args for "claude-handoff-manager" or the "uv run --directory ..."
  form depending on how you installed.)


2.5 Install the SessionStart hook (for fully automatic restore)

  The MCP tools work without the hook, but auto-inject-on-new-session comes
  from session_hook.py. Wire it into ~/.claude/settings.json:

  {
    "hooks": {
      "SessionStart": [
        {
          "hooks": [
            {
              "type": "command",
              "command": "python3 ~/.claude/mcp-servers/handoff-manager/src/handoff_manager/session_hook.py"
            }
          ]
        }
      ]
    }
  }

  The hook ALWAYS exits 0 (a failing hook must never block your session), reads
  ~/.claude/handoffs, injects the newest pending handoff, and marks it consumed.
  If it can't write the consumed flag it warns on stderr and the handoff may
  reappear next session.


2.6 Clipboard support (matters mainly on Linux)

  save_handoff and copy_handoff_to_clipboard copy the message to your clipboard:
    - macOS   : uses pbcopy (built in)
    - Windows : uses clip (built in)
    - Linux   : tries xclip, then xsel, then wl-copy (Wayland). If none are
                installed it returns "No clipboard tool found. Install xclip,
                xsel, or wl-copy." The handoff is still saved; only the copy is
                skipped. Install one, e.g.:  sudo apt install xclip


--------------------------------------------------------------------------
 3. HOW TO USE (typical workflows)
--------------------------------------------------------------------------

3.1 The everyday flow
  1. You work normally. Near the message cap Claude asks:
     "Would you like me to save a handoff summary for this session?"
  2. Say yes. Claude drafts a summary (goal, key decisions, files touched, next
     steps), shows it, and calls save_handoff. It's now on disk + clipboard.
  3. Type /clear yourself (it cannot be triggered programmatically) or start a
     fresh session.
  4. Next session:
       - With the hook installed  -> handoff is auto-injected; Claude continues.
       - Without the hook         -> say "continue from last handoff" (or "load
         handoff"). Claude lists handoffs, you pick one, it resumes.

3.2 Manually saving mid-session
  Just ask: "save a handoff" / "wrap up and save". Claude writes the summary and
  calls save_handoff(message=..., working_dir=<cwd>, project_name=<name>).

3.3 Resuming a specific older handoff
  Ask "list handoffs". Claude calls list_handoffs and shows IDs + previews. Tell
  it which one; it calls load_handoff(<id>) then consume_handoff(<id>).

3.4 Housekeeping
  - "copy the last handoff to my clipboard" -> copy_handoff_to_clipboard
  - "delete handoff <id>" / "delete all handoffs" -> delete_handoff


--------------------------------------------------------------------------
 4. FULL TOOL REFERENCE  (6 tools)
--------------------------------------------------------------------------

All tools return a plain string. String args default to "" (= "not provided").


4.1  save_handoff
  Purpose: Save a handoff to disk and copy it to the clipboard.
  Params:
    message       (str, REQUIRED)  Full handoff summary text. Must be non-empty.
    working_dir   (str, "")        Project dir. If empty, uses os.getcwd().
    project_name  (str, "")        Friendly name. If empty, falls back to slug.
  Behavior: builds id = "<UTC YYYYMMDD_HHMMSS>_<slug>", writes
            ~/.claude/handoffs/<id>.json with pending=true, copies message to
            clipboard.
  Returns: success text with ID, path, project, clipboard status, PENDING note.
           Blank message -> "Error: handoff message cannot be empty."
  Example:
    save_handoff(
      message="Continuing... Goal: refactor X. Done: A,B. Next: C.",
      working_dir="/home/cd/Documents/myproject",
      project_name="myproject")


4.2  load_handoff
  Purpose: Load a handoff's full content to continue work. Does NOT change
           pending (use consume_handoff for that).
  Params:
    handoff_id    (str, "")   Specific ID. If empty, loads the most recent.
    working_dir   (str, "")   When handoff_id empty, restrict to this project's slug.
  Behavior: if handoff_id given -> load exactly that file (error if missing).
            else -> newest handoff; if working_dir set, only files whose name
            contains that slug, then newest of those.
  Returns: a block with ID, project, saved-at, working dir, pending flag, and
           the full "--- HANDOFF MESSAGE ---".
           Errors: "Error: No handoff found with ID '<id>'.",
                   "No handoffs found. Nothing to resume.",
                   "No handoffs found for working directory: <dir>".
  Example:
    load_handoff(handoff_id="20260616_125242_home-cd-Documents-myproject")
    load_handoff(working_dir="/home/cd/Documents/myproject")   # newest for project


4.3  consume_handoff
  Purpose: Mark a pending handoff consumed (pending=false) so it won't auto-load
           again. Usually called right after load_handoff (or by the hook).
  Params:
    handoff_id    (str, "")   Specific ID. If empty, consumes the most recent
                              PENDING one.
  Behavior: flips pending to false for the given/first-pending handoff.
  Returns: "Handoff '<id>' marked as consumed.", or
           "Error: No handoff found with ID '<id>'.", or
           "No pending handoffs to consume."
  Example:
    consume_handoff(handoff_id="20260616_125242_home-cd-Documents-myproject")
    consume_handoff()   # most recent pending


4.4  copy_handoff_to_clipboard
  Purpose: Copy a handoff's message to the system clipboard for manual pasting.
  Params:
    handoff_id    (str, "")   Specific ID. If empty, copies the most recent.
  Returns: "Handoff '<id>': <clipboard status>".
           Errors: "Error: No handoff found with ID '<id>'." or
                   "No handoffs found."
           (See 2.6 for per-platform clipboard tools.)
  Example:
    copy_handoff_to_clipboard()
    copy_handoff_to_clipboard(handoff_id="20260616_125242_...")


4.5  list_handoffs
  Purpose: List saved handoffs, newest-first, with previews.
  Params:
    working_dir   (str, "")   Filter to this project's slug. Empty = list all.
    limit         (int, 10)   Max number of handoffs to return.
  Behavior: sort newest-first, optional slug filter, truncate to limit. Each
            entry shows ID (+ [PENDING] marker if pending), project, saved
            timestamp, working dir, and an 80-char single-line preview.
  Returns: "Found N handoff(s):\n\n...", or "No handoffs saved yet.", or
           "No handoffs found for: <dir>".
  Example:
    list_handoffs(working_dir="/home/cd/Documents/myproject", limit=5)
    list_handoffs()   # everything, up to 10


4.6  delete_handoff
  Purpose: Delete one handoff, or all of them. Irreversible.
  Params:
    handoff_id    (str, "")     ID to delete. Required unless delete_all=True.
    delete_all    (bool, False) If True, deletes EVERY saved handoff.
  Behavior: delete_all -> unlink all files, report count; else require
            handoff_id and unlink that file.
  Returns: "Deleted all N handoff(s).", "Deleted handoff: <id>",
           "Error: provide a handoff_id or set delete_all=True.", or
           "Error: No handoff found with ID '<id>'."
  Example:
    delete_handoff(handoff_id="20260616_125242_home-cd-Documents-myproject")
    delete_handoff(delete_all=True)   # wipe everything — use with care


--------------------------------------------------------------------------
 5. THE SESSIONSTART HOOK (session_hook.py)
--------------------------------------------------------------------------

Not an MCP tool — a standalone script Claude Code runs on session start.
  - Reads ~/.claude/handoffs; if the dir is missing, exits silently (0).
  - Iterates handoff files newest-first; skips unreadable/non-JSON files and
    non-pending records.
  - For the FIRST pending handoff: prints an [AUTO-HANDOFF] block (project,
    working dir, ID, and the full message wrapped in
    "--- HANDOFF CONTEXT --- ... --- END HANDOFF ---") so Claude sees it, then
    sets pending=false.
  - If it can't write the consumed flag, warns on stderr (handoff may reappear).
  - ALWAYS exits 0 so a hook failure never blocks your session.


--------------------------------------------------------------------------
 6. REFERENCE: files, locations, internals
--------------------------------------------------------------------------

  Handoff storage dir : ~/.claude/handoffs/   (auto-created on server start)
  Handoff filename    : <id>.json  where id = "<UTC YYYYMMDD_HHMMSS>_<slug>"
  Slug rule           : working dir, strip leading/trailing "/", "/"->"-",
                        space->"_"; empty -> "default"
  Sort order          : newest-first by file mtime (all listings)
  Console script      : claude-handoff-manager -> handoff_manager.server:main
  Module entry        : python -m handoff_manager.server
                        (runs mcp.run(transport="stdio"))
  Dependency          : mcp >= 1.0.0
  Source files        : server.py (tools), session_hook.py (hook),
                        __init__.py (version)

Common gotchas:
  - /clear is manual. Claude must never claim it cleared the session — only you
    can type /clear.
  - Slug matching is substring-based. working_dir filters keep files whose name
    CONTAINS the slug, so nested/similar paths can co-match. Prefer an explicit
    handoff_id when you need an exact handoff.
  - load_handoff does not consume. Pair it with consume_handoff (the interactive
    flow does this automatically) or the pending handoff stays pending for the
    auto-hook.
  - Clipboard is best-effort on Linux. Missing xclip/xsel/wl-copy only skips the
    copy; the save itself still succeeds.

==========================================================================
