nats-channel-mcp
Allows AI agents to send and receive messages via NATS pub/sub, enabling event-driven workflows and inter-agent communication through NATS channels.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nats-channel-mcppublish 'Hello World' to subject 'greetings'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nats-channel-mcp
An MCP channel server that bridges NATS pub/sub into Claude Code sessions.
Publish a message to a NATS subject → it arrives in Claude as a <channel> tag → Claude acts on it and can publish back to any subject using the built-in reply tool.
This is the primitive for wiring Claude agents together via NATS.
you → nats pub → NATS → channel-server (MCP subprocess) → <channel> tag → Claude acts
↓
you ← nats sub ← NATS ← channel-server ← reply(to, text) tool call ←─────────┘Prerequisites
Install these before anything else:
Tool | Version | Install |
≥ 1.0 |
| |
any | ||
≥ 2.1.80 |
| |
nats CLI (optional) | any | for testing/publishing |
Claude Code must be authenticated with a claude.ai account (claude auth login). This uses OAuth — not an API key.
Related MCP server: claude-connect-nats-mcp
Install
git clone <this-repo>
cd nats-channel-mcp
bun installThe Most Important Part: Instructions
The --instructions string is your agent's standing orders. It is injected directly into Claude's system prompt. It tells Claude:
Who it is (its name, role)
What the incoming
<channel>messages meanWhat to do with them
Where to send results (which NATS subject to publish to when done)
Get this right and everything else is plumbing.
Option A: Instructions file (recommended)
Create a markdown file with the agent's instructions:
# AGENT.md
You are **Aria**, a code reviewer specializing in TypeScript.
When you receive a <channel> message:
1. Read the code or diff provided
2. Write a concise review (3-5 bullet points max)
3. Use the reply tool to publish your review to `reviews.done`
Be direct. No preamble.Pass it with --instructions-file:
"args": ["--instructions-file", "./AGENT.md", "--name", "aria", "--subscribe", "agents.aria"]Option B: Inline instructions
For simple agents, inline in .mcp.json:
"args": ["--instructions", "You are aria. Review code in <channel> messages and reply to reviews.done.", "--name", "aria", "--subscribe", "agents.aria"]⚠️ Long inline instructions in JSON are hard to read and edit. Use --instructions-file for anything real.
Quick Start
1. Start NATS:
nats-server2. Create your agent directory:
my-agent/
AGENT.md ← your instructions (the important part)
.mcp.json ← MCP server config3. Write your instructions (AGENT.md):
You are my-agent. When you receive a <channel> message, respond to it
and use the reply tool to publish your response to `agents.done`.4. Configure the MCP server (.mcp.json):
{
"mcpServers": {
"nats": {
"command": "bun",
"args": [
"/absolute/path/to/claude-nats-channel/channel-server.ts",
"--name", "my-agent",
"--subscribe", "agents.my-agent",
"--instructions-file", "./AGENT.md"
]
}
}
}Note: The path to
channel-server.tsmust be absolute.
5. Start Claude with channel support:
cd my-agent
claude --mcp-config .mcp.json --dangerously-load-development-channels server:natsYou'll see a one-time confirmation prompt — choose option 1 to proceed. After that:
Listening for channel messages from: server:nats6. Send a message:
nats pub agents.my-agent "Please respond."Claude receives it, acts on it, and can reply via the reply tool.
CLI Reference
bun channel-server.ts [options]Flag | Required | Description |
| ✅ | Agent name. Used in channel source attribute and default instructions. |
| ★ | NATS subject to subscribe to. Repeatable: |
| ★ | Path to a topics file (one subject per line, |
| ☆ | Path to a markdown file whose contents become the MCP instructions (system prompt). Recommended. |
| ☆ | Inline instructions string. Falls back to a minimal default if neither is given. |
| — | NATS server URL. Default: |
| — | Enable the Unix-socket control channel for hot subscription management. See Control Socket below. |
| — | Disable self-echo suppression. By default the server stamps an |
| — | Enable JetStream-backed durable delivery and the |
★ At least one of --subscribe or --topics-file is required.
☆ At least one of --instructions-file or --instructions is strongly recommended.
Control Socket
Long-running agents often need to join and leave channels without restarting. Pass --control-socket <path> and the server will listen on a Unix-domain socket that accepts newline-delimited JSON commands:
{"action": "subscribe", "subject": "rooms.breakout-42"}
{"action": "unsubscribe", "subject": "rooms.breakout-42"}
{"action": "delete-durable", "subject": "rooms.breakout-42"}delete-durable is only meaningful when --jetstream is on. It unsubscribes and removes the durable consumer for the subject, scoped to durables this server created.
This is intended for orchestrators (session managers, dashboards) that coordinate a fleet of agents and need to attach or detach channels at runtime. The socket is created on startup and unlinked on SIGTERM / SIGINT. Any existing file at the path is unlinked first.
Example:
# Start an agent with a control socket
bun channel-server.ts \
--name aria \
--subscribe agents.aria \
--control-socket /tmp/nats-ctrl-aria.sock
# From another process, hot-subscribe
echo '{"action":"subscribe","subject":"rooms.breakout-42"}' \
| nc -U -q 1 /tmp/nats-ctrl-aria.sockThe flag is purely opt-in: if you don't pass --control-socket, no socket is created and behavior is unchanged from prior versions. Errors in the control channel never affect NATS or MCP message flow — malformed JSON, unknown actions, and client disconnects are logged to stderr and the server keeps running.
Security: the socket is created with the filesystem permissions of the user running the server. Use a directory only that user can reach (e.g. $XDG_RUNTIME_DIR) if you need stronger isolation. The protocol has no authentication — anyone who can connect() to the path can mutate the subscription list.
JetStream Mode
Pass --jetstream and the server uses NATS JetStream for durable delivery instead of core pub/sub. This fixes the main fragility of the default mode: messages published while the subscriber is down are buffered and delivered on reconnect.
Requires nats-server to be running with JetStream enabled (nats-server -js).
What changes:
Durable per subscription. Each
--subscribe SUBJcreates a durable consumer named${name}__${slug(SUBJ)}. On reconnect (network blip, process restart, session resume) the consumer resumes from its last-acked sequence — messages buffered while the channel-server was offline get delivered automatically. A freshly-created durable starts at "now" (DeliverPolicy=New), matching the empty-inbox expectation of a freshly-spawned agent;DeliverPolicyis frozen at consumer creation, so subsequent rebinds always resume from last-acked regardless.Auto-managed stream. The server creates a stream called
channel-mcpon startup if it doesn't exist, with 3h retention and file storage. Subjects are the union of every--subscribearg any channel-server in the fleet has seen; the subject set only grows, and retention/storage is never modified after creation. To change retention, usenats stream edit channel-mcpout of band.replayMCP tool. A second tool appears on the MCP surface. Claude can callreplay(subject, since?, limit?)to fetch historical messages. Results land as tool output, not as a channel injection, so they don't double-fire into the live inbox. The response is a JSON array of{subject, from, ts, seq, text}per message.seqis the NATS stream sequence — useful if an agent wants to dedupe across replay/live overlap.delete-durablesocket action. Orchestrators that manage a fleet of agents can remove a subject's durable immediately via the control socket (see above). Each channel-server tracks the durable names it created in memory and refusesdelete-durablefor any name outside that set; on restart the set is lost, but the consumer's ownInactiveThresholdreaps anything that leaks.
Limitations:
Pauses longer than 1h lose messages. The per-consumer
InactiveThresholdis 1h (hardcoded). If a subscriber is gone for longer, JetStream deletes the durable; the next bind starts at "now" and messages published during the gap are skipped. Raise the constant inchannel-server.tsif you need to survive longer pauses.At-least-once. A duplicate delivery is possible after a crash/ack-timeout. If a message triggers a non-idempotent side-effect, the producer should include its own dedup key in the payload; this server is content-blind.
msg.ack()is OS-pipe-level. channel-server acks onceprocess.stdout.write()accepts the buffer for the MCP notification. If Claude Code crashes between pipe receive and consumption, the ack has already fired and the message is lost to that session. This is an MCP-protocol limitation, not a JetStream one.
Example:
# Start NATS with JetStream
nats-server -js
# Start the channel-server in JetStream mode
bun channel-server.ts \
--name aria \
--subscribe agents.aria \
--jetstreamThe reply Tool
Claude uses this to publish back to NATS:
reply(to: "<nats-subject>", text: "<message>")Your instructions should tell Claude exactly which subject to publish to and when. Example:
"When you finish your analysis, use reply(to='pipeline.done', text='')."
On first use, Claude will ask for permission. Choose "Yes, and don't ask again" to suppress future prompts for that session.
How Messages Appear in Claude
<channel source="nats" subject="agents.my-agent">
the message content here
</channel>The subject attribute shows which subscription delivered the message — useful when an agent subscribes to multiple subjects.
Topics / Subscriptions
The second thing worth getting right (after instructions) is which subjects your agent subscribes to.
Option A: Single subject (simple)
"args": ["--subscribe", "agents.my-agent", ...]Option B: Topics file (recommended for multi-level setups)
Create topics.txt alongside AGENT.md:
# topics.txt — one subject per line, # = comment, blank lines ignored
# Direct (messages specifically for this agent)
agents.my-agent
# Team channel (shared with other agents on the same task)
myapp.project-01.epic-xyz.task-abc.*
# Epic-level broadcast
myapp.project-01.epic-xyz.>
# Workspace-wide announcements
myapp.>
# Breakout rooms (add/remove as needed)
# myapp.breakout.sprint-planningPass it with --topics-file:
"args": ["--topics-file", "./topics.txt", ...]Wildcard subjects
NATS wildcards work as you'd expect:
Pattern | Matches |
| Exactly that subject |
| Any single token after |
| Any subject starting with |
A message published to agents.team is received by any agent subscribed to agents.team, agents.*, or agents.>.
The channel source attribute
When a message arrives via a wildcard subscription, the <channel> tag shows the actual subject it was published to:
<channel source="nats" subject="agents.team">
broadcast to the whole team
</channel>Your instructions can tell Claude to behave differently based on which subject a message came from.
Multi-Agent Chains
Each agent subscribes to its own subject. You "introduce" agents by telling each one about the next step in their instructions:
Agent 1 (AGENT.md):
You are step-1. When you receive a task in a <channel> message:
1. Process it
2. Use reply(to="agents.step-2", text="<your output>") to pass it forwardAgent 2 (AGENT.md):
You are step-2. When you receive input in a <channel> message:
1. Build on it
2. Use reply(to="pipeline.done", text="<final output>") when completeStart both agents before dispatching. Messages published before an agent is subscribed are lost (fire-and-forget). For durability, use NATS JetStream.
See examples/intro-chain/ for a working end-to-end example.
Known Limitations & Gotchas
Issue | Details |
Tool approval prompts | Claude asks permission before calling |
Fire-and-forget by default | Without |
One-time startup confirmation |
|
Research preview | Requires Claude Code ≥ v2.1.80. The |
Absolute path in | The path to |
NATS auth not implemented |
|
The Key Name Coupling (important)
The MCP server key in .mcp.json and the --dangerously-load-development-channels server:<key> flag must match exactly. If they don't, Claude starts silently — no channel listener, no error.
{ "mcpServers": { "nats": { ... } } }
// ^^^^
// This must match ──────────────────────────────────────┐claude --dangerously-load-development-channels server:nats
# ^^^^Convention: always use nats as the key name. The examples follow this convention.
If you need a different key name (e.g. running multiple channel servers per session), use a CHANNEL_KEY variable in your launch script so both places stay in sync automatically — see examples/intro-chain/run.sh for the pattern.
Architecture
┌──────────────────────────────────────────────────────────┐
│ Claude Code session │
│ │
│ System prompt includes: <instructions from AGENT.md> │
│ │
│ Receives: <channel source="nats" subject="agents.x"> │
│ message content │
│ </channel> │
│ │
│ Sends: reply(to="agents.y", text="response") ──────┼──→ NATS
└──────────────────────────────────────────────────────────┘ ↑
↑ notifications/claude/channel (MCP) │
│ │
┌───────────────────────────────┐ │
│ channel-server.ts │ ←── NATS ────────────────────┘
│ (MCP subprocess) │
│ │
│ nc.subscribe(subject) │
│ → mcp.notification() │
│ │
│ reply tool │
│ → nc.publish(to, text) │
└───────────────────────────────┘The channel server runs as a subprocess spawned by Claude Code (via .mcp.json). It owns the NATS connection. Claude Code never touches NATS directly.
Roadmap
--subscriberepeatable for multiple initial subjectsHot subscription management via Unix socket (add/remove without restart)
NATS JetStream support for durable delivery (
--jetstream)NATS authentication via credentials file (
--nats-creds)
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/except-pass/nats-channel-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server