Skip to main content
Glama
jmlago

genswarms-fleet-mcp

by jmlago
README.md
# genswarms-fleet-mcp

**MCP server over a fleet of GenSwarms swarms**: connect any MCP harness
(Claude Code, claude.ai, Cursor, ...) and observe, configure, debug and —
opt-in — operate your swarms. One connector, the whole fleet.

```
Claude Code / claude.ai / Cursor
        │ MCP (stdio)
        ▼
 genswarms-fleet-mcp  (thin adapter — holds NO authority)
        ├──▶ dashboard read API   (responses pre-redacted per config_schema)
        └──▶ engine REST API      (op gate + scoped tokens server-side)
```

This is the outbound twin of
[genswarms-mcp-gateway](https://github.com/jmlago/genswarms-mcp-gateway)
(which lets caged agents consume external MCP servers). Direction here:
the world consuming the swarms.

## Quickstart (Claude Code)

```bash
# 1. fleet config (token fields are env var NAMES, never secrets)
mkdir -p ~/.config/genswarms
curl -o ~/.config/genswarms/fleet.json \
  https://raw.githubusercontent.com/jmlago/genswarms-fleet-mcp/main/fleet.json.example
$EDITOR ~/.config/genswarms/fleet.json

# 2. register the server (npx builds it straight from GitHub via `prepare`)
claude mcp add genswarms-fleet --scope user \
  -e FLEET_MCP_CONFIG=$HOME/.config/genswarms/fleet.json \
  -e BITPRIME_DASH_TOKEN=... \
  -- npx -y github:jmlago/genswarms-fleet-mcp
```

Restart/`/mcp` once and ask: *"why is bitprime slow?"* →
`get_events(bitprime, level=error)` → *"which agent is stuck?"* →
`get_agent_history(...)` → *"bump the proxy budget"* →
`patch_object_config(...)` → audited in the overlay.

From a checkout instead: `npm install && npm test`, then point the harness
at `node /path/to/genswarms-fleet-mcp/dist/index.js` with the same env.

## Tools, in four tiers

Tiers exist per swarm, derived from what its fleet entry provides; tools are
only REGISTERED when at least one swarm qualifies.

**Tier 1 — observe** (`dashboard_url`): `list_swarms`, `get_dashboard`, `get_events`,
`get_session_history`, `get_session_logs`, `get_config`. Read-only; config
responses arrive already redacted against each package's `config_schema` —
secrets structurally cannot appear here.

**Tier 2 — configure** (`engine_url`): `patch_object_config`, `get_overlay`.
Works with the **narrow `GENSWARMS_CONFIG_API_TOKEN`** — the engine's
config_schema op gate is the authority (only `x-mutable` keys, host-escape
keys always rejected, 422 relayed verbatim). Every change lands in the
overlay audit trail; a leaked config token's blast radius is the tuning
surface each package declared.

**Agent debugging** (prefer `dashboard_url`; legacy fallback to `engine_url`):
`list_agents`, `get_agent_history`, `get_agent_logs`. The missing surface
when diagnosing WHY an agent misbehaves. Current dashboards expose these
as token-gated read routes. A fleet without a dashboard endpoint can use its engine token only when
`read_only` is false. Dashboard HTTP errors never trigger an engine fallback.

**Tier 3 — operate** (`enable_operate: true` in the fleet file, full engine
token): `send_task` (the test/probe surface), `restart_agent`,
`list_objects`, `snapshot` (effective config as `.exs` — commit it to make
runtime mutations permanent). **Off by default; not even registered
without the flag.** The catalog deliberately EXCLUDES create/delete swarm,
add/remove agent/object, scale, `route_message` and `clear_overlay` —
that's operator-CLI territory, never a harness tool.

## Fleet config

`FLEET_MCP_CONFIG` points at a JSON file. Token fields are **env var
NAMES** (the ecosystem's x-secret contract) — secrets never live in the
file; values come from the MCP server process env:

```json
{
  "read_only": true,
  "enable_operate": false,
  "defaults": {
    "dashboard_url": "http://127.0.0.1:4996",
    "dashboard_token_env": "FLEET_DASHBOARD_TOKEN"
  },
  "swarms": {
    "wingston": {
      "dashboard_url": "http://wingston-dashboard:4001",
      "dashboard_token_env": "WINGSTON_DASH_TOKEN",
      "engine_url": "http://wingston-engine:4000",
      "config_token_env": "WINGSTON_CONFIG_TOKEN",
      "operate_token_env": "WINGSTON_OPERATE_TOKEN"
    },
    "bitprime": {
      "dashboard_url": "http://192.168.1.100:4001",
      "dashboard_token_env": "BITPRIME_DASH_TOKEN"
    }
  }
}
```

`read_only: true` removes every configure/operate tool at registration time.
Switching it on in a running fleet also rejects previously registered engine
tools before HTTP dispatch. Restart to add tools after widening permissions.
`defaults` is useful for an embedded fleet: one shared dashboard serves every
co-located swarm, and `list_swarms` discovers projects cast after the MCP process
started without duplicating endpoint configuration.

**Hot-reload**: per-call lookups re-read the file when its mtime changes —
adding a swarm to the fleet needs **no server restart**. (A broken edit
keeps the last good fleet; a live session never goes blind. Tool
REGISTRATION — tier gating, `enable_operate` — deliberately stays
startup-time, so tier-3 tools cannot appear because a file changed under a
live session.)

## Connecting a NEW swarm

On the swarm side you need up to two surfaces:

1. **Dashboard (tier 1)** — the swarm declares a
   [genswarms-dashboard](https://github.com/genlayerlabs/genswarms-dashboard)
   object (`gsp add swarmidx:genlayerlabs/genswarms-dashboard@… --as
   object:dashboard`). Loopback = no token; exposed = set a token and pass
   its env NAME as `dashboard_token_env`.
2. **Engine REST (tiers 2/3 and legacy agent debugging)** — the engine BEAM serves it
   (`Genswarms.Application.start_web_server(port: …)` or `genswarms.up`)
   with `GENSWARMS_API_TOKEN` (full) and `GENSWARMS_CONFIG_API_TOKEN`
   (config-scoped) set on the ENGINE side.

Then add the entry to `fleet.json` — live, thanks to hot-reload. If the
entry reuses already-exported token env vars (or the endpoints are
tokenless loopback), the new swarm is queryable immediately; if it needs
NEW env vars, add them to the server's `env` block and reconnect once.
Verify with `get_dashboard("<name>")`.

The bundled Claude Code skill walks an agent through exactly this — see
below.

## Claude Code skill

[`.claude/skills/genswarms-fleet-use/SKILL.md`](.claude/skills/genswarms-fleet-use/SKILL.md)
teaches a Claude Code session when and how to use the tools (diagnosis
playbook, tier map, and the connect-a-new-swarm procedure). Install it for
yourself with:

```bash
mkdir -p ~/.claude/skills/genswarms-fleet-use
cp .claude/skills/genswarms-fleet-use/SKILL.md ~/.claude/skills/genswarms-fleet-use/
```

## Build & test

```bash
npm install
npm test    # builds + 7 e2e tests speaking REAL MCP stdio against a fake
            # fleet: tier gating, multi-swarm routing, per-tier token
            # pass-through, engine 422 relay, operate-flag registration,
            # fleet.json hot-reload, agent-debug tier
```

## Not a swarmidx package — on purpose

Per the gsp design admission criteria, swarmidx indexes what a swarm
references by content to constitute itself (bodies, policies, handlers,
swarms). This server is an **external client** — exactly the category the
criteria exclude — so it ships via GitHub releases, not the notary.

## Roadmap

- Streamable HTTP transport (claude.ai remote connectors) with its own
  bearer at the front door
- MCP resources for dashboards (subscribe to a swarm's live feed)
- Per-swarm tool visibility (today a tool registers fleet-wide and errors
  per-swarm when the tier is missing)