Skip to main content
Glama
README.md
# Agora — NATS + MCP

A shared communication bus so that **multiple agents can access and talk to each other**.

NATS is the message bus; MCP is the entry point that lets an MCP client (Claude Code,
Codex, or any other) reach into the Agora. There is no fixed roster — any number of
agents can join by subscribing to the bus, and any MCP client can list, message, and
query them. Agents come and go freely; the control plane just tracks who is currently online.

## Architecture

```
MCP clients (Claude Code / Codex / any MCP client)
      │
  MCP Server (FastMCP, stdio)
      │
  Control Plane (FastAPI :8100)
      │
     NATS (:4222)
   ┌──┬──┬──┬── … ──┐        ← any number of agents can join/leave
 agent  agent  agent  …       (echo, researcher, writer, your own…)
      │
  Webhooks → n8n → your stack
```

## Quick Start

```bash
# 1. Start NATS (native binary — no Docker needed)
nats-server -js -m 8222 --name swarm-nats
# (Alternative, if you prefer Docker:)
#   docker run -d --name nats -p 4222:4222 -p 8222:8222 nats:latest -js -m 8222

# 2. Create the Python env (system Python 3.9 is too old for `mcp`; use 3.11+)
#    This repo ships a ready-to-use venv at .venv313 (Python 3.13).
#    To recreate it:  uv venv --python 3.13 .venv313 && .venv313/bin/pip install -r requirements.txt

# 3. Start the control plane (FastAPI on :8100)
export PYTHONPATH=.
.venv313/bin/python -m core.control_plane

# 4. Start agents (each in its own terminal or as a background process)
.venv313/bin/python -m agents.echo_agent
#    Team example (two cooperating agents):
#      .venv313/bin/python -m agents.team_example researcher
#      .venv313/bin/python -m agents.team_example writer

# 5. Use the MCP server from Claude Code (see mcp-config.snippet.json)
#    Add it to your Claude Code / claude_desktop_config.json mcpServers block.
```

There is also a `run.sh` helper and a `Makefile` for the common commands:

```bash
./run.sh nats        # start NATS
./run.sh control     # start the control plane
./run.sh echo        # start the echo agent
./run.sh bridge <id> # join a turn-based AI (Claude Code/GPT) — see JOINING-AI-AGENT.md
./run.sh test        # run the unit tests
make nats / make control / make echo / make test
```

**Joining:** long-running agent → [JOINING.md](JOINING.md) · turn-based AI →
[JOINING-AI-AGENT.md](JOINING-AI-AGENT.md) · call over HTTP without NATS →
[A2A.md](A2A.md).

## MCP Tools

Once the MCP server is wired in, an MCP client gets these tools (all take flat arguments):

| Tool | Purpose |
|---|---|
| `agora_list_agents` | List agents currently on the bus (optional status filter) |
| `agora_get_agent` | Details for one agent |
| `agora_send_message` | Fire-and-forget direct message to an agent |
| `agora_request_reply` | Send a request and wait for the agent's reply |
| `agora_broadcast` | Message all agents (or a topic) |
| `agora_publish` | Publish to any raw NATS subject |

There's also a **remote MCP server** (Streamable HTTP) so you can add the Agora
as a connector in **Claude web / ChatGPT web** (`search` + `fetch` included) —
see **[MCP-CONNECT.md](MCP-CONNECT.md)**.

## A2A — external access (other devices / other GPTs)

The Agora also speaks the **[A2A protocol](https://a2a-protocol.org)** as its
public contract, so outside agents (another machine, a custom GPT, any A2A SDK
client) can discover and call Agora agents over HTTP — no NATS, no bus
membership. The control plane serves Agent Cards and bridges A2A Tasks to NATS.

| Method | Path | Purpose |
|---|---|---|
| GET | `/.well-known/agent-card.json` | Card for the Agora |
| GET | `/a2a/agents` | Live catalog of agent cards |
| GET | `/a2a/{id}/.well-known/agent-card.json` | One agent's card |
| POST | `/a2a/{id}` | JSON-RPC `message/send` · `message/stream` · `tasks/get` · `tasks/cancel` |

Set `A2A_TOKEN` to require a bearer token, and `A2A_BASE_URL` (+ a tunnel) to
expose it publicly. **See [A2A.md](A2A.md)** for the full guide: client example,
the worker reply contract, auth, and exposing to remote/cloud clients.

## NATS Subject Conventions

| Pattern | Purpose |
|---|---|
| `agent.{id}.inbox` | Direct message to a specific agent |
| `agent.{id}.status` | Agent status updates |
| `agora.broadcast` | Message all agents |
| `agora.topic.{topic}` | Topic-based pub/sub |
| `agora.request.{id}` | Request/reply pattern |
| `control.register` | Agent registration |
| `control.deregister` | Agent deregistration |
| `control.heartbeat` | Agent heartbeats (carry full info, so a restarted control plane re-registers agents) |

## Creating a New Agent

Any agent can join the bus by subclassing `BaseAgent` — that's how "multiple agents can
access" the Agora:

```python
from core.base_agent import BaseAgent
from core.models import SwarmMessage

class MyAgent(BaseAgent):
    def __init__(self):
        super().__init__(agent_id="my-agent", agent_type="worker", capabilities=["analyze"])

    async def handle_message(self, subject: str, message: SwarmMessage) -> None:
        if message.payload.get("action") == "analyze":
            result = {"ok": True}
            # For a request, reply on the one-shot reply subject:
            await self.reply_to_request(message, result)

if __name__ == "__main__":
    import asyncio
    asyncio.run(MyAgent().start())
```

## NATS Monitoring

NATS exposes a monitoring endpoint at `http://localhost:8222` when started with `-m 8222`.