@modelcontextprotocol/ext-apps 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,27 +2,29 @@ import { Implementation } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { App, McpUiAppCapabilities } from "../app";
3
3
  export * from "../app";
4
4
  /**
5
- * Options for configuring the useApp hook.
5
+ * Options for configuring the {@link useApp} hook.
6
6
  *
7
- * Note: This interface does NOT expose App options like `autoResize`.
8
- * The hook creates the App with default options (autoResize: true). If you need
9
- * custom App options, create the App manually instead of using this hook.
7
+ * Note: This interface does NOT expose {@link App} options like `autoResize`.
8
+ * The hook creates the `App` with default options (`autoResize: true`). If you
9
+ * need custom `App` options, create the `App` manually instead of using this hook.
10
10
  *
11
11
  * @see {@link useApp} for the hook that uses these options
12
- * @see {@link useAutoResize} for manual auto-resize control with custom App options
12
+ * @see {@link useAutoResize} for manual auto-resize control with custom `App` options
13
13
  */
14
14
  export interface UseAppOptions {
15
15
  /** App identification (name and version) */
16
16
  appInfo: Implementation;
17
- /** Features and capabilities this app provides */
17
+ /**
18
+ * Declares what features this app supports.
19
+ */
18
20
  capabilities: McpUiAppCapabilities;
19
21
  /**
20
- * Called after App is created but before connection.
22
+ * Called after {@link App} is created but before connection.
21
23
  *
22
24
  * Use this to register request/notification handlers that need to be in place
23
25
  * before the initialization handshake completes.
24
26
  *
25
- * @param app - The newly created App instance
27
+ * @param app - The newly created `App` instance
26
28
  *
27
29
  * @example Register a notification handler
28
30
  * ```typescript
@@ -41,10 +43,10 @@ export interface UseAppOptions {
41
43
  onAppCreated?: (app: App) => void;
42
44
  }
43
45
  /**
44
- * State returned by the useApp hook.
46
+ * State returned by the {@link useApp} hook.
45
47
  */
46
48
  export interface AppState {
47
- /** The connected App instance, null during initialization */
49
+ /** The connected {@link App} instance, null during initialization */
48
50
  app: App | null;
49
51
  /** Whether initialization completed successfully */
50
52
  isConnected: boolean;
@@ -54,16 +56,19 @@ export interface AppState {
54
56
  /**
55
57
  * React hook to create and connect an MCP App.
56
58
  *
57
- * This hook manages the complete lifecycle of an {@link App}: creation, connection,
58
- * and cleanup. It automatically creates a {@link PostMessageTransport} to window.parent
59
- * and handles initialization.
59
+ * This hook manages {@link App} creation and connection. It automatically
60
+ * creates a {@link PostMessageTransport} to window.parent and handles
61
+ * initialization.
62
+ *
63
+ * This hook is part of the optional React integration. The core SDK (`App`,
64
+ * `PostMessageTransport`) is framework-agnostic and can be used with any UI
65
+ * framework or vanilla JavaScript.
60
66
  *
61
67
  * **Important**: The hook intentionally does NOT re-run when options change
62
68
  * to avoid reconnection loops. Options are only used during the initial mount.
63
- *
64
- * **Note**: This is part of the optional React integration. The core SDK
65
- * (App, PostMessageTransport) is framework-agnostic and can be
66
- * used with any UI framework or vanilla JavaScript.
69
+ * Furthermore, the `App` instance is NOT closed on unmount. This avoids cleanup
70
+ * issues during React Strict Mode's double-mount cycle. If you need to
71
+ * explicitly close the `App`, call {@link App.close} manually.
67
72
  *
68
73
  * @param options - Configuration for the app
69
74
  * @returns Current connection state and app instance. If connection fails during
@@ -3,15 +3,17 @@ import { App } from "../app";
3
3
  /**
4
4
  * React hook that automatically reports UI size changes to the host.
5
5
  *
6
- * Uses ResizeObserver to watch `document.body` and `document.documentElement` for
6
+ * Uses `ResizeObserver` to watch `document.body` and `document.documentElement` for
7
7
  * size changes and sends `ui/notifications/size-changed` notifications.
8
8
  *
9
+ * The hook automatically cleans up the `ResizeObserver` when the component unmounts.
10
+ *
9
11
  * **Note**: This hook is rarely needed since the {@link useApp} hook automatically enables
10
12
  * auto-resize by default. This hook is provided for advanced cases where you
11
13
  * create the {@link App} manually with `autoResize: false` and want to add auto-resize
12
14
  * behavior later.
13
15
  *
14
- * @param app - The connected App instance, or null during initialization
16
+ * @param app - The connected {@link App} instance, or null during initialization
15
17
  * @param elementRef - Currently unused. The hook always observes `document.body`
16
18
  * and `document.documentElement` regardless of this value. Passing a ref will
17
19
  * cause unnecessary effect re-runs; omit this parameter.
@@ -30,7 +32,7 @@ import { App } from "../app";
30
32
  * { autoResize: false } // Disable default auto-resize
31
33
  * );
32
34
  *
33
- * const transport = new PostMessageTransport(window.parent);
35
+ * const transport = new PostMessageTransport(window.parent, window.parent);
34
36
  * myApp.connect(transport)
35
37
  * .then(() => setApp(myApp))
36
38
  * .catch((err) => setError(err));
@@ -2,11 +2,13 @@ import { McpUiTheme } from "../types";
2
2
  /**
3
3
  * React hook that provides the current document theme reactively.
4
4
  *
5
- * Uses a MutationObserver to watch for changes to the `data-theme` attribute
5
+ * Uses a `MutationObserver` to watch for changes to the `data-theme` attribute
6
6
  * or `class` on `document.documentElement`. When the theme changes (e.g., from
7
7
  * host context updates), the hook automatically re-renders your component with
8
8
  * the new theme value.
9
9
  *
10
+ * The `MutationObserver` is automatically disconnected when the component unmounts.
11
+ *
10
12
  * @returns The current theme ("light" or "dark")
11
13
  *
12
14
  * @example Conditionally render based on theme
@@ -14,14 +14,13 @@ import { McpUiHostContext } from "../types";
14
14
  * this hook ensures they work correctly by setting the `color-scheme` property
15
15
  * based on the host's theme preference.
16
16
  *
17
- * @param app - The connected App instance, or null during initialization
17
+ * @param app - The connected {@link App} instance, or null during initialization
18
18
  * @param initialContext - Initial host context from the connection (optional).
19
19
  * If provided, styles and theme will be applied immediately on mount.
20
20
  *
21
- * @example Basic usage with useApp
21
+ * @example
22
22
  * ```tsx
23
- * import { useApp } from '@modelcontextprotocol/ext-apps/react';
24
- * import { useHostStyleVariables } from '@modelcontextprotocol/ext-apps/react';
23
+ * import { useApp, useHostStyleVariables } from '@modelcontextprotocol/ext-apps/react';
25
24
  *
26
25
  * function MyApp() {
27
26
  * const { app, isConnected } = useApp({
@@ -29,8 +28,8 @@ import { McpUiHostContext } from "../types";
29
28
  * capabilities: {},
30
29
  * });
31
30
  *
32
- * // Automatically apply host style variables and theme
33
- * useHostStyleVariables(app);
31
+ * // Apply host styles - pass initial context to apply styles from connect() immediately
32
+ * useHostStyleVariables(app, app?.getHostContext());
34
33
  *
35
34
  * return (
36
35
  * <div style={{ background: 'var(--color-background-primary)' }}>
@@ -40,15 +39,6 @@ import { McpUiHostContext } from "../types";
40
39
  * }
41
40
  * ```
42
41
  *
43
- * @example With initial context
44
- * ```tsx
45
- * const [hostContext, setHostContext] = useState<McpUiHostContext | null>(null);
46
- *
47
- * // ... get initial context from app.connect() result
48
- *
49
- * useHostStyleVariables(app, hostContext);
50
- * ```
51
- *
52
42
  * @see {@link applyHostStyleVariables} for the underlying styles function
53
43
  * @see {@link applyDocumentTheme} for the underlying theme function
54
44
  * @see {@link useHostFonts} for applying host fonts
@@ -67,7 +57,7 @@ export declare function useHostStyleVariables(app: App | null, initialContext?:
67
57
  * The hook also applies fonts from the initial host context when
68
58
  * the app first connects.
69
59
  *
70
- * @param app - The connected App instance, or null during initialization
60
+ * @param app - The connected {@link App} instance, or null during initialization
71
61
  * @param initialContext - Initial host context from the connection (optional).
72
62
  * If provided, fonts will be applied immediately on mount.
73
63
  *
@@ -107,13 +97,24 @@ export declare function useHostStyleVariables(app: App | null, initialContext?:
107
97
  */
108
98
  export declare function useHostFonts(app: App | null, initialContext?: McpUiHostContext | null): void;
109
99
  /**
110
- * React hook that applies host styles, fonts, and theme.
100
+ * Applies all host styling (CSS variables, theme, and fonts) to match the host application.
111
101
  *
112
102
  * This is a convenience hook that combines {@link useHostStyleVariables} and
113
103
  * {@link useHostFonts}. Use the individual hooks if you need more control.
114
104
  *
115
- * @param app - The connected App instance, or null during initialization
105
+ * @param app - The connected {@link App} instance, or null during initialization
116
106
  * @param initialContext - Initial host context from the connection (optional).
107
+ * Pass `app?.getHostContext()` to apply styles immediately on mount.
108
+ *
109
+ * @example
110
+ * ```tsx
111
+ * function MyApp() {
112
+ * const { app } = useApp({ appInfo, capabilities: {} });
113
+ * useHostStyles(app, app?.getHostContext());
114
+ *
115
+ * return <div style={{ background: 'var(--color-background-primary)' }}>...</div>;
116
+ * }
117
+ * ```
117
118
  *
118
119
  * @see {@link useHostStyleVariables} for style variables and theme only
119
120
  * @see {@link useHostFonts} for fonts only
@@ -1,7 +1,25 @@
1
1
  /**
2
- * Server Helpers for MCP Apps.
2
+ * Utilities for MCP servers to register tools and resources that display interactive UIs.
3
+ *
4
+ * Use these helpers instead of the base SDK's `registerTool` and `registerResource` when
5
+ * your tool should render an {@link app!App} in the client. They handle UI metadata normalization
6
+ * and provide sensible defaults for the MCP Apps MIME type ({@link RESOURCE_MIME_TYPE}).
3
7
  *
4
8
  * @module server-helpers
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps/server';
13
+ *
14
+ * // Register a tool that displays a widget
15
+ * registerAppTool(server, "weather", {
16
+ * description: "Get weather forecast",
17
+ * _meta: { ui: { resourceUri: "ui://weather/widget.html" } },
18
+ * }, handler);
19
+ *
20
+ * // Register the HTML resource the tool references
21
+ * registerAppResource(server, "Weather Widget", "ui://weather/widget.html", {}, readCallback);
22
+ * ```
5
23
  */
6
24
  import { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE, McpUiResourceMeta, McpUiToolMeta } from "../app.js";
7
25
  import type { McpServer, RegisteredTool, ResourceMetadata, ToolCallback, ReadResourceCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -10,7 +28,8 @@ import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
10
28
  export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE };
11
29
  export type { ResourceMetadata, ToolCallback, ReadResourceCallback };
12
30
  /**
13
- * Tool configuration (same as McpServer.registerTool).
31
+ * Base tool configuration matching the standard MCP server tool options.
32
+ * Extended by {@link McpUiAppToolConfig} to add UI metadata requirements.
14
33
  */
15
34
  export interface ToolConfig {
16
35
  title?: string;
@@ -21,7 +40,14 @@ export interface ToolConfig {
21
40
  _meta?: Record<string, unknown>;
22
41
  }
23
42
  /**
24
- * MCP App Tool configuration for `registerAppTool`.
43
+ * Configuration for tools that render an interactive UI.
44
+ *
45
+ * Extends {@link ToolConfig} with a required `_meta` field that specifies UI metadata.
46
+ * The UI resource can be specified in two ways:
47
+ * - `_meta.ui.resourceUri` (preferred)
48
+ * - `_meta["ui/resourceUri"]` (deprecated, for backward compatibility)
49
+ *
50
+ * @see {@link registerAppTool} for the recommended way to register app tools
25
51
  */
26
52
  export interface McpUiAppToolConfig extends ToolConfig {
27
53
  _meta: {
@@ -41,10 +67,22 @@ export interface McpUiAppToolConfig extends ToolConfig {
41
67
  });
42
68
  }
43
69
  /**
44
- * MCP App Resource configuration for `registerAppResource`.
70
+ * MCP App Resource configuration for {@link registerAppResource}.
71
+ *
72
+ * Extends the base MCP SDK `ResourceMetadata` with optional UI metadata
73
+ * for configuring security policies and rendering preferences.
74
+ *
75
+ * @see {@link registerAppResource} for usage
45
76
  */
46
77
  export interface McpUiAppResourceConfig extends ResourceMetadata {
78
+ /**
79
+ * Optional UI metadata for the resource.
80
+ * Used to configure security policies (CSP) and rendering preferences.
81
+ */
47
82
  _meta?: {
83
+ /**
84
+ * UI-specific metadata including CSP configuration and rendering preferences.
85
+ */
48
86
  ui?: McpUiResourceMeta;
49
87
  [key: string]: unknown;
50
88
  };
@@ -52,14 +90,16 @@ export interface McpUiAppResourceConfig extends ResourceMetadata {
52
90
  /**
53
91
  * Register an app tool with the MCP server.
54
92
  *
55
- * This is a convenience wrapper around `server.registerTool` that will allow more backwards-compatibility.
93
+ * This is a convenience wrapper around `server.registerTool` that normalizes
94
+ * UI metadata: if `_meta.ui.resourceUri` is set, the legacy `_meta["ui/resourceUri"]`
95
+ * key is also populated (and vice versa) for compatibility with older hosts.
56
96
  *
57
97
  * @param server - The MCP server instance
58
98
  * @param name - Tool name/identifier
59
- * @param config - Tool configuration with required `ui` field
60
- * @param handler - Tool handler function
99
+ * @param config - Tool configuration with `_meta` field containing UI metadata
100
+ * @param cb - Tool handler function
61
101
  *
62
- * @example
102
+ * @example Basic usage
63
103
  * ```typescript
64
104
  * import { registerAppTool } from '@modelcontextprotocol/ext-apps/server';
65
105
  * import { z } from 'zod';
@@ -69,13 +109,50 @@ export interface McpUiAppResourceConfig extends ResourceMetadata {
69
109
  * description: "Get current weather for a location",
70
110
  * inputSchema: { location: z.string() },
71
111
  * _meta: {
72
- * [RESOURCE_URI_META_KEY]: "ui://weather/widget.html",
112
+ * ui: { resourceUri: "ui://weather/widget.html" },
73
113
  * },
74
114
  * }, async (args) => {
75
115
  * const weather = await fetchWeather(args.location);
76
116
  * return { content: [{ type: "text", text: JSON.stringify(weather) }] };
77
117
  * });
78
118
  * ```
119
+ *
120
+ * @example Tool visibility - create app-only tools for UI actions
121
+ * ```typescript
122
+ * import { registerAppTool } from '@modelcontextprotocol/ext-apps/server';
123
+ * import { z } from 'zod';
124
+ *
125
+ * // Main tool - visible to both model and app (default)
126
+ * registerAppTool(server, "show-cart", {
127
+ * description: "Display the user's shopping cart",
128
+ * _meta: {
129
+ * ui: {
130
+ * resourceUri: "ui://shop/cart.html",
131
+ * visibility: ["model", "app"],
132
+ * },
133
+ * },
134
+ * }, async () => {
135
+ * const cart = await getCart();
136
+ * return { content: [{ type: "text", text: JSON.stringify(cart) }] };
137
+ * });
138
+ *
139
+ * // App-only tool - hidden from the model, only callable by the UI
140
+ * registerAppTool(server, "update-quantity", {
141
+ * description: "Update item quantity in cart",
142
+ * inputSchema: { itemId: z.string(), quantity: z.number() },
143
+ * _meta: {
144
+ * ui: {
145
+ * resourceUri: "ui://shop/cart.html",
146
+ * visibility: ["app"],
147
+ * },
148
+ * },
149
+ * }, async ({ itemId, quantity }) => {
150
+ * const cart = await updateCartItem(itemId, quantity);
151
+ * return { content: [{ type: "text", text: JSON.stringify(cart) }] };
152
+ * });
153
+ * ```
154
+ *
155
+ * @see {@link registerAppResource} to register the HTML resource referenced by the tool
79
156
  */
80
157
  export declare function registerAppTool<OutputArgs extends ZodRawShapeCompat | AnySchema, InputArgs extends undefined | ZodRawShapeCompat | AnySchema = undefined>(server: Pick<McpServer, "registerTool">, name: string, config: McpUiAppToolConfig & {
81
158
  inputSchema?: InputArgs;
@@ -85,22 +162,21 @@ export declare function registerAppTool<OutputArgs extends ZodRawShapeCompat | A
85
162
  * Register an app resource with the MCP server.
86
163
  *
87
164
  * This is a convenience wrapper around `server.registerResource` that:
88
- * - Defaults the MIME type to "text/html;profile=mcp-app"
165
+ * - Defaults the MIME type to {@link RESOURCE_MIME_TYPE} (`"text/html;profile=mcp-app"`)
89
166
  * - Provides a cleaner API matching the SDK's callback signature
90
167
  *
91
168
  * @param server - The MCP server instance
92
169
  * @param name - Human-readable resource name
93
- * @param uri - Resource URI (should match the `ui` field in tool config)
170
+ * @param uri - Resource URI (should match the `_meta.ui` field in tool config)
94
171
  * @param config - Resource configuration
95
172
  * @param readCallback - Callback that returns the resource contents
96
173
  *
97
- * @example
174
+ * @example Basic usage
98
175
  * ```typescript
99
176
  * import { registerAppResource } from '@modelcontextprotocol/ext-apps/server';
100
177
  *
101
178
  * registerAppResource(server, "Weather Widget", "ui://weather/widget.html", {
102
179
  * description: "Interactive weather display",
103
- * mimeType: RESOURCE_MIME_TYPE,
104
180
  * }, async () => ({
105
181
  * contents: [{
106
182
  * uri: "ui://weather/widget.html",
@@ -109,5 +185,22 @@ export declare function registerAppTool<OutputArgs extends ZodRawShapeCompat | A
109
185
  * }],
110
186
  * }));
111
187
  * ```
188
+ *
189
+ * @example With CSP configuration for external domains
190
+ * ```typescript
191
+ * registerAppResource(server, "Music Player", "ui://music/player.html", {
192
+ * description: "Audio player with external soundfonts",
193
+ * _meta: {
194
+ * ui: {
195
+ * csp: {
196
+ * connectDomains: ["https://api.example.com"], // For fetch/WebSocket
197
+ * resourceDomains: ["https://cdn.example.com"], // For scripts/styles/images
198
+ * },
199
+ * },
200
+ * },
201
+ * }, readCallback);
202
+ * ```
203
+ *
204
+ * @see {@link registerAppTool} to register tools that reference this resource
112
205
  */
113
206
  export declare function registerAppResource(server: Pick<McpServer, "registerResource">, name: string, uri: string, config: McpUiAppResourceConfig, readCallback: ReadResourceCallback): void;