1. Introduction
If you look at a ChatGPT App simply as “just another web server,” you’ll quickly end up with an architectural zoo: some Next.js here, an MCP server there, an agent somewhere else, a commerce backend elsewhere — and it all blurs into one big “server” in your head.
It’s much better to accept from the start that it’s a layer cake:
- on top — the ChatGPT UI, which we don’t control but must adapt to;
- beneath it — our widget on the Apps SDK (Next.js 16, React 19), rendered inside the chat;
- further down — the MCP server with tools/resources/prompts;
- optionally — an agents layer that orchestrates complex scenarios;
- and at the very bottom — your “earthly” services: DB, external APIs, commerce/ACP (the protocol for commerce scenarios), etc.
In the course notes, you can draw this path as a chain:
User → ChatGPT Widget → Apps SDK → MCP Gateway (Auth) → Agent Service → ACP / Stripe.
Our task now is to turn this chain into a clear mental model.
2. The stack at a glance
First let’s look at the whole picture, then go layer by layer.
flowchart TD
U[User in ChatGPT] --> C["ChatGPT UI chat + Apps panel"]
C --> W["Your app widget (Apps SDK, Next.js)"]
W --> M["MCP server (tools/resources/prompts)"]
M --> AG["Agent(s) (Agents SDK, orchestration)"]
AG --> B["Backends and ACP DB, services, payments"]
There are several important things to note.
First, the user only sees two layers: the ChatGPT UI and your widget. Everything below is “backstage.”
Second, the MCP protocol is not a random acronym but an official standard through which the Apps SDK talks to your tools: the server must be able to list tools, accept call_tool, and return a link to a UI resource to render in ChatGPT.
Third, the Agents and ACP layers are formally optional, but in real commercial apps they show up almost always: somewhere you need to plan a multi-step scenario, somewhere you need to take payments.
Now let’s break down each layer individually.
Insight: ChatGPT is a framework
Integration with ChatGPT isn’t in a single place — it’s spread across many integration points. For a developer, this most closely resembles working with a framework. The framework decides when and where to call your code; you just add the right things in the right places.
With ChatGPT that’s exactly how it works:
- widgets — register via mcp-resources, the GPT decides when to show them
- mcp-tools — the GPT decides when to invoke them
- product feed — you can add it to the model via an mcp-tool, but the standard path is via site register merchant
- ACP/InstantCheckout — a separate API
- Authorization — a separate MCP auth server.
3. Layer 1 — ChatGPT UI: our “host”
The ChatGPT UI is OpenAI’s browser (and mobile) interface where the user holds the primary conversation. It has the familiar input field, message history, model selection buttons, and the Apps tab (Store/Composer).
We do not program this layer. We don’t have access to its code, DOM, or styles. But it sets the boundaries:
- this is where the user “selects” your app explicitly (via Store/Composer) or implicitly (the model suggests the app);
- this is where ChatGPT decides: answer with plain text, call your tool, render a widget, or do all of the above;
- this is where core UX patterns live: inline widget, fullscreen mode, PiP window, etc. (details in module 8).
Practically, remember: the ChatGPT UI is our host application. We embed inside it, not the other way around. The GPT server will load your widget’s code onto its own servers, strip anything unnecessary, and only then load it into its chat from its own domain.
4. Layer 2 — Apps SDK and widget (Next.js 16 inside the chat)
The next layer is your UI code written in React/Next.js using the Apps SDK.
The mental model is simple: it’s like a mini SPA rendered as an embeddable widget in the chat. But with caveats:
- your code runs in a sandbox: limited DOM, its own rules for network requests, and a special window.openai object to communicate with ChatGPT (we’ll have a separate lecture on this);
- the widget does not control the conversation flow: the user writes in the shared chat, the model decides when to call your app, and you respond only within your “frame”;
- the Apps SDK handles it all: synchronizing widget state with the conversation history, processing tool results, working with MCP, etc.
From a Next.js developer’s point of view, this looks quite familiar: you have pages/components, hooks, props. But instead of the classic fetch('/api/...') you’ll more often rely on tools defined in your MCP server and on special Apps SDK hooks (more on those later in the course).
To make it a bit more concrete, recall our sample project — the hypothetical GiftGenius. It’s an app that helps select gifts by parameters: for whom, what budget, for what occasion, etc.
A tiny piece of the future UI (without SDK specifics yet, just the idea):
// GiftSummary.tsx — a simple React component of our app
type GiftIdea = {
id: string;
title: string;
price: number;
};
interface GiftSummaryProps {
ideas: GiftIdea[];
}
export function GiftSummary({ ideas }: GiftSummaryProps) {
return (
<ul>
{ideas.map((idea) => (
<li key={idea.id}>
{idea.title} — ${idea.price}
</li>
))}
</ul>
);
}
Later this component will receive ideas not out of thin air but from an MCP server tool result (ToolOutput), but architecturally the important bit is different: all such code lives in the “second layer” and is responsible only for rendering state.
5. Layer 3 — MCP server: the world of tools and data
Now we move further down into the server side.
Model Context Protocol (MCP) is the standard describing how an LLM client (ChatGPT, Apps SDK, Agents) communicates with your server. It defines which tools are available, their input/output schemas, how to invoke them, and which additional resources/prompts can be loaded.
A minimal MCP server for the Apps SDK must do three things:
- return a list of tools with their JSON Schema and metadata;
- handle tool invocations — accept a call_tool request, execute the business logic, and return a structured result;
- optionally return html, js, css, etc. if a tool is associated with a specific widget that needs to be rendered.
An important point: MCP is a transport-agnostic protocol. For ChatGPT Apps we care about its HTTP variant with a streamable implementation, but transport details and message formats are a topic for the MCP module (level 6). For now, it’s enough to understand that the Apps SDK talks “downward” to your MCP server, not to arbitrary REST endpoints.
Architecturally, the MCP layer often looks like a separate microservice:
flowchart LR
subgraph App["Your ChatGPT app"]
W["Widget (Next.js + Apps SDK)"]
M["MCP server (@modelcontextprotocol/sdk)"]
end
W <-- JSON-RPC over HTTP/SSE --> M
M --> DB[(Gift catalog)]
M --> EXT[External APIs]
Inside the MCP server you write regular TypeScript/Node code, use databases, queues, third-party APIs, etc. The official TypeScript SDK for MCP handles JSON-RPC serialization, schema validation, and routing of calls.
For our GiftGenius, one of the MCP tools might be called search_gifts. At the TypeScript level it can look like a normal function:
// Pseudo-code: business logic inside the MCP server
export async function searchGifts(params: {
recipient: string;
budget: number;
}) {
// here you query your DB/catalog
const items = await findGiftsInCatalog(params);
return items.slice(0, 10);
}
Later we’ll wrap it as an MCP tool with a schema description, but the key is: this layer is your “normal” backend that speaks to the world via MCP.
6. Layer 4 — Agents SDK: the brain for complex scenarios
Not every app needs agents, but as soon as a scenario stops being “one tool call — one answer,” the agent layer becomes very useful.
An agent is essentially a managed LLM process that:
- reads the user’s request and facts from the conversation history;
- plans a sequence of steps: which tools to call, in what order, with which arguments;
- analyzes results and can decide to “re-call a tool,” “ask the user for clarification,” or “build a more complex answer”;
- sometimes stores state between steps (memory, sessions, checkpoints — that’s level 12).
Agents SDK gives a structured way to describe such scenarios: which tools are available to the agent, how to store and restore state, how to limit loops, etc. Agents run inside your backend and let you use OpenAI’s power as you see fit, without ChatGPT Apps widget constraints.
In our stack, the agent usually sits in the backend between the MCP layer and your domain APIs. It can use external APIs, internal functions, and MCP tools as its “hands,” while serving as the “brain.”
For example, a GiftGenius scenario might look like this:
- The user writes “pick a gift for my mom up to $50.”
- ChatGPT calls your app’s search_gifts tool.
- Behind search_gifts in the backend is an Agent that decides to first clarify a couple of details (interests, occasion).
- The user provides additional preferences.
- ChatGPT calls your app’s search_gifts tool again with extra arguments.
- The agent on the server may call additional tools (e.g., availability checks).
- It returns prepared options to ChatGPT and possibly a link to a widget for visualization.
Later in the course we’ll dive into the agent run cycle, idempotency, and safety, but for overall architecture it’s important to note: the agent layer is optional yet very powerful “brains” that offload complex orchestration from you.
7. Layer 5 — ACP/Backend: money, data, and earthly concerns
The lowest layer is your regular services:
- databases (product catalogs, users, orders);
- external APIs (payment providers, logistics, third-party SaaS);
- specialized protocols such as ACP (Agentic Commerce Protocol) for commerce scenarios and Instant Checkout.
ACP describes how ChatGPT and agents communicate with your commerce backend: requests to select SKUs, create a cart, place an order, handle returns, webhooks for success/failure, etc.
For GiftGenius, it might look like this:
- the MCP tool search_gifts reads from a product feed/DB;
- the agent, once a specific item is selected, initiates a commerce intent (via ACP);
- your ACP-compatible backend tells the PaymentService to “charge,” informs ChatGPT about status;
- the user sees in ChatGPT that the order is placed, without leaving for an external site.
Now that we’ve covered the layers, let’s look at a concrete end-to-end scenario.
8. End-to-end: how a user request flows through all layers
Take the request: “Pick a gift for my mom up to $50; she likes reading and tea.”
Break it down step by step.
- The user types in ChatGPT. This is the first layer — the ChatGPT UI. For the user, everything looks like a normal chat.
- The model reads the conversation history, your app’s metadata (descriptions, categories, permissions), and decides that GiftGenius is a suitable candidate. According to Apps SDK discovery rules, the model considers tool descriptions, past usage, context, and even brand mentions.
- ChatGPT either:
- invokes your app’s tool immediately without UI (a tool-first scenario);
- or suggests in its reply: “I can use GiftGenius to help pick a gift,” and invokes your tool.
- ChatGPT sends a call_tool request to your MCP server for the search_gifts tool. The MCP server executes business logic: queries the DB/feed, filters by budget and preferences, and returns JSON with a list of suitable items.
- The tool result returns to ChatGPT. It can:
- just use it as data for a text answer (“Here are 3 gift ideas...”), without showing a widget;
- or render a widget, passing the ToolOutput to your component to render product cards.
- Only at this moment does your GiftGenius widget (Apps SDK) start, and your Next.js code is rendered inside the chat. The widget can, for example, show a form with clarifying fields: “Recipient,” “Budget,” “Interests.” The user can click buttons or continue typing — the model will synchronize this with the app.
- When the widget needs real data (the gift catalog), it does not call fetch('https://my-backend/gifts') directly. Instead, it initiates an MCP tool call: ChatGPT again sends a call_tool request to your MCP server for search_gifts.
- If the scenario is multi-step (needs clarifications, ranking, extra availability checks, suggesting alternatives), the agent layer takes care of planning, workflow management, and agent orchestration.
- When the user decides to “buy” a specific item, ChatGPT initiates a purchase via the ACP protocol. The commerce backend, via ACP and Instant Checkout, processes the transaction, returns status, triggers webhooks, and ChatGPT shows the final status to the user (“Order placed, here’s the receipt”).
From a developer’s perspective, it’s nice that each layer has clear responsibilities. And all layers are connected via new standardized protocols (MCP, ACP), not the same old REST calls.
All of this is the logical picture: what layers exist and how requests flow through them. Next we’ll look at the physical side: how exactly these layers can be deployed in code and infrastructure — as a single Next.js monolith or as multiple services (we’re not talking about monolith vs microservices as architectural styles here).
9. Next.js monolith vs split architecture
A natural question: “Does all of this have to be a bunch of separate services? Can I just build one Next.js monolith and be done?”
Answer: you can. In the course we’ll go from simple to complex. At the start it’s perfectly okay to put “almost everything” in one repository and even a single runtime:
flowchart LR
U[ChatGPT] --> W["Next.js App (Apps SDK)"]
W --> M["MCP endpoint (in the same Next.js)"]
M --> DB[(DB/catalog)]
That is, your Next.js server (API routes or a separate server) simultaneously:
- serves the UI widget (Apps SDK pages/components),
- implements the MCP endpoint (JSON-RPC over HTTP),
- talks to the DB/external APIs.
This is convenient in dev mode and for early versions of the app: fewer moving parts, simpler deployment.
However, as functionality grows, reasons to split layers appear:
- the MCP server needs to scale independently (many heavy tools);
- the financial backend lives on its own domain, is governed by other teams, and requires special security;
- agent logic can be extracted into a separate application with its own monitoring and SLA.
Then the picture starts to look closer to what we’ve already seen:
flowchart TD
U[ChatGPT] --> W[Next.js + Apps SDK]
W --> MG[MCP Gateway]
MG --> M1[MCP Gifts Server]
MG --> M2[MCP Analytics Server]
M1 --> AG[Agent Service]
AG --> ACP[Commerce/ACP Backend]
An MCP Gateway is added — a common entry point for ChatGPT. It routes calls to different MCP servers, works with REST APIs, manages authorization, rate limiting, etc.
We’ll start coding examples with the more monolithic scenario, but from the very beginning we’ll organize the code so it can be split into parts with minimal pain.
10. Where exactly you will write code (and what you delegate)
Since we’ve outlined how layers can be assembled into a monolith or a distributed architecture, it’s useful to explicitly fix where exactly you’ll write code and what remains with other services/teams.
From a TypeScript/Next.js developer’s perspective, it helps to mark the zones you control.
In the widget (Apps SDK + Next.js) you:
- write React components that render tool state and user input;
- use Apps SDK hooks to read ToolInput/ToolOutput and widget state (widget state);
- configure the visual mode (inline/fullscreen/PiP, themes, sizes — this will be in level 8);
- interact with ChatGPT via window.openai for more advanced scenarios (a separate course module).
In the MCP server you:
- describe tools/resources/prompts using the MCP SDK;
- implement the business logic of tools (essentially regular TypeScript functions that talk to DBs, APIs, etc.);
- optimize schemas and responses so the model can read them conveniently (fewer hallucinations, more structure).
In the agent layer (if you use the Agents SDK) you:
- describe which tools are available to the agent and what goals it has;
- configure the run cycle, memory, loop control;
- ensure the agent doesn’t go off the rails or get stuck in endless planning.
In ACP/backends you:
- either integrate with existing commerce services (Stripe, your store with a product feed, etc.);
- or design a new backend that understands ACP and can accept and return orders.
Important: the same person rarely owns all layers in a mature product. But at the prototype stage (and in this course) we hope you can at least understand where each piece of code lives.
11. How architecture impacts UX and platform policy
Although UX and policies are separate modules, at the architecture level it’s already important to understand how your layer split will affect UX and platform requirements. So let’s make a couple of notes in advance.
First, the sandbox. The widget can’t recklessly crawl the internet and collect user data — everything goes through controlled tools and the permissions declared in MCP/Store. The platform expects you to accurately describe what data and actions your app needs and will base App discovery/suggestions on those descriptions.
Second, the UX flow. Because the model can temporarily “forget” your app or, conversely, suggest it too aggressively, the architecture must be interruption-friendly: if an agent didn’t finish a long workflow and the user changed the topic, the app should handle it gracefully. In the course, multi-step scenarios and workflow orchestration will be built on MCP tools and the agent layer.
Third, sales. As soon as your app starts taking money, additional requirements kick in: security, logging, ACP contracts, etc. How you split layers (UI, MCP, Agents, ACP/Backend) will strongly affect how painful your Store review and security audit will be.
First takeaways
I hope you’ve built a mental overview map:
- the top layers (ChatGPT UI + Apps SDK) define how the user sees and experiences your app;
- the middle layer (MCP) is the standard way to give the model tools and data;
- the agent and commerce layers turn your app from a “data viewer” into a full product with logic and payments.
In level two we’ll start with the most interesting part: download the official Apps SDK template based on Next.js, run it locally, and connect it to ChatGPT in Dev Mode. In other words, we’ll first get hands-on with the Apps SDK/widget layer, while MCP/agents will either be stubs or a built-in backend.
But keep the current scheme in mind already: it’s like looking at a monorepo and understanding that the apps/ folder is the UI, services/mcp is the protocol, services/agent is the orchestrator, and services/commerce is money.
12. Common misunderstandings about the stack architecture
Mistake #1: thinking that a ChatGPT App = just “a webhook to my REST API.”
You might do this out of habit from the “bot” world: the model just sends POST requests to your URL, and then whatever happens happens. In reality, the Apps SDK and MCP stand between the model and your code. You need to describe tools, their schemas, and behavior — not just “listen” to arbitrary HTTP requests.
Mistake #2: mixing the UI and business-logic layers.
A popular anti-pattern is to pull complex domain logic right into the widget and make the MCP layer a thin shim. As a result, the UI becomes heavy, hard to test, and ill-suited for reuse outside ChatGPT. It’s far more robust to keep rules and data access at the MCP/agent level and let the widget focus on rendering and light interactivity.
Mistake #3: ignoring MCP and writing “your own protocol.”
Sometimes there’s a temptation: “why do I need MCP, I’ll just return JSON and the model will figure it out.” In short demos this might appear to “work,” but you immediately lose standard discovery, inspection, authorization, and multi-client support that MCP and the Apps SDK provide out of the box.
Mistake #4: building the entire app around a single layer.
Some do “everything in the agent,” overloading it with responsibilities. Others cram everything into MCP tools. Others build a giant Next.js monolith. It’s better to accept that each layer has its area of responsibility: UI — rendering, MCP — data/actions access, agent — orchestration, ACP/Backend — domain invariants and money.
Mistake #5: underestimating the impact of architecture on Store review and security.
If you have a single server that is simultaneously an MCP endpoint, an ACP resource, stores secrets, and logs everything “as is,” security and content policy review can drag on. A split architecture with clear boundaries and protocols greatly simplifies life in later stages.
GO TO FULL VERSION