Skip to content

Expose content array alongside structuredContent in workflow templates#4384

Merged
JAORMX merged 1 commit intomainfrom
embedded-resource-content-access
Mar 26, 2026
Merged

Expose content array alongside structuredContent in workflow templates#4384
JAORMX merged 1 commit intomainfrom
embedded-resource-content-access

Conversation

@JAORMX
Copy link
Collaborator

@JAORMX JAORMX commented Mar 26, 2026

Summary

  • The MCP spec (2025-11-25) defines structuredContent and content as separate concerns: structuredContent must conform to outputSchema, while the content array is the backward-compatible representation (text, images, embedded resources). The vmcp workflow engine only exposed structuredContent to templates, so composite tools could not access embedded resource data from downstream steps.
  • Additionally, ContentArrayToMap explicitly ignored EmbeddedResource content types, so even the fallback path (no structuredContent) dropped resource data.

This PR:

  • Handles ContentTypeResource in ContentArrayToMap, mapping embedded resource text/data to resource, resource_1, etc.
  • Adds a Content field to StepResult and wires it through the workflow engine, so the raw content array is preserved alongside StructuredContent.
  • Exposes content array data as a separate content namespace in templates via {{.steps.X.content.*}}, keeping structuredContent clean for outputSchema validation.

Template access after this change:

  • {{.steps.get_sbom.output.format}} -- structuredContent field (schema-conformant)
  • {{.steps.get_sbom.content.resource}} -- embedded resource text from content array
  • {{.steps.get_sbom.content.text}} -- text content from content array

Type of change

  • Bug fix

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)

Changes

File Change
pkg/vmcp/conversion/content.go Handle ContentTypeResource in ContentArrayToMap with resource, resource_1, etc. key pattern
pkg/vmcp/composer/composer.go Add Content []vmcp.Content field to StepResult
pkg/vmcp/composer/workflow_context.go Update RecordStepSuccess to accept and store Content; fix Clone() to copy it
pkg/vmcp/composer/workflow_engine.go Return full ToolCallResult from callToolWithRetry; pass Content through to RecordStepSuccess
pkg/vmcp/composer/template_expander.go Expose content key in buildStepsContext via ContentArrayToMap
pkg/vmcp/client/client.go Keep structuredContent clean (no flat merge)
pkg/vmcp/session/internal/backend/mcp_session.go Same as client.go
pkg/vmcp/conversion/conversion_test.go Tests for resource mapping in ContentArrayToMap
pkg/vmcp/composer/template_expander_test.go Tests for {{.steps.X.content.resource}} and namespace independence
pkg/vmcp/composer/workflow_engine_test.go Workflow test: step accesses embedded resource via .content and structured data via .output

Does this introduce a user-facing change?

Composite tool workflows can now access embedded resource content from downstream steps via {{.steps.stepID.content.resource}}. Structured output remains accessible via {{.steps.stepID.output.field}} as before.

Special notes for reviewers

The key design decision is keeping output and content as independent template namespaces rather than merging content array keys into structuredContent. Merging would violate outputSchema validation when additionalProperties: false is set, since the extra keys from the content array would fail schema checks.

When a backend returns only a Content array (no StructuredContent), output and content both resolve to ContentArrayToMap(result.Content), so {{.steps.X.output.text}} and {{.steps.X.content.text}} return the same value. This is the expected fallback behavior.

Generated with Claude Code

@github-actions github-actions bot added the size/S Small PR: 100-299 lines changed label Mar 26, 2026
@codecov
Copy link

codecov bot commented Mar 26, 2026

Codecov Report

❌ Patch coverage is 85.71429% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.49%. Comparing base (6b8f044) to head (4b9613e).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
pkg/vmcp/composer/workflow_context.go 57.14% 2 Missing and 1 partial ⚠️
pkg/vmcp/composer/workflow_engine.go 85.71% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4384      +/-   ##
==========================================
+ Coverage   69.43%   69.49%   +0.05%     
==========================================
  Files         485      485              
  Lines       49493    49514      +21     
==========================================
+ Hits        34367    34409      +42     
+ Misses      12466    12443      -23     
- Partials     2660     2662       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Mar 26, 2026
@JAORMX JAORMX changed the title Make embedded resource content accessible from composite tool templates Expose content array alongside structuredContent in workflow templates Mar 26, 2026
ContentArrayToMap previously ignored EmbeddedResource content types,
making it impossible for composite tool workflows to chain on embedded
resource data (e.g., an SBOM returned by get_referrer_content).

Handle ContentTypeResource in ContentArrayToMap with "resource",
"resource_1", "resource_2" key pattern. Expose the content array as
a separate template namespace ({{.steps.X.content.*}}) alongside the
existing output namespace ({{.steps.X.output.*}}), keeping
structuredContent clean for outputSchema validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JAORMX JAORMX force-pushed the embedded-resource-content-access branch from 7318079 to 4b9613e Compare March 26, 2026 11:53
@github-actions github-actions bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Mar 26, 2026
@JAORMX JAORMX merged commit 60d91c4 into main Mar 26, 2026
42 of 43 checks passed
@JAORMX JAORMX deleted the embedded-resource-content-access branch March 26, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Small PR: 100-299 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants