OpenAI’s Apps SDK: A Developer’s Guide to Getting Started
This developer's guide to the ChatGPT Apps SDK covers the basic concepts, the role of Model Context Protocol, and how to set up your server.
Nov 1st, 2025 5:00am by
Photo by Ethan Currier on Unsplash.
What Are ChatGPT Apps and What Is Their Purpose?
What exactly is a ChatGPT App for? The primary point of an app is to enhance a ChatGPT conversation, nothing more. Think of them as visual experiences that appear in various guises (for instance, carousels) that integrate seamlessly into a conversation without breaking the user context. OpenAI is relying on your app’s metadata to accurately describe the questions that it can answer — either as a whole, or as part of the app. We will see that come up later. But beyond this, I imagine this will play out like the Apple App Store, with some apps being favored by OpenAI and some being blackballed. Without favor, your app will not be able to act as a merchant. Let’s take a quick look at the effect OpenAI is hoping for.Understanding the Inline App Display
The inline display mode appears directly in the flow of the conversation. Imagine that you are on ChatGPT and are asking about pizzas in San Francisco:
Let’s take a look, from top to bottom, at the various fixed aspects:
- At the top is the query; this will be what your app’s metadata suggests it can answer.
- Then, just above the main visuals, is an icon with the app name “Pizzazz.”
- Then we see the inline display, which is your “app content.”
- Finally, it tails off with a model-generated response. (Although it might also be largely driven by your app; the documentation is ambiguous here.)
But don’t read anything into the above until you’ve understood what the main technology you need is.
The Core Technology: Model Context Protocol (MCP)
The central star of any OpenAI App is Model Context Protocol (or MCP), which has fortunately been written about quite a bit a few months ago. We’ve even written about rich components through MCP, so even that isn’t new. But don’t read any further until you have grokked the basics of MCP, because the Apps SDK leans heavily on it — treating your app as a set of tool calls. The idea is that you describe enough about your tools that ChatGPT knows when it can use them, and will return some type of fixed data format that it can plug into a design. But why MCP? A lot of the reasons come down to its protocol-agnostic nature, and use of natural language in descriptions — which large language models are good with. OAuth 2.1 flows are used for access control. Plus, you can easily run local MCP servers. So, your minimal MCP server for Apps SDK has to implement these three capabilities at least:- It must list (or advertise) all of its tools, and the shape of the incoming and return data.
- It must respond to the model calling the tool via
call_tool. - The tool must then return structured content, and optionally point to anything else needed by the ChatGPT client to render your content.
The Role and Rules of Your MCP Server
So your MCP server is the foundation of your app. It exposes tools that the model can call, and returns the packaged structured data plus component HTML that the ChatGPT client renders inline. It also might need to deal with any authentication needed to access certain resources. Within your server, there are a number of rules. There should be one “action-focused” job per tool. This may not be efficient to write, but it makes the customer journey more refined. For example, read and write behavior would certainly be different calls. Apart from the action-oriented tool name, the model is also looking for action-oriented sentences within the description of the tool that start “Use this when ….” The example in the docs is, “Use this when the user wants to view their kanban board.” In addition to returning structured data, each tool on your MCP server should also reference an HTML UI template in its descriptor. This HTML template will be rendered in an iframe by ChatGPT. We will look at this briefly below.Example App
The fastest way to get started is to use the supported official Python SDK or the Typescript SDK. Then add your preferred web framework. We’ll end this post with the HTML UI template registration that ChatGPT will render with an iframe, as we saw in the examples at the top. These are all in the documentation. Otherwise, use the given examples to fashion your first attempts. The node example is the one featured. First, the MCP server is created:// Create an MCP server
const server = new McpServer({ name: "kanban-server", version: "1.0.0"});
Then we register the UI template:
// UI resource (no inline data assignment; host will inject data)
server.registerResource(
"kanban-widget",
"ui://widget/kanban-board.html",
{},
async () => ({
contents: [
{
uri: "ui://widget/kanban-board.html",
mimeType: "text/html+skybridge",
text: `
<div id="kanban-root"></div>
${KANBAN_CSS ? `<style>${KANBAN_CSS}</style>` : ""}
<script type="module">${KANBAN_JS}</script>
`.trim(),
_meta: {
/*
Renders the widget within a rounded border and shadow.
Otherwise, the HTML is rendered full-bleed in the conversation
*/
"openai/widgetPrefersBorder": true,
/*
Assigns a subdomain for the HTML.
When set, the HTML is rendered within `chatgpt-com.web-sandbox.oaiusercontent.com`
It's also used to configure the base url for external links.
*/
"openai/widgetDomain": 'https://chatgpt.com',
/*
Required to make external network requests from the HTML code.
Also used to validate `openai.openExternal()` requests.
*/
'openai/widgetCSP': {
// Maps to `connect-src` rule in the iframe CSP
connect_domains: ['https://chatgpt.com'],
// Maps to style-src, style-src-elem, img-src, font-src, media-src etc. in the iframe CSP
resource_domains: ['https://*.oaistatic.com'],
}
}
},
],
})
);
ui://widget/kanban-board.html. Also, note the required mime type of text/html+skybridge. (It is assumed that CSS and HTML content exists for the kanban board itself.)
Finally, the tool is registered:
server.registerTool(
"kanban-board",
{
title: "Show Kanban Board",
_meta: {
// associate this tool with the HTML template
"openai/outputTemplate": "ui://widget/kanban-board.html",
// labels to display in ChatGPT when the tool is called
"openai/toolInvocation/invoking": "Displaying the board",
"openai/toolInvocation/invoked": "Displayed the board"
},
inputSchema: { tasks: z.string() }
},
async () => {
return {
content: [{ type: "text", text: "Displayed the kanban board!" }],
structuredContent: {}
};
}
);
outTemplate matches the template uri.
Conclusion
I’m fairly certain a friendlier way of doing all this will emerge — either through pick and matched UI templates, or with better libraries. But for now, the pioneers of ChatGPT Apps must work with what OpenAI has provided; the prize being an early presence in the burgeoning AI-based economy.
YOUTUBE.COM/THENEWSTACK
Tech moves fast, don't miss an episode. Subscribe to our YouTube
channel to stream all our podcasts, interviews, demos, and more.