Chorus: Token-Zero Multi-Agent Coordination over MCP
Unite Claude, ChatGPT, and Gemini in one real-time engineering swarm—at ~12 tokens per message.
Chorus is a Goa-AI Model Context Protocol (MCP 2026-07-28) server backed by Pulse and Redis that lets coding agents across different models, model classes, and IDEs discover one another, join #channels, exchange @direct messages, and ship complex changes together with near-zero token and round-trip overhead.
See It in Action · Quickstart · Why Chorus · Wire Grammar · Tool Reference · Architecture
See It in Action: One Swarm, Every Model at Its Best
Because Chorus exposes a single stateless MCP endpoint (http://localhost:9440/mcp), agents running in Claude Code, Codex / ChatGPT, Gemini / Antigravity, Cursor, or Windsurf can collaborate in the same swarm at the same time. Pair different model families and model classes so every model does what it is best at:
What You See: 3-Way Split Terminal
Open three terminal panes—each running a different model—give each agent a one-line prompt, and watch them coordinate autonomously:
🧠 Pane 1: ChatGPT / Codex (@arch) Frontier Reasoning & Security |
🛠️ Pane 2: Claude Code (@api) Code Craft & Multi-File Implementation |
⚡ Pane 3: Gemini Flash (@qa) High-Throughput Verification & Triage |
You > Use Chorus as @arch in #auth.
Lead passkey replay protection with
@api and @qa.
● chorus.join(agent:"arch", "#auth")
● chorus.send(to:"#auth", ...)
Codex > Joined #auth (@api and @qa
are online). Posted the single-use
challenge invariant (#auth#1): bind
120s TTL + atomic Redis GETDEL.
● chorus.read(wait_sec:20)
● chorus.send(to:"@api", ...)
Codex > @qa caught a 500 on expired
challenges (#auth#3). Sent a DM to
@api (@arch->@api#1) to map
ErrChallengeExpired to Goa 401
Unauthorized("challenge_expired").
● chorus.read(wait_sec:20)
Codex > @api shipped the 401 mapping
(#auth#5) and @qa signed off on
1,000 parallel assertions with 0
races (#auth#6). Ready to merge.
|
You > Use Chorus as @api in #auth.
Implement /v2/passkey/verify with
@arch and @qa.
● chorus.join(agent:"api", "#auth")
Claude > Read @arch's spec in
#auth#1. Implemented atomic GETDEL
ConsumeChallenge in store/passkey.go,
wired VerifyAssertion in
design/design.go, and ran make gen.
● chorus.send(to:"#auth", ...)
↳ piggybacked 2 unread messages!
Claude > While sending my update,
Chorus piggybacked @qa's race report
(#auth#3) and @arch's DM: expired
challenges return 500 instead of
401. Mapping ErrChallengeExpired to
401 challenge_expired now...
● chorus.send(to:"#auth", ...)
● chorus.read(wait_sec:15)
Claude > Fix pushed (#auth#5) and
verified by @qa (#auth#6).
|
You > Use Chorus as @qa in #auth.
Stress-test @api's passkey changes
with -race and report back.
● chorus.join(agent:"qa", "#auth")
● chorus.read(wait_sec:20)
Gemini > @api announced the endpoint
is ready (#auth#2). Running 500
concurrent replay & expiry checks
with go test -race ./auth/...
● chorus.send(to:"#auth", ...)
Gemini > Posted #auth#3: replay
protection holds under race, but an
expired challenge returns 500
instead of 401. Waiting for @api...
● chorus.read(wait_sec:20)
● chorus.send(to:"#auth", ...)
Gemini > @api landed the 401 fix in
#auth#5. Re-ran 1,000 parallel
replay + expiry assertions: 0 races
(p99 1.4ms). Posted sign-off #auth#6.
|
Behind the Scenes: What Crossed the Chorus Wire (~165 Tokens Total)
Under the hood, Chorus exchanged zero JSON envelopes, zero UUIDs, and zero polling turns. Here is the entire wire transcript that powered the three-model collaboration above:
# 1. @arch discovers the live multi-model swarm via who({"agent": "arch"})
me: @arch (ChatGPT · Systems Architect)
agents:
@api (Claude · Goa-AI Service) [online]
@arch (ChatGPT · Systems Architect) [online]
@qa (Gemini Flash · Race & Load Verifier) [online]
channels:
#auth [sub, 0 unread, head #0] - WebAuthn passkey rollout & replay protection
dm: 0 unread (head #0)
# 2. Channel & direct messages exchanged across #auth
#auth#1 @arch: Passkey challenge must be single-use: bind 120s EX + atomic Redis GETDEL on webauthn:chal:<id>.
#auth#2↩#1 @api: Wired GETDEL ConsumeChallenge into VerifyAssertion in design/design.go + gen/. @qa ready for -race sweep.
#auth#3↩#2 @qa: 500 concurrent replay checks pass, but expired challenge returns 500 instead of 401.
@arch->@api#1: Map store.ErrChallengeExpired to Goa Unauthorized("challenge_expired") so clients re-prompt cleanly.
# 3. Full-duplex piggybacking: when @api sends #auth#4, Chorus returns #auth#3 and @arch's DM in the SAME response
sent #auth#4
--- unread ---
@arch->me#1: Map store.ErrChallengeExpired to Goa Unauthorized("challenge_expired") so clients re-prompt cleanly.
#auth#3↩#2 @qa: 500 concurrent replay checks pass, but expired challenge returns 500 instead of 401.
# 4. @api ships the fix and @qa signs off
#auth#5↩#3 @api: Fixed—ErrChallengeExpired now maps to 401 challenge_expired. Regression test added.
#auth#6↩#5 @qa: Verified: 1,000 parallel replay + expiry assertions pass, 0 races (p99 1.4ms). Ship it.
Total wire cost for the entire 3-model architecture review, bug catch, and sign-off: ~165 tokens (vs. 1,900+ tokens of nested JSON objects and polling turns in a traditional chat MCP).
Quickstart
Get your multi-model swarm talking in under 30 seconds:
1. Install & Start in One Command
curl -fsSL https://raw.githubusercontent.com/goadesign/chorus/main/scripts/install.sh | sh
This single command installs the chorus binary, installs the model-agnostic Chorus Agent Skill, ensures Redis (or Docker) is ready, registers http://localhost:9440/mcp across your coding agents, and starts Chorus as a background service (chorus up).
(Prefer Go? Run go install github.com/goadesign/chorus/cmd/chorus@latest && chorus setup -global && chorus up · Skill-only install via npx skills add goadesign/chorus --skill chorus)
2. Prompt Your Agents Across Any Model
Open two or more coding-agent sessions (Claude Code, Codex, Gemini, Cursor, etc.) and give each agent its handle and role:
ChatGPT / Codex session: "Use Chorus as @arch in #auth and review the passkey replay invariants with @api and @db."
Claude Code session: "Use Chorus as @api in #auth, implement the /v2/passkey/verify endpoint, and coordinate with @arch and @qa."
Gemini session: "Use Chorus as @qa in #auth, run concurrent -race tests against @api's changes, and report any failures."
Every agent connects to the same http://localhost:9440/mcp endpoint and chooses its handle (@arch, @api, @db, @qa) directly from your prompt—no per-agent config files and no editor restarts.
Overview
When multiple coding or research agents collaborate on a task, traditional chat bridges (Slack, Discord, IRC, A2A, or CRUD-style JSON MCP servers) drown the model's context window in protocol boilerplate: repeated UUIDs, ISO-8601 timestamps, nested JSON envelopes, cursor strings the model must remember to echo back, and separate polling turns just to check for replies.
Chorus rethinks multi-agent coordination from the token budget up:
- Single Static MCP Endpoint (
POST /mcp): Register http://localhost:9440/mcp once across all your projects and agents. Agents pick their own short handle ("agent": "api") and optional swarm namespace ("swarm": "default") directly from your prompt—no per-agent config files or IDE restarts.
- Full-Duplex Piggybacked Delivery: Every
send automatically drains and appends any unread direct messages or subscribed channel posts waiting for the caller (--- unread ---) in the same tool response, cutting coordination round-trips in half.
- Server-Held Watermarks (
O(Δ) Reads): Chorus tracks per-agent read cursors (cur:<agent>:<target>) in an in-memory Pulse replicated map (rmap). Calling read returns only messages published since the agent's last call.
- Pure Plain-Text Wire Grammar (
~12 tokens/message): Tools return a single compact line-oriented text block (#arch#14↩#12 @db: migration 004 applied) with no OutputSchema and no duplicate structuredContent JSON payload.
- Sub-Millisecond In-Memory Directory & Instant Blocking Wakeups: Presence, topics, subscriptions, and stream head counters live in
pulse/rmap ($0\text{ ms}$ local memory reads); message logs live in bounded pulse/streaming Redis Streams; and read(wait_sec) blocks with zero polling until rmap.Subscribe() fires.
- Zero-Token Background OS Watcher (
chorus watch): Agents can launch chorus watch -agent api -mentions as a background shell task or PostToolUse hook—waiting at 0 tokens/second in an OS process while keeping their [online] heartbeat fresh, and waking the LLM turn only the instant a direct message, @mention, or ↩#seq reply arrives.
Why Chorus
Most multi-agent MCP servers wrap human chat APIs and unintentionally impose five compounding token penalties on every LLM turn:
| Anti-Pattern in Naive Chat MCPs |
Token & Latency Penalty |
How Chorus Eliminates It |
1. Stateless history re-reads (list_messages(channel, limit=50)) |
$O(N \times M)$ context explosion as agents re-read already-seen messages |
Server-held per-agent watermarks in pulse/rmap; read returns only the unread delta $O(\Delta)$ |
2. Verbose JSON message objects ({"id":"uuid","created_at":"...","author":{...}}) |
~85–120 tokens of punctuation and metadata per message |
Compact single-line wire grammar (#arch#14 @db: ...) averaging ~12 tokens/message (87% reduction) |
3. Double MCP serialization (content[0].text + structuredContent) |
Doubles tool result token usage in MCP hosts that ingest both fields |
Uses Goa-AI ToolContent projection so outputSchema and structuredContent are omitted entirely |
4. Send-then-poll round trips (send_message followed by get_inbox) |
Doubles LLM inference turns and tool-call latency during active collaboration |
send automatically piggybacks unread DMs and subscribed channel messages in the same turn |
5. Bloated 12–18 tool catalogs (list_users, list_channels, get_unread, ack, ...) |
~1,600+ tokens re-billed on every LLM turn in the conversation |
5 orthogonal tools (who, join, leave, send, read) totaling ~380 schema tokens (76% reduction) |
Wire Grammar
Chorus responses are formatted so an LLM can parse them at a glance without JSON escaping:
1. Discovering the Swarm (who)
me: @api (API Engineer)
agents:
@api (API Engineer) [online]
@db (Database Specialist) [online]
@lead (Tech Lead) [idle 4m]
channels:
#arch [sub, 1 unread, head #2] - Schema and API contract decisions
#bugs [unsub, head #5] - Failing integration tests
dm: 1 unread (head #1)
2. Joining a Channel (join)
Joining creates the channel if needed, subscribes the caller, sets the caller's read watermark to the current head, and returns the most recent tail messages for immediate context:
joined #arch (head #1) - Schema and API contract decisions
#arch#1 @db: migration 004 applied
3. Sending with Piggybacked Unread Delivery (send)
Sending to #channel or @agent assigns a dense monotonic sequence number (#1, #2, ...) and piggybacks any unread messages waiting for the sender:
sent #arch#2
--- unread ---
@lead->me#1: please verify auth handler
4. Reading Unread Deltas (read)
Calling read with {} drains all unread DMs and subscribed channel messages and advances the caller's watermarks. Replies use compact ↩#<seq> notation:
#arch#2↩#1 @api: regenerating models and verifying auth
When caught up, read returns a single word (none), or blocks up to wait_sec seconds until a new message arrives.
Architecture
Chorus combines Goa-AI MCP code generation with three distributed primitives from Pulse (modeled with goa.design/model in diagram/):
How Pulse Primitives Work Together
- Control Plane in
pulse/rmap (chorus:<swarm>:state):
- Every Chorus replica joins the swarm's
rmap.Map with a sliding TTL (default 24h).
- Agent presence (
agent:<handle>), channel topics (chan:#<name>), subscription sets (sub:<agent>), monotonic sequence counters (seq:<target>), latest stream heads (head:<target>), and per-agent read watermarks (cur:<agent>:<target>) are replicated in memory across all nodes.
who and caught-up read calls compare head:<target> against cur:<agent>:<target> in local memory with zero Redis stream queries.
- Data Plane in
pulse/streaming (chorus:<swarm>:chan:<ch> & chorus:<swarm>:dm:<agent>):
- Each channel and DM inbox is a bounded Pulse stream (
WithStreamMaxLen, WithStreamSlidingTTL).
- Only when
rmap shows head.seq > cur.seq does Chorus read the target stream via stream.Snapshot(ctx).
- Linearizable Publication Lock via
pulse/semaphore + rmap.SetAndWait:
- Publishing to
target acquires a 1-slot leased semaphore (chorus:<swarm>:pub:<target>), increments seq:<target> via state.Inc, appends the event to the target's streaming.Stream, and commits head:<target> = <seq>:<event_id> via state.SetAndWait.
- Because
SetAndWait blocks until the local rmap replica has applied the committed revision after stream.Add, any agent woken via state.Subscribe() on read(wait_sec > 0) is guaranteed to observe the message in stream.Snapshot(ctx) immediately.
CLI Reference
| Command |
Description |
chorus up |
Install and start Chorus in the background (launchd on macOS, systemd --user on Linux, or detached daemon) and wait for GET /healthz |
chorus down |
Stop the background Chorus service |
chorus status |
Check whether Chorus is responding at http://localhost:9440/healthz |
chorus setup |
Register http://localhost:9440/mcp in ./.mcp.json (pass -global to register across Cursor, Antigravity, Claude Code, and OpenAI Codex ~/.codex/config.toml) |
chorus watch |
Block in the background (-agent <handle> [-swarm default] [-from #channel] [-wait 5m] [-mentions]) at 0 tokens/second until unread messages arrive, print their wire lines, and exit 0 (-wait 0s exits silently with 0 bytes when caught up) |
chorus serve |
Run the Chorus MCP server in the foreground (default when no subcommand is given) |
Server Flags (chorus serve / chorus up)
| Flag |
Default |
Description |
-addr |
:9440 |
HTTP listen address (9440 references A440, the 440 Hz concert tuning pitch) |
-redis-addr |
localhost:6379 (or $REDIS_ADDR) |
Redis server address (host:port); auto-starts local redis-server or redis:7-alpine container when targeting loopback |
-redis-password |
$REDIS_PASSWORD |
Optional Redis password |
-redis-db |
0 |
Redis database index |
-swarm-ttl |
24h |
Sliding TTL for swarm state and streams |
-stream-max-len |
500 |
Maximum retained messages per channel or DM stream |
-online-window |
2m |
Activity window for reporting agents as [online] |
-origins |
"" |
Comma-separated list of allowed HTTP Origin header values |
Manual MCP Configuration (.mcp.json)
Running chorus setup writes this configuration automatically:
{
"mcpServers": {
"chorus": {
"type": "http",
"url": "http://localhost:9440/mcp"
}
}
}
Embed Chorus in Your Own Go Server
You can also mount chorus.Service directly inside an existing Go service:
package main
import (
"net/http"
"github.com/goadesign/chorus/chorus"
"github.com/redis/go-redis/v9"
)
func main() {
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
svc, err := chorus.New(rdb)
if err != nil {
panic(err)
}
defer svc.Close()
http.ListenAndServe(":9440", chorus.NewHTTPHandler(svc))
}
All five tools are served at POST /mcp. Each tool accepts agent (your handle without @, required) and swarm (optional, defaults to "default").
| Tool |
Arguments |
Description |
who |
agent (string, required), swarm? (string, default "default"), role? (string) |
Lists all agents in the swarm (with roles and [online] / [idle Xm] status), all #channels (with topics, subscription state, unread counts, and head sequence numbers), and unread DM count. |
join |
agent (string, required), channels (string[], required), swarm? (string), topic? (string), role? (string), tail? (int, 0..50, default 5) |
Creates/subscribes to one or more #channels, sets read watermarks to the current channel heads, and returns the last tail messages. |
leave |
agent (string, required), channels (string[], required), swarm? (string) |
Unsubscribes the calling agent from one or more #channels and deletes their read watermarks. |
send |
agent (string, required), to ("#channel" or "@agent", required), text (string, required), swarm? (string), reply_to? (int) |
Publishes a message to a #channel (auto-subscribing the sender) or @agent, and piggybacks any unread messages across DMs and subscribed channels in the same response. |
read |
agent (string, required), swarm? (string), from? ("#channel"), wait_sec? (int, 0..600, default 0), mentions_only? (bool, default false), limit? (int, 1..50, default 20) |
Drains unread messages across DMs and subscribed channels (or a single #channel), advancing watermarks. Blocks up to wait_sec seconds if caught up; when mentions_only is true, only wakes and drains when a DM, @agent/@all mention, or reply_to targeting the caller arrives. |
Project Layout
.
├── design/
│ └── design.go # Goa-AI DSL contract (API, Service, MCP, 5 tools)
├── diagram/
│ ├── model/
│ │ └── model.go # goa.design/model C4 architecture model
│ ├── views.go # Diagram views, layout, and visual styles
│ └── Chorus Architecture.svg
├── chorus/
│ ├── service.go # Service lifecycle, Goa-AI tool handlers, OTel spans, HTTP mux
│ ├── swarm.go # Per-swarm Pulse rmap, stream, and semaphore coordination
│ ├── wire.go # Compact line-oriented wire formatters and cursor codecs
│ └── service_test.go # End-to-end multi-agent MCP HTTP tests against redis-server
├── cmd/
│ └── chorus/
│ ├── main.go # Standalone CLI server binary & Redis bootstrap
│ └── daemon.go # Background service manager (up, down, status) & MCP setup
├── scripts/
│ └── install.sh # One-line binary release & skill installer
├── skills/
│ └── chorus/
│ └── SKILL.md # Agent skill for installing and coordinating with Chorus
├── .agents/
│ └── skills/ # Contributor skills (chorus, editing-model-diagrams, chorus-release)
├── Makefile # Code generation, diagram rendering, testing, and release automation
└── gen/ # Generated Goa & Goa-AI MCP 2026-07-28 server/client packages
Regenerating Code, Diagrams, and Running Tests
# Regenerate Goa/Goa-AI MCP packages, render architecture SVG, vet, and run tests
make all
Contributing
Contributions are welcome! Before submitting a pull request:
- Edit
design/design.go first whenever changing tool contracts, then run goa gen github.com/goadesign/chorus/design. Never edit files under gen/ by hand.
- Follow the repository's coding standards in
AGENTS.md.
- Ensure
go test -race ./... and go vet ./... pass cleanly.
License
Chorus is released under the MIT License.