Agents MCP Server
MCP server for agent-to-agent communication over NATS with live session push via Claude Code Channels.
README
Agents MCP Server
MCP server for agent-to-agent communication over NATS with live session push via Claude Code Channels. State is in-memory; history lives on a JetStream stream; transport is single-bus (AGENTS_NATS_URL); delivery is uniform for local and cross-host targets. Runs as stdio (one process per user) or as a shared remote server over streamable HTTP.
Design Principles
Built following Block's MCP Playbook:
- Outcomes, not operations — one tool = one agent story
- Flatten arguments — primitives, enums, strong defaults
- Instructions are context — descriptions are prompts for LLMs
- Respect token budget — every response reports
_meta: {chars, lines, ms} - Curate ruthlessly — fewer tools = less LLM decision overhead
Prerequisites
- A NATS server reachable via
AGENTS_NATS_URL, with JetStream enabled - For local stdio use: Node.js >= 18
- For remote HTTP deploys: a container runtime (Docker / Kubernetes)
No DuckDB, no on-disk state, no schema migrations.
Transports
The server picks its transport at boot via AGENTS_TRANSPORT:
| Mode | Value | Use case |
|---|---|---|
| stdio (default) | stdio |
One MCP process per user, spawned by Claude Code over stdin/stdout |
| HTTP | http |
Single shared process fronted by streamable HTTP; each Claude Code session negotiates its own binding |
Stdio binding is implicit from the moment agent_register is called; HTTP binding is per-session (each connected client carries its own sessionBinding). In both modes, the same tool set and the same NATS subjects are in use — callers see no semantic difference.
Install / Update (local stdio)
One command, idempotent — works the same on first install and for every subsequent update:
curl -fsSL https://raw.githubusercontent.com/Piotr1215/agents-mcp-server/main/scripts/install.sh \
| bash -s -- --nats-url=nats://your-endpoint:4222
On update, --nats-url is optional — the existing endpoint in ~/.claude.json is preserved:
curl -fsSL https://raw.githubusercontent.com/Piotr1215/agents-mcp-server/main/scripts/install.sh | bash
Then /mcp reconnect in any active Claude session (or relaunch claude). That's it.
What the installer does:
- Verifies prereqs:
git,node >= 18,npm,jq. Hard-fails with a clear message if any are missing. - Clones (first run) or fast-forward pulls (subsequent runs) into
~/.local/share/agents-mcp-server(override with--dirorAGENTS_MCP_DIR). - Runs
npm install, which triggers thepreparescript (tsc) — no manualnpm run buildstep, ever. - Writes/updates the
mcpServers.agentsentry in~/.claude.jsonusingjq(idempotent, preserves every other entry). Backs up the file to~/.claude.json.bak-<epoch>before writing.
Prefer to review before running
curl -fsSL https://raw.githubusercontent.com/Piotr1215/agents-mcp-server/main/scripts/install.sh -o install.sh
less install.sh
bash install.sh --nats-url=nats://your-endpoint:4222
Local development
Clone the repo directly and use npm link. The prepare script means every npm install rebuilds, and npm run build / npm test still work normally. Only the symlink-path .claude.json entry needs adjusting to point at your dev checkout.
Remote HTTP deploy
Published as a Docker image for shared deployments (homelab proving ground, loft.rocks rollout, per #124):
piotrzan/agents-mcp-server:<version>
The image bakes in no AGENTS_* defaults — callers (Kubernetes Deployment, docker run -e …) set AGENTS_NATS_URL, AGENTS_TRANSPORT, and AGENTS_HTTP_PORT explicitly. The server fails loud on missing NATS so misconfiguration is caught at boot.
Exposed endpoints:
GET /health→{"status":"ok","version":"<x.y.z>","sessions":<count>}POST /mcp→ Streamable HTTP MCP endpoint (stateful; session id returned inMcp-Session-Idheader)GET /mcp→ server-initiated SSE stream used by Claude Code for live<channel>notification push
Client config for Claude Code:
{
"mcpServers": {
"agents": {
"type": "http",
"url": "http://agents-mcp.<your-host>/mcp"
}
}
}
Configuration reference
| Env | Default | Notes |
|---|---|---|
AGENTS_NATS_URL |
(no default) | Required; server refuses to start if NATS is unreachable |
AGENTS_TRANSPORT |
stdio (code default; no default in the Docker image) |
stdio or http |
AGENTS_HTTP_PORT |
3000 (code default) |
HTTP mode only |
AGENTS_HISTORY_MAX_AGE_MS |
30d |
JetStream stream retention |
AGENTS_HISTORY_MAX_BYTES |
512 MiB |
JetStream stream cap |
AGENTS_HISTORY_MAX_MSGS_PER_SUBJECT |
10000 |
Per-subject cap |
AGENTS_LOG_FILE |
unset | When set, writes a local audit log (stdio installs); unset in the default Docker image |
snd CLI
snd is published by the installer as a bin alongside the server:
snd <agent> <msg...> DM to agent
snd -t <agent> <msg...> DM (explicit)
snd -g <group> <msg...> broadcast to group
snd --human … <msg...> prefix payload with [HUMAN] (wrapper does this for interactive use)
snd --tail subscribe to every DM/broadcast/channel event on the bus (read-only)
Only dependency is AGENTS_NATS_URL. snd talks NATS directly, so it works the same regardless of which MCP transport mode you're on.
Real-time session push
agent_register both joins the conversation and binds the session's identity. From that point on:
- DMs where
to_agent == your namearrive as<channel source="agents" kind="dm" …>tags. - Broadcasts where
group == your grouparrive as<channel source="agents" kind="broadcast" …>tags. - Channel posts arrive as
<channel source="agents" kind="channel" …>tags.
In stdio mode the session is the process; in HTTP mode each connected client holds its own binding and SSE stream. Echo suppression happens at the handler: you never see your own outbound message pushed back at you.
Sessions that haven't called agent_register yet stay send-only; inbound is still captured by the JetStream audit stream and available via channel_history / dm_history / group_history for catch-up reads.
Tools
All tools use name for identification (agents know their names from prompts). Every response includes _meta: { chars, lines, ms } for token awareness.
agent_register
Register as an agent. Returns peers in your group.
{ name: "researcher", description: "Finds information", group?: "default" }
// Returns: { agent_id: "researcher-a1b2c3d4", group: "default", peers: [...] }
agent_id is deterministic — <name>-<sha256(name@host)[:8]> — so it survives process restarts.
agent_deregister
Unregister when done. Idempotent — succeeds even if already gone.
{ name: "researcher" }
agent_broadcast
Send a message to all other agents in a group.
{ name: "researcher", message: "Found the data", priority?: "normal", group?: "all" }
agent_dm
Direct message to a specific agent.
{ name: "researcher", to: "analyst", message: "Check this" }
agent_discover
List active agents (local + remote presence cache).
{ include_stale?: false, group?: "research" }
agent_groups
List groups with agent counts.
{}
channel_send
Post to a channel (async bulletin board — no live nudge; use agent_broadcast / agent_dm for push).
{ name: "researcher", channel: "general", message: "Update complete" }
channel_history
Get channel messages. detailed: true returns full metadata.
{ channel: "general", limit?: 50, detailed?: false }
dm_history
Get DM history between two agents.
{ name: "researcher", with_agent: "analyst", limit?: 50, detailed?: false }
channel_list
List channels with message counts.
{}
group_history
Get recent broadcasts for a group.
{ group: "research", limit?: 50 }
messages_since
Poll for new messages since a given JetStream sequence.
{ since_id?: 0, limit?: 100 }
poll_messages
Poll DMs + broadcasts addressed to a given agent since last check.
{ name: "researcher", since_id?: 0 }
How It Works
One bus, one audit store. Presence, DMs, broadcasts, and channel posts all flow through NATS on AGENTS_NATS_URL. A single JetStream stream (agents-history) captures every DM/channel/broadcast subject for history reads.
State
- Agent registry — in-memory
Mapof local agents; remote peers served from the NATS presence cache (10s beat, 30s TTL). No on-disk state, no DuckDB. - Message history — JetStream stream
agents-historywith subject filteragents.dm.>,agents.channel.>,agents.broadcast.>. Retention: 30d / 512 MiB / 10 000 msgs per subject (env-tunable). Every*_historytool opens an ephemeral JetStream consumer with a subject filter, drains up tolimit, deletes the consumer.
NATS subjects
agents.presence— presence beats (not retained in the stream)agents.dm.<base64url(to_agent)>— direct messagesagents.channel.<base64url(channel_name)>— channel postsagents.broadcast.<base64url(group)>— group broadcasts
End-to-end trace (channel post)
bob channel_send("#eng", "hi")
│
└──► NATS publish agents.channel.<b64url(#eng)>
│
┌──────────────┴──────────────┐
▼ ▼
JetStream stream agents-mcp-server sessions
agents-history bound to other agents
│ │
▼ ▼
channel_history reads notifications/claude/channel →
return this seq later <channel source="agents" kind="channel" …>
rendered live in the bound session
Sub-second session push
When Claude Code is launched with --dangerously-load-development-channels server:agents, the same subprocess handles both tool calls and the experimental claude/channel capability — no separate channel binary. Each NATS subscription fan-ins into every bound session whose binding matches the target. Publishers never see their own messages pushed back.
Token Efficiency
Every response includes _meta:
Active agents (2) in group 'default':
- alice (alice-7c3f9a81): active | group: default | host: serval | local
- bob (bob-f600ddba): active | group: default | host: agents-mcp-pod | remote
---
_meta: {"chars":170,"lines":3,"ms":8}
Development
npm install
npm run build
npm test
Docker image:
docker build -t agents-mcp-server:dev .
docker run --rm -e AGENTS_NATS_URL=nats://host.docker.internal:4222 -p 3000:3000 agents-mcp-server:dev
curl http://localhost:3000/health
License
MIT
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。