# wf_mcp End-To-End Runbook This runbook shows one complete successful path through the platform: ```text 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: ```text demo.personal ``` which exposes one workflow-ready capability: ```text 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: ```text 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: ```json { "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: ```yaml 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: ```yaml tool: wf.admin.list_connections arguments: {} ``` Expected idea: ```json [ { "id": "demo.personal", "server": "demo", "account": "personal", "enabled": true } ] ``` ## 2. Refresh The Upstream Catalog ```yaml 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: ```yaml tool: wf.admin.get_catalog arguments: {} ``` for the raw upstream MCP snapshot, and: ```yaml 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: ```yaml tool: wf.admin.list_sources arguments: { "limit": 20 } ``` Then inspect only the source you care about: ```yaml 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: ```yaml 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: ```yaml 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: ```yaml tool: wf.workflow.call_capability arguments: { "qualified_name": "demo.personal.echo_tool", "payload": { "text": "hello" } } ``` Expected shape: ```json { "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 `echoed` into workflow state - ends on the node's `ok` outcome ```yaml 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_bindings` says `"demo": "demo.personal"`, the saved artifact is normalized to logical node ref `demo.echo_tool` - the saved dependency contract records what was observed from `demo.personal.echo_tool` at creation time - the draft is compiled into a raw workflow plan and validated before saving Inspect the saved result if needed: ```yaml 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. ```yaml 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: ```json { "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 ```yaml 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. ```yaml tool: wf.workflow.validate_deployment arguments: deployment_id: "echo.personal" live_check: true ``` Expected successful shape: ```json { "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 ```yaml tool: wf.workflow.run_deployment arguments: { "deployment_id": "echo.personal", "workflow_input": { "text": "hello" } } ``` Expected shape: ```json { "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`](durable_run_operations.md). ## 10. Rebind The Same Artifact Later If another compatible account appears: ```text demo.work ``` you do **not** need to rewrite artifact version `1`. Create another deployment: ```json { "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: ```text 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: 1. `wf.admin.list_connections` 2. `wf.admin.refresh_connection_catalog` 3. `wf.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: ```text wf.admin.inspect_source wf.workflow.inspect_artifact wf.workflow.validate_deployment ``` ## Read Next - [`wf_mcp_operator_manual.md`](wf_mcp_operator_manual.md) for the short mental model and tool-family map - [`wf_mcp_troubleshooting.md`](wf_mcp_troubleshooting.md) for non-happy-path discovery and deployment failures - [`workflow_capabilities.md`](workflow_capabilities.md) for raw tool versus workflow capability - [`workflow_drafts.md`](workflow_drafts.md) for the preferred authoring format - [`workflow_artifacts.md`](workflow_artifacts.md) for immutable artifacts, deployments, and dependency contracts ## Workspace Variant If the client is iterating with an LLM, prefer a draft workspace: 1. Create a minimal workspace from the selected capability. 2. Fetch the workspace by id when context is needed. 3. Patch it by id and revision. 4. 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: 1. `wf.workflow.list_capabilities` with a query such as `echo`. 2. `wf.workflow.call_capability` with a small payload to verify the selected capability behaves as expected. 3. `wf.workflow.create_minimal_draft_workspace` with a `request` object that contains schemas plus canonical `input` and `output` binding lists. 4. `wf.workflow.list_draft_workspaces` if the client needs to rediscover existing workspace ids. 5. `wf.workflow.get_draft_workspace` with `include_draft=true` if the client needs to inspect the full current draft. 6. Use focused helpers such as `wf.workflow.set_draft_name` or `wf.workflow.set_draft_route`, or call `wf.workflow.patch_draft_workspace` with the current `revision` for arbitrary JSON Patch edits. 7. `wf.workflow.validate_draft_workspace` if capabilities changed or you want to refresh diagnostics without editing the draft. 8. `wf.workflow.create_artifact_from_workspace` after validation is clean. Use `wf.workflow.create_wrapper_from_workspace` instead when the workspace is a reusable wrapper around a raw capability. 9. `wf.workflow.save_deployment`, then `validate_deployment`, then `run_deployment`. 10. `wf.workflow.delete_draft_workspace` when the mutable authoring session is no longer needed. `create_artifact_from_workspace` also uses a `request` object: ```json { "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..v`. ## 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 ```yaml 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 ```yaml 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..output` uses `source` local -> `target` state. - Top-level `output` uses `path` graph -> `target` local output payload. For explicit final output projection from state, use: ```json { "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: ```yaml 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: ```yaml 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 ```yaml 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: ```text workflow.echo_wrapper.v1 ``` ### 5. Deploy And Test The Wrapper Saved wrappers that use logical sources need a deployment binding when called: ```yaml 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: ```yaml tool: wf.workflow.call_capability arguments: { "qualified_name": "workflow.echo_wrapper.v1", "deployment_id": "echo_wrapper.personal", "payload": { "text": "hello" } } ``` Expected shape: ```json { "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. ```yaml tool: wf.workflow.delete_deployment arguments: deployment_id: "echo.personal" ``` Expected: ```json { "deployment_id": "echo.personal", "deleted": true } ```