Skip to main content
Glama
README.md
# ensemble-mcp

Let a strong AI **master** (Claude Code) hand coding work to cheaper **sub-agents** over [MCP](https://modelcontextprotocol.io). Each sub-agent can only change the paths its role owns, so several can work in parallel without colliding.

- Zero dependencies (Python 3.9+, stdlib only)
- Any OpenAI-compatible model: DeepSeek (default), OpenAI, OpenRouter, Ollama
- Roles, prompts and models live in one `ensemble.json`
- Ownership is enforced, including through symlinks. Secrets and `.git` are never touched

## How it works

```
┌──────────────────────────────┐
│ MASTER  (Claude Code)        │  plans, defines contracts, reviews, runs tests
└──────────────┬───────────────┘
               │ MCP: delegate_task / delegate_parallel
┌──────────────▼───────────────┐
│ ensemble-mcp                 │  builds prompt, calls model, checks paths
└───────┬──────────┬───────────┘
        │          │          │
┌───────▼───┐ ┌────▼──────┐ ┌─▼─────────┐
│ backend   │ │ web       │ │ tests     │   sub-agents (cheap model)
│ src/      │ │ web/      │ │ tests/    │   each limited to its own paths
└───────────┘ └───────────┘ └───────────┘
```

The master owns the shared interfaces (contracts) that no sub-agent may change, and reviews everything that comes back.

## Quick start

```bash
pip install git+https://github.com/VeeraPD/ensemble-mcp
cd your-project
ensemble-mcp init                  # creates ensemble.json
export DEEPSEEK_API_KEY=sk-...     # or put it in ./.env (git-ignored)
ensemble-mcp check                 # verifies config, key and prompts
claude mcp add ensemble -- ensemble-mcp -c "$PWD/ensemble.json" serve
```

Copy [templates/CLAUDE.md](templates/CLAUDE.md) into your project so the master knows the workflow, then ask Claude to build something.

## Multi-level agents

For large projects, add a middle level: one **area lead** per part of the codebase. Each area lead is a Claude Code subagent with its own `ensemble.json` and MCP server.

```
┌────────────────────────────────────────────┐
│ LEVEL 1  MASTER                            │  cross-area contracts, final review
└───────────────┬────────────────┬───────────┘
                │                │
      ┌─────────▼──────┐  ┌──────▼─────────┐
      │ LEVEL 2        │  │ LEVEL 2        │
      │ mobile lead    │  │ web lead       │  Claude Code subagents,
      │ (subagent)     │  │ (subagent)     │  one per area
      └───┬───────┬────┘  └───┬────────┬───┘
          │       │           │        │
       ┌──▼──┐ ┌──▼───┐    ┌──▼──┐  ┌──▼───┐
       │ ui  │ │store │    │ api │  │front │   LEVEL 3  workers
       └─────┘ └──────┘    └─────┘  └──────┘   (cheap models, owned paths)
```

```bash
claude mcp add ensemble-mobile -- ensemble-mcp -c /abs/mobile/ensemble.json serve
claude mcp add ensemble-web    -- ensemble-mcp -c /abs/web/ensemble.json serve
```

Rules:
- Cross-area contracts live in a folder only the master edits.
- Owned paths must not overlap between configs.
- Each area lead reports a short summary upward; the master does not re-read every file.
- Add this level only when one lead can no longer hold the whole codebase. Otherwise the single-level setup above is simpler and cheaper.

## Configuration

`ensemble.json`, paths relative to the file:

```json
{
  "provider": {
    "base_url": "https://api.deepseek.com",
    "api_key_env": "DEEPSEEK_API_KEY",
    "models": ["deepseek-chat", "deepseek-reasoner"],
    "default_model": "deepseek-chat"
  },
  "shared_context": ["docs/CONVENTIONS.md"],
  "roles": {
    "backend": { "owns": ["src/"], "instructions_file": "docs/agents/backend.md" },
    "tests":   { "owns": ["tests/"], "instructions": "Test through public APIs only." }
  }
}
```

| Key | Meaning |
|---|---|
| `provider.base_url` | Any OpenAI-compatible base URL. Ollama: `http://localhost:11434/v1` with `"api_key_env": ""` |
| `provider.models`, `default_model` | Allowed models. Use the default first, a stronger one only for steps that fail |
| `shared_context` | Files added to every role's prompt (coding rules) |
| `roles.<name>.owns` | Files or folders the role may change |
| `roles.<name>.instructions_file` / `instructions` | The role's prompt |
| `max_parallel` | Cap for parallel tasks (default 5) |

Optional: `roles.<name>.context`, `max_context_bytes`, `provider.timeout`, `provider.max_retries`, `provider.max_tokens`, `audit_log`.

## MCP tools

| Tool | Purpose |
|---|---|
| `delegate_task` | One role, one task: `role`, `task`, `context_files`, `model`, `dry_run` |
| `delegate_parallel` | Several tasks with distinct roles, run concurrently |
| `list_roles` | Roles, owned paths, models |

A good task states the goal, the exact file paths, the interfaces to match and how to check the result, and passes the relevant contracts as `context_files`. Each call returns the files changed and any paths that were rejected for being outside the role.

## CLI

```
ensemble-mcp init [dir]               create ensemble.json
ensemble-mcp check                    validate setup
ensemble-mcp run ROLE "task" -f file  run one task without MCP (--dry-run to preview)
ensemble-mcp serve                    MCP server on stdio (default)
```

## Safety

Model output is untrusted. A file is only saved if its path is inside the project, not a secret or `.git`, and under the role's `owns` (checked before and after following symlinks). Nothing the model returns is ever executed. Review every diff, and note that context files are sent to your model provider. See [SECURITY.md](SECURITY.md) and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).

## Development

```bash
PYTHONPATH=src python3 -m unittest discover -s tests
```

[MIT](LICENSE). Not affiliated with Anthropic or DeepSeek.

Maintenance

ActivityMaintained
ResponsivenessNo issues