utilcn logoutilcn
ChatGPT Widget

ChatGPT Backend

MCP server, widget registry helpers, and sample tool for the ChatGPT widget backend.

Backend Pairing

Run the widget build script before starting the MCP server so the backend can serve the latest widget HTML bundle.

Overview

The backend block provisions an MCP server with SSE transport, registers the sample add tool, and exposes helper utilities for packaging widget assets. Install it in your backend project to host the ChatGPT widget and deliver structured responses that the frontend renders.

pnpm dlx shadcn@latest add https://utilcn.dev/r/chatgpt-app-backend.json
bunx --bun shadcn@latest add https://utilcn.dev/r/chatgpt-app-backend.json
npx shadcn@latest add https://utilcn.dev/r/chatgpt-app-backend.json
yarn shadcn@latest add https://utilcn.dev/r/chatgpt-app-backend.json

Project Files

  • src/mcp-server.ts creates an HTTP server on port 8000 that exposes /mcp for SSE sessions and a POST endpoint for messages.
  • src/tools/add.ts registers the add tool, validates input with Zod, and returns structured content that maps to the widget props.
  • src/lib/widget-util.ts resolves the built widget HTML, registers MCP resources, and emits metadata required by ChatGPT.

Configure Asset Location

Point the widget helper at the directory that contains the assets generated by the frontend build script:

const ASSETS_DIR = path.resolve(
  __dirname,
  '..',
  '..',
  '..',
  'web',
  'assets',
); // <-- update to wherever your front end `build-chatgpt-widgets.ts` puts your `assets` directory

If your project places assets in a different folder (for example ../frontend/dist/assets), update ASSETS_DIR to match. The helper looks for ${componentName}.html files and falls back to hashed variations such as add-xxxx.html.

Customise Tools

registerAdd demonstrates how to map tool output to widget props:

server.registerTool('add', { /* metadata */ }, (args) => {
  const { a, b } = addInputParser.parse(args);
  return {
    structuredContent: { a, b, sum: a + b },
    _meta: widgetMeta(addWidget),
  };
});

Reuse registerWidgetResource and widgetMeta for additional widgets. Each widget needs a unique id, templateUri, and descriptive metadata so ChatGPT knows how to render it.

You should create a package.json script to start this up in your environment

Run the MCP Server

# navigate to the mcp-server.ts
bun mcp-server.ts

or

pnpm tsx registry/default/chatgpt-app/mcp-server.ts

The server listens on http://localhost:${PORT ?? 8000} and exposes:

  • GET /mcp for establishing SSE sessions.
  • POST /mcp/messages?sessionId=... for relaying follow-up requests.

Remember to rebuild the frontend assets whenever the widget changes; otherwise buildWidgetHtml will throw if it cannot locate the HTML template.

You will need to use ngrok to make your local url public to ChatGPT

ChatGPT Widget Frontend

Learn how to build and update the widget assets consumed by this server.