Orchestra MCP Server
Official# Orchestra MCP Server
A Model Context Protocol (MCP) server for the [Orchestra API](https://docs.getorchestra.io/api/introduction). End users can connect directly to Orchestra's hosted MCP endpoint and authenticate with their Orchestra API key.
## Quick Start
Use Orchestra's hosted MCP endpoint:
- URL: `https://mcp.getorchestra.io/orchestra`
- Required header: `Authorization: Bearer <YOUR_ORCHESTRA_API_KEY>`
- API key location: [Orchestra workspace settings](https://app.getorchestra.io/settings/workspace)
## Available Tools
<!-- The table below is generated by scripts/update_readme.py — do not edit by hand. -->
<!-- available-tools:start -->
| Tool | Auth required | Purpose | Category |
|------|---------------|---------|------|
| `whats_broken` | Yes | Failing and warning pipeline runs in a window, pre-joined to the task runs that failed inside them, with messages, platform links and duration anomalies. | Triage |
| `diagnose` | Yes | Deep dive on one task run: parameters, upstream task statuses, log tail and artifact filenames. | Triage |
| `pipeline_context` | Yes | A pipeline's metadata, full definition, integrations, recent run outcomes and median succeeded duration. | Triage |
| `get_pipeline` | Yes | Fetch a single pipeline. Provide exactly one selector: pipeline_id, alias, or repository together with yaml_path (`GET /pipeline`). | Pipelines |
| `list_pipelines` | Yes | List pipelines (`GET /pipelines`). | Pipelines |
| `create_pipeline` | Yes | Create a pipeline (`POST /pipelines`). | Pipelines |
| `update_pipeline` | Yes | Update a pipeline by selector (`PUT /pipelines`). | Pipelines |
| `delete_pipeline` | Yes | **Disabled by default.** Delete a pipeline. Provide exactly one selector: pipeline_id, alias, or repository together with yaml_path (`DELETE /pipelines`). Set `ORCHESTRA_ENABLE_DELETE` to expose it. | Pipelines |
| `get_pipeline_data` | Yes | Get pipeline data (`GET /pipelines/data`). | Pipelines |
| `start_pipeline` | Yes | Start a pipeline run (`POST /pipelines/{pipeline_id_or_alias}/start`). | Pipelines |
| `pause_pipeline` | Yes | Pause or unpause a pipeline (`PUT /pipelines/{pipeline_id_or_alias}/pause`). | Pipelines |
| `validate_pipeline` | No | Validate pipeline schema (`POST /pipelines/schema`). | Pipelines |
| `migrate_pipeline` | Yes | Migrate an Orchestra-backed pipeline to git-backed storage. Identify it with pipeline_id or alias, and omit working_branch when it equals default_branch (`PATCH /pipelines/storage-settings`). | Pipelines |
| `import_pipeline` | Yes | Import a pipeline (`POST /pipelines/import`). | Pipelines |
| `get_pipeline_run_status` | Yes | Get pipeline run status (`GET /pipeline_runs/{pipeline_run_id}/status`). | Pipeline Runs |
| `list_pipeline_runs` | Yes | List pipeline runs with optional filters. status accepts comma-separated values: CREATED, RUNNING, SUCCEEDED, WARNING, FAILED, CANCELLING, CANCELLED (`GET /pipeline_runs`). | Pipeline Runs |
| `cancel_pipeline_run` | Yes | Cancel a running pipeline run by its ID (`POST /pipeline_runs/{pipeline_run_id}/cancel`). | Pipeline Runs |
| `get_pipeline_run_lineage_url` | No | Build the URL of a pipeline run's lineage graph in the Orchestra UI (derived from `ORCHESTRA_ENV`). | Pipeline Runs |
| `list_task_runs_for_pipeline_run` | Yes | List task runs for a pipeline run (`GET /pipeline_runs/{pipeline_run_id}/task_runs`). | Task Runs |
| `list_task_runs` | Yes | List task runs (`GET /task_runs`). | Task Runs |
| `list_incidents` | Yes | List incidents (`GET /incidents`). | Incidents |
| `get_incident` | Yes | Get an incident (`GET /incidents/{incident_id}`). | Incidents |
| `update_incident` | Yes | Update an incident (`PATCH /incidents/{incident_id}`). | Incidents |
| `list_incident_events` | Yes | List an incident's timeline (`GET /incidents/{incident_id}/events`). | Incidents |
| `get_incident_external_event` | Yes | Get an external event's payload (`GET /incidents/{incident_id}/events/{incident_event_id}/external_event`). | Incidents |
| `merge_incidents` | Yes | Merge incidents (`POST /incidents/{incident_id}/merge`). | Incidents |
| `unmerge_incidents` | Yes | Unmerge incidents (`POST /incidents/{incident_id}/unmerge`). | Incidents |
| `mute_incident` | Yes | Mute an incident (`POST /incidents/{incident_id}/mute`). | Incidents |
| `unmute_incident` | Yes | Unmute an incident (`POST /incidents/{incident_id}/unmute`). | Incidents |
| `create_incident_comment` | Yes | Write to an incident's timeline (`POST /incidents/{incident_id}/comments`). | Incidents |
| `list_operations` | Yes | List operations (`GET /operations`). | Operations |
| `list_assets` | Yes | List assets (`GET /assets`). | Assets |
| `get_asset_by_id` | Yes | Get an asset (`GET /assets/{asset_id}`). | Assets |
| `list_task_run_logs` | Yes | List task run logs (`GET /pipeline_runs/{pipeline_run_id}/task_runs/{task_run_id}/logs`). | Logs |
| `download_task_run_log` | Yes | Download a task run log (`GET /pipeline_runs/{pipeline_run_id}/task_runs/{task_run_id}/logs/download`). | Logs |
| `list_task_run_artifacts` | Yes | List task run artifacts (`GET /pipeline_runs/{pipeline_run_id}/task_runs/{task_run_id}/artifacts`). | Artifacts |
| `download_task_run_artifact` | Yes | Download a task run artifact (`GET /pipeline_runs/{pipeline_run_id}/task_runs/{task_run_id}/artifacts/download`). | Artifacts |
| `list_integration_connections` | Yes | List integration connections (`GET /integrations/connections`). | Integrations |
| `list_environments` | Yes | List environments (`GET /environments`). | Environments |
| `create_environment` | Yes | Create an environment (`POST /environments`). | Environments |
| `get_environment` | Yes | Get an environment (`GET /environments/{environment_id}`). | Environments |
| `update_environment` | Yes | Update an environment (`PATCH /environments/{environment_id}`). | Environments |
| `delete_environment` | Yes | **Disabled by default.** Delete an environment (`DELETE /environments/{environment_id}`). Set `ORCHESTRA_ENABLE_DELETE` to expose it. | Environments |
| `get_integration_state_for_state_aware` | Yes | Get integration state (`GET /state/{integration}`). | State |
| `list_accounts` | Yes | List workspaces (`GET /accounts`). | Accounts |
| `list_agent_avatars` | Yes | List agent avatars (`GET /agents/avatar-catalog`). | Agents |
| `create_agent` | Yes | Create an agent (`POST /agents`). | Agents |
| `list_agents` | Yes | List agents (`GET /agents`). | Agents |
| `get_agent` | Yes | Get an agent (`GET /agents/{agent_id}`). | Agents |
| `update_agent` | Yes | Update an agent (`PATCH /agents/{agent_id}`). | Agents |
| `delete_agent` | Yes | **Disabled by default.** Delete an agent (`DELETE /agents/{agent_id}`). Set `ORCHESTRA_ENABLE_DELETE` to expose it. | Agents |
| `list_agent_skills` | Yes | List an agent's skills (`GET /agents/{agent_id}/skills`). | Agents |
| `set_agent_skills` | Yes | Set an agent's skills (`PUT /agents/{agent_id}/skills`). | Agents |
| `list_agent_integrations` | Yes | List an agent's integrations (`GET /agents/{agent_id}/integrations`). | Agents |
| `set_agent_integrations` | Yes | Set an agent's integrations (`PUT /agents/{agent_id}/integrations`). | Agents |
| `get_agent_usage` | Yes | Get agent token usage (`GET /usage`). | Agents |
| `list_agent_sessions` | Yes | List agent sessions (`GET /sessions`). | Agent Sessions |
| `create_agent_session` | Yes | Start an agent session (`POST /sessions`). | Agent Sessions |
| `get_agent_session` | Yes | Get an agent session (`GET /sessions/{session_id}`). | Agent Sessions |
| `send_agent_session_message` | Yes | Send a message to an agent session (`POST /sessions/{session_id}`). | Agent Sessions |
| `get_agent_session_history_messages` | Yes | Read an agent session's messages (`GET /sessions/{session_id}/history/messages`). | Agent Sessions |
| `cancel_agent_session_prompt` | Yes | Cancel an agent session prompt (`POST /sessions/{session_id}/cancel`). | Agent Sessions |
| `list_skills` | Yes | List skills (`GET /skills`). | Skills |
| `create_skill` | Yes | Create a skill (`POST /skills`). | Skills |
| `get_skill` | Yes | Get a skill (`GET /skills/{skill_id}`). | Skills |
| `update_skill` | Yes | Update a skill (`PATCH /skills/{skill_id}`). | Skills |
| `delete_skill` | Yes | **Disabled by default.** Delete a skill (`DELETE /skills/{skill_id}`). Set `ORCHESTRA_ENABLE_DELETE` to expose it. | Skills |
| `import_skill` | Yes | Import a skill from git (`POST /skills/import`). | Skills |
| `list_audit_events` | Yes | List audit events (`GET /audit_events`). | Audit |
| `get_incident_settings` | Yes | Get incident settings (`GET /incident_settings`). | Incident settings |
| `update_incident_settings` | Yes | Update incident settings (`PATCH /incident_settings`). | Incident settings |
| `list_monitors` | Yes | List monitors (`GET /monitors`). | Monitors |
| `create_monitor` | Yes | Create a monitor (`POST /monitors`). | Monitors |
| `reorder_monitors` | Yes | Reorder monitors (`POST /monitors/reorder`). | Monitors |
| `get_monitor` | Yes | Get a monitor (`GET /monitors/{monitor_id}`). | Monitors |
| `update_monitor` | Yes | Update a monitor (`PUT /monitors/{monitor_id}`). | Monitors |
| `delete_monitor` | Yes | **Disabled by default.** Delete a monitor (`DELETE /monitors/{monitor_id}`). Set `ORCHESTRA_ENABLE_DELETE` to expose it. | Monitors |
<!-- available-tools:end -->
### Cursor
Add this to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
```json
{
"mcpServers": {
"orchestra": {
"url": "https://mcp.getorchestra.io/orchestra",
"headers": {
"Authorization": "Bearer <YOUR_ORCHESTRA_API_KEY>"
}
}
}
}
```
### Claude Code
Add the hosted server with:
```bash
claude mcp add --transport http --header "Authorization: Bearer <YOUR_ORCHESTRA_API_KEY>" orchestra https://mcp.getorchestra.io/orchestra
```
### Other MCP clients
Any MCP client that supports remote HTTP/SSE servers can connect with this shape:
```json
{
"mcpServers": {
"orchestra": {
"url": "https://mcp.getorchestra.io/orchestra",
"headers": {
"Authorization": "Bearer <YOUR_ORCHESTRA_API_KEY>"
}
}
}
}
```
## Managing Multiple Workspaces
If you need to connect to multiple Orchestra workspaces, you can set up separate MCP server connections with workspace-specific API keys:
```json
{
"mcpServers": {
"orchestra-data-quality-tests": {
"url": "https://mcp.getorchestra.io/orchestra",
"headers": {
"Authorization": "Bearer <DATA_QUALITY_WORKSPACE_API_KEY>"
}
},
"orchestra-sales-integrations": {
"url": "https://mcp.getorchestra.io/orchestra",
"headers": {
"Authorization": "Bearer <SALES_WORKSPACE_API_KEY>"
}
}
}
}
```
## Run Locally
This section discusses how to run the MCP server locally, and is mainly intended for contributors.
### Prerequisites
- Python 3.11 or higher
- [uv](https://github.com/astral-sh/uv) package manager
- Orchestra API key
### Install dependencies
```bash
# Install uv if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install project dependencies
uv sync
```
### Set API key
```bash
export ORCHESTRA_API_KEY="your-orchestra-api-key"
```
### (Optional) Select environment for local runs
For local development, the server defaults to `app`. You can override it with `ORCHESTRA_ENV`:
```bash
export ORCHESTRA_ENV="dev"
```
Valid values:
- `app` (default)
- `stage`
- `dev`
### (Optional) Enable destructive deletion
By default, the destructive `delete_pipeline` and `delete_environment` tools are not registered to avoid accidental destructive actions.
To expose them, set `ORCHESTRA_ENABLE_DELETE` before starting the server:
```bash
export ORCHESTRA_ENABLE_DELETE="true"
```
Only the following values are recognized:
- `"true"`
- `"TRUE"`
- `"1"`
### Run the server
```bash
python -m orchestramcp.server
```
The server fetches every Orchestra OpenAPI spec on startup and exposes the operations
each API flags for the MCP. Set `ORCHESTRA_OPENAPI_URL` (Orchestra API),
`ORCHESTRA_PLATFORM_OPENAPI_URL` (Orchestra Platform API) or
`ORCHESTRA_AGENTS_OPENAPI_URL` (Orchestra Agents API) to point at a specific spec
(e.g. a local file) instead of the environment default. Startup fails if any spec
cannot be fetched, rather than serving a partial set of tools.
### (Optional) Accept OAuth access tokens
The hosted Lambda can also accept OAuth 2.1 access tokens issued by Orchestra's
authorization server, alongside API keys. Set all three variables to turn it on —
with any of them unset, every bearer token is treated as an API key:
```bash
export ORCHESTRA_OAUTH_ISSUER="https://app.getorchestra.io"
export ORCHESTRA_OAUTH_JWKS_URI="https://app.getorchestra.io/oauth/jwks.json"
export ORCHESTRA_OAUTH_RESOURCE_URL="https://mcp.getorchestra.io/orchestra"
```
The issuer is per-environment — swap `app` for `stage` or `dev` — and the rest of its
metadata, `jwks_uri` included, is published at
`<issuer>/.well-known/oauth-authorization-server`.
`ORCHESTRA_OAUTH_RESOURCE_URL` must be the MCP URL exactly as a user types it into their
client, path included: it is published as the `resource` of the
[RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) metadata document, and it is the
audience every token is checked against. The authorization server has to serve that same
identifier — it mints a token only for a resource it is configured for, and stamps `aud`
with its own spelling of it. Verified tokens are forwarded to the Orchestra API unchanged.
### Testing the OAuth flow locally
`scripts/local_resource_server.py` fronts the real Lambda handler with an HTTP socket,
so an MCP client can run the whole flow against the production code path:
```bash
ORCHESTRA_ENV=dev \
ORCHESTRA_OAUTH_ISSUER=https://dev.getorchestra.io \
ORCHESTRA_OAUTH_JWKS_URI=https://dev.getorchestra.io/oauth/jwks.json \
ORCHESTRA_OAUTH_RESOURCE_URL=https://mcp-dev.getorchestra.io/orchestra \
uv run python scripts/local_resource_server.py
```
```bash
claude mcp add --transport http orchestra-local http://127.0.0.1:8788/orchestra
```
Connecting should take you through discovery, client registration and a consent screen,
after which tool calls run as your user rather than as an account-wide API key.
The resource URL is the deployed dev identifier rather than the local address, because it
has to be one the authorization server serves *and* one the Orchestra API accepts as an
audience — a token minted for `127.0.0.1` is refused by both. The cost is that the
metadata pointer in the 401 names the deployed host, so a client that follows it lands
somewhere this server is not; discovery then depends on the client falling back to
probing the address it connected to. Testing against a deployed environment avoids that,
and is also the only way to exercise the edge routing `/orchestra/.well-known/*`.
## Development
- Run `uv run pytest` to run tests.
- Run `uv run ruff check .` and `uv run ruff format .` to check and format code.
PRs to main will trigger CI checks in GitHub, `main` branch merges release to the dev & stage environments, and Github releases relase to the production environment.
TDQS
Scored across 49 tools
Most tools have clearly distinct purposes, but the pipeline retrieval trio (get_pipeline, get_pipeline_data, pipeline_context) and the task-run list variants (list_task_runs vs list_task_runs_for_pipeline_run) create some overlap. Descriptions help differentiate, but an agent could still misselect among them.
Predominantly consistent verb_noun pattern (get_, list_, create_, update_, etc.), with a few outliers like whats_broken, pipeline_context, and diagnose that break the pattern. Overall the naming is readable and mostly predictable.
49 tools is well above the recommended 3-15 range and is heavy even for a broad orchestration platform. Many tools could be consolidated or omitted, and the sheer count risks overwhelming an agent and increasing selection errors.
Covers core CRUD and lifecycle operations for pipelines, incidents, monitors, environments, and task runs, but lacks delete operations for several resources (e.g., delete_pipeline, delete_monitor, delete_environment) and manual incident creation. Agents can work around most gaps.