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

[![skills.sh](https://skills.sh/b/agent-runbooks/throng-mcp)](https://skills.sh/agent-runbooks/throng-mcp/throng)

An MCP server that lets your agent hand work to another one. Any MCP client can call it, and the task goes to any supported harness.

- **Another model's view.** A review by another vendor's model, a design critique, a cheaper model for a mechanical pass.
- **Second opinions.** Hand one agent's result to another: Codex reviews, Claude fixes, Codex checks again, each in its own long-lived session.
- **Real sessions, not one-shots.** The nested agent keeps running; a follow-up goes to the same agent with everything it already knows.
- **Background work.** Several agents run at once while you carry on.
- **Answers by schema.** Pass a JSON Schema and the result comes back as data that fits it, not prose to parse: a list of findings, a verdict, a plan, ready to feed into the next step.

Supported harnesses: Claude Code, Codex, OpenCode (and every model it can reach), Gemini CLI, Cursor CLI, GitHub Copilot CLI. Setup for each is under [Install](#2-the-agents-to-run).

Any other ACP agent can be described in the config as a custom harness: its command, and the mode or options that make it auto-approve or ask. throng installs nothing for it and guesses nothing; see [Custom harnesses](docs/configuration.md#custom-harnesses).

## Example

You're in Claude Code and have just changed the payment flow.

> Ask Codex to find a way this payment flow could charge someone twice. Don't change any files.

Claude calls `run_thronglet` with `agent: "codex/gpt-6-sol:high"`, a self-contained prompt, the repository path and a `schema`, so the findings come back as data: file, line, steps to reproduce.

> Give the findings to Opus. Have it fix each one, add a test for it and run the tests.

A second `run_thronglet`, with `agent: "claude/opus"`. Codex's findings go into its prompt; Opus edits the code and runs the tests.

> Now show the same Codex session what changed. Can it still make a customer pay twice?

That is `send_message` into the first session: Codex still has its findings in context and checks the fixes against them instead of starting over.

Any of these calls takes `background: true`: it returns at once and `wait_thronglet` collects the result later, which is how several agents work while you carry on.

## Install

Three parts, in order: the server, the agents it may run, and the client it is called from. The skill at the end is optional.

<details>
<summary><strong>Claude Code: the plugin, server and skill in one go</strong></summary>

The plugin covers parts 1, 3 and 4; the agents from part 2 you still install yourself.

```bash
claude plugin marketplace add agent-runbooks/throng-mcp
claude plugin install throng@throng-mcp
```

</details>

### 1. The server

The npm package [`throng-mcp`](https://www.npmjs.com/package/throng-mcp). Needs node `^22.13 || >=24`. Tested with node 24.11.1, claude 2.1.282, codex 0.156.1, opencode 1.18.30.

Two ways to run it:

- `npx -y throng-mcp` straight in the client config, below. Nothing to install: npx fetches the package on the first start and caches it. The package has no dependencies, so that is one tarball and nothing else.
- A global install, then the command is `throng-mcp`. Starts faster than going through npx.

  ```bash
  npm i -g throng-mcp
  ```

### 2. The agents to run

Each agent needs its own CLI installed and logged in. throng talks to agents over ACP (Agent Client Protocol); Claude Code and Codex need an ACP adapter, and throng ships none. Install only the agents you want to delegate to.

<details>
<summary><strong>Claude Code</strong></summary>

```bash
npm i -g @agentclientprotocol/claude-agent-acp
claude auth status
```

throng passes the path of the `claude` it finds on PATH to the adapter (`CLAUDE_CODE_EXECUTABLE`), so the nested agent runs your installed, logged-in CLI. If `claude auth status` says not logged in, put `CLAUDE_CODE_OAUTH_TOKEN` into the config, see [docs/configuration.md](docs/configuration.md#auth).

</details>

<details>
<summary><strong>Codex</strong></summary>

```bash
npm i -g @agentclientprotocol/codex-acp
codex login
```

The adapter gets the path of your `codex` the same way (`CODEX_PATH`).

</details>

<details>
<summary><strong>OpenCode</strong></summary>

OpenCode speaks ACP itself (`opencode acp`), so there is no adapter. Install it per https://opencode.ai/docs, then:

```bash
opencode auth login
```

Custom providers live in your `~/.config/opencode/opencode.json`; throng doesn't touch it.

</details>

<details>
<summary><strong>Gemini CLI</strong></summary>

Gemini CLI speaks ACP itself (`gemini --acp`), so there is no adapter.

```bash
npm i -g @google/gemini-cli
gemini
```

Gemini CLI needs a Gemini API key ([Google AI Studio](https://aistudio.google.com/apikey)) or Vertex: run `gemini` once, choose "Use Gemini API Key" and paste the key, or set `GEMINI_API_KEY`. The nested agent uses that key. Signing in with a personal Google account no longer works for Gemini CLI. Its limits under throng: [docs/harnesses.md](docs/harnesses.md#gemini-cli).

</details>

<details>
<summary><strong>Cursor CLI</strong></summary>

Cursor CLI speaks ACP itself (`cursor-agent acp`), so there is no adapter.

```bash
curl https://cursor.com/install -fsS | bash
agent login
```

throng finds `cursor-agent` on PATH and authenticates with the login `agent login` saved; without that login every run fails with `harness_unavailable` and Cursor's message asking to run `agent login`. Its limits under throng: [docs/harnesses.md](docs/harnesses.md#cursor-cli).

</details>

<details>
<summary><strong>GitHub Copilot CLI</strong></summary>

Copilot CLI speaks ACP itself (`copilot --acp`), so there is no adapter.

```bash
npm i -g @github/copilot
copilot login
```

throng finds `copilot` on PATH; the nested agent uses the login `copilot login` saved, or the GitHub CLI's. With a login from the GitHub CLI only, Copilot offers no model choice and every run fails with `model_rejected`; run `copilot login`. Its limits under throng: [docs/harnesses.md](docs/harnesses.md#github-copilot-cli).

</details>

### 3. The MCP client

The client is the session that calls throng. It may be the same program as one of the agents above, or a different one. The server speaks stdio and the command is `npx -y throng-mcp`, or `throng-mcp` after a global install.

<details>
<summary><strong>Claude Code</strong></summary>

```bash
claude mcp add --scope user throng -- npx -y throng-mcp
```

`--scope user` registers it for every project; without it, for the current project only. `claude mcp list` shows the result.

</details>

<details>
<summary><strong>Codex</strong></summary>

```bash
codex mcp add throng -- npx -y throng-mcp
```

</details>

<details>
<summary><strong>OpenCode</strong></summary>

In `~/.config/opencode/opencode.json`:

```json
{
  "mcp": {
    "throng": {
      "type": "local",
      "command": ["npx", "-y", "throng-mcp"],
      "enabled": true
    }
  }
}
```

</details>

Any other MCP client: register a stdio server with that command.

### 4. The skill, optional

[`skills/throng`](skills/throng/SKILL.md) tells the calling agent how to use the server: when to delegate, how to write the prompt, background runs, follow-ups, structured output, what a refused permission means. It is a file for the agent and does not install the server.

<details>
<summary><strong>skills CLI: Claude Code, Codex, opencode and others</strong></summary>

```bash
npx skills add agent-runbooks/throng-mcp --skill throng -g -a claude-code -y
```

`-g` installs into the agent's user directory, for every project; without it, into the current project. `-a codex` or `-a opencode` for the other agents. `npx skills update` pulls later changes.

</details>

<details>
<summary><strong>By hand</strong></summary>

Copy `skills/throng` into your agent's skills directory: `~/.claude/skills`, `~/.codex/skills`, `~/.config/opencode/skills`. A global install also leaves it under the installed package, `$(npm root -g)/throng-mcp/skills/throng`.

</details>

Agents pick skills up at session start, so open a new session after installing.

### Check

In a new session, ask the agent to call `list_harnesses`. It starts each installed adapter without a prompt (seconds, no tokens) and lists the installed harnesses with their models and effort levels; `unavailable` holds the ones that are configured or installed but broken, with the reason. A built-in harness you haven't installed is not listed, and `run_thronglet` on it says what to install. If a run then fails during the handshake, the usual cause is auth: `claude auth status`, `codex login`, `opencode auth login`, `gemini` (set the API key once), `agent login` (Cursor), `copilot login` (Copilot). More in [troubleshooting](docs/configuration.md#troubleshooting).

Before the first run, two things to know. The agent edits the directory you name, with your user's rights; throng adds no isolation and rolls nothing back. Give it only trees you would let an agent edit unattended, and give parallel writers a worktree each.

## Using throng

`agent` names harness, model and effort in one string, `<harness>/<model>[:<effort>]`:

```
claude/opus:max
codex/gpt-6-sol:xhigh
opencode/openrouter/z-ai/glm-5.3-flash
gemini/gemini-2.5-pro
cursor/gpt-5.4-nano
copilot/auto
```

The model is one of the harness's own values; `list_harnesses` has the current list. Effort is `low | medium | high | xhigh | max`; omitted means the harness default.

A call:

```json
{
  "agent": "codex/gpt-6-sol:high",
  "prompt": "In this repository, add a --verbose flag to the CLI in src/cli.ts and a test for it. Run pnpm test. Leave the changes uncommitted and end with the list of files you changed.",
  "cwd": "/work/my-app",
  "description": "add --verbose flag"
}
```

The prompt is self-contained: the nested session sees nothing of the calling conversation. The call returns the agent's final message, a `session_id` for follow-ups, and why the turn stopped.

| tool | what it does |
|---|---|
| `run_thronglet` | starts a session and runs the first turn |
| `send_message` | runs the next turn in an existing session; `steer: true` interrupts the running turn instead of waiting for it |
| `wait_thronglet` | collects the result of a turn started with `background: true` |
| `list_thronglets` | the sessions and what each is doing |
| `cancel_thronglet` | stops a session's running turn |
| `list_harnesses` | the installed harnesses, their models and effort levels |

`background: true` on `run_thronglet` or `send_message` returns as soon as the turn is running; `wait_thronglet` collects the result later, which is how several agents run at once. `schema` asks for structured output: the agent ends with a JSON value matching the JSON Schema, and the result comes back as `structured` instead of `text`.

A failure is an MCP tool error with `code`, `message` and, when the session exists, `session_id` and the partial `text`. Every field, error code and stop reason of every tool is in [DESIGN §3](docs/DESIGN.md#3-external-contract).

## Permissions and safety

What a nested agent may do comes from the config file, never from a tool parameter, so the calling model cannot grant itself more than you allowed. Optional `~/.config/throng/config.yaml`:

```yaml
harness_mode: auto        # auto | ask
permission_answers: deny  # auto | allow | deny | elicit
```

- `harness_mode` is the mode the harness runs in: `auto` (the default) is its own auto-approve mode (Claude `auto`, Codex `agent`, OpenCode as configured, Gemini `yolo`, Cursor `--force`, Copilot `allow_all`), `ask` its asking mode.
- `permission_answers` is how throng answers what the harness still asks: `deny` (the default) refuses, `allow` allows once, `elicit` shows you a dialog in the MCP client (needs elicitation support; Claude Code has it), `auto` shows the dialog when the client can and refuses otherwise.
- `harness_mode: auto` with `permission_answers: allow` is effectively bypass.
- Both settings govern what a harness asks about. Some harnesses act without asking: Cursor CLI applies file edits on its own in either mode ([docs/harnesses.md](docs/harnesses.md#cursor-cli)).
- The older `permissions: auto | allow_all | deny_all | elicit` still works as a shorthand for a pair (auto + deny, ask + allow, ask + deny, ask + elicit).

throng never answers "always allow", so no rule gets written into the agent's project settings. Per-harness overrides, extra env for an adapter, timeouts and the nesting limit are in [docs/configuration.md](docs/configuration.md).

## Documentation

- [docs/configuration.md](docs/configuration.md): the config file, environment variables, permissions in detail, auth, troubleshooting.
- [docs/harnesses.md](docs/harnesses.md): how each harness behaves under throng, its limits and quirks.
- [DESIGN §3](docs/DESIGN.md#3-external-contract): the exact inputs, results, error codes and stop reasons of every tool.
- [skills/throng/SKILL.md](skills/throng/SKILL.md): working patterns for the calling agent.
- [docs/development.md](docs/development.md): for maintainers; tests, smoke runs against real harnesses, files on disk.
- [Agent Runbooks](https://github.com/agent-runbooks/skills): multi-step procedures a session runs through subagents. Any step of a runbook can go to any harness through throng.

## License

MIT, see [LICENSE](LICENSE).

TDQS

A4.3/5.0

Scored across 6 tools

Disambiguation5/5

Each tool maps to a clear, distinct operation: running a new agent, waiting on a session, sending a message to an existing session, cancelling, and two distinct listing tools (harnesses vs sessions). The run/send/wait/cancel lifecycle boundaries are well delineated.

Naming Consistency4/5

Five of six tools follow a consistent verb_noun pattern (run_thronglet, wait_thronglet, cancel_thronglet, list_harnesses, list_thronglets). send_message breaks the suffix convention but is still clear and consistent in its verb-first style.

Tool Count5/5

Six tools tightly cover the agent-session lifecycle (run, wait, message, cancel) plus discovery (list harnesses, list sessions). No bloat, no thin spots.

Completeness5/5

The full session lifecycle is covered: launch, observe, continue/steer, cancel, and both discovery surfaces. Background collection via wait_thronglet rounds out async workflows, leaving no obvious dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues