Orchestrator MCP
Use Codex from Claude Code, or Claude Code from Codex, through the subscriptions you already have.
If you pay for more than one AI subscription, Orchestrator MCP helps you use each model for what it does best. It runs the installed Codex or Claude Code CLI under your existing login, routes the work automatically, prevents an agent from consulting itself, and keeps a record of every consultation.
No provider API key is required for this setup. Authentication stays inside the Codex and Claude Code command-line apps on your computer. Orchestrator only checks whether they are signed in — it never reads, stores, or returns a credential. The optional direct routing path described later is the only part that talks to a provider endpoint, and therefore the only part that needs a key.
With Orchestrator MCP, your agent can:
- ask another vendor's agent for coding, research, writing, reasoning, or review help;
- continue a consultation across several turns;
- choose the best configured agent automatically;
- review consultation history in a local dashboard.
Quick start
1. Install with Homebrew
brew tap crAK1644/tap
brew install orchestrator-mcp-server
Apple Silicon uses a prebuilt package. Intel macOS and Linux build the dependencies from source, which can take about 15 minutes. On those systems, the uvx setup shown below is usually faster and does not install the server permanently.
2. Sign in to the agent CLIs
Sign in to each agent you want Orchestrator to use:
codex login
claude auth login
These commands use the normal Codex and Claude Code account login. Orchestrator only checks whether the CLI is signed in; it does not read or store the login credentials.
3. Create a configuration file
Create config.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 }
The full annotated example is in config.example.yaml.
4. Add it to your MCP client
For Claude Code:
claude mcp add orchestrator \
--env ORCHESTRATOR_CONFIG=$PWD/config.yaml \
--env ORCHESTRATOR_HOST_RUNTIME=claude \
-- orchestrator-mcp-server
For Codex, add this to ~/.codex/config.toml:
[mcp_servers.orchestrator]
command = "orchestrator-mcp-server"
env = { ORCHESTRATOR_CONFIG = "/absolute/path/to/config.yaml", ORCHESTRATOR_HOST_RUNTIME = "codex" }
Restart your MCP client after changing its configuration.
Install with uvx instead
You can run the same server without installing it permanently:
claude mcp add orchestrator \
--env ORCHESTRATOR_CONFIG=$PWD/config.yaml \
--env ORCHESTRATOR_HOST_RUNTIME=claude \
-- uvx orchestrator-mcp-server
For Codex:
[mcp_servers.orchestrator]
command = "uvx"
args = ["orchestrator-mcp-server"]
env = { ORCHESTRATOR_CONFIG = "/absolute/path/to/config.yaml", ORCHESTRATOR_HOST_RUNTIME = "codex" }
The project is published to PyPI as orchestrator-mcp-server. The shorter PyPI name belongs to another project.
What it does
Orchestrator has two independent paths. They can be used together or one at a time.
| Path | Talks to | Needs an API key | Tools |
|---|---|---|---|
| Consultation | The Codex or Claude Code CLI on your computer, under its own login | No | consult, list_consult_agents, get_consultation |
| Direct routing | A model endpoint through LiteLLM | Yes, unless the endpoint is local and unauthenticated | ask, list_capabilities |
The consultation path is the reason this project exists. The direct routing path is older and optional. Configure the consult section only, and the two ask tools are never registered.
The consultation path
The consult tool starts the Codex or Claude Code command-line app already installed and signed in on your computer. Claude Code can ask Codex, and Codex can ask Claude Code, using the subscriptions already connected to those CLIs.
The consulted agent can answer, but it cannot change files, run commands, use MCP tools, or start subagents. Orchestrator also removes agents that use the same runtime as the caller, which prevents consultation loops.
ORCHESTRATOR_HOST_RUNTIME tells Orchestrator which agent is making the request. It must be claude or codex. The server excludes that runtime from the available targets, so a host can never consult itself. The value comes from the environment only; a calling model cannot set it as a tool argument.
consult
What you send:
| Field | Required | Meaning |
|---|---|---|
capability |
yes | One of coding, research, writing, reasoning, review. |
prompt |
yes | The task or question. Up to 100,000 characters. |
context |
no | Source material. Up to 1,000,000 characters. Treated as evidence to read, never as instructions to follow. |
source_mode |
no | auto, document, web, or model. Defaults to auto. |
consultation_id |
no | Omit on the first call. Send the returned ID back on later calls to continue the same session. |
target_agent |
no | An explicit agent ID, which overrides the scores. The tool advertises the configured IDs as a fixed list, so a calling model cannot name an agent you did not configure. |
conversation_label |
no | A free-text label saved with the consultation. Up to 200 characters. |
Source modes:
| Mode | What the consulted agent gets |
|---|---|
auto |
Picks document when context is set, and model otherwise. Resolved before the request is sent; a target never receives auto. |
document |
Your context, with every tool switched off. The agent is instructed to answer from that material or say it cannot. |
web |
The target CLI's own web search, and nothing else. Bounded by web_turn_limit, which defaults to 8 assistant turns. |
model |
Neither. The agent answers from what it already knows. |
What you get back. Every outcome uses the same envelope:
| Field | Meaning |
|---|---|
ok |
False exactly when error is set. A failed consultation never carries answer text, so check this before reading content. |
consultation_id |
The handle for continuing this conversation. Null only when the call failed before a consultation existed. |
content.answer |
The agent's answer. |
content.assumptions |
What it assumed to answer. |
content.uncertainties |
What it was not sure about. |
content.follow_up_questions |
What it would ask you next. |
content.sources |
Title, locator, and type (document, web, or model) for each source it used. |
route |
Which agent answered: ID, runtime, model, capability score, priority, and whether you chose it explicitly. |
usage |
Token counts, when the CLI reports them. |
latency_ms |
Elapsed time. |
error |
A code, a message, the agent involved, and sometimes a required_action command for you to run. |
All five content fields are required of the consulted agent. A list may be empty but never missing: "no uncertainties" is a claim the agent has to make, not something you infer from an absent key.
Consultation error codes: agent_not_installed, connection_required, configured_model_unavailable, agent_unavailable, session_not_found, session_busy, session_target_mismatch, protocol_validation_failed, web_search_unavailable, transport_error, timeout, invalid_request, no_agent_available.
Behaviour worth knowing:
- Routing is predictable: the highest capability score wins, then the lowest priority number, then the agent ID. A score of 0, or a capability left out of the map, means that agent is not eligible.
- If the chosen agent fails, that is the answer. Orchestrator does not quietly try the next one.
- A consultation is pinned to the agent, runtime, and model that started it. Naming a different
target_agentlater returnssession_target_mismatchrather than switching. - If the CLI answers as a different model than the one configured, the consultation fails with
configured_model_unavailableinstead of returning an answer from a model nobody chose. For Codex this is checked against the session log the CLI writes under~/.codex/sessions, becausecodex exec --jsonon 0.146 does not name the model anywhere in its output. If neither source names one, the configured name is reported unverified — absent metadata is not treated as evidence of substitution, so a quiet release of either CLI does not become an outage. - Two processes cannot advance the same consultation at once. The second gets
session_busy. - Nothing is ever run through a shell. Every CLI call is an argument list, and the prompt is written to the process's standard input.
list_consult_agents
Returns the host runtime and one row per configured agent: agent_id, runtime, model, priority, enabled, scores, web_search, excluded_as_host, plus installed, authenticated, and a detail string from the last status check.
get_consultation
Takes a consultation_id and returns the stored consultation: target agent, runtime and model, capability, the source modes used, label, status, whether a native session is still bound, timestamps, every turn, and the routing decision that picked the agent.
Agent options
Each entry under consult.agents accepts:
| Option | Default | Meaning |
|---|---|---|
runtime |
required | codex or claude. |
command |
required | Executable name or absolute path. Resolved on PATH; an absolute path is safest for GUI-launched clients. |
model |
required | The model to ask for, and the one the answer is checked against. |
priority |
100 | Lower wins a tie. |
enabled |
true | Set false to keep an agent configured but out of routing. |
scores |
none | 0–100 per capability. Missing means 0, which means not eligible. |
web_search |
false | Allows source_mode: web for this agent. Asking for web against an agent without it returns web_search_unavailable rather than quietly answering without a search. |
reasoning_effort |
unset | low, medium, high, xhigh, or max. Codex only; setting it on a claude agent refuses to start, because that runtime would ignore it silently. |
Consultation settings
| Setting | Default | Meaning |
|---|---|---|
database_path |
~/.orchestrator-mcp/consultations.sqlite3 |
Where consultations are stored. |
managed_agents_path |
~/.orchestrator-mcp/agents.yaml |
The file the dashboard writes. |
timeout_s |
180 | Time limit for one consultation turn. Raise it for slow, high-effort reviews. |
web_turn_limit |
8 | Assistant turns allowed in web mode before the child process is stopped. |
store_full_content |
true | Set false to store only metadata and routing information. |
dashboard |
off | See below. |
The direct routing path
ask and list_capabilities route a request to a model deployment through LiteLLM. This is separate from the consultation path and, for any hosted provider, it is the part that needs an API key.
The ask tool routes a named capability, such as coding or research, to the deployment assigned to it. LiteLLM handles load balancing, retries, cooldowns, and fallbacks. A deployment can be a local model or another endpoint already configured for LiteLLM.
What you send:
| Field | Required | Meaning |
|---|---|---|
capability |
yes | One of your configured capability names, advertised as a fixed list. |
prompt |
yes | The task or question. |
context |
no | Source material. When set, the model is told to answer only from it and to say so otherwise. |
system |
no | Extra instructions placed before the conversation. Orchestrator's own instructions are always applied last, so they cannot be turned off from here. |
response_schema |
no | A JSON Schema, as an object or as a JSON string. The reply is validated against it locally. |
temperature |
0.2 | Forced to 0 whenever response_schema is set. |
max_output_tokens |
from limits |
Per-request override. |
What you get back:
| Field | Meaning |
|---|---|
ok |
False exactly when error is set. A failed call carries neither content nor data. |
content |
The answer text. Null when response_schema was used. |
data |
The validated structured answer, when response_schema was used. |
insufficient_context |
True when the model said the provided context did not answer the question. |
model_used |
Which deployment answered. |
fallback_used |
Whether a fallback capability handled it. |
finish_reason, usage, latency_ms |
Stop reason, tokens, elapsed time. |
error |
A code and a message. |
Routing error codes: invalid_request, no_deployment, upstream_error, rate_limited, context_exceeded, schema_validation_failed, timeout, content_filtered, auth_failed, output_truncated.
list_capabilities returns each configured capability with its description, the deployments behind it, and the capabilities it falls back to.
You can leave capabilities and model_list out of config.yaml if you only want subscription-based consultations. In that setup, Orchestrator only shows the three consultation tools.
Dashboard and consultation history
The dashboard is off by default because it can display every stored consultation.
Enable it in config.yaml:
consult:
dashboard:
enabled: true
editable: false
Start it separately:
ORCHESTRATOR_CONFIG=config.yaml orchestrator-mcp-dashboard
Open http://127.0.0.1:8765. The dashboard only listens on your computer. It shows configured agents, recent consultations, prompts, answers, routing, usage, latency, and errors. host accepts only a loopback address, and a request whose Host header names anything else is refused.
The dashboard runs as its own process and reads the configuration once, at startup — the same as the MCP server. Neither one notices a change the other made. It never runs a login command; the connect commands on the page are text for you to copy.
Editing agents from the browser
Set editable: true and open /agents. This is a second flag on purpose: turning the dashboard on gets you a window, and editing is a different thing to agree to.
What the form can change: consult agents only — runtime, command, model, priority, enabled, the five capability scores, web search, and reasoning effort. Nothing else is editable from the browser. capabilities, model_list, router_settings, limits, timeout_s, web_turn_limit, store_full_content, and the dashboard's own host and port are config-file settings.
Agents you add here are written to ~/.orchestrator-mcp/agents.yaml, with 0600 permissions in a 0700 directory. The dashboard never writes config.yaml. Agents defined there are listed on the page but not editable, and the page says so.
The two files are merged at startup and neither one wins. An agent ID defined in both files stops the server from starting, and the error names the ID and both paths. There is no precedence rule, because a precedence rule is how you get an edit that saves and then does nothing. The form refuses a save that would create that state, and it checks config.yaml as it is on disk rather than as it was when the dashboard started — so an agent you add to that file by hand is refused here right away, and one you delete from it stops being refused. Only the IDs are re-read, so it works in one direction: an agent added to config.yaml while the page is open does not appear in the read-only table until the dashboard restarts, but it is still enough to block a save the next startup would reject.
If config.yaml is empty or half-written when the page reads it — the state an editor leaves for a moment while saving — the check falls back to the agents the dashboard started with. Reading that moment as "this file defines no agents" is exactly how a duplicate would slip through.
An agent that exists in both files keeps its row and its delete button, since deleting the copy here is the only fix available from the browser.
Changes take effect when the MCP server next starts. The page says so after every save, and it warns you when the running server is on an older configuration than the one on disk.
By default, consultation prompts and answers are saved in SQLite. Set store_full_content: false to save only metadata and routing information.
Configuration
ORCHESTRATOR_CONFIG points to the configuration file. If it is not set, the server looks for config.yaml in its working directory.
Important sections:
| Section | Purpose |
|---|---|
consult |
Configures logged-in CLI agents, history, and the dashboard. |
capabilities |
Names and explains the work types available to ask. |
model_list |
Connects each capability to one or more LiteLLM models. |
router_settings |
Controls retries, cooldowns, and fallbacks. |
limits |
Sets request size, output, repair, and timeout limits for ask. Consultations have their own caps and their own timeout_s. |
Several deployments may use the same capability name. LiteLLM will balance requests between them.
You may configure only ask, only consult, or both. If consult is missing, the consultation tools are not shown. If capabilities and model_list are both missing, the model tools are not shown.
The default limits block, which applies to ask only:
| Setting | Key | Default |
|---|---|---|
| Prompt | max_prompt_chars |
100,000 characters |
| Context | max_context_chars |
400,000 characters |
| System instructions | max_system_chars |
10,000 characters |
| JSON Schema | max_schema_chars |
20,000 characters |
| Output | max_output_tokens |
4,096 tokens |
| Whole request | request_timeout_s |
120 seconds |
| Schema repair attempts | schema_repair_attempts |
1 |
Invalid configuration is rejected when the server starts instead of failing during a request.
Guardrails and limits
Orchestrator MCP checks request and response structure. It does not know whether a model's factual claims are true.
It does enforce these rules:
- Unknown capabilities and oversized requests are rejected before contacting a provider.
- Structured replies are checked locally against your JSON Schema.
- Truncated, filtered, malformed, and failed answers are returned as errors, not partial answers.
- Errors use stable codes such as
connection_required,timeout, andsession_busy. - The response always identifies the model and whether a fallback was used.
- Consulted agents run in answer-only mode and may not act on your computer.
- A consulted agent cannot route work back to the same agent runtime.
- Login credentials are not read or stored. Authentication stays in the vendor's CLI.
Important limits:
- Treat caller-provided JSON Schemas as trusted input. A complex regular expression can use a large amount of CPU.
- Provider error text is shortened and common secret formats are redacted, but unusual secrets may still appear. Do not forward errors to an untrusted place.
- The consultation database may contain full prompts and answers. Keep it private or disable full-content storage.
System requirements
- macOS or Linux. Windows is not tested; the Homebrew instructions are macOS only, and the server has only been run on POSIX systems.
- Python 3.11, 3.12, or 3.13
- Homebrew or
uv - An MCP client that supports stdio, such as Claude Code or Codex
- The Codex or Claude Code CLI installed and signed in
- For optional direct routing: a model endpoint configured through LiteLLM
Testing
Run the offline test suite:
uv sync
uv run pytest -q
The tests use fake providers and CLI agents. They do not need a network connection or spend money.
To test your real model configuration:
uv run python smoke_live.py
To test real Codex and Claude Code consultations:
ORCHESTRATOR_HOST_RUNTIME=claude uv run python smoke_consult_live.py
The smoke tests make real requests and may use paid capacity from your configured services. Do not run them in CI unless that is intentional.
Troubleshooting
| Problem | What to do |
|---|---|
config not found: config.yaml |
Set ORCHESTRATOR_CONFIG to an absolute path. MCP clients may start the server from a different directory. |
| Startup names a missing capability | Make sure every capability has a deployment and every fallback names a real capability. |
no_deployment |
All deployments are unavailable or cooling down. Check the provider and cooldown_time. |
output_truncated |
Raise max_output_tokens. |
schema_validation_failed |
Simplify the schema or use a model with stronger structured-output support. |
timeout on a local model |
Increase request_timeout_s; model loading is included in the time limit. |
consult is missing |
Add the consult section and check that the client loaded the correct config file. |
| Host runtime error at startup | Set ORCHESTRATOR_HOST_RUNTIME to claude or codex in the MCP client's environment. |
agent_not_installed |
Use an absolute path in the agent's command; GUI apps may have a smaller PATH than your shell. |
connection_required |
Run the login command shown in the error in your own terminal, then try again. |
| Every consultation starts over | Return the previous consultation_id with the next call. |
| A dashboard change does not appear | Restart the MCP server; it reads configuration at startup. |
Bug reports
Open an issue and include the returned response envelope. Remove paths, credentials, and other private information before attaching your configuration.
For routing or retry problems, a LiteLLM debug log is useful:
LITELLM_LOG=DEBUG uv run python smoke_live.py 2>debug.log
Contributing
Issues and pull requests are welcome.
- Fork the repository and create a branch.
- Make the change and add a test that fails without it.
- Run
uv run pytest -q. - Open a pull request.
Keep private configuration, login data, and consultation databases out of commits.
Releasing
- Update
versioninpyproject.toml. - Create a GitHub Release tagged
vX.Y.Z. - The release workflow runs the tests, checks the version, and publishes to PyPI using Trusted Publishing.
- Update the formula in the
homebrew-taprepository and rebuild its Apple Silicon bottle.
Not included
Deliberately out of scope for now:
- Consulted agents cannot act. No file changes, no commands, no MCP tools, no subagents. Answers only.
- No streaming. A consultation returns one complete envelope.
- No consultations from the dashboard. The page reads history and edits agent configuration; it has never started an agent process, and that is worth keeping until there is a reason to give it up.
- No runtime settings in the dashboard. Timeouts, storage, and the dashboard's own host and port are config-file settings. A page that can change the port it is served on is a footgun that deserves its own design.
- No accounts on the dashboard. Its protection is a loopback bind, a
Hostheader check, and a per-process token — enough for one person on one machine, not for a shared host. - No automatic restart. Both processes read the configuration once, at startup.
- No multi-user or shared state. One SQLite file, local to your computer.
- No semantic intent routing, streaming tool results, automatic PII removal, or shared Redis state on the routing path. LiteLLM can provide some of these through its own configuration and callbacks.
License and support
This project uses the MIT License.
Built with LiteLLM, Pydantic, and the Python MCP SDK.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file orchestrator_mcp_server-0.3.0.tar.gz.
File metadata
- Download URL: orchestrator_mcp_server-0.3.0.tar.gz
- Upload date:
- Size: 290.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c68737395e9e48e1293c2c9ede47504b503ee290a2ae3da03f1bef274671b7d6
|
|
| MD5 |
558e108b93145ed48fc5bbf32d6477a8
|
|
| BLAKE2b-256 |
2423a721c50feb93ea3bd8528d618beefa6fc90822479515329c92910bcd82ca
|
File details
Details for the file orchestrator_mcp_server-0.3.0-py3-none-any.whl.
File metadata
- Download URL: orchestrator_mcp_server-0.3.0-py3-none-any.whl
- Upload date:
- Size: 89.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2d676e178f9e85c82fe4ad43c3d3f7718234c0f9bdd79e61dc74533a161437f
|
|
| MD5 |
f9f7d3a662fa23b2f5dd5ef953ab236a
|
|
| BLAKE2b-256 |
f76a40137f16a5eb4ac67997a82369f5ca7f837d463c2aad5f4f7ad2865b17f9
|