Skip to main content
Glama
README.md
# agent-mesh-mcp

MCP server for a distributed AI agent mesh. Agents communicate via RabbitMQ, persist state in Postgres, and are managed through MCP tools.

Part of the **Agent Mesh** ecosystem:

| Repo | What | Language |
|------|------|----------|
| **[agent-mesh-mcp](https://github.com/openbiocure/agent-mesh-mcp)** | MCP server — tools, events, webhooks, channels | JavaScript |
| **[agent-mesh-worker](https://github.com/openbiocure/agent-mesh-worker)** | Worker daemon — warm sessions, scheduling, RabbitMQ | Python |
| **[agent-mesh-lib](https://github.com/openbiocure/agent-mesh-lib)** | Shared library — identity, guardrails, memory, hooks | Python |
| **[agent-mesh-hooks](https://github.com/openbiocure/agent-mesh-hooks)** | CLI hooks for Claude Code — auto-inject prompts + memory | Python |
| **[agent-mesh-memory](https://github.com/openbiocure/agent-mesh-memory)** | Temporal knowledge graph memory (Graphiti + FalkorDB) | Python |

## Architecture

![Architecture](docs/architecture.png)

[PlantUML source](docs/architecture.puml) — regenerate with `plantuml docs/architecture.puml`

## Agent Addressing

Agents use a `slug@repo:scope` address format:

```
devops-engineer                        # unique role, no repo needed
js-engineer@obc-connectors             # same role, different repos
js-engineer@obc-platform
architect@obc-datalake:landing         # same role + repo, different scopes
architect@obc-datalake:warehouse
```

Each address produces a **deterministic UUID** (v5) — identical in Python and JS. No DB lookup needed to know an agent's ID.

## Transports

- **stdio** (`index.mjs`) — for local Claude Code / Cursor
- **HTTP** (`server.mjs`) — remote access via Streamable HTTP + Keycloak OAuth

## Install

```bash
npm install -g agent-mesh-mcp
```

## Tools

### Agents

| Tool | Description |
|------|-------------|
| `create_agent` | Create agent with slug, prompt, model, config |
| `update_agent` | Update prompt (bumps version), model, config |
| `get_agent` | Full details: prompt, guardrails, online workers |
| `list_agents` | All registered + online agents |
| `ask_agent` | Send a message to another agent via RabbitMQ |
| `get_task_result` | Poll async task result |
| `cancel_agent` | Cancel a running task |
| `tail_agent` | Live activity feed from a running agent |

### Guardrails

Pre-execution tool call validation. Three layers (fast to slow):

1. **regex** — pattern match on tool input (microseconds)
2. **validator** — Guardrails AI (DetectPII, ToxicLanguage, etc.)
3. **llm_judge** — LLM evaluates criteria before allowing

| Tool | Description |
|------|-------------|
| `create_guardrail` | Create a guardrail rule (global or per-agent) |
| `list_guardrails` | List all rules, filter by agent |
| `delete_guardrail` | Remove a rule |

```
# Block git push to prod for all agents
create_guardrail(tool="Bash", pattern="git push.*prod", action="block")

# PII detection on all tool inputs for a specific agent
create_guardrail(tool="*", type="validator", agent="js-engineer@obc-connectors",
                 config={"validator":"DetectPII","params":["EMAIL_ADDRESS"]})
```

### Scheduling

| Tool | Description |
|------|-------------|
| `create_schedule` | Cron or interval schedule for an agent |
| `list_schedules` | All schedules with run history |
| `delete_schedule` | Remove a schedule |
| `run_schedule` | Fire a schedule immediately |

### Incidents

| Tool | Description |
|------|-------------|
| `create_incident` | Track a P1/P2/P3 incident |
| `list_incidents` | Filter by severity/status |
| `update_incident` | Change status, assign agent, link PR |
| `resolve_incident` | Mark as resolved |

### Releases

| Tool | Description |
|------|-------------|
| `create_release` | Track a deployment with steps + rollback |
| `list_releases` | All releases by status |
| `get_release` | Full release details |
| `update_release` | Update status (with transition validation) |
| `close_release` | Mark deployed/rolled back |

### Approvals

| Tool | Description |
|------|-------------|
| `request_approval` | Request human approval (notifies via Telegram) |
| `get_approval` | Check approval status |
| `list_approvals` | All pending/approved/rejected |
| `approve` / `reject` | Respond to an approval |
| `comment_approval` | Add context to an approval |

### Other

| Tool | Description |
|------|-------------|
| `create_feature` | Track a feature request |
| `list_features` | All features by status |
| `update_feature` | Change status/priority |
| `report_mesh_bug` | Report a bug in the mesh itself |
| `list_mesh_bugs` | All mesh bugs |
| `update_mesh_bug` | Fix/close a mesh bug |
| `search_conversations` | Search agent conversation history |
| `notify` | Send a notification via Telegram |
| `get_setting` / `set_setting` / `list_settings` | Mesh configuration |

## Events

Domain events are emitted on state changes and published to RabbitMQ:

```
incident.created → notifies Telegram (P1/P2), publishes to event.incident.created
release.status_changed → notifies subscribers, triggers deploy queue
approval.requested → sends Telegram buttons, publishes event
feature.created → publishes event
mesh.shutdown → all workers stop consuming
mesh.resume → workers restart
```

Workers can bind to event topics in their `worker.json`:

```json
{
  "event_topics": ["event.incident.*", "event.release.*"]
}
```

## Kill Switch

```
POST /mesh/shutdown   → MESH_ENABLED=false, pauses schedules, broadcasts event
POST /mesh/resume     → MESH_ENABLED=true, resumes schedules
GET  /mesh/status     → current state
```

Also available via Telegram: `/shutdown`, `/resume`, `/meshstatus`

## Status Transitions

YAML-based validation stored in `mesh_settings`. Prevents invalid state changes:

```yaml
incident:
  open: [investigating]
  investigating: [fix_submitted, resolved]
  fix_submitted: [resolved, investigating]

release:
  ready: [approved, cancelled]
  approved: [deploying]
  deploying: [deployed, rolled_back]
```

## Worker (agent-mesh-worker)

The worker is a separate Python package: [agent-mesh-worker](https://github.com/openbiocure/agent-mesh-worker)

**worker.json:**

```json
{
  "slug": "js-engineer",
  "cwd": "/path/to/repo",
  "scope": "hotfix",
  "prompt_file": ".claude/agents/js-engineer.md"
}
```

Repo is derived from `basename(cwd)`. Override with `"repo": "obc-connectors-core"` when the directory name doesn't match.

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `RABBITMQ_URL` | `amqp://guest:guest@localhost:5672/` | RabbitMQ connection |
| `RABBITMQ_MGMT_URL` | `http://localhost:15672` | RabbitMQ management API |
| `EXCHANGE_NAME` | `agents` | RabbitMQ exchange |
| `AGENT_NAME` | `unknown` | Calling agent's address (set by worker) |
| `AGENT_ID` | — | Calling agent's UUID (set by worker) |
| `WORKER_ID` | — | Worker instance UUID (set by worker) |
| `ASK_TIMEOUT` | `900` | Reply timeout in seconds |
| `MCP_PORT` | `3100` | HTTP server port |
| `KEYCLOAK_URL` | `http://localhost:8080` | Keycloak base URL |
| `KEYCLOAK_REALM` | `master` | Keycloak realm |
| `KEYCLOAK_CLIENT_ID` | `agent-mesh` | OAuth client ID |
| `KEYCLOAK_CLIENT_SECRET` | — | OAuth client secret |
| `DATABASE_URL` | — | Postgres connection (Prisma) |

## License

MIT