Agent Orchestrator
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., "@Agent OrchestratorList all registered agents."
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.
Agent Orchestrator
A shared MCP (Model Context Protocol) hub that lets multiple coding-agent CLIs register themselves and talk to each other — bidirectional, non-blocking, no fixed ports to remember.
Ships with adapters for OpenCode and for Claude Code itself (via a companion claude-channel plugin, since Claude Code has no server of its own); the adapter interface is designed so support for other agent CLIs (Codex, Pi, Antigravity, ...) can be added without touching the core.
Why This Exists
Bridges that spawn a subprocess, wait for it to finish, and collect the result don't let two agents actually converse — one side always blocks. Agent Orchestrator instead runs as a small standalone daemon that both agents connect to as MCP clients:
Either side sends a message at any time with
agent_send. Delivery is live for every adapter — the target starts processing it in its active turn immediately, the same way a human's message would. Nothing is queued.The reply comes back the same way: as the other agent's own
agent_sendcall targeting you. There is no polling step in the middle —agent_readexists separately, for pulling a target's own past history (e.g. what it did during a long async turn), not for noticing new inbound messages.Registration is explicit and per-instance: you register only the specific agent window you want exposed, so a "working" window you never register stays completely invisible to the orchestrator.
Every registration also names a
session— an orchestrator-level group, agreed out of band with whoever you want to reach. Agents in different sessions can't see or address each other even if both are registered, which is what stops two unrelated conversations from crossing wires (a real failure mode this project hit and fixed — see Troubleshooting).
Related MCP server: central-mcp
Architecture
Claude Code ── stdio ──► claude-channel plugin ──┐
(this session) (local :dynamic, REST+SSE)│
├── HTTP (streamable MCP) ──► Agent Orchestrator daemon
opencode window A (registered) ────────────────────┘ :8765/mcp (registry + per-agent adapter)
│
opencode window B ───────────────────────────────────────────── REST + SSE (per adapter)
(unregistered, never touched) ▼
opencode A's own
HTTP API (:port), or
claude-channel's (:dynamic)The daemon (
agent-orchestrator) is a long-running process exposing one MCP endpoint over Streamable HTTP. Claude Code and every opencode window connect to it as independent clients (stateful MCP sessions,mcp-session-idheader).The registry tracks every registered agent (
{agentId, agentType, session, host, port, label}) and, per agent, holds one persistent event stream plus a per-backend-session ring buffer (last 500 events, monotonic cursor — reading history never silently stalls once one passes the cap). Every lookup is scoped bysession: an agentId that exists but belongs to a different session is treated as not found.Adapters translate the registry's generic calls into a specific backend's protocol. The
opencodeadapter talks to opencode's own REST + SSE API (/session,/session/:id/prompt_async,/event). Theclaudeadapter talks the same REST + SSE shape, but to a localclaude-channelcompanion process (see below), not to Claude Code directly.
Reaching Claude Code
Claude Code, unlike opencode, has no server of its own to adapt to — so it can't be registered directly. claude-channel (src/channel-plugin/claude-channel.ts) closes that gap: it's a Claude Code "channel" plugin (research preview), spawned by Claude Code itself as a stdio MCP subprocess when the session starts with --channels/--dangerously-load-development-channels. It exposes a small local REST+SSE surface that mirrors the opencode adapter's shape, so the claude adapter can register and talk to it exactly like any other agent — inbound text gets pushed straight into the live session via the claude/channel notification (real push, not polling), and Claude's replies (via the plugin's reply tool) come back out over SSE.
The port is dynamic (OS-assigned, 0 by default) specifically so multiple Claude Code windows can each run their own instance without colliding — there's no fixed address to hardcode. Since there's nothing external to detect a not-yet-running process's port from, the instance reports its own to the model that owns it: baked into that session's connect-time system prompt, and re-checkable anytime via the plugin's whoami tool. Every live instance's {pid, port, host, startedAt} is also on disk at runtime/claude-channel-instances/<pid>.json, garbage-collected on each new startup (a file whose pid is no longer alive is a previous instance that crashed or was force-killed without cleanup, not a conflict — a dynamic port can't collide by construction). See skills/agent-orchestrator/references/claude-channel.md for setup.
With more than one claude-type agent registered in the same session (one per window), list_agents won't tell them apart beyond the label each was given at registration — always check it before assuming an agentId is yours. Windows on genuinely unrelated tasks should use different session names instead, so they can't see each other at all.
Prerequisites
Node.js >= 18.0.0
OpenCode CLI installed and authenticated (
npm install -g opencode-ai, thenopencode login)
Installation
npm install
npm run buildRunning the daemon
npm start
# Agent Orchestrator listening on http://127.0.0.1:8765/mcpIt needs to be running before either Claude Code or opencode connects to it. Run it in its own terminal, or set it up as a background service — there's no auto-start built in on purpose, since it's a shared resource independent of any one agent's lifecycle.
Supervised mode (recommended for background running)
npm run start:supervised runs the daemon under scripts/supervisor.js: stdout/stderr are captured to runtime/daemon.out.log / runtime/daemon.err.log, every start/exit is recorded in runtime/daemon.supervisor.log (exit code/signal), and the daemon is respawned with backoff after a crash. The daemon also installs crash guards that log uncaughtException / unhandledRejection stacks synchronously before exiting — a silent death becomes a diagnosable log line instead of a vanished hidden console. On Windows, pair it with a Task Scheduler entry at logon so it survives reboots.
Environment variables
Variable | Default | Description |
|
| Host the MCP endpoint binds to |
|
| Port the MCP endpoint listens on |
Persistence
Registrations and event history survive daemon restarts. Every change to the registry (an agent registering or unregistering, an event landing in a buffer) is written to runtime/state.json, debounced to at most one write every 500ms, with a final flush on shutdown. On boot the state is loaded back: each persisted agent is re-validated with a live health check and only re-registered when it's still reachable (a window that died while the daemon was down is skipped), and its per-session event buffers are restored so agent_read history survives. MCP client connections themselves are transient — each client must reconnect and re-initialize after a restart — but the registrations they rely on persist.
Connecting Claude Code
claude mcp add --transport http agent-orchestrator -s user http://127.0.0.1:8765/mcpConnecting opencode
opencode mcp add
# follow the prompt: transport = http, url = http://127.0.0.1:8765/mcpRegistering an agent instance
This is the deliberate, per-instance step that keeps unregistered windows invisible to the orchestrator — there's no automatic/background registration, no plugin hook, no polling for new instances. You decide, per window, when it joins.
The full operational guide (how to detect your own port safely, the complete tool reference, a worked example) lives in skills/agent-orchestrator/SKILL.md — written to be followed by any agent CLI, not just Claude Code. AGENTS.md at the repo root points opencode (and other AGENTS.md-aware CLIs) at it automatically.
MCP Tools
Every tool below takes a mandatory session — an orchestrator-level group name, not a backend chat id (that's backendSessionId, a separate thing). Only agents sharing the same session can see or address each other; agree on a name with whoever you want to reach before registering.
register_agent
Register a running agent instance. Input: { agentType, session, port, host?, label? }. Output: the stored AgentRecord. The first registration with a new session name creates it.
unregister_agent
Input: { agentId, session }. Stops its event stream and drops its buffers. Returns false if that agentId isn't registered in that session — even if it exists in a different one.
list_agents
Input: { session }. Returns every agent registered in that session — nothing from any other.
agent_create_backend_session
Creates a chat thread on the target's own backend (e.g. a fresh opencode session) — not related to the orchestrator session beyond needing it to find the agent. Input: { agentId, session, title?, agent? }. Output: { backendSessionId, title }.
agent_list_backend_sessions
Input: { agentId, session }. Returns every chat thread known to that agent's backend (not just ones created through the orchestrator — opencode's session store is shared across all its own instances).
agent_send
Input: { agentId, session, text, backendSessionId?, agent?, system? }. Delivers live into the target's active turn for every adapter — this call itself returns as soon as the backend accepts it ({ accepted: true }), not once the target replies, but the reply itself also arrives live, as the target's own agent_send call back to you. Don't follow it with an agent_read loop; there's nothing there to wait for. Fails if agentId isn't registered in session. backendSessionId is optional: omit it to use the session's single canonical conversation — the daemon resolves (or creates) one backend session per (session, agent) automatically and persists it (claude → 'default'; opencode → a dedicated session titled with the orchestrator session name).
agent_read
Input: { agentId, session, backendSessionId, sinceCursor? }. Output: { events, cursor, hasMore, droppedEvents? }. Retrieves a target's past event history — useful for checking what an agent did during a long async turn, not for receiving new messages (those arrive live via agent_send, see above). droppedEvents appears if your cursor pointed at history already evicted from the 500-event ring buffer.
agent_status
Input: { agentId, session }. Returns the agent's record plus a live reachable health check.
user_send
Send a message to the human user. Input: { session, text }. Output: { accepted }. The Telegram bot delivers it to the user's chat — it only ever reaches the human, never another agent.
user_send_file
Send one or more local files (+ optional message) to the human user. Input: { session, paths, text? }. Output: { accepted, files }. The bot uploads each file to Telegram (sendDocument; text becomes the caption on the first file only). All paths must exist on the daemon's machine. Delivery is best-effort — if the bot isn't running, the event is not queued.
reply (claude-channel only, not the orchestrator daemon)
A separate MCP server — the local claude-channel process, not agent-orchestrator itself. Input: { text }. Writes to that channel's own local transcript, for a human directly watching it (e.g. via /events). It does not reach another orchestrator agent; to answer whoever messaged Claude Code, call agent_send targeting their agentId, same as any other agent.
Adding a new agent type
Implement the AgentAdapter interface (src/adapters/types.ts) for the new backend and register it in src/adapters/index.ts. The registry, event buffering, cursor logic, and every MCP tool are already generic — they don't know or care which adapter they're talking to.
Telegram bridge
A parallel, human-facing channel that runs completely outside the agent graph: a separate telegram-bot/ process (grammY) talks to an internal /bridge REST + SSE surface on the daemon. It mirrors inter-agent messages live to a Telegram chat and lets a human send messages to any agent — without agents ever learning the channel exists. No new agent type, no registration, no changes to adapters or to the registry's event buffers; the daemon just emits a mirrored message onto an in-memory bus whenever an agent_send is delivered.
Endpoint | Description |
| List all orchestrator session names |
| List agents registered in session X |
| List A's backend chat threads |
| Read A's past events for thread B since cursor N |
| Send a message to an agent; body |
| SSE stream of inter-agent messages in session X |
Every /bridge route requires the shared BRIDGE_TOKEN, sent either as Authorization: Bearer <token> or ?token=<token>. Leave BRIDGE_TOKEN unset to disable the surface entirely (all routes return 403).
Variable | Default | Description |
| — | Shared secret for |
For a noob-friendly setup, run cd telegram-bot && npm run setup — it asks for the bot token interactively (create the bot with @BotFather, message it once so your chat id can be detected), generates the shared BRIDGE_TOKEN, and writes both telegram-bot/.env and the root .env automatically. Then restart the daemon (it loads .env at startup) and run npm run dev in telegram-bot/.
Run and configure the Telegram side from telegram-bot/ — see telegram-bot/README.md for its env vars and start instructions. The daemon must be restarted (with BRIDGE_TOKEN set) to pick up the /bridge surface.
Troubleshooting
Port already in use — set AGENT_ORCHESTRATOR_PORT to something else and re-register both clients against the new URL.
register_agent fails with "Could not verify a reachable agent" — the port doesn't respond, or it's not actually opencode. Re-run the port-detection step from inside the target window; ports change every time opencode restarts.
Sent a message but the other agent never seemed to receive it — check you called agent_send, not agent_read (there's nothing to receive by polling) or, on the Claude Code side, the channel's own reply tool (that only writes to its local transcript, never to another agent).
Task finished but no completion report came back — reporting back isn't automatic. The receiving agent has no guarantee it consulted the skill before acting, so its default is to answer wherever it normally would (its own console/human), not through the channel. Ask for the report explicitly in the message: "reply via agent_send to <your agentId> when done."
A reply landed on the wrong agent, or agent_send/list_agents claims an agentId doesn't exist even though it was just registered — almost always a session mismatch: either the two sides agreed on different names, or one side is reusing a memorized agentId from an earlier, unrelated session instead of re-checking list_agents for the current one. This is exactly the isolation working as intended (see Why This Exists) — re-confirm both sides are using the same session string, not just guessing.
Logs — runtime/orchestrator.log (created next to wherever you run the daemon from).
Development
npm run typecheck
npm run lint
npm test
npm run buildTests are network-free: adapter SSE parsing and the registry's cursor/ring-buffer logic are both tested against fakes, no real opencode instance required.
License
MIT. See LICENSE for details.
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.
Related MCP Servers
- Alicense-qualityBmaintenanceA shared memory and coordination server for multiple AI coding agents, built on the Model Context Protocol (MCP).5MIT
- Alicense-qualityBmaintenanceA centralized MCP hub for managing multiple coding agents across projects, enabling parallel, non-blocking dispatch and orchestration from any MCP-capable client.5MIT
- Alicense-qualityBmaintenanceA local MCP server that connects AI coding agents (Claude Code, Codex, Cursor, etc.) on the same machine via a shared message bus, enabling them to chat, delegate tasks, and collaborate privately without cloud or internet.2113MIT
- Alicense-qualityDmaintenanceMCP server that enables AI coding agents to communicate, share state, and coordinate work in real time via MCP tools or REST API.895MIT
Related MCP Connectors
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
User-owned memory for AI agents, Copilot, Claude, IDEs, CLIs, and chat apps over remote MCP.
Agent-native collaboration network: orchestrate a team of long-running agents from any MCP client.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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/Hackerprod/Agent-Orchestrator'
If you have feedback or need assistance with the MCP directory API, please join our Discord server