Skip to content

feat: add structuredContent tool output with schema#415

Merged
MQ37 merged 5 commits into
masterfrom
structured-output-schema
Jan 29, 2026
Merged

feat: add structuredContent tool output with schema#415
MQ37 merged 5 commits into
masterfrom
structured-output-schema

Conversation

@MQ37
Copy link
Copy Markdown
Contributor

@MQ37 MQ37 commented Jan 28, 2026

Executive Summary

This PR introduces structured output schemas for Apify Actor and dataset tools, enhancing the Model Context Protocol (MCP) integration. It enables better type safety and understanding of tool outputs by providing explicit schemas.

Key Changes

  • Structured Schemas: Added structured output schemas for Actor and dataset tools to provide clear output definitions.
  • Dynamic Tools: Updated dynamic Actor tools to include outputSchema.
  • Testing: Added integration tests for widget structured content and fixed get-actor-run output.

- Add callActorOutputSchema for call-actor and direct actor tool outputs
- Add getActorRunOutputSchema for get-actor-run tool (preserves original nested dataset structure)
- Add datasetItemsOutputSchema for get-actor-output and get-dataset-items tools
- Update buildActorResponseContent to return both text content and structured content
- Add outputSchema definitions to call-actor, get-actor-run, get-actor-output, and get-dataset-items tools
- Fix critical bug: properly destructure buildActorResponseContent result in server task execution path
- All tools now expose MCP-compliant structured output alongside human-readable text
@github-actions github-actions Bot added the t-ai Issues owned by the AI team. label Jan 28, 2026
@MQ37 MQ37 force-pushed the structured-output-schema branch from ce0271a to 5f1866c Compare January 28, 2026 19:09
MQ37 added 4 commits January 28, 2026 23:54
commit 0ade868
Author: Eva Baldasseroni <37815707+baldasseva@users.noreply.github.com>
Date:   Wed Jan 28 15:11:39 2026 +0100

    feat: add hot reload to widgets [internal] (#411)

commit 37d5b0e
Author: Jiří Spilka <jiri.spilka@apify.com>
Date:   Wed Jan 28 13:34:46 2026 +0100

    feat: Update npm scripts and documentation for build and start commands (#412)

    * Update npm scripts and documentation for build and start commands

    * Update build:web script to include dependency installation

commit e93c11b
Author: jakcinmarina <52315405+jakcinmarina@users.noreply.github.com>
Date:   Wed Jan 28 12:26:08 2026 +0100

    feat: add internal tools for call-actor tool calls (#398)

    * feat: add internal tools for call-actor tool calls

    * feat: introduce internal tools for UI mode with shared utilities

    - Add openaiOnly flag to tools for UI mode-specific tool availability
    - Create new fetch-actor-details-internal tool with flexible output options
    - Move search-actors-internal to internal category (UI mode only)
    - Delete deprecated fetch-actor-schema tool
    - Extract shared schema and utilities: actorDetailsOutputOptionsSchema, resolveOutputOptions, buildCardOptions, getMcpToolsMessage, buildActorNotFoundResponse, buildActorDetailsTextResponse
    - Create searchAndFilterActors utility to prevent accidental rental actor filtering omission
    - Build server instructions dynamically based on UI mode with conditional content
    - Add UI category to toolCategories with mode-specific tools
    - Update tool loader to filter openaiOnly tools and load UI category in openai mode
    - Update integration tests for new tool availability in UI mode

    * feat: improve UI mode widget context for actor run completion

    - Extend WidgetState interface to include run completion metadata (runStatus, datasetId, itemCount, runId)
    - Widget now calls setWidgetState when run reaches terminal status, automatically sending completion context to model
    - Update call-actor tool message to clarify that model can retrieve results via get-actor-output once widget notifies completion
    - This allows model to respect user requests for results without strict polling restrictions, as context is provided by widget state
    - Removes confusing 'below' references from earlier widget message updates

    * fix: update actor details functionality for internal tool usage. Clearly handle UI related things.

    * fix: remove unused actor schema output definition from structured-output-schemas.ts

    * fix: use `useRef` to ensure state consistency in ActorRun updates

    * fix: add dynamic description handling for `callActor` tool based on UI mode

    ---------

    Co-authored-by: MQ37 <themq37@gmail.com>
    Co-authored-by: Jiri Spilka <jirka.spilka@gmail.com>

commit 69942c8
Author: Jiří Spilka <jiri.spilka@apify.com>
Date:   Wed Jan 28 12:13:34 2026 +0100

    fix: Move development setup details to DEVELOPMENT.md and update related references (#409)

    * fix: Move development setup details to DEVELOPMENT.md and update related references
    * fix: clarify ui=openai query
…ests

- Add `outputSchema` to direct Actor tools in `src/tools/actor.ts`
- Fix integration tests to use `callActorOutputSchema` for validating dynamic Actor tools
- Add new tests for structured output validation
- Update AGENTS.md with coding guidelines
@MQ37 MQ37 marked this pull request as ready for review January 29, 2026 12:17
@MQ37
Copy link
Copy Markdown
Contributor Author

MQ37 commented Jan 29, 2026

YOLO @jirispilka

@MQ37 MQ37 merged commit f9512c4 into master Jan 29, 2026
4 checks passed
@MQ37 MQ37 deleted the structured-output-schema branch January 29, 2026 12:25
Copy link
Copy Markdown
Collaborator

@jirispilka jirispilka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ops, somehow I forgot to post the review last night, here it is, too late

Comment thread src/tools/dataset.ts
limit: parsed.limit,
};

return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v)}\n\`\`\`` }], structuredContent };
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ouch, this is my fault, but v is really bad variable name :facepalm

* Contains both text content and structured content.
*/
export type ActorResponseResult = {
content: ({ type: 'text'; text: string })[];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can define a new type

export type TextContent = { type: 'text'; text: string };
export type TextContentArray = TextContent[];

And then reuse it everywhere

MQ37 added a commit that referenced this pull request May 18, 2026
…ugh (#859)

## 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 fix** — `call-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 #860 (see comment).

## Follow-up
- Split `call-actor` MCP pass-through into a dedicated tool, or move to
code-mode as the default. Issue: #860
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-ai Issues owned by the AI team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants