Files
lda-wf/docs/workflow_capabilities.md
T

453 lines
15 KiB
Markdown

# Workflow Capabilities
This document separates the things an MCP-facing platform can expose from the
things a workflow should usually consume.
For the short operator/client workflow using these concepts, see
[`wf_mcp_operator_manual.md`](wf_mcp_operator_manual.md).
For the identifier rule behind source/capability names, see
[`structural_refs.md`](structural_refs.md). In short: dotted qualified names
are display strings and compatibility input only; saved refs should be
structural.
The short version:
```text
raw capability != workflow capability
```
An MCP tool may be callable and still be a poor workflow node until someone
wraps it into a cleaner workflow-facing contract.
## Core Terms
### Source
A source is a named owner of capabilities.
Examples:
- `everything.default`: an upstream MCP connection source
- `wf.std`: the local workflow standard library, including reusable nodes and
reducers
- `wf.mcp`: local workflow helpers for interacting with MCP backends
- `wf.admin`: privileged control-plane capabilities
A source is **not** defined by one protocol. MCP connection sources commonly
own tools, prompts, and resources. Local sources may own only node specs, or
node specs plus documentation prompts/resources, or only privileged admin
tools.
The source answers:
```text
who owns this capability?
```
not:
```text
what protocol transported it?
```
### Raw Capability
A raw capability is what a source directly provides.
Examples:
- an upstream MCP tool
- an upstream MCP prompt
- an upstream MCP resource
- a local admin tool
- a local standard-library helper
Raw capabilities are useful for direct inspection and direct invocation, but
their interface is shaped by the provider. That provider-facing interface may
not be pleasant or safe for workflow composition.
### Workflow Capability
A workflow capability is a workflow-facing `NodeSpec`.
Today, a `NodeSpec` has:
- one input schema
- one output schema
- one or more named outcomes
It does **not** currently support a different output schema per outcome.
The workflow capability answers:
```text
how should a graph use this thing?
```
instead of:
```text
how did the original provider happen to expose this thing?
```
### Wrapper / Adapter Artifact
A wrapper artifact is a saved reusable bridge from a raw capability to a
workflow capability. In storage it is not a second artifact family: it is a
`WorkflowArtifact` with `kind="wrapper"`.
Examples:
- convert a raw `isError` result into workflow outcomes such as `ok` and
`error`
- map a provider-specific `status` field into workflow outcomes such as
`needs_input`, `done`, and `failed`
- strip a provider result envelope and expose only the data a graph should use
- normalize awkward parameter names or output shapes for repeated workflow use
A wrapper artifact is useful when a human or LLM has learned once how to make a
provider capability workflow-friendly and should not have to rediscover that
interpretation every time.
Expected properties:
- saved
- inspectable
- reusable
- dependency-aware
- directly testable before use in a graph
### Workflow Artifact
A workflow artifact is a saved graph.
It may depend on:
- workflow capabilities from local sources
- generated workflow wrappers around upstream tools
- saved wrapper artifacts authored earlier
- other saved workflow artifacts later, once graph-as-node is real
### Deployment
A deployment binds a saved workflow artifact to concrete runtime sources.
This is where logical source names can resolve to concrete accounts or
connections, for example:
```text
context7 -> context7.default
```
or later:
```text
crm -> salesforce.work
```
## Why Raw MCP Tools Are Not Automatically Good Workflow Nodes
MCP tools are general callable capabilities. Workflow nodes need contracts that
compose well inside graphs.
Common mismatch examples:
### Status Encoded In Output
A provider may return:
```json
{
"status": "needs_input",
"message": "pick one"
}
```
A workflow usually wants an explicit outcome:
```text
needs_input
```
so the graph can branch without every downstream node re-parsing provider
status strings.
### Error Encoded By Transport Convention
An MCP tool call has protocol-level success/error handling. A workflow often
wants errors represented as explicit graph outcomes that must be wired.
### Provider Envelopes
A provider may return:
```json
{
"ok": true,
"data": {
"items": [...]
}
}
```
while the graph wants:
```json
{
"items": [...]
}
```
MCP text content blocks are a common provider-envelope case. When an MCP tool
returns exactly one text content block, the SDK keeps the raw `content` list and
also exposes a convenience `text` field. Wrapper hints prefer `text` so a string
state field can map the actual text without writing the raw content-block list.
Multiple content blocks or non-text content still require explicit wrapper
decisions.
### Human-Friendly Versus Graph-Friendly Inputs
A raw tool may be good for interactive human use but awkward for stateful graph
composition. A wrapper can expose the small stable interface the graph actually
needs.
## Generated Versus Authored Workflow Capabilities
Some workflow capabilities can be generated mechanically:
- a simple MCP tool whose result already maps cleanly to one output schema and
`ok` / `error` outcomes
Others should be authored and saved:
- tools with domain-specific status fields
- tools whose raw shape is too provider-centric
- tools whose result needs durable semantic interpretation
The platform should support both.
## Capability Steps In Drafts
A capability-backed draft step records its selected capability in `use`, plus
metadata, canonical input bindings, output bindings, and routes. Creation can
set these common fields in one revision-checked operation:
```bash
wf draft add capability report --revision 3 --step publish \
--capability local.report.publish \
--description "Publish report" --retry 2 --timeout-seconds 30 \
--input state.report.title=request.title \
--value request.format='"markdown"' --route ok=__end__
```
Focused update changes only fields explicitly supplied by the caller. Omitted
metadata is preserved; explicit null through JSON-RPC/MCP, or the matching CLI
`--clear-*` flag, removes that override. Supplying canonical input bindings
replaces the complete ordered list atomically. This update preserves `use`,
routes, and outputs. Use their dedicated operations, or remove/add when
changing the selected capability.
The transport operation names are:
- JSON-RPC: `workflow.draft_workspaces.add_step_from_capability`
- JSON-RPC: `workflow.draft_workspaces.update_capability_step`
- MCP: `wf.workflow.add_step_from_capability`
- MCP: `wf.workflow.update_capability_step`
## Authoring Loop
A client authoring workflows, including an LLM client, should be able to:
1. inspect available sources
2. inspect raw capabilities
3. inspect existing workflow capabilities and saved wrappers
4. call a workflow capability directly once
5. inspect the normalized output and outcome
6. author a workflow draft
7. validate or patch the draft
8. save the compiled workflow artifact
Drafts are the preferred authoring format for this loop. See
[`workflow_drafts.md`](workflow_drafts.md). Raw workflow plans remain an escape
hatch for advanced clients and compiler outputs.
Saved wrapper artifacts can be called with a deployment id when they use logical
source names. The deployment supplies the concrete source bindings for that
test call, matching the way `run_deployment` resolves a full saved workflow.
Reusable wrapper and workflow artifacts should be authored against logical
source names by default. If an author discovers a concrete capability such as
`everything.default.echo`, the saved artifact should normally depend on a
logical reference such as `everything.echo` plus a required capability entry.
The deployment is responsible for binding `everything` to `everything.default`,
`everything.work`, or any other compatible concrete source.
This is especially important for LLM-authored workflows. The LLM should not
need to infer dependency metadata by parsing formatted names, and a saved
artifact should not accidentally become tied to the first account used during
exploration.
That direct-call surface is different from:
- calling the raw upstream MCP tool
- running a full workflow artifact
- using privileged admin tools
It exists so authors can test the workflow-facing contract before composing it.
The workflow-facing MCP surface now has dedicated discovery tools for the
authoring loop:
- `wf.workflow.list_capabilities`
- lists compact paged enabled planner-visible workflow-ready node spec
summaries, with optional query/source filtering
- also includes saved wrapper artifacts under source id `workflow`
- includes the owning `source_id`, outcomes, and top-level input/output field
names, but not full schemas
- `wf.workflow.inspect_capability`
- returns one full workflow capability contract with schemas and outcomes
- includes `wrapper_hints`, a conservative scaffold for creating a wrapper
draft from the inspected capability
- `wf.workflow.create_draft_workspace_from_capability`
- inspects one workflow capability, applies its `wrapper_hints`, and creates a
patchable draft workspace
- returns the hints it used so clients can immediately patch uncertain maps,
schemas, or routes by revision
- `wf.workflow.call_capability`
- executes one such capability once for direct testing
- returns `qualified_name`, `source_id`, `kind`, optional `deployment_id`,
`outcome`, `output`, and `diagnostics`
These are authoring-plane tools. They do not replace the privileged
`wf.admin.list_sources` source inventory, and older planner-catalog projections
may remain while callers migrate to the workflow-facing surface.
Recommended discovery order:
1. Use `wf.admin.list_sources` to find capability owners and preview source
contents.
2. Use `wf.workflow.list_capabilities` with `source_id` or `query` to find
workflow-ready node specs.
3. Use `wf.workflow.inspect_capability` only for the selected capability's full
schema contract.
4. Use `wf.workflow.call_capability` with a plain input object to test the
selected contract once before composing it into a draft.
For the copy-less wrapper authoring path, use:
1. `wf.workflow.inspect_capability` to inspect the source capability and review
`wrapper_hints`.
2. `wf.workflow.create_draft_workspace_from_capability` to create a patchable
draft workspace from those hints.
3. Focused patch helpers such as `wf.workflow.set_step_input_bindings`,
`wf.workflow.set_step_output_bindings`,
`wf.workflow.set_workflow_output_bindings`, their compatibility map adapters,
and `wf.workflow.set_draft_route` to fix low-confidence hints or explicit
`missing_decisions`.
JSON-RPC clients use
`workflow.draft_workspaces.set_workflow_output_bindings` for the same
canonical replacement.
4. `wf.workflow.validate_draft_workspace` to refresh diagnostics.
5. `wf.workflow.create_wrapper_from_workspace` to save the wrapper artifact.
6. `wf.workflow.call_capability` with `workflow.<artifact_id>.v<version>` to
test the saved wrapper.
See `examples/mcp_wrapper_authoring_flow.py` for the same flow through the
Python handler layer.
## Wrapper Authoring Hints
`wf.workflow.inspect_capability` returns `wrapper_hints` for planner-visible
capabilities. These hints are scaffolding for draft creation, not semantic
guarantees.
Declared capability outcomes are authoritative and are preserved by default.
Boolean output fields may appear as `outcome_candidates` when they have
control-like names such as `success`, `error`, `approved`, or `has_more`, but
they are never wired automatically. A wrapper author must explicitly confirm
whether those fields should become routing conditions.
`confidence` is intentionally coarse:
- `high`: simple object input/output schemas and no missing decisions.
- `medium`: usable scaffold with candidate decisions, such as boolean outcome
candidates.
- `low`: missing or nested output choices require explicit authoring.
`missing_decisions` is a typed list of decisions the author should resolve
before saving a wrapper. MCP clients should show these prominently rather than
treating the scaffold as complete.
`create_draft_workspace_from_capability` also returns `next_actions`.
This is advisory guidance for clients that cannot easily read the full docs.
It summarizes whether the scaffold is safe-looking enough to validate, which
tool to call next, and concrete patch examples for common missing decisions.
`patch_examples` may include top-level output projection bindings, which use
`path` / `target` (not step-level `source` / `target`).
`next_actions.can_save_now` is not enforced. A caller can still save a low
confidence draft, but the field exists to make that risk explicit.
`next_actions` is advisory guidance, not validation authority. It gives MCP
clients a compact "what should I call next?" pointer, while diagnostics,
artifact validation, deployment validation, and runtime status remain the
source of truth.
Deployment validation and run lifecycle responses also expose `next_actions`.
For runnable deployments this points to `wf.workflow.run_deployment`; for
unrunnable deployments it points back to validation after the caller repairs
bindings, sources, or schema drift. Failed runs never suggest reading an
unbounded trace; trace guidance always uses a bounded `trace_range`.
## Relationship To Capability Sources
Sources own capability kinds:
```text
tools
node_specs
reducers
prompts
resources
```
Possible later additions may include full workflow artifacts as first-class
projected capability kinds, but they should not erase the raw versus
workflow-facing distinction. Saved wrapper artifacts are already projected as
workflow capabilities because they have a node-like callable boundary today.
Examples:
- `everything.default.tools["search"]`
- raw upstream capability
- `everything.default.node_specs["everything.default.search"]`
- generated workflow-facing wrapper, if the raw tool maps cleanly
- `user.crm.lookup_customer`
- saved authored wrapper around a raw upstream tool, if the original contract
needed interpretation
## Current System Truth
Today:
- `wf_platform.CapabilitySource` already owns buckets for tools, node specs,
prompts, and resources
- connection sources represent upstream MCP snapshots
- `wf.std` owns local reusable workflow node specs and reducers
- `wf.mcp` owns workflow-facing MCP runtime helpers
- discovered upstream tools can already become workflow node specs
- saved artifacts can be tagged with `kind="workflow"` or `kind="wrapper"`
- `wf.workflow.call_capability` can execute one planner-visible workflow
capability directly and return normalized `outcome` / `output`
- `wf.workflow.list_capabilities` and `wf.workflow.inspect_capability` project
saved wrapper artifacts as workflow capabilities under source id `workflow`
Not yet implemented:
- per-outcome output schemas
- graph-as-node for saved workflows
These are separate next steps. The current model should leave room for them
without pretending they already exist.