Skip to content

Commit e719be3

Browse files
Merge pull request #314 from jonathanhefner/patterns-doc
Add Patterns guide doc
2 parents c76f827 + 36268be commit e719be3

File tree

14 files changed

+643
-85
lines changed

14 files changed

+643
-85
lines changed

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ Guest UI (App) <--PostMessageTransport--> Host (AppBridge) <--MCP Client--> MCP
8282

8383
JSDoc `@example` tags use `{@includeCode ./file.examples.ts#regionName}` to pull in type-checked code from companion `.examples.ts`/`.examples.tsx` files. Regions are marked with `//#region name` and `//#endregion name`, wrapped in functions (whose parameters provide types for external values). Region names follow `exportedName_variant` or `ClassName_methodName_variant` pattern (e.g., `useApp_basicUsage`, `App_hostCapabilities_checkAfterConnection`).
8484

85+
Standalone docs in `docs/` (listed in `typedoc.config.mjs` `projectDocuments`) can also have type-checked companion `.ts`/`.tsx` files using the same `@includeCode` pattern.
86+
8587
## Full Examples
8688

8789
Uses npm workspaces. Full examples in `examples/` are separate packages:

docs/migrate_from_openai_apps.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
---
2+
title: Migrate OpenAI App
3+
---
4+
15
# Migrating from OpenAI Apps SDK to MCP Apps SDK
26

37
This guide helps you migrate from the OpenAI Apps SDK to the MCP Apps SDK (`@modelcontextprotocol/ext-apps`).
@@ -50,7 +54,7 @@ This guide helps you migrate from the OpenAI Apps SDK to the MCP Apps SDK (`@mod
5054

5155
### Server-Side Migration Example
5256

53-
### Before (OpenAI)
57+
#### Before (OpenAI)
5458

5559
```typescript
5660
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -110,7 +114,7 @@ function createServer() {
110114
}
111115
```
112116

113-
### After (MCP Apps)
117+
#### After (MCP Apps)
114118

115119
```typescript
116120
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -184,7 +188,7 @@ function createServer() {
184188
6. **Helper Functions**: MCP provides `registerAppTool()` and `registerAppResource()` helpers
185189
7. **Not Yet Implemented**: `_meta["openai/toolInvocation/invoking"]`, `_meta["openai/toolInvocation/invoked"]`, and `_meta["openai/widgetDescription"]` don't have MCP equivalents yet
186190

187-
## Client-side
191+
## Client-Side
188192

189193
### Quick Start Comparison
190194

@@ -302,7 +306,7 @@ function createServer() {
302306
|| `app.getHostVersion()` | Returns `{ name, version }` of host |
303307
|| `app.getHostCapabilities()` | Check `serverTools`, `openLinks`, `logging`, etc. |
304308

305-
### Full Migration Example
309+
### Client-Side Migration Example
306310

307311
#### Before (OpenAI)
308312

docs/patterns.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: Patterns
3+
---
4+
5+
# MCP Apps Patterns
6+
7+
This document covers common patterns and recipes for building MCP Apps.
8+
9+
## Tools that are private to Apps
10+
11+
Set {@link types!McpUiToolMeta.visibility Tool.\_meta.ui.visibility} to `["app"]` to make tools only callable by Apps (hidden from the model). This is useful for UI-driven actions like updating quantities, toggling settings, or other interactions that shouldn't appear in the model's tool list.
12+
13+
{@includeCode ../src/server/index.examples.ts#registerAppTool_appOnlyVisibility}
14+
15+
_See [`examples/system-monitor-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/system-monitor-server) for a full implementation of this pattern._
16+
17+
## Reading large amounts of data via chunked tool calls
18+
19+
Some host platforms have size limits on tool call responses, so large files (PDFs, images, etc.) cannot be sent in a single response. Use an app-only tool with chunked responses to bypass these limits while keeping the data out of model context.
20+
21+
**Server-side**: Register an app-only tool that returns data in chunks with pagination metadata:
22+
23+
{@includeCode ./patterns.tsx#chunkedDataServer}
24+
25+
**Client-side**: Loop calling the tool until all chunks are received:
26+
27+
{@includeCode ./patterns.tsx#chunkedDataClient}
28+
29+
_See [`examples/pdf-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/pdf-server) for a full implementation of this pattern._
30+
31+
## Giving errors back to model
32+
33+
**Server-side**: Tool handler validates inputs and returns `{ isError: true, content: [...] }`. The model receives this error through the normal tool call response.
34+
35+
**Client-side**: If a runtime error occurs (e.g., API failure, permission denied, resource unavailable), use {@link app!App.updateModelContext updateModelContext} to inform the model:
36+
37+
{@includeCode ../src/app.examples.ts#App_updateModelContext_reportError}
38+
39+
## Matching host styling (CSS variables, theme, and fonts)
40+
41+
Use the SDK's style helpers to apply host styling, then reference them in your CSS:
42+
43+
- **CSS variables** — Use `var(--color-background-primary)`, etc. in your CSS
44+
- **Theme** — Use `[data-theme="dark"]` selectors or `light-dark()` function for theme-aware styles
45+
- **Fonts** — Use `var(--font-sans)` or `var(--font-mono)` with fallbacks (e.g., `font-family: var(--font-sans, system-ui, sans-serif)`)
46+
47+
**Vanilla JS:**
48+
49+
{@includeCode ./patterns.tsx#hostStylingVanillaJs}
50+
51+
**React:**
52+
53+
{@includeCode ./patterns.tsx#hostStylingReact}
54+
55+
_See [`examples/basic-server-vanillajs/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) and [`examples/basic-server-react/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) for full implementations of this pattern._
56+
57+
## Entering / Exiting fullscreen
58+
59+
Toggle fullscreen mode by calling {@link app!App.requestDisplayMode requestDisplayMode}:
60+
61+
{@includeCode ../src/app.examples.ts#App_requestDisplayMode_toggle}
62+
63+
Listen for display mode changes via {@link app!App.onhostcontextchanged onhostcontextchanged} to update your UI:
64+
65+
{@includeCode ../src/app.examples.ts#App_onhostcontextchanged_respondToDisplayMode}
66+
67+
_See [`examples/shadertoy-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/shadertoy-server) for a full implementation of this pattern._
68+
69+
## Passing contextual information from the App to the Model
70+
71+
Use {@link app!App.updateModelContext updateModelContext} to keep the model informed about what the user is viewing or interacting with. Structure the content with YAML frontmatter for easy parsing:
72+
73+
{@includeCode ../src/app.examples.ts#App_updateModelContext_appState}
74+
75+
_See [`examples/map-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/map-server) for a full implementation of this pattern._
76+
77+
## Sending large follow-up messages
78+
79+
When you need to send more data than fits in a message, use {@link app!App.updateModelContext updateModelContext} to set the context first, then {@link app!App.sendMessage sendMessage} with a brief prompt to trigger a response:
80+
81+
{@includeCode ../src/app.examples.ts#App_sendMessage_withLargeContext}
82+
83+
_See [`examples/transcript-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/transcript-server) for a full implementation of this pattern._
84+
85+
## Persisting widget state
86+
87+
To persist widget state across conversation reloads (e.g., current page in a PDF viewer, camera position in a map), use [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) with a stable identifier provided by the server.
88+
89+
**Server-side**: Tool handler generates a unique `widgetUUID` and returns it in `CallToolResult._meta.widgetUUID`:
90+
91+
{@includeCode ./patterns.tsx#persistDataServer}
92+
93+
**Client-side**: Receive the UUID in {@link app!App.ontoolresult ontoolresult} and use it as the storage key:
94+
95+
{@includeCode ./patterns.tsx#persistData}
96+
97+
_See [`examples/map-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/map-server) for a full implementation of this pattern._
98+
99+
## Pausing computation-heavy widgets when out of view
100+
101+
Widgets with animations, WebGL rendering, or polling can consume significant CPU/GPU even when scrolled out of view. Use [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to pause expensive operations when the widget isn't visible:
102+
103+
{@includeCode ./patterns.tsx#visibilityBasedPause}
104+
105+
_See [`examples/shadertoy-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/shadertoy-server) for a full implementation of this pattern._
106+
107+
## Lowering perceived latency
108+
109+
Use {@link app!App.ontoolinputpartial ontoolinputpartial} to receive streaming tool arguments as they arrive, allowing you to show a loading preview before the complete input is available.
110+
111+
{@includeCode ../src/app.examples.ts#App_ontoolinputpartial_progressiveRendering}
112+
113+
> [!IMPORTANT]
114+
> Partial arguments are "healed" JSON — the host closes unclosed brackets/braces to produce valid JSON. This means objects may be incomplete (e.g., the last item in an array may be truncated). Don't rely on partial data for critical operations; use it only for preview UI.
115+
116+
_See [`examples/threejs-server/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/threejs-server) for a full implementation of this pattern._

0 commit comments

Comments
 (0)