mcp-media-orchestrator
# MCP Media Orchestrator
A [FastMCP](https://github.com/jlowin/fastmcp) server that fronts several independent
image, video and audio generation backends behind **one capability-oriented tool
surface**.
An MCP client asks for "a 10 second video, at most 1080p". It does not ask for
Replicate, or Runway, or a specific model id. Routing, safety screening, job
lifecycle, concurrency limits and retention all live server-side, which is what makes
this an orchestration platform rather than a collection of adapters.
The design rationale is in [ARCHITECTURE.md](ARCHITECTURE.md). That document is the
point of this repository; the code is the proof it holds up.
## Quickstart
No credentials required. A deterministic in-process backend ships with the server, so
the full job lifecycle runs without a network call to any provider. FastMCP itself
performs a version check against PyPI on startup, which is its behaviour and not
this server's.
```bash
uv venv && uv pip install -e ".[dev]"
uv run pytest -q # full lifecycle, routing, safety and timeout coverage
uv run mcp-media-orchestrator
```
Register it with an MCP client:
```json
{
"mcpServers": {
"media-orchestrator": {
"command": "uv",
"args": ["--directory", "/path/to/mcp-media-orchestrator", "run",
"mcp-media-orchestrator"]
}
}
}
```
## Tools
| Tool | Purpose |
|---|---|
| `list_capabilities` | Every model reachable through this server, with cost, latency and ceilings |
| `generate_media` | Start a generation. Returns a `job_id` immediately, never blocks |
| `get_job` | Poll a job until `state` is terminal |
| `cancel_job` | Best effort provider-side, always terminal locally |
| `list_jobs` | Recent jobs, newest first, optionally filtered by state |
There is deliberately no `replicate_generate` or `elevenlabs_generate`. A client that
knows which provider to call is holding the platform's topology in its context, and
every provider change becomes a client change.
## Adding a backend
Implement four methods and register the adapter. Nothing else in the system changes.
```python
class MyBackend:
name = "mybackend"
def capabilities(self) -> Sequence[Capability]: ...
async def submit(self, request, capability) -> str: ...
async def poll(self, provider_ref) -> BackendStatus: ...
async def cancel(self, provider_ref) -> None: ...
```
Backends publish capabilities synchronously so routing never blocks on a provider,
and they are submit/poll rather than await-to-completion because every serious media
generation API is already asynchronous underneath.
## Configuration
Environment variables, prefix `MEDIA_ORCH_`. See [.env.example](.env.example).
Credentials are read from the environment and never accepted as tool arguments. Tool
arguments are authored by a language model and travel through the client's context,
so a tool that takes an API key has already published it to the transcript.
## Status
Reference implementation. Job state is in-process, which is the first thing to
replace for a production deployment. See the scaling notes at the end of
[ARCHITECTURE.md](ARCHITECTURE.md).
MIT licensed.
TDQS
Scored across 5 tools
Each tool maps to a distinct part of the media generation workflow: capability discovery, job submission, status polling, cancellation, and history listing. There is no meaningful overlap or risk of selecting the wrong tool for a given operation.
All tool names follow a consistent verb_noun pattern using snake_case: list_capabilities, generate_media, get_job, cancel_job, list_jobs. The verbs clearly indicate the action and the nouns clearly indicate the target resource.
Five tools is well-scoped for an orchestration server handling asynchronous media generation. Each tool covers a necessary part of the workflow without redundant or excessive surface area.
The toolset covers the full asynchronous job lifecycle: discover capabilities, create a job, poll for completion, cancel if needed, and list past jobs. There are no obvious dead ends for the stated purpose of routing and orchestrating media generation.