@mcp-ts/sdk 1.3.10 → 1.5.0
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 +20 -27
- package/dist/adapters/agui-adapter.d.mts +16 -0
- package/dist/adapters/agui-adapter.d.ts +16 -0
- package/dist/adapters/agui-adapter.js +185 -0
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs +185 -0
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +2 -0
- package/dist/adapters/agui-middleware.d.ts +2 -0
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/adapters/ai-adapter.d.mts +21 -0
- package/dist/adapters/ai-adapter.d.ts +21 -0
- package/dist/adapters/ai-adapter.js +175 -0
- package/dist/adapters/ai-adapter.js.map +1 -1
- package/dist/adapters/ai-adapter.mjs +175 -0
- package/dist/adapters/ai-adapter.mjs.map +1 -1
- package/dist/adapters/langchain-adapter.d.mts +16 -0
- package/dist/adapters/langchain-adapter.d.ts +16 -0
- package/dist/adapters/langchain-adapter.js +179 -0
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/langchain-adapter.mjs +179 -0
- package/dist/adapters/langchain-adapter.mjs.map +1 -1
- package/dist/client/index.d.mts +4 -190
- package/dist/client/index.d.ts +4 -190
- package/dist/client/index.js +218 -54
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +215 -55
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +31 -17
- package/dist/client/react.d.ts +31 -17
- package/dist/client/react.js +447 -103
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +443 -105
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +5 -4
- package/dist/client/vue.d.ts +5 -4
- package/dist/client/vue.js +239 -63
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +236 -64
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index-DcYfpY3H.d.mts +295 -0
- package/dist/index-GfC_eNEv.d.ts +295 -0
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1120 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1097 -60
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +18 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +18 -5
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +86 -4
- package/dist/shared/index.d.ts +86 -4
- package/dist/shared/index.js +874 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +865 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/tool-router-Bo8qZbsD.d.ts +325 -0
- package/dist/tool-router-XnWVxPzv.d.mts +325 -0
- package/dist/{types-CW6lghof.d.mts → types-CfCoIsWI.d.mts} +27 -1
- package/dist/{types-CW6lghof.d.ts → types-CfCoIsWI.d.ts} +27 -1
- package/package.json +15 -12
- package/src/adapters/agui-adapter.ts +79 -0
- package/src/adapters/ai-adapter.ts +75 -0
- package/src/adapters/langchain-adapter.ts +75 -1
- package/src/client/core/app-host.ts +252 -65
- package/src/client/core/constants.ts +30 -0
- package/src/client/index.ts +6 -1
- package/src/client/react/index.ts +3 -0
- package/src/client/react/use-app-host.ts +8 -15
- package/src/client/react/use-mcp-apps.tsx +262 -49
- package/src/client/react/use-mcp.ts +23 -12
- package/src/client/utils/app-host-utils.ts +62 -0
- package/src/client/vue/use-mcp.ts +23 -12
- package/src/server/index.ts +2 -0
- package/src/server/mcp/oauth-client.ts +34 -9
- package/src/shared/index.ts +36 -0
- package/src/shared/meta-tools.ts +387 -0
- package/src/shared/schema-compressor.ts +124 -0
- package/src/shared/tool-index.ts +499 -0
- package/src/shared/tool-router.ts +469 -0
- package/src/shared/types.ts +30 -0
package/dist/client/index.d.ts
CHANGED
|
@@ -1,191 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export { D as Disposable, a as DisposableStore, E as Emitter, b as Event, c as McpConnectionState } from '../events-CK3N--3g.js';
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export { A as APP_HOST_DEFAULTS, a as AppHost, D as DEFAULT_MCP_APP_CSP, S as SANDBOX_PROXY_READY_METHOD, b as SANDBOX_RESOURCE_READY_METHOD, c as SSEClient, d as SSEClientOptions } from '../index-GfC_eNEv.js';
|
|
2
|
+
export { D as Disposable, a as DisposableStore, E as Emitter, b as Event, M as McpConnectionEvent, c as McpConnectionState, d as McpObservabilityEvent } from '../events-CK3N--3g.js';
|
|
3
|
+
export { p as McpRpcRequest, q as McpRpcResponse, v as ToolInfo } from '../types-CfCoIsWI.js';
|
|
4
|
+
import '@modelcontextprotocol/ext-apps/app-bridge';
|
|
5
5
|
import '@modelcontextprotocol/sdk/types.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Stateless RPC-over-stream client for MCP connections.
|
|
9
|
-
*
|
|
10
|
-
* Uses single POST requests with `Accept: text/event-stream` for every RPC call.
|
|
11
|
-
* Progress events and the final rpc-response are delivered in the same response.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
interface SSEClientOptions {
|
|
15
|
-
/** MCP endpoint URL */
|
|
16
|
-
url: string;
|
|
17
|
-
/** User/Client identifier */
|
|
18
|
-
identity: string;
|
|
19
|
-
/** Optional auth token for authenticated requests */
|
|
20
|
-
authToken?: string;
|
|
21
|
-
/** Callback for MCP connection state changes */
|
|
22
|
-
onConnectionEvent?: (event: McpConnectionEvent) => void;
|
|
23
|
-
/** Callback for observability/logging events */
|
|
24
|
-
onObservabilityEvent?: (event: McpObservabilityEvent) => void;
|
|
25
|
-
/** Callback for connection status changes */
|
|
26
|
-
onStatusChange?: (status: ConnectionStatus) => void;
|
|
27
|
-
/** Callback for MCP App UI events */
|
|
28
|
-
onEvent?: (event: McpAppsUIEvent) => void;
|
|
29
|
-
/** Enable debug logging @default false */
|
|
30
|
-
debug?: boolean;
|
|
31
|
-
}
|
|
32
|
-
type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
33
|
-
declare class SSEClient {
|
|
34
|
-
private readonly options;
|
|
35
|
-
private resourceCache;
|
|
36
|
-
private connected;
|
|
37
|
-
constructor(options: SSEClientOptions);
|
|
38
|
-
connect(): void;
|
|
39
|
-
disconnect(): void;
|
|
40
|
-
isConnected(): boolean;
|
|
41
|
-
getSessions(): Promise<SessionListResult>;
|
|
42
|
-
connectToServer(params: ConnectParams): Promise<ConnectResult>;
|
|
43
|
-
disconnectFromServer(sessionId: string): Promise<DisconnectResult>;
|
|
44
|
-
listTools(sessionId: string): Promise<ListToolsRpcResult>;
|
|
45
|
-
callTool(sessionId: string, toolName: string, toolArgs: Record<string, unknown>): Promise<unknown>;
|
|
46
|
-
restoreSession(sessionId: string): Promise<RestoreSessionResult>;
|
|
47
|
-
finishAuth(sessionId: string, code: string): Promise<FinishAuthResult>;
|
|
48
|
-
listPrompts(sessionId: string): Promise<ListPromptsResult>;
|
|
49
|
-
getPrompt(sessionId: string, name: string, args?: Record<string, string>): Promise<unknown>;
|
|
50
|
-
listResources(sessionId: string): Promise<ListResourcesResult>;
|
|
51
|
-
readResource(sessionId: string, uri: string): Promise<unknown>;
|
|
52
|
-
preloadToolUiResources(sessionId: string, tools: Array<{
|
|
53
|
-
name: string;
|
|
54
|
-
_meta?: unknown;
|
|
55
|
-
}>): void;
|
|
56
|
-
getOrFetchResource(sessionId: string, uri: string): Promise<unknown>;
|
|
57
|
-
hasPreloadedResource(uri: string): boolean;
|
|
58
|
-
clearResourceCache(): void;
|
|
59
|
-
private sendRequest;
|
|
60
|
-
private readRpcResponseFromStream;
|
|
61
|
-
private sleep;
|
|
62
|
-
private parseRpcResponse;
|
|
63
|
-
private buildUrl;
|
|
64
|
-
private buildHeaders;
|
|
65
|
-
private extractUiResourceUri;
|
|
66
|
-
private emitUiEventIfPresent;
|
|
67
|
-
private log;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Abstraction layer for the AppHost's network communication.
|
|
72
|
-
*
|
|
73
|
-
* This interface decouples the `AppHost` from the concrete networking implementation (like `SSEClient`).
|
|
74
|
-
* Implementation can be:
|
|
75
|
-
* 1. `SSEClient`: Direct connection to MCP Server (Browser -> Server).
|
|
76
|
-
* 2. `ProxyClient`: Connection via an intermediary API (Browser -> Next.js API -> Server).
|
|
77
|
-
*/
|
|
78
|
-
interface AppHostClient {
|
|
79
|
-
/**
|
|
80
|
-
* Check if the client is connected
|
|
81
|
-
*/
|
|
82
|
-
isConnected(): boolean;
|
|
83
|
-
/**
|
|
84
|
-
* Get list of active sessions
|
|
85
|
-
*/
|
|
86
|
-
getSessions(): Promise<SessionListResult>;
|
|
87
|
-
/**
|
|
88
|
-
* Call a tool on a specific session
|
|
89
|
-
*/
|
|
90
|
-
callTool(sessionId: string, toolName: string, toolArgs: Record<string, unknown>): Promise<unknown>;
|
|
91
|
-
/**
|
|
92
|
-
* Read a resource from a specific session
|
|
93
|
-
*/
|
|
94
|
-
readResource(sessionId: string, uri: string): Promise<unknown>;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* MCP App Host
|
|
99
|
-
*
|
|
100
|
-
* Bridges the gap between an iframe (MCP App) and the SSEClient (MCP Server).
|
|
101
|
-
* Handles secure iframe sandboxing, resource loading, and bi-directional
|
|
102
|
-
* communication via the AppBridge protocol.
|
|
103
|
-
*
|
|
104
|
-
* Key features:
|
|
105
|
-
* - Secure iframe sandboxing with minimal permissions
|
|
106
|
-
* - Resource preloading for instant MCP App UI loading
|
|
107
|
-
* - Cache-aware resource fetching (SSEClient cache → local cache → direct fetch)
|
|
108
|
-
* - Support for ui:// and mcp-app:// resource URIs
|
|
109
|
-
*/
|
|
110
|
-
|
|
111
|
-
interface AppHostOptions {
|
|
112
|
-
/** Enable debug logging @default false */
|
|
113
|
-
debug?: boolean;
|
|
114
|
-
}
|
|
115
|
-
interface AppMessageParams {
|
|
116
|
-
role: string;
|
|
117
|
-
content: unknown;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Host for MCP Apps embedded in iframes.
|
|
121
|
-
* Manages secure communication between the app and the MCP server.
|
|
122
|
-
*/
|
|
123
|
-
declare class AppHost {
|
|
124
|
-
private readonly client;
|
|
125
|
-
private readonly iframe;
|
|
126
|
-
private bridge;
|
|
127
|
-
private sessionId?;
|
|
128
|
-
private resourceCache;
|
|
129
|
-
private debug;
|
|
130
|
-
/** Callback for app messages (e.g., chat messages from the app) */
|
|
131
|
-
onAppMessage?: (params: AppMessageParams) => void;
|
|
132
|
-
constructor(client: AppHostClient, iframe: HTMLIFrameElement, options?: AppHostOptions);
|
|
133
|
-
/**
|
|
134
|
-
* Start the host. This prepares the bridge handlers but doesn't connect yet.
|
|
135
|
-
* The actual connection happens in launch() after HTML is loaded.
|
|
136
|
-
* @returns Promise that resolves immediately (bridge connects during launch)
|
|
137
|
-
*/
|
|
138
|
-
start(): Promise<void>;
|
|
139
|
-
/**
|
|
140
|
-
* Preload UI resources to enable instant app loading.
|
|
141
|
-
* Call this when tools are discovered to cache their UI resources.
|
|
142
|
-
*/
|
|
143
|
-
preload(tools: Array<{
|
|
144
|
-
_meta?: unknown;
|
|
145
|
-
}>): void;
|
|
146
|
-
/**
|
|
147
|
-
* Launch an MCP App from a URL or MCP resource URI.
|
|
148
|
-
* Loads the HTML first, then establishes bridge connection.
|
|
149
|
-
*/
|
|
150
|
-
launch(url: string, sessionId?: string): Promise<void>;
|
|
151
|
-
/**
|
|
152
|
-
* Wait for app to signal initialization complete
|
|
153
|
-
*/
|
|
154
|
-
private onAppReady;
|
|
155
|
-
/**
|
|
156
|
-
* Wait for iframe to finish loading
|
|
157
|
-
*/
|
|
158
|
-
private onIframeReady;
|
|
159
|
-
/**
|
|
160
|
-
* Send tool input arguments to the MCP App.
|
|
161
|
-
* Call this after launch() when tool input is available.
|
|
162
|
-
*/
|
|
163
|
-
sendToolInput(args: Record<string, unknown>): void;
|
|
164
|
-
/**
|
|
165
|
-
* Send tool result to the MCP App.
|
|
166
|
-
* Call this when the tool call completes.
|
|
167
|
-
*/
|
|
168
|
-
sendToolResult(result: unknown): void;
|
|
169
|
-
/**
|
|
170
|
-
* Send tool cancellation to the MCP App.
|
|
171
|
-
* Call this when the tool call is cancelled or fails.
|
|
172
|
-
*/
|
|
173
|
-
sendToolCancelled(reason: string): void;
|
|
174
|
-
private configureSandbox;
|
|
175
|
-
private initializeBridge;
|
|
176
|
-
private connectBridge;
|
|
177
|
-
private handleToolCall;
|
|
178
|
-
private handleOpenLink;
|
|
179
|
-
private handleMessage;
|
|
180
|
-
private launchMcpApp;
|
|
181
|
-
private fetchResourceWithCache;
|
|
182
|
-
private preloadResource;
|
|
183
|
-
private getSessionId;
|
|
184
|
-
private isMcpUri;
|
|
185
|
-
private hasClientCache;
|
|
186
|
-
private extractUiResourceUri;
|
|
187
|
-
private decodeContent;
|
|
188
|
-
private log;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export { AppHost, McpConnectionEvent, McpObservabilityEvent, SSEClient, type SSEClientOptions };
|
package/dist/client/index.js
CHANGED
|
@@ -254,22 +254,88 @@ var SSEClient = class {
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
};
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
"
|
|
266
|
-
|
|
267
|
-
"
|
|
268
|
-
|
|
269
|
-
"
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
257
|
+
|
|
258
|
+
// src/client/core/constants.ts
|
|
259
|
+
var SANDBOX_PROXY_READY_METHOD = "ui/notifications/sandbox-proxy-ready";
|
|
260
|
+
var SANDBOX_RESOURCE_READY_METHOD = "ui/notifications/sandbox-resource-ready";
|
|
261
|
+
var APP_HOST_DEFAULTS = {
|
|
262
|
+
/** Default timeout for waiting for the sandbox proxy to be ready (ms). */
|
|
263
|
+
SANDBOX_TIMEOUT_MS: 1e4,
|
|
264
|
+
/** Default host info reported to guest apps. */
|
|
265
|
+
HOST_INFO: { name: "mcp-ts-host", version: "1.0.0" },
|
|
266
|
+
/** Supported MCP App URI schemes. */
|
|
267
|
+
URI_SCHEMES: ["ui://", "mcp-app://"],
|
|
268
|
+
/** Default theme for the host context. */
|
|
269
|
+
THEME: "dark",
|
|
270
|
+
/** Default platform for the host context. */
|
|
271
|
+
PLATFORM: "web",
|
|
272
|
+
/** Default max height for the iframe container (px). */
|
|
273
|
+
MAX_HEIGHT: 6e3
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// src/client/utils/app-host-utils.ts
|
|
277
|
+
var DEFAULT_SANDBOX_TIMEOUT_MS = APP_HOST_DEFAULTS.SANDBOX_TIMEOUT_MS;
|
|
278
|
+
async function setupSandboxProxyIframe(iframe, sandboxProxyUrl) {
|
|
279
|
+
iframe.style.width = "100%";
|
|
280
|
+
iframe.style.height = "100%";
|
|
281
|
+
iframe.style.border = "none";
|
|
282
|
+
iframe.style.backgroundColor = "transparent";
|
|
283
|
+
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
284
|
+
const onReady = new Promise((resolve, reject) => {
|
|
285
|
+
let settled = false;
|
|
286
|
+
const cleanup = () => {
|
|
287
|
+
window.removeEventListener("message", messageListener);
|
|
288
|
+
iframe.removeEventListener("error", errorListener);
|
|
289
|
+
};
|
|
290
|
+
const timeoutId = setTimeout(() => {
|
|
291
|
+
if (!settled) {
|
|
292
|
+
settled = true;
|
|
293
|
+
cleanup();
|
|
294
|
+
reject(new Error("Timed out waiting for sandbox proxy iframe to be ready"));
|
|
295
|
+
}
|
|
296
|
+
}, DEFAULT_SANDBOX_TIMEOUT_MS);
|
|
297
|
+
const messageListener = (event) => {
|
|
298
|
+
if (event.source === iframe.contentWindow) {
|
|
299
|
+
if (event.data?.method === SANDBOX_PROXY_READY_METHOD) {
|
|
300
|
+
if (!settled) {
|
|
301
|
+
settled = true;
|
|
302
|
+
clearTimeout(timeoutId);
|
|
303
|
+
cleanup();
|
|
304
|
+
resolve();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
const errorListener = () => {
|
|
310
|
+
if (!settled) {
|
|
311
|
+
settled = true;
|
|
312
|
+
clearTimeout(timeoutId);
|
|
313
|
+
cleanup();
|
|
314
|
+
reject(new Error("Failed to load sandbox proxy iframe"));
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
window.addEventListener("message", messageListener);
|
|
318
|
+
iframe.addEventListener("error", errorListener);
|
|
319
|
+
});
|
|
320
|
+
iframe.src = sandboxProxyUrl.href;
|
|
321
|
+
return { onReady };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/client/core/app-host.ts
|
|
325
|
+
var DEFAULT_MCP_APP_CSP = {
|
|
326
|
+
"default-src": "'self'",
|
|
327
|
+
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' https: blob:",
|
|
328
|
+
"style-src": "'self' 'unsafe-inline' https:",
|
|
329
|
+
"connect-src": "'self' https: wss:",
|
|
330
|
+
"img-src": "'self' data: https: blob:",
|
|
331
|
+
"font-src": "'self' data: https:",
|
|
332
|
+
"media-src": "'self' https: blob:",
|
|
333
|
+
"frame-src": "'none'",
|
|
334
|
+
"object-src": "'none'",
|
|
335
|
+
"base-uri": "'self'"
|
|
336
|
+
};
|
|
337
|
+
var HOST_INFO = APP_HOST_DEFAULTS.HOST_INFO;
|
|
338
|
+
var MCP_URI_SCHEMES = APP_HOST_DEFAULTS.URI_SCHEMES;
|
|
273
339
|
var AppHost = class {
|
|
274
340
|
constructor(client, iframe, options) {
|
|
275
341
|
this.client = client;
|
|
@@ -278,10 +344,12 @@ var AppHost = class {
|
|
|
278
344
|
__publicField(this, "sessionId");
|
|
279
345
|
__publicField(this, "resourceCache", /* @__PURE__ */ new Map());
|
|
280
346
|
__publicField(this, "debug");
|
|
281
|
-
|
|
347
|
+
__publicField(this, "sandboxConfig");
|
|
348
|
+
__publicField(this, "options");
|
|
282
349
|
__publicField(this, "onAppMessage");
|
|
283
|
-
this.
|
|
284
|
-
this.
|
|
350
|
+
this.options = options || {};
|
|
351
|
+
this.debug = this.options.debug ?? false;
|
|
352
|
+
this.sandboxConfig = this.options.sandbox;
|
|
285
353
|
this.bridge = this.initializeBridge();
|
|
286
354
|
}
|
|
287
355
|
// ============================================
|
|
@@ -308,19 +376,35 @@ var AppHost = class {
|
|
|
308
376
|
}
|
|
309
377
|
}
|
|
310
378
|
/**
|
|
311
|
-
* Launch an MCP App from a URL
|
|
379
|
+
* Launch an MCP App from a URL, MCP resource URI, or RAW HTML.
|
|
312
380
|
* Loads the HTML first, then establishes bridge connection.
|
|
313
381
|
*/
|
|
314
|
-
async launch(
|
|
382
|
+
async launch(source, sessionId) {
|
|
315
383
|
if (sessionId) this.sessionId = sessionId;
|
|
316
384
|
const initializedPromise = this.onAppReady();
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
385
|
+
let htmlToRender = source.html;
|
|
386
|
+
if (!htmlToRender && source.uri) {
|
|
387
|
+
if (this.isMcpUri(source.uri)) {
|
|
388
|
+
htmlToRender = await this.readMcpAppHtml(source.uri);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!htmlToRender && source.uri && !this.isMcpUri(source.uri)) {
|
|
392
|
+
this.iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads");
|
|
393
|
+
this.iframe.src = source.uri;
|
|
394
|
+
await this.onIframeReady();
|
|
395
|
+
await this.connectBridge();
|
|
396
|
+
} else if (htmlToRender) {
|
|
397
|
+
if (!this.sandboxConfig) {
|
|
398
|
+
throw new Error("Sandbox configuration requires a proxy URL to render HTML safely.");
|
|
399
|
+
}
|
|
400
|
+
await this.launchSandboxedHtml(htmlToRender, this.sandboxConfig);
|
|
401
|
+
await this.connectBridge();
|
|
402
|
+
this.log("Sending HTML resource to sandbox proxy (MCP Apps notification)");
|
|
403
|
+
await this.bridge.sendSandboxResourceReady({
|
|
404
|
+
html: htmlToRender,
|
|
405
|
+
csp: this.sandboxConfig.csp
|
|
406
|
+
});
|
|
321
407
|
}
|
|
322
|
-
await this.onIframeReady();
|
|
323
|
-
await this.connectBridge();
|
|
324
408
|
this.log("Waiting for app initialization");
|
|
325
409
|
await Promise.race([
|
|
326
410
|
initializedPromise,
|
|
@@ -331,6 +415,19 @@ var AppHost = class {
|
|
|
331
415
|
]);
|
|
332
416
|
this.log("App launched and ready");
|
|
333
417
|
}
|
|
418
|
+
// Set host context manually
|
|
419
|
+
setHostContext(context) {
|
|
420
|
+
this.options.hostContext = context;
|
|
421
|
+
if (this.bridge) {
|
|
422
|
+
this.bridge.setHostContext(context);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Send streaming inputs manually
|
|
426
|
+
sendToolInputPartial(params) {
|
|
427
|
+
if (this.bridge) {
|
|
428
|
+
this.bridge.sendToolInputPartial(params);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
334
431
|
/**
|
|
335
432
|
* Wait for app to signal initialization complete
|
|
336
433
|
*/
|
|
@@ -381,14 +478,17 @@ var AppHost = class {
|
|
|
381
478
|
this.log("Sending tool cancellation to app");
|
|
382
479
|
this.bridge.sendToolCancelled({ reason });
|
|
383
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Tell the guest UI the resource is being torn down (unload / cleanup).
|
|
483
|
+
* Forwards to {@link AppBridge.teardownResource} on `@modelcontextprotocol/ext-apps/app-bridge`.
|
|
484
|
+
*/
|
|
485
|
+
teardownResource(params = {}) {
|
|
486
|
+
this.log("Sending resource teardown to app");
|
|
487
|
+
this.bridge.teardownResource(params);
|
|
488
|
+
}
|
|
384
489
|
// ============================================
|
|
385
490
|
// Private: Initialization
|
|
386
491
|
// ============================================
|
|
387
|
-
configureSandbox() {
|
|
388
|
-
if (this.iframe.sandbox.value !== SANDBOX_PERMISSIONS) {
|
|
389
|
-
this.iframe.sandbox.value = SANDBOX_PERMISSIONS;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
492
|
initializeBridge() {
|
|
393
493
|
const bridge = new appBridge.AppBridge(
|
|
394
494
|
null,
|
|
@@ -397,12 +497,10 @@ var AppHost = class {
|
|
|
397
497
|
openLinks: {},
|
|
398
498
|
serverTools: {},
|
|
399
499
|
logging: {},
|
|
400
|
-
// Declare support for model context updates
|
|
401
500
|
updateModelContext: { text: {} }
|
|
402
501
|
},
|
|
403
502
|
{
|
|
404
|
-
|
|
405
|
-
hostContext: {
|
|
503
|
+
hostContext: this.options.hostContext || {
|
|
406
504
|
theme: "dark",
|
|
407
505
|
platform: "web",
|
|
408
506
|
containerDimensions: { maxHeight: 6e3 },
|
|
@@ -411,19 +509,56 @@ var AppHost = class {
|
|
|
411
509
|
}
|
|
412
510
|
}
|
|
413
511
|
);
|
|
512
|
+
bridge.fallbackRequestHandler = this.options.onFallbackRequest;
|
|
414
513
|
bridge.oncalltool = (params) => this.handleToolCall(params);
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
514
|
+
if (this.options.onReadResource) {
|
|
515
|
+
bridge.onreadresource = async (params) => {
|
|
516
|
+
const resp = await this.options.onReadResource(params.uri);
|
|
517
|
+
return {
|
|
518
|
+
contents: resp.contents.map((c) => ({
|
|
519
|
+
uri: params.uri,
|
|
520
|
+
text: c.text,
|
|
521
|
+
blob: c.blob
|
|
522
|
+
}))
|
|
523
|
+
};
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
bridge.onopenlink = async (params, extra) => {
|
|
527
|
+
if (this.options.onOpenLink) {
|
|
528
|
+
return await this.options.onOpenLink(params, extra);
|
|
529
|
+
}
|
|
530
|
+
return this.handleOpenLink(params);
|
|
531
|
+
};
|
|
532
|
+
bridge.onmessage = async (params, extra) => {
|
|
533
|
+
if (this.options.onMessage) {
|
|
534
|
+
return await this.options.onMessage(params, extra);
|
|
535
|
+
}
|
|
536
|
+
return this.handleMessage(params);
|
|
537
|
+
};
|
|
538
|
+
bridge.onloggingmessage = (params) => {
|
|
539
|
+
this.log(`App log [${params.level}]: ${params.data}`);
|
|
540
|
+
if (this.options.onLoggingMessage) {
|
|
541
|
+
this.options.onLoggingMessage(params);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
418
544
|
bridge.onupdatemodelcontext = async () => ({});
|
|
419
|
-
bridge.onsizechange = async (
|
|
420
|
-
|
|
421
|
-
if (
|
|
545
|
+
bridge.onsizechange = async (params) => {
|
|
546
|
+
const { width, height } = params;
|
|
547
|
+
if (height !== void 0 && height > 0) {
|
|
548
|
+
this.iframe.style.height = `${height}px`;
|
|
549
|
+
}
|
|
550
|
+
if (width !== void 0 && width > 0) this.iframe.style.minWidth = `min(${width}px, 100%)`;
|
|
551
|
+
if (this.options.onSizeChanged) {
|
|
552
|
+
this.options.onSizeChanged(params);
|
|
553
|
+
}
|
|
422
554
|
return {};
|
|
423
555
|
};
|
|
424
|
-
bridge.onrequestdisplaymode = async (params) =>
|
|
425
|
-
|
|
426
|
-
|
|
556
|
+
bridge.onrequestdisplaymode = async (params, extra) => {
|
|
557
|
+
if (this.options.onRequestDisplayMode) {
|
|
558
|
+
return await this.options.onRequestDisplayMode(params, extra);
|
|
559
|
+
}
|
|
560
|
+
return { mode: params.mode === "fullscreen" ? "fullscreen" : "inline" };
|
|
561
|
+
};
|
|
427
562
|
return bridge;
|
|
428
563
|
}
|
|
429
564
|
async connectBridge() {
|
|
@@ -437,6 +572,9 @@ var AppHost = class {
|
|
|
437
572
|
this.log("Bridge connected successfully");
|
|
438
573
|
} catch (error) {
|
|
439
574
|
this.log("Bridge connection failed", "error");
|
|
575
|
+
if (this.options.onError) {
|
|
576
|
+
this.options.onError(error instanceof Error ? error : new Error(String(error)));
|
|
577
|
+
}
|
|
440
578
|
throw error;
|
|
441
579
|
}
|
|
442
580
|
}
|
|
@@ -444,8 +582,11 @@ var AppHost = class {
|
|
|
444
582
|
// Private: Bridge Event Handlers
|
|
445
583
|
// ============================================
|
|
446
584
|
async handleToolCall(params) {
|
|
447
|
-
if (
|
|
448
|
-
|
|
585
|
+
if (this.options.onCallTool) {
|
|
586
|
+
return await this.options.onCallTool(params);
|
|
587
|
+
}
|
|
588
|
+
if (!this.client || !this.client.isConnected()) {
|
|
589
|
+
throw new Error("Client disconnected or not provided");
|
|
449
590
|
}
|
|
450
591
|
const sessionId = await this.getSessionId();
|
|
451
592
|
if (!sessionId) {
|
|
@@ -469,13 +610,19 @@ var AppHost = class {
|
|
|
469
610
|
// ============================================
|
|
470
611
|
// Private: Resource Loading
|
|
471
612
|
// ============================================
|
|
472
|
-
async
|
|
473
|
-
|
|
474
|
-
|
|
613
|
+
async launchSandboxedHtml(html, config) {
|
|
614
|
+
const sandboxUrlString = config.url instanceof URL ? config.url.href : config.url;
|
|
615
|
+
const url = new URL(sandboxUrlString, globalThis.location?.href);
|
|
616
|
+
if (config.csp && Object.keys(config.csp).length > 0) {
|
|
617
|
+
url.searchParams.set("csp", JSON.stringify(config.csp));
|
|
475
618
|
}
|
|
619
|
+
const { onReady } = await setupSandboxProxyIframe(this.iframe, url);
|
|
620
|
+
await onReady;
|
|
621
|
+
}
|
|
622
|
+
async readMcpAppHtml(uri) {
|
|
476
623
|
const sessionId = await this.getSessionId();
|
|
477
|
-
if (!sessionId) {
|
|
478
|
-
throw new Error("No active session");
|
|
624
|
+
if (!sessionId && !this.options.onReadResource) {
|
|
625
|
+
throw new Error("No active session.");
|
|
479
626
|
}
|
|
480
627
|
const response = await this.fetchResourceWithCache(sessionId, uri);
|
|
481
628
|
if (!response?.contents?.length) {
|
|
@@ -486,10 +633,18 @@ var AppHost = class {
|
|
|
486
633
|
if (!html) {
|
|
487
634
|
throw new Error(`Invalid content in resource: ${uri}`);
|
|
488
635
|
}
|
|
489
|
-
|
|
490
|
-
this.iframe.src = URL.createObjectURL(blob);
|
|
636
|
+
return html;
|
|
491
637
|
}
|
|
492
638
|
async fetchResourceWithCache(sessionId, uri) {
|
|
639
|
+
if (this.options.onReadResource) {
|
|
640
|
+
return await this.options.onReadResource(uri);
|
|
641
|
+
}
|
|
642
|
+
if (!sessionId) {
|
|
643
|
+
throw new Error("No active session");
|
|
644
|
+
}
|
|
645
|
+
if (!this.client) {
|
|
646
|
+
throw new Error("No client to read resource from");
|
|
647
|
+
}
|
|
493
648
|
if (this.hasClientCache()) {
|
|
494
649
|
return this.client.getOrFetchResource(sessionId, uri);
|
|
495
650
|
}
|
|
@@ -502,8 +657,11 @@ var AppHost = class {
|
|
|
502
657
|
}
|
|
503
658
|
async preloadResource(uri) {
|
|
504
659
|
try {
|
|
660
|
+
if (this.options.onReadResource) {
|
|
661
|
+
return await this.options.onReadResource(uri);
|
|
662
|
+
}
|
|
505
663
|
const sessionId = await this.getSessionId();
|
|
506
|
-
if (!sessionId) return null;
|
|
664
|
+
if (!sessionId || !this.client) return null;
|
|
507
665
|
return await this.client.readResource(sessionId, uri);
|
|
508
666
|
} catch (error) {
|
|
509
667
|
this.log(`Preload failed for ${uri}`, "warn");
|
|
@@ -515,6 +673,7 @@ var AppHost = class {
|
|
|
515
673
|
// ============================================
|
|
516
674
|
async getSessionId() {
|
|
517
675
|
if (this.sessionId) return this.sessionId;
|
|
676
|
+
if (!this.client) return void 0;
|
|
518
677
|
const result = await this.client.getSessions();
|
|
519
678
|
return result.sessions?.[0]?.sessionId;
|
|
520
679
|
}
|
|
@@ -522,6 +681,7 @@ var AppHost = class {
|
|
|
522
681
|
return MCP_URI_SCHEMES.some((scheme) => url.startsWith(scheme));
|
|
523
682
|
}
|
|
524
683
|
hasClientCache() {
|
|
684
|
+
if (!this.client) return false;
|
|
525
685
|
return "getOrFetchResource" in this.client && typeof this.client.getOrFetchResource === "function";
|
|
526
686
|
}
|
|
527
687
|
extractUiResourceUri(tool) {
|
|
@@ -551,7 +711,11 @@ var AppHost = class {
|
|
|
551
711
|
}
|
|
552
712
|
};
|
|
553
713
|
|
|
714
|
+
exports.APP_HOST_DEFAULTS = APP_HOST_DEFAULTS;
|
|
554
715
|
exports.AppHost = AppHost;
|
|
716
|
+
exports.DEFAULT_MCP_APP_CSP = DEFAULT_MCP_APP_CSP;
|
|
717
|
+
exports.SANDBOX_PROXY_READY_METHOD = SANDBOX_PROXY_READY_METHOD;
|
|
718
|
+
exports.SANDBOX_RESOURCE_READY_METHOD = SANDBOX_RESOURCE_READY_METHOD;
|
|
555
719
|
exports.SSEClient = SSEClient;
|
|
556
720
|
//# sourceMappingURL=index.js.map
|
|
557
721
|
//# sourceMappingURL=index.js.map
|