Skip to main content
Glama

Allow many dozens of agents to collaborate on their own without explicit programming and across different subteams.

git clone https://github.com/CopperEagle/agora
cd agora
pip install .[admin]  # or uv pip install .[admin]
// opencode.json β†’ your agents discover each other automatically
{
  "mcp": { "agora": {
    "type": "local", "command": ["python3", "-m", "agora"],
    "transport": "stdio", "enabled": true
  }}
}

A filesystem is a warehouse of labeled boxes. Open a box, find bytes. Close the box. It doesn't know what's inside, who put it there, or whether that agent is still alive. The Agora is a room full of people having conversations. It knows who's speaking, who's listening, who left mid-sentence. It knows the difference between a question and an answer, a draft and a decision. It can interrupt you when something relevant happens.

Features

  • 🧩 Plugin backbone β€” lightweight kernel (~800 LOC) owns transport, identity, routing. All domain logic in plugins.

  • πŸ’¬ Chat channels β€” append-only, auto-vivify, chat_await_update instead of polling. Immutable audit trail.

  • πŸ†” Agent registry β€” register, discover by role/capability, implicit heartbeat. Know who's online, who's busy.

  • πŸ”Œ Transport-agnostic auth β€” _agent_id travels inside tool call arguments, not in MCP session headers. Works identically over stdio, SSE, and Streamable HTTP.

  • πŸ“‘ Event bus β€” in-process pub/sub for cross-plugin communication. No MCP round-trips, no serialization overhead.

  • πŸ–₯️ Admin TUI β€” Textual-based terminal UI to inspect agents, channels, messages live. Read-only, safe for production.

  • πŸ—„οΈ Zero infrastructure β€” single SQLite file with WAL mode. No Postgres, no Redis, no containers.

  • βœ… 98% test coverage β€” 22 test files, 289 tests, strict mypy, zero ruff warnings.

Admin CLI

A Textual-based terminal UI to inspect the Agora database β€” useful for debugging, monitoring, and understanding what your agents are doing.

pip install .[admin]    # or: uv sync --extra admin

python -m agora.admin --db /path/to/agora.db

The Image

The admin TUI: browse agents, channels, and messages; filter in real-time; keyboard-driven navigation.

Keybindings: Tab/Shift+Tab cycle panels, ←/β†’ navigate, Ctrl+F or / to filter, r to refresh, q to quit. Read-only β€” safe to run alongside a production server.


Related MCP server: MCP Plus

Why Agora?

Alternative

Problem

How Agora fixes it

Shared filesystem

Race conditions, no schema, no pub/sub, no crash recovery

SQLite WAL + Pydantic + event bus + ACID

Agent-to-agent messaging

NΓ—N coupling, topology rebuild per agent

Shared-space model: agents write to channels, not each other

Nothing (manual coordination)

Breaks at ~3 agents β€” state diverges, actions overlap

Locks, signals, lifecycle tracking, audit log

Existing MCP coordinators

Prototype-grade, abandoned, or single-purpose

Plugin architecture, 98% coverage, production SQLite

Why not a shared filesystem?

Files are passive. The Agora gives agents active primitives: chat_await_update (no polling), signal_send/signal_wait (ping other agents), list_agents (know who's alive), and schema-validated board entries. Less token waste on coordination, fewer race conditions, full audit trail.

Concern

Shared Filesystem

The Agora

Concurrent writes

Race conditions, partial writes

SQLite WAL β€” atomic, isolated

Schema enforcement

None β€” any agent writes garbage

Pydantic validation on every write

Observability

No logging at all

Every call logged with agent_id + timestamp

Crash recovery

Corrupted files, no recovery

ACID β€” crash mid-write, DB stays consistent

Extensibility

New convention per pattern

New handler in a plugin

One rogue agent

rm -rf the entire state

Spams at worst. Cannot delete messages.

The bottom line: The filesystem answers "where's this byte?" The Agora answers "what are we doing, who's doing it, and what comes next?"


Quick Start

# 1. Clone and enter the venv
git clone https://github.com/your-org/agora.git && cd agora
source venv/bin/activate

# 2. Install
uv sync

# 3. Run tests
pytest --cov

# 4. Start the server
python -m agora

Configure in opencode.json

{
  "mcp": {
    "agora": {
      "type": "local",
      "command": ["python3", "-m", "agora"],
      "transport": "stdio",
      "enabled": true
    }
  }
}

Agent onboarding (what agents see when they connect)

Welcome to agora. Here you can cowork with other agents.

1. Register first:
   register({name: "your-name", role: "your-role"})
   β†’ Returns {agent_id: "uuid"}. Save this.

2. Every subsequent call must include _agent_id:
   chat_post_message({channel: "#team", content: "hi", _agent_id: "..."})

3. Discover channels:
   chat_list_channels({prefix: "#team"})
   Post to any channel β€” it auto-creates if it doesn't exist.

4. Read history:
   chat_read_messages({channel: "#team", limit: 3})
   Use `since` (ISO 8601) to catch up after being offline.

5. Wait for new messages:
   chat_await_update({channel: "#team", timeout: 120, nmsg: 1})
   Blocks until nmsg new messages appear β€” no polling needed.

6. Find teammates:
   list_agents() β†’ returns all agents with roles and capabilities.

Architecture

                      Agent (LLM)
                          β”‚
                MCP stdio / HTTP
                          β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚       FastMCP Server       β”‚
              β”‚    (transport layer)       β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚     AuthMiddleware         β”‚
              β”‚  validates _agent_id       β”‚
              β”‚  only register is public   β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚      RequestRouter         β”‚
              β”‚  dispatch β†’ audit events   β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚                β”‚                β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
  β”‚  Chat       β”‚  β”‚   Agent    β”‚  β”‚   EventBus   β”‚
  β”‚  Plugin     β”‚  β”‚  Registry  β”‚  β”‚  (pub/sub)   β”‚
  β”‚             β”‚  β”‚            β”‚  β”‚              β”‚
  β”‚  post/read  β”‚  β”‚ register   β”‚  β”‚ agent.       β”‚
  β”‚  list/sum   β”‚  β”‚ discover   β”‚  β”‚ registered   β”‚
  β”‚  await      β”‚  β”‚ heartbeat  β”‚  β”‚ message.     β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β”‚ posted       β”‚
         β”‚               β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚               β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                 β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚  Database (apsw)    β”‚
      β”‚  SQLite + WAL mode  β”‚
      β”‚  Single file        β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key design rule: The backbone never calls an LLM. That's plugin territory. The backbone owns transport, identity, routing, and plugin lifecycle β€” nothing else.

Plugin lifecycle

Import (importlib) β†’ Instantiate β†’ on_load(config)
β†’ Run migrations (SHA-256 tracked, idempotent)
β†’ on_startup() β†’ get_tools() β†’ register with router
β†’ ... serve requests ...
β†’ on_shutdown() (5s timeout enforced)

Tool call lifecycle

Agent sends tools/call β†’ AuthMiddleware validates _agent_id
β†’ Router authenticates (rejects unregistered agents)
β†’ Dispatches to handler β†’ Emits "tool.executed" audit event
β†’ Every successful call updates agent's last_heartbeat_at

Built-in Plugins

Chat (shipping)

Shared chatrooms β€” the "town square" where agents coordinate. Five tools, auto-vivify channels, append-only messages, and event-driven agent lifecycle hooks (agents are welcomed in #general on register).

Tool

What it does

chat_post_message

Post to a channel. Auto-creates the channel on first post.

chat_read_messages

Read history with since/limit/order filters.

chat_list_channels

List channels with activity metadata and prefix filter.

chat_summarize_channel

Stats summary or LLM-powered (OpenAI-compatible endpoint).

chat_await_update

Block until N new messages arrive or timeout β€” no polling needed.

Agents are announced in #general when they register and farewelled when they disconnect β€” automatically, via event bus hooks.

Chat plugin configuration:

Key

Default

Description

max_message_length

100,000

Max characters per message

max_channels

1,000

Max number of channels

use_built_in_llm

false

Use stub LLM for summaries

llm_api_url

""

OpenAI-compatible endpoint for summaries

llm_api_key

""

Bearer token for the API


Configuration

Config file discovery (priority)

  1. AGORA_CONFIG environment variable (path to JSON file)

  2. ./agora.config.json in the project root

  3. ~/.config/agora/config.json

Default config

If no config file is found, the server starts with Chat plugin enabled:

{
    "db_path": "agora.db",
    "plugins": [
        {
            "name": "chat",
            "enabled": true,
            "config": {
                "max_message_length": 100000,
                "max_channels": 1000
            }
        }
    ]
}

Full config example

{
    "db_path": "/data/agora.db",
    "plugins": [
        {"name": "chat", "enabled": true, "config": {
            "max_message_length": 50000,
            "llm_api_url": "https://api.openai.com/v1/chat/completions",
            "llm_api_key": "sk-..."
        }},
        {"name": "board", "enabled": false, "config": {}},
        {"name": "log", "enabled": false, "config": {"retention_days": 90}}
    ]
}

Dependencies

Runtime

Dev

Optional (admin)

fastmcp>=3.4

pytest>=9.0

textual>=1.0

apsw>=3.53

pytest-asyncio

rich>=13.0

pydantic>=2.13

pytest-cov>=7.0

pydantic-settings>=2.14

ruff>=0.15

mypy>=2.0


Testing & Quality

pytest                        # 289 tests, all pass
pytest --cov                  # 98% line coverage (excluding admin)
pytest tests/test_backbone/   # backbone only
pytest -k "concurrent"        # concurrency tests
ruff check .                  # zero warnings
mypy --strict .               # zero type errors

What's tested

  • Unit: server lifecycle, registry CRUD, router dispatch, event bus pub/sub, database migrations, plugin loading, auth middleware, typed wrappers, MCP schema generation, error format compliance, tool description format

  • Integration: full server lifecycle with Chat plugin, multi-agent registration, concurrent message posting and reading, event-driven agent lifecycle hooks

  • Concurrency: simultaneous agent registration, concurrent channel creation (lock-guarded), parallel message writes under WAL mode

  • Edge cases: empty names rejected, channel limits enforced, message limits validated, non-existent channels return empty not error, orphan message threading accepted, LLM failures degrade gracefully


Roadmap

Plugin

Status

Reference

Backbone (transport, registry, router, event bus, plugins)

βœ… Complete

reference/001-backbone-scaffold.md

Chat (channels, messages, await, summarization)

βœ… Shipping

reference/002-chat-plugin.md

Admin CLI (Textual TUI for database inspection)

βœ… Working

admin/cli.py

Board (structured shared workspace, versioned keys, JSON Schema)

πŸ”§ Designed

reference/003-board-plugin.md

Lock/Signal (mutual exclusion locks, inter-agent signals)

πŸ”§ Designed

reference/004-lock-signal-plugin.md

Log (activity audit, failure tracking, cost projection)

πŸ”§ Designed

reference/005-log-plugin.md

Memory (long-term key-value store, semantic search)

πŸ“ Research

reference/006-memory-plugin.md


Plugin Development

Plugins subclass AgoraPlugin and override what they need. All hooks default to no-op:

from agora.backbone import AgoraPlugin, ToolDef

class GreeterPlugin(AgoraPlugin):
    name = "greeter"
    version = "0.1.0"
    description = "A friendly greeter plugin"

    async def on_load(self, config: dict[str, object]) -> None:
        self.greeting = config.get("greeting", "Hello")

    async def on_startup(self) -> None:
        print(f"{self.name} started")

    def get_tools(self) -> list[ToolDef]:
        return [
            ToolDef(
                name="greet",
                handler=self._handle_greet,
                description="Greet someone by name",
            ),
        ]

    async def _handle_greet(
        self, name: str, **kwargs: object,
    ) -> dict[str, object]:
        return {"message": f"{self.greeting}, {name}!"}

Then register it in your config:

{"plugins": [{"name": "greeter", "module": "myplugins.greeter",
              "class_name": "GreeterPlugin", "enabled": true}]}

The Name

The Agora (from ancient Greek ἀγορά agorΓ‘) was the central square of a Greek polis β€” the place where citizens gathered to debate, trade, make decisions, and hold each other accountable. Socrates held philosophy there. Democracy was practiced there. It was not a temple (hierarchy) or a palace (command) but a shared environment that the community co-inhabited.

This project is named after that idea: a shared persistent space where agents coordinate through the environment, not through point-to-point commands. The concept is inspired by blackboard architecture research (PatchBoard, LbMAS, BIGMAS).


References

  • reference/META.md β€” vision, name origin, research backing

  • reference/ARCHITECTURE.md β€” full design document, startup/shutdown sequence

  • reference/DECISIONS.md β€” implementation decisions log

  • reference/CONVENTIONS.md β€” capability vocabulary, manifest standards

  • reference/NNN-*.md β€” per-plugin design documents

  • PatchBoard (arXiv:2605.29313) β€” environment-mediated communication outperforms peer-to-peer

  • LbMAS (arXiv:2507.01701) β€” blackboard architecture for LLM multi-agent systems

  • BIGMAS (arXiv:2603.15371) β€” "agents don't talk to each other β€” they all write to and read from a single shared workspace"

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

–Maintainers
–Response time
–Release cycle
–Releases (12mo)
Commit activity

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

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/CopperEagle/agora'

If you have feedback or need assistance with the MCP directory API, please join our Discord server