Skip to content

fix(call-actor): synthesize structuredContent on MCP server pass-through#859

Merged
MQ37 merged 1 commit into
feat/migrate-direct-actor-tools-runresponsefrom
fix/call-actor-mcp-passthrough-structured-content
May 18, 2026
Merged

fix(call-actor): synthesize structuredContent on MCP server pass-through#859
MQ37 merged 1 commit into
feat/migrate-direct-actor-tools-runresponsefrom
fix/call-actor-mcp-passthrough-structured-content

Conversation

@MQ37
Copy link
Copy Markdown
Contributor

@MQ37 MQ37 commented May 18, 2026

Context

call-actor with MCP-server pass-through syntax (actor: "apify/actors-mcp-server:search-apify-docs") fails on master with MCP error -32600: Tool call-actor has an output schema but did not return structured content. Repro with mcpc --json @apify tools-call call-actor actor:='"apify/actors-mcp-server:search-apify-docs"' input:='{"query":"weather"}'. Regression introduced in #415 (f9512c4, 2026-01-29) when outputSchema was added to call-actor; the MCP-pass-through code in handleMcpToolCall has been returning { content } only since #274 (2025-09-18). Both ingredients sat dormant until the SDK on ^1.25.2 (already enforcing since 1.11.4) saw them combined.

Solution

In handleMcpToolCall, synthesize a sentinel RunResponse matching getActorRunOutputSchema.required (runId: 'mcp-passthrough', actorId: baseActorName, status, storages: {}, summary, nextStep) and forward result.isError from the remote tool. The remote tool's payload still flows through content. Stacks on #853.

Worth your attention

  • Sentinel runId'mcp-passthrough' is a deliberate non-Apify literal so logs / dashboards never mistake it for a real run id. Open to mcp-passthrough:${mcpToolName} for greppability per tool if preferred.
  • Integration coverage — the existing happy-path test for this code path didn't catch the regression because it never calls client.listTools(), so the SDK never builds the validator cache. The new test at tests/integration/suite.ts:813 calls listTools() first to mirror real clients (mcpc, Claude Desktop), which is the only way to surface this class of bug at the integration layer.
  • Not the architectural fixcall-actor doing two unrelated things (Apify run vs. MCP tool pass-through) under one tool name is the root cause; this PR keeps that shape and just makes the response spec-compliant. Follow-up tracked in call-actor mixes Apify-run and MCP-tool pass-through under one schema #860 (see comment).

Follow-up

handleMcpToolCall returned { content: result.content } only — no structuredContent,
no isError. Since #415 added outputSchema to call-actor, MCP SDK >= 1.11.4 rejects
the response with -32600 'has an output schema but did not return structured content'
once the client has called listTools() to cache validators (i.e. every real client).

Synthesize a sentinel RunResponse matching getActorRunOutputSchema.required keys —
runId/actorId/status/storages/summary/nextStep — and forward result.isError. The
remote tool's payload still flows through content; no behavior change for callers
that don't validate against outputSchema.

Reproduces with mcpc:
  mcpc --json @apify tools-call call-actor \
    actor:='"apify/actors-mcp-server:search-apify-docs"' input:='{"query":"weather"}'
@MQ37 MQ37 merged commit 9cf3a39 into feat/migrate-direct-actor-tools-runresponse May 18, 2026
10 checks passed
@MQ37 MQ37 deleted the fix/call-actor-mcp-passthrough-structured-content branch May 18, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants