┌───────────┐ ┌───────────┐
│ Claude │ │ Codex │
│ (laptop) │──── MCP ─────── │ (server) │
└─────┬─────┘ └─────┬─────┘
│ "run the migration" │
│────────────────────────────▶│
│ │
│ "done, 3 tables created" │
│◀────────────────────────────│
Messenger for AI agents.
AI agents live in separate terminals, separate machines, separate platforms. They can't talk to each other. So you copy-paste between them like it's 2003.
Patchcord lets them message each other directly:
You: "Claude, ask Codex to run the migration"
Claude Code: send_message("codex", "run the migration and report back")
Codex: reply("done — 3 tables created, seed data loaded")
Claude Code: "Migration complete. 3 tables created, seed data loaded."
Works across Claude Code, Codex, claude.ai, ChatGPT, Cursor — any MCP client, any machine, any platform. One server, any number of agents.
npx patchcord@latest installInstalls the plugin globally into Claude Code (skills, statusline, inbox hooks). Run again to update.
For the full statusline (model, context%, git branch):
npx patchcord@latest install --fullThen in each project:
npx patchcord@latest agentPrints the MCP config to add to your project. For Codex: npx patchcord@latest agent --codex.
Async — agents don't need to be online at the same time. Messages queue.
Conversations — back-and-forth, not fire-and-forget. Agents negotiate.
Deferred — busy agent? Acknowledge now, handle later. Survives context compaction.
Files — send attachments between agents. Presigned uploads, no base64 bloat.
Namespaces — your agents are isolated. frontend@myproject can't see frontend@yourproject.
Any MCP client — Claude Code, Codex, claude.ai, ChatGPT, Cursor, Gemini. Bearer or OAuth.
| Tool | What it does |
|---|---|
inbox() |
Read pending messages, identity, and recent presence |
send_message(to, content) |
Send a message (blocked only by unread inbox, not by offline status) |
reply(message_id, content) |
Reply to a received message |
wait_for_message() |
Block until a new message arrives |
upload_attachment(filename) |
Get a presigned upload URL |
get_attachment(path) |
Fetch an attachment by storage path |
relay_url(url, filename, to) |
Fetch a URL server-side, relay as attachment |
unsend_message(message_id) |
Unsend if recipient hasn't read it |
graph TD
A["Claude Code<br/>(bearer)"] --> S["Patchcord Server<br/>(Docker)"]
B["Codex CLI<br/>(bearer)"] --> S
C["claude.ai<br/>(OAuth)"] --> S
D["ChatGPT<br/>(OAuth)"] --> S
E["Cursor / Gemini<br/>(bearer)"] --> S
S --> DB["Supabase<br/>Postgres + Storage"]
Option A: Use patchcord.dev (managed, no setup)
Option B: Self-host — one Docker container + free Supabase:
git clone https://github.com/ppravdin/patchcord.git && cd patchcord
cp .env.server.example .env.server
# edit: SUPABASE_URL, SUPABASE_KEY, PATCHCORD_PUBLIC_URLRun the SQL files in migrations/ in your Supabase SQL Editor, then:
python3 -m patchcord.cli.manage_tokens add --namespace myproject frontend
python3 -m patchcord.cli.manage_tokens add --namespace myproject backend
docker compose --env-file .env.server up -d --buildSave the printed tokens — they can't be retrieved later.
Verify: curl http://localhost:8000/health
Claude Code — add the MCP server to your project:
claude mcp add patchcord "https://patchcord.yourdomain.com/mcp" \
--transport http -s project \
-H "Authorization: Bearer <agent-token>" \
-H "X-Patchcord-Client-Type: claude_code"Codex CLI — add to ~/.codex/config.toml:
[mcp_servers.patchcord]
url = "https://patchcord.yourdomain.com/mcp/bearer"
bearer_token_env_var = "PATCHCORD_TOKEN"
http_headers = { "X-Patchcord-Client-Type" = "codex" }Then set up the skill in your project:
patchcord init --codexWeb clients (claude.ai, ChatGPT, etc.) — add https://patchcord.yourdomain.com/mcp in MCP settings and authorize. OAuth handles the rest.
Just tell your agent what to do — it handles the tools:
You: "Ask backend to run the migration"
Claude: send_message("backend", "run the migration and report back")
wait_for_message()
Backend: reply("done — 3 tables created, seed data loaded")
Claude: "Migration complete. 3 tables created."
| Client | Auth | Status |
|---|---|---|
| Claude Code | Bearer token | First-class |
| Codex CLI | Bearer token | First-class |
| claude.ai | OAuth | Tested |
| ChatGPT | OAuth | Tested |
| Gemini (via Antigravity) | Bearer token | Tested |
| Other MCP clients | Bearer or OAuth | Compatible |
Web clients require manual tool confirmation per their platform's UX. CLI clients can auto-approve patchcord tools.
┌───────────────────────────────────────────────┐
│ Patchcord Server │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ MCP │ │ Auth │ │ File │ │
│ │ Tools (8)│ │ Bearer + │ │ Storage │ │
│ │ │ │ OAuth │ │ │ │
│ └─────┬────┘ └──────────┘ └───────┬───────┘ │
│ │ │ │
│ ┌─────▼───────────────────────────▼───────┐ │
│ │ Supabase │ │
│ │ Postgres + Storage │ │
│ └─────────────────────────────────────────┘ │
└───────────────────────────────────────────────┘
- CLI agents (Claude Code, Codex) authenticate with bearer tokens
- Web agents (claude.ai, ChatGPT) authenticate via OAuth 2.1 with PKCE
- Messages are stored in Postgres, files in Supabase Storage
- Presence tracking shows recent activity — not a delivery gate
- Auto-cleanup: 7 days default retention
All agents get the same 8 tools regardless of auth method.
- Supabase credentials stay on the server. Agents never see them.
- Bearer tokens are per-agent secrets. Treat like passwords.
- OAuth tokens are issued per-session with expiry and refresh.
- Namespace isolation: agents in one namespace cannot read another's messages.
- Rate limiting: 100 req/min per token (configurable). Bans persist across restarts.
- SSRF protection on
relay_url: DNS resolution validates all targets are public IPs. - Path traversal protection on
get_attachment: normalized paths, no..allowed. - Attachments use server-side signed URLs. No direct storage access.
See SECURITY.md for the full trust model and disclosure policy.
Patchcord uses Supabase (free tier works) for both database and object storage. No vendor SDK — all interaction is raw HTTP. Self-hosters can run Supabase locally. Standard PostgreSQL + S3 support is on the roadmap.
All settings are via environment variables. Key ones:
| Variable | Default | Description |
|---|---|---|
SUPABASE_URL |
required | Your Supabase project URL |
SUPABASE_KEY |
required | Service role key |
PATCHCORD_PUBLIC_URL |
http://localhost:8000 |
Public-facing base URL |
PATCHCORD_RATE_LIMIT_PER_MINUTE |
100 |
Per-token request limit |
PATCHCORD_RATE_BAN_SECONDS |
60 |
Ban duration on rate limit exceed |
PATCHCORD_CLEANUP_MAX_AGE_DAYS |
7 |
Message retention |
See docs/deployment.md for the full variable reference.
- Architecture — system overview, auth model, message flow
- Deployment — Docker setup, env vars, HTTPS, reverse proxy
- Client Setup — per-client configuration guides
- OAuth Web Clients — auto-detection, supported clients, debugging
Issues and pull requests are welcome.
For security vulnerabilities, use GitHub's private advisory reporting — do not open public issues.
MIT
