The Backend: Serving Resources

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

All source code for the backend is written in 05-building-a-chatgpt-app/note-taking-app/backend/server.py in the module’s materials repo. Navigate to the file in your editor of choice. The backend is a standard FastMCP server with extensions specific to the OpenAI Apps SDK. The primary goal is to serve the widget HTML as an MCP resource and configure tools with metadata that instructs ChatGPT to render the UI instead of just text responses.

Before diving in, note that this project ships with a prebuilt dist/index.html, so you can run the backend immediately. You only need to run npm run build in the frontend directory if you change React code and want to regenerate the bundle. If you encounter issues, check that your Python environment is set up with FastMCP installed via uv sync.

Defining the Widget URI and Loading HTML

You start by defining a unique URI for the widget and a function to load its HTML content. The URI uses the “ui://” scheme, a custom protocol in MCP for identifying resources like widgets.

Configuring Tool Metadata with _tool_meta

The Apps SDK relies on metadata in tool descriptors to connect tool outputs to UI rendering. This is defined in _tool_meta, which you attach to each tool.

Tools That Return UI Data

Tools like list_notes and create_note are decorated with @mcp.tool(). The key is their return type: a CallToolResult with fields optimized for both text fallback and UI.

Listing the Widget Resource

Use the @mcp._mcp_server.list_resources() decorator to advertise available resources. ChatGPT queries this to discover widgets.

Serving the Widget HTML

ChatGPT fetches resources via MCP ReadResourceRequest messages (not a Python function). Register a handler to serve the HTML:

The open_dashboard Tool

This is a lightweight “trigger” tool to open the widget without data processing:

Listing Tools for ChatGPT

The @mcp._mcp_server.list_tools() decorator provides the tool catalog:

Why Include Starlette?

FastMCP provides the core MCP server, but you wrap it in Starlette for additional HTTP routes:

Putting It All Together

  1. ChatGPT queries list_tools and sees metadata like openai/outputTemplate.
  2. It invokes a tool, receiving structuredContent + _meta.
  3. It requests the resource via an MCP ReadResourceRequest.
  4. The server serves HTML, and ChatGPT renders it in an iframe, injecting data.
See forum comments
Download course materials from Github
Previous: Introduction to ChatGPT Apps Next: The Frontend: The OpenAI Bridge