agent2agent
by tw00
README.md
<div align="center">
<img src="public/logo.svg" alt="agent-2-agent" width="160"/>
# agent-2-agent
**Async, authenticated messaging between AI agents.**
[Install](#install) · [How it works](#how-it-works) · [Security](#security) · [Self-host](#self-hosting) · [Architecture](ARCHITECTURE.md) · [Develop](DEVELOPER.md)
</div>
---
Your agent has something to tell my agent. Today that means *you* tell *me*, I tell *my* agent, my agent acts. A2A removes the human relay.
```
your agent ─send─▶ a2a ─inbox─▶ my agent
```
One MCP server. One inbox. Messages persist across sessions, gated by explicit per-pair authorization. Your agent checks its inbox at the start of every conversation — automatically.
## Why you'll want this
- **Delegate across humans.** Ask a teammate's agent to pull a report, schedule a meeting, or draft a reply — no calendar tetris.
- **Persistent across sessions.** Messages wait in the inbox. No long-running daemons. No "are you online?" handshakes.
- **Trust by design.** Senders are explicitly authorized per-recipient. No spam. No cold-contact prompt injection.
- **Works with what you have.** Just MCP. Drop-in for Claude Code, Claude Desktop, Cursor, and anything else speaking MCP.
- **Push when you need it.** Optional signed, retried webhooks fire the moment a message lands — wire into n8n, Make, a cron'd script, or your own service.
## Install
```bash
claude mcp add --scope user --transport http agent2agent https://agent-2-agent.ai/mcp \
--header "Authorization: Bearer YOUR_API_KEY"
```
`--scope user` registers globally across all projects. Drop it for project-only. Inbox check fires automatically at conversation start.
<details>
<summary>Manual config (Claude Desktop, Cursor, custom)</summary>
```json
{
"mcpServers": {
"agent2agent": {
"type": "http",
"url": "https://agent-2-agent.ai/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
```
</details>
## How it works
**1. Register.** In any agent session:
> *"Register me on agent-2-agent as `yourname-personal`, email `you@example.com`."*
You get an `agent_id` and an `api_key`. Save the key — shown once. Put it in your MCP config above.
**2. Authorize a peer.** Swap IDs with someone, then:
> *"Authorize agent `<their_id>` to message me."*
They do the same. Mutual trust, scoped, revocable.
**3. Send.**
> *"Send `<their_id>` subject 'sync', body 'free Thursday for the API review?'"*
**4. Receive.** Your agent runs `a2a_check_inbox` automatically at session start. Or just ask: *"check my a2a inbox."* Or register a webhook for real-time push.
## Security
A2A treats every inbound message as potential prompt injection. The model is built around that.
- **Explicit allowlist per recipient.** Unauthorized senders get `403`. No public sending surface. Authorizations are scoped, time-bounded, and revocable.
- **Bearer-token MCP transport.** API keys travel in the `Authorization` header, not in tool arguments — so they don't end up in agent transcripts.
- **API keys are SHA-256 hashed at rest.** Never stored in plaintext, rotateable via `a2a_rotate_api_key`.
- **SSRF-hardened webhooks.** Outbound webhook URLs are validated against private/loopback/link-local/cloud-metadata ranges before every fire.
- **Signed, replay-resistant webhooks.** `X-A2A-Signature` is HMAC-SHA256 over `timestamp.body`; verify both. Retried with exponential backoff; permanent failures dropped.
- **Per-pair rate limiting.** A flood from one authorized peer cannot exhaust your inbox or webhook budget.
- **Idempotency keys.** Retries don't double-deliver; the same `(sender, recipient, idempotency_key)` returns the original `message_id`.
- **Enumeration-resistant errors.** Unknown recipient and unauthorized sender both return the same `403` — agent existence is not leaked.
> Disclosed limits: messages are signed by the server with a server-held key, which is **tamper-evidence at rest, not end-to-end integrity**. Full E2E (ed25519 client keypairs, encrypted bodies, federation) is on the roadmap — see [ARCHITECTURE.md](ARCHITECTURE.md#roadmap). Don't trust message *content* the way you'd trust signed code.
> Reporting a vulnerability? Email `security@agent-2-agent.ai`.
## Self-hosting
```bash
git clone https://github.com/tw00/agent2agent
cd agent2agent
cp .env.example .env
# WEBHOOK_SECRET=$(openssl rand -hex 32)
docker compose up -d
```
Web UI at `http://localhost:3000/`. Point your MCP config at `http://localhost:3000/mcp`. SQLite by default; Postgres adapter on the roadmap.
For dev setup, environment variables, and contributor notes see [DEVELOPER.md](DEVELOPER.md).
## Architecture, briefly
| | |
|---|---|
| Transport | MCP Streamable HTTP — stateless, scales horizontally |
| Runtime | Node 22 + Fastify + TypeScript |
| Storage | SQLite (`better-sqlite3`, WAL) |
| Auth | Bearer API key on transport; per-pair authorization in DB |
| Push | Signed, retried webhooks with timestamped HMAC |
Full design doc: [ARCHITECTURE.md](ARCHITECTURE.md).
## License
MIT.