Skip to main content

Cross-agent memory handoff via MCP — agents pass the baton instead of re-exploring the repo

Project description

Baton 🏃‍♂️→🏃

Cross-agent memory handoff via MCP. Claude Code, Codex, and Antigravity hand off project context to each other, so a fresh agent starts warm instead of burning quota re-exploring the codebase.

The problem

You hit Claude Code's usage limit mid-task. You open Codex to continue — and watch it re-list directories, re-read files, and re-derive decisions you already made. That re-exploration costs 5–10% of the new agent's quota before any real work happens. Multiply by every handoff, every teammate, every morning you re-open a project.

How it works

┌─────────────┐   ┌──────────┐   ┌─────────────┐
│ Claude Code │   │  Codex   │   │ Antigravity │
│ (hooks +    │   │ (hooks + │   │ (rules +    │
│  CLAUDE.md) │   │AGENTS.md)│   │  MCP config)│
└──────┬──────┘   └────┬─────┘   └──────┬──────┘
       │    MCP (stdio, pull-based)     │
       └───────────────┼────────────────┘
                ┌──────┴──────┐
                │ Baton MCP   │  FastMCP, Python
                │ server      │  4 tools, no LLM calls
                └──────┬──────┘
                ┌──────┴──────────────┐
                │ .agent-memory/      │  ← lives IN the repo,
                │   digest.md         │    committed to git
                │   events.jsonl      │
                └─────────────────────┘

Three principles:

  1. The server is dumb. No LLM calls, no API keys, no embeddings. The agent already has the session in context — it writes the digest and passes it as a tool argument. The server stores strings.
  2. Automation lives client-side. MCP is pull-based; hooks and standing instructions make agents actually use the tools (see Reliability below).
  3. Memory travels with the repo. .agent-memory/ is committed to git. Clone the repo, get the memory — teammates and CI included. No cloud, no accounts, no sync.

Quickstart

Baton is on PyPI as baton-mcp; all you need is uv:

# in any project you work on:
cd ~/my-project
uvx --from baton-mcp baton init
Running from a source checkout instead
git clone <this repo> && cd baton && uv sync

cd ~/my-project
uv run --project /path/to/baton baton init

baton init detects how it was launched and writes matching configs: the uvx install emits uvx --from baton-mcp baton-server entries, the source checkout emits uv run --project /path/to/baton baton-server entries.

baton init is idempotent and does everything for Claude Code: creates .agent-memory/, registers the MCP server (.mcp.json), installs hooks and pre-allowed permissions (.claude/settings.json), and appends workflow instructions to CLAUDE.md/AGENTS.md. It prints the config blocks for Codex (~/.codex/config.toml) and Antigravity (~/.gemini/antigravity/mcp_config.json) rather than silently editing global files.

Watch memory move between agents live:

uvx --from baton-mcp baton watch

The four tools

Tool What it does
memory_load Returns the trusted digest + recent events. Called at session start, before exploring.
memory_checkpoint(digest) Overwrites the digest (≤2000 tokens, enforced). Called after each task.
memory_log(type, text) Appends one event: decision, blocker, or todo.
memory_handoff(to_agent, note) Writes a handoff block into the digest and logs the event.

Agent identity comes from BATON_AGENT in each client's MCP config — no per-call parameters.

Reliability: making agents actually use it

Instructions alone don't guarantee tool calls. Baton makes both directions deterministic in Claude Code:

  • Loading — a SessionStart hook injects the digest into context before the first prompt. Zero tool calls, can't be skipped.
  • Saving — a PostToolUse hook marks .agent-memory/.dirty on any file edit; memory_checkpoint clears it; a Stop hook blocks the agent from finishing while the flag exists, telling it to checkpoint first (with a loop guard via stop_hook_active). Q&A turns pass through untouched.
  • Permissionsbaton init pre-allows mcp__baton__* and pre-approves the project server, so there are zero prompts.

Codex gets the same SessionStart digest injection via ~/.codex/hooks.json (same schema), plus default_tools_approval_mode = "approve" so tool calls run unprompted. Antigravity relies on the AGENTS.md instructions.

Storage

digest.md — one overwritten markdown file, hard-capped at ~2000 tokens: current task, handoff note, architecture summary, annotated file map, decisions & constraints, tried-and-failed (saves the next agent from repeating dead ends), next steps. Its header tells the reading agent to trust it rather than re-verify — that's where the token savings come from.

events.jsonl — append-only log: checkpoint | decision | blocker | todo | handoff | load. Loading appends a load event, so the viewer shows a handoff being received.

Cold start vs. warm start

Measured on a real repo (Codex 0.142 / gpt-5.5, identical prompt: "You are taking over this project from another agent. State the current task and the concrete next steps."):

Cold (no Baton) Warm (Baton)
Exploration commands 12 file reads/listings 0 (one memory_load call)
Total tokens 128,830 54,270 (−58%)
Orientation cost¹ ~77k tokens ~2k tokens

¹ Tokens beyond Codex's fixed per-session system overhead (~52k), i.e. what getting oriented actually cost.

The key ingredient: memory_load prepends a server-side trust preamble ("do NOT re-explore what this states"). Without it, agents load the digest and then re-explore anyway — we measured that too (210k tokens).

Honest claim: the digest reduces exploration, it doesn't eliminate it. An agent will still verify what it's about to change — it just stops re-deriving what's already known.

Limitations

  • Deterministic save enforcement (Stop hook) is Claude Code-only today; Codex and Antigravity rely on instructions plus explicit prompts.
  • Pre-allowed permissions take effect after the repo is trusted once.
  • events.jsonl can merge-conflict across git branches (append-only helps; see roadmap).

Roadmap (deliberately not built)

  • Vector search / semantic retrieval — the digest fits in one read today
  • Server-side summarization — would add API keys; the agent summarizes
  • Cloud sync / multi-machine — git already does this
  • Web dashboard — baton watch does this without ports
  • Merge-friendly event log (per-branch segments)
  • Multi-project global memory

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

baton_mcp-0.1.0.tar.gz (17.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

baton_mcp-0.1.0-py3-none-any.whl (17.8 kB view details)

Uploaded Python 3

File details

Details for the file baton_mcp-0.1.0.tar.gz.

File metadata

  • Download URL: baton_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 17.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for baton_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 27a1a18a83881f230227f37f5ea317eb04579074a2e98e80d2eb8127e9ed2be2
MD5 97dbddb213e33db23ee5eb5c252f7f9c
BLAKE2b-256 9173927926d4d1a60b4b9575206815dce1f2bfba2343a021dffff3ef31aded0c

See more details on using hashes here.

Provenance

The following attestation bundles were made for baton_mcp-0.1.0.tar.gz:

Publisher: publish.yml on P-aveen06/baton

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file baton_mcp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: baton_mcp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for baton_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 afea9e0837c4b70ead7c78682c4a6059c744e039f63cec3eee71aa995170300c
MD5 ffb83f5b9a53868a86a001ce064212ac
BLAKE2b-256 b26f4c68940c24253647d4bdefb897ef377ffd13a443341dfb440fff800c6ca8

See more details on using hashes here.

Provenance

The following attestation bundles were made for baton_mcp-0.1.0-py3-none-any.whl:

Publisher: publish.yml on P-aveen06/baton

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page