Workflows

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

@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

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:

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:

This 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:

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

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:

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:, and dashboard: resources are accepted too.

  • Requiredtag

    string

    Selects a deployment tag within the workflow resource. Use workflows_list_tags to 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 accept tag, 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) and startKind: 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, a definition parameter is always a name string), defaulting to the latest version. The returned definition is 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 autonomy narrative (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 causal completesWithoutCaller verdict (yes, no, or conditional) with narrated waitsOn lines when it is not yes, invocable actions with their params (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 params the 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 definitions array (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 definitions array, 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_start to start a run
  • Use workflows_fire_action to advance an instance after reading its current state.
  • workflows_deploy_definition to 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.

Was this page helpful?