← 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

Authentication is a Mutator API key as a bearer token — the same keys the public API uses, created under Settings → API keys (workspace owner, any paid plan). See the public API documentation, which says what a key reaches on each plan.

A read key reaches every tool except mutator_start_run. Issue one of those unless the agent genuinely needs to start runs.

Claude Code

claude mcp add --transport http mutator https://mutator.app/mcp --header "Authorization: Bearer loop_sk_..."

Anything with a JSON config

{
  "mcpServers": {
    "mutator": {
      "type": "http",
      "url": "https://mutator.app/mcp",
      "headers": { "Authorization": "Bearer loop_sk_..." }
    }
  }
}

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 at all 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

ToolScopeWhat it does
mutator_whoamireadWhich workspace this key reaches and what it may do
mutator_list_automationsreadEvery automation with id, name and status
mutator_get_automationreadOne automation: status, schedule, spending limits
mutator_list_runsreadRecent runs of one automation
mutator_get_runreadOne run, with per-step detail
mutator_list_approvalsreadAlways empty: approvals were removed
mutator_get_analyticsreadHow published content performed
mutator_get_spend_limitsreadDaily and monthly ceilings, and what is left
mutator_start_runwriteStart a run. Dry by default
mutator_list_brandsreadThe brands an automation can belong to
mutator_list_connectionsreadConnected accounts, and the id a publish step needs
mutator_list_formatsreadThe content formats, and the niches they suit
mutator_list_step_typesreadEvery step an automation can be built from
mutator_create_automationwriteCreate an automation. Draft only
mutator_save_automation_graphwriteReplace its steps. Draft only
mutator_set_schedulewriteSet 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 createRun refuses every trigger but test unless 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_formats returns 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, and the one write tool checks the scope itself 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.