@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.
Files changed (86) hide show
  1. package/README.md +20 -27
  2. package/dist/adapters/agui-adapter.d.mts +16 -0
  3. package/dist/adapters/agui-adapter.d.ts +16 -0
  4. package/dist/adapters/agui-adapter.js +185 -0
  5. package/dist/adapters/agui-adapter.js.map +1 -1
  6. package/dist/adapters/agui-adapter.mjs +185 -0
  7. package/dist/adapters/agui-adapter.mjs.map +1 -1
  8. package/dist/adapters/agui-middleware.d.mts +2 -0
  9. package/dist/adapters/agui-middleware.d.ts +2 -0
  10. package/dist/adapters/agui-middleware.js.map +1 -1
  11. package/dist/adapters/agui-middleware.mjs.map +1 -1
  12. package/dist/adapters/ai-adapter.d.mts +21 -0
  13. package/dist/adapters/ai-adapter.d.ts +21 -0
  14. package/dist/adapters/ai-adapter.js +175 -0
  15. package/dist/adapters/ai-adapter.js.map +1 -1
  16. package/dist/adapters/ai-adapter.mjs +175 -0
  17. package/dist/adapters/ai-adapter.mjs.map +1 -1
  18. package/dist/adapters/langchain-adapter.d.mts +16 -0
  19. package/dist/adapters/langchain-adapter.d.ts +16 -0
  20. package/dist/adapters/langchain-adapter.js +179 -0
  21. package/dist/adapters/langchain-adapter.js.map +1 -1
  22. package/dist/adapters/langchain-adapter.mjs +179 -0
  23. package/dist/adapters/langchain-adapter.mjs.map +1 -1
  24. package/dist/client/index.d.mts +4 -190
  25. package/dist/client/index.d.ts +4 -190
  26. package/dist/client/index.js +218 -54
  27. package/dist/client/index.js.map +1 -1
  28. package/dist/client/index.mjs +215 -55
  29. package/dist/client/index.mjs.map +1 -1
  30. package/dist/client/react.d.mts +31 -17
  31. package/dist/client/react.d.ts +31 -17
  32. package/dist/client/react.js +447 -103
  33. package/dist/client/react.js.map +1 -1
  34. package/dist/client/react.mjs +443 -105
  35. package/dist/client/react.mjs.map +1 -1
  36. package/dist/client/vue.d.mts +5 -4
  37. package/dist/client/vue.d.ts +5 -4
  38. package/dist/client/vue.js +239 -63
  39. package/dist/client/vue.js.map +1 -1
  40. package/dist/client/vue.mjs +236 -64
  41. package/dist/client/vue.mjs.map +1 -1
  42. package/dist/index-DcYfpY3H.d.mts +295 -0
  43. package/dist/index-GfC_eNEv.d.ts +295 -0
  44. package/dist/index.d.mts +5 -3
  45. package/dist/index.d.ts +5 -3
  46. package/dist/index.js +1120 -59
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +1097 -60
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/server/index.d.mts +2 -2
  51. package/dist/server/index.d.ts +2 -2
  52. package/dist/server/index.js +18 -5
  53. package/dist/server/index.js.map +1 -1
  54. package/dist/server/index.mjs +18 -5
  55. package/dist/server/index.mjs.map +1 -1
  56. package/dist/shared/index.d.mts +86 -4
  57. package/dist/shared/index.d.ts +86 -4
  58. package/dist/shared/index.js +874 -0
  59. package/dist/shared/index.js.map +1 -1
  60. package/dist/shared/index.mjs +865 -1
  61. package/dist/shared/index.mjs.map +1 -1
  62. package/dist/tool-router-Bo8qZbsD.d.ts +325 -0
  63. package/dist/tool-router-XnWVxPzv.d.mts +325 -0
  64. package/dist/{types-CW6lghof.d.mts → types-CfCoIsWI.d.mts} +27 -1
  65. package/dist/{types-CW6lghof.d.ts → types-CfCoIsWI.d.ts} +27 -1
  66. package/package.json +15 -12
  67. package/src/adapters/agui-adapter.ts +79 -0
  68. package/src/adapters/ai-adapter.ts +75 -0
  69. package/src/adapters/langchain-adapter.ts +75 -1
  70. package/src/client/core/app-host.ts +252 -65
  71. package/src/client/core/constants.ts +30 -0
  72. package/src/client/index.ts +6 -1
  73. package/src/client/react/index.ts +3 -0
  74. package/src/client/react/use-app-host.ts +8 -15
  75. package/src/client/react/use-mcp-apps.tsx +262 -49
  76. package/src/client/react/use-mcp.ts +23 -12
  77. package/src/client/utils/app-host-utils.ts +62 -0
  78. package/src/client/vue/use-mcp.ts +23 -12
  79. package/src/server/index.ts +2 -0
  80. package/src/server/mcp/oauth-client.ts +34 -9
  81. package/src/shared/index.ts +36 -0
  82. package/src/shared/meta-tools.ts +387 -0
  83. package/src/shared/schema-compressor.ts +124 -0
  84. package/src/shared/tool-index.ts +499 -0
  85. package/src/shared/tool-router.ts +469 -0
  86. package/src/shared/types.ts +30 -0
@@ -1,191 +1,5 @@
1
- import { M as McpConnectionEvent, d as McpObservabilityEvent, e as McpAppsUIEvent } from '../events-CK3N--3g.js';
2
- export { D as Disposable, a as DisposableStore, E as Emitter, b as Event, c as McpConnectionState } from '../events-CK3N--3g.js';
3
- import { s as SessionListResult, e as ConnectParams, h as ConnectResult, j as DisconnectResult, n as ListToolsRpcResult, r as RestoreSessionResult, k as FinishAuthResult, L as ListPromptsResult, l as ListResourcesResult } from '../types-CW6lghof.js';
4
- export { p as McpRpcRequest, q as McpRpcResponse, T as ToolInfo } from '../types-CW6lghof.js';
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 };
@@ -254,22 +254,88 @@ var SSEClient = class {
254
254
  }
255
255
  }
256
256
  };
257
- var HOST_INFO = { name: "mcp-ts-host", version: "1.0.0" };
258
- var SANDBOX_PERMISSIONS = [
259
- "allow-scripts",
260
- // Required for app JavaScript execution
261
- "allow-forms",
262
- // Required for form submissions
263
- "allow-same-origin",
264
- // Required for Blob URL correctness
265
- "allow-modals",
266
- // Required for dialogs/alerts
267
- "allow-popups",
268
- // Required for opening links
269
- "allow-downloads"
270
- // Required for file downloads
271
- ].join(" ");
272
- var MCP_URI_SCHEMES = ["ui://", "mcp-app://"];
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
- /** Callback for app messages (e.g., chat messages from the app) */
347
+ __publicField(this, "sandboxConfig");
348
+ __publicField(this, "options");
282
349
  __publicField(this, "onAppMessage");
283
- this.debug = options?.debug ?? false;
284
- this.configureSandbox();
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 or MCP resource URI.
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(url, sessionId) {
382
+ async launch(source, sessionId) {
315
383
  if (sessionId) this.sessionId = sessionId;
316
384
  const initializedPromise = this.onAppReady();
317
- if (this.isMcpUri(url)) {
318
- await this.launchMcpApp(url);
319
- } else {
320
- this.iframe.src = url;
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
- // Initial host context
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
- bridge.onopenlink = this.handleOpenLink.bind(this);
416
- bridge.onmessage = this.handleMessage.bind(this);
417
- bridge.onloggingmessage = (params) => this.log(`App log [${params.level}]: ${params.data}`);
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 ({ width, height }) => {
420
- if (height !== void 0) this.iframe.style.height = `${height}px`;
421
- if (width !== void 0) this.iframe.style.minWidth = `min(${width}px, 100%)`;
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
- mode: params.mode === "fullscreen" ? "fullscreen" : "inline"
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 (!this.client.isConnected()) {
448
- throw new Error("Client disconnected");
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 launchMcpApp(uri) {
473
- if (!this.client.isConnected()) {
474
- throw new Error("Client must be connected");
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
- const blob = new Blob([html], { type: "text/html" });
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