
AI Media Router
io.github.jutsuhqPublished by GitHub account jutsuhqv0.1.0Updated Nov 1, 2026
Image and video generation across 40+ providers with your own keys, plus a local asset library.
The publisher's own server: 370.ai — AI Gateway: 100+ Chat Models, Image & Speech Generation
Overview
Local MCP server that generates images and videos through 40+ providers using your own API keys, with price comparison and a searchable local asset library.
- What you can do
- Compare catalog prices and estimate costs across providers before generating
- Generate images and videos, run batches, poll jobs and cancel them, with USD budgets that refuse jobs before submission
- Search, review, rate and export generated assets from a local library, plus local ffmpeg edits such as trim, concat, overlay and mux audio
- Save named references and placeholders to keep characters or products consistent across shots
- Inspect job history, lineage and per-model stats, and save recipes or presets to rerun a generation or a whole chain
- Read and edit provider specs to fix a provider whose API changed
- When to use it
- Useful when an assistant should create images or video from prompts and you already hold keys for one or more generation providers, especially when you want to compare prices, cap spending, and keep every result in a local, searchable library instead of expiring provider URLs.
- Requirements
- Runs as a local process (prebuilt binary, Rust build, or the published container image) over stdio. Needs at least one provider key, set via the CLI key command or environment: FAL_KEY, KIE_API_KEY, REPLICATE_API_TOKEN, RUNWARE_API_KEY or WAVESPEED_API_KEY, among others. Optional ComfyUI server URL and token, and ffmpeg/ffprobe for previews and video metadata. Network access to the chosen providers.
Installation
In SourceWeft
- Open AI Media Router in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
[AI Media Router]
One MCP server for AI image and video generation across 40+ providers, with your own keys and a local library that remembers everything.
[CI] [Latest release] [License: MIT] [MCP server]
AI Media Router is an open-source MCP server that lets Claude, Cursor, Codex or any MCP client generate images and videos with Seedance, Kling, Veo, Wan, FLUX, GPT Image, Nano Banana, Seedream and more, through 43 providers: fal, Replicate, kie, Runware, WaveSpeed, OpenRouter, BytePlus and 35 others, or your own ComfyUI server. The agent compares prices across providers before it spends, pays each one with your own key, and gets every result back as a local file it can search, review, reuse as a reference and chain into the next shot. Self-hosted, a single Rust binary, MIT.
For example, on November 1, 2026 the price catalog listed Seedance 2.5 text-to-video at 720p with audio from $0.216 a second (302.AI) to $0.462 (fal) among the providers the router can call: the same 10-second clip for $2.16 or $4.62. search_prices shows the agent that spread before it picks.
Contents:
- Why
- Supported providers
- Compared with other ways to generate from an agent
- Quick start
- How a generation works
- The five pillars
- Production pipelines
- Placeholders
- Costs and budgets
- Checks before spending
- When a job fails
- Interface
- Fixing a provider whose API changed
- Architecture
- Testing
- FAQ
Why
Principles: bring your own keys (no middleman account), the agent chooses provider and price (no hidden routing), model and input pass through unchanged (no lagging unified schema). Details are in docs/concept.md.
Supported providers
43 built-in provider specs, each a TOML file in providers/ with recorded fixtures. ai-media-router providers lists them with the env vars each one still needs.
Models are not a fixed list: model goes to the provider as written, so a model a provider adds today works today. get_provider shows the model id format and an example call. Specs not yet checked against a live response say so in their notes; live verification of every spec is on the roadmap.
Compared with other ways to generate from an agent
AI Media Router is not a hosted gateway like OpenRouter: there is no account, no balance and no markup, and OpenRouter itself is one of the providers it calls. It does not route to the cheapest provider by itself either; it shows prices and the agent or you choose.
Quick start
1. Install. Prebuilt binaries for Linux (x86_64, arm64), macOS (Apple silicon, Intel) and Windows (x86_64) are on the releases page. You can also build from source with Rust 1.89 or later:
Or run the container (stdio MCP; /data holds the library):
2. Add keys in a terminal. The value is read from a hidden prompt, or from stdin, and never passes through an agent or a tool call:
On Windows there is no hidden prompt: pipe the value in (Get-Clipboard | ai-media-router keys set FAL_KEY).
--store auto (the default) keeps the secret in the OS keychain (macOS keychain, Linux Secret Service
via secret-tool) when one is available, else in <home>/config.toml under [env], written atomically
with mode 0600; --store keychain or --store file forces one. In config.toml a keychain-held key
appears as FAL_KEY = "keychain:ai-media-router/<id>", with no secret in the file. The process
environment still overrides both, and keys apply without a restart.
ai-media-router keys list shows every key variable, which providers use it, whether it is set, its source
(environment, keychain or config file), its last four characters and the exact keys set command.
ai-media-router keys test kie makes a free auth check where its spec defines auth_check: accepted, refused,
inconclusive, not_supported or not_configured. ai-media-router keys remove FAL_KEY deletes a key.
<home> is set by --home or AI_MEDIA_ROUTER_HOME. By default it is the OS data dir: ~/Library/Application Support/jutsu.ai-media-router on macOS, %APPDATA%\jutsu\ai-media-router\data on Windows, ~/.local/share/ai-media-router on Linux. ai-media-router providers lists every provider and the env vars it still needs.
3. Connect an agent. For Claude Code:
For Claude Desktop (claude_desktop_config.json), Cursor (~/.cursor/mcp.json) and other clients that take an mcpServers block, use the binary's full path, since these apps may not see your shell's PATH:
Then ask the agent something like "find the cheapest Seedance 2.5 offer at 720p among my providers and make a 5-second clip of a paper boat on a rainy street".
serve refuses a non-loopback address unless AI_MEDIA_ROUTER_TOKEN is set. Then every request needs Authorization: Bearer <token>, and save_spec / reset_spec stay disabled unless --allow-spec-writes is given.
4. Save a preset. A preset is a saved single generation; --input merges over its input field by field:
The preset supplies provider, model, input, save_to, offer and budget; fields given in the call win.
ai-media-router recipes list shows the presets.
Optional extras:
How a generation works
A job moves through these states:
The five pillars
Provenance in files
Every generated file carries where it came from, so a file shared, moved or restored from a backup
still knows its history without the database. Saved outputs get one JSON record: generator
(router version), job_id, provider, model, input (snippets expanded, {{asset:…}} / {{ref:…}}
placeholders kept), prompt and parents (the asset ids the job read).
API keys are never written: values of the provider's key variables are replaced by <redacted>,
URLs lose their query string and credentials (or become {{asset:<id>}} when the library knows them),
and data URIs are left out. The asset id and size are those of the file with its metadata.
import_asset reads the record back: it fills prompt, adds provider and model to tags, links the
asset to its job (recreating it as a finished job in another library, so save_recipe {from_job}
works) and to its parents. The result has provenance, missing_parents (import those files to link
them) and a hint. Importing the same file again is harmless and links parents imported since.
Set EMBED_METADATA = "false" in [env] to store provider files unchanged. Embedding makes every
job's file unique, so two jobs returning identical bytes no longer share one asset.
Production pipelines
Single generations are rarely the deliverable. The router supports the whole production loop: lock what must not change, fan out variants, keep only the good takes, chain stills into video, and finish locally.
Example: a three-shot product ad.
Every step is recorded, so get_lineage on the final video shows the whole graph back to the product photo:
Placeholders
model and input go to the provider unchanged, except for these placeholders. The job stores the input with its placeholders, so it can be rerun or saved as a recipe.
An unknown name fails with the list of known ones. A parent job that fails also fails its waiting children, with the error parent job X failed: … (step `name` (job X) failed: … inside a recipe run).
Recipes
A recipe saves what worked so it can run again with new values. It holds either one generation (provider, model, input) or a whole chain of steps. Each step is a generation or one of the ffmpeg edits (extract_frame, reframe, trim, concat, overlay, mux_audio), and later steps take earlier outputs through {{step:<name>}}. A single-generation recipe is a preset: save it with save_recipe {name, provider, model, input, save_to, offer_id, budget, description} and run it directly with generate {recipe, input}, which merges the call's input over the preset's field by field; vars fills {{var:…}}.
- One call.
run_recipecreates every step's job at once, in one batch. A step that uses another waits for it, like a{{job:…}}chain; edit steps arelocaljobs that the worker runs with ffmpeg when their inputs are done. It returns the last step's job, andget_batch {batch_id}follows the whole run. The batch'soutputsare the final steps' files (those no other step uses), andjobs[].stepnames each step. - Failures. A failed step fails every step after it, with an error that names the failed step.
- Money. Each generation step carries its own
estimated_cost_usdoroffer_id. Withbudget, the sum of all steps is admitted before anything is submitted, as for a batch.preflight {recipe, vars}checks every step (checks named<step>/<check>) andestimate_cost {recipe, vars}lists each step's price and the sum. - From lineage.
save_recipe {name, from_job: <final job>, chain: true}(orfrom_asset) walks back through the jobs that made its inputs, down to imported files and references, and saves them as steps; their job and asset ids become{{step:…}}. Withoutchainit copies that one job, as before. - Variables and prompts.
{{var:<name>}}works in any step. Every recipe is the MCP promptrecipe:<name>, which lists its steps and variables, and the resourceai-media-router://recipes/<name>.
Costs and budgets
The estimate is the first of a, b, c that exists. Without a budget, the estimate is only stored on the job.
- Offers.
search_pricesreturns catalog offers, cheapest first, each with a stableoffer_id(provider, model, variant, mode, resolution, audio, quality; the price is not part of it). It lists only the user's configured providers by default (once at least one key is set);configured_only: false(CLIprices --all) lists every provider. Video and image prices come from ai-media-api-prices;kind: "image"lists image offers.estimate_costshows the number and the formula used to get it. A saved preset already carries itsoffer_id, so running it needs no price search. - Seconds are read from
duration,duration_seconds,duration_secs,seconds,video_lengthorlength."5s"counts as 5. - Output count is read from
num_images,n,num_outputs,image_count,number_of_images,sample_count,batch_size,num_videosorcount. The top level is checked first, then nested objects. - Spending. A job counts its reported USD cost once known, otherwise its estimate. Failed jobs count nothing.
- Batches are admitted against the budget as one sum, so a batch either fits or is refused whole.
Not paying twice
Every job stores a fingerprint: a hash of provider, model and the input after placeholder expansion. Library files
count by asset id, which is a content hash, so the same files give the same fingerprint whether they come through
{{asset:…}}, {{ref:…}} or {{job:…}}, and whatever upload URL they got.
reuse: trueongenerate,run_recipeorgenerate_batchdefaults (CLI--reuse): when a job with the same fingerprint is done, its outputs are all still in the library and none is rejected, the call returns at once with a new job. That job isdone, costs 0 USD, names the original inreused_from, and links the same assets as outputs and inputs. It is filed into its ownsave_to, belongs to its own batch, and chains like any job.get_statsand the price-history median skip reused jobs, so they count only real generations. Reuse is off by default, because models are not deterministic and a repeat is usually meant as a new variant.- Identical requests in flight. A request identical to one started in the last 5 minutes that is still waiting
or running is not submitted again, even without
reuse: agents call again when their client times out. With the samesave_tothe call returns that job, with anotesaying so. Otherwise, or in a batch, it gets a new job that waits for the original and takes its outputs at no cost. Identical items of one batch are variants and are not merged.reuse: false(CLI--no-reuse) always submits. - Before running.
preflightandestimate_cost(both takereuse) returnreuse: {state, job_id}and an estimate of 0 when the run would be reused.
Checks before spending
preflight takes the arguments of generate and runs every check generate would run, without sending or spending anything:
- provider and keys;
- snippets and placeholders: assets, references, parent jobs;
- upload support and
save_to; - cost and budget.
It also returns the input as the provider would receive it and the exact HTTP requests. ready says whether generate would start. On the CLI it is generate --dry-run, which exits 1 when a check fails.
doctor checks the installation:
- the data directory and the database;
- ffmpeg and ffprobe;
- configured providers, broken overrides and fixtures that no longer match;
- jobs that no worker is polling;
- the last day's failure kinds.
doctor --online also pings each configured provider. Run it first when something does not work.
When a job fails
Every failed job carries error_kind, retryable and a hint with what to do next. list_jobs {status: "failed", error_kind} finds failed jobs.
The kind is classified in this order:
- The provider's error text, using built-in patterns plus the spec's
[errors.kinds]. - The HTTP status.
- The engine's own messages.
get_stats sums the kinds up per provider and model.
Automatic retries. Pass retry: {attempts: 1-5, delay_secs} to generate, run_recipe or generate_batch defaults,
or --retry N --retry-delay S on the CLI. After a retryable failure the same job is submitted again, and the n-th retry
waits n × delay_secs (default 30). progress says which attempt failed and why, and attempts counts submissions.
Retries are off by default, because some providers bill a failed attempt.
Interface
The same operations exist as MCP tools, REST endpoints and CLI commands. They are defined once, in src/api/. Keys are the exception: keys set and keys remove are CLI-only, so a secret never passes through a tool call; the MCP tools list_keys and test_key only report status. The MCP server also sends short instructions, so an agent knows the order of the tools without reading this file.
Generate and follow jobs
Library, review and memory
References and collections
Budgets
Media operations (ffmpeg, local, free)
Each result is a new asset made by a finished job with provider local, so lineage, history and save_to treat it like a generation. Without ffmpeg these tools fail with install instructions. CLI: ai-media-router media frame|reframe|trim|concat|overlay|mux.
Snippets, recipes and MCP prompts
Provider specs
For agents. skills/ai-media-router/SKILL.md is a ready agent skill: setup, the generation loop, rules on money and failures, placeholders, batches and recipes. Copy it into your agent's skills folder. The MCP server exposes it as the resource ai-media-router://guide, along with every provider spec (ai-media-router://providers/<key>) and recipe (ai-media-router://recipes/<name>). AGENTS.md is for agents working on this repository.
Fixing a provider whose API changed
No rebuild and no restart. The same loop works from an agent over MCP and from a shell:
Error messages already point at the broken field. For example: kie submit (builtin providers/kie.toml): capture id pointer /data/taskId matched nothing in the response: {…}.
To make a fix permanent:
- Copy the override into
providers/kie.toml. - Add a fixture under
providers/fixtures/kie/. A debug trace already has the fixture shape. - Run
cargo test.
The format is described in docs/provider-spec.md.
Adding a provider is the same loop with a new file:
- Write
providers/<key>.tomlfollowing the spec format. Put the provider's name from the price catalog intocatalog. - Add fixtures from the docs' example responses.
- Run
cargo test.
Parts of a spec that have not been verified go into its notes, which get_provider shows to agents.
Architecture
-
Providers are data, not code. Each
providers/<key>.tomldescribes:- auth;
- the HTTP steps;
- JSON pointers to ids, status and outputs;
- uploads.
build.rsembeds every spec and fixture. Files in<home>/providers/override built-ins and are reloaded when they change. -
One request type end to end. The request a spec renders is the same one that is sent, shown by
check_spec, stored in a job's trace and compared with fixtures, so these cannot drift apart. Credentials never leave the auth layer: traces show<ENV_NAME>instead. -
Auth schemes live in the engine, so specs only name env vars. Supported:
bearer,header,query,headers,basic,jwt_hs256(Kling) andcommand(gcloud tokens). -
Jobs survive restarts and parallel processes. State lives in SQLite. Every process runs a worker, and each job is polled by whichever process wins the
UPDATE … RETURNINGlease. A retry or a job whose parents finished goes back through the same queue. -
One schema, no migrations (pre-release).
src/db/schema.sqlcreates the database. A library from another schema version is refused with a message instead of being migrated. -
Results are always local. URLs, data URIs, base64 and authenticated downloads are all streamed into
<home>/assets/, named by content hash.
Testing
cargo test:
- validates every spec: required steps, auth fields, pointer and template lints, unique keys;
- replays every fixture;
- runs the engine and the API end to end against a mock provider.
scripts/emulate.py runs the real binary as an MCP server over stdio against a local emulator of every provider. The emulator answers the way each spec says that API answers. It checks:
- every route of every provider: submit, poll, result, download and uploads;
- every MCP tool, MCP prompt and MCP resource;
- batches, chains and
last_frame; - error kinds;
- every recorded fixture, replayed over HTTP.
--only kie,fal narrows the run. CI runs both commands on Linux, and cargo test on macOS and Windows too.
FAQ
What is an MCP server for image and video generation?
A program an AI agent such as Claude, Cursor or Codex calls through the Model Context Protocol to make images and videos. AI Media Router is one: the agent calls generate and get_job, and the router talks to the provider's API and saves the result.
Is AI Media Router free? Yes, MIT-licensed. You pay each provider directly at its own prices with your own key. The router adds no markup and has no account; ComfyUI jobs on your own server cost 0 USD.
Does it pick the cheapest provider automatically?
No. search_prices lists offers cheapest first, and the agent or you choose. There is no silent routing or fallback, so cost and quality stay explicit decisions.
Which models can it use?
Any model a supported provider serves, for example Seedance, Kling, Veo, Wan, Hailuo, Runway, Luma, LTX, FLUX, GPT Image, Nano Banana, Imagen and Seedream. model and input pass through unchanged.
Is it a fal MCP or Replicate MCP alternative? It covers fal and Replicate as two of its 43 providers, behind one set of tools, with price comparison, budgets and a local library on top. Use a provider's own MCP server if you only ever use that provider.
Is it like OpenRouter for images and video? In the sense of one interface over many providers, yes. Unlike OpenRouter it runs on your machine and has no account or balance: each provider bills you directly. OpenRouter is one of the providers it can call.
Does it work with ComfyUI?
Yes. Set COMFYUI_URL and your server becomes provider comfyui: the input is an API-format workflow, and files referenced as {{asset:…}} in a LoadImage node are uploaded for you. Comfy's hosted partner models are the separate provider comfy.
Where are generated files stored?
On your machine, in <home>/assets/, named by content hash and indexed in SQLite, with the prompt, provider, model and parents written into the file itself. Provider URLs that expire do not matter.
Can an agent overspend?
Not with a budget set. set_budget creates a USD cap, and a job is refused before anything is sent when what was spent plus its estimate would exceed it. The check works from estimates, so a provider that bills more than its listed price can still go over by that difference.
License
MIT, see LICENSE.
Source: README.md at commit f4b9472
Tools
0Version history
1- v0.1.0LatestNov 1, 2026
Score
50% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts
Other Router integrations
370.ai — AI Gateway: 100+ Chat Models, Image & Speech Generation
ai.routerOfficial publisher
Remote AI gateway MCP server that lets an assistant call 100+ chat models, generate images, and convert text to speech through one 370.ai API key.

Bike Router
Published by GitHub account JonasHeinickeBio
MCP bridge to a self-hosted pyBikeRouter API that plans OSM-based bike routes with weather, sights and GeoJSON/GPX exports.

Uniswap Universal Router Decoder
Published by GitHub account Elnaril
Local MCP server that decodes and encodes Uniswap Universal Router calldata for V2, V3 and V4 swaps, read-only and never signing or sending.

x402 Router
Published by GitHub account camerxnduff
Read-only MCP server that searches a probed registry of live x402 pay-per-call endpoints and routes an agent to the best-scored one.

GenPark Consistent Hash Ring Router
Published by GitHub account alphaparkinc
A local MCP server providing a consistent hashing ring with virtual nodes, for routing distributed LLM caches and partitioning agents.