From e7fa7b89811356e829f6fc447596cd6069185021 Mon Sep 17 00:00:00 2001 From: Yolanda Robla Date: Mon, 2 Feb 2026 10:57:39 +0100 Subject: [PATCH 1/2] Document default step outputs for composite tools Add comprehensive documentation for the defaultResults field in Virtual MCP Server composite tools. This feature provides fallback values when workflow steps are skipped due to conditional logic or continue-on-error behavior. Changes: - Add "Default step outputs" section to composite tools guide - Document when defaultResults are required (conditional steps and onError.action: continue) - Include configuration examples for both use cases - Document validation behavior and error messages - Add cross-references from "Steps" and "Error handling" sections to help users discover the feature - Fix pre-existing markdownlint errors (heading style, JSX comment) Resolves: #489 --- docs/toolhive/guides-vmcp/composite-tools.mdx | 162 +++++++++++++++++- 1 file changed, 157 insertions(+), 5 deletions(-) diff --git a/docs/toolhive/guides-vmcp/composite-tools.mdx b/docs/toolhive/guides-vmcp/composite-tools.mdx index 1b2b07ee..70e90921 100644 --- a/docs/toolhive/guides-vmcp/composite-tools.mdx +++ b/docs/toolhive/guides-vmcp/composite-tools.mdx @@ -117,7 +117,7 @@ spec: When a client calls this composite tool, vMCP executes all three steps in sequence and returns the paper content. -**Structured content vs JSON text** +### Structured content vs JSON text MCP servers can return data in two ways: @@ -222,7 +222,6 @@ spec: Collect and correlate data from multiple backend MCP servers: -{/* prettier-ignore */} ```yaml title="VirtualMCPServer resource" spec: config: @@ -232,15 +231,22 @@ spec: parameters: type: object properties: + package_name: + type: string + ecosystem: + type: string repo: type: string required: + - package_name + - ecosystem - repo steps: - id: vulnerability_scan - tool: osv.scan_dependencies + tool: osv.query_vulnerability arguments: - repository: '{{.params.repo}}' + package_name: '{{.params.package_name}}' + ecosystem: '{{.params.ecosystem}}' - id: secret_scan tool: gitleaks.scan_repo arguments: @@ -250,7 +256,7 @@ spec: arguments: repo: '{{.params.repo}}' title: 'Security Scan Results' - body: 'Found {{.steps.vulnerability_scan.output.count}} vulnerabilities' + body: 'Vulnerability scan completed for {{.params.package_name}}' dependsOn: [vulnerability_scan, secret_scan] onError: action: continue @@ -300,6 +306,15 @@ spec: action: abort # abort | continue | retry ``` +:::tip + +When using the `condition` field, downstream steps that reference the +conditional step's output may require +[default step outputs](#default-step-outputs) to handle cases where the +condition evaluates to false. + +::: + ### Elicitation (user prompts) Request input from users during workflow execution: @@ -343,6 +358,143 @@ spec: retryCount: 3 ``` +:::tip + +When using `onError.action: continue`, downstream steps that reference this +step's output may require [default step outputs](#default-step-outputs) to +handle cases where the step fails. + +::: + +### Default step outputs + +When steps can be skipped (due to `condition` being false or +`onError.action: continue`), later steps that reference their outputs need +fallback values. Use `defaultResults` to provide these values. + +#### When defaultResults are required + +You must provide `defaultResults` when **all** of these conditions are true: + +1. A step can be skipped (has a `condition` field or `onError.action: continue`) +2. A downstream step references the skipped step's output in its arguments +3. The downstream step doesn't have its own condition that would skip it too + +#### Configuration + +Define default values that match the expected output structure: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: optional_security_check + description: Run security scan with optional vulnerability check + parameters: + type: object + properties: + package_name: + type: string + ecosystem: + type: string + run_vuln_scan: + type: boolean + default: false + required: + - package_name + - ecosystem + steps: + # Step 1: Optional vulnerability scan + - id: vuln_scan + tool: osv.query_vulnerability + arguments: + package_name: '{{.params.package_name}}' + ecosystem: '{{.params.ecosystem}}' + condition: '{{.params.run_vuln_scan}}' + # highlight-start + defaultResults: + vulns: [] + # highlight-end + # Step 2: Create report using scan results + - id: create_report + tool: docs.create_document + arguments: + title: 'Security Report' + # This references vuln_scan output, so defaultResults are needed + body: + 'Found {{len .steps.vuln_scan.output.vulns}} vulnerabilities' + dependsOn: [vuln_scan] +``` + +#### Continue on error example + +When using `onError.action: continue`, provide defaults for potential failures: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: multi_source_data + description: Gather data from multiple sources, continue on failures + steps: + # Step 1: Fetch from primary source (may fail) + - id: fetch_primary + tool: api.get_data + arguments: + source: 'primary' + onError: + action: continue + # highlight-start + defaultResults: + status: 'unavailable' + data: null + # highlight-end + # Step 2: Aggregate results + - id: aggregate + tool: processing.combine_data + arguments: + # Uses fetch_primary output even if it failed + primary: '{{.steps.fetch_primary.output.data}}' + dependsOn: [fetch_primary] +``` + +#### Validation + +vMCP validates `defaultResults` at configuration time: + +- **Missing defaults**: If a step can be skipped and downstream steps reference + its output, but `defaultResults` is not provided, vMCP returns a validation + error +- **Structure**: The `defaultResults` value can be any valid JSON type (object, + array, string, number, boolean, null) +- **No runtime checks**: vMCP does not verify that `defaultResults` match the + actual output structure—you must ensure they match the format your downstream + steps expect + +#### Example validation error + +```yaml +# This will fail validation +steps: + - id: conditional_step + tool: backend.fetch + condition: '{{.params.enabled}}' + # Missing defaultResults! + - id: use_result + tool: backend.process + arguments: + # References conditional_step output + data: '{{.steps.conditional_step.output.value}}' + dependsOn: [conditional_step] +``` + +**Error message:** + +```text +step 'conditional_step' can be skipped but is referenced by downstream steps +without defaultResults defined +``` + ## Template syntax Access workflow context in arguments: From 75a287d449664f2ad02e2523d493e4c3c9cfe922 Mon Sep 17 00:00:00 2001 From: Yolanda Robla Date: Tue, 3 Feb 2026 11:45:30 +0100 Subject: [PATCH 2/2] changes from review --- docs/toolhive/guides-vmcp/composite-tools.mdx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/toolhive/guides-vmcp/composite-tools.mdx b/docs/toolhive/guides-vmcp/composite-tools.mdx index 70e90921..e05db643 100644 --- a/docs/toolhive/guides-vmcp/composite-tools.mdx +++ b/docs/toolhive/guides-vmcp/composite-tools.mdx @@ -117,7 +117,7 @@ spec: When a client calls this composite tool, vMCP executes all three steps in sequence and returns the paper content. -### Structured content vs JSON text +## Structured content vs JSON text MCP servers can return data in two ways: @@ -369,16 +369,15 @@ handle cases where the step fails. ### Default step outputs When steps can be skipped (due to `condition` being false or -`onError.action: continue`), later steps that reference their outputs need +`onError.action: continue`), downstream steps that reference their outputs need fallback values. Use `defaultResults` to provide these values. #### When defaultResults are required -You must provide `defaultResults` when **all** of these conditions are true: +You must provide `defaultResults` when **both** of these conditions are true: 1. A step can be skipped (has a `condition` field or `onError.action: continue`) 2. A downstream step references the skipped step's output in its arguments -3. The downstream step doesn't have its own condition that would skip it too #### Configuration @@ -467,7 +466,7 @@ vMCP validates `defaultResults` at configuration time: error - **Structure**: The `defaultResults` value can be any valid JSON type (object, array, string, number, boolean, null) -- **No runtime checks**: vMCP does not verify that `defaultResults` match the +- **No type checking**: vMCP does not verify that `defaultResults` match the actual output structure—you must ensure they match the format your downstream steps expect @@ -518,6 +517,10 @@ The following functions are available for use in templates: | `quote` | Quote a string value | `{{quote .params.name}}` | | `index` | Access array elements by index | `{{index .steps.s1.output.items 0}}` | +All +[Go template built-in functions](https://pkg.go.dev/text/template#hdr-Functions) +are also supported (e.g., `len`, `eq`, `and`, `or`, `printf`). + ### Accessing step outputs When an MCP server returns structured content, you can access output fields