@frontmcp/uipack 0.7.1 → 0.8.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.
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @packageDocumentation
9
9
  */
10
- export { type AIPlatformType, type UIMetadata, type BuildUIMetaOptions, type BuildToolDiscoveryMetaOptions, buildUIMeta, buildToolDiscoveryMeta, buildOpenAICSP, buildFrontMCPCSP, } from './platform-meta';
10
+ export { type AIPlatformType, type ExtAppsMimeTypeVariant, type UIMetadata, type BuildUIMetaOptions, type BuildToolDiscoveryMetaOptions, buildUIMeta, buildToolDiscoveryMeta, buildOpenAICSP, buildFrontMCPCSP, getExtAppsMimeType, isExtAppsMimeType, } from './platform-meta';
11
11
  export { type ResolvedServingMode, type ResolveServingModeOptions, resolveServingMode, isPlatformModeSupported, getDefaultServingMode, platformUsesStructuredContent, platformSupportsWidgets, } from './serving-mode';
12
12
  export { type TextContentBlock, type BuildToolResponseOptions, type ToolResponseContent, buildToolResponseContent, } from './response-builder';
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,6BAA6B,EAElC,WAAW,EACX,sBAAsB,EACtB,cAAc,EACd,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAE9B,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EAExB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAE3B,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,6BAA6B,EAElC,WAAW,EACX,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAEhB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAE9B,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EAExB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC"}
package/adapters/index.js CHANGED
@@ -26,6 +26,8 @@ __export(adapters_exports, {
26
26
  buildToolResponseContent: () => buildToolResponseContent,
27
27
  buildUIMeta: () => buildUIMeta,
28
28
  getDefaultServingMode: () => getDefaultServingMode,
29
+ getExtAppsMimeType: () => getExtAppsMimeType,
30
+ isExtAppsMimeType: () => isExtAppsMimeType,
29
31
  isPlatformModeSupported: () => isPlatformModeSupported,
30
32
  platformSupportsWidgets: () => platformSupportsWidgets,
31
33
  platformUsesStructuredContent: () => platformUsesStructuredContent,
@@ -33,7 +35,30 @@ __export(adapters_exports, {
33
35
  });
34
36
  module.exports = __toCommonJS(adapters_exports);
35
37
 
38
+ // libs/uipack/src/adapters/platform-meta.constants.ts
39
+ var DISPLAY_MODE_MAP = {
40
+ // Standard MCP Apps modes
41
+ inline: "inline",
42
+ fullscreen: "fullscreen",
43
+ pip: "pip",
44
+ // OpenAI-style aliases
45
+ widget: "inline",
46
+ panel: "fullscreen"
47
+ };
48
+
36
49
  // libs/uipack/src/adapters/platform-meta.ts
50
+ function getExtAppsMimeType(variant = "standard") {
51
+ switch (variant) {
52
+ case "profile":
53
+ return "text/html;profile=mcp-app";
54
+ case "standard":
55
+ default:
56
+ return "text/html+mcp";
57
+ }
58
+ }
59
+ function isExtAppsMimeType(mimeType) {
60
+ return mimeType === "text/html+mcp" || mimeType === "text/html;profile=mcp-app";
61
+ }
37
62
  function buildUIMeta(options) {
38
63
  const { uiConfig, platformType, html, token, directUrl, rendererType, contentHash, manifestUri } = options;
39
64
  const meta = {};
@@ -114,6 +139,21 @@ function buildClaudeMeta(meta, uiConfig) {
114
139
  if (uiConfig.prefersBorder !== void 0) {
115
140
  meta["claude/prefersBorder"] = uiConfig.prefersBorder;
116
141
  }
142
+ if (uiConfig.resourceUri) {
143
+ meta["ui/resourceUri"] = uiConfig.resourceUri;
144
+ }
145
+ if (uiConfig.csp) {
146
+ const csp = {};
147
+ if (uiConfig.csp.connectDomains?.length) {
148
+ csp.connectDomains = uiConfig.csp.connectDomains;
149
+ }
150
+ if (uiConfig.csp.resourceDomains?.length) {
151
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
152
+ }
153
+ if (Object.keys(csp).length > 0) {
154
+ meta["ui/csp"] = csp;
155
+ }
156
+ }
117
157
  return meta;
118
158
  }
119
159
  function buildGeminiMeta(meta, uiConfig) {
@@ -152,14 +192,7 @@ function buildGenericMeta(meta, uiConfig) {
152
192
  }
153
193
  }
154
194
  if (uiConfig.displayMode) {
155
- const displayModeMap = {
156
- inline: "inline",
157
- fullscreen: "fullscreen",
158
- pip: "pip",
159
- widget: "inline",
160
- panel: "fullscreen"
161
- };
162
- const mappedMode = displayModeMap[uiConfig.displayMode];
195
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
163
196
  if (mappedMode) {
164
197
  meta["ui/displayMode"] = mappedMode;
165
198
  }
@@ -187,15 +220,7 @@ function buildExtAppsMeta(meta, uiConfig) {
187
220
  }
188
221
  }
189
222
  if (uiConfig.displayMode) {
190
- const displayModeMap = {
191
- inline: "inline",
192
- fullscreen: "fullscreen",
193
- pip: "pip",
194
- // Map OpenAI-style values
195
- widget: "inline",
196
- panel: "fullscreen"
197
- };
198
- const mappedMode = displayModeMap[uiConfig.displayMode];
223
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
199
224
  if (mappedMode) {
200
225
  meta["ui/displayMode"] = mappedMode;
201
226
  }
@@ -254,12 +279,7 @@ function buildToolDiscoveryMeta(options) {
254
279
  }
255
280
  }
256
281
  if (uiConfig.displayMode) {
257
- const displayModeMap = {
258
- inline: "inline",
259
- fullscreen: "fullscreen",
260
- pip: "pip"
261
- };
262
- const mappedMode = displayModeMap[uiConfig.displayMode];
282
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
263
283
  if (mappedMode) {
264
284
  meta["ui/displayMode"] = mappedMode;
265
285
  }
@@ -271,7 +291,32 @@ function buildToolDiscoveryMeta(options) {
271
291
  meta["ui/domain"] = uiConfig.sandboxDomain;
272
292
  }
273
293
  break;
274
- // Claude, Gemini, IDEs don't need discovery metadata
294
+ case "claude":
295
+ meta["ui/resourceUri"] = staticWidgetUri;
296
+ meta["ui/mimeType"] = "text/html+mcp";
297
+ if (uiConfig.displayMode) {
298
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
299
+ if (mappedMode) {
300
+ meta["ui/displayMode"] = mappedMode;
301
+ }
302
+ }
303
+ if (uiConfig.prefersBorder !== void 0) {
304
+ meta["ui/prefersBorder"] = uiConfig.prefersBorder;
305
+ }
306
+ if (uiConfig.csp) {
307
+ const csp = {};
308
+ if (uiConfig.csp.connectDomains?.length) {
309
+ csp.connectDomains = uiConfig.csp.connectDomains;
310
+ }
311
+ if (uiConfig.csp.resourceDomains?.length) {
312
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
313
+ }
314
+ if (Object.keys(csp).length > 0) {
315
+ meta["ui/csp"] = csp;
316
+ }
317
+ }
318
+ break;
319
+ // Gemini, IDEs don't need discovery metadata
275
320
  // They use inline HTML at call time
276
321
  default:
277
322
  break;
@@ -491,6 +536,8 @@ ${htmlContent}
491
536
  buildToolResponseContent,
492
537
  buildUIMeta,
493
538
  getDefaultServingMode,
539
+ getExtAppsMimeType,
540
+ isExtAppsMimeType,
494
541
  isPlatformModeSupported,
495
542
  platformSupportsWidgets,
496
543
  platformUsesStructuredContent,
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared constants for platform metadata adapters.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * MCP Apps display modes per specification.
8
+ */
9
+ export type ExtAppsDisplayMode = 'inline' | 'fullscreen' | 'pip';
10
+ /**
11
+ * Mapping from generic display modes to MCP Apps specific values.
12
+ *
13
+ * Maps both standard MCP Apps modes and platform-specific aliases:
14
+ * - inline, fullscreen, pip: Standard MCP Apps modes (pass through)
15
+ * - widget: OpenAI-style alias for 'inline'
16
+ * - panel: OpenAI-style alias for 'fullscreen'
17
+ */
18
+ export declare const DISPLAY_MODE_MAP: Record<string, ExtAppsDisplayMode>;
19
+ /**
20
+ * Map a display mode string to MCP Apps display mode.
21
+ *
22
+ * @param mode - The display mode string to map
23
+ * @returns The mapped MCP Apps display mode, or undefined if not recognized
24
+ */
25
+ export declare function mapDisplayMode(mode: string): ExtAppsDisplayMode | undefined;
26
+ //# sourceMappingURL=platform-meta.constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-meta.constants.d.ts","sourceRoot":"","sources":["../../src/adapters/platform-meta.constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,YAAY,GAAG,KAAK,CAAC;AAEjE;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAQ/D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE3E"}
@@ -11,11 +11,50 @@
11
11
  * @packageDocumentation
12
12
  */
13
13
  import type { UITemplateConfig, UIContentSecurityPolicy } from '../types';
14
+ export { DISPLAY_MODE_MAP, type ExtAppsDisplayMode } from './platform-meta.constants';
14
15
  /**
15
16
  * Supported AI platform types.
16
17
  * Used to determine which metadata format to generate.
17
18
  */
18
19
  export type AIPlatformType = 'openai' | 'claude' | 'gemini' | 'cursor' | 'continue' | 'cody' | 'generic-mcp' | 'ext-apps' | 'unknown';
20
+ /**
21
+ * MCP Apps MIME type variants.
22
+ *
23
+ * - `'standard'`: `text/html+mcp` - The standard MCP Apps MIME type
24
+ * - `'profile'`: `text/html;profile=mcp-app` - Profile-based MIME type variant
25
+ */
26
+ export type ExtAppsMimeTypeVariant = 'standard' | 'profile';
27
+ /**
28
+ * Get the appropriate MCP Apps MIME type.
29
+ *
30
+ * MCP Apps supports two MIME type formats:
31
+ * - Standard: `text/html+mcp` (default)
32
+ * - Profile: `text/html;profile=mcp-app`
33
+ *
34
+ * The profile variant may be preferred by some hosts for content negotiation.
35
+ *
36
+ * @param variant - The MIME type variant to use
37
+ * @returns The MCP Apps MIME type string
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * import { getExtAppsMimeType } from '@frontmcp/uipack/adapters';
42
+ *
43
+ * // Default standard MIME type
44
+ * getExtAppsMimeType(); // 'text/html+mcp'
45
+ *
46
+ * // Profile-based MIME type
47
+ * getExtAppsMimeType('profile'); // 'text/html;profile=mcp-app'
48
+ * ```
49
+ */
50
+ export declare function getExtAppsMimeType(variant?: ExtAppsMimeTypeVariant): string;
51
+ /**
52
+ * Check if a MIME type is a valid MCP Apps MIME type.
53
+ *
54
+ * @param mimeType - The MIME type to check
55
+ * @returns True if the MIME type is a valid MCP Apps MIME type
56
+ */
57
+ export declare function isExtAppsMimeType(mimeType: string): boolean;
19
58
  /**
20
59
  * UI metadata to include in tool response _meta field.
21
60
  * Contains both universal fields and platform-specific annotations.
@@ -1 +1 @@
1
- {"version":3,"file":"platform-meta.d.ts","sourceRoot":"","sources":["../../src/adapters/platform-meta.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform-meta.d.ts","sourceRoot":"","sources":["../../src/adapters/platform-meta.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"iife-generator.d.ts","sourceRoot":"","sources":["../../src/bridge-runtime/iife-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC;IACvE,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAyF7E;AA6zBD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,EACvD,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAM,GACnD,MAAM,CAYR;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAAuB,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;CAK9B,CAAC"}
1
+ {"version":3,"file":"iife-generator.d.ts","sourceRoot":"","sources":["../../src/bridge-runtime/iife-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC;IACvE,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAyF7E;AAm7BD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,EACvD,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAM,GACnD,MAAM,CAYR;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAAuB,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;CAK9B,CAAC"}
@@ -211,16 +211,43 @@ var ExtAppsAdapter = {
211
211
  capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
212
212
  trustedOrigins: ${originsArray},
213
213
  trustedOrigin: null,
214
+ originTrustPending: false,
214
215
  pendingRequests: {},
215
216
  requestId: 0,
216
217
  hostCapabilities: {},
217
218
  canHandle: function() {
218
219
  if (typeof window === 'undefined') return false;
219
220
  if (window.parent === window) return false;
220
- // Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
221
+
222
+ // Check for OpenAI SDK - defer to OpenAIAdapter
223
+ if (window.openai && window.openai.canvas) return false;
221
224
  if (window.openai && typeof window.openai.callTool === 'function') return false;
225
+
226
+ // Explicit ext-apps marker
222
227
  if (window.__mcpPlatform === 'ext-apps') return true;
223
- return true;
228
+ if (window.__extAppsInitialized) return true;
229
+
230
+ // Claude MCP Apps mode (2026+) - uses ext-apps protocol
231
+ if (window.__mcpAppsEnabled) return true;
232
+
233
+ // Legacy Claude detection - defer to ClaudeAdapter
234
+ if (window.claude) return false;
235
+ if (window.__claudeArtifact) return false;
236
+ if (window.__mcpPlatform === 'claude') return false;
237
+ if (typeof location !== 'undefined') {
238
+ try {
239
+ var url = new URL(location.href);
240
+ var hostname = url.hostname.toLowerCase();
241
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
242
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
243
+ if (isClaudeHost || isAnthropicHost) return false;
244
+ } catch (e) {
245
+ // If URL parsing fails, fall through to other checks
246
+ }
247
+ }
248
+
249
+ // Do NOT default to true for any iframe
250
+ return false;
224
251
  },
225
252
  initialize: function(context) {
226
253
  var self = this;
@@ -262,6 +289,11 @@ var ExtAppsAdapter = {
262
289
  context.toolInput = params.arguments || {};
263
290
  window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
264
291
  break;
292
+ case 'ui/notifications/tool-input-partial':
293
+ // Streaming: merge partial input with existing
294
+ context.toolInput = Object.assign({}, context.toolInput, params.arguments || {});
295
+ window.dispatchEvent(new CustomEvent('tool:input-partial', { detail: { arguments: context.toolInput } }));
296
+ break;
265
297
  case 'ui/notifications/tool-result':
266
298
  context.toolOutput = params.content;
267
299
  context.structuredContent = params.structuredContent;
@@ -272,18 +304,26 @@ var ExtAppsAdapter = {
272
304
  Object.assign(context.hostContext, params);
273
305
  context.notifyContextChange(params);
274
306
  break;
307
+ case 'ui/notifications/cancelled':
308
+ window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
309
+ break;
275
310
  }
276
311
  },
277
312
  isOriginTrusted: function(origin) {
278
313
  if (this.trustedOrigins.length > 0) {
279
314
  return this.trustedOrigins.indexOf(origin) !== -1;
280
315
  }
281
- // When no trusted origins configured, trust first message origin (trust-on-first-use).
282
- // SECURITY WARNING: This creates a race condition - whichever iframe sends the first
283
- // message establishes permanent trust. For production, always configure trustedOrigins.
316
+ // Trust-on-first-use: trust first message origin.
317
+ // SECURITY WARNING: For production, always configure trustedOrigins.
284
318
  if (!this.trustedOrigin) {
319
+ // Guard against race condition where multiple messages arrive simultaneously
320
+ if (this.originTrustPending) {
321
+ return false;
322
+ }
285
323
  if (window.parent !== window && origin) {
324
+ this.originTrustPending = true;
286
325
  this.trustedOrigin = origin;
326
+ this.originTrustPending = false; // Reset after successful trust establishment
287
327
  return true;
288
328
  }
289
329
  return false;
@@ -353,6 +393,34 @@ var ExtAppsAdapter = {
353
393
  },
354
394
  requestClose: function(context) {
355
395
  return this.sendRequest('ui/close', {});
396
+ },
397
+ // Extended ext-apps methods (full specification)
398
+ updateModelContext: function(context, data, merge) {
399
+ if (!this.hostCapabilities.modelContextUpdate) {
400
+ return Promise.reject(new Error('Model context update not supported'));
401
+ }
402
+ return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
403
+ },
404
+ log: function(context, level, message, data) {
405
+ if (!this.hostCapabilities.logging) {
406
+ // Fallback to console logging if host doesn't support it
407
+ var logFn = console[level] || console.log;
408
+ logFn('[Widget] ' + message, data);
409
+ return Promise.resolve();
410
+ }
411
+ return this.sendRequest('ui/log', { level: level, message: message, data: data });
412
+ },
413
+ registerTool: function(context, name, description, inputSchema) {
414
+ if (!this.hostCapabilities.widgetTools) {
415
+ return Promise.reject(new Error('Widget tool registration not supported'));
416
+ }
417
+ return this.sendRequest('ui/registerTool', { name: name, description: description, inputSchema: inputSchema });
418
+ },
419
+ unregisterTool: function(context, name) {
420
+ if (!this.hostCapabilities.widgetTools) {
421
+ return Promise.reject(new Error('Widget tool unregistration not supported'));
422
+ }
423
+ return this.sendRequest('ui/unregisterTool', { name: name });
356
424
  }
357
425
  };
358
426
  `.trim();
@@ -372,12 +440,26 @@ var ClaudeAdapter = {
372
440
  }),
373
441
  canHandle: function() {
374
442
  if (typeof window === 'undefined') return false;
443
+
444
+ // If MCP Apps is enabled, let ext-apps adapter handle it
445
+ if (window.__mcpAppsEnabled) return false;
446
+ if (window.__mcpPlatform === 'ext-apps') return false;
447
+ if (window.__extAppsInitialized) return false;
448
+
449
+ // Legacy Claude detection
375
450
  if (window.__mcpPlatform === 'claude') return true;
376
451
  if (window.claude) return true;
377
452
  if (window.__claudeArtifact) return true;
378
453
  if (typeof location !== 'undefined') {
379
- var href = location.href;
380
- if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
454
+ try {
455
+ var url = new URL(location.href);
456
+ var hostname = url.hostname.toLowerCase();
457
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
458
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
459
+ if (isClaudeHost || isAnthropicHost) return true;
460
+ } catch (e) {
461
+ // If URL parsing fails, fall through
462
+ }
381
463
  }
382
464
  return false;
383
465
  },
@@ -722,6 +804,42 @@ FrontMcpBridge.prototype.requestClose = function() {
722
804
  return this._adapter.requestClose(this._context);
723
805
  };
724
806
 
807
+ // Extended ext-apps methods (full specification)
808
+ FrontMcpBridge.prototype.updateModelContext = function(context, merge) {
809
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
810
+ if (!this._adapter.updateModelContext) {
811
+ return Promise.reject(new Error('updateModelContext not supported on this platform'));
812
+ }
813
+ return this._adapter.updateModelContext(this._context, context, merge);
814
+ };
815
+
816
+ FrontMcpBridge.prototype.log = function(level, message, data) {
817
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
818
+ if (!this._adapter.log) {
819
+ // Fallback to console
820
+ var logFn = console[level] || console.log;
821
+ logFn('[Widget] ' + message, data);
822
+ return Promise.resolve();
823
+ }
824
+ return this._adapter.log(this._context, level, message, data);
825
+ };
826
+
827
+ FrontMcpBridge.prototype.registerTool = function(name, description, inputSchema) {
828
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
829
+ if (!this._adapter.registerTool) {
830
+ return Promise.reject(new Error('registerTool not supported on this platform'));
831
+ }
832
+ return this._adapter.registerTool(this._context, name, description, inputSchema);
833
+ };
834
+
835
+ FrontMcpBridge.prototype.unregisterTool = function(name) {
836
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
837
+ if (!this._adapter.unregisterTool) {
838
+ return Promise.reject(new Error('unregisterTool not supported on this platform'));
839
+ }
840
+ return this._adapter.unregisterTool(this._context, name);
841
+ };
842
+
725
843
  FrontMcpBridge.prototype.setWidgetState = function(state) {
726
844
  Object.assign(this._context.widgetState, state);
727
845
  this._saveWidgetState();
package/build/index.js CHANGED
@@ -3004,16 +3004,43 @@ var ExtAppsAdapter = {
3004
3004
  capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
3005
3005
  trustedOrigins: ${originsArray},
3006
3006
  trustedOrigin: null,
3007
+ originTrustPending: false,
3007
3008
  pendingRequests: {},
3008
3009
  requestId: 0,
3009
3010
  hostCapabilities: {},
3010
3011
  canHandle: function() {
3011
3012
  if (typeof window === 'undefined') return false;
3012
3013
  if (window.parent === window) return false;
3013
- // Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
3014
+
3015
+ // Check for OpenAI SDK - defer to OpenAIAdapter
3016
+ if (window.openai && window.openai.canvas) return false;
3014
3017
  if (window.openai && typeof window.openai.callTool === 'function') return false;
3018
+
3019
+ // Explicit ext-apps marker
3015
3020
  if (window.__mcpPlatform === 'ext-apps') return true;
3016
- return true;
3021
+ if (window.__extAppsInitialized) return true;
3022
+
3023
+ // Claude MCP Apps mode (2026+) - uses ext-apps protocol
3024
+ if (window.__mcpAppsEnabled) return true;
3025
+
3026
+ // Legacy Claude detection - defer to ClaudeAdapter
3027
+ if (window.claude) return false;
3028
+ if (window.__claudeArtifact) return false;
3029
+ if (window.__mcpPlatform === 'claude') return false;
3030
+ if (typeof location !== 'undefined') {
3031
+ try {
3032
+ var url = new URL(location.href);
3033
+ var hostname = url.hostname.toLowerCase();
3034
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
3035
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
3036
+ if (isClaudeHost || isAnthropicHost) return false;
3037
+ } catch (e) {
3038
+ // If URL parsing fails, fall through to other checks
3039
+ }
3040
+ }
3041
+
3042
+ // Do NOT default to true for any iframe
3043
+ return false;
3017
3044
  },
3018
3045
  initialize: function(context) {
3019
3046
  var self = this;
@@ -3055,6 +3082,11 @@ var ExtAppsAdapter = {
3055
3082
  context.toolInput = params.arguments || {};
3056
3083
  window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
3057
3084
  break;
3085
+ case 'ui/notifications/tool-input-partial':
3086
+ // Streaming: merge partial input with existing
3087
+ context.toolInput = Object.assign({}, context.toolInput, params.arguments || {});
3088
+ window.dispatchEvent(new CustomEvent('tool:input-partial', { detail: { arguments: context.toolInput } }));
3089
+ break;
3058
3090
  case 'ui/notifications/tool-result':
3059
3091
  context.toolOutput = params.content;
3060
3092
  context.structuredContent = params.structuredContent;
@@ -3065,18 +3097,26 @@ var ExtAppsAdapter = {
3065
3097
  Object.assign(context.hostContext, params);
3066
3098
  context.notifyContextChange(params);
3067
3099
  break;
3100
+ case 'ui/notifications/cancelled':
3101
+ window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
3102
+ break;
3068
3103
  }
3069
3104
  },
3070
3105
  isOriginTrusted: function(origin) {
3071
3106
  if (this.trustedOrigins.length > 0) {
3072
3107
  return this.trustedOrigins.indexOf(origin) !== -1;
3073
3108
  }
3074
- // When no trusted origins configured, trust first message origin (trust-on-first-use).
3075
- // SECURITY WARNING: This creates a race condition - whichever iframe sends the first
3076
- // message establishes permanent trust. For production, always configure trustedOrigins.
3109
+ // Trust-on-first-use: trust first message origin.
3110
+ // SECURITY WARNING: For production, always configure trustedOrigins.
3077
3111
  if (!this.trustedOrigin) {
3112
+ // Guard against race condition where multiple messages arrive simultaneously
3113
+ if (this.originTrustPending) {
3114
+ return false;
3115
+ }
3078
3116
  if (window.parent !== window && origin) {
3117
+ this.originTrustPending = true;
3079
3118
  this.trustedOrigin = origin;
3119
+ this.originTrustPending = false; // Reset after successful trust establishment
3080
3120
  return true;
3081
3121
  }
3082
3122
  return false;
@@ -3146,6 +3186,34 @@ var ExtAppsAdapter = {
3146
3186
  },
3147
3187
  requestClose: function(context) {
3148
3188
  return this.sendRequest('ui/close', {});
3189
+ },
3190
+ // Extended ext-apps methods (full specification)
3191
+ updateModelContext: function(context, data, merge) {
3192
+ if (!this.hostCapabilities.modelContextUpdate) {
3193
+ return Promise.reject(new Error('Model context update not supported'));
3194
+ }
3195
+ return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
3196
+ },
3197
+ log: function(context, level, message, data) {
3198
+ if (!this.hostCapabilities.logging) {
3199
+ // Fallback to console logging if host doesn't support it
3200
+ var logFn = console[level] || console.log;
3201
+ logFn('[Widget] ' + message, data);
3202
+ return Promise.resolve();
3203
+ }
3204
+ return this.sendRequest('ui/log', { level: level, message: message, data: data });
3205
+ },
3206
+ registerTool: function(context, name, description, inputSchema) {
3207
+ if (!this.hostCapabilities.widgetTools) {
3208
+ return Promise.reject(new Error('Widget tool registration not supported'));
3209
+ }
3210
+ return this.sendRequest('ui/registerTool', { name: name, description: description, inputSchema: inputSchema });
3211
+ },
3212
+ unregisterTool: function(context, name) {
3213
+ if (!this.hostCapabilities.widgetTools) {
3214
+ return Promise.reject(new Error('Widget tool unregistration not supported'));
3215
+ }
3216
+ return this.sendRequest('ui/unregisterTool', { name: name });
3149
3217
  }
3150
3218
  };
3151
3219
  `.trim();
@@ -3165,12 +3233,26 @@ var ClaudeAdapter = {
3165
3233
  }),
3166
3234
  canHandle: function() {
3167
3235
  if (typeof window === 'undefined') return false;
3236
+
3237
+ // If MCP Apps is enabled, let ext-apps adapter handle it
3238
+ if (window.__mcpAppsEnabled) return false;
3239
+ if (window.__mcpPlatform === 'ext-apps') return false;
3240
+ if (window.__extAppsInitialized) return false;
3241
+
3242
+ // Legacy Claude detection
3168
3243
  if (window.__mcpPlatform === 'claude') return true;
3169
3244
  if (window.claude) return true;
3170
3245
  if (window.__claudeArtifact) return true;
3171
3246
  if (typeof location !== 'undefined') {
3172
- var href = location.href;
3173
- if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
3247
+ try {
3248
+ var url = new URL(location.href);
3249
+ var hostname = url.hostname.toLowerCase();
3250
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
3251
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
3252
+ if (isClaudeHost || isAnthropicHost) return true;
3253
+ } catch (e) {
3254
+ // If URL parsing fails, fall through
3255
+ }
3174
3256
  }
3175
3257
  return false;
3176
3258
  },
@@ -3515,6 +3597,42 @@ FrontMcpBridge.prototype.requestClose = function() {
3515
3597
  return this._adapter.requestClose(this._context);
3516
3598
  };
3517
3599
 
3600
+ // Extended ext-apps methods (full specification)
3601
+ FrontMcpBridge.prototype.updateModelContext = function(context, merge) {
3602
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
3603
+ if (!this._adapter.updateModelContext) {
3604
+ return Promise.reject(new Error('updateModelContext not supported on this platform'));
3605
+ }
3606
+ return this._adapter.updateModelContext(this._context, context, merge);
3607
+ };
3608
+
3609
+ FrontMcpBridge.prototype.log = function(level, message, data) {
3610
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
3611
+ if (!this._adapter.log) {
3612
+ // Fallback to console
3613
+ var logFn = console[level] || console.log;
3614
+ logFn('[Widget] ' + message, data);
3615
+ return Promise.resolve();
3616
+ }
3617
+ return this._adapter.log(this._context, level, message, data);
3618
+ };
3619
+
3620
+ FrontMcpBridge.prototype.registerTool = function(name, description, inputSchema) {
3621
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
3622
+ if (!this._adapter.registerTool) {
3623
+ return Promise.reject(new Error('registerTool not supported on this platform'));
3624
+ }
3625
+ return this._adapter.registerTool(this._context, name, description, inputSchema);
3626
+ };
3627
+
3628
+ FrontMcpBridge.prototype.unregisterTool = function(name) {
3629
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
3630
+ if (!this._adapter.unregisterTool) {
3631
+ return Promise.reject(new Error('unregisterTool not supported on this platform'));
3632
+ }
3633
+ return this._adapter.unregisterTool(this._context, name);
3634
+ };
3635
+
3518
3636
  FrontMcpBridge.prototype.setWidgetState = function(state) {
3519
3637
  Object.assign(this._context.widgetState, state);
3520
3638
  this._saveWidgetState();