Skip to main content
Glama
README.md
# @alexgenovese/mcp-fleet-worker

> **Spawn headless OpenCode workers on cheap models** — delegate expensive tool-using tasks to a fleet of parallel workers with isolated git worktrees. Optional **Brick routing** selects the optimal model per task automatically.

[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Node.js](https://img.shields.io/badge/node-%3E%3D18-339933)](package.json)
[![TypeScript](https://img.shields.io/badge/typescript-5.3-3178C6)](#)
[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-FF6B35)](#)
[![Brick Routing](https://img.shields.io/badge/Brick-Smart%20Routing-9B59B6)](https://github.com/regolo-ai/brick-SR1)

---

## MCP Activation Keywords

Le seguenti keyword attivano questo MCP in Claude Code / OpenCode:

| Keyword | Azione |
|---|---|
| `fleet-worker` | Attivazione generica |
| `delegate to GLM` | Usa `regolo/glm5.2-beta` come worker |
| `spawn a GLM worker` | Idem |
| `cheap parallel agent` | Spawn worker su modello economico |
| `grunt worker` | Worker per task ripetitivi |
| `offload to glm` | Delega task a GLM |
| `z.ai worker` | Worker su endpoint z.ai |
| `GLM-5.2 worker` | Worker su glm5.2 |
| `sonnet worker` | Worker Sonnet |
| `haiku worker` | Worker Haiku |
| `cheap coding agent` | Worker economico generico |
| `fan out workers` | Spawn multipli in parallelo |
| `non-Anthropic model in OpenCode` | Worker su modello non-Anthropic |
| `ANTHROPIC_BASE_URL worker` | Worker su endpoint custom |
| `fleet-worker list models` | `fleet_worker_list_models` |
| `fleet-worker spawn` | `fleet_worker_spawn` |
| `fleet-worker collect` | `fleet_worker_collect` |
| `fleet-worker status` | `fleet_worker_status` |
| `fleet-worker cancel` | `fleet_worker_cancel` |

Aggiungi al tuo `.claude/hooks/PreToolUse` o usa direttamente i tool MCP via Claude Code.

---

## What Is This?

`mcp-fleet-worker` is an [MCP server](https://modelcontextprotocol.io) that lets you **spawn headless OpenCode workers** on any model configured in your `opencode.json` — from local Ollama models to cheap remote providers like GLM via z.ai.

Each worker runs in an **isolated git worktree** with its own `CLAUDE_CONFIG_DIR`, so multiple workers run side-by-side without conflicting. Workers are treated as disposable "grunt agents": a powerful orchestrator (Opus) fans out tasks, collects results, and lands the completed branches.

### Why?

- **Cost optimization**: run cheap models (GLM-5.2, Haiku, Sonnet) for routine coding tasks, reserve Opus for orchestration
- **Parallel execution**: spawn N workers simultaneously, each in its own worktree
- **Isolation**: each worker gets a fresh git branch + scratch directory
- **Fleet-ops ready**: designed to pair with [fleet-ops](https://opencode.ai) for test-gated landing

---

## Technology Stack

| Technology | Version | Purpose |
|---|---|---|
| **Node.js** | >= 18 | Runtime |
| **TypeScript** | 5.3 | Language |
| **@modelcontextprotocol/sdk** | ^1.0.0 | MCP server framework |
| **zod** | ^3.23.0 | Runtime schema validation |
| **git worktree** | system | Worker isolation |
| **OpenCode** | any | Spawned agent process |
| **Brick** (optional) | SR1 | Smart model routing via Regolo API |

---

## Architecture

```
                    ┌─────────────────────────────────┐
                    │       Claude Code (Opus)         │
                    │    (orchestrator / caller)        │
                    └──────────┬──────────────────────┘
                               │ MCP (stdio)
                    ┌──────────▼──────────────────────┐
                    │     mcp-fleet-worker server      │
                    │                                   │
                    │  ┌─ fleet_worker_spawn            │
                    │  ├─ fleet_worker_collect          │
                    │  ├─ fleet_worker_status           │
                    │  ├─ fleet_worker_list_models      │
                    │  └─ fleet_worker_cancel           │
                    └──────────┬──────────────────────┘
                               │
                    ┌──────────▼──────────────────────┐
                    │      Git Worktree (.fleet-work/) │
                    │                                   │
                    │  worker-1/  ← branch `fleet/id1` │
                    │  worker-2/  ← branch `fleet/id2` │
                    │  ...                              │
                    └──────────┬──────────────────────┘
                               │
                    ┌──────────▼──────────────────────┐
                    │   ~/.fleet-worker/scratch/<id>/  │
                    │   (CLAUDE_CONFIG_DIR per worker) │
                    └─────────────────────────────────┘
```

### Flow

1. **Caller** sends a prompt + optional model to `fleet_worker_spawn`
2. **Server** creates an isolated git worktree (`.fleet-work/<id>/`) with a dedicated branch (`fleet/<id>`)
3. **Server** spawns `opencode run -m <model> --agent coder --auto --dir <worktree> --format json "<prompt>"`
4. **Caller** polls `fleet_worker_collect` or waits for completion
5. **Worker** finishes → output is captured → `fleet_worker_cancel` cleans up worktree + scratch dir

---

## Getting Started

### Prerequisites

- Node.js >= 18
- npm or pnpm
- [OpenCode](https://opencode.ai) installed (the `opencode` CLI in PATH)
- A git repository to use as the worker base directory

### Installation

```bash
# Clone
git clone https://github.com/alexgenovese/mcp-fleet-worker.git
cd mcp-fleet-worker

# Install deps
npm install

# Build
npm run build

# Test
node dist/index.js
```

### Configuration

Add to your `opencode.json` (or to your MCP hub config):

```json
{
  "mcpServers": {
    "mcp-fleet-worker": {
      "command": "node",
      "args": ["/path/to/mcp-fleet-worker/dist/index.js"],
      "env": {
        "FLEET_WORKER_BASE_DIR": "/path/to/your/git/repo",
        "FLEET_WORKER_DEFAULT_MODEL": "regolo/glm5.2-beta",
        "FLEET_WORKER_BRICK_URL": "https://api.regolo.ai",
        "FLEET_WORKER_BRICK_API_KEY": "your-regolo-api-key",
        "OPENCODE_PATH": "opencode"
      }
    }
  }
}
```

**Environment Variables:**

| Variable | Default | Description |
|---|---|---|
| `FLEET_WORKER_BASE_DIR` | `process.cwd()` | Git repo for worktree isolation |
| `FLEET_WORKER_DEFAULT_MODEL` | `regolo/gemma4-31b` | Fallback model when routing is off/unavailable |
| `FLEET_WORKER_BRICK_URL` | *(empty)* | Brick API URL (Regolo). When set, enables smart routing |
| `FLEET_WORKER_BRICK_API_KEY` | *(empty)* | API key for Brick (Regolo API key) |
| `OPENCODE_PATH` | `opencode` | Path to opencode binary |
| `XDG_CONFIG_HOME` | *unset for worker* | Stripped so worker uses fresh config |

---

## Brick Smart Routing (Optional)

When `FLEET_WORKER_BRICK_URL` and `FLEET_WORKER_BRICK_API_KEY` are set, `fleet_worker_spawn` **without an explicit `model` parameter** will call Brick's `/v1/routing/test` endpoint to select the optimal model based on:

- **Capability** — 6 dimensions (coding, math_reasoning, creative_synthesis, instruction_following, planning_agentic, world_knowledge)
- **Complexity** — easy / medium / hard
- **Cost** — penalizes expensive models for simple tasks

### How It Works

```
fleet_worker_spawn(prompt="fix typo in README.md")
  ↓
  Brick analyzes prompt → complexity: easy, capability: {instruction_following: 0.9}
  ↓
  Brick selects: ollama/qwen3:4b (cheapest capable model)
  ↓
  Worker spawns on ollama/qwen3:4b
  ↓
  fleet_worker_status shows: routed=true, costSavings=95%
```

### When Routing Kicks In

| Call | Behavior |
|---|---|
| `fleet_worker_spawn(prompt="...", model="regolo/glm5.2-beta")` | Explicit model → **no routing**, use specified model |
| `fleet_worker_spawn(prompt="...")` + Brick configured | **Brick routes** automatically |
| `fleet_worker_spawn(prompt="...")` + Brick not configured | Falls back to `FLEET_WORKER_DEFAULT_MODEL` |

### Cost Savings Tracking

Every routed worker records its `costSavings` percentage (estimated vs. using the fallback model). This appears in:

- `fleet_worker_spawn` response (`routing.costSavings`)
- `fleet_worker_status` output (per-worker `costSavings`)
- `fleet_worker_collect` result (`routing.costSavings`)

See [USE_CASES.md](USE_CASES.md) for detailed cost-saving scenarios for developers and CTOs.

---

## Project Structure

```
mcp-fleet-worker/
├── src/
│   ├── index.ts              # MCP server entry point
│   ├── types.ts              # WorkerSpec, WorkerProcess, WorkerStatus
│   ├── lib/
│   │   ├── models.ts         # opencode.json parser (JSONC-aware)
│   │   ├── router.ts         # Brick routing client (Regolo API)
│   │   ├── worktree.ts       # Git worktree create/remove/cleanup
│   │   ├── config.ts         # Scratch directory (~/.fleet-worker/)
│   │   ├── id.ts             # Short UUID generator (8 chars)
│   │   └── worker-manager.ts # Spawn/track/kill opencode children
│   └── tools/
│       └── register.ts       # 5 MCP tool handlers
├── dist/                     # Compiled JavaScript
├── package.json
├── tsconfig.json
└── README.md
```

---

## Key Features

### 1. Multi-Model Support
Reads all providers and models from `opencode.json` — Ollama, GLM, custom OpenAI-compatible endpoints. Use `fleet_worker_list_models` to see what's available.

### 2. Git Worktree Isolation
Each worker runs in a dedicated git worktree (`git worktree add -b fleet/<id>`), so multiple workers can modify the same repo simultaneously without merge conflicts at runtime.

### 3. Background Execution
Workers spawn as child processes with captured stdout/stderr. Collect results async via `fleet_worker_collect`.

### 4. Cleanup
`fleet_worker_cancel` with `cleanup: true` removes:
- The worktree directory (`.fleet-work/<id>/`)
- The local git branch (`fleet/<id>`)
- The scratch config dir (`~/.fleet-worker/scratch/<id>/`)

### 5. JSONC Parsing
Reads `opencode.json` even with comments (`//` and `/* */`) — no need to strip them manually.

---

## MCP Tools

### `fleet_worker_list_models`
List all available models grouped by provider, with context window and cost info.

### `fleet_worker_spawn`
Spawn a headless worker on any model.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `prompt` | string | *required* | Task prompt for the worker |
| `model` | string | env/FALLBACK | `provider/model-id` format |
| `agent` | string | `coder` | OpenCode agent type |
| `worktree` | string | `FLEET_WORKER_BASE_DIR` | Git repo path |
| `branch` | string | `fleet/<id>` | Git branch name |
| `maxTurns` | number | 50 | Max interaction turns |
| `timeout` | number | 300 | Timeout in seconds |

### `fleet_worker_collect`
Get a worker's result.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `workerId` | string | *required* | Worker ID from spawn |
| `wait` | boolean | false | Block until completion |
| `timeout` | number | 30 | Max wait in seconds |

### `fleet_worker_status`
List all workers with status, model, branch, elapsed time.

### `fleet_worker_cancel`
Stop and clean up a worker.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `workerId` | string | *required* | Worker ID to cancel |
| `cleanup` | boolean | true | Remove worktree + scratch |

---

## Development Workflow

1. **Install** → `npm install`
2. **Build** → `npm run build`
3. **Watch** → `npm run dev` (tsc --watch)
4. **Test locally** → `node dist/index.js` (then connect with MCP inspector or [Claude Desktop](https://claude.ai/download))

To test with the MCP inspector:

```bash
npx @modelcontextprotocol/inspector node dist/index.js
```

---

## Testing

```bash
# Type check
npx tsc --noEmit

# Build
npm run build

# Quick smoke test (list models)
node -e "
import('./dist/index.js').then(() => console.log('Server loads OK'))
"
```

---

## License

MIT © [Alex Geno](https://github.com/alexgenovese)