External Publication
Visit Post

MCP Apps in ChatGPT are fundamentally broken - 2 critical bugs

OpenAI Developer Community May 6, 2026
Source

If it’s of any help, here’s what I see when testing:

And my test code:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({
  name: "meta-probe",
  version: "1.0.0",
});

const TEMPLATE_URI = "ui://widget/meta-probe.html";

const widgetHtml = `<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>
      body {
        font-family: system-ui, sans-serif;
        margin: 0;
        padding: 16px;
        color: #111;
        background: #fff;
      }
      h1 { font-size: 18px; margin: 0 0 8px; }
      pre {
        white-space: pre-wrap;
        background: #f5f5f5;
        border: 1px solid #ddd;
        border-radius: 6px;
        padding: 12px;
      }
    </style>
  </head>
  <body>
    <h1>Widget rendered</h1>
    <p>If you can see this, the iframe received template HTML.</p>
    <pre id="debug">Waiting for bridge data...</pre>

    <script>
      const debug = document.getElementById("debug");

      function snapshot(label) {
        const data = {
          label,
          hasOpenAI: Boolean(window.openai),
          toolOutput: window.openai?.toolOutput ?? null,
          toolResponseMetadata: window.openai?.toolResponseMetadata ?? null,
          widgetState: window.openai?.widgetState ?? null
        };

        debug.textContent = JSON.stringify(data, null, 2);
      }

      snapshot("initial");

      window.addEventListener("openai:set_globals", (event) => {
        snapshot("openai:set_globals");
      });

      window.addEventListener("message", (event) => {
        const msg = event.data;
        if (msg?.method === "ui/notifications/tool-result") {
          debug.textContent = JSON.stringify(
            {
              label: "ui/notifications/tool-result",
              params: msg.params
            },
            null,
            2
          );
        }
      });
    </script>
  </body>
</html>`;

server.registerResource("meta-probe-widget", TEMPLATE_URI, {}, async () => ({
  contents: [
    {
      uri: TEMPLATE_URI,
      mimeType: "text/html",
      text: widgetHtml,
      _meta: {
        "openai/widgetDescription": "Validates MCP _meta delivery.",
        "openai/widgetPrefersBorder": true,
        "openai/widgetCSP": {
          connect_domains: [],
          resource_domains: [],
        },
      },
    },
  ],
}));

server.registerTool(
  "show_meta_probe",
  {
    title: "Show meta probe",
    description: "Render a read-only widget that verifies MCP tool result metadata.",
    inputSchema: {},
    annotations: {
      readOnlyHint: true,
      destructiveHint: false,
      openWorldHint: false,
    },
    _meta: {
      "openai/outputTemplate": TEMPLATE_URI,
      "openai/toolInvocation/invoking": "Running meta probe...",
      "openai/toolInvocation/invoked": "Meta probe ready",
    },
  },
  async () => ({
    content: [
      {
        type: "text",
        text: "hello from the MCP server",
      },
    ],
    structuredContent: {
      visibleMessage: "hello from the MCP server",
      structuredOnlyValue: 12345,
    },
    _meta: {
      secretProbeValue: "private meta reached widget",
      largePrivateState: {
        hydratedFromMeta: true,
        timestamp: new Date().toISOString(),
      },
    },
  })
);

export default server;

Discussion in the ATmosphere

Loading comments...