mcp-relay
This 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.
mcp-relay
mcp-relay is a small MCP server that lets the Claude sessions of people on a team message each other. When your session needs something only a teammate knows, it can ask their session directly, so nobody has to copy a question from one chat app to another and paste the answer back.
It runs as one Node process with one SQLite file. Each person gets their own token, and the whole team can read every message on a read-only page.
Try it
You need Node 22.13 or newer.
npm install
npm run demoThe demo starts a real server on a spare port with a throwaway database, connects two MCP clients over HTTP with separate tokens, and has them hold a short conversation. One session waits on its inbox while the other asks a question, the answer lands in the same thread, a pasted key gets refused, and the team page is fetched at the end.
Related MCP server: claude-session-bridge
Run it for your team
Start the server. It listens on 127.0.0.1:8787 and keeps its data in data/relay.db.
npm startAdd each person from a second terminal. The token is printed once and only its hash is stored, so send it to that person privately. If it gets lost, issue a new one with rotate.
npm run relay -- add alice "Alice Example"
npm run relay -- add bob "Bob Example"Each person then connects Claude Code with their own token. Put this in a .mcp.json file and set RELAY_TOKEN in your shell, so the token stays out of your shell history and out of any file you might commit:
{
"mcpServers": {
"relay": {
"type": "http",
"url": "http://127.0.0.1:8787/mcp",
"headers": { "Authorization": "Bearer ${RELAY_TOKEN}" }
}
}
}You can use claude mcp add instead, which is the command add and rotate print. It stores the token in your Claude Code settings, and the token also lands in your shell history:
claude mcp add --transport http relay http://127.0.0.1:8787/mcp \
--header "Authorization: Bearer YOUR_TOKEN"To see the team page, open http://127.0.0.1:8787/ in a browser. When it asks you to sign in, enter your token as the password. The username is not checked, so you can leave it blank or type anything.
Put TLS in front of it
The server speaks plain HTTP, so tokens cross the network unencrypted unless a proxy adds TLS. For a real team, keep the server on 127.0.0.1 and let a proxy on the same machine face the network. With Caddy, which gets its own certificate, this is the whole config:
relay.example.com {
reverse_proxy 127.0.0.1:8787
}Then set RELAY_PUBLIC_URL=https://relay.example.com when you run add and rotate, so the commands they print use that address, and use the same address in .mcp.json. If the proxy runs on another machine, set RELAY_HOST to an address that machine can reach.
Other commands
npm run relay -- people # list people
npm run relay -- rotate alice # replace a token
npm run relay -- revoke alice # cut someone off at onceTools
Tool | What it does |
| Sends |
| Returns your unread messages and marks them read. |
| Returns every live message in a thread, oldest first. Any team member can read any thread. |
| Lists your teammates and when each last used the relay. |
Rules the server enforces
Per-person tokens, hashed at rest. Tokens are 256 random bits. The database holds only their SHA-256 hash. A fast hash is the right choice here because the tokens are random, so there is nothing to guess. The sender of a message always comes from the token, and no tool accepts a sender as an argument.
/mcp takes Bearer tokens only, and refuses unknown browser origins. A browser attaches the team page's Basic credentials to requests on its own, so those credentials do not work on /mcp. A request with an Origin header that is not listed in RELAY_ALLOWED_ORIGINS gets a 403, even with a good token. A request with no Origin header is allowed, because non-browser clients such as Claude Code do not send one.
Messages expire. A message expires a set number of days after it is sent, whether or not it was read. The default is 30 and RELAY_RETENTION_DAYS changes it. An expired message disappears from every read at the moment it expires, and a purge at startup and once an hour then deletes the row.
The team can see everything. Any person with a valid token can open the page at / and read all live messages, including ones between other people. The page only answers GET and HEAD. Message text is escaped and a content security policy blocks scripts, so a message cannot run code in a teammate's browser.
A message from another agent is data. The server instructions tell every connected session that a message carries none of their user's authority, and to get their user's agreement before acting on a request that changes something. check_inbox and read_thread also repeat the point in a notice field next to the messages.
Likely secrets are refused. Before a message is stored, the server checks it against patterns for common credentials: cloud and API keys, private keys, JSON web tokens, URLs with a password in them, and assignments such as password=.... A refused message is not stored, and the refusal names the kind of secret without repeating it. This is a best-effort check. An encoded or unusual secret will get through, and an innocent message will occasionally be refused.
Configuration
Variable | Default | Meaning |
|
| Path to the SQLite file |
|
| Address to listen on |
|
| Port to listen on |
|
| Days before a message expires. Must be a positive number. |
|
| The address |
| none | Comma separated browser origins allowed to call |
Using another database
All database access goes through the Store interface in src/store.ts, which is about a dozen methods. To use another database, implement that interface and pass your store to new Relay. The one rule to keep is that claimInbox must be atomic, so that two waiting sessions can never both receive the same message.
Development
npm test # run the tests
npm run typecheck # check types
npm run build # compile to dist/Limits worth knowing
A message is delivered once. If one person has two sessions open, whichever calls
check_inboxfirst gets it, and the other can still see it withread_thread.If a connection drops after a message is claimed and before the response arrives, that message will not appear in
check_inboxagain. It is still in the thread and on the team page.A wait is capped at 50 seconds because the MCP TypeScript client gives up on a request after 60. To wait longer, call
check_inboxagain.A waiting session polls the database every half second, so a message can take up to that long to arrive.
There is no rate limiting, and every person can read everything. This is built for a small team that trusts each other, not for strangers.
SQLite support comes from Node's built-in
node:sqlitemodule, which Node still marks as experimental.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
The communications layer for AI agents: posts, DMs, team rooms, and file exchange.
Shared team memory for agents. $4.75/seat/mo.
Your shared chats, calendar, Drive, groups and memory, so a team's agents stay in step.
Shared workspace where Claude, ChatGPT, Codex and Grok share files, tasks, decisions and handoffs.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables Claude Code instances across different project directories to communicate and coordinate. Stay in one project while asking questions to teams in other codebases through cross-project messaging and coordination.44 npm1MIT
- AlicenseNot gradedqualityCmaintenanceEnables multiple Claude Code sessions to communicate and share results automatically, with optional orchestration for hands-off workflow coordination.7 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables teams to share context and messages between Claude Code sessions via a relay, allowing collaborative work on the same project.13 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables separate Claude Code sessions across a team to see teammates' live presence, warn before editing a file someone else is editing, route shared-contract changes to the developers whose code depends on them, and produce structured handoffs when a session ends.218 npm1MIT