@modelcontextprotocol/ext-apps 0.0.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.
- package/README.md +75 -0
- package/dist/src/app-bridge.d.ts +594 -0
- package/dist/src/app-bridge.js +29 -0
- package/dist/src/app-bridge.test.d.ts +1 -0
- package/dist/src/app.d.ts +620 -0
- package/dist/src/app.js +29 -0
- package/dist/src/message-transport.d.ts +129 -0
- package/dist/src/react/index.d.ts +34 -0
- package/dist/src/react/index.js +29 -0
- package/dist/src/react/useApp.d.ts +101 -0
- package/dist/src/react/useAutoResize.d.ts +51 -0
- package/dist/src/types.d.ts +838 -0
- package/package.json +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# @modelcontextprotocol/ext-apps
|
|
2
|
+
|
|
3
|
+
[](https://modelcontextprotocol.github.io/ext-apps/api/)
|
|
4
|
+
|
|
5
|
+
This repo contains the SDK and [specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx) for MCP Apps Extension ([SEP-1865](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1865)).
|
|
6
|
+
|
|
7
|
+
MCP Apps are a proposed standard inspired by [MCP-UI](https://mcpui.dev/) and [OpenAI's Apps SDK](https://developers.openai.com/apps-sdk/) to allow MCP Servers to display interactive UI elements in conversational MCP clients / chatbots.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This SDK serves two audiences:
|
|
12
|
+
|
|
13
|
+
### App Developers
|
|
14
|
+
|
|
15
|
+
Build interactive UIs that run inside MCP-enabled chat clients.
|
|
16
|
+
|
|
17
|
+
- **SDK for Apps**: `@modelcontextprotocol/ext-apps` — [API Docs](https://modelcontextprotocol.github.io/ext-apps/api/modules/app.html)
|
|
18
|
+
- **React hooks**: `@modelcontextprotocol/ext-apps/react` — [API Docs](https://modelcontextprotocol.github.io/ext-apps/api/modules/_modelcontextprotocol_ext-apps_react.html)
|
|
19
|
+
|
|
20
|
+
### Host Developers
|
|
21
|
+
|
|
22
|
+
Embed and communicate with MCP Apps in your chat application.
|
|
23
|
+
|
|
24
|
+
- **SDK for Hosts**: `@modelcontextprotocol/ext-apps/app-bridge` — [API Docs](https://modelcontextprotocol.github.io/ext-apps/api/modules/app-bridge.html)
|
|
25
|
+
|
|
26
|
+
There's no _supported_ host implementation in this repo (beyond the [examples/basic-host](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) example).
|
|
27
|
+
|
|
28
|
+
We have [contributed a tentative implementation](https://github.com/MCP-UI-Org/mcp-ui/pull/147) of hosting / iframing / sandboxing logic to the [MCP-UI](https://github.com/idosal/mcp-ui) repository, and expect OSS clients may use it, while other clients might roll their own hosting logic.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
This repo is in flux and isn't published to npm yet. When it is, it will use the `@modelcontextprotocol/ext-apps` package.
|
|
33
|
+
|
|
34
|
+
In the meantime you can depend on the SDK library in a Node.js project by installing it with its git URL:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -S git+https://github.com/modelcontextprotocol/ext-apps.git
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Your `package.json` will then look like:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@modelcontextprotocol/ext-apps": "git+https://github.com/modelcontextprotocol/ext-apps.git"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> [!NOTE]
|
|
51
|
+
> The build tools (`esbuild`, `tsx`, `typescript`) are in `dependencies` rather than `devDependencies`. This is intentional: it allows the `prepare` script to run when the package is installed from git, since npm doesn't install devDependencies for git dependencies.
|
|
52
|
+
>
|
|
53
|
+
> Once the package is published to npm with pre-built `dist/`, these can be moved back to `devDependencies`.
|
|
54
|
+
|
|
55
|
+
## Examples
|
|
56
|
+
|
|
57
|
+
- [`examples/basic-server-react`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) — Example MCP server with tools that return UI Apps (React)
|
|
58
|
+
- [`examples/basic-server-vanillajs`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) — Example MCP server with tools that return UI Apps (vanilla JS)
|
|
59
|
+
- [`examples/basic-host`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) — Bare-bones example of hosting MCP Apps
|
|
60
|
+
|
|
61
|
+
To run the examples end-to-end:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
npm i
|
|
65
|
+
npm run examples:start
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then open http://localhost:8080/
|
|
69
|
+
|
|
70
|
+
## Resources
|
|
71
|
+
|
|
72
|
+
- [Quickstart Guide](https://modelcontextprotocol.github.io/ext-apps/api/documents/Quickstart.html)
|
|
73
|
+
- [API Documentation](https://modelcontextprotocol.github.io/ext-apps/api/)
|
|
74
|
+
- [Draft Specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx)
|
|
75
|
+
- [SEP-1865 Discussion](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1865)
|
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
3
|
+
import { Implementation, LoggingMessageNotification, Notification, PingRequest, Request, Result } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { Protocol, ProtocolOptions, RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
5
|
+
import { type McpUiSandboxResourceReadyNotification, type McpUiSizeChangedNotification, type McpUiToolInputNotification, type McpUiToolInputPartialNotification, type McpUiToolResultNotification, McpUiAppCapabilities, McpUiHostCapabilities, McpUiHostContext, McpUiHostContextChangedNotification, McpUiInitializedNotification, McpUiMessageRequest, McpUiMessageResult, McpUiOpenLinkRequest, McpUiOpenLinkResult, McpUiResourceTeardownRequest, McpUiSandboxProxyReadyNotification } from "./types";
|
|
6
|
+
export * from "./types";
|
|
7
|
+
export { RESOURCE_URI_META_KEY } from "./app";
|
|
8
|
+
export { PostMessageTransport } from "./message-transport";
|
|
9
|
+
/**
|
|
10
|
+
* Options for configuring AppBridge behavior.
|
|
11
|
+
*
|
|
12
|
+
* @see ProtocolOptions from @modelcontextprotocol/sdk for available options
|
|
13
|
+
*/
|
|
14
|
+
export type HostOptions = ProtocolOptions & {
|
|
15
|
+
hostContext?: McpUiHostContext;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Protocol versions supported by this AppBridge implementation.
|
|
19
|
+
*
|
|
20
|
+
* The SDK automatically handles version negotiation during initialization.
|
|
21
|
+
* Hosts don't need to manage protocol versions manually.
|
|
22
|
+
*/
|
|
23
|
+
export declare const SUPPORTED_PROTOCOL_VERSIONS: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Extra metadata passed to request handlers.
|
|
26
|
+
*
|
|
27
|
+
* This type represents the additional context provided by the Protocol class
|
|
28
|
+
* when handling requests, including abort signals and session information.
|
|
29
|
+
* It is extracted from the MCP SDK's request handler signature.
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
type RequestHandlerExtra = Parameters<Parameters<AppBridge["setRequestHandler"]>[1]>[1];
|
|
34
|
+
/**
|
|
35
|
+
* Host-side bridge for communicating with a single Guest UI (App).
|
|
36
|
+
*
|
|
37
|
+
* AppBridge extends the MCP SDK's Protocol class and acts as a proxy between
|
|
38
|
+
* the host application and a Guest UI running in an iframe. It automatically
|
|
39
|
+
* forwards MCP server capabilities (tools, resources, prompts) to the Guest UI
|
|
40
|
+
* and handles the initialization handshake.
|
|
41
|
+
*
|
|
42
|
+
* ## Architecture
|
|
43
|
+
*
|
|
44
|
+
* **Guest UI ↔ AppBridge ↔ Host ↔ MCP Server**
|
|
45
|
+
*
|
|
46
|
+
* The bridge proxies requests from the Guest UI to the MCP server and forwards
|
|
47
|
+
* responses back. It also sends host-initiated notifications like tool input
|
|
48
|
+
* and results to the Guest UI.
|
|
49
|
+
*
|
|
50
|
+
* ## Lifecycle
|
|
51
|
+
*
|
|
52
|
+
* 1. **Create**: Instantiate AppBridge with MCP client and capabilities
|
|
53
|
+
* 2. **Connect**: Call `connect()` with transport to establish communication
|
|
54
|
+
* 3. **Wait for init**: Guest UI sends initialize request, bridge responds
|
|
55
|
+
* 4. **Send data**: Call `sendToolInput()`, `sendToolResult()`, etc.
|
|
56
|
+
* 5. **Teardown**: Call `sendResourceTeardown()` before unmounting iframe
|
|
57
|
+
*
|
|
58
|
+
* @example Basic usage
|
|
59
|
+
* ```typescript
|
|
60
|
+
* import { AppBridge, PostMessageTransport } from '@modelcontextprotocol/ext-apps/app-bridge';
|
|
61
|
+
* import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
62
|
+
*
|
|
63
|
+
* // Create MCP client for the server
|
|
64
|
+
* const client = new Client({
|
|
65
|
+
* name: "MyHost",
|
|
66
|
+
* version: "1.0.0",
|
|
67
|
+
* });
|
|
68
|
+
* await client.connect(serverTransport);
|
|
69
|
+
*
|
|
70
|
+
* // Create bridge for the Guest UI
|
|
71
|
+
* const bridge = new AppBridge(
|
|
72
|
+
* client,
|
|
73
|
+
* { name: "MyHost", version: "1.0.0" },
|
|
74
|
+
* { openLinks: {}, serverTools: {}, logging: {} }
|
|
75
|
+
* );
|
|
76
|
+
*
|
|
77
|
+
* // Set up iframe and connect
|
|
78
|
+
* const iframe = document.getElementById('app') as HTMLIFrameElement;
|
|
79
|
+
* const transport = new PostMessageTransport(
|
|
80
|
+
* iframe.contentWindow!,
|
|
81
|
+
* iframe.contentWindow!,
|
|
82
|
+
* );
|
|
83
|
+
*
|
|
84
|
+
* bridge.oninitialized = () => {
|
|
85
|
+
* console.log("Guest UI initialized");
|
|
86
|
+
* // Now safe to send tool input
|
|
87
|
+
* bridge.sendToolInput({ arguments: { location: "NYC" } });
|
|
88
|
+
* };
|
|
89
|
+
*
|
|
90
|
+
* await bridge.connect(transport);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare class AppBridge extends Protocol<Request, Notification, Result> {
|
|
94
|
+
private _client;
|
|
95
|
+
private _hostInfo;
|
|
96
|
+
private _capabilities;
|
|
97
|
+
private _appCapabilities?;
|
|
98
|
+
private _hostContext;
|
|
99
|
+
private _appInfo?;
|
|
100
|
+
/**
|
|
101
|
+
* Create a new AppBridge instance.
|
|
102
|
+
*
|
|
103
|
+
* @param _client - MCP client connected to the server (for proxying requests)
|
|
104
|
+
* @param _hostInfo - Host application identification (name and version)
|
|
105
|
+
* @param _capabilities - Features and capabilities the host supports
|
|
106
|
+
* @param options - Configuration options (inherited from Protocol)
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const bridge = new AppBridge(
|
|
111
|
+
* mcpClient,
|
|
112
|
+
* { name: "MyHost", version: "1.0.0" },
|
|
113
|
+
* { openLinks: {}, serverTools: {}, logging: {} }
|
|
114
|
+
* );
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
constructor(_client: Client, _hostInfo: Implementation, _capabilities: McpUiHostCapabilities, options?: HostOptions);
|
|
118
|
+
/**
|
|
119
|
+
* Get the Guest UI's capabilities discovered during initialization.
|
|
120
|
+
*
|
|
121
|
+
* Returns the capabilities that the Guest UI advertised during its
|
|
122
|
+
* initialization request. Returns `undefined` if called before
|
|
123
|
+
* initialization completes.
|
|
124
|
+
*
|
|
125
|
+
* @returns Guest UI capabilities, or `undefined` if not yet initialized
|
|
126
|
+
*
|
|
127
|
+
* @example Check Guest UI capabilities after initialization
|
|
128
|
+
* ```typescript
|
|
129
|
+
* bridge.oninitialized = () => {
|
|
130
|
+
* const caps = bridge.getAppCapabilities();
|
|
131
|
+
* if (caps?.tools) {
|
|
132
|
+
* console.log("Guest UI provides tools");
|
|
133
|
+
* }
|
|
134
|
+
* };
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* @see {@link McpUiAppCapabilities} for the capabilities structure
|
|
138
|
+
*/
|
|
139
|
+
getAppCapabilities(): McpUiAppCapabilities | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* Get the Guest UI's implementation info discovered during initialization.
|
|
142
|
+
*
|
|
143
|
+
* Returns the Guest UI's name and version as provided in its initialization
|
|
144
|
+
* request. Returns `undefined` if called before initialization completes.
|
|
145
|
+
*
|
|
146
|
+
* @returns Guest UI implementation info, or `undefined` if not yet initialized
|
|
147
|
+
*
|
|
148
|
+
* @example Log Guest UI information after initialization
|
|
149
|
+
* ```typescript
|
|
150
|
+
* bridge.oninitialized = () => {
|
|
151
|
+
* const appInfo = bridge.getAppVersion();
|
|
152
|
+
* if (appInfo) {
|
|
153
|
+
* console.log(`Guest UI: ${appInfo.name} v${appInfo.version}`);
|
|
154
|
+
* }
|
|
155
|
+
* };
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
getAppVersion(): Implementation | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* Optional handler for ping requests from the Guest UI.
|
|
161
|
+
*
|
|
162
|
+
* The Guest UI can send standard MCP `ping` requests to verify the connection
|
|
163
|
+
* is alive. The AppBridge automatically responds with an empty object, but this
|
|
164
|
+
* handler allows the host to observe or log ping activity.
|
|
165
|
+
*
|
|
166
|
+
* Unlike the other handlers which use setters, this is a direct property
|
|
167
|
+
* assignment. It is optional; if not set, pings are still handled automatically.
|
|
168
|
+
*
|
|
169
|
+
* @param params - Empty params object from the ping request
|
|
170
|
+
* @param extra - Request metadata (abort signal, session info)
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* bridge.onping = (params, extra) => {
|
|
175
|
+
* console.log("Received ping from Guest UI");
|
|
176
|
+
* };
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
onping?: (params: PingRequest["params"], extra: RequestHandlerExtra) => void;
|
|
180
|
+
/**
|
|
181
|
+
* Register a handler for size change notifications from the Guest UI.
|
|
182
|
+
*
|
|
183
|
+
* The Guest UI sends `ui/notifications/size-changed` when its rendered content
|
|
184
|
+
* size changes, typically via ResizeObserver. Set this callback to dynamically
|
|
185
|
+
* adjust the iframe container dimensions based on the Guest UI's content.
|
|
186
|
+
*
|
|
187
|
+
* Note: This is for Guest UI → Host communication. To notify the Guest UI of
|
|
188
|
+
* host viewport changes, use {@link app.App.sendSizeChanged}.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* bridge.onsizechange = ({ width, height }) => {
|
|
193
|
+
* if (width != null) {
|
|
194
|
+
* iframe.style.width = `${width}px`;
|
|
195
|
+
* }
|
|
196
|
+
* if (height != null) {
|
|
197
|
+
* iframe.style.height = `${height}px`;
|
|
198
|
+
* }
|
|
199
|
+
* };
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* @see {@link McpUiSizeChangedNotification} for the notification type
|
|
203
|
+
* @see {@link app.App.sendSizeChanged} for Host → Guest UI size notifications
|
|
204
|
+
*/
|
|
205
|
+
set onsizechange(callback: (params: McpUiSizeChangedNotification["params"]) => void);
|
|
206
|
+
/**
|
|
207
|
+
* Register a handler for sandbox proxy ready notifications.
|
|
208
|
+
*
|
|
209
|
+
* This is an internal callback used by web-based hosts implementing the
|
|
210
|
+
* double-iframe sandbox architecture. The sandbox proxy sends
|
|
211
|
+
* `ui/notifications/sandbox-proxy-ready` after it loads and is ready to receive
|
|
212
|
+
* HTML content.
|
|
213
|
+
*
|
|
214
|
+
* When this fires, the host should call {@link sendSandboxResourceReady} with
|
|
215
|
+
* the HTML content to load into the inner sandboxed iframe.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* bridge.onsandboxready = async () => {
|
|
220
|
+
* const resource = await mcpClient.request(
|
|
221
|
+
* { method: "resources/read", params: { uri: "ui://my-app" } },
|
|
222
|
+
* ReadResourceResultSchema
|
|
223
|
+
* );
|
|
224
|
+
*
|
|
225
|
+
* bridge.sendSandboxResourceReady({
|
|
226
|
+
* html: resource.contents[0].text,
|
|
227
|
+
* sandbox: "allow-scripts"
|
|
228
|
+
* });
|
|
229
|
+
* };
|
|
230
|
+
* ```
|
|
231
|
+
*
|
|
232
|
+
* @internal
|
|
233
|
+
* @see {@link McpUiSandboxProxyReadyNotification} for the notification type
|
|
234
|
+
* @see {@link sendSandboxResourceReady} for sending content to the sandbox
|
|
235
|
+
*/
|
|
236
|
+
set onsandboxready(callback: (params: McpUiSandboxProxyReadyNotification["params"]) => void);
|
|
237
|
+
/**
|
|
238
|
+
* Called when the Guest UI completes initialization.
|
|
239
|
+
*
|
|
240
|
+
* Set this callback to be notified when the Guest UI has finished its
|
|
241
|
+
* initialization handshake and is ready to receive tool input and other data.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* bridge.oninitialized = () => {
|
|
246
|
+
* console.log("Guest UI ready");
|
|
247
|
+
* bridge.sendToolInput({ arguments: toolArgs });
|
|
248
|
+
* };
|
|
249
|
+
* ```
|
|
250
|
+
*
|
|
251
|
+
* @see {@link McpUiInitializedNotification} for the notification type
|
|
252
|
+
* @see {@link sendToolInput} for sending tool arguments to the Guest UI
|
|
253
|
+
*/
|
|
254
|
+
set oninitialized(callback: (params: McpUiInitializedNotification["params"]) => void);
|
|
255
|
+
/**
|
|
256
|
+
* Register a handler for message requests from the Guest UI.
|
|
257
|
+
*
|
|
258
|
+
* The Guest UI sends `ui/message` requests when it wants to add a message to
|
|
259
|
+
* the host's chat interface. This enables interactive apps to communicate with
|
|
260
|
+
* the user through the conversation thread.
|
|
261
|
+
*
|
|
262
|
+
* The handler should process the message (add it to the chat) and return a
|
|
263
|
+
* result indicating success or failure. For security, the host should NOT
|
|
264
|
+
* return conversation content or follow-up results to prevent information
|
|
265
|
+
* leakage.
|
|
266
|
+
*
|
|
267
|
+
* @param callback - Handler that receives message params and returns a result
|
|
268
|
+
* - params.role - Message role (currently only "user" is supported)
|
|
269
|
+
* - params.content - Message content blocks (text, image, etc.)
|
|
270
|
+
* - extra - Request metadata (abort signal, session info)
|
|
271
|
+
* - Returns: Promise<McpUiMessageResult> with optional isError flag
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* bridge.onmessage = async ({ role, content }, extra) => {
|
|
276
|
+
* try {
|
|
277
|
+
* await chatManager.addMessage({ role, content, source: "app" });
|
|
278
|
+
* return {}; // Success
|
|
279
|
+
* } catch (error) {
|
|
280
|
+
* console.error("Failed to add message:", error);
|
|
281
|
+
* return { isError: true };
|
|
282
|
+
* }
|
|
283
|
+
* };
|
|
284
|
+
* ```
|
|
285
|
+
*
|
|
286
|
+
* @see {@link McpUiMessageRequest} for the request type
|
|
287
|
+
* @see {@link McpUiMessageResult} for the result type
|
|
288
|
+
*/
|
|
289
|
+
set onmessage(callback: (params: McpUiMessageRequest["params"], extra: RequestHandlerExtra) => Promise<McpUiMessageResult>);
|
|
290
|
+
/**
|
|
291
|
+
* Register a handler for external link requests from the Guest UI.
|
|
292
|
+
*
|
|
293
|
+
* The Guest UI sends `ui/open-link` requests when it wants to open an external
|
|
294
|
+
* URL in the host's default browser. The handler should validate the URL and
|
|
295
|
+
* open it according to the host's security policy and user preferences.
|
|
296
|
+
*
|
|
297
|
+
* The host MAY:
|
|
298
|
+
* - Show a confirmation dialog before opening
|
|
299
|
+
* - Block URLs based on a security policy or allowlist
|
|
300
|
+
* - Log the request for audit purposes
|
|
301
|
+
* - Reject the request entirely
|
|
302
|
+
*
|
|
303
|
+
* @param callback - Handler that receives URL params and returns a result
|
|
304
|
+
* - params.url - URL to open in the host's browser
|
|
305
|
+
* - extra - Request metadata (abort signal, session info)
|
|
306
|
+
* - Returns: Promise<McpUiOpenLinkResult> with optional isError flag
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```typescript
|
|
310
|
+
* bridge.onopenlink = async ({ url }, extra) => {
|
|
311
|
+
* if (!isAllowedDomain(url)) {
|
|
312
|
+
* console.warn("Blocked external link:", url);
|
|
313
|
+
* return { isError: true };
|
|
314
|
+
* }
|
|
315
|
+
*
|
|
316
|
+
* const confirmed = await showDialog({
|
|
317
|
+
* message: `Open external link?\n${url}`,
|
|
318
|
+
* buttons: ["Open", "Cancel"]
|
|
319
|
+
* });
|
|
320
|
+
*
|
|
321
|
+
* if (confirmed) {
|
|
322
|
+
* window.open(url, "_blank", "noopener,noreferrer");
|
|
323
|
+
* return {};
|
|
324
|
+
* }
|
|
325
|
+
*
|
|
326
|
+
* return { isError: true };
|
|
327
|
+
* };
|
|
328
|
+
* ```
|
|
329
|
+
*
|
|
330
|
+
* @see {@link McpUiOpenLinkRequest} for the request type
|
|
331
|
+
* @see {@link McpUiOpenLinkResult} for the result type
|
|
332
|
+
*/
|
|
333
|
+
set onopenlink(callback: (params: McpUiOpenLinkRequest["params"], extra: RequestHandlerExtra) => Promise<McpUiOpenLinkResult>);
|
|
334
|
+
/**
|
|
335
|
+
* Register a handler for logging messages from the Guest UI.
|
|
336
|
+
*
|
|
337
|
+
* The Guest UI sends standard MCP `notifications/message` (logging) notifications
|
|
338
|
+
* to report debugging information, errors, warnings, and other telemetry to the
|
|
339
|
+
* host. The host can display these in a console, log them to a file, or send
|
|
340
|
+
* them to a monitoring service.
|
|
341
|
+
*
|
|
342
|
+
* This uses the standard MCP logging notification format, not a UI-specific
|
|
343
|
+
* message type.
|
|
344
|
+
*
|
|
345
|
+
* @param callback - Handler that receives logging params
|
|
346
|
+
* - params.level - Log level: "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency"
|
|
347
|
+
* - params.logger - Optional logger name/identifier
|
|
348
|
+
* - params.data - Log message and optional structured data
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```typescript
|
|
352
|
+
* bridge.onloggingmessage = ({ level, logger, data }) => {
|
|
353
|
+
* const prefix = logger ? `[${logger}]` : "[Guest UI]";
|
|
354
|
+
* console[level === "error" ? "error" : "log"](
|
|
355
|
+
* `${prefix} ${level.toUpperCase()}:`,
|
|
356
|
+
* data
|
|
357
|
+
* );
|
|
358
|
+
* };
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
set onloggingmessage(callback: (params: LoggingMessageNotification["params"]) => void);
|
|
362
|
+
/**
|
|
363
|
+
* Verify that the guest supports the capability required for the given request method.
|
|
364
|
+
* @internal
|
|
365
|
+
*/
|
|
366
|
+
assertCapabilityForMethod(method: Request["method"]): void;
|
|
367
|
+
/**
|
|
368
|
+
* Verify that a request handler is registered and supported for the given method.
|
|
369
|
+
* @internal
|
|
370
|
+
*/
|
|
371
|
+
assertRequestHandlerCapability(method: Request["method"]): void;
|
|
372
|
+
/**
|
|
373
|
+
* Verify that the host supports the capability required for the given notification method.
|
|
374
|
+
* @internal
|
|
375
|
+
*/
|
|
376
|
+
assertNotificationCapability(method: Notification["method"]): void;
|
|
377
|
+
/**
|
|
378
|
+
* Verify that task creation is supported for the given request method.
|
|
379
|
+
* @internal
|
|
380
|
+
*/
|
|
381
|
+
protected assertTaskCapability(_method: string): void;
|
|
382
|
+
/**
|
|
383
|
+
* Verify that task handler is supported for the given method.
|
|
384
|
+
* @internal
|
|
385
|
+
*/
|
|
386
|
+
protected assertTaskHandlerCapability(_method: string): void;
|
|
387
|
+
/**
|
|
388
|
+
* Get the host capabilities passed to the constructor.
|
|
389
|
+
*
|
|
390
|
+
* @returns Host capabilities object
|
|
391
|
+
*
|
|
392
|
+
* @see {@link McpUiHostCapabilities} for the capabilities structure
|
|
393
|
+
*/
|
|
394
|
+
getCapabilities(): McpUiHostCapabilities;
|
|
395
|
+
/**
|
|
396
|
+
* Handle the ui/initialize request from the guest.
|
|
397
|
+
* @internal
|
|
398
|
+
*/
|
|
399
|
+
private _oninitialize;
|
|
400
|
+
/**
|
|
401
|
+
* Update the host context and notify the Guest UI of changes.
|
|
402
|
+
*
|
|
403
|
+
* Compares the new context with the current context and sends a
|
|
404
|
+
* `ui/notifications/host-context-changed` notification containing only the
|
|
405
|
+
* fields that have changed. If no fields have changed, no notification is sent.
|
|
406
|
+
*
|
|
407
|
+
* Common use cases include notifying the Guest UI when:
|
|
408
|
+
* - Theme changes (light/dark mode toggle)
|
|
409
|
+
* - Viewport size changes (window resize)
|
|
410
|
+
* - Display mode changes (inline/fullscreen)
|
|
411
|
+
* - Locale or timezone changes
|
|
412
|
+
*
|
|
413
|
+
* @param hostContext - The complete new host context state
|
|
414
|
+
*
|
|
415
|
+
* @example Update theme when user toggles dark mode
|
|
416
|
+
* ```typescript
|
|
417
|
+
* bridge.setHostContext({ theme: "dark" });
|
|
418
|
+
* ```
|
|
419
|
+
*
|
|
420
|
+
* @example Update multiple context fields
|
|
421
|
+
* ```typescript
|
|
422
|
+
* bridge.setHostContext({
|
|
423
|
+
* theme: "dark",
|
|
424
|
+
* viewport: { width: 800, height: 600 }
|
|
425
|
+
* });
|
|
426
|
+
* ```
|
|
427
|
+
*
|
|
428
|
+
* @see {@link McpUiHostContext} for the context structure
|
|
429
|
+
* @see {@link McpUiHostContextChangedNotification} for the notification type
|
|
430
|
+
*/
|
|
431
|
+
setHostContext(hostContext: McpUiHostContext): void;
|
|
432
|
+
/**
|
|
433
|
+
* Send a host context change notification to the app.
|
|
434
|
+
* Only sends the fields that have changed (partial update).
|
|
435
|
+
*/
|
|
436
|
+
sendHostContextChange(params: McpUiHostContextChangedNotification["params"]): Promise<void> | void;
|
|
437
|
+
/**
|
|
438
|
+
* Send complete tool arguments to the Guest UI.
|
|
439
|
+
*
|
|
440
|
+
* The host MUST send this notification after the Guest UI completes initialization
|
|
441
|
+
* (after {@link oninitialized} callback fires) and complete tool arguments become available.
|
|
442
|
+
* This notification is sent exactly once and is required before {@link sendToolResult}.
|
|
443
|
+
*
|
|
444
|
+
* @param params - Complete tool call arguments
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* ```typescript
|
|
448
|
+
* bridge.oninitialized = () => {
|
|
449
|
+
* bridge.sendToolInput({
|
|
450
|
+
* arguments: { location: "New York", units: "metric" }
|
|
451
|
+
* });
|
|
452
|
+
* };
|
|
453
|
+
* ```
|
|
454
|
+
*
|
|
455
|
+
* @see {@link McpUiToolInputNotification} for the notification type
|
|
456
|
+
* @see {@link oninitialized} for the initialization callback
|
|
457
|
+
* @see {@link sendToolResult} for sending results after execution
|
|
458
|
+
*/
|
|
459
|
+
sendToolInput(params: McpUiToolInputNotification["params"]): Promise<void>;
|
|
460
|
+
/**
|
|
461
|
+
* Send streaming partial tool arguments to the Guest UI.
|
|
462
|
+
*
|
|
463
|
+
* The host MAY send this notification zero or more times while tool arguments
|
|
464
|
+
* are being streamed, before {@link sendToolInput} is called with complete
|
|
465
|
+
* arguments. This enables progressive rendering of tool arguments in the
|
|
466
|
+
* Guest UI.
|
|
467
|
+
*
|
|
468
|
+
* The arguments represent best-effort recovery of incomplete JSON. Guest UIs
|
|
469
|
+
* SHOULD handle missing or changing fields gracefully between notifications.
|
|
470
|
+
*
|
|
471
|
+
* @param params - Partial tool call arguments (may be incomplete)
|
|
472
|
+
*
|
|
473
|
+
* @example Stream partial arguments as they arrive
|
|
474
|
+
* ```typescript
|
|
475
|
+
* // As streaming progresses...
|
|
476
|
+
* bridge.sendToolInputPartial({ arguments: { loc: "N" } });
|
|
477
|
+
* bridge.sendToolInputPartial({ arguments: { location: "New" } });
|
|
478
|
+
* bridge.sendToolInputPartial({ arguments: { location: "New York" } });
|
|
479
|
+
*
|
|
480
|
+
* // When complete, send final input
|
|
481
|
+
* bridge.sendToolInput({ arguments: { location: "New York", units: "metric" } });
|
|
482
|
+
* ```
|
|
483
|
+
*
|
|
484
|
+
* @see {@link McpUiToolInputPartialNotification} for the notification type
|
|
485
|
+
* @see {@link sendToolInput} for sending complete arguments
|
|
486
|
+
*/
|
|
487
|
+
sendToolInputPartial(params: McpUiToolInputPartialNotification["params"]): Promise<void>;
|
|
488
|
+
/**
|
|
489
|
+
* Send tool execution result to the Guest UI.
|
|
490
|
+
*
|
|
491
|
+
* The host MUST send this notification when tool execution completes successfully,
|
|
492
|
+
* provided the UI is still displayed. If the UI was closed before execution
|
|
493
|
+
* completes, the host MAY skip this notification. This must be sent after
|
|
494
|
+
* {@link sendToolInput}.
|
|
495
|
+
*
|
|
496
|
+
* @param params - Standard MCP tool execution result
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
501
|
+
*
|
|
502
|
+
* const result = await mcpClient.request(
|
|
503
|
+
* { method: "tools/call", params: { name: "get_weather", arguments: args } },
|
|
504
|
+
* CallToolResultSchema
|
|
505
|
+
* );
|
|
506
|
+
* bridge.sendToolResult(result);
|
|
507
|
+
* ```
|
|
508
|
+
*
|
|
509
|
+
* @see {@link McpUiToolResultNotification} for the notification type
|
|
510
|
+
* @see {@link sendToolInput} for sending tool arguments before results
|
|
511
|
+
*/
|
|
512
|
+
sendToolResult(params: McpUiToolResultNotification["params"]): Promise<void>;
|
|
513
|
+
/**
|
|
514
|
+
* Send HTML resource to the sandbox proxy for secure loading.
|
|
515
|
+
*
|
|
516
|
+
* This is an internal method used by web-based hosts implementing the
|
|
517
|
+
* double-iframe sandbox architecture. After the sandbox proxy signals readiness
|
|
518
|
+
* via `ui/notifications/sandbox-proxy-ready`, the host sends this notification
|
|
519
|
+
* with the HTML content to load.
|
|
520
|
+
*
|
|
521
|
+
* @param params - HTML content and sandbox configuration:
|
|
522
|
+
* - `html`: The HTML content to load into the sandboxed iframe
|
|
523
|
+
* - `sandbox`: Optional sandbox attribute value (e.g., "allow-scripts")
|
|
524
|
+
*
|
|
525
|
+
* @internal
|
|
526
|
+
* @see {@link onsandboxready} for handling the sandbox proxy ready notification
|
|
527
|
+
*/
|
|
528
|
+
sendSandboxResourceReady(params: McpUiSandboxResourceReadyNotification["params"]): Promise<void>;
|
|
529
|
+
/**
|
|
530
|
+
* Request graceful shutdown of the Guest UI.
|
|
531
|
+
*
|
|
532
|
+
* The host MUST send this request before tearing down the UI resource (before
|
|
533
|
+
* unmounting the iframe). This gives the Guest UI an opportunity to save state,
|
|
534
|
+
* cancel pending operations, or show confirmation dialogs.
|
|
535
|
+
*
|
|
536
|
+
* The host SHOULD wait for the response before unmounting to prevent data loss.
|
|
537
|
+
*
|
|
538
|
+
* @param params - Empty params object
|
|
539
|
+
* @param options - Request options (timeout, etc.)
|
|
540
|
+
* @returns Promise resolving when Guest UI confirms readiness for teardown
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```typescript
|
|
544
|
+
* try {
|
|
545
|
+
* await bridge.sendResourceTeardown({});
|
|
546
|
+
* // Guest UI is ready, safe to unmount iframe
|
|
547
|
+
* iframe.remove();
|
|
548
|
+
* } catch (error) {
|
|
549
|
+
* console.error("Teardown failed:", error);
|
|
550
|
+
* }
|
|
551
|
+
* ```
|
|
552
|
+
*/
|
|
553
|
+
sendResourceTeardown(params: McpUiResourceTeardownRequest["params"], options?: RequestOptions): Promise<import("./types").McpUiResourceTeardownResult>;
|
|
554
|
+
private forwardRequest;
|
|
555
|
+
private forwardNotification;
|
|
556
|
+
/**
|
|
557
|
+
* Connect to the Guest UI via transport and set up message forwarding.
|
|
558
|
+
*
|
|
559
|
+
* This method establishes the transport connection and automatically sets up
|
|
560
|
+
* request/notification forwarding based on the MCP server's capabilities.
|
|
561
|
+
* It proxies the following server capabilities to the Guest UI:
|
|
562
|
+
* - Tools (tools/call, tools/list_changed)
|
|
563
|
+
* - Resources (resources/list, resources/read, resources/templates/list, resources/list_changed)
|
|
564
|
+
* - Prompts (prompts/list, prompts/list_changed)
|
|
565
|
+
*
|
|
566
|
+
* After calling connect, wait for the `oninitialized` callback before sending
|
|
567
|
+
* tool input and other data to the Guest UI.
|
|
568
|
+
*
|
|
569
|
+
* @param transport - Transport layer (typically PostMessageTransport)
|
|
570
|
+
* @returns Promise resolving when connection is established
|
|
571
|
+
*
|
|
572
|
+
* @throws {Error} If server capabilities are not available. This occurs when
|
|
573
|
+
* connect() is called before the MCP client has completed its initialization
|
|
574
|
+
* with the server. Ensure `await client.connect()` completes before calling
|
|
575
|
+
* `bridge.connect()`.
|
|
576
|
+
*
|
|
577
|
+
* @example
|
|
578
|
+
* ```typescript
|
|
579
|
+
* const bridge = new AppBridge(mcpClient, hostInfo, capabilities);
|
|
580
|
+
* const transport = new PostMessageTransport(
|
|
581
|
+
* iframe.contentWindow!,
|
|
582
|
+
* iframe.contentWindow!,
|
|
583
|
+
* );
|
|
584
|
+
*
|
|
585
|
+
* bridge.oninitialized = () => {
|
|
586
|
+
* console.log("Guest UI ready");
|
|
587
|
+
* bridge.sendToolInput({ arguments: toolArgs });
|
|
588
|
+
* };
|
|
589
|
+
*
|
|
590
|
+
* await bridge.connect(transport);
|
|
591
|
+
* ```
|
|
592
|
+
*/
|
|
593
|
+
connect(transport: Transport): Promise<void>;
|
|
594
|
+
}
|