@sanity/workflow-mcp 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +403 -0
- package/README.md +104 -39
- package/bin/run.js +0 -5
- package/dist/_chunks-es/index.js +710 -0
- package/dist/index.cjs +665 -486
- package/dist/index.d.cts +125 -95
- package/dist/index.d.ts +125 -95
- package/dist/index.js +2 -519
- package/dist/stdio.d.ts +14 -10
- package/dist/stdio.js +119 -68
- package/package.json +9 -6
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/stdio.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# @sanity/workflow-mcp
|
|
2
|
+
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ff3eb5b: **BREAKING:** the library export is now host-neutral raw tool defs instead of engine-bound impls. `buildTools`, `BuiltTools`, `ToolImpl`, and `ToolDescriptor` are gone; consume `WORKFLOW_TOOLS` (per-tool `{name, description, inputSchema, annotations, handler}`) plus `registerWorkflowTools(server, getContext)` for MCP hosts and `toolInputJsonSchema(def)` for JSON-schema consumers. Tool input schemas are zod shapes (single source of truth); handlers take a lazy `{engine, access?}` context thunk supplied by the host per call, so identity and engine construction live entirely in the host layer. The stdio server keeps its env contract and system-actor attribution, with three behaviour deltas: an invalid `status` on `list_workflow_instances` now errors instead of silently defaulting to `in_flight`; input-validation failures over MCP read as the SDK's InvalidParams rendering instead of the old tool-prefixed text; and a client omitting `arguments` entirely now gets an InvalidParams error where the old host defaulted to `{}`.
|
|
8
|
+
- adf32f7: **BREAKING:** `buildTools(engine)` no longer takes an options bag — the `access` identity pin is gone. Tool calls act as the user behind the engine client's token, and the stdio host declares `executionContext {kind: 'mcp', id: 'workflow-mcp'}` at engine construction.
|
|
9
|
+
- e4c2405: New `deploy_workflow_definition` tool — deploys an authored workflow definition
|
|
10
|
+
into the addressed workflow environment, completing the authoring loop (guide →
|
|
11
|
+
validate → deploy) without a human round-trip to the CLI. The tool desugars the
|
|
12
|
+
authoring-shape JSON through `defineWorkflow` before touching the engine and
|
|
13
|
+
returns the engine's per-definition outcome `{name, version, status}`. Deploys
|
|
14
|
+
are create-only and content-addressed: content identical to the latest deployed
|
|
15
|
+
version is an `unchanged` no-op, any change mints the next version, and a
|
|
16
|
+
deployed version is never patched. Agents self-correct against
|
|
17
|
+
`validate_workflow_definition` first; deploy failures surface as tool errors.
|
|
18
|
+
- 5cb0850: `diagnose_workflow` returns `explanations` — per unsatisfied exit transition, its insight summary ("the "Publish" transition: Needs: «Legal approved» must be true"), giving agents the concrete unblock story alongside the verdict.
|
|
19
|
+
- 7ffdc77: **BREAKING:** The server is org-authed and every instance-operating tool call now addresses its own workflow environment — all boot-time project/dataset/resource/tag pinning is gone.
|
|
20
|
+
- The stdio host's boot config is `SANITY_AUTH_TOKEN` (org-level, required) plus optional `SANITY_API_HOST`. `SANITY_PROJECT_ID`, `SANITY_DATASET`, `WORKFLOW_TAG`, `WORKFLOW_RESOURCE_TYPE`, and `WORKFLOW_RESOURCE_ID` are no longer read.
|
|
21
|
+
- `list_workflow_instances`, `get_workflow_state`, `diagnose_workflow`, and `fire_action` gain two required parameters: `workflow_resource` (a resource-shaped GDR, `<type>:<id>`) and `tag`. There is no default environment — a call without an address is a validation error, never a guess. The two authoring tools stay address-free.
|
|
22
|
+
- The stdio host builds one engine per distinct `(workflow_resource, tag)` from the org token, cached for the life of the process.
|
|
23
|
+
- New export `workflowAddressFromInput(input)` (with `WorkflowEnvironmentAddress`) — the host side of the address contract: embedding hosts read the address from the tool input in their `getContext` seam and build or reuse the engine for it.
|
|
24
|
+
|
|
25
|
+
- 0709ab1: **BREAKING:** `recentHistory[].type` now carries the engine's display title (e.g. "Stage entered") instead of the raw discriminator (`stageEntered`) — a consumer branching on the old discriminator values must switch to the display titles (or read the summary). The summary switch is also exhaustive now: `aborted`, `opApplied`, and `fieldQueryDiscarded` entries get real one-line summaries instead of falling through to the raw enum.
|
|
26
|
+
|
|
27
|
+
The state projection also reads definition snapshots through the engine's `parseDefinitionSnapshot` (a corrupt snapshot fails with the instance id attached; `done` derivation keeps its explicit tolerance), error text flows through the engine's `errorMessage`, and disabled-action phrasing covers the engine's new `instance-aborted` verdict.
|
|
28
|
+
|
|
29
|
+
- 15337f1: Adoption telemetry and request-tag attribution for the stdio server. Every tool invocation logs `Editorial Workflows MCP Tool Called` with the tool name and a success flag only — never tool arguments or results. The shell uses the standard consent recipe (CI or `DO_NOT_TRACK` deny without a network call; otherwise the account status from `GET /intake/telemetry-status`), posts batches to `/intake/batch`, flushes on the default interval plus an awaited flush on shutdown, and feeds the same logger into `createEngine` so engine adoption events flow from agent-driven usage. Clients now carry the `sanity.workflows-mcp` request-tag prefix — deliberately outside the zero-rated `sanity.workflows.*` family, so agent-driven traffic bills; per-request tags are bare ops, landing as `sanity.workflows-mcp.<op>` in request logs.
|
|
30
|
+
- 91540de: **BREAKING:** every shell-defined telemetry event name now carries the product name — `Editorial Workflows CLI Command Executed`, `Editorial Workflows Definition Shared` (workflow-cli), `Editorial Workflows MCP Tool Called` (workflow-mcp), `Editorial Workflows Session Started` (workflow-react) — replacing the bare `Workflow <X>` forms, which never shipped. Event names are matched verbatim downstream.
|
|
31
|
+
|
|
32
|
+
Shell context parity: every self-owned telemetry shell now attaches its execution environment, following the sanity CLI and Studio precedents. User identity stays sink-side — the intake service resolves the sender from the authenticated session on every event.
|
|
33
|
+
- CLI: `updateUserProperties` with the sanity CLI's set (cliVersion, platform, architecture, runtime, runtime version, projectId, dataset) plus `surface: 'cli'` and `orgId` resolved once per invocation from the projects API (deadline-capped, degrade-soft — a failed lookup omits the property, never blocks a command).
|
|
34
|
+
- SDK: Studio-style context enrichment on every posted event — `surface: 'sdk'`, package version, environment, userAgent, project coordinates.
|
|
35
|
+
- MCP: `updateUserProperties` on store initialization — `surface: 'mcp'`, package version, runtime info, the addressed project coordinates; the server handshake now reports the real package version instead of `0.0.0`.
|
|
36
|
+
- React hosts (Studio, SDK, custom): the session layer pushes `{surface: host}` into the HOST's own session stream via `updateUserProperties` — Studio-hosted events get an explicit surface marker without touching Studio's transport.
|
|
37
|
+
- The `Editorial Workflows Definition Shared` marker carries the deploy run's `deployId`, so donated definitions group with the deploy events that minted them.
|
|
38
|
+
|
|
39
|
+
- d6e92ed: Now published with public npm access (previously restricted) — installable without `@sanity` org membership.
|
|
40
|
+
- 66135fd: New `list_workflow_definitions` tool — lists the deployed workflow definitions
|
|
41
|
+
(latest version per name) with `name`, `title`, optional `description`,
|
|
42
|
+
`version`, and `startable`, so an agent can discover what workflow types exist
|
|
43
|
+
without guessing definition names.
|
|
44
|
+
- 23cf131: Render the engine's new `subject-permission-denied` disabled reason — the actor lacks write permission on a subject document in that subject's own resource.
|
|
45
|
+
- ced40e4: Request tags move to workflows-owned families with the runtime as the segment: `sanity.workflows.cli`, `sanity.workflows.studio`, `sanity.workflows.sdk` — never another team's family (`sanity.cli.*`, `sanity.studio.*`) — and platform analysis zero-rates `sanity.workflows.*`. The MCP server deliberately sits outside that family as `sanity.workflows-mcp` (no dot boundary), so agent-driven traffic bills. Under any workflows-family prefix the engine's tag wrapper strips the now-redundant `workflow.` root from every relative op tag — stamped fallback and explicit alike — so traffic lands as `sanity.workflows.cli.deploy` with exactly one workflows segment; on raw consumer clients and under foreign host prefixes the root stays, since there it is what identifies the traffic. The Studio adapter's engine and resource-router clients and the SDK telemetry shell's intake client now set their family prefix themselves instead of riding the host's.
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- 6c21009: Correct public TSDoc and README claims that contradicted the implementation: CLI auth precedence (an explicit `SANITY_AUTH_TOKEN` wins over the `sanity login` session), the real CLI verbs (`workflow.diagnose`, `workflow.availableActions`) and document types (`sanity.workflow.instance` / `sanity.workflow.definition`), guard predicates reading the delta-mode `before()`/`after()` natives, glob patterns supporting multiple `*`s, where `$can` actually binds (requirements and the editField gate, not action filters alone), the editField target contract (any-scope match with activity-first precedence), guard observation fan-out across the engine dataset plus each watched subject's dataset, the object value expression being op-payload-only, and the MCP descriptor / SDK observer export pointers. The deploy-time `$can` rejection error now states where `$can` actually binds (the caller-bound projection: action filters, requirements, editable predicates) instead of claiming action filters are its only home.
|
|
50
|
+
- 444b0e6: The authoring guide now teaches that `$params` is rejected in action filters too — the filter decides whether an action is enabled before the caller supplies args, so `$params` binds only in the action's effect `bindings` and where-op `where`s.
|
|
51
|
+
- 4ca17bb: The authoring guide now teaches that caller-scoped vars (`$actor`/`$assigned`/`$can`/`$params`) belong in action filters only — the engine advances transitions and resolves activity gates with no caller bound, and deploy rejects caller reads at those sites.
|
|
52
|
+
- 5143707: The instance-history summary renders an `effectCompleted` entry as `settled (<status>)` instead of `completed (<status>)` — effect runs can now settle as `cancelled` (abort), and "completed (cancelled)" read as a contradiction.
|
|
53
|
+
- 12cdbd6: The authoring guide now teaches `$fields` visibility: reads must name a field entry declared in the site's lexical window (transition filters can't see activity fields — stage-scope the decisions transitions route on), and the validator rejects undeclared reads, unreachable stages, and unresolvable `fieldRead` sources.
|
|
54
|
+
- ced40e4: `createTelemetryIntake` / `isTelemetryEnvDenied` — the standard Sanity-intake consent/transport recipe (account status from `GET /intake/telemetry-status` with resolved-only caching, `POST /intake/batch` `{projectId, batch}` transport, the `telemetry.*` request tags, and the CI/`DO_NOT_TRACK` environment denial) now lives once in the engine package as structural mirrors of `@sanity/telemetry`'s store options — still no runtime dependency on that package. The SDK and MCP shells consume it instead of carrying twin copies. `WorkflowConfig` additionally accepts an optional `telemetry` logger the CLI routes every event to in place of its built-in shell, and the mirrored event type's `description` is optional to match the real package, so events built with the real `defineEvent` flow through the mirrored logger seam.
|
|
55
|
+
- dc64968: Published tarballs no longer include sourcemaps (whose `sourcesContent` embedded the complete TypeScript source) or inline code comments in the compiled JS — license markers and bundler annotations (`/*@__PURE__*/`-style tree-shaking hints) are preserved. Types and TSDoc in `.d.ts` files ship unchanged, and the tarballs shrink accordingly (workflow-engine: ~3× smaller). Each tarball now ships its `CHANGELOG.md`, so release notes reach consumers who can install the package but cannot see the repository.
|
|
56
|
+
- 114dcae: Ship the MIT `LICENSE` file in the published package. The package.json already declared `"license": "MIT"`, but the license text itself was missing from the tarball.
|
|
57
|
+
- Updated dependencies [4ca17bb]
|
|
58
|
+
- Updated dependencies [5143707]
|
|
59
|
+
- Updated dependencies [0709ab1]
|
|
60
|
+
- Updated dependencies [6bbf3ca]
|
|
61
|
+
- Updated dependencies [9f5a40f]
|
|
62
|
+
- Updated dependencies [d192d68]
|
|
63
|
+
- Updated dependencies [6c21009]
|
|
64
|
+
- Updated dependencies [109f900]
|
|
65
|
+
- Updated dependencies [109f900]
|
|
66
|
+
- Updated dependencies [444b0e6]
|
|
67
|
+
- Updated dependencies [2511bb5]
|
|
68
|
+
- Updated dependencies [d4fd8e6]
|
|
69
|
+
- Updated dependencies [30ed0e8]
|
|
70
|
+
- Updated dependencies [2834704]
|
|
71
|
+
- Updated dependencies [2511bb5]
|
|
72
|
+
- Updated dependencies [2511bb5]
|
|
73
|
+
- Updated dependencies [2511bb5]
|
|
74
|
+
- Updated dependencies [4607d59]
|
|
75
|
+
- Updated dependencies [52f6024]
|
|
76
|
+
- Updated dependencies [a8f1cb9]
|
|
77
|
+
- Updated dependencies [adf32f7]
|
|
78
|
+
- Updated dependencies [5cb0850]
|
|
79
|
+
- Updated dependencies [70a136c]
|
|
80
|
+
- Updated dependencies [91540de]
|
|
81
|
+
- Updated dependencies [8455fbb]
|
|
82
|
+
- Updated dependencies [325c2d1]
|
|
83
|
+
- Updated dependencies [d6e92ed]
|
|
84
|
+
- Updated dependencies [6c21009]
|
|
85
|
+
- Updated dependencies [7ffdc77]
|
|
86
|
+
- Updated dependencies [325c2d1]
|
|
87
|
+
- Updated dependencies [7ffdc77]
|
|
88
|
+
- Updated dependencies [ced40e4]
|
|
89
|
+
- Updated dependencies [9b1b40d]
|
|
90
|
+
- Updated dependencies [8c319b2]
|
|
91
|
+
- Updated dependencies [dc64968]
|
|
92
|
+
- Updated dependencies [23cf131]
|
|
93
|
+
- Updated dependencies [d192d68]
|
|
94
|
+
- Updated dependencies [15337f1]
|
|
95
|
+
- Updated dependencies [2d1a8e1]
|
|
96
|
+
- Updated dependencies [12cdbd6]
|
|
97
|
+
- Updated dependencies [437b544]
|
|
98
|
+
- Updated dependencies [ced40e4]
|
|
99
|
+
- @sanity/workflow-engine@0.15.0
|
|
100
|
+
|
|
101
|
+
## 0.5.1
|
|
102
|
+
|
|
103
|
+
### Patch Changes
|
|
104
|
+
|
|
105
|
+
- 83d5924: Tool descriptions identify a workflow definition by its `name` (formerly "workflow type id"), matching the engine's vocabulary; the README points instance starting at the workflow CLI's `start` command.
|
|
106
|
+
- 7a655bd: The authoring guide no longer teaches decision-as-failure. The review-loop example records the editor's decision in a stage-scoped field the transition filters read (both actions resolve `done`), and the modeling defaults spell out that decisions are fields while `failed` is reserved for work that genuinely could not complete.
|
|
107
|
+
- ad9b6f6: The authoring guide's condition-variable list now includes `$self`/`$stage`/`$parent`/`$ancestors` and the new `$effectStatus` effect-drained read, and names the engine's exported `CONDITION_VARS` inventory as the source of truth. The guide remains a curated view (it deliberately omits `$effects` and the spawn-bound vars), and a drift test now pins every non-excluded inventory var to the guide text.
|
|
108
|
+
- df00bbb: The client's API version now comes from the engine's exported `ENGINE_API_VERSION` pin instead of a hand-copied literal, so every client bound to the engine stays on one version by construction.
|
|
109
|
+
- 5c69928: **BREAKING:** runtime API coherence pass — one result shape, one session noun, nameable engine args.
|
|
110
|
+
- `DispatchResult` → `OperationResult`, and its `fired` flag → `changed` with one meaning honored by every verb: this call changed the instance's workflow state (`false` = successful no-op — an idempotent re-fire, a tick that wrote nothing, a `setStage` already at the target, an abort of an already-terminal instance; failures still throw). `tick` derives it from the instance revision, so a persisted activity-gate flip that didn't unlock a transition still reports `changed: true`. `abortInstance` documents its always-`0` cascade (a terminal instance doesn't cascade; ancestor propagation reports on the ancestors).
|
|
111
|
+
- `startInstance` now returns `OperationResult` instead of a bare `WorkflowInstance` — its cascade is finally observable. Reach the instance via `result.instance`.
|
|
112
|
+
- Engine argument types are exported and nameable: the exported `*Args` shapes are now exactly what `Engine` methods take, and the raw `workflow.*` namespace takes `<Verb>Args & EngineScopeArgs` (the new exported scope type: `client` / `tag` / `workflowResource` / `resourceClients`). New named args for the read/housekeeping verbs: `InstanceRefArgs`, `ChildrenArgs`, `GuardsForDefinitionArgs`, `InstancesForDocumentArgs`, `QueryArgs`, `QueryInScopeArgs`, `FindPendingEffectsArgs`, `DrainEffectsArgs`, `SessionArgs`. `EvaluateArgs` is likewise scope-free now.
|
|
113
|
+
- Verb-surface parity between `Engine` and `workflow.*` is exact and documented: `Engine.evaluateInstance` → `Engine.evaluate` (matching `workflow.evaluate`); engine-only members (`session`, `subscriptionDocumentsForInstance`, `drainEffects`, `verifyDeployedDefinitions`) and the namespace-only `workflow.permissions` are called out as deliberate.
|
|
114
|
+
- One noun for the reactive session handle: `engine.instance(doc, opts?)` → `engine.session({instance, access?, grantsFromPath?})` (args object — no positional outlier), still returning `InstanceSession`; workflow-react's `WorkflowInstanceController` → `WorkflowSession`; the studio/sdk hooks `useWorkflowInstance` → `useWorkflowSession`. "Instance" now always means the document.
|
|
115
|
+
- Test bench mirrors the engine: `bench.startInstance` returns `OperationResult`, and the `Bench*Args` types are the engine-bound shapes minus `access`.
|
|
116
|
+
- CLI reports follow the rename: the report objects for `fire-action` / `move-stage` / `abort` (and `fire-action`'s `--json` payload) now say `changed` instead of `fired`.
|
|
117
|
+
|
|
118
|
+
- 113a796: README: the `createEngine` sample passes the shipped `tag` argument instead of the nonexistent `tags`, and the POC warning banner is replaced with a plain statement of how the tool surface is developed (against the eval harness).
|
|
119
|
+
- 2f081a3: Render a `mutation-guard-denied` verdict from its `denied` payload via the engine's shared `deniedGuardLabels` (guard name over id), and prefix `isError` tool results with a thrown `WorkflowError`'s stable `kind` (e.g. `[mutation-guard-denied] …`) so the model can branch on the discriminant without parsing the message.
|
|
120
|
+
- 6ee9068: The authoring guide says a field's `initialValue` seeds the field (not "the slot"), matching the engine's single field vocabulary.
|
|
121
|
+
- Updated dependencies [a0443c4]
|
|
122
|
+
- Updated dependencies [a0443c4]
|
|
123
|
+
- Updated dependencies [ad9b6f6]
|
|
124
|
+
- Updated dependencies [83d5924]
|
|
125
|
+
- Updated dependencies [7a655bd]
|
|
126
|
+
- Updated dependencies [d9c8179]
|
|
127
|
+
- Updated dependencies [0a7979a]
|
|
128
|
+
- Updated dependencies [df00bbb]
|
|
129
|
+
- Updated dependencies [5c69928]
|
|
130
|
+
- Updated dependencies [2f081a3]
|
|
131
|
+
- Updated dependencies [df00bbb]
|
|
132
|
+
- Updated dependencies [113a796]
|
|
133
|
+
- Updated dependencies [df00bbb]
|
|
134
|
+
- Updated dependencies [2829643]
|
|
135
|
+
- Updated dependencies [6ee9068]
|
|
136
|
+
- Updated dependencies [2829643]
|
|
137
|
+
- Updated dependencies [a0443c4]
|
|
138
|
+
- @sanity/workflow-engine@0.14.0
|
|
139
|
+
|
|
140
|
+
## 0.5.0
|
|
141
|
+
|
|
142
|
+
### Minor Changes
|
|
143
|
+
|
|
144
|
+
- ba1ba64: `fire_action` can now pass action `params`.
|
|
145
|
+
|
|
146
|
+
The MCP `fire_action` tool gains an optional `params` property — a free-form
|
|
147
|
+
object keyed by param name — alongside `instance_id`/`activity`/`action`. It is
|
|
148
|
+
forwarded to `engine.fireAction({ ... params })`, so an action that declares a
|
|
149
|
+
required param (e.g. article-review's `reject`, whose `note` is `required: true`)
|
|
150
|
+
can be driven through MCP. Absent `params` behaves exactly as before; a present
|
|
151
|
+
non-object value is rejected with `fire_action: params must be an object`.
|
|
152
|
+
|
|
153
|
+
### Patch Changes
|
|
154
|
+
|
|
155
|
+
- 5bbc3f0: Internal refactor: functions that took 3+ positional parameters now take a single named options object. No public API or behavior change — the package's exported surface (and, for the CLI, its commands and output) is unchanged.
|
|
156
|
+
- f417252: Fix `get_workflow_state` dropping the `mutation-guard-denied` disabled reason. `formatDisabledReason` keyed its copy off a loose `Record<string, string>` that had no entry for the engine's `mutation-guard-denied` `DisabledReason` kind, so a guard-blocked action silently degraded to the kebab-cased enum (`"mutation guard denied"`) and lost the denying `guardIds` entirely. It's now an exhaustive `switch` over the typed `DisabledReason` union — a new engine kind fails the build instead of degrading silently, and the guard case renders its `guardIds`.
|
|
157
|
+
|
|
158
|
+
Also de-duplicates two internal idioms the structural clone detector misses: the tool-input parsers share `asObject`/`requireString` guards (error strings unchanged), and `tools.test.ts` uses a shared `seededBench()` helper in place of the repeated per-describe `beforeEach` trio.
|
|
159
|
+
|
|
160
|
+
- Updated dependencies [416d4c9]
|
|
161
|
+
- Updated dependencies [4dfe72a]
|
|
162
|
+
- Updated dependencies [5bbc3f0]
|
|
163
|
+
- Updated dependencies [0bf7503]
|
|
164
|
+
- Updated dependencies [6e51913]
|
|
165
|
+
- Updated dependencies [3229f41]
|
|
166
|
+
- @sanity/workflow-engine@0.13.0
|
|
167
|
+
|
|
168
|
+
## 0.4.0
|
|
169
|
+
|
|
170
|
+
### Minor Changes
|
|
171
|
+
|
|
172
|
+
- 5b10863: **BREAKING:** Compositional Field kinds, Sanity-aligned scalar names, and `todoList`/`notes` as sugar.
|
|
173
|
+
|
|
174
|
+
Lean into Sanity's model so any value shape composes instead of minting a bespoke array kind per row layout:
|
|
175
|
+
- **Add `object` and `array` kinds** — `{ type: 'object', name, fields: [...] }` and `{ type: 'array', name, of: [...] }`, mirroring Sanity's `object.fields` / `array.of`. `fields`/`of` are lightweight recursive field shapes (`{ type, name, title?, description?, fields?/of? }`) — no slot-level `source`/`editable`/`required`, since a sub-field's value comes from the parent value. Object/array entries persist their declared shape on the instance, and writes are validated against it.
|
|
176
|
+
- **Scalar names aligned to Sanity** — drop the `value.` prefix: `value.string`→`string`, `value.number`→`number`, `value.boolean`→`boolean`, `value.dateTime`→`datetime`, `value.url`→`url`, `value.actor`→`actor`. **Adds** `text` (multiline) and `date` (date-only, `YYYY-MM-DD`).
|
|
177
|
+
- **Add the singular `assignee` kind** — one `Assignee` (`{type:'user',id} | {type:'role',role}`), the singular of `assignees`. It's the value shape for a single user-or-role assignee (e.g. a `todoList` row's owner); it is NOT wired into the engine's `$assigned` / inbox gate (that remains the task-level plural `assignees`).
|
|
178
|
+
- **`todoList` and `notes` are now authoring sugar** that desugar to an `array` kind: `todoList` → `array of { label, status, assignee?, dueDate? }`, `notes` → `array of { body, actor, at }` (the `actor`/`at` sub-fields match the `audit` op's stamp names, so an `audit`-appended row lands in the declared shape). **The bespoke `checklist` kind is removed** — a plain checklist is a `todoList` used with `{label, status}` only.
|
|
179
|
+
|
|
180
|
+
The `ChecklistItem` / `NoteItem` value types are removed (the kinds they typed are gone); `FieldShape` is now exported. `@sanity/workflow-examples` and the `@sanity/workflow-mcp` authoring guide are updated to the new names and kinds.
|
|
181
|
+
|
|
182
|
+
- d850aef: **BREAKING:** Workflow definitions are now immutable and content-addressed — the author no longer writes a `version`.
|
|
183
|
+
|
|
184
|
+
A definition's `version` (and a new `contentHash` fingerprint) are stamped onto the deployed _document_ at deploy time, derived from the content itself — the same envelope as `_id`/`_type`/`tag`. `defineWorkflow` no longer accepts a `version` (the authoring schema rejects it), and the `WorkflowDefinition` type no longer carries one.
|
|
185
|
+
|
|
186
|
+
`deployDefinitions` is now create-only: it hashes the authored content and compares it to the latest deployed version of that name. Identical content is a no-op (`unchanged`); any change mints the next version (`created`). It never patches a deployed version in place, so a definition can no longer change out from under the instances pinned to it. (Engine-side only — true immutability of the deployed document still requires a Content Lake guard; the engine enforces nothing.) Deploying two definitions with the same name in one batch is now rejected.
|
|
187
|
+
|
|
188
|
+
Instances pin `pinnedContentHash` alongside `pinnedVersion`, so a consumer can detect a deployed definition that has drifted from what an instance started on.
|
|
189
|
+
|
|
190
|
+
API changes:
|
|
191
|
+
- `defineWorkflow({ version })` / `WorkflowDefinition.version` removed — drop `version` from authored definitions.
|
|
192
|
+
- `DeployDefinitionResult.status` drops `'updated'` (now `'created' | 'unchanged'`); `DiffEntry.status` drops `'update'` (now `'create' | 'unchanged'`) — a content change is always a new version.
|
|
193
|
+
- `isDefinitionUnchanged` removed; the content fingerprint is now exposed as `hashDefinitionContent`. `DeployedDefinition` is exported for consumers reading deployed documents.
|
|
194
|
+
- Spawn refs (`task.subworkflows.definition.version`) are unchanged — you can still pin another definition's deployed version.
|
|
195
|
+
|
|
196
|
+
- 6d0f74c: Publish `@sanity/workflow-mcp` to npm as a restricted package, consistent with the other workflow packages. It was previously marked `private`, so it got version-bumped but never published — `npx @sanity/workflow-mcp` now resolves to the stdio MCP server bin.
|
|
197
|
+
- e5a862a: **BREAKING:** Split the field `source` union into two purpose-built types.
|
|
198
|
+
|
|
199
|
+
One `Source` union served two unrelated jobs — how a field _initialises_ vs what
|
|
200
|
+
an op _writes_ — and an illegal mix was caught by a runtime `throw "…is a
|
|
201
|
+
field-entry origin, not a value"` rather than the type system. It is now two types:
|
|
202
|
+
- **`FieldSource`** — a field's seed recipe, exposed as the field's optional
|
|
203
|
+
`initialValue` property (renamed from `source`). Arms: `input` (renamed from
|
|
204
|
+
`init`), `query`, `literal`, `fieldRead`.
|
|
205
|
+
- **`ValueExpr`** — an op's `value` payload (property unchanged). Arms:
|
|
206
|
+
`literal`, `param`, `actor`, `now`, `self`, `stage`, `fieldRead`, `object`.
|
|
207
|
+
|
|
208
|
+
Authoring changes:
|
|
209
|
+
- Rename the field property `source` → `initialValue`; it is now **optional**.
|
|
210
|
+
- The `write` arm is **dropped** — an absent `initialValue` is working memory
|
|
211
|
+
(the slot starts empty and an op fills it later), the common default.
|
|
212
|
+
- The `init` arm is renamed to `input`.
|
|
213
|
+
- The exported `Source` type is replaced by `FieldSource` and `ValueExpr`.
|
|
214
|
+
|
|
215
|
+
The runtime field-entry-origin `throw` is gone: the split makes that state
|
|
216
|
+
unrepresentable.
|
|
217
|
+
|
|
218
|
+
**Also removes the `query` field _kind_ (the untyped escape hatch).** There is
|
|
219
|
+
no longer an `unknown`-valued field — every field declares a concrete kind, and
|
|
220
|
+
"computed" is just a concrete kind + `initialValue: {type:'query'}`. A
|
|
221
|
+
query-sourced field's GROQ result is validated against its declared kind:
|
|
222
|
+
- conforming results are stored (with `resolvedAt` stamped — now driven by the
|
|
223
|
+
`query` _source_, present on any kind, not the old `query` kind);
|
|
224
|
+
- a non-conforming result is **discarded** (the field falls to its default) and
|
|
225
|
+
a new `fieldQueryDiscarded` history entry is logged — fail-soft for runtime
|
|
226
|
+
lake data, rather than the previous hard throw. `FieldKind`, `FieldValueMap`,
|
|
227
|
+
`ResolvedFieldEntry` (`resolvedAt` is now optional) and `InitialFieldValue`
|
|
228
|
+
change accordingly.
|
|
229
|
+
|
|
230
|
+
Field `initialValue` lives only on deployed definitions (never on instances) and
|
|
231
|
+
the shared/value discriminator strings are unchanged, so this is a type-level
|
|
232
|
+
break only — redeploy definitions, no instance data migration.
|
|
233
|
+
|
|
234
|
+
- 22690bb: Add two authoring tools to the MCP surface so an agent can turn a description
|
|
235
|
+
into a workflow definition. `get_workflow_authoring_guide` returns the DSL shape,
|
|
236
|
+
the GROQ condition built-ins, the sugars, modeling defaults (e.g. rejections loop
|
|
237
|
+
back rather than dead-ending), and two worked JSON examples;
|
|
238
|
+
`validate_workflow_definition` runs the same structural, cross-field, and GROQ
|
|
239
|
+
checks as deploy and returns either the desugared definition (exactly what would
|
|
240
|
+
deploy) or a path-prefixed list of every problem. The calling agent generates the
|
|
241
|
+
definition and self-corrects against the validator — neither tool deploys, a human
|
|
242
|
+
deploys the validated definition via the CLI. Both tools are engine-independent.
|
|
243
|
+
Covered by two new evals that drive a real model through the guide → validate
|
|
244
|
+
loop: `author-from-description` (explicit prompt — proves the loop converges)
|
|
245
|
+
and `author-good-shape` (vague prompt — proves the guide's modeling defaults
|
|
246
|
+
steer the model to a good shape: a decline loops back, one terminal stage).
|
|
247
|
+
|
|
248
|
+
### Patch Changes
|
|
249
|
+
|
|
250
|
+
- Updated dependencies [5b10863]
|
|
251
|
+
- Updated dependencies [ae1434e]
|
|
252
|
+
- Updated dependencies [d850aef]
|
|
253
|
+
- Updated dependencies [9519c76]
|
|
254
|
+
- Updated dependencies [9233cd3]
|
|
255
|
+
- Updated dependencies [3ea4953]
|
|
256
|
+
- Updated dependencies [e5a862a]
|
|
257
|
+
- @sanity/workflow-engine@0.12.0
|
|
258
|
+
|
|
259
|
+
## 0.3.0
|
|
260
|
+
|
|
261
|
+
### Minor Changes
|
|
262
|
+
|
|
263
|
+
- 128caa7: Ship a runnable stdio bin so the server can be launched directly (e.g. `npx
|
|
264
|
+
@sanity/workflow-mcp`) without a wrapping `tsx` invocation. The boot path moves
|
|
265
|
+
into a new `./stdio` package entry (`runStdioServer`), built by pkg-utils
|
|
266
|
+
alongside the library; `bin/run.js` is a thin shebang shim over the compiled
|
|
267
|
+
output, and the dev entry shares the same boot function. The library entry (`.`)
|
|
268
|
+
stays free of the MCP transport dependency, so embedding `buildTools` in another
|
|
269
|
+
host doesn't pull in stdio plumbing.
|
|
270
|
+
|
|
271
|
+
### Patch Changes
|
|
272
|
+
|
|
273
|
+
- Updated dependencies [3b8f3de]
|
|
274
|
+
- @sanity/workflow-engine@0.11.0
|
|
275
|
+
|
|
276
|
+
## 0.2.0
|
|
277
|
+
|
|
278
|
+
### Minor Changes
|
|
279
|
+
|
|
280
|
+
- 552081b: Add a `diagnose_workflow` MCP tool. It wraps the engine's `diagnose` verb to
|
|
281
|
+
answer "why isn't this instance moving?": it returns a verdict (`state`:
|
|
282
|
+
progressing, waiting, blocked, completed, aborted, or stuck), a one-line
|
|
283
|
+
plain-English `summary`, and — when stuck — the structured `cause` and the
|
|
284
|
+
`remediations` that would unstick it. A pure read; the remediations are
|
|
285
|
+
advisory (none are executable through the MCP server today), so it tells an
|
|
286
|
+
agent whether an instance is healthy (waiting on a human, or will advance on
|
|
287
|
+
its own) or genuinely stuck (a failed effect/task, a dead-end transition) and
|
|
288
|
+
what a human or admin tool would need to do about it.
|
|
289
|
+
|
|
290
|
+
### Patch Changes
|
|
291
|
+
|
|
292
|
+
- Updated dependencies [fae3553]
|
|
293
|
+
- @sanity/workflow-engine@0.10.0
|
|
294
|
+
|
|
295
|
+
## 0.1.5
|
|
296
|
+
|
|
297
|
+
### Patch Changes
|
|
298
|
+
|
|
299
|
+
- Updated dependencies [2e4c980]
|
|
300
|
+
- Updated dependencies [fd6b866]
|
|
301
|
+
- @sanity/workflow-engine@0.9.0
|
|
302
|
+
|
|
303
|
+
## 0.1.4
|
|
304
|
+
|
|
305
|
+
### Patch Changes
|
|
306
|
+
|
|
307
|
+
- Updated dependencies [c298ee2]
|
|
308
|
+
- Updated dependencies [8131dd5]
|
|
309
|
+
- Updated dependencies [8bf467e]
|
|
310
|
+
- @sanity/workflow-engine@0.8.0
|
|
311
|
+
|
|
312
|
+
## 0.1.3
|
|
313
|
+
|
|
314
|
+
### Patch Changes
|
|
315
|
+
|
|
316
|
+
- 129258c: Add task `requirements` — a readiness axis for visible-but-not-yet-executable tasks, distinct from ACL and guards.
|
|
317
|
+
|
|
318
|
+
A task definition can now declare `requirements: { <name>: <groq-condition> }`: preconditions over the rendered scope that gate _executability_ without affecting visibility. While a requirement is unmet the task stays visible, and every action on it is reported disabled with a new `requirements-unmet` verdict that names the unmet requirements; `TaskEvaluation.unmetRequirements` summarises the same set at the task level so a consumer can explain why the whole task is gated. Requirements are all-of (express any-of inside a single condition), evaluated in the same scope as `filter`, and — like every engine-side check — advisory; the lake remains the enforcement point.
|
|
319
|
+
|
|
320
|
+
Diagnostics gain a `blocked` state: an active task held by an unmet requirement is reported as visible-but-not-yet-executable (healthy and transient), distinct from `stuck`, and a requirements-held task no longer masks an actionable sibling in the `waiting` verdict.
|
|
321
|
+
|
|
322
|
+
The CLI `fire-action` listing and the MCP server's disabled-reason copy both render the new reason; the CLI `diagnose` command renders the new `blocked` state.
|
|
323
|
+
|
|
324
|
+
- Updated dependencies [ef927bc]
|
|
325
|
+
- Updated dependencies [32f5e64]
|
|
326
|
+
- Updated dependencies [df7582f]
|
|
327
|
+
- Updated dependencies [ed4174c]
|
|
328
|
+
- Updated dependencies [129258c]
|
|
329
|
+
- @sanity/workflow-engine@0.7.0
|
|
330
|
+
|
|
331
|
+
## 0.1.2
|
|
332
|
+
|
|
333
|
+
### Patch Changes
|
|
334
|
+
|
|
335
|
+
- Updated dependencies [c6d1762]
|
|
336
|
+
- @sanity/workflow-engine@0.6.0
|
|
337
|
+
|
|
338
|
+
## 0.1.1
|
|
339
|
+
|
|
340
|
+
### Patch Changes
|
|
341
|
+
|
|
342
|
+
- dbc4afa: Move the instance-reasoning behind the CLI's read commands into the engine so every consumer shares one implementation rather than re-deriving it. The engine now exports the stuck-instance classifier (`diagnoseInstance` with `Diagnosis` / `StuckCause` / `DiagnoseInput`, plus `abortReason`, `openStage`, `assigneesOf`, `diagnoseInputFromEvaluation`), the available-actions projection (`availableActions` with `AvailableAction`), and definition-diff classification (`diffEntry` / `computeDiffEntries` with `DiffEntry` / `DeployTarget`) — all reusing the engine's own doc-id minting and no-op verdict. Two instance-id-keyed verbs compose this with `evaluate`: `workflow.diagnose` and `workflow.availableActions` (also on the `Engine` wrapper).
|
|
343
|
+
|
|
344
|
+
The CLI's `diagnose`, `fire-action`, `deploy`, and `definition diff` commands keep only their rendering and now call the engine for the reasoning, and the MCP's `get_workflow_state` projects its action verdicts through the same shared `availableActions`. No behavioural change to the CLI or MCP surfaces.
|
|
345
|
+
|
|
346
|
+
- Updated dependencies [dbc4afa]
|
|
347
|
+
- Updated dependencies [e2a6d5d]
|
|
348
|
+
- Updated dependencies [aa87a1c]
|
|
349
|
+
- @sanity/workflow-engine@0.5.0
|
|
350
|
+
|
|
351
|
+
## 0.1.0
|
|
352
|
+
|
|
353
|
+
### Minor Changes
|
|
354
|
+
|
|
355
|
+
- fcf938a: Migrate to the new workflow authoring DSL: name identity (`name` /
|
|
356
|
+
`initialStage` / `currentStage` / `instance.definition`), bare state kinds,
|
|
357
|
+
type-tagged sources, bare op types with `{scope, state}` targets, GROQ-string
|
|
358
|
+
conditions and effect bindings, `predicates` as a name→GROQ map, explicit task
|
|
359
|
+
`activation`, action `status` sugar, claim pairs and audit ops, and
|
|
360
|
+
`subworkflows` fan-out. The example definitions showcase the new sugar; the
|
|
361
|
+
CLI and MCP projections read the new stored shapes (terminal = stage with no
|
|
362
|
+
transitions); the React controller's `fireAction` takes `task` instead of
|
|
363
|
+
`taskId`; the MCP wire names things what they are — `definition` (the
|
|
364
|
+
definition's `name`) on the `list_workflow_instances` filter and instance
|
|
365
|
+
projections, and `task` / `action` (names, not ids) on `fire_action` and
|
|
366
|
+
the per-task projections, replacing `workflow_id` / `task_id` /
|
|
367
|
+
`action_id`.
|
|
368
|
+
|
|
369
|
+
### Patch Changes
|
|
370
|
+
|
|
371
|
+
- Updated dependencies [4b5f59a]
|
|
372
|
+
- Updated dependencies [fcf938a]
|
|
373
|
+
- Updated dependencies [6c4e0a0]
|
|
374
|
+
- Updated dependencies [12e23b0]
|
|
375
|
+
- Updated dependencies [12e23b0]
|
|
376
|
+
- @sanity/workflow-engine@0.4.0
|
|
377
|
+
|
|
378
|
+
## 0.0.2
|
|
379
|
+
|
|
380
|
+
### Patch Changes
|
|
381
|
+
|
|
382
|
+
- Updated dependencies [41ddd80]
|
|
383
|
+
- Updated dependencies [0c6ccde]
|
|
384
|
+
- @sanity/workflow-engine@0.3.0
|
|
385
|
+
|
|
386
|
+
## 0.0.1
|
|
387
|
+
|
|
388
|
+
### Patch Changes
|
|
389
|
+
|
|
390
|
+
- eff2a77: Fix the stdio MCP server identifying itself to clients as the placeholder `workflow-slop`. It now reports its real name, `workflow-mcp`, in both the `Server` handshake and the startup log.
|
|
391
|
+
- f63cab0: Fix subject-title enrichment crashing the tools for any instance that has a subject document. `fetchSubjectTitles` read subject docs through the engine's tag-scoped `query`, which rejects any query missing the `$engineTags` filter — but subject docs (e.g. articles) are ordinary content and aren't engine-tagged, so the read threw and took down the whole `list_workflow_instances` / `get_workflow_state` / `fire_action` call. Subject docs are now read directly off `engine.client` (outside the tag scope, with access delegated to the client's credential), and a failed lookup degrades to a bare `subject.ref` instead of failing the tool call.
|
|
392
|
+
- Updated dependencies [d04cbea]
|
|
393
|
+
- Updated dependencies [870188e]
|
|
394
|
+
- Updated dependencies [a4f6d81]
|
|
395
|
+
- Updated dependencies [eff2a77]
|
|
396
|
+
- Updated dependencies [ce6960d]
|
|
397
|
+
- Updated dependencies [dff88ff]
|
|
398
|
+
- Updated dependencies [782272a]
|
|
399
|
+
- Updated dependencies [2b800a8]
|
|
400
|
+
- Updated dependencies [0432670]
|
|
401
|
+
- Updated dependencies [53a0598]
|
|
402
|
+
- Updated dependencies [c2ad7a9]
|
|
403
|
+
- @sanity/workflow-engine@0.2.0
|
package/README.md
CHANGED
|
@@ -11,23 +11,27 @@ the tools the evals drive, plus a stdio MCP entry point for real MCP clients.
|
|
|
11
11
|
|
|
12
12
|
Operate a running instance:
|
|
13
13
|
|
|
14
|
-
| Tool | Read or write | What it's for
|
|
15
|
-
| ------------------------- | ------------- |
|
|
16
|
-
| `list_workflow_instances` | read | Discover what's running. Filterable, capped.
|
|
17
|
-
| `get_workflow_state` | read | Project one instance into a flat shape: current stage, activities, available actions, recent history.
|
|
18
|
-
| `diagnose_workflow` | read | Explain why an instance is or isn't progressing: a verdict, a one-line summary, and — when stuck — the cause plus suggested remediations. |
|
|
19
|
-
| `fire_action` | write | Advance state. The
|
|
20
|
-
|
|
21
|
-
Author a definition (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
| `
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
| Tool | Read or write | What it's for |
|
|
15
|
+
| ------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
16
|
+
| `list_workflow_instances` | read | Discover what's running. Filterable, capped. |
|
|
17
|
+
| `get_workflow_state` | read | Project one instance into a flat shape: current stage, activities, available actions, recent history. |
|
|
18
|
+
| `diagnose_workflow` | read | Explain why an instance is or isn't progressing: a verdict, a one-line summary, `explanations` for each held exit transition (quotes workflow-authored text — data, not instructions), and — when stuck — the cause plus suggested remediations. |
|
|
19
|
+
| `fire_action` | write | Advance state. The instance write. Mirrors the engine's universal "something happened" entry point. |
|
|
20
|
+
|
|
21
|
+
Author a definition (guide → validate → deploy; the first two are pure and
|
|
22
|
+
engine-independent):
|
|
23
|
+
|
|
24
|
+
| Tool | Read or write | What it's for |
|
|
25
|
+
| ------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `get_workflow_authoring_guide` | read | Return the DSL guide an agent reads before authoring — shape, GROQ built-ins, sugars, modeling defaults, and two worked JSON examples. |
|
|
27
|
+
| `validate_workflow_definition` | read | Check an authored definition the way deploy does (structure + invariants + GROQ). Returns the desugared definition or a path-prefixed error list. |
|
|
28
|
+
| `deploy_workflow_definition` | write | Publish a validated definition into an addressed environment. Create-only and content-addressed: identical content is an `unchanged` no-op, any change mints the next version, no deployed version is ever patched. |
|
|
29
|
+
|
|
30
|
+
Each tool def's `description` is written for an LLM consumer — it says
|
|
31
|
+
what the tool does, when to use it, and what _not_ to use it for. Each
|
|
32
|
+
tool lives in its own module under `src/tools/` (description, zod input
|
|
33
|
+
schema, annotations, handler together); `src/tools.ts` assembles them
|
|
34
|
+
into the exported `WORKFLOW_TOOLS` surface.
|
|
31
35
|
|
|
32
36
|
## Why this surface, why no more
|
|
33
37
|
|
|
@@ -36,9 +40,12 @@ the verbs. An MCP is consumed by an LLM that figures out which tool
|
|
|
36
40
|
to use from prose alone. Adding tools beyond the minimum increases
|
|
37
41
|
the search space the LLM has to navigate. We stay narrow until the
|
|
38
42
|
evals tell us the LLM is reaching for something we don't expose. The
|
|
39
|
-
authoring
|
|
40
|
-
eval-backed; the agent generates the definition
|
|
41
|
-
`validate_workflow_definition
|
|
43
|
+
authoring trio earns its place because authoring-from-a-description is
|
|
44
|
+
eval-backed; the agent generates the definition, self-corrects against
|
|
45
|
+
`validate_workflow_definition`, then publishes with
|
|
46
|
+
`deploy_workflow_definition` — safe to hand an agent because deploy is
|
|
47
|
+
create-only: it can never patch or clobber what's already deployed, and
|
|
48
|
+
running instances keep the definition version they started under.
|
|
42
49
|
|
|
43
50
|
## Run the stdio server
|
|
44
51
|
|
|
@@ -56,31 +63,93 @@ it's `bin/run.js`, and once the package is published this is what
|
|
|
56
63
|
node packages/workflow-mcp/bin/run.js
|
|
57
64
|
```
|
|
58
65
|
|
|
59
|
-
Both share the one boot path in `src/stdio.ts
|
|
60
|
-
|
|
61
|
-
`
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
Both share the one boot path in `src/stdio.ts`. The server is org-authed,
|
|
67
|
+
so boot config is just the org-level `SANITY_AUTH_TOKEN` (required) and
|
|
68
|
+
`SANITY_API_HOST` (optional) — no project, dataset, resource, or tag.
|
|
69
|
+
Every instance-operating tool call names its own workflow environment
|
|
70
|
+
instead: a `workflow_resource` parameter (a resource-shaped GDR,
|
|
71
|
+
`<type>:<id>` — e.g. `dataset:abc123.production`, `media-library:mlXyz`)
|
|
72
|
+
plus a `tag`. There is no default environment — a call without an address
|
|
73
|
+
is a validation error, never a guess — and the host builds one engine per
|
|
74
|
+
distinct `(resource, tag)`, cached for the life of the process. The `dev`
|
|
75
|
+
script loads the env from the repo-root `.env`, so copy `.env.example` to
|
|
76
|
+
`.env` to get started.
|
|
77
|
+
|
|
78
|
+
### Telemetry
|
|
79
|
+
|
|
80
|
+
The stdio server reports adoption telemetry to Sanity: one
|
|
81
|
+
`Editorial Workflows MCP Tool Called` event per tool invocation carrying the
|
|
82
|
+
tool name and a success
|
|
83
|
+
flag — never tool arguments or results — plus the engine's own adoption
|
|
84
|
+
events. Nothing is sent unless the account's telemetry consent resolves to
|
|
85
|
+
granted (the account-wide status `sanity telemetry status` shows). Set
|
|
86
|
+
`DO_NOT_TRACK=1` to opt a process out; CI environments (`CI` set) never
|
|
87
|
+
send. Separately from consent, every Content Lake request the server issues
|
|
88
|
+
carries a request tag under the `sanity.workflows-mcp` prefix (composing as
|
|
89
|
+
`sanity.workflows-mcp.<op>`) for request-log attribution — deliberately outside
|
|
90
|
+
the zero-rated `sanity.workflows.*` family, so agent-driven traffic bills.
|
|
66
91
|
|
|
67
92
|
## API
|
|
68
93
|
|
|
94
|
+
The package is a host-neutral library: it never builds an engine, reads
|
|
95
|
+
the environment, or handles auth. A host supplies those per call through
|
|
96
|
+
a context thunk — that's what lets one core serve both a boot-time stdio
|
|
97
|
+
process and a hosted per-request server.
|
|
98
|
+
|
|
99
|
+
For an MCP host, register everything with the bundled glue. The
|
|
100
|
+
`getContext` seam receives the request `extra` (a hosted server derives
|
|
101
|
+
auth from it) and the parsed tool input, which carries the per-call
|
|
102
|
+
workflow environment address — `workflowAddressFromInput` extracts it so
|
|
103
|
+
the host can build (or reuse) the engine for that environment:
|
|
104
|
+
|
|
69
105
|
```ts
|
|
70
|
-
import {
|
|
71
|
-
import {
|
|
106
|
+
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
107
|
+
import {createEngine, resourceGdr, type Engine} from '@sanity/workflow-engine'
|
|
108
|
+
import {registerWorkflowTools, workflowAddressFromInput} from '@sanity/workflow-mcp'
|
|
109
|
+
|
|
110
|
+
const server = new McpServer({name: 'my-host', version: '1.0.0'})
|
|
111
|
+
|
|
112
|
+
// One engine per addressed environment, reused across calls.
|
|
113
|
+
// clientFor(resource): your host's @sanity/client for that resource —
|
|
114
|
+
// the bundled stdio host's version lives in `src/engine-cache.ts`.
|
|
115
|
+
const engines = new Map<string, Engine>()
|
|
116
|
+
|
|
117
|
+
registerWorkflowTools(server, (_extra, input) => {
|
|
118
|
+
const {workflowResource, tag} = workflowAddressFromInput(input)
|
|
119
|
+
const key = `${resourceGdr(workflowResource)} ${tag}`
|
|
120
|
+
const engine =
|
|
121
|
+
engines.get(key) ?? createEngine({client: clientFor(workflowResource), workflowResource, tag})
|
|
122
|
+
engines.set(key, engine)
|
|
123
|
+
return {engine}
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Hosts with their own wrappers (error handling, telemetry, response
|
|
128
|
+
envelopes) iterate the raw defs instead — and non-MCP consumers derive
|
|
129
|
+
the JSON-schema descriptor from each def's zod shape:
|
|
72
130
|
|
|
73
|
-
|
|
74
|
-
|
|
131
|
+
```ts
|
|
132
|
+
import {toolInputJsonSchema, WORKFLOW_TOOLS} from '@sanity/workflow-mcp'
|
|
75
133
|
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
134
|
+
for (const def of WORKFLOW_TOOLS) {
|
|
135
|
+
// def.name, def.description, def.inputSchema (zod), def.annotations
|
|
136
|
+
// def.handler(contextThunk, input) → plain projected data
|
|
137
|
+
// toolInputJsonSchema(def) → `input_schema` for the Anthropic Messages API
|
|
138
|
+
}
|
|
80
139
|
```
|
|
81
140
|
|
|
141
|
+
A handler's context is `{engine, access?}` — `access` overrides the
|
|
142
|
+
engine's per-call actor resolution for hosts whose token can't resolve a
|
|
143
|
+
real user (the stdio host pins a system actor this way). The thunk is
|
|
144
|
+
lazy: the guide and validate tools never invoke it, so a per-request host
|
|
145
|
+
never builds an engine for them.
|
|
146
|
+
|
|
82
147
|
## Open gaps
|
|
83
148
|
|
|
149
|
+
- **No environment discovery.** Every operational call names its workflow
|
|
150
|
+
environment, but there is no tool to enumerate the environments that
|
|
151
|
+
exist across the org — the agent (or its operator prompt) must know the
|
|
152
|
+
resource + tag. Stays out until the evals demand it.
|
|
84
153
|
- **No `set_stage` tool.** Admin override; intentionally omitted from
|
|
85
154
|
the LLM surface until we have an eval case that needs it (and a
|
|
86
155
|
story for the actor identity that would authorise the override).
|
|
@@ -93,7 +162,3 @@ const {descriptors, impls} = buildTools(engine)
|
|
|
93
162
|
- **No subscription / streaming.** MCP supports it; we haven't needed
|
|
94
163
|
it yet. Pull-based polling via `get_workflow_state` covers the eval
|
|
95
164
|
cases.
|
|
96
|
-
- **No `deploy_workflow_definition`.** `validate_workflow_definition` is
|
|
97
|
-
the authoring tool; a human deploys the validated definition via the
|
|
98
|
-
CLI. Deploy is a Content Lake write that inherits the actor-identity
|
|
99
|
-
story, so it stays out of the LLM surface for now.
|
package/bin/run.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Production entry for the published package: loads the compiled boot
|
|
3
|
-
// path from ./dist/stdio.js (built by pkg-utils via the `./stdio`
|
|
4
|
-
// export). The dev counterpart, bin/workflow-mcp.ts, loads the TS
|
|
5
|
-
// source through tsx instead.
|
|
6
|
-
|
|
7
2
|
import {runStdioServer} from '../dist/stdio.js'
|
|
8
3
|
|
|
9
4
|
await runStdioServer()
|