Workflows

Workflows MCP tools reference

Every operation @sanity/workflow-mcp gives an agent host, with its input, result, and errors, plus the embedding API.

This page lists every operation that @sanity/workflow-mcp gives an agent host, with its input, result, and errors. It also lists the exports a host uses to build engines, record telemetry, and report errors. For how a host wraps the operations in tools, see Connect an agent over MCP.

How to call an operation

Most operations are async functions you call as operation({engine}, input). The engine serves one workflow environment, and its client’s token is the acting identity. Three operations differ:

  • listWorkflowDefinitions({engine}) takes no input.
  • listWorkflowPartitions({client}) takes a client for one resource instead of an engine.
  • validateWorkflowDefinitions({definitions}) is synchronous and takes no engine.

Inputs use camelCase, such as instanceId and includeCompleted. The operations don’t validate their input at runtime, so check each tool’s input against your own schema first. The one exception is the list cursor, which listWorkflowInstances decodes and checks itself. Each operation returns plain data, and each result has an exported type, such as StartWorkflowResult or DeployDefinitionsResult.

Each entry lists a suggested tool name. The authoring guide and some error messages use those names, so a host that uses different names or input spellings should check that this text still makes sense to its agent.

Workflow environment address

There’s no default environment. Build each engine for the address of one deployment: the workflowResource and partition of a deployment in your sanity.workflow.ts. A deployment that omits partition uses default. The address type is WorkflowEnvironmentAddress:

  • RequiredworkflowResource

    WorkflowResource, required

    The resource that holds the workflow documents, as {type, id}. type is 'dataset', 'canvas', 'media-library', or 'dashboard'. A dataset id is <projectId>.<dataset>. To parse a resource string such as dataset:PROJECT_ID.DATASET_NAME from tool input, call parseResourceGdr from @sanity/workflow-engine. A malformed string throws Invalid resource GDR "<value>": expected "<type>:<id>" with no document part.

  • Requiredpartition

    string, required

    The deployment partition within that resource: lowercase letters, digits, and dashes, starting with a letter or digit. listWorkflowPartitions lists the partitions with deployed definitions. An address with a tag key throws [contract-violation] "tag" is not supported. Replace tag: "dev" with partition: "dev". Keep the existing partition value to retain access to stored workflows..

Results and errors

An operation returns plain data that your host can serialize as the tool result. The guide’s example sends it as indented JSON in one text content item.

An operation throws on failure. Render the error for the agent with workflowErrorText:

  • An engine error starts with its stable [kind], for example [instance-not-found], [contract-violation], or [reader-model-acknowledgement].
  • Any other error shows its message.

Some results carry authored text, such as titles, descriptions, conditions, or subject titles. Tell your agent to treat that text as data, not instructions, for example in your tool description.

Discovery and operations

  • listWorkflowPartitions({client})(): Promise<ListWorkflowPartitionsResult>

    Lists the partitions that have deployed definitions in one resource, sorted ascending. A partition with nothing deployed doesn’t appear. An empty list means the resource holds no deployed workflows.

    Takes a client for the workflow resource instead of an engine, and no input. Build the client with workflowClientConfig. Read-only. Suggested tool name: workflows_list_partitions. Result: {partitions: string[]}.

  • listWorkflowDefinitions({engine})(): Promise<ListWorkflowDefinitionsResult>

    The catalog of deployed workflow types, 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) or autonomous (a system starts runs in reaction to a document). startKind is a classification, not a restriction.

    Takes no input. Read-only. Suggested tool name: workflows_list_definitions. Result: {definitions: {name, title, description?, version, startable, startKind}[]}.

  • getWorkflowDefinition({engine}, input)(definition, version): Promise<ProjectedDefinition>

    One deployed definition’s full content, by name, defaulting to the latest version. The returned definition is the stored form without the document envelope. You can pass it to validateWorkflowDefinitions and deployWorkflowDefinitions as is. To edit a workflow, read it, change it, validate it, and deploy the next version.

    Read-only. Suggested tool name: workflows_get_definition. Result: {name, version, definition}.

    Error, unknown definition: no deployed definition "nope" in this workflow environment — workflows_list_definitions shows what is deployed

    Parameters

    • definition

      string

      The definition’s name, as listWorkflowDefinitions lists it.

    • version

      number

      A specific deployed version to read. Defaults to the latest.

  • listWorkflowInstances({engine}, input?)(definition, document, includeCompleted, assignment, limit, cursor): Promise<ListWorkflowInstancesResult>

    Lists instances in the engine’s partition, most recently changed first. Every input field is optional.

    assignment filters to one person. It also adds counts of active activities in the current stage for each instance:

    • held: assigned directly to the person.
    • routed: offered through one of the person’s roles, with no person assigned.
    • unrouted: no person or role selected.

    The counts don’t promise that an action is available. A page can hold fewer instances than limit, because the document filter drops instances that don’t watch that document. When has_more is true, call again with the same filters and pass next_cursor as cursor.

    Read-only. Suggested tool name: workflows_list_instances. Result: {instances: {instanceId, definition, workflowTitle?, currentStage, currentStageTitle?, lastChangedAt, done, subject?: {ref, title?}, assignment?: {unrouted, routed, held}}[], has_more, next_cursor?}.

    Error, malformed cursor: workflows_list_instances: invalid cursor

    Error, cursor from other filters or another environment: workflows_list_instances: cursor does not match the current filters

    Parameters

    • definition

      string

      Restrict to instances of this workflow definition, by its name (for example 'article-review').

    • document

      GdrUri

      Only instances that reference this document, as the subject or from any field. Pass a global document reference (GDR), for example dataset:PROJECT_ID:DATASET_NAME:article-1.

    • includeCompleted

      boolean

      true includes completed and aborted instances. Defaults to false.

    • assignment

      {userId, roles?, states?}

      Filter to one person’s assignments. userId is the person’s Sanity user ID. roles lists their project role names, matched exactly. states lists the states to include: 'unrouted', 'routed', or 'held'. Defaults to all three.

    • limit

      number

      Results per page. Defaults to DEFAULT_LIST_LIMIT, 25. The operation doesn’t cap it, so set a maximum in your tool’s schema.

    • cursor

      string

      The next_cursor from the previous page. Pass the same filters and use the same environment.

  • getWorkflowState({engine}, input)(instanceId): Promise<ProjectedInstanceState>

    Returns everything actionable about one instance: the current stage, the subject, a summary of what the workflow waits on, recent history, and each in-scope activity on the current stage. For each activity, it reports:

    • Its classification: interactive, autonomous, off-system, or hybrid.
    • A completesWithoutCaller verdict: yes, no, or conditional. When it isn’t yes, waitsOn lines explain what it waits for.
    • The actions a caller can fire, each with an allowed or disabled verdict and its declared params.
    • The automations the engine fires on its own.

    Activities and actions that don’t apply to this stage visit or caller are left out.

    Read-only. Suggested tool name: workflows_get_state. Result: {instanceId, definition, workflowTitle?, currentStage, currentStageTitle?, done, subject?, activities, autonomy, recentHistory}.

    Error, unknown instance: [instance-not-found] Workflow instance dev.wf-instance.missing not found

    Parameters

    • instanceId

      string

      The workflow instance ID.

  • diagnoseWorkflow({engine}, input)(instanceId): Promise<ProjectedDiagnosis>

    Explains why an instance is or isn’t progressing. state is progressing, waiting, blocked, completed, aborted, or stuck. A stuck instance adds a cause. remediations are advisory, and no operation runs them.

    Read-only. Suggested tool name: workflows_diagnose. Result: {instanceId, state, summary, cause?, explanations?, remediations}.

    Parameters

    • instanceId

      string

      The workflow instance ID.

  • startWorkflow({engine}, input)(definition, version, initialFields, instanceId): Promise<StartWorkflowResult>

    Starts a deployed definition. Only definitions with startable: true start here, because child workflows only start from a parent. Every start requirement must pass. The engine runs the cascade before the result returns, so the instance can already be past its first stage.

    Pass your own instanceId to make a start safe to retry. If a start fails partway, retry it with the same ID. If the instance was created but its first automatic step failed, the result still holds the instance, with a startNotSettled note.

    Writes. Suggested tool name: workflows_start. Result: the getWorkflowState shape, plus startNotSettled?.

    Error, unknown definition: no deployed definition "nope" in this workflow environment — workflows_list_definitions shows what is deployed

    Error, undeclared field: workflow declares no field "bogus" — input fields: subject

    Parameters

    • definition

      string

      The definition’s name, as listWorkflowDefinitions lists it.

    • version

      number

      The deployed definition version to start from. Defaults to the highest.

    • initialFields

      Record<string, unknown>

      Values for the workflow’s input-sourced fields, keyed by field name. A doc.ref value is an object with a GDR id and a document type, for example {subject: {id: 'dataset:PROJECT_ID:DATASET_NAME:article-1', type: 'article'}}.

    • instanceId

      string

      The ID to start the instance under. Retrying with the same ID resumes that start instead of creating a second instance. Leave it out to generate a new ID.

  • fireWorkflowAction({engine}, input)(instanceId, activity, action, params): Promise<ProjectedInstanceState>

    Fires an action on an activity in the current stage, as an editor would, with values for the action’s declared params. Pick the activity and action from getWorkflowState. Actions that the engine fires on its own are listed as automations, and this operation rejects them. Transitions that become eligible run in the same call. A second call with the same input can fire again from the new stage.

    Writes. Suggested tool name: workflows_fire_action. Result: the getWorkflowState shape.

    Error, activity not in the current stage: [contract-violation] Activity "write" not found in current stage "review" of article-review

    Parameters

    • instanceId

      string

      The workflow instance ID.

    • activity

      string

      An activity in the current stage, as getWorkflowState returns it.

    • action

      string

      An action on that activity that getWorkflowState lists as allowed.

    • params

      Record<string, unknown>

      Values for the action’s declared params, keyed by param name. Required when the action declares a required param. The engine checks the values against the declared params and rejects the call if a required one is missing.

Authoring

  • WORKFLOW_AUTHORING_GUIDE(): string

    The guide an agent reads before it writes a definition, as one Markdown string. It covers the definition shape, GROQ condition built-ins, shorthand forms, the rules the validator enforces, and worked JSON examples, and it refers to the suggested tool names. Suggested tool name: workflows_get_authoring_guide.

  • validateWorkflowDefinitions({definitions})(definitions): ValidateDefinitionsResult

    Checks structure, cross-field rules, and GROQ syntax without reading the Content Lake. It’s synchronous and takes no engine. Validate a parent and its child workflows together. Each result is the desugared definition that would deploy, or a path-prefixed list of every problem.

    Deploy also checks duplicate names, spawn dependencies, resource bindings, project roles, and the reader-model acknowledgement. So a valid result doesn’t guarantee that a deploy succeeds. sanity-workflows definitions validate <file...> --json prints the same report.

    Read-only. Suggested tool name: workflows_validate_definition. Result: {valid, results: ({valid: true, definition} | {valid: false, error})[]}.

    Parameters

    • definitions

      readonly Record<string, unknown>[]

      Workflow definitions as JSON objects in authoring shape. A single workflow is a one-element array.

  • deployWorkflowDefinitions({engine}, input)(definitions, expectedMinReaderModel): Promise<DeployDefinitionsResult>

    Deploys definitions into the engine’s partition. Pass a parent and the children it spawns in one call, and the children deploy first. Deploys are create-only. Content identical to the latest version returns unchanged. Any change returns created with the next version, and a new name starts at version 1. Running instances keep the version they started under.

    It runs the same checks as validateWorkflowDefinitions, but throws one error that lists every definition that fails.

    Writes. Repeating a deploy with the same content changes nothing. Suggested tool name: workflows_deploy_definition. Result: {results: {name, version, status}[], deployId}.

    Error, invalid definition: definitions[0] ("x"): defineWorkflow("x") failed validation (3 issues):

    Error, acknowledgement below the floor: [reader-model-acknowledgement] Deployment acknowledges reader model 4; the submitted definitions require at least 10, and this writer supports acknowledgements through 11.

    Parameters

    • definitions

      readonly Record<string, unknown>[]

      Workflow definitions as JSON objects in authoring shape, the same values validateWorkflowDefinitions takes.

    • expectedMinReaderModel

      number

      The highest reader model that every runtime sharing this workflow resource already supports. Defaults to 4. Definitions with role-constrained assignment fields need 8, and singular assignment fields need 9. Required subject, doc.ref, or doc.refs fields need 10, and an abort block needs 11. Raise it only after you upgrade every Studio, CLI, agent host, Function, and other runtime. See Reader-model acknowledgement and Upgrade Workflows packages.

Embedding API

Agent hosts use these exports to build engines and clients, record telemetry, and render errors. The package root is the only entry point.

  • createWorkflowEngine({address, client, executionContext?, telemetry?})(): Engine

    Creates an engine bound to one resource and partition, without caching. A long-lived host can cache engines itself. The client’s token identifies the actor, so keep its authentication unchanged for the engine’s lifetime. executionContext adds an advisory host label to history entries, and telemetry takes the logger from createMcpSessionTelemetry.

    The engine accepts document references into any resource the client’s token can reach, and reads a subject’s title from the subject’s own resource. Scope that token to only the resources the agent should reach. For a narrower engine, build one with createEngine from @sanity/workflow-engine and your own resourceClients resolver, and pass it to the operations.

  • workflowClientConfig({resource, token?, base})(): Base & WorkflowClientPolicy & WorkflowResourceAddressing

    Builds the client config for engine traffic on top of your host’s own config, base. Pass {} if you have none. It keeps the requester, headers, and apiHost from base.

    It then sets the engine API version, useCdn: false, the published perspective, and the request-tag prefix sanity.workflows-mcp. For a dataset, it sets projectId and dataset. For any other resource, it sets resource. It removes the other addressing style, so a leftover value from base can’t point the client at another environment.

  • createMcpSessionTelemetry({clientFor, denied, nodeEnv?, sessionId?})(): McpSessionTelemetry

    Opt-in telemetry for an embedding host. Embedding the package doesn’t turn it on. clientFor(resource) returns an authenticated client. Create one per MCP session, and pass its logger to createWorkflowEngine. Call bind(resource) with each call’s workflow resource before the engine runs. The first dataset resource owns the session, and later calls don’t change it. Await flush() before end() when the session closes.

    Nothing is sent when denied is true, or when CI or DO_NOT_TRACK is set to a true value such as 1 or true. Otherwise, the account’s telemetry consent decides.

  • workflowErrorText(error)(): string

    The text a failed tool call should show: [<kind>] <message> for a WorkflowError, otherwise the message.

Each operation also exports its input type, such as StartWorkflowInput and ListWorkflowInstancesInput. Result types are ListWorkflowDefinitionsResult, ListWorkflowInstancesResult, ListWorkflowPartitionsResult, StartWorkflowResult, DeployDefinitionsResult, ProjectedDefinitionSummary, ProjectedDefinition, ProjectedInstanceSummary, ProjectedInstanceState, ProjectedActivity, ProjectedActionVerdict, ProjectedAutomation, ProjectedHistoryEntry, ProjectedSubject, ProjectedDiagnosis, ValidateDefinitionResult, and ValidateDefinitionsResult. See TypeDoc for their fields.

The package has one peer dependency, @sanity/workflow-engine, at the same version. It doesn’t depend on zod or @modelcontextprotocol/sdk, so your host chooses its own schema library and MCP SDK.

Was this page helpful?