Skip to content

structuredContent not passed to window.openai.toolOutput in MCP widgets #179

@aditinayak01

Description

@aditinayak01

Summary

When a tool returns structuredContent with an openai/outputTemplate, the structured data does not reach window.openai.toolOutput in the widget iframe. The value is always null, even though the MCP server response is correct and the widget HTML loads successfully.

Environment

  • ChatGPT Interface: Web (Chrome 143.0.0.0)
  • MCP SDK Version: @modelcontextprotocol/sdk@1.25.2 (Node.js) and fastmcp@0.9.0 (Python)
  • Transport: Both Streamable HTTP (POST) and SSE tested
  • Server: Local via ngrok tunnel

Expected Behavior

Per the [MCP documentation](https://developers.openai.com/apps-sdk/build/mcp-server):

The widget reads those payloads through window.openai.toolOutput and window.openai.toolResponseMetadata

When a tool returns:

{
  "content": [{"type": "text", "text": "10 + 20 = 30"}],
  "structuredContent": {"a": 10, "b": 20, "result": 30},
  "_meta": {"openai/outputTemplate": "ui://widget/calculator.html"}
}

The widget should receive window.openai.toolOutput = {a: 10, b: 20, result: 30}

Actual Behavior

In the widget iframe, window.openai.toolOutput is always null:

{
  "displayMode": "inline",
  "theme": "dark",
  "locale": "en-US",
  "toolInput": {"a": 10, "b": 20},
  "toolOutput": null,  // Always null
  "toolResponseMetadata": null,
  "widgetState": null
}

Reproduction

Test Server (Node.js)

Verified via MCP Inspector that the server returns correct responses:

Tool Response:

{
  "content": [{"type": "text", "text": "10 + 20 = 30"}],
  "structuredContent": {"a": 10, "b": 20, "result": 30},
  "_meta": {"openai/outputTemplate": "ui://widget/calculator-complete.html"}
}

Resource Response:

{
  "contents": [{
    "uri": "ui://widget/calculator-complete.html",
    "mimeType": "text/html+skybridge",
    "text": "<!DOCTYPE html>...",
    "_meta": {"openai/widgetPrefersBorder": true}
  }]
}

Both responses are correct according to documentation.

Widget HTML:

const data = window.openai?.toolOutput;
console.log("toolOutput:", data); // Logs: toolOutput: null

if (data?.a != null && data?.b != null && data?.result != null) {
  // Never executes - data is always null
  root.innerHTML = `<div>${data.a} + ${data.b} = ${data.result}</div>`;
}

Browser Console Output

When widget loads in ChatGPT:

toolOutput: null

No errors, no warnings about the widget itself. The HTML loads, mimeType is correct, but structuredContent never reaches toolOutput.

Official Example Also Broken

Tested the official pizzaz_server_node example:

Browser errors:

GET http://localhost:4444/pizzaz-carousel-2d2b.js net::ERR_CONNECTION_REFUSED
GET http://localhost:4444/pizzaz-carousel-2d2b.css net::ERR_CONNECTION_REFUSED

The widget tries to load assets from localhost which fails when accessed remotely.

Server crashes:

RangeError: Maximum call stack size exceeded
    at SSEServerTransport.close
    at Server.close
    at transport.onclose

Infinite recursion on connection close.

Impact

  • Widgets cannot receive structured data - they can only display static content or debug info
  • Official examples don't work when accessed remotely (via ngrok or production URLs)
  • Documentation examples are misleading - they suggest a workflow that doesn't function

Tested Implementations

Tried multiple approaches, all with same result (toolOutput: null):

  1. Node.js with manual JSON-RPC handling
  2. Node.js with StreamableHTTPServerTransport
  3. Node.js with SSEServerTransport
  4. Python with FastMCP
  5. Official pizzaz example (crashes + asset loading issues)

All servers return correct responses per MCP Inspector, but ChatGPT never passes structuredContent to the widget.

Questions

  1. Is structuredContenttoolOutput functionality currently broken in ChatGPT?
  2. Are there undocumented requirements for widgets to receive structured data?
  3. Should the official examples be updated to work with remote servers?

Related Issues

Workaround

Currently using text-only tools without widgets, which work correctly.


Note: Both custom implementations and official examples verified to send correct MCP responses. The issue appears to be in ChatGPT's widget infrastructure not passing structuredContent to window.openai.toolOutput.

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