Mutator
Draft and test-run formats that turn a product photo or website into TikTok and Instagram videos, and read results. Cannot publish.
Hosted MCP Server
npx add-mcp 'https://mutator.app/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
MCP server
Connect an AI agent — Claude, ChatGPT, Cursor, anything that speaks Model Context Protocol — to a Mutator workspace, so it can find automations, test them, read what happened and report what came out.
Endpoint: https://mutator.app/mcp
The one thing to know
No tool on this server can post anything.
That is not a scope you can widen or a permission you can grant. It is the shape of the server. An agent can build a run, watch it, read what came out and tell you about it; a person opens Mutator and decides whether any of it reaches a real account.
Nor can the public API, since approvals were removed on 20 September 2026: a run publishes nothing at all, and a post exists only once a person has put a particular file in a particular account's schedule, in Mutator, with their name on the decision. There is no key of any scope that reaches that. See what marketing may claim about this, and what the platform reviews were told.
Connecting
Two ways in, and both reach exactly the same tools:
- OAuth, for clients that are given only the address: Claude and ChatGPT connectors, Claude Code, and any client that follows the MCP authorization spec. The client opens Mutator in a browser, the workspace owner signs in, picks the workspace and chooses whether it may start runs.
- An API key as a bearer token, for clients configured with a header: the same keys the public API uses, created under Settings → API keys. See the public API documentation, which says what a key reaches on each plan.
Either needs the workspace owner, on any paid plan. read reaches every tool except the four that write: mutator_start_run, mutator_create_automation, mutator_save_automation_graph and mutator_set_schedule. Give read only unless the agent genuinely needs to build or run things. Nothing either way can publish.
Claude or ChatGPT
In Claude, add a custom connector under Settings → Connectors. In ChatGPT, add a connector with OAuth. Paste https://mutator.app/mcp and nothing else, then approve it in the window Mutator opens. Connected apps are listed under Settings → API keys, where each can be disconnected.
Claude Code
claude mcp add --transport http mutator https://mutator.app/mcp
Then run /mcp in Claude Code and sign in. To use a key instead, add --header "Authorization: Bearer loop_sk_..." to the command.
Anything with a JSON config
{
"mcpServers": {
"mutator": {
"type": "http",
"url": "https://mutator.app/mcp",
"headers": { "Authorization": "Bearer loop_sk_..." }
}
}
}
How OAuth works here
Mutator is its own authorization server and /mcp its only protected resource, following the MCP authorization spec (2025-06-18 and later):
- A request without a key or token gets
401withWWW-Authenticate: Bearer resource_metadata="https://mutator.app/.well-known/oauth-protected-resource/mcp". The handshake included: a client that got a200frominitializewould never offer to sign in. - Metadata:
/.well-known/oauth-protected-resource(RFC 9728) and/.well-known/oauth-authorization-server(RFC 8414). - Clients register themselves at
/oauth/register(RFC 7591). Public clients use PKCE alone; a client that names no auth method gets a secret. /oauth/authorizetakes the code flow with PKCE (S256only) and aresourceofhttps://mutator.app/mcp(RFC 8707). The person approves on/connect, which shows where the answer will be sent. An app's name is its own claim; the redirect address is fixed when it registers./oauth/tokenissues an access token for an hour and a refresh token for 90 days, rotated on every use. A refresh token presented again after it was traded in ends the connection./oauth/revoke(RFC 7009) ends it too.- An access token is accepted at
/mcponly, never by/api/v1, and stops working the moment the connection is revoked or the workspace leaves a paid plan, like a key.
Check it works by asking the agent to call mutator_whoami, or from a shell:
curl -s https://mutator.app/mcp -H "Authorization: Bearer loop_sk_..." -H "content-type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
GET https://mutator.app/mcp needs no key or token and returns a description of the server and its tools — useful for checking reachability, and for anything cataloguing MCP servers.
That same URL opens a page in a browser. One address serves both: a request asking for text/html gets the page, and everything else — a POST, or a GET sending */* or application/json — gets the protocol. Nothing about the endpoint changed for a client that was already using it.
Building an automation
An agent reads the vocabulary before it writes: mutator_list_brands for the brand the automation belongs to, mutator_list_step_types for the steps that exist, mutator_list_formats for the content formats, and mutator_list_connections for the account a publish step must name — there is no "post to TikTok" field, only "post to this connection, which happens to be TikTok".
Then mutator_create_automation with a graph of { key, type, position, config } nodes and { sourceKey, targetKey } edges. Most settings have defaults, so config: {} is usually enough. Two branches out of one step is how the same idea is produced two ways.
An invalid graph is saved anyway, with the problems returned as issues. That is deliberate: half-built is a normal state for a draft, and failing the whole call would lose the work. Two settings are refused outright at save time rather than at run time, because the executor refuses them hours later once somebody has activated and walked away — several versions of one video, and an image batch that is not 1 or exactly 4.
Tools
| Tool | Scope | What it does |
|---|---|---|
mutator_whoami | read | Which workspace this key reaches and what it may do |
mutator_list_automations | read | Every automation with id, name and status |
mutator_get_automation | read | One automation: status, schedule, spending limits |
mutator_list_runs | read | Recent runs of one automation |
mutator_get_run | read | One run, with per-step detail |
mutator_list_approvals | read | Always empty: approvals were removed |
mutator_get_analytics | read | How published content performed |
mutator_get_spend_limits | read | Daily and monthly ceilings, and what is left |
mutator_start_run | write | Start a run. Dry by default |
mutator_list_brands | read | The brands an automation can belong to |
mutator_list_connections | read | Connected accounts, and the id a publish step needs |
mutator_list_formats | read | The content formats, and the niches they suit |
mutator_list_step_types | read | Every step an automation can be built from |
mutator_create_automation | write | Create an automation. Draft only |
mutator_save_automation_graph | write | Replace its steps. Draft only |
mutator_set_schedule | write | Set when it would run. Saved switched off |
Dry by default
mutator_start_run runs dry unless the caller passes dryRun: false. A dry run skips publishing (and scheduling a repeat), not generation. It is not free: create and understand ignore dryRun, so a dry run makes the same paid generation calls a real run does, costs the same credits or provider charges, and is checked against the same spend caps (src/server/engine/runs.ts, the comment above assertSpendWithinCaps).
The public API leaves dryRun undefined and lets the engine decide, which is right for an integration somebody wrote on purpose. Here the caller is a model that may have inferred the entire call from one sentence, so the safe reading of silence is "test it": whatever it generates stops short of any social account. That limits what a mistaken call can publish, not what it can spend, which is why the tool description tells the model to start a run only when the user wants one.
idempotencyKey is required, 8–80 characters, and namespaced per key on the way through. Retrying with the same value returns the original run rather than starting a second one.
What this server is not
- Not an activator. An agent can build an automation and save it as a draft. It cannot switch one on. That is safe because a draft genuinely cannot run — it has no active version, the scheduler fires only active automations, and
createRunrefuses every trigger buttestunless the automation is active, which no API key can set. Activation is where a person reads what was built and takes responsibility for it, so it stays in Mutator. - Not a publisher. Above.
- Not a trend service. There is no virality, trending or discovery data in this product.
mutator_list_formatsreturns a hand-written catalogue, and a niche orders it rather than filtering it. An agent asked for "viral formats" can offer these; it cannot say any of them is trending. - Not stateful. No session id is issued, so any replica answers any request and a deploy loses nothing. Each call carries its own key.
Implementation notes
The server speaks JSON-RPC directly rather than using the official SDK. MCP is JSON-RPC 2.0 with four methods that matter to a tools-only server — initialize, tools/list, tools/call, ping — and the SDK's value is in transports that assume a long-lived process owning a socket. A Next route handler is neither.
Authentication, rate limiting and error shaping are shared with the public API, and the endpoint carries no scope of its own: reaching it needs only a valid key or token, and the write tools check the scope themselves at call time. Requiring write at the door would lock read-only keys out of initialize.
No CORS headers, deliberately. Every MCP client that matters connects from a server or a desktop process, and authentication is a bearer token rather than a cookie, so there is nothing for a browser to be tricked into sending.
The protocol versions spoken are 2025-06-18, 2025-03-26 and 2024-11-05. An unknown version is answered with the newest rather than refused.