Set up the MCP server
Connect an AI agent to your workflows over MCP: an org token authenticates the server, and each tool call addresses the environment it acts on.
Prerelease
Editorial Workflows is a prerelease, built in public. Read How the prerelease works before you rely on it.
@sanity/workflow-mcp is an MCP server that exposes your workflows as tools an agent can call. It does two jobs: operate workflows (list what’s deployed and what’s running, inspect and diagnose an instance, start runs and fire actions to move work forward) and author new ones (the agent reads the DSL guide, validates what it wrote, and deploys it into an environment you name). It ships as the workflow-mcp binary. The server is standalone during early access and may eventually be folded into the Sanity MCP.
In this guide, you’ll connect an AI agent (Claude Code, Claude Desktop, Cursor, or anything else that speaks MCP) to your editorial workflows: install the server, authenticate it, and see how the agent addresses your workflow environments. For the sanity.workflow.ts configuration and CLI-driven deploys, see Set up the workflow CLI; the MCP server addresses the workflow environments configured there, and can deploy definitions into them itself.
Public package
@sanity/workflow-mcp is published publicly on npm; installing it needs no @sanity org membership.
Install the server
The server runs over stdio, so your MCP client launches it; you don’t start it yourself. Install the package globally so clients can spawn the workflow-mcp binary:
npm install -g @sanity/workflow-mcp @sanity/workflow-engine
pnpm add -g @sanity/workflow-mcp @sanity/workflow-engine
yarn global add @sanity/workflow-mcp @sanity/workflow-engine
bun add -g @sanity/workflow-mcp @sanity/workflow-engine
Install both packages from the same Editorial Workflows release.
A client can also spawn the server without an install:
npx -y @sanity/workflow-mcppnpm dlx -y @sanity/workflow-mcp
yarn dlx -y @sanity/workflow-mcp
bunx -y @sanity/workflow-mcpThis resolves the version when the client boots the server, so a session can silently pick up a new release; pinning a version in the command trades that for a stale pin.
Authentication
Boot configuration is two environment variables:
RequiredSANITY_AUTH_TOKEN
string
SANITY_API_HOST
string
The API host. Defaults to
https://api.sanity.io.
The server also emits adoption telemetry, following your account’s telemetry consent: each tool invocation logs an “Editorial Workflows MCP Tool Called” event carrying only the tool name and a success flag, never tool arguments or results. Set DO_NOT_TRACK=1 in the server’s environment to opt out.
The server is org-authed on the same model as the Sanity MCP: auth says who may act, and each tool call says where. Which workflows the agent can actually touch is decided by the token’s access in Content Lake, per call.
Authentication
The server authenticates with the token supplied in its environment. Scope that token to the required resources and permissions; do not give the agent broader access than it needs.
Workflow history records actions as the identity behind the token (the engine resolves it via /users/me), and every entry carries an advisory execution-context stamp (kind mcp, id workflow-mcp) marking that the action came through this server. The token both authorizes and identifies the actor; it cannot distinguish the human driving the agent from the token’s own identity. A trigger that cascades during one of the agent’s writes also executes under this token; history marks those entries as triggered, so automation stays distinguishable from actions the agent fired.
Register with your MCP client
Everything is configured through the client’s server registration. Claude Code registers the server with one command; Claude Desktop, Cursor, and most other clients take the same shape as JSON:
claude mcp add sanity-workflows \ --env SANITY_AUTH_TOKEN=<token> \ -- workflow-mcp
{
"mcpServers": {
"sanity-workflows": {
"command": "workflow-mcp",
"env": {"SANITY_AUTH_TOKEN": "<token>"}
}
}
}A GUI-launched client (Claude Desktop among them) may not inherit your shell PATH and then cannot resolve workflow-mcp. Register with the npx form instead, accepting the version caveat above: "command": "npx", "args": ["-y", "@sanity/workflow-mcp"].
The workflow environment address
Every tool call that touches workflow data names where that data lives, with two parameters:
Requiredworkflow_resource
string
The resource holding the workflow documents, as
<type>:<id>. A dataset is the common case (dataset:yourprojectid.workflows);canvas:,media-library:, anddashboard:resources are accepted too.Requiredtag
string
Which environment’s copy within that resource (
prod,test, and so on).
These are exactly the workflowResource and tag of a deployment in your sanity.workflow.ts: the address of a deployment the CLI shipped.
// sanity.workflow.ts
{
tag: 'prod',
workflowResource: {type: 'dataset', id: 'yourprojectid.workflows'}
}There is no default environment. A call without an address is a validation error, never a guess: the server requires an explicit address on every call, and the token’s actual access decides the rest.
In practice, tell the agent once (“workflows live in the dataset yourprojectid.workflows with the tag prod”) and it threads the address through every call; the tool descriptions instruct it to ask rather than guess when it does not know. One server process serves all your environments: prod and test are different addresses on the same connection.
The tools
The server exposes ten tools: seven for operations and three for authoring.
Operations
All operation tools need an environment address.
list_workflow_definitions(): read
The catalog: which workflow types are deployed (latest version per name). Each entry reports
startable(false for child workflows that only run under a parent) andstartKind:interactive= a person starts runs from a picker;autonomous= a system starts runs in reaction to a document. A classification, not a restriction.get_workflow_definition(): read
One deployed definition’s full content, by
name(in this server, adefinitionparameter is always a name string), defaulting to the latest version. The returneddefinitionis valid deploy input, which is how an agent edits a workflow: read it, modify it, validate, and deploy the next version.list_workflow_instances(): read
Discovers workflow instances with optional definition, document, and include_completed filters. Results are paginated: limit accepts 1 to 100 and defaults to 25. Continue with next_cursor while has_more is true, keeping the same filters.
get_workflow_state(): read
Everything actionable about one instance: stage, a workflow-level
autonomynarrative (whether the workflow runs itself and where it waits on someone), in-scope activities with their executor classification (interactive, autonomous, off-system, or hybrid) and a causalcompletesWithoutCallerverdict (yes, no, or conditional) with narratedwaitsOnlines when it is not yes, invocable actions with theirparams(each marked required or optional) and why any are disabled, automations the engine fires on its own listed per activity, and recent history.diagnose_workflow(): read
The “why isn’t this moving?” check: classifies an instance as healthy or stuck, and names what would unstick it.
start_workflow(): write
Starts a named definition with initial_fields and an optional instance_id idempotency key. Every ordered start requirement must pass. Child workflows remain spawn-only.
fire_action(): write
The instance write: fires an invocable action on an activity, exactly as an editor would, supplying values for the
paramsthe state projection declares. Triggers are the engine’s to fire; they surface as automations and this tool rejects them.
Authoring
get_workflow_authoring_guide(): read
The DSL guide an agent reads before writing a workflow definition.
validate_workflow_definition(): read
The same checks a deploy runs, over a
definitionsarray (a single workflow is a one-element array): per definition, the expanded (desugared) form that would deploy, or a path-prefixed error list the agent can fix from.deploy_workflow_definition(): write
Publishes validated definitions (a
definitionsarray, even for a single workflow; a parent and the child workflows it spawns belong in one call) into an addressed environment, returning{name, version, status}per definition.
Deployments are create-only and content-addressed: content identical to the latest deployed version is a no-op (unchanged), and any change mints the next version. A deployed version is never patched, and running instances keep the definition version they started under.
What the agent can and can’t do
The agent can survey, inspect, and diagnose freely. It changes state through exactly three doors:
start_workflowto start a runfire_actionto advance an instancedeploy_workflow_definitionto publish workflow definitions
The Content Lake is the boundary. The checks the tools make (disabled actions, guard verdicts, validation) are advisory conveniences for the agent; enforcement is dataset access control (custom roles) evaluated against the server’s token and parameters. A token that can’t write in an environment can’t fire actions or deploy into it.
Next steps
- Set up the workflow CLI: deploy definitions and the sanity.workflow.ts deployments the environment addresses point at.
- Reference: the constructs and engine verbs behind these tools.
- Test your workflows: prove a definition in the in-memory bench before an agent deploys it.