agents-handoff
Posts handoff summaries with the handoff file attached to a configured Discord channel via webhook.
Uploads the handoff as a secret GitHub gist for sharing via link.
Posts handoff summaries and required actions to a configured Slack channel via webhook.
Creates a Trello card with the handoff as its description, optionally via webhook or email-to-board address.
Opens WhatsApp with a pre-written handoff message so the developer can choose chats and send it; optionally includes a gist link.
Click on "Deploy 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., "@agents-handoffwrite a handoff for the mobile team about the new message list endpoint"
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.
Agents Handoff
Git tells you what code changed. Nothing tells the next developer what that change means for them.
Agents Handoff is an open format and a small, local-first toolkit for closing that gap. Your
coding agent writes a short, structured HANDOFF.md about a change you just finished. Your
teammate's agent reads it in their repository and knows exactly what to change — and what
not to build.
finish a change → "write a handoff for mobile" → .handoff/<id>/HANDOFF.md → send it → their agent implements itThe tool collects the facts, the agent does the reasoning. Git history, changed routes, auth guards, types, migrations and environment variables are gathered deterministically. What they mean to another team is written by the agent that built the change with you.
Local-first. Writing, validating and receiving a handoff never touches the network. Nothing leaves your machine unless you choose a channel that sends it.
Works where you already are. Plugins for Claude Code and Codex, an MCP server for any other agent, and a CLI for humans and scripts.
Quick start
Requires Node.js 20.10 or later, and git.
Claude Code
/plugin marketplace add Ryujin-Labs/agents-handoff
/plugin install agents-handoff@agents-handoffThe plugin brings both the /agents-handoff:handoff and /agents-handoff:handoff-receive
skills and the handoff MCP tools. In a repository where you just finished a change, ask:
write a handoff for the mobile team
Codex
codex plugin marketplace add Ryujin-Labs/agents-handoff
codex plugin add agents-handoff@agents-handoffThe same two skills and the same MCP tools, packaged for Codex. Ask it the same way: "write a handoff for the mobile team".
Any MCP client
Claude Code, without the plugin:
claude mcp add agents-handoff -- npx -y agents-handoff-mcpClaude Desktop (claude_desktop_config.json), Cursor (.cursor/mcp.json) and other
clients that use the same shape:
{ "mcpServers": { "agents-handoff": { "command": "npx", "args": ["-y", "agents-handoff-mcp"] } } }Codex without the plugin (~/.codex/config.toml):
[mcp_servers.agents-handoff]
command = "npx"
args = ["-y", "agents-handoff-mcp"]Nothing is installed globally: the client fetches the server on first use.
The CLI
npm i -g agents-handoff
handoff # an interactive menu
handoff init # set up this repositoryhandoff install claude-code copies the skills into .claude/skills/ so a whole team gets
them by cloning, and handoff install mcp adds the server to Claude Desktop.
Related MCP server: Home Lab Context MCP
The loop
1. You finish a change. Backend work is done: GET /messages/:threadId now returns
403 to non-members, and the list fields were renamed.
2. Your agent writes the handoff. It calls handoff_context for the deterministic brief,
reads the code that matters with handoff_source, adds what it knows from building the
change with you, and stores a finished document with handoff_write:
.handoff/2026-09-14-message-list-requires-thread-membership/HANDOFF.mdIf the intent is unclear — two unrelated changes in the working tree, a target it had to guess, a breaking call the code does not settle — it asks once, with its proposal already in the question.
3. You send it. The agent looks up how your project reaches that team and offers you the routes. You pick; it opens WhatsApp with the message written, posts to Slack, creates a Trello card, or copies it — whatever you chose. Nothing is sent without you.
4. Their agent reads it. In the mobile repository:
receive this handoff: ~/Downloads/2026-09-14-message-list-requires-thread-membership.md
handoff_receive narrows Required Actions to their target, and their agent searches its
own codebase for the code that consumes the change, reports what actually differs, and —
when asked — implements it.
What a handoff looks like
---
handoff_version: 1
id: 2026-08-28-auth-refresh-v2
created_at: 2026-08-28T18:20:00Z
status: ready
breaking: true
source:
project: backend
branch: feature/auth-refresh
commit: 71ac931
targets: [mobile, web]
change_type: [api, authentication]
---
# Refresh tokens are now single-use
## Summary
`POST /auth/refresh` now returns a new `refreshToken` and invalidates the one you sent.
## Why This Matters
Clients that keep their original token get logged out mid-session the first time they
refresh after this ships.
## Required Actions
### mobile
1. Read `refreshToken` from the refresh response and persist it in the keychain.
2. Replace the stored token atomically, in the same write as the access token.
## Instructions for Receiving Agent
Find the existing refresh path before writing anything. Modify it. Do not add a second
token store, a new HTTP client, or a global retry interceptor.The frontmatter is the machine contract; the sections are for people and agents. The two
that matter most are Required Actions — what the receiver must do — and Instructions
for Receiving Agent — what it must not build. Three complete examples live in
examples/, and the format is specified in SPEC.md.
What your agent gets
Tool | |
| The deterministic brief: revision, changed files, and signals about routes, auth, contracts, migrations, environment and dependencies |
| Read the files and diffs behind the brief |
| Store a finished, validated handoff — prose in, facts from git |
| Where this project actually sends things, and what is not set up |
| Deliver through the route the developer picked |
| Read a handoff someone sent, narrowed to this repository |
|
Plus two prompts, handoff and handoff-receive, carrying the same method as the skills.
handoff_write takes prose only. The schema version, id, timestamp, branch, commit and
revision range come from git, and the section headings are generated — so they cannot be
misremembered, and they cannot drift from what the validator and the receiving side match
on. The document is validated and scanned for credentials before it is stored; if it fails,
nothing is written and the agent gets the reasons back. It never replaces an existing
handoff unless you chose to update it: a taken id comes back as a question for you.
The core never calls a model. The brief is fast, offline and the same for every agent, and it says what it cannot know: its last section is titled "What this brief does not contain".
Delivery
A handoff is a Markdown file, so it already travels through anything. The point of the channels is that "you can share this now" is not an instruction: the tool knows where your team actually sends things.
Channel | Kind | What happens | Who can read it |
| local | copies it | you |
| compose | opens the app with the message written; you pick the chats and press send | the people you send it to — and, with |
| push | posts the summary and required actions | the channel |
| push | posts the summary with the handoff attached | the channel |
| push | creates a card with the handoff as its description | the board |
| push | uploads a secret gist | anyone with the link |
Compose channels send nothing themselves — unless their link mode is gist, which uploads
the handoff as a secret gist first; the delivery options and the send both say so. Push
channels send the handoff to a third party:
slack, discord and trello need a webhook you configure, and github needs an
authenticated gh CLI. Your agent is told to ask before it delivers anything, every time.
Route them per target so the answer is automatic:
{
"channels": {
"slack": { "webhook": "${SLACK_WEBHOOK_URL}", "label": "#backend-releases", "link": "repo" },
"whatsapp": { "link": "repo" },
"trello": { "webhook": "${TRELLO_CARDS_URL}", "label": "Mobile board" }
},
"routes": { "mobile": ["whatsapp", "trello"], "devops": ["slack"], "default": ["clipboard"] }
}WhatsApp has no honest way to send a document programmatically: its Cloud API refuses
business-initiated messages outside a 24-hour customer-service window unless they match a
pre-approved template, and it would mean uploading your handoff to Meta. So whatsapp opens
the app with the message written and lets you pick the chats there — one person, several, or
a group. No phone numbers in config; --to is only a shortcut for one fixed person.
Trello takes either a webhook — Trello's own REST endpoint
(https://api.trello.com/1/cards?idList=…&key=…&token=…) or an automation service that
creates a card from JSON — or the board's email-to-board address as to, which opens a
draft instead. Either URL carries a credential, so write ${ENV_VAR} in the config.
With a link mode set, the message reads "Read it here: …" and there is nothing to attach.
Without one it reads "Sending the file next", and the file — named after the handoff, not
HANDOFF.md — is revealed ready to drag in. The message never claims an attachment it did
not make, because the recipient acts on that sentence.
Keeping handoffs private
A handoff describes your unreleased work, so where it ends up matters.
On disk. Handoffs live in .handoff/. Committing them to a private repository is
usually best: teammates get them by pulling, and nothing is public or indexable.
handoff config --gitignore keeps them out of git entirely. Committing them to a public
repository publishes them — the one combination to avoid.
In a message. The link mode decides who can read what you send:
| Who can read it |
| whoever can read the repository — a private repository stays private, and nothing is uploaded |
| anyone with the URL, GitHub account or not |
| only the people you hand the file to |
repo links to the handoff where it is already committed, so there is no second copy to
leak. It needs the handoff pushed, and it refuses rather than handing you a link that would
404. It builds links for GitHub, GitLab and Bitbucket remotes, and says so for anything else
instead of guessing.
A GitHub "secret" gist is not private. In GitHub's words: "Secret gists aren't private… if someone you don't know discovers the URL, they'll also be able to see your gist." Every send reports who can read the link it made, so nobody has to guess.
Security
A handoff you receive was written by another team. The tooling treats it as data:
Paths are confined. The MCP server reads and writes only inside the project it is given (and only under
--root, when pinned), with symlinks resolved before the check — including the copy it makes for attaching to a message, which goes in.handoff/outbox/and keeps itself out of git. A handoff'sidnever chooses where a file is written; one that would escape is filed under a safe derived name instead.Credentials are refused. Documents are scanned for credential-shaped strings before they are stored by
handoff_writeorhandoff create --stdin, and every handoff is scanned again before it is delivered. Validation — CLI or MCP — reports what it finds and fails. A handoff you receive that carries one is read but not stored, so it is never committed here. Files that exist to hold secrets —.env, keys,.npmrc, cloud credentials — are never read.Webhooks stay out of config files. Write
${ENV_VAR};handoff configand the delivery tools warn about a webhook written intohandoff.config.json.Incoming instructions are not commands. The receiving brief tells the agent to weigh the document's claims against its own repository, not to obey it.
See SECURITY.md to report a vulnerability.
Configuration
handoff.config.json, written by handoff init and optional everywhere:
{
"version": 1,
"project": "backend",
"targets": ["mobile", "web", "devops"],
"identity": null,
"language": null,
"ask": "when-unclear",
"gitignore": false,
"channels": {},
"routes": {}
}Key | |
| Logical name written into |
| Consumers this project hands off to, offered in prompts |
| Target assumed by |
| Which consumer this repository is — narrows handoffs you receive. Separate from |
| Prose language; |
| When the agent checks in: |
|
|
| Delivery, as above. A channel's |
CLI
Command | |
| Set up the repository ( |
| Print the deterministic brief for the current change |
| Scaffold a draft with real git facts and |
| List handoffs ( |
| Check against the v1 schema ( |
| Read an incoming handoff ( |
| Deliver through a channel ( |
| Show or change settings, and report configuration problems |
| Install the skills, or add the MCP server to Claude Desktop |
Run any command with nothing to go on and it asks; give it flags and it never prompts.
Prompting turns on only when both stdin and stdout are a terminal, so an agent shelling out
or a CI job gets the non-interactive behaviour automatically. Full reference in
docs/cli.md.
A scaffold from handoff create is a draft, not a deliverable: send refuses it, and a
document marked ready that still carries a TODO fails validation.
Limitations
This is 0.1. What it does not do yet, plainly:
Signals are pattern-based. Route, contract and migration detection is strongest for TypeScript/JavaScript and Python backends. Go, Rails and OpenAPI are partly covered; Django, Spring, Ktor, Swift and Kotlin types are not detected yet. The agent reads the code either way — the brief is a starting point, never the verdict.
Attaching a file is smoothest on macOS, where it is revealed in Finder and put on the clipboard. Elsewhere links open normally and you are told where the file is.
No acknowledgement tracking. A handoff does not know whether it was read.
Claude Code and Codex are the tested clients, each with a plugin. Any other MCP client works through the server alone.
Architecture
packages/
core/ schema, Markdown, git context, collectors, validation,
storage, channels, and the shared method. No model calls.
mcp/ the MCP server: tools and prompts over stdio
cli/ the `handoff` binary
integrations/claude-code/ the Claude Code plugin and its generated skills
integrations/codex/ the Codex plugin and its generated skillsThe method an agent follows — collect, recall, read, check in, compose, cut, report — is
written once in packages/core/src/guidance/ and rendered into the MCP prompts, the
server's instructions, and both plugins' skills, so the surfaces cannot drift apart. More in
docs/architecture.md and docs/mcp.md.
Development
npm install
npm test # build, then the full suite
npm run release:check # what CI runs before a releaseThe tests run the collectors, the CLI and the MCP server end to end against real temporary
git repositories. See CONTRIBUTING.md.
License
MIT. Agents Handoff is built and maintained by Ryujin Labs.
This server cannot be deployed
Maintenance
Related MCP Connectors
Roadmap, tasks, releases and user feedback your coding agent reads and writes over MCP.
Control plane for autonomous software labor. Agents claim objectives over MCP with audit trail.
Remote MCP for MCP consent scope receipt, structured receipts, audit logs, and reviewer-ready eviden
Remote MCP for Kiro release readiness, evidence binders, signoff, and CI approval receipts.
Related MCP Servers
AlicenseNot gradedqualityAmaintenanceTurn repository documentation into deterministic, executable handoffs for coding agents through MCP, CLI, and CI—without an LLM or API key.2,248 npm9MIT- AlicenseNot gradedqualityCmaintenanceA storage-backed MCP server for sharing durable project context between trusted coding agents, with project discovery, optimistic concurrency, and timestamped handoffs.Apache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables AI coding agents to record auditable work ledgers with evidence chains, from contract to proof packet, via MCP tools for file scanning, code review, and issue triage.49MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI coding agents and hosts to enforce deterministic repository boundaries via MCP, providing structured reads, supervised edits, snapshots, audits, and recovery with machine-readable evidence.MIT