Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions specification/draft/apps.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,60 @@ Guest UI behavior:
* Guest UI SHOULD check `availableDisplayModes` in host context before requesting a mode change.
* Guest UI MUST handle the response mode differing from the requested mode.

`ui/set-widget-state` - Persist widget state for inclusion in future prompts

```typescript
// Request
{
jsonrpc: "2.0",
id: 4,
method: "ui/set-widget-state",
params: {
toolId: string, // Unique identifier for this widget/tool instance
toolName: string, // Human-readable name of the tool (shown in prompts)
content: string // JSON string containing the widget's current state
}
}

// Success Response
{
jsonrpc: "2.0",
id: 4,
result: {} // Empty result on success
}

// Error Response (if failed)
{
jsonrpc: "2.0",
id: 4,
error: {
code: -32000, // Implementation-defined error
message: "Failed to store widget state"
}
}
```

This request allows apps to persist state that the model can see in subsequent messages. The state is stored per-conversation and sent with each completion request, enabling the model to be aware of the current widget configuration and user selections.

**Important:** Widget state is ephemeral and not persisted across browser sessions or page reloads. Apps SHOULD send their initial state after receiving the `ui/notifications/tool-input` notification to ensure the model has context from the start of each session.

Host behavior:
* Host MUST store the widget state associated with the current conversation.
* Host MUST include all stored widget states in subsequent completion requests to the model.
* Host SHOULD treat widget state content as untrusted third-party data and wrap it appropriately when presenting to the model.
* Host MAY implement size limits on widget state content.

Guest UI behavior:
* Guest UI SHOULD call this method whenever its state changes meaningfully in a way that the model should be aware of.
* Guest UI SHOULD use a consistent `toolId` for the same widget instance.
* Guest UI SHOULD serialize state as a JSON string in the `content` field.
* Guest UI SHOULD include relevant context such as selected items, current view configuration, and user choices.

Example use cases:
* A chart widget reports which data point the user has selected
* A form widget reports current field values before submission
* A data explorer reports active filters and sort order

#### Notifications (Host → UI)

`ui/notifications/tool-input` - Host MUST send this notification with the complete tool arguments after the Guest UI's initialize request completes.
Expand Down
49 changes: 49 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import {
McpUiToolResultNotificationSchema,
McpUiRequestDisplayModeRequest,
McpUiRequestDisplayModeResultSchema,
McpUiSetWidgetStateRequest,
McpUiSetWidgetStateResultSchema,
} from "./types";
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";

Expand Down Expand Up @@ -886,6 +888,53 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
);
}

/**
* Set widget state that will be included in future prompts.
*
* This allows apps to persist state that Claude can see in subsequent messages.
* The state is treated as untrusted third-party content and will be wrapped
* in a document block with explicit trust metadata on the backend.
*
* Call this method whenever your widget's state changes meaningfully in a way
* that Claude should be aware of. The state is stored per-conversation and
* sent with each completion request.
*
* @param params - Widget state parameters
* @param params.toolId - Unique identifier for this widget/tool instance
* @param params.toolName - Human-readable name of the tool (shown in prompts)
* @param params.content - JSON string containing the widget's current state
* @param options - Request options (timeout, etc.)
* @returns Result indicating success or error
*
* @example Persist chart configuration
* ```typescript
* await app.setWidgetState({
* toolId: "customer-segmentation-chart",
* toolName: "Customer Segmentation",
* content: JSON.stringify({
* xAxis: "revenue",
* yAxis: "engagement",
* hiddenSegments: ["inactive"]
* })
* });
* ```
*
* @see {@link McpUiSetWidgetStateRequest} for request structure
*/
setWidgetState(
params: McpUiSetWidgetStateRequest["params"],
options?: RequestOptions,
) {
return this.request(
<McpUiSetWidgetStateRequest>{
method: "ui/set-widget-state",
params,
},
McpUiSetWidgetStateResultSchema,
options,
);
}

/**
* Notify the host of UI size changes.
*
Expand Down
42 changes: 42 additions & 0 deletions src/generated/schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions src/generated/schema.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions src/generated/schema.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions src/spec.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,3 +548,35 @@ export interface McpUiToolMeta {
*/
visibility?: McpUiToolVisibility[];
}

/**
* @description Request to set widget state that will be included in future prompts.
* The state is treated as untrusted third-party content and will be wrapped
* in a document block with explicit trust metadata.
* @see {@link app.App.setWidgetState} for the method that sends this request
*/
export interface McpUiSetWidgetStateRequest {
method: "ui/set-widget-state";
params: {
/** @description Unique identifier for this widget/tool instance. Typically the app_id or tool name. */
toolId: string;
/** @description Human-readable name of the tool (shown in prompts). */
toolName: string;
/** @description JSON string containing the widget's current state. This will be included in future completion requests. */
content: string;
};
}

/**
* @description Result from setting widget state.
* @see {@link McpUiSetWidgetStateRequest}
*/
export interface McpUiSetWidgetStateResult {
/** @description True if the host failed to store the widget state. */
isError?: boolean;
/**
* Index signature required for MCP SDK `Protocol` class compatibility.
* Note: The schema intentionally omits this to enforce strict validation.
*/
[key: string]: unknown;
}
10 changes: 9 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export {
type McpUiRequestDisplayModeResult,
type McpUiToolVisibility,
type McpUiToolMeta,
type McpUiSetWidgetStateRequest,
type McpUiSetWidgetStateResult,
} from "./spec.types.js";

// Import types needed for protocol type unions (not re-exported, just used internally)
Expand All @@ -53,6 +55,7 @@ import type {
McpUiMessageRequest,
McpUiResourceTeardownRequest,
McpUiRequestDisplayModeRequest,
McpUiSetWidgetStateRequest,
McpUiHostContextChangedNotification,
McpUiToolInputNotification,
McpUiToolInputPartialNotification,
Expand All @@ -67,6 +70,7 @@ import type {
McpUiMessageResult,
McpUiResourceTeardownResult,
McpUiRequestDisplayModeResult,
McpUiSetWidgetStateResult,
} from "./spec.types.js";

// Re-export all schemas from generated/schema.ts (already PascalCase)
Expand Down Expand Up @@ -101,6 +105,8 @@ export {
McpUiRequestDisplayModeResultSchema,
McpUiToolVisibilitySchema,
McpUiToolMetaSchema,
McpUiSetWidgetStateRequestSchema,
McpUiSetWidgetStateResultSchema,
} from "./generated/schema.js";

// Re-export SDK types used in protocol type unions
Expand Down Expand Up @@ -129,7 +135,7 @@ import {
* All request types in the MCP Apps protocol.
*
* Includes:
* - MCP UI requests (initialize, open-link, message, resource-teardown, request-display-mode)
* - MCP UI requests (initialize, open-link, message, resource-teardown, request-display-mode, set-widget-state)
* - MCP server requests forwarded from the app (tools/call, resources/*, prompts/list)
* - Protocol requests (ping)
*/
Expand All @@ -139,6 +145,7 @@ export type AppRequest =
| McpUiMessageRequest
| McpUiResourceTeardownRequest
| McpUiRequestDisplayModeRequest
| McpUiSetWidgetStateRequest
| CallToolRequest
| ListToolsRequest
| ListResourcesRequest
Expand Down Expand Up @@ -186,6 +193,7 @@ export type AppResult =
| McpUiMessageResult
| McpUiResourceTeardownResult
| McpUiRequestDisplayModeResult
| McpUiSetWidgetStateResult
| CallToolResult
| ListToolsResult
| ListResourcesResult
Expand Down
Loading