15 KiB
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.
For the identifier rule behind source/capability names, see
structural_refs.md. In short: dotted qualified names
are display strings and compatibility input only; saved refs should be
structural.
The short version:
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 sourcewf.std: the local workflow standard library, including reusable nodes and reducerswf.mcp: local workflow helpers for interacting with MCP backendswf.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:
who owns this capability?
not:
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:
how should a graph use this thing?
instead of:
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
isErrorresult into workflow outcomes such asokanderror - map a provider-specific
statusfield into workflow outcomes such asneeds_input,done, andfailed - 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:
context7 -> context7.default
or later:
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:
{
"status": "needs_input",
"message": "pick one"
}
A workflow usually wants an explicit outcome:
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:
{
"ok": true,
"data": {
"items": [...]
}
}
while the graph wants:
{
"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/erroroutcomes
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:
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:
- inspect available sources
- inspect raw capabilities
- inspect existing workflow capabilities and saved wrappers
- call a workflow capability directly once
- inspect the normalized output and outcome
- author a workflow draft
- validate or patch the draft
- save the compiled workflow artifact
Drafts are the preferred authoring format for this loop. See
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
- inspects one workflow capability, applies its
wf.workflow.call_capability- executes one such capability once for direct testing
- returns
qualified_name,source_id,kind, optionaldeployment_id,outcome,output, anddiagnostics
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:
- Use
wf.admin.list_sourcesto find capability owners and preview source contents. - Use
wf.workflow.list_capabilitieswithsource_idorqueryto find workflow-ready node specs. - Use
wf.workflow.inspect_capabilityonly for the selected capability's full schema contract. - Use
wf.workflow.call_capabilitywith a plain input object to test the selected contract once before composing it into a draft.
For the copy-less wrapper authoring path, use:
wf.workflow.inspect_capabilityto inspect the source capability and reviewwrapper_hints.wf.workflow.create_draft_workspace_from_capabilityto create a patchable draft workspace from those hints.- 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, andwf.workflow.set_draft_routeto fix low-confidence hints or explicitmissing_decisions. JSON-RPC clients useworkflow.draft_workspaces.set_workflow_output_bindingsfor the same canonical replacement. wf.workflow.validate_draft_workspaceto refresh diagnostics.wf.workflow.create_wrapper_from_workspaceto save the wrapper artifact.wf.workflow.call_capabilitywithworkflow.<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:
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.CapabilitySourcealready owns buckets for tools, node specs, prompts, and resources- connection sources represent upstream MCP snapshots
wf.stdowns local reusable workflow node specs and reducerswf.mcpowns workflow-facing MCP runtime helpers- discovered upstream tools can already become workflow node specs
- saved artifacts can be tagged with
kind="workflow"orkind="wrapper" wf.workflow.call_capabilitycan execute one planner-visible workflow capability directly and return normalizedoutcome/outputwf.workflow.list_capabilitiesandwf.workflow.inspect_capabilityproject saved wrapper artifacts as workflow capabilities under source idworkflow
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.