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
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 Workflows setup: 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 Workflows release.
npm 7+ and pnpm install peer dependencies automatically. A strict peer installer must add @modelcontextprotocol/sdk and zod alongside @sanity/workflow-mcp, including when it runs the bundled binary.
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 “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
Addressed tools use workflow_resource and tag to select an environment. workflows_list_tags is the exception: it accepts only workflow_resource so you can discover the tags deployed to that resource.
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
Selects a deployment tag within the workflow resource. Use
workflows_list_tagsto discover the observed tags and confirm the intended environment before calling another tool.
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. An addressed tool fails when workflow_resource or tag is missing. workflows_list_tags is the exception because it discovers tags from workflow_resource alone.
Tell the agent which workflow resource to use. Let it call workflows_list_tags, confirm the intended tag, and then pass the complete address to every other tool. One server can work across all of your Workflows environments.
The tools
The server exposes eleven tools under the plural workflows_* names: eight for discovery and workflow operations, plus three for authoring definitions.
Operations
workflows_list_tags needs only workflow_resource. The other seven operation tools require the complete workflow_resource and tag address.
workflows_list_tags(): read
Lists the observed tags that currently have deployed definitions in the supplied
workflow_resource, sorted in ascending order. It returns{tags: string[]}, does not accepttag, and returns an empty array when the resource has no observed tags. Use the result to confirm the tag before calling an addressed tool.workflows_list_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.workflows_get_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.workflows_list_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.
workflows_get_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.workflows_diagnose(): read
The “why isn’t this moving?” check: classifies an instance as healthy or stuck, and names what would unstick it.
workflows_start(): 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.
workflows_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
workflows_get_authoring_guide(): read
The DSL guide an agent reads before writing a workflow definition.
workflows_validate_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.workflows_deploy_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.
Definitions that use role-constrained assignee or assignees fields require reader model 8. Upgrade every Studio, CLI, MCP server, Function, server, and application that shares the workflow resource, then pass a reviewed expectedMinReaderModel: 8 to workflows_deploy_definition. Definitions without a feature that raises the floor remain on the model-4 baseline.
Embed the tools in another MCP server
Use registerWorkflowTools to add Workflows tools to an existing MCP server. The bundled tools accept workflow_resource and tag on each addressed request. This example keeps authentication and transport in the host-owned tokenFor and baseClientConfigFor functions, then uses the package-owned client and engine policy for the selected environment.
import {createClient, type ClientConfig} from '@sanity/client'
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'
import {
createWorkflowEngine,
registerWorkflowTools,
type WorkflowToolExtra,
workflowAddressFromInput,
workflowClientConfig,
} from '@sanity/workflow-mcp'
declare function tokenFor(extra: WorkflowToolExtra): string
declare function baseClientConfigFor(extra: WorkflowToolExtra): ClientConfig
const server = new McpServer({name: 'my-host', version: '1.0.0'})
registerWorkflowTools(server, (extra, input) => {
const address = workflowAddressFromInput(input)
const client = createClient(
workflowClientConfig({
resource: address.workflowResource,
token: tokenFor(extra),
base: baseClientConfigFor(extra),
}),
)
return {engine: createWorkflowEngine({address, client})}
})workflowClientConfig preserves host settings such as its requester, headers, and API host, then pins the API version, request-tag family, published perspective, and non-CDN reads required by the engine. It also removes the unused resource-addressing style. createWorkflowEngine binds that client to one resource and tag without caching. A long-lived host can cache engines outside the helper.
For custom registration, WORKFLOW_TOOLS and the individual tool definitions expose host-neutral input schemas. When requiresAddress is true, merge your host's environment parameters into the registered schema and use toolInputJsonSchema for the definition-only JSON Schema. Render thrown failures with workflowErrorText, and reuse WORKFLOW_TAG_DESCRIPTION for your tag parameter. If the host offers tag discovery, register LIST_WORKFLOW_TAGS_TOOL_NAME and use LIST_WORKFLOW_TAGS_DESCRIPTION rather than rewriting its model-facing guidance.
The package uses Zod 3 through zod/v3. Mixing a Zod 4 schema into these definitions fails type checking. zod and @modelcontextprotocol/sdk are peer dependencies, so embedding hosts must provide compatible versions.
Each wire tool also has a camel-case *Tool export. The workflows_fire_action wire tool keeps the fireActionTool export. ProjectedDefinitionSummary is available for projected catalog results. Tag discovery has no definition input; use the exported workflow-resource constants when composing its host schema.
What the agent can and can’t do
The agent can survey, inspect, and diagnose freely. It changes state through exactly three doors:
workflows_startto start a run- Use
workflows_fire_actionto advance an instance after reading its current state. workflows_deploy_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.