Skip to content

[bug] v2 apps/mcp: advertised output schema describes structuredContent, but calls return the CallToolResult envelope #2108

Description

@JRGiardiniere

Executor version

apps framework 0.0.1-beta.0 (source references below are from the v2 branch at fd03c8a)

How do you run Executor?

Executor Cloud

Operating system

Windows

Integration involved

Remote MCP server (Metabase, https://<instance>.metabaseapp.com/api/metabase-mcp) imported with mcpOperations through accountOperations

What happened

For tools imported with mcpOperations, the output type in tools.search is the upstream MCP outputSchema. That schema describes structuredContent, but the call resolves to the full MCP result: { content, structuredContent, isError?, _meta? }.

tools.search signature for Metabase search:

Promise<{ data: Array<{ id: number, type: ..., name: string, ... }> }>

What the call actually returns:

{ content: [{ type: "text", text: "{\"data\":[...]}" }], structuredContent: { data: [...] } }

The same thing happens with every Metabase tool we called (search, read_resource, execute_sql).

Source:

  • packages/apps/src/implementation/mcp-tools.ts:55: the upstream output schema validates result.structuredContent
  • packages/apps/src/implementation/mcp-tools.ts:60: the full result is returned
  • packages/apps/src/implementation/protocol-operations.ts:42-46: the same upstream outputSchema is copied onto the operation unchanged, and that's what the signature is generated from
  • packages/app-templates/executor/skills/app-authoring/integrations.md:34: documents that results retain content, structuredContent, isError and _meta
  • apps/local/server/test/catalog.test.ts:861-880: asserts outputSchema is { account: string } and, in the same test, that the call result is { content, structuredContent: { account }, _meta }

What you expected

The type in tools.search matches what the call returns.

Related: #851 fixed this in v1 by declaring the output as the CallToolResult shape with the upstream schema nested under structuredContent. It also added typeCheckOutputTypeScript, which compiles the advertised output type against a real result. We didn't find an equivalent check in v2. #1769 (closed) returned structuredContent as the data.

Steps to reproduce

  1. Deploy an app using the authenticated remote MCP template from integrations.md, pointed at an MCP server whose tools declare an outputSchema:
    export default defineApp({ accounts: { service: provider.many() } }, async ({ accounts, signal }) =>
      accountOperations(accounts.service, async (account) => mcpOperations({
        url: "https://<instance>.metabaseapp.com/api/metabase-mcp",
        headers: { Authorization: "Bearer " + account.fields.access_token },
        signal,
      }), { signal }),
    )
  2. In execute, run tools.search({ query: "<app-slug>" }) and read the output type of any tool.
  3. Call that tool and return the raw result. It has content and structuredContent at the top level, and the fields from step 2 sit under structuredContent.

Diagnostics / logs

None. The workaround is to read r.structuredContent instead of following the signature.

Before you submit

  • I searched the open issues for a duplicate.
  • I removed all keys, tokens, and credentials from this report.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions