Featured

Deploy OpenClaw in 60 seconds — 20% off logoDeploy OpenClaw in 60 seconds — 20% off

Launch OpenClaw on Hostinger in about 60 seconds and keep your agent live 24/7. Our referral link gives you 20% off, no coupon code needed.

Launch on Hostinger
Run your Hermes agent on Hostinger, fully managed logoRun your Hermes agent on Hostinger, fully managed

Launch Hermes on Hostinger in one click, fully managed, no VPS knowledge needed. Use code ZACAARON10 for 10% off.

Launch on Hostinger
Crawl and scrape any site into clean data, 10% off logoCrawl and scrape any site into clean data, 10% off

Firecrawl crawls and scrapes any site into clean markdown for your agent. Get 1,000 free credits, and new users get 10% off their first purchase.

Try Firecrawl free
6,000+ web scrapers for your AI agent, start free logo6,000+ web scrapers for your AI agent, start free

Apify gives your agent live web data: 6,000+ prebuilt scrapers and actors, MCP-ready. Sign up free with $5 in usage credits.

Try Apify free
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you
SEO data APIs for your agent, $1 free credit logoSEO data APIs for your agent, $1 free credit

DataForSEO gives your agent live access to SERP results, keyword data, backlinks, and on-page SEO data through one API. New accounts get a $1 credit, good for up to 20,000 keyword or backlink lookups.

Try DataForSEO free
Reach 48,000+ AI builders

A flat monthly placement in front of developers actively installing AI tools. No lock-in, cancel anytime.

Advertise here

Works with

Claude CodeClaude DesktopCursorVS CodeClineCodex CLIOpenClaw+ any MCP client

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

MCP server for agent-to-agent communication over NATS with live session push via Claude Code Channels.

README.md

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:

  1. Outcomes, not operations — one tool = one agent story
  2. Flatten arguments — primitives, enums, strong defaults
  3. Instructions are context — descriptions are prompts for LLMs
  4. Respect token budget — every response reports _meta: {chars, lines, ms}
  5. 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:

  1. Verifies prereqs: git, node >= 18, npm, jq. Hard-fails with a clear message if any are missing.
  2. Clones (first run) or fast-forward pulls (subsequent runs) into ~/.local/share/agents-mcp-server (override with --dir or AGENTS_MCP_DIR).
  3. Runs npm install, which triggers the prepare script (tsc) — no manual npm run build step, ever.
  4. Writes/updates the mcpServers.agents entry in ~/.claude.json using jq (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 in Mcp-Session-Id header)
  • 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 name arrive as <channel source="agents" kind="dm" …> tags.
  • Broadcasts where group == your group arrive 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 Map of 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-history with subject filter agents.dm.>, agents.channel.>, agents.broadcast.>. Retention: 30d / 512 MiB / 10 000 msgs per subject (env-tunable). Every *_history tool opens an ephemeral JetStream consumer with a subject filter, drains up to limit, deletes the consumer.

NATS subjects

  • agents.presence — presence beats (not retained in the stream)
  • agents.dm.<base64url(to_agent)> — direct messages
  • agents.channel.<base64url(channel_name)> — channel posts
  • agents.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

See related servers & alternatives →

Related MCP servers

Browse all →

Related guides

Hand-picked reading to help you choose and use Developer Tools servers.