Metadata-Version: 2.4
Name: orchestrator-mcp-server
Version: 0.4.2
Summary: Use Codex from Claude Code, or Claude Code from Codex, through the subscriptions you already have.
Project-URL: Homepage, https://github.com/crAK1644/orchestrator-mcp
Project-URL: Issues, https://github.com/crAK1644/orchestrator-mcp/issues
Author: Ayberk Karataban
License-Expression: MIT
License-File: LICENSE
Keywords: claude,code-review,codex,llm,mcp,orchestrator
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.11
Requires-Dist: mcp<3,>=2.0
Requires-Dist: pydantic>=2.9
Requires-Dist: pyyaml>=6.0
Description-Content-Type: text/markdown

<h1 align="center">Orchestrator MCP</h1>

<p align="center">
  <strong>one agent. second opinion. same terminal.</strong>
</p>

<p align="center">
  Make Claude Code ask Codex. Make Codex ask Claude Code.<br>
  Use the subscriptions already signed in on your computer.<br>
  <strong>No provider API keys to configure.</strong>
</p>

<p align="center">
  <a href="https://github.com/crAK1644/orchestrator-mcp/stargazers"><img src="https://img.shields.io/github/stars/crAK1644/orchestrator-mcp?style=flat&color=yellow" alt="GitHub stars"></a>
  <a href="https://pypi.org/project/orchestrator-mcp-server/"><img src="https://img.shields.io/pypi/v/orchestrator-mcp-server?style=flat" alt="PyPI version"></a>
  <a href="https://pypi.org/project/orchestrator-mcp-server/"><img src="https://img.shields.io/pypi/pyversions/orchestrator-mcp-server?style=flat" alt="Python versions"></a>
  <a href="https://github.com/crAK1644/orchestrator-mcp/actions/workflows/test.yml"><img src="https://github.com/crAK1644/orchestrator-mcp/actions/workflows/test.yml/badge.svg" alt="Tests"></a>
  <a href="LICENSE"><img src="https://img.shields.io/github/license/crAK1644/orchestrator-mcp?style=flat" alt="MIT License"></a>
</p>

<p align="center">
  <a href="#before--after">See it</a> ·
  <a href="#install">Install</a> ·
  <a href="#what-you-get">What you get</a> ·
  <a href="#reviews-with-a-checkpoint">Reviews</a> ·
  <a href="#security-model">Security</a> ·
  <a href="#local-dashboard">Dashboard</a>
</p>

---

Orchestrator MCP is a local [Model Context Protocol](https://modelcontextprotocol.io) server that lets one coding agent consult another. It launches the Codex, Claude Code, or experimental Antigravity CLI already installed and authenticated on your machine, routes the request, and returns a structured answer.

It does not ask for a provider key, proxy provider traffic, or silently switch models. Authentication remains inside each vendor's CLI.

## Before / After

<table>
<tr>
<th width="50%">Without Orchestrator</th>
<th width="50%">With Orchestrator</th>
</tr>
<tr>
<td valign="top">

1. Copy the prompt, diff, and context.
2. Open another coding agent.
3. Recreate the task and paste everything.
4. Bring the answer back.
5. Repeat when you need a follow-up.

</td>
<td valign="top">

1. Call `orchestrator_consult`.
2. Get the other agent's structured answer.
3. Reuse `consultation_id` for follow-ups.

The conversation stays connected from the same client.

</td>
</tr>
</table>

Same subscriptions. Less context shuffling.

```text
 Claude Code host  ──►  Orchestrator MCP  ──►  Codex CLI
 Codex host        ──►  Orchestrator MCP  ──►  Claude Code CLI
 Any host          ──►  Orchestrator MCP  ──►  Antigravity CLI (experimental)

                         local routing
                    no provider API keys
                   no same-runtime loops
```

The host's own runtime is always excluded. Claude Code cannot consult Claude Code through this server, and Codex cannot consult Codex.

## Install

**Homebrew:**

```bash
brew tap crAK1644/tap
brew install orchestrator-mcp-server
```

Apple Silicon uses a prebuilt package. Intel macOS and Linux build dependencies from source; use the [`uvx` option](#run-with-uvx-instead) if you want a faster, temporary install.

### 1. Sign in to the agent CLIs

Sign in to each agent you want Orchestrator to use:

```bash
codex login
claude auth login
```

These are the normal Codex and Claude Code login flows. Orchestrator checks readiness, but never reads or stores their credentials.

### 2. Create `config.yaml`

```yaml
consult:
  database_path: ~/.orchestrator-mcp/consultations.sqlite3
  timeout_s: 180

  agents:
    codex:
      runtime: codex
      command: codex
      model: gpt-5.6-sol
      priority: 10
      web_search: true
      scores: { coding: 95, research: 90, reasoning: 95, review: 90 }

    claude:
      runtime: claude
      command: claude
      model: claude-opus-4-6
      priority: 10
      web_search: true
      scores: { coding: 90, research: 95, writing: 95, review: 95 }
```

See [`config.example.yaml`](config.example.yaml) for every option and an experimental Antigravity example.

### 3. Add the server to your MCP client

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

<br>

```bash
claude mcp add orchestrator \
  --env ORCHESTRATOR_CONFIG=$PWD/config.yaml \
  --env ORCHESTRATOR_HOST_RUNTIME=claude \
  -- orchestrator-mcp-server
```

</details>

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

<br>

Add this to `~/.codex/config.toml`:

```toml
[mcp_servers.orchestrator]
command = "orchestrator-mcp-server"
env = { ORCHESTRATOR_CONFIG = "/absolute/path/to/config.yaml", ORCHESTRATOR_HOST_RUNTIME = "codex" }
```

</details>

Restart the MCP client after changing its configuration.

> [!TIP]
> Use an absolute `ORCHESTRATOR_CONFIG` path. GUI-launched clients often start in a different working directory and inherit a smaller `PATH` than your terminal.

### Run with `uvx` instead

<details>
<summary><strong>Show the temporary-install configuration</strong></summary>

<br>

No permanent server install is required:

```bash
claude mcp add orchestrator \
  --env ORCHESTRATOR_CONFIG=$PWD/config.yaml \
  --env ORCHESTRATOR_HOST_RUNTIME=claude \
  -- uvx orchestrator-mcp-server
```

For Codex:

```toml
[mcp_servers.orchestrator]
command = "uvx"
args = ["orchestrator-mcp-server"]
env = { ORCHESTRATOR_CONFIG = "/absolute/path/to/config.yaml", ORCHESTRATOR_HOST_RUNTIME = "codex" }
```

The PyPI distribution is named `orchestrator-mcp-server`; the shorter PyPI name belongs to another project.

</details>

## What you get

| Capability | What it does |
|---|---|
| **Second opinion** | Ask another vendor's coding agent about code, research, writing, reasoning, or review. |
| **Connected follow-ups** | Continue the native CLI session by returning its `consultation_id`. |
| **Predictable routing** | Rank configured agents by capability score, priority, then agent ID. |
| **Explicit model choice** | Verify the responding model when the CLI exposes that information; fail on a detected substitution. |
| **Review panel** | Ask one reviewer, or up to five in deep mode, over the same approved material. |
| **Local history** | Store consultations and reviews in SQLite, with an optional loopback dashboard. |
| **Answer-only isolation** | Consulted agents cannot edit files, run commands, use MCP tools, or start subagents. |

### The three consultation tools

| Tool | Purpose |
|---|---|
| `orchestrator_consult` | Start or continue a structured consultation. |
| `orchestrator_list_consult_agents` | Show configured agents, routing scores, installation, and login readiness. |
| `orchestrator_get_consultation` | Retrieve a stored consultation, its turns, usage, and routing decision. |

The review tools are opt-in: without a `consult.review` block, they are not advertised at all.

## How consultation works

`orchestrator_consult` selects the eligible agent with the highest capability score. Lower `priority` wins a score tie; agent ID breaks the final tie. A missing capability or a score of `0` makes an agent ineligible.

The selected CLI runs under its existing login and returns one response envelope:

| Field | Meaning |
|---|---|
| `ok` | False exactly when `error` is set. Check this before reading the answer. |
| `consultation_id` | Handle for continuing the same native conversation. |
| `content` | Answer, assumptions, uncertainties, follow-up questions, and sources. |
| `route` | Agent, runtime, model, score, priority, and whether it was selected explicitly. |
| `usage` | Token counts when the CLI reports them. |
| `latency_ms` | End-to-end elapsed time. |
| `error` | Stable error code, message, agent, and sometimes a command the user must run. |

If the chosen agent fails, Orchestrator returns that failure. It does not quietly fall through to a different model.

### Choose the evidence source

| `source_mode` | What the consulted agent receives |
|---|---|
| `auto` | `document` when context is present; otherwise `model`. |
| `document` | Only the supplied context, with action tools disabled. |
| `web` | The target CLI's own web search. `web_turn_limit` bounds it on Claude; Codex is bounded by `timeout_s` alone. |
| `model` | No context and no web search; answer from model knowledge. |

<details>
<summary><strong>Consult request fields and agent options</strong></summary>

<br>

Request fields:

| Field | Required | Meaning |
|---|---|---|
| `capability` | yes | `coding`, `research`, `writing`, `reasoning`, or `review`. |
| `prompt` | yes | Task or question, up to 100,000 characters. |
| `context` | no | Evidence, up to 1,000,000 characters. |
| `source_mode` | no | `auto`, `document`, `web`, or `model`. |
| `consultation_id` | no | Return the previous ID to continue the conversation. |
| `target_agent` | no | Choose one configured agent instead of automatic routing. |
| `conversation_label` | no | Label stored with the consultation, up to 200 characters. |

Agent configuration:

| Option | Default | Meaning |
|---|---|---|
| `runtime` | required | `codex`, `claude`, or `antigravity`. |
| `command` | required | Executable name or absolute path. |
| `model` | required | Requested model and, where possible, verified responding model. |
| `priority` | `100` | Lower wins a score tie. |
| `enabled` | `true` | Keep the agent configured but out of routing when false. |
| `scores` | none | 0–100 per capability; missing means ineligible. |
| `web_search` | `false` | Permit `source_mode: web` for this agent. |
| `reasoning_effort` | unset | `low`, `medium`, `high`, `xhigh`, or `max`; Codex only. |

</details>

## Reviews, with a checkpoint

A consultation asks one agent. A review asks one or more configured reviewers the same question over the same material.

```text
 plan review          approve + run          synthesize
 sends nothing   ──►  reviewers answer  ──►  host records conclusion
      │                    in parallel                │
      └─ scope              one-time token            └─ every Critical kept
         reviewers
         secret hits
         request count
```

Enable reviews in `config.yaml`:

```yaml
consult:
  review:
    reviewers: [codex]          # standard: exactly one
    deep_reviewers: [codex, claude]  # deep: one to five
```

The workflow is deliberately split:

1. `orchestrator_review` creates a plan and **sends nothing**. The plan shows reviewers, material size, web access, request count, and locations of credential-shaped text.
2. Show that plan to the user. `orchestrator_review_run` spends its one-time token and asks reviewers in parallel.
3. Read every result and call `orchestrator_finalize_review`. Reviewer replies alone leave the review at `awaiting_synthesis`.

Finalization must preserve every machine-readable Critical finding, even when other reviewers disagree with it. Deep mode also requires the host agent to record its own findings before seeing the reviewers' answers.

> [!IMPORTANT]
> Material sent to a reviewer may remain in that vendor CLI's own history. Orchestrator cannot erase Codex, Claude Code, or Antigravity session logs.

<details>
<summary><strong>Review tool reference</strong></summary>

<br>

| Tool | What it does |
|---|---|
| `orchestrator_review` | Plan a review and show what would be sent. Sends nothing. |
| `orchestrator_review_run` | Spend the token and ask reviewers in parallel. |
| `orchestrator_retry_review` | Re-run failed reviewers without discarding successful answers. |
| `orchestrator_finalize_review` | Record the host's synthesis; the only path to `complete`. |
| `orchestrator_cancel_review` | Cancel a review while retaining answers already received. |
| `orchestrator_apply_fixes` | Return selected findings and fix steps. Changes no files. |
| `orchestrator_record_fix_round` | Record the host's claim about a fix round. |
| `orchestrator_test_reviewers` | Check installation and login readiness without sending project material. |
| `orchestrator_get_review` / `orchestrator_list_reviews` | Read one review or recent review metadata. |
| `orchestrator_delete_review` | Delete a review, its rechecks, and linked consultations. |
| `orchestrator_request_delete_all` / `orchestrator_delete_all_reviews` | Preview and confirm deletion of an exact history snapshot. |

Reviews default to `web: false`. Reviewers cannot change files or run commands. `orchestrator_apply_fixes` is a plan for work the host agent performs; it never applies a patch itself.

Credential-shaped values are masked before storage. `secrets="send_as_is"` is an explicit escape hatch for a false positive: it requires the exact original goal and context again, sends those originals to the reviewers, and still stores only the redacted copy.

`store_full_content: false` does not apply here in full. A review's goal and context are stored either way — the second half of the approval handshake reads them back to send what was approved — and reviewer answers and findings are not. That leaves nothing to prove every Critical survived synthesis, so `orchestrator_finalize_review` refuses, and the review stays at `awaiting_synthesis`. Finalization is refused on the same grounds when a reviewer answered only in unparseable prose, or when its findings were truncated.

</details>

## Security model

| Property | Guarantee |
|---|---|
| **Credentials** | No provider key setting exists. Orchestrator never reads, stores, returns, or refreshes a CLI's own credential. A credential you put in a prompt is material, not a credential here — see the warning below. |
| **Process launch** | Commands are executed as argument lists, never through a shell. |
| **Self-consultation** | `ORCHESTRATOR_HOST_RUNTIME` comes from the environment and cannot be overridden by a tool call. |
| **Agent permissions** | Consulted agents are answer-only, except for the target CLI's bounded search in explicit web mode. |
| **Model identity** | A detected mismatch fails with `configured_model_unavailable`. Missing CLI metadata is reported as unverified, not invented. |
| **Storage** | SQLite directory permissions are `0700`; the database and managed agent file are `0600`. |
| **Dashboard** | Loopback only, with host-header checks and a per-process token. |
| **Review approval** | Plans bind the scope to a one-time token before reviewer requests are made. |

> [!WARNING]
> **Redaction covers the review path, and only this database.** In a review, credential-shaped values are replaced before every insert — the goal, the context, the manifest, and every reviewer's answer. A plain `orchestrator_consult` is not redacted: its prompts, context, and answers are stored as you sent them. Detection is best-effort pattern matching rather than a scanner with perfect recall, so a secret with no recognizable shape survives it either way. Keep the database private, or set `store_full_content: false`.
>
> **Vendor history is outside all of this.** Material sent to a reviewer also lands in that reviewer's own CLI history — Codex writes `~/.codex/sessions/`, and the others keep their own logs. Orchestrator cannot redact or erase those files. It does read from them, in two places and for two fields: the Codex adapter opens the rollout file for the session it just ran to recover the model identity the CLI does not otherwise report, and opens the newest rollout to read the rate-limit numbers the service last returned. Nothing else is taken from either file.

Two more limits worth knowing:

- CLI error text is shortened and common secret formats are redacted, but an unusual one may still appear in a returned error. Do not forward a raw error envelope somewhere untrusted.
- A caller-supplied JSON Schema is trusted input. A pathological regular expression in one can consume a large amount of CPU.

Orchestrator checks structure, routing, permissions, and model identity where observable. It cannot prove that a model's factual claims are true.

<details>
<summary><strong>Experimental Antigravity runtime</strong></summary>

<br>

Antigravity (`agy`) uses its own login and OS keyring, but its isolation is weaker than Codex or Claude Code:

- It inherits MCP servers from your `agy` settings. Headless mode denies tools by default, and Orchestrator fails the consultation if a tool step is reported, but this is detection rather than prevention. Do not enable it if you loosened headless permissions.
- It accepts prompts in process arguments rather than standard input. Other users on a shared machine may be able to read those arguments while the process runs.
- It has no login-status command, so readiness is reported as unverified until a real request succeeds or fails.

Large prompts are split across turns because Linux limits one argument to 128 KiB. Gemini models have handled this transport in testing; some non-Gemini models may reject the fragments as prompt injection. `reasoning_effort` and web mode are not available for this runtime.

</details>

## Local dashboard

The optional dashboard shows agents, routing decisions, prompts, answers, usage, latency, errors, reviews, and recorded fix rounds. It is off by default because it can display everything stored in the consultation database.

```yaml
consult:
  dashboard:
    enabled: true
    editable: false
```

Start it separately:

```bash
ORCHESTRATOR_CONFIG=/absolute/path/to/config.yaml orchestrator-mcp-dashboard
```

Open [http://127.0.0.1:8765](http://127.0.0.1:8765).

Set `editable: true` to manage consult agents and reviewer selection in the browser. Browser-managed agents are written to `~/.orchestrator-mcp/agents.yaml`; the dashboard never rewrites `config.yaml`, runs login commands, or starts consultations.

Both the MCP server and dashboard read configuration at startup. Restart them to pick up changes.

## Configuration

`ORCHESTRATOR_CONFIG` points to the YAML file. If unset, the server looks for `config.yaml` in its working directory.

| Setting | Default | Meaning |
|---|---|---|
| `database_path` | `~/.orchestrator-mcp/consultations.sqlite3` | Consultation and review history. |
| `managed_agents_path` | `~/.orchestrator-mcp/agents.yaml` | Agents written by the dashboard. |
| `timeout_s` | `180` | Limit for one consultation turn. |
| `web_turn_limit` | `8` | Assistant turns allowed in web mode. Enforced by the Claude runtime only. |
| `store_full_content` | `true` | Set false to keep metadata and routing only — except a review's goal and context, which are stored either way. Reviews cannot be finalized under it — see below. |
| `review` | absent | Configured reviewers; absent means no review tools. |
| `dashboard` | off | Loopback history UI and optional agent editor. |

`consult` is the only top-level section. Configuration from releases before 0.4 containing `capabilities`, `model_list`, `router_settings`, or `limits` is rejected at startup because direct API routing was removed.

## System requirements

- macOS or Linux. Windows is not currently tested.
- Python 3.11, 3.12, or 3.13.
- Homebrew or [`uv`](https://docs.astral.sh/uv/).
- A stdio MCP client such as Claude Code or Codex.
- At least one other supported agent CLI installed and signed in.

## Test it

The offline suite uses fake CLI agents. It needs no network and spends no model capacity:

```bash
uv sync
uv run pytest -q
```

Live smoke tests use the agents in your configuration:

```bash
ORCHESTRATOR_HOST_RUNTIME=claude uv run python smoke_consult_live.py
ORCHESTRATOR_HOST_RUNTIME=claude uv run python smoke_review_live.py
```

Live tests make real requests and may use paid capacity. Do not run them in CI unless that is intentional.

## Troubleshooting

| Problem | Fix |
|---|---|
| `config not found: config.yaml` | Set `ORCHESTRATOR_CONFIG` to an absolute path. |
| `no_agent_available` | Give an enabled, non-host agent a positive score for the requested capability. |
| `agent_not_installed` | Use an absolute path for `command`; GUI apps often inherit a smaller `PATH`. |
| `connection_required` | Run the login command returned in `required_action`, then retry. |
| Host runtime error | Set `ORCHESTRATOR_HOST_RUNTIME` to `claude`, `codex`, or `antigravity`. |
| Every consultation starts over | Return the previous `consultation_id` on the next call. |
| `timeout` during a review | Raise `consult.timeout_s`; high-effort review can take much longer than 180 seconds. |
| Dashboard changes do not appear | Restart the MCP server; configuration is loaded at startup. |
| Startup names a removed block | Delete pre-0.4 direct-routing keys: `capabilities`, `model_list`, `router_settings`, and `limits`. |

## Deliberately not included

- No direct provider API routing or provider API-key configuration.
- No file edits, shell commands, MCP tools, or subagents for consulted agents.
- No automatic fixes; the host agent owns edits and tests.
- No streaming; each consultation returns one complete envelope.
- No dashboard-initiated consultations.
- No automatic configuration reload.
- No multi-user or shared state.
- No account system for the loopback dashboard.

## Contributing

Issues and pull requests are welcome.

1. Fork the repository and create a branch.
2. Make the change and add a test that fails without it.
3. Run `uv run pytest -q`.
4. Open a pull request.

Keep private configuration, login data, and consultation databases out of commits. For bugs, [open an issue](https://github.com/crAK1644/orchestrator-mcp/issues) with the response envelope after removing paths, credentials, and other private information.

## License

[MIT](LICENSE) · [PyPI](https://pypi.org/project/orchestrator-mcp-server/) · [GitHub issues](https://github.com/crAK1644/orchestrator-mcp/issues)

Built with [Pydantic](https://docs.pydantic.dev) and the [Python MCP SDK](https://github.com/modelcontextprotocol/python-sdk).
