window.openai.toolOutput always null - widget never receives tool result data

Summary

My MCP server returns valid structuredContent but the widget never receives the data. window.openai.toolOutput is always
null, and no openai:set_globals event fires.

Environment

  • Browser: Safari (also tested Chrome)
  • MCP Server: Python/FastAPI deployed on AWS Lambda
  • Widget: Vanilla HTML/JS (no React)
  • MIME type: text/html;profile=mcp-app
  • Testing in: Developer Mode (Settings → Connectors → Developer mode)

What Works

  • MCP server responds correctly to all JSON-RPC methods (initialize, tools/list, tools/call, resources/read)
  • tools/call returns proper structuredContent with book data (verified via curl)
  • Widget HTML loads in ChatGPT iframe
  • Widget JavaScript executes
  • window.openai exists with 48 properties/methods

What Doesn’t Work

  • window.openai.toolOutput is always null
  • window.openai.widgetState is always null
  • No openai:set_globals event ever fires
  • No postMessage with ui/notifications/tool-result received
  • Widget shows “Runtime error” after loading state

Console Logs from Widget

[WOEB] Script starting…
[WOEB] document.readyState: “loading”
[WOEB] typeof window.openai: “object”
[WOEB] window.openai keys: [“openExternal”, “setOpenInAppUrl”, …] (48)
[WOEB] toolOutput: null
[WOEB] widgetState: null
[WOEB] displayMode: “inline”
[WOEB] maxHeight: undefined
[WOEB] render() called
[WOEB] No data yet, showing loading state

MCP Server Response (tools/call)

json
{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
      "content": [{"type": "text", "text": "Found 8 wonderful books!"}],
      "structuredContent": {
        "view": "grid",
        "books": [{"id": "animal-factopia", "title": "Animal FACTopia!", "price_gbp": 10.99}],
        "total_count": 8
      },
      "_meta": {
        "openai/outputTemplate": "ui://widget/book-shop.html",
        "openai/widgetAccessible": true,
        "openai/toolInvocation/invoking": "Loading books...",
        "openai/toolInvocation/invoked": "Found books!"
      }
    }
  }

MCP Server Response (tools/list)

json
{
    "tools": [{
      "name": "search_books",
      "title": "Browse & Recommend Books",
      "inputSchema": {},
      "_meta": {
        "openai/outputTemplate": "ui://widget/book-shop.html",
        "openai/widgetAccessible": true,
        "openai/widgetCSP": {"connect_domains": [], "resource_domains": []},
        "ui": {"resourceUri": "ui://widget/book-shop.html"}
      }
    }]
  }
}

Widget Code (simplified)

Loading...

What I’ve Tried

  1. Verified MCP responses with curl - all correct
  2. Added openai/outputTemplate to both tools/list and tools/call
  3. Added/removed openai/widgetCSP configuration (with empty arrays, with domains, without)
  4. Called notifyIntrinsicHeight() and notifyIntrinsicWidth()
  5. Listened for openai:set_globals and postMessage events
  6. Polled window.openai.toolOutput every 100ms
  7. Tried calling window.openai.callTool() from widget
  8. Cleared browser cache, reconnected MCP server multiple times

Questions

  1. Is there a required handshake or initialization the widget must perform before receiving data?
  2. Is there documentation on exactly how window.openai.toolOutput gets populated?
  3. Are vanilla HTML widgets supported, or is the React SDK required?
  4. Is a specific openai/widgetCSP configuration required for toolOutput to be populated, even when the widget doesn’t load external resources? I’m testing in Developer Mode where CSP checks appear disabled - could this affect how tool results are delivered?

This topic was automatically closed after 24 hours. New replies are no longer allowed.