MCP Apps Architecture
Understanding how MCP (Model Context Protocol) apps work helps you build better integrations. This guide explains the architecture behind Pancake apps.
Core Components
A Pancake app has two main parts:
1. MCP Server
The server exposes tools that AI assistants can call. In Pancake, views, actions, and data endpoints are all exposed as tools.
import { createApp, defineView } from '@pancake-apps/server';
const app = createApp({
name: 'my-app',
version: '1.0.0',
views: {
search: defineView({
description: 'Search for items',
input: z.object({ query: z.string() }),
handler: async ({ query }) => searchItems(query),
ui: { html: './src/views/search.html' },
}),
},
});
app.start({ port: 3000 });The server exposes:
POST /mcp- MCP JSON-RPC endpointGET /openapi.json- OpenAPI spec for ChatGPTGET /.well-known/openai-plugin.json- OpenAI plugin manifest
2. View UI
Views are web components that render inside the AI host. They run in a sandboxed iframe and communicate with the host via postMessage.
function SearchView() {
const { data } = useViewParams();
return (
<div>
{data.items.map(item => (
<ItemCard key={item.id} item={item} />
))}
</div>
);
}Request Flow
Here's what happens when a user interacts with your app:
User Expresses Intent
The user types something like "Search for laptops under $1000" in Claude or ChatGPT.
AI Selects Your Tool
The AI analyzes the request and determines your search tool matches. It extracts parameters from the natural language.
MCP Server Executes
The AI sends a tool call to your server:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"query": "laptops under $1000"
}
}
}Handler Runs
Your handler fetches data and returns it with a UI reference:
handler: async ({ query }) => {
const items = await searchItems(query);
return { items };
}Host Retrieves View
The AI host requests the view HTML:
{
"method": "resources/read",
"params": {
"uri": "pancake://ui/view/search"
}
}View Renders
The host loads your view into a sandboxed iframe. The view receives the data from your handler.
Protocol Comparison
Pancake supports multiple protocols. Here's how they differ:
| Aspect | MCP (Claude) | OpenAI Actions (ChatGPT) |
|---|---|---|
| Protocol | JSON-RPC 2.0 | REST API |
| Communication | postMessage | window.openai API |
| Tool Discovery | tools/list | OpenAPI spec |
| View Loading | resources/read | Output templates |
Pancake handles these differences automatically. Your code stays the same.
Security Model
Views run in a sandboxed environment:

This architecture provides:
- Isolation: Your code can't access the host directly
- Controlled communication: Only postMessage, no direct API access
- CSP policies: Network requests restricted to declared domains
View Lifecycle
1. INITIALIZE
└─→ Host creates iframe
└─→ View script loads
└─→ PancakeProvider establishes connection
2. HYDRATE
└─→ View receives tool input/output data
└─→ Initial render with server data
3. INTERACTIVE
└─→ User interacts with view
└─→ View calls actions, fetches data
└─→ View may message the AI
4. CLEANUP
└─→ Host notifies view of destruction
└─→ Cleanup handlers run
└─→ Iframe removedAlways wrap your view with PancakeProvider. It handles connection setup, data hydration, and lifecycle management.
MCP vs OpenAI Comparison
MCP uses JSON-RPC 2.0 over postMessage:
// Tool call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "search", "arguments": { "query": "test" } }
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": { "items": [...] }
}ChatGPT uses the window.openai API:
// Access tool output
const data = window.openai.toolOutput;
// Call another tool
await window.openai.callTool('search', { query: 'test' });
// Send follow-up message
window.openai.sendFollowUpMessage({ prompt: 'Show me more' });Why Pancake Abstracts This
Without abstraction:
// Platform-specific code
if (typeof window.openai !== 'undefined') {
const data = window.openai.toolOutput;
window.openai.callTool('book', { id: itemId });
} else {
const data = await waitForMcpData();
postMessage({ method: 'tools/call', ... });
}With Pancake:
// Universal code
const { data } = useViewParams();
const { dispatch } = useAction();
await dispatch('book', { id: itemId });Next Steps
- Host-Guest Communication: How views communicate with hosts
- Building Views: Create interactive UIs
- API Reference: Complete API documentation