18 KiB
wf_mcp End-To-End Runbook
This runbook shows one complete successful path through the platform:
configured connection
-> refreshed catalog
-> source/capability discovery
-> direct capability test
-> saved workflow artifact
-> saved deployment
-> validated deployment
-> deployment run
The example uses an imaginary upstream MCP connection:
demo.personal
which exposes one workflow-ready capability:
demo.personal.echo_tool
The saved artifact uses the logical source alias demo, so the same workflow can
later be deployed against demo.work or another compatible account.
Minimal Lifecycle Summary
The shortest dependable lifecycle is:
list_capabilities
inspect_capability
create_draft_workspace_from_capability
patch_draft_workspace
validate_draft_workspace
create_artifact_from_workspace
save_deployment
validate_deployment
run_deployment
inspect_run or read_run_trace only when needed
resume_run only when status is interrupted
delete_deployment for temporary deployments
Do not expect a newly saved workflow to appear as a new MCP tool in an existing
client session. Use run_deployment and call_capability as stable front doors.
0. Know The Three Names
This example deliberately uses three related names:
| Name | Meaning |
|---|---|
demo.personal |
concrete configured connection/source |
demo.personal.echo_tool |
concrete discovered workflow capability |
demo.echo_tool |
logical capability ref stored in the saved artifact |
The deployment later binds:
{
"demo": "demo.personal"
}
That is the important separation between reusable workflow definition and concrete account choice.
1. Add Or Confirm The Connection
If the connection does not exist yet:
tool: wf.admin.add_connection
arguments:
{
"connection_id": "demo.personal",
"server": "demo",
"account": "personal",
"metadata": {
"transport": "stdio",
"command": "python",
"args": ["path/to/demo_server.py"]
}
}
If it already exists, inspect the configured set:
tool: wf.admin.list_connections
arguments: {}
Expected idea:
[
{
"id": "demo.personal",
"server": "demo",
"account": "personal",
"enabled": true
}
]
2. Refresh The Upstream Catalog
tool: wf.admin.refresh_connection_catalog
arguments:
{
"connection_id": "demo.personal"
}
This is the discovery step. It asks the upstream MCP server what it currently exposes and updates the stored catalog snapshot.
After this succeeds, these views become useful:
tool: wf.admin.get_catalog
arguments: {}
for the raw upstream MCP snapshot, and:
tool: wf.admin.get_planner_catalog
arguments: {}
for the planner-facing node-spec view.
Prefer the progressive-discovery tools below for ordinary use; full catalogs can be large.
3. Discover The Source
First list compact source summaries:
tool: wf.admin.list_sources
arguments:
{
"limit": 20
}
Then inspect only the source you care about:
tool: wf.admin.inspect_source
arguments:
{
"source_id": "demo.personal"
}
You want to confirm:
- the source is enabled
- it is planner-visible
- it owns the expected generated node spec / workflow capability
4. Discover And Inspect The Workflow Capability
Find the workflow-ready capability:
tool: wf.workflow.list_capabilities
arguments:
{
"source_id": "demo.personal",
"query": "echo",
"limit": 20
}
The list response is intentionally compact. It includes source_id, outcomes,
and top-level input_fields / output_fields, but not full JSON schemas.
Then inspect its full contract:
tool: wf.workflow.inspect_capability
arguments:
{
"qualified_name": "demo.personal.echo_tool"
}
This is where you learn:
- input schema
- output schema
- declared outcomes
- whether it is async
- whether it is actually a good workflow-facing contract
5. Test The Capability Directly
Before composing a workflow, call the node contract once:
tool: wf.workflow.call_capability
arguments:
{
"qualified_name": "demo.personal.echo_tool",
"payload": {
"text": "hello"
}
}
Expected shape:
{
"outcome": "ok",
"output": {
"echoed": "hello"
}
}
This is the authoring REPL step. It tests the workflow-facing contract, not just the raw upstream MCP tool call.
6. Save A Workflow Artifact
Create a one-node workflow that:
- accepts
input.text - calls the discovered echo capability
- stores
echoedinto workflow state - ends on the node's
okoutcome
tool: wf.workflow.create_artifact_from_draft
arguments:
{
"artifact_id": "echo",
"version": 1,
"title": "Echo",
"outcomes": ["completed"],
"source_bindings": {
"demo": "demo.personal"
},
"draft": {
"name": "echo",
"input_schema": {
"type": "object",
"properties": {
"text": {
"type": "string"
}
},
"required": ["text"]
},
"state_schema": {
"type": "object",
"properties": {
"echoed": {
"type": "string",
"reducer": "wf.std.replace"
}
}
},
"output_schema": {
"type": "object",
"properties": {
"echoed": {
"type": "string"
}
},
"required": ["echoed"]
},
"start": "echo",
"steps": {
"echo": {
"use": "demo.personal.echo_tool",
"input": [
{
"target": "text",
"path": "input.text"
}
],
"output": [
{
"source": "echoed",
"target": "state.echoed"
}
]
}
},
"routes": {
"echo": {
"ok": "__end__"
}
}
}
}
Important behavior:
- because
source_bindingssays"demo": "demo.personal", the saved artifact is normalized to logical node refdemo.echo_tool - the saved dependency contract records what was observed from
demo.personal.echo_toolat creation time - the draft is compiled into a raw workflow plan and validated before saving
Inspect the saved result if needed:
tool: wf.workflow.inspect_artifact
arguments:
{
"artifact_id": "echo",
"version": 1
}
7. Save A Deployment
Artifacts are reusable definitions. Deployments decide which concrete sources run them.
tool: wf.workflow.save_deployment
arguments:
{
"deployment": {
"id": "echo.personal",
"artifact_id": "echo",
"artifact_version": 1,
"bindings": {
"demo": "demo.personal",
"wf.std": "wf.std"
}
}
}
The wf.std self-binding is present when the saved graph depends on standard
library capabilities. This tiny echo graph does not need much from wf.std, but
keeping local system-source bindings explicit is the current general pattern.
System-source bindings can look redundant:
{
"wf.std": "wf.std",
"wf.mcp": "wf.mcp"
}
They mean "bind the artifact's logical local source to the concrete local source
with the same id." They are not external account bindings. Keep them explicit
for now when validation reports binding_missing for wf.std or wf.mcp.
Later the platform may make system-source self-bindings implicit, but current
artifacts and deployments use one uniform binding mechanism for both local and
external sources.
8. Validate Before Running
tool: wf.workflow.validate_deployment
arguments:
{
"deployment_id": "echo.personal"
}
You want a runnable result with no blocking diagnostics.
Validation catches problems such as:
- a bound source is disabled or missing
- a required capability disappeared
- the saved dependency schema drifted from the live capability
Optional Live Source Check
Use this before a real run when you need to know whether the bound upstream source can currently answer.
tool: wf.workflow.validate_deployment
arguments:
deployment_id: "echo.personal"
live_check: true
Expected successful shape:
{
"deployment_id": "echo.personal",
"status": "runnable",
"diagnostics": []
}
If a bound upstream source is down, expect status="unrunnable" and a diagnostic
with code="source_unreachable".
9. Run The Deployment
tool: wf.workflow.run_deployment
arguments:
{
"deployment_id": "echo.personal",
"workflow_input": {
"text": "hello"
}
}
Expected shape:
{
"status": "completed",
"output": {
"echoed": "hello"
},
"diagnostics": [],
"trace_count": 1
}
Do not request trace detail by default. run_deployment returns trace_count
as the total original trace length and supports explicit ranged debug reads such
as "trace_range": {"start": 0, "limit": 10}. Trace entries may contain
resolved inputs, outputs, and state changes.
Capture the returned run_id. Use wf.workflow.inspect_run to read the stored
summary later, wf.workflow.read_run_trace for small debug slices, and
wf.workflow.resume_run only when the run status is interrupted. The full
operational contract lives in
durable_run_operations.md.
10. Rebind The Same Artifact Later
If another compatible account appears:
demo.work
you do not need to rewrite artifact version 1.
Create another deployment:
{
"id": "echo.work",
"artifact_id": "echo",
"artifact_version": 1,
"bindings": {
"demo": "demo.work",
"wf.std": "wf.std"
}
}
Then validate it. If demo.work.echo_tool is compatible with the saved contract,
the same workflow artifact can run there.
Full Minimal Sequence
For a client that already has an enabled connection, the normal minimal flow is:
wf.admin.refresh_connection_catalog
wf.admin.list_sources
wf.workflow.list_capabilities
wf.workflow.inspect_capability
wf.workflow.call_capability
wf.workflow.validate_draft
wf.workflow.create_artifact_from_draft
wf.workflow.save_deployment
wf.workflow.validate_deployment
wf.workflow.run_deployment
wf.workflow.list_capabilities only lists graph-usable workflow capabilities.
Control tools such as wf.workflow.inspect_run, wf.workflow.read_run_trace,
draft workspace mutation helpers, and admin operations are discovered through
MCP tools/list or the client's search-tools surface.
Common Failure Points
The connection exists but nothing is discoverable
Check:
wf.admin.list_connectionswf.admin.refresh_connection_catalogwf.admin.inspect_source
A configured connection can exist with zero loaded capabilities until refresh succeeds.
The upstream server has tools but no prompts/resources
That is valid. resources/list and prompts/list are optional MCP families.
A tools-only server should still refresh successfully.
The raw proxy tool is callable but not pleasant in a graph
That is the raw-tool versus workflow-capability distinction. Use or build a workflow-facing wrapper when the raw tool's shape is provider-centric.
The draft is close but has one wrong field
Use wf.workflow.patch_draft instead of asking the client to rewrite the whole
workflow. Draft patching uses JSON Patch and revalidates the patched result.
The deployment used to run but now fails validation
Likely causes:
- source disabled
- capability removed
- schema drift from the saved dependency snapshot
Inspect:
wf.admin.inspect_source
wf.workflow.inspect_artifact
wf.workflow.validate_deployment
Read Next
wf_mcp_operator_manual.mdfor the short mental model and tool-family mapwf_mcp_troubleshooting.mdfor non-happy-path discovery and deployment failuresworkflow_capabilities.mdfor raw tool versus workflow capabilityworkflow_drafts.mdfor the preferred authoring formatworkflow_artifacts.mdfor immutable artifacts, deployments, and dependency contracts
Workspace Variant
If the client is iterating with an LLM, prefer a draft workspace:
- Create a minimal workspace from the selected capability.
- Fetch the workspace by id when context is needed.
- Patch it by id and revision.
- Save an artifact from the workspace after validation is clean.
This avoids resending the whole draft object every turn. The saved artifact is still immutable and should be deployed through the normal deployment path.
Concrete MCP sequence:
wf.workflow.list_capabilitieswith a query such asecho.wf.workflow.call_capabilitywith a small payload to verify the selected capability behaves as expected.wf.workflow.create_minimal_draft_workspacewith arequestobject that contains schemas plus canonicalinputandoutputbinding lists.wf.workflow.list_draft_workspacesif the client needs to rediscover existing workspace ids.wf.workflow.get_draft_workspacewithinclude_draft=trueif the client needs to inspect the full current draft.- Use focused helpers such as
wf.workflow.set_draft_nameorwf.workflow.set_draft_route, or callwf.workflow.patch_draft_workspacewith the currentrevisionfor arbitrary JSON Patch edits. wf.workflow.validate_draft_workspaceif capabilities changed or you want to refresh diagnostics without editing the draft.wf.workflow.create_artifact_from_workspaceafter validation is clean. Usewf.workflow.create_wrapper_from_workspaceinstead when the workspace is a reusable wrapper around a raw capability.wf.workflow.save_deployment, thenvalidate_deployment, thenrun_deployment.wf.workflow.delete_draft_workspacewhen the mutable authoring session is no longer needed.
create_artifact_from_workspace also uses a request object:
{
"request": {
"workspace_id": "echo_draft",
"artifact_id": "echo",
"version": 1,
"title": "Echo",
"outcomes": ["completed"],
"source_bindings": {
"demo": "demo.personal",
"wf.std": "wf.std"
}
}
}
create_wrapper_from_workspace accepts the same request shape except there is
no kind field. It always saves kind="wrapper" and the result is discoverable
as a workflow capability named workflow.<artifact_id>.v<version>.
Wrapper Happy Path
Use this path when a raw/provider capability is callable but you want a reusable workflow-facing wrapper with explicit schemas, outcomes, and bindings.
1. Inspect The Source Capability
tool: wf.workflow.inspect_capability
arguments:
{
"qualified_name": "demo.personal.echo_tool"
}
The response includes wrapper_hints. These hints are scaffolding, not final
business logic. They suggest draft schemas and basic input/output bindings.
2. Create A Draft Workspace From The Capability
tool: wf.workflow.create_draft_workspace_from_capability
arguments:
{
"request": {
"workspace_id": "echo_wrapper_draft",
"capability_name": "demo.personal.echo_tool",
"name": "echo_wrapper",
"title": "Echo Wrapper Draft"
}
}
This creates a mutable, revisioned workspace using the inspected
wrapper_hints.
After create_draft_workspace_from_capability, inspect next_actions.
If recommended_next_tool is wf.workflow.patch_draft_workspace, apply or
adapt the returned patch_examples before saving. If it recommends
wf.workflow.validate_draft_workspace, validate the draft before creating an
artifact.
3. Patch Or Validate The Workspace
When patching output bindings, keep the two levels separate:
- Step-level
steps.<id>.outputusessourcelocal ->targetstate. - Top-level
outputusespathgraph ->targetlocal output payload.
For explicit final output projection from state, use:
{
"path": "state.result_text",
"target": "result_text"
}
Do not use source at top level. source belongs to step output bindings.
If the hints are good enough, validate:
tool: wf.workflow.validate_draft_workspace
arguments:
{
"request": {
"workspace_id": "echo_wrapper_draft"
}
}
If one field is wrong, use a focused helper or JSON Patch. For example, change one route:
tool: wf.workflow.set_draft_route
arguments:
{
"request": {
"workspace_id": "echo_wrapper_draft",
"revision": 1,
"step_id": "echo",
"outcome": "error",
"target": "__end__"
}
}
4. Save The Workspace As A Wrapper Artifact
tool: wf.workflow.create_wrapper_from_workspace
arguments:
{
"request": {
"workspace_id": "echo_wrapper_draft",
"artifact_id": "echo_wrapper",
"version": 1,
"title": "Echo Wrapper",
"outcomes": ["ok", "error"],
"source_bindings": {
"demo": "demo.personal"
}
}
}
The saved wrapper appears in workflow capability discovery as:
workflow.echo_wrapper.v1
5. Deploy And Test The Wrapper
Saved wrappers that use logical sources need a deployment binding when called:
tool: wf.workflow.save_deployment
arguments:
{
"deployment": {
"id": "echo_wrapper.personal",
"artifact_id": "echo_wrapper",
"artifact_version": 1,
"bindings": {
"demo": "demo.personal",
"wf.std": "wf.std"
}
}
}
Then test the wrapper through the workflow-facing REPL tool:
tool: wf.workflow.call_capability
arguments:
{
"qualified_name": "workflow.echo_wrapper.v1",
"deployment_id": "echo_wrapper.personal",
"payload": {
"text": "hello"
}
}
Expected shape:
{
"qualified_name": "workflow.echo_wrapper.v1",
"kind": "wrapper_artifact",
"outcome": "completed",
"output": {
"echoed": "hello"
},
"diagnostics": []
}
Current limitation: saved wrappers are executed through the deployment runtime,
so call_capability reports the wrapper run status (completed, failed, or
interrupted) rather than remapping inner node outcomes. True graph-as-node
outcome propagation belongs in wf_core subgraph support.
See examples/mcp_wrapper_authoring_flow.py for this same sequence through the
Python handler layer.
Cleanup Temporary Deployments
Temporary test deployments can be removed without touching immutable artifacts.
tool: wf.workflow.delete_deployment
arguments:
deployment_id: "echo.personal"
Expected:
{
"deployment_id": "echo.personal",
"deleted": true
}