-
Notifications
You must be signed in to change notification settings - Fork 477
Description
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) andfastmcp@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.toolOutputandwindow.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):
- Node.js with manual JSON-RPC handling
- Node.js with
StreamableHTTPServerTransport - Node.js with
SSEServerTransport - Python with FastMCP
- Official pizzaz example (crashes + asset loading issues)
All servers return correct responses per MCP Inspector, but ChatGPT never passes structuredContent to the widget.
Questions
- Is
structuredContent→toolOutputfunctionality currently broken in ChatGPT? - Are there undocumented requirements for widgets to receive structured data?
- Should the official examples be updated to work with remote servers?
Related Issues
contentfield in tool results is not forwarded to the model when widget is rendered #144 -contentfield not forwarded to model when widget is rendered- Similar issue but focused on what the model receives, not what the widget receives
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.