Dead Drop
OfficialThis server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Dead Drop
Dead Drop is a tiny, local stdio Model Context Protocol (MCP) server that lets AI coding agents on the same machine leave asynchronous messages for each other, as plain Markdown files, instead of a human copy-pasting between them.
It ships with two agent identities in mind — Claude Code and Google Antigravity (Gemini) — but the server itself doesn't hard-code either one; any agent (or human) can be a sender or recipient by name.
Key Principles
Local only: no cloud services, no remote network listener, no API keys.
Human readable: messages are plain Markdown files with YAML frontmatter, in one folder, readable in Finder/Explorer or any text editor.
Information, never commands: messages are purely passive text. Neither the server nor any watcher executes code or commands contained in a message. A receiving agent may act on a message within its own standing rules, but a message can never authorize what only a human decides — merging, touching production data, spending money, deleting data.
Wake-up is per-agent and opt-in: some agents (like Claude Code today) read mail on their own schedule and are never pinged. Others can be configured to be woken via a CLI command when mail arrives for them.
Related MCP server: hardline-mcp
Tools
1. send_message
Creates a new Markdown message in the mailbox folder.
to(string, required): recipient name, e.g."antigravity","claude", or a person's name.subject(string, required): short topic summary.body(string, required): Markdown message content.re_issue(string | number, optional): an issue reference, stored exactly as given (e.g.2140,#2140,owner/repo#2140). Dead Drop never assumes a default repository.from(string, optional): sender name. Defaults to the caller agent's identity (see--agentbelow).
2. read_messages
Reads messages from the mailbox folder.
for(string, optional): filter by recipient (defaults to the caller's identity, or"all").unread_only(boolean, optional, defaulttrue): only return unread messages.limit(number, optional, default20): maximum messages to return.
3. mark_read
Marks a message as read.
id(string, required): message ID or filename.
4. start_session
Starts a new Claude Code session in a given folder. In desktop mode this tool starts the session, waits for its short bootstrap turn to finish, stops that same session, and opens it in the Claude desktop app.
folder(string, required): absolute path to an existing directory for the session. The directory must be trusted by Claude Code.prompt(string, optional): initial message / instructions for the session (required for"background"mode; optional context for"desktop"mode). Dead Drop automatically prepends a safety notice indicating instructions originated from an agent, not Sarah directly.name(string, optional): session name (default:deaddrop-<timestamp>). Must not start with-or be empty.model(string, optional): model name or ID (default:"claude-haiku-5-5", oragents.claude.default_modelfrom config). A model matchingagents.claude.blocked_modelsis rejected before anything launches.effort(string, optional): reasoning effort level ("low","medium","high","xhigh","max").permission_mode(string, optional): permission mode (default:"auto"from config). Must be inagents.claude.allowed_permission_modes.handoff(string, optional,"desktop"|"background"): handoff mode (default:"desktop"from config).from(string, optional): sender identity. Defaults to caller agent identity.
Returns desktop session ID (local_<sessionId>) and instructions for delegating the task via the desktop app's send_message tool, or background session details if handoff: "background".
5. start_antigravity_session
Starts a new Antigravity (Gemini) conversation with agentapi new-conversation and returns its conversation ID.
prompt(string, required): initial instructions. Dead Drop prepends the same "came from an agent, not Sarah" notice asstart_session.title(string, optional): conversation title shown in Antigravity. Must not start with-or be empty.model(string, optional,"flash_lite"|"flash"|"pro"): Antigravity model tier. Omitted means agentapi's default,flash.project(string, optional): the Antigravity project to start the conversation in: an exact project name (e.g."intervent") or an absolute folder of one. See Antigravity project id for the default.register_as(string, optional): also register the conversation as a Dead Drop recipient under this name, so mail sent to it wakes the conversation. Addsagents.<name> = { wake_on_mail: true, wake_method: "agentapi", conversation_id, project_id }to.config.json. The name must be 1-64 lowercase letters, digits or-, not starting with-. Dead Drop refuses, before starting anything, a name that is already an agent in.config.jsonor the defaults, is reserved (all,sarah,unknown), or already appears astoorfromon any message in the mailbox (recipients likewrenget mail without a config entry). It never overwrites an existing recipient.from(string, optional): sender identity. Defaults to caller agent identity.
The result names the conversation ID, the model, the project actually used and where that project came from (the project parameter, .config.json, or the sidecar), so a caller who forgot project can see where the conversation landed. All values are passed to agentapi as separate arguments with no shell, and the prompt always follows --, so it can never be read as a flag. The session token is passed only in agentapi's environment; it never appears in arguments, results, errors or logs.
--profile is not supported yet. It isn't yet known what an Antigravity profile can set (for example which model it uses), so a caller couldn't tell what they were starting. Issue #26 investigates profiles and adds the parameter once that's documented.
File Format
Mailbox folder: defaults to
~/deaddrop, overridable withDEADDROP_DIR.Filename pattern:
YYYY-MM-DD_HHmmss_<from>_to_<to>_<slug>.mdExample:
2026-09-21_144808_claude_to_antigravity_initial-handshake.md
---
id: 2026-09-21_144808_claude_to_antigravity_initial-handshake
date: 2026-09-21T14:48:08+10:00
from: claude
to: antigravity
subject: Initial handshake
re_issue: "42"
read: false
read_at: null
---
Hello! Dead Drop is live.Configuration
All configuration lives in a single JSON file inside the mailbox folder: <mailbox>/.config.json (e.g. ~/deaddrop/.config.json), under a per-agent agents map:
{
"agents": {
"antigravity": {
"wake_on_mail": true,
"wake_method": "agentapi",
"conversation_id": "<conversation-id>",
"project_id": "<optional-antigravity-project-id>",
"agentapi_path": "/optional/custom/path/to/agentapi"
},
"claude": {
"wake_on_mail": false,
"wake_method": "unsupported",
"default_model": "claude-haiku-5-5",
"blocked_models": [],
"default_permission_mode": "auto",
"allowed_permission_modes": [
"auto",
"default",
"acceptEdits",
"plan"
],
"default_handoff": "desktop",
"cli_path": "/optional/custom/path/to/claude"
}
}
}Each key under agents is a recipient name (matched case-insensitively against to/for), with:
wake_on_mail(boolean): whether Dead Drop should try to wake this agent when mail arrives for it. Defaults tofalse— an agent that is missing fromagentsentirely, or haswake_on_mailunset orfalse, is never woken. Wake-up is opt-in only. Either way, the message file is always written; when wake-up is skipped, Dead Drop just logs why to stderr.wake_method: how to wake this agent, dispatched by string so adding a new agent's wake method later doesn't require a schema change:"agentapi"— the only method that's actually wired up today. Pings the agent via Antigravity'sagentapiCLI, using that agent'sconversation_id."unsupported"— this agent cannot be woken from outside at all. This is Claude Code's value today: Claude Code channels only work in the terminal research-preview mode, not the desktop app, so Dead Drop reportswake_method: "unsupported"honestly (for humans and for a future settings UI) instead of silently no-op'ing.
conversation_id(required whenwake_methodis"agentapi"): the ID of the one dedicated conversation this agent should be woken in. Dead Drop never guesses the most recently used conversation, and never creates a new one. If it's missing or empty, Dead Drop still writes the message file — it just logs a warning to stderr and skips the wake-up.project_id(optional, only meaningful withwake_method: "agentapi"): the Antigravity project ID passed toagentapiasANTIGRAVITY_PROJECT_ID. On an agent written byregister_asit is the project its conversation was started in, and wakes use it. Onagents.antigravityit is also the default project forstart_antigravity_session. See Antigravity project id.agentapi_path(optional, only meaningful withwake_method: "agentapi"): absolute path to theagentapiCLI executable, if it isn't on yourPATH. Can also be set via theDEADDROP_AGENTAPI_PATHenvironment variable, which takes priority over this field. If neither is set, Dead Drop checks the common install location under your home directory, then falls back to assumingagentapiis onPATH.default_model(optional, Claude only): the modelstart_sessionuses when the caller gives none. Defaults to"claude-haiku-5-5"when unset. The settings UI never writes this field; edit it by hand.blocked_models(optional, Claude only, default[]): a list of model denylist patterns. Each pattern is a whole-string glob:*matches any run of characters, everything else is literal, and matching is case-insensitive."sonnet"blocks onlysonnet;"*sonnet*"blocksclaude-sonnet-5-5,SONNETand any other model containingsonnet. The check runs on the resolved model, so a blockeddefault_modelis rejected too. A blocked start fails with an error naming the model and pattern, and nothing is spawned or logged. Malformed values (anything other than an array of non-empty strings) fail closed: everystart_sessionfor Claude is rejected until the value is fixed. The settings UI never writes this field; edit it by hand.
Settings Web UI (deaddrop settings)
To view and edit agent wake configurations without hand-editing JSON, start the local settings UI:
npm run settings
# or
node index.js settings [--port=3344]
# or
npx deaddrop settingsThis starts a lightweight, on-demand local HTTP server that:
Binds to loopback only: strictly
127.0.0.1(never0.0.0.0), running only in the foreground while the command is active. PressCtrl+Cto stop.Port: defaults to
3344(falling back to an available ephemeral port if in use, or customizable via--port=<n>).Agent Wake Configuration:
Dynamically lists all agents configured under
agents.Lets you toggle
wake_on_mailper agent.Lets you edit
conversation_idfor agents usingwake_method: "agentapi"(such as Antigravity).Displays agents with
wake_method: "unsupported"(such as Claude Code) with a disabled toggle and a clear explanation of why external wake-ups are unsupported.Atomically saves configuration edits back to
<mailbox>/.config.jsonwhile preserving unmanaged properties.
Recent Messages: provides a read-only list of recent mailbox messages (date, sender, recipient, subject, read/unread status). Message bodies are not rendered.
Environment variables
Variable | Purpose | Default |
| Mailbox folder |
|
| Path to the | auto-detected, else |
| Path to the | auto-detected (env -> config -> PATH -> desktop bundle) |
Claude Code Sessions (start_session)
Handoff Modes
"desktop"(default): Starts a session in the background with a minimal bootstrap prompt, waits for it to become idle, stops it withclaude stop <id>, and opens it in the Claude desktop app viaclaude://resume?session=<sessionId>(macOS only). The desktop app adopts it aslocal_<sessionId>. To run the actual task live in the desktop app, the caller (which must be a desktop Claude Code session) calls its desktopsend_messagetool withsession_id: "local_<sessionId>". If the caller does not have desktop session tools, it should usehandoff: "background"."background": Starts a standard background CLI session that executes unattended until completion.promptis required.
Prerequisites
One-time Login: The Claude CLI must be authenticated independently of the desktop app (
claude auth login).Trusted Workspace: The target
foldermust already be trusted by Claude Code; otherwise the CLI will refuse with"Workspace not trusted".
Permission Guardrail
Allowed permission modes default to
["auto", "default", "acceptEdits", "plan"].bypassPermissionsis strictly rejected by default unless Sarah explicitly adds it toagents.claude.allowed_permission_modesin<mailbox>/.config.json.
Opting in to bypassPermissions
Add it to the list. The mode is accepted only when it appears in the list; there is no other special case for it:
{
"agents": {
"claude": {
"allowed_permission_modes": ["auto", "default", "acceptEdits", "plan", "bypassPermissions"]
}
}
}Bypass mode means the Claude Code session runs its tools without asking for per-action approval. This is the owner's choice; Dead Drop never enables it on its own.
A missing allowed_permission_modes field in an agent's entry falls back to the default list above. The fallback applies when the field is absent from the agent entry, not only when the whole config file is missing.
default_permission_mode and permission_mode
default_permission_mode(inagents.claude, default"auto") is used whenstart_sessiongets nopermission_mode.A per-call
permission_modeoverrides it for that call.Either value must be in
allowed_permission_modes, orstart_sessionrejects the call.
Keep orchestrator and builders in the same mode
Claude Code holds a cross-session message for its user's approval when the sender runs in a different permission mode, and may let it expire. So an orchestrator and the builders it starts through Dead Drop should use the same permission mode. This is the behaviour of Claude Code's SendMessage tool, not a Dead Drop guarantee.
The Claude desktop app's own start_session (not Dead Drop's) only starts a session in the caller's permission mode or a lower one (as of 2026-10-27).
Session Log (.sessions.jsonl)
Every spawned session is logged to <mailbox>/.sessions.jsonl (e.g. ~/deaddrop/.sessions.jsonl) with restricted file permissions (0600). A launcher field says which tool started it. Claude sessions ("launcher":"claude") track handoff and outcome (handed_off, still_running, or background):
{"time":"2026-10-20T12:00:00+08:00","launcher":"claude","from":"antigravity","folder":"/path/to/repo","name":"deaddrop-1729468000000","model":"claude-haiku-5-5","effort":"medium","permission_mode":"auto","handoff":"desktop","outcome":"handed_off","id":"b73a79df","sessionId":"uuid-value","prompt":"..."}Antigravity conversations started by start_antigravity_session are logged with "launcher":"antigravity". The session token is never logged:
{"time":"2026-10-28T10:00:00+08:00","launcher":"antigravity","from":"wren","title":"Second opinion","model":"pro","project_id":"<project-id>","project_source":"param","conversation_id":"<conversation-id>","register_as":"gemini-reviewer","prompt":"..."}Viewing Sessions
Inspect running background sessions:
claude agents --json --allOpen in Claude Desktop: once a session is idle or stopped, open
claude://resume?session=<sessionId>
Installation
git clone <this-repo-url>
cd deaddrop
npm install
npm testRegistration
Each agent is registered with its own --agent=<name> identity, which becomes its default from when sending and its default filter when reading.
We recommend project-scoped registration so Dead Drop tools are only loaded in sessions working on this repository, preventing unrelated projects from accidentally sending or receiving messages.
Claude Code
Use --scope local to register Dead Drop for this project repository only:
claude mcp add --scope local deaddrop node /path/to/deaddrop/index.js -- --agent=claudeThis stores the configuration in local project settings without injecting Dead Drop into unrelated Claude Code sessions. (Only use --scope user if you deliberately want Dead Drop active in every project on your machine).
Antigravity (Gemini)
Antigravity natively discovers workspace-scoped MCP servers from .agents/mcp_config.json at the repository root. Create .agents/mcp_config.json in your project workspace:
{
"mcpServers": {
"deaddrop": {
"command": "node",
"args": [
"/path/to/deaddrop/index.js",
"--agent=antigravity"
],
"env": {
"DEADDROP_DIR": "/path/to/your/mailbox"
}
}
}
}Replace node with an absolute path if node isn't on the PATH that Antigravity launches with, and /path/to/deaddrop with where you cloned this repo.
Make sure.agents/ is included in your .gitignore so machine-specific absolute paths are never committed to version control.
Multi-Project Setup & Mailbox Isolation
When using Dead Drop on multiple projects on the same machine, MCP registration scope is only half of the picture — the mailbox location (DEADDROP_DIR) controls message isolation:
Separate by default (Isolated projects): When setting up Dead Drop for a second, separate project, configure a distinct
DEADDROP_DIRfor that project's registration (e.g.DEADDROP_DIR: "/Users/<you>/deaddrop/other-project"or a project-local.deaddropfolder), in addition to using a project-scoped registration (--scope localor.agents/mcp_config.json). If two projects use separate MCP registrations but both point to the default~/deaddropmailbox, messages addressed to generic agent names ("claude","antigravity") will still land in the same shared mailbox and cross over.Shared on purpose (Cross-project coordination): Deliberately pointing two projects at the same
DEADDROP_DIR(e.g. the default~/deaddrop) is how you opt into cross-project messaging when you specifically want agents working on different repositories to coordinate with each other.For example, to share this mailbox with a second project in Antigravity, create
/Users/<you>/Repos/other-project/.agents/mcp_config.json:{ "mcpServers": { "deaddrop": { "command": "node", "args": [ "/Users/<you>/Repos/deaddrop/index.js", "--agent=antigravity" ], "env": { "DEADDROP_DIR": "/Users/<you>/deaddrop" } } } }(Remember to add
.agents/to.gitignoreinother-projecttoo, since it contains local absolute paths. Adjust--agent=if you want a distinct identity for that project's agent).For Claude Code in the second project's folder:
claude mcp add --scope local -e DEADDROP_DIR=/Users/<you>/deaddrop deaddrop node /Users/<you>/Repos/deaddrop/index.js -- --agent=claude
Antigravity Wake-Up & Sidecar Setup
Antigravity's agentapi CLI requires two ephemeral credentials to communicate with the running IDE instance:
ANTIGRAVITY_LS_ADDRESS: Host and port of the active language server local RPC endpoint.ANTIGRAVITY_CSRF_TOKEN: A per-launch authentication token.
When Dead Drop runs in a process that Antigravity did not launch (such as Dead Drop's MCP server spawned by Claude Code), these environment variables are absent from process.env. To discover them reliably without fragile process-table scraping, Dead Drop uses an Antigravity sidecar:
Run the sidecar registration command once:
npm run setup-sidecarThis writes
~/.gemini/config/sidecars/deaddrop/sidecar.json.When Antigravity starts, its internal
SidecarManagerautomatically starts Dead Drop's background exporter (bin/sidecar.js), providing it with the live credentials.The sidecar atomically writes
<mailbox>/.antigravity_session.json(file permissions0600).When Claude Code (or any external caller) sends mail to Antigravity, Dead Drop reads
<mailbox>/.antigravity_session.jsonand passes the active address and CSRF token toagentapi.The sidecar also exports the Antigravity project it runs in (
project_id).agentapineeds that project id when it is called from a process Antigravity didn't launch:new-conversationfails with "project_id is required when providing project_env_config" without it.
After updating Dead Drop, restart the sidecar (restart Antigravity, or disable and re-enable the deaddrop sidecar) so the running sidecar picks up the new code and exports project_id. Until it does, wakes still work as before but carry no project id, and start_antigravity_session needs project or agents.antigravity.project_id.
Antigravity project id
agentapi calls from Dead Drop use the first of these that is set:
the
projectparameter ofstart_antigravity_session, resolved read-only against Antigravity's project registry (~/.gemini/config/projects/<id>.json) by exact project name or by absolute git folder. An unknown or ambiguous value is an error and nothing starts;the woken agent's own
project_id(wakes only);agents.antigravity.project_idin.config.json;the sidecar's exported
project_id(orANTIGRAVITY_PROJECT_IDwhen Dead Drop itself runs inside Antigravity).
The sidecar is registered for all projects, so its project is whichever Antigravity started it in. Pass project whenever the conversation belongs to a specific project. If none of these is set, start_antigravity_session fails before starting anything; a wake goes ahead without a project id.
Graceful Failure
If <mailbox>/.antigravity_session.json is missing, unreadable, or stale (the sidecar PID is no longer running), Dead Drop skips the wake-up, logs an informative notice to stderr advising to run npm run setup-sidecar, and proceeds normally. As with all Dead Drop wake failures, the message file itself is always delivered safely.
When agentapi itself fails, it exits with code 1 and prints the reason as JSON on stdout, usually with nothing on stderr. Dead Drop reports agentapi's own error text, the exit code, stderr and stdout in the wake-up note (with the session token redacted), so the cause is visible instead of a bare "Command failed".
Known Limitations
Multi-window Antigravity: Dead Drop v1 assumes a single running Antigravity instance. In multi-window or multi-profile setups, the sidecar reflects the most recently active instance.
Any other agent
Run the server with --agent=<name> (or set DEADDROP_AGENT=<name>), pointed at a shared DEADDROP_DIR, and it can send and receive mail the same way.
License
AGPL-3.0 — if you modify Dead Drop and run it as a network service, you must make your modified source available to users of that service.
This server cannot be deployed
Maintenance
Related MCP Connectors
Inbox for AI agents: one address per agent to message, share files and pay other agents.
Shared memory and mail for your AI agents. Verified with Claude Code; other MCP clients in testing.
End-to-end encrypted messaging and work coordination for autonomous AI agents.
Durable agent-to-agent handoffs and shared scratchpad for multi-agent workflows.
Related MCP Servers
- AlicenseAqualityDmaintenanceLocal inter-agent messaging for AI coding agents via filesystem relay.42MIT
- AlicenseAqualityBmaintenanceEnables local AI coding agents to message each other on one machine using a durable SQLite mailbox and live-ask tools.91MIT
- AlicenseCqualityAmaintenanceEnables durable message passing between Codex project agents with MCP tools and automatic wake-up delivery, storing messages as JSON on disk.32MIT
- AlicenseNot gradedqualityBmaintenanceEnables real-time, file-based messaging between terminal AI agents (like Claude Code, Gemini CLI) allowing them to collaborate on tasks directly without a server or network, using JSON files and atomic rename for coordination.MIT