Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
DEPRECATED: Replaced by mcp-app-builder. Previously used to build MCP servers with tools, resources, and prompts via mcp-use.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/widgets/model-context.md
1# Model Context Annotations23Keep the AI model aware of what the user is currently seeing in your widget — without requiring explicit tool calls or storing data in developer-managed state.45Both APIs feed into a shared global registry that serializes to an indented tree string, pushed to the host via `ui/update-model-context` (MCP Apps) or `setWidgetState` (ChatGPT Apps SDK). Updates are batched via `queueMicrotask`.67---89## When to Use1011| Situation | Use |12|-----------|-----|13| Annotate what the user is *seeing* right now | `<ModelContext>` or `modelContext.set()` |14| Annotate in JSX, tied to component lifecycle | `<ModelContext content="...">` |15| Annotate from an event handler or outside React | `modelContext.set(key, value)` |16| Persist structured state the model reads on future turns | `setState` from `useWidget` |1718Do **not** use `<ModelContext>` as a replacement for `setState`. They serve different purposes:19- `setState` = developer-managed state (cart, selections, filters). Explicit, you control the shape.20- `ModelContext` = declarative description of what the user *sees*. Set it and forget it.2122---2324## `<ModelContext>` Component2526Declarative, lifecycle-tied, nesting-aware. Removes itself from the tree on unmount — no cleanup needed.2728```tsx29import { ModelContext, useWidget, McpUseProvider } from "mcp-use/react";3031export default function DashboardWidget() {32const { props, isPending } = useWidget<{ activeTab: string }>();33const [hovered, setHovered] = useState<string | null>(null);3435if (isPending) return <McpUseProvider autoSize><div>Loading...</div></McpUseProvider>;3637return (38<McpUseProvider autoSize>39{/* Root annotation — present for the full widget lifetime */}40<ModelContext content="User is viewing the analytics dashboard">4142{/* Re-registers automatically when props.activeTab changes */}43<ModelContext content={`Active tab: ${props.activeTab}`} />4445{/* Conditional — only present while something is hovered */}46{hovered && (47<ModelContext content={`Hovering over chart: ${hovered}`} />48)}4950{/* Nesting — children become child nodes in the model's tree */}51<ModelContext content="Metrics panel">52<ModelContext content="Revenue chart visible" />53<ModelContext content="User count visible" />54</ModelContext>5556<div>{/* widget UI */}</div>57</ModelContext>58</McpUseProvider>59);60}61```6263The model receives an indented tree:64```65- User is viewing the analytics dashboard66- Active tab: overview67- Hovering over chart: Revenue Q468- Metrics panel69- Revenue chart visible70- User count visible71```7273### Nesting Rules7475- `<ModelContext>` at the same JSX level → siblings (flat list at that depth)76- `<ModelContext>` inside another's `children` → child node in the tree77- Self-closing `<ModelContext content="..." />` → leaf node, no children needed7879---8081## `modelContext` Module-Level API8283Imperative, works anywhere — event handlers, plain functions, `useEffect`, outside React entirely. Entries are always root-level (no parent).8485```tsx86import { modelContext } from "mcp-use/react";8788// From an event handler89function onProductSelect(product: Product) {90modelContext.set("selection", `User selected: ${product.name} ($${product.price})`);91}9293function onDrawerClose() {94modelContext.remove("selection");95}9697// From useEffect (lifecycle-aware)98useEffect(() => {99modelContext.set("page", `Viewing page ${currentPage} of ${totalPages}`);100return () => modelContext.remove("page");101}, [currentPage, totalPages]);102```103104| Method | Description |105|--------|-------------|106| `modelContext.set(key, content)` | Register or update a named entry. Same key = overwrite. |107| `modelContext.remove(key)` | Remove an entry by key. |108| `modelContext.clear()` | Remove all entries (component-based and imperative). |109110**Important:** Unlike `<ModelContext>`, `modelContext.set()` entries are NOT automatically cleaned up on component unmount. Always call `.remove(key)` in cleanup logic, or use `<ModelContext>` when you need automatic lifecycle management.111112---113114## How the Tree is Sent to the Model115116The serialized string is sent under a reserved `__model_context` key:117- **MCP Apps**: `ui/update-model-context` with `structuredContent.__model_context`118- **ChatGPT Apps SDK**: `setWidgetState` with `__model_context` merged into the state object119120`__model_context` is **filtered from the developer-facing `state`** returned by `useWidget` — it never appears in your code.121122Calling `setState` from `useWidget` preserves the current `__model_context` value, so user state updates never wipe annotations.123124---125126## Common Patterns127128### Tab-switching widget129130```tsx131const [tab, setTab] = useState("overview");132133<ModelContext content={`User is on the ${tab} tab`}>134<TabContent tab={tab} />135</ModelContext>136```137138### Selected item (imperative)139140```tsx141function onSelect(item: Item) {142modelContext.set("selected", `Selected: ${item.name}`);143}144function onDeselect() {145modelContext.remove("selected");146}147```148149### Multi-level dashboard150151```tsx152<ModelContext content="Dashboard — overview mode">153<ModelContext content={`Showing ${period} data`} />154<ModelContext content={`${visibleCharts.length} charts visible`} />155</ModelContext>156```157158---159160## Reference161162- Full API reference: https://docs.mcp-use.com/typescript/server/widget-components/modelcontext163- Example server: `examples/server/ui/model-context/`164