@frontmcp/uipack 0.7.2 → 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.
package/esm/index.mjs CHANGED
@@ -4586,6 +4586,17 @@ var DEFAULT_RENDERER_ASSETS = {
4586
4586
  }
4587
4587
  };
4588
4588
 
4589
+ // libs/uipack/src/adapters/platform-meta.constants.ts
4590
+ var DISPLAY_MODE_MAP = {
4591
+ // Standard MCP Apps modes
4592
+ inline: "inline",
4593
+ fullscreen: "fullscreen",
4594
+ pip: "pip",
4595
+ // OpenAI-style aliases
4596
+ widget: "inline",
4597
+ panel: "fullscreen"
4598
+ };
4599
+
4589
4600
  // libs/uipack/src/adapters/platform-meta.ts
4590
4601
  function buildUIMeta(options) {
4591
4602
  const { uiConfig, platformType, html, token, directUrl, rendererType, contentHash, manifestUri } = options;
@@ -4667,6 +4678,21 @@ function buildClaudeMeta(meta, uiConfig) {
4667
4678
  if (uiConfig.prefersBorder !== void 0) {
4668
4679
  meta["claude/prefersBorder"] = uiConfig.prefersBorder;
4669
4680
  }
4681
+ if (uiConfig.resourceUri) {
4682
+ meta["ui/resourceUri"] = uiConfig.resourceUri;
4683
+ }
4684
+ if (uiConfig.csp) {
4685
+ const csp = {};
4686
+ if (uiConfig.csp.connectDomains?.length) {
4687
+ csp.connectDomains = uiConfig.csp.connectDomains;
4688
+ }
4689
+ if (uiConfig.csp.resourceDomains?.length) {
4690
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
4691
+ }
4692
+ if (Object.keys(csp).length > 0) {
4693
+ meta["ui/csp"] = csp;
4694
+ }
4695
+ }
4670
4696
  return meta;
4671
4697
  }
4672
4698
  function buildGeminiMeta(meta, uiConfig) {
@@ -4695,14 +4721,7 @@ function buildGenericMeta(meta, uiConfig) {
4695
4721
  }
4696
4722
  }
4697
4723
  if (uiConfig.displayMode) {
4698
- const displayModeMap = {
4699
- inline: "inline",
4700
- fullscreen: "fullscreen",
4701
- pip: "pip",
4702
- widget: "inline",
4703
- panel: "fullscreen"
4704
- };
4705
- const mappedMode = displayModeMap[uiConfig.displayMode];
4724
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4706
4725
  if (mappedMode) {
4707
4726
  meta["ui/displayMode"] = mappedMode;
4708
4727
  }
@@ -4730,15 +4749,7 @@ function buildExtAppsMeta(meta, uiConfig) {
4730
4749
  }
4731
4750
  }
4732
4751
  if (uiConfig.displayMode) {
4733
- const displayModeMap = {
4734
- inline: "inline",
4735
- fullscreen: "fullscreen",
4736
- pip: "pip",
4737
- // Map OpenAI-style values
4738
- widget: "inline",
4739
- panel: "fullscreen"
4740
- };
4741
- const mappedMode = displayModeMap[uiConfig.displayMode];
4752
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4742
4753
  if (mappedMode) {
4743
4754
  meta["ui/displayMode"] = mappedMode;
4744
4755
  }
@@ -4797,12 +4808,7 @@ function buildToolDiscoveryMeta(options) {
4797
4808
  }
4798
4809
  }
4799
4810
  if (uiConfig.displayMode) {
4800
- const displayModeMap = {
4801
- inline: "inline",
4802
- fullscreen: "fullscreen",
4803
- pip: "pip"
4804
- };
4805
- const mappedMode = displayModeMap[uiConfig.displayMode];
4811
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4806
4812
  if (mappedMode) {
4807
4813
  meta["ui/displayMode"] = mappedMode;
4808
4814
  }
@@ -4814,7 +4820,32 @@ function buildToolDiscoveryMeta(options) {
4814
4820
  meta["ui/domain"] = uiConfig.sandboxDomain;
4815
4821
  }
4816
4822
  break;
4817
- // Claude, Gemini, IDEs don't need discovery metadata
4823
+ case "claude":
4824
+ meta["ui/resourceUri"] = staticWidgetUri;
4825
+ meta["ui/mimeType"] = "text/html+mcp";
4826
+ if (uiConfig.displayMode) {
4827
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4828
+ if (mappedMode) {
4829
+ meta["ui/displayMode"] = mappedMode;
4830
+ }
4831
+ }
4832
+ if (uiConfig.prefersBorder !== void 0) {
4833
+ meta["ui/prefersBorder"] = uiConfig.prefersBorder;
4834
+ }
4835
+ if (uiConfig.csp) {
4836
+ const csp = {};
4837
+ if (uiConfig.csp.connectDomains?.length) {
4838
+ csp.connectDomains = uiConfig.csp.connectDomains;
4839
+ }
4840
+ if (uiConfig.csp.resourceDomains?.length) {
4841
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
4842
+ }
4843
+ if (Object.keys(csp).length > 0) {
4844
+ meta["ui/csp"] = csp;
4845
+ }
4846
+ }
4847
+ break;
4848
+ // Gemini, IDEs don't need discovery metadata
4818
4849
  // They use inline HTML at call time
4819
4850
  default:
4820
4851
  break;
@@ -5209,16 +5240,43 @@ var ExtAppsAdapter = {
5209
5240
  capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
5210
5241
  trustedOrigins: ${originsArray},
5211
5242
  trustedOrigin: null,
5243
+ originTrustPending: false,
5212
5244
  pendingRequests: {},
5213
5245
  requestId: 0,
5214
5246
  hostCapabilities: {},
5215
5247
  canHandle: function() {
5216
5248
  if (typeof window === 'undefined') return false;
5217
5249
  if (window.parent === window) return false;
5218
- // Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
5250
+
5251
+ // Check for OpenAI SDK - defer to OpenAIAdapter
5252
+ if (window.openai && window.openai.canvas) return false;
5219
5253
  if (window.openai && typeof window.openai.callTool === 'function') return false;
5254
+
5255
+ // Explicit ext-apps marker
5220
5256
  if (window.__mcpPlatform === 'ext-apps') return true;
5221
- return true;
5257
+ if (window.__extAppsInitialized) return true;
5258
+
5259
+ // Claude MCP Apps mode (2026+) - uses ext-apps protocol
5260
+ if (window.__mcpAppsEnabled) return true;
5261
+
5262
+ // Legacy Claude detection - defer to ClaudeAdapter
5263
+ if (window.claude) return false;
5264
+ if (window.__claudeArtifact) return false;
5265
+ if (window.__mcpPlatform === 'claude') return false;
5266
+ if (typeof location !== 'undefined') {
5267
+ try {
5268
+ var url = new URL(location.href);
5269
+ var hostname = url.hostname.toLowerCase();
5270
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
5271
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
5272
+ if (isClaudeHost || isAnthropicHost) return false;
5273
+ } catch (e) {
5274
+ // If URL parsing fails, fall through to other checks
5275
+ }
5276
+ }
5277
+
5278
+ // Do NOT default to true for any iframe
5279
+ return false;
5222
5280
  },
5223
5281
  initialize: function(context) {
5224
5282
  var self = this;
@@ -5260,6 +5318,11 @@ var ExtAppsAdapter = {
5260
5318
  context.toolInput = params.arguments || {};
5261
5319
  window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
5262
5320
  break;
5321
+ case 'ui/notifications/tool-input-partial':
5322
+ // Streaming: merge partial input with existing
5323
+ context.toolInput = Object.assign({}, context.toolInput, params.arguments || {});
5324
+ window.dispatchEvent(new CustomEvent('tool:input-partial', { detail: { arguments: context.toolInput } }));
5325
+ break;
5263
5326
  case 'ui/notifications/tool-result':
5264
5327
  context.toolOutput = params.content;
5265
5328
  context.structuredContent = params.structuredContent;
@@ -5270,18 +5333,26 @@ var ExtAppsAdapter = {
5270
5333
  Object.assign(context.hostContext, params);
5271
5334
  context.notifyContextChange(params);
5272
5335
  break;
5336
+ case 'ui/notifications/cancelled':
5337
+ window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
5338
+ break;
5273
5339
  }
5274
5340
  },
5275
5341
  isOriginTrusted: function(origin) {
5276
5342
  if (this.trustedOrigins.length > 0) {
5277
5343
  return this.trustedOrigins.indexOf(origin) !== -1;
5278
5344
  }
5279
- // When no trusted origins configured, trust first message origin (trust-on-first-use).
5280
- // SECURITY WARNING: This creates a race condition - whichever iframe sends the first
5281
- // message establishes permanent trust. For production, always configure trustedOrigins.
5345
+ // Trust-on-first-use: trust first message origin.
5346
+ // SECURITY WARNING: For production, always configure trustedOrigins.
5282
5347
  if (!this.trustedOrigin) {
5348
+ // Guard against race condition where multiple messages arrive simultaneously
5349
+ if (this.originTrustPending) {
5350
+ return false;
5351
+ }
5283
5352
  if (window.parent !== window && origin) {
5353
+ this.originTrustPending = true;
5284
5354
  this.trustedOrigin = origin;
5355
+ this.originTrustPending = false; // Reset after successful trust establishment
5285
5356
  return true;
5286
5357
  }
5287
5358
  return false;
@@ -5351,6 +5422,34 @@ var ExtAppsAdapter = {
5351
5422
  },
5352
5423
  requestClose: function(context) {
5353
5424
  return this.sendRequest('ui/close', {});
5425
+ },
5426
+ // Extended ext-apps methods (full specification)
5427
+ updateModelContext: function(context, data, merge) {
5428
+ if (!this.hostCapabilities.modelContextUpdate) {
5429
+ return Promise.reject(new Error('Model context update not supported'));
5430
+ }
5431
+ return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
5432
+ },
5433
+ log: function(context, level, message, data) {
5434
+ if (!this.hostCapabilities.logging) {
5435
+ // Fallback to console logging if host doesn't support it
5436
+ var logFn = console[level] || console.log;
5437
+ logFn('[Widget] ' + message, data);
5438
+ return Promise.resolve();
5439
+ }
5440
+ return this.sendRequest('ui/log', { level: level, message: message, data: data });
5441
+ },
5442
+ registerTool: function(context, name, description, inputSchema) {
5443
+ if (!this.hostCapabilities.widgetTools) {
5444
+ return Promise.reject(new Error('Widget tool registration not supported'));
5445
+ }
5446
+ return this.sendRequest('ui/registerTool', { name: name, description: description, inputSchema: inputSchema });
5447
+ },
5448
+ unregisterTool: function(context, name) {
5449
+ if (!this.hostCapabilities.widgetTools) {
5450
+ return Promise.reject(new Error('Widget tool unregistration not supported'));
5451
+ }
5452
+ return this.sendRequest('ui/unregisterTool', { name: name });
5354
5453
  }
5355
5454
  };
5356
5455
  `.trim();
@@ -5370,12 +5469,26 @@ var ClaudeAdapter = {
5370
5469
  }),
5371
5470
  canHandle: function() {
5372
5471
  if (typeof window === 'undefined') return false;
5472
+
5473
+ // If MCP Apps is enabled, let ext-apps adapter handle it
5474
+ if (window.__mcpAppsEnabled) return false;
5475
+ if (window.__mcpPlatform === 'ext-apps') return false;
5476
+ if (window.__extAppsInitialized) return false;
5477
+
5478
+ // Legacy Claude detection
5373
5479
  if (window.__mcpPlatform === 'claude') return true;
5374
5480
  if (window.claude) return true;
5375
5481
  if (window.__claudeArtifact) return true;
5376
5482
  if (typeof location !== 'undefined') {
5377
- var href = location.href;
5378
- if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
5483
+ try {
5484
+ var url = new URL(location.href);
5485
+ var hostname = url.hostname.toLowerCase();
5486
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
5487
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
5488
+ if (isClaudeHost || isAnthropicHost) return true;
5489
+ } catch (e) {
5490
+ // If URL parsing fails, fall through
5491
+ }
5379
5492
  }
5380
5493
  return false;
5381
5494
  },
@@ -5720,6 +5833,42 @@ FrontMcpBridge.prototype.requestClose = function() {
5720
5833
  return this._adapter.requestClose(this._context);
5721
5834
  };
5722
5835
 
5836
+ // Extended ext-apps methods (full specification)
5837
+ FrontMcpBridge.prototype.updateModelContext = function(context, merge) {
5838
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
5839
+ if (!this._adapter.updateModelContext) {
5840
+ return Promise.reject(new Error('updateModelContext not supported on this platform'));
5841
+ }
5842
+ return this._adapter.updateModelContext(this._context, context, merge);
5843
+ };
5844
+
5845
+ FrontMcpBridge.prototype.log = function(level, message, data) {
5846
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
5847
+ if (!this._adapter.log) {
5848
+ // Fallback to console
5849
+ var logFn = console[level] || console.log;
5850
+ logFn('[Widget] ' + message, data);
5851
+ return Promise.resolve();
5852
+ }
5853
+ return this._adapter.log(this._context, level, message, data);
5854
+ };
5855
+
5856
+ FrontMcpBridge.prototype.registerTool = function(name, description, inputSchema) {
5857
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
5858
+ if (!this._adapter.registerTool) {
5859
+ return Promise.reject(new Error('registerTool not supported on this platform'));
5860
+ }
5861
+ return this._adapter.registerTool(this._context, name, description, inputSchema);
5862
+ };
5863
+
5864
+ FrontMcpBridge.prototype.unregisterTool = function(name) {
5865
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
5866
+ if (!this._adapter.unregisterTool) {
5867
+ return Promise.reject(new Error('unregisterTool not supported on this platform'));
5868
+ }
5869
+ return this._adapter.unregisterTool(this._context, name);
5870
+ };
5871
+
5723
5872
  FrontMcpBridge.prototype.setWidgetState = function(state) {
5724
5873
  Object.assign(this._context.widgetState, state);
5725
5874
  this._saveWidgetState();
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/uipack",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "FrontMCP UIpack - Bundling, build tools, and platform adapters for MCP UI development (React-free core)",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -54,8 +54,11 @@
54
54
  },
55
55
  "./esm": null
56
56
  },
57
+ "engines": {
58
+ "node": ">=22.0.0"
59
+ },
57
60
  "dependencies": {
58
- "@frontmcp/utils": "0.7.2",
61
+ "@frontmcp/utils": "0.8.0",
59
62
  "@swc/core": "^1.5.0",
60
63
  "enclave-vm": "^2.7.0",
61
64
  "esbuild": "^0.27.1",
@@ -2335,16 +2335,43 @@ var ExtAppsAdapter = {
2335
2335
  capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
2336
2336
  trustedOrigins: ${originsArray},
2337
2337
  trustedOrigin: null,
2338
+ originTrustPending: false,
2338
2339
  pendingRequests: {},
2339
2340
  requestId: 0,
2340
2341
  hostCapabilities: {},
2341
2342
  canHandle: function() {
2342
2343
  if (typeof window === 'undefined') return false;
2343
2344
  if (window.parent === window) return false;
2344
- // Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
2345
+
2346
+ // Check for OpenAI SDK - defer to OpenAIAdapter
2347
+ if (window.openai && window.openai.canvas) return false;
2345
2348
  if (window.openai && typeof window.openai.callTool === 'function') return false;
2349
+
2350
+ // Explicit ext-apps marker
2346
2351
  if (window.__mcpPlatform === 'ext-apps') return true;
2347
- return true;
2352
+ if (window.__extAppsInitialized) return true;
2353
+
2354
+ // Claude MCP Apps mode (2026+) - uses ext-apps protocol
2355
+ if (window.__mcpAppsEnabled) return true;
2356
+
2357
+ // Legacy Claude detection - defer to ClaudeAdapter
2358
+ if (window.claude) return false;
2359
+ if (window.__claudeArtifact) return false;
2360
+ if (window.__mcpPlatform === 'claude') return false;
2361
+ if (typeof location !== 'undefined') {
2362
+ try {
2363
+ var url = new URL(location.href);
2364
+ var hostname = url.hostname.toLowerCase();
2365
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
2366
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
2367
+ if (isClaudeHost || isAnthropicHost) return false;
2368
+ } catch (e) {
2369
+ // If URL parsing fails, fall through to other checks
2370
+ }
2371
+ }
2372
+
2373
+ // Do NOT default to true for any iframe
2374
+ return false;
2348
2375
  },
2349
2376
  initialize: function(context) {
2350
2377
  var self = this;
@@ -2386,6 +2413,11 @@ var ExtAppsAdapter = {
2386
2413
  context.toolInput = params.arguments || {};
2387
2414
  window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
2388
2415
  break;
2416
+ case 'ui/notifications/tool-input-partial':
2417
+ // Streaming: merge partial input with existing
2418
+ context.toolInput = Object.assign({}, context.toolInput, params.arguments || {});
2419
+ window.dispatchEvent(new CustomEvent('tool:input-partial', { detail: { arguments: context.toolInput } }));
2420
+ break;
2389
2421
  case 'ui/notifications/tool-result':
2390
2422
  context.toolOutput = params.content;
2391
2423
  context.structuredContent = params.structuredContent;
@@ -2396,18 +2428,26 @@ var ExtAppsAdapter = {
2396
2428
  Object.assign(context.hostContext, params);
2397
2429
  context.notifyContextChange(params);
2398
2430
  break;
2431
+ case 'ui/notifications/cancelled':
2432
+ window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
2433
+ break;
2399
2434
  }
2400
2435
  },
2401
2436
  isOriginTrusted: function(origin) {
2402
2437
  if (this.trustedOrigins.length > 0) {
2403
2438
  return this.trustedOrigins.indexOf(origin) !== -1;
2404
2439
  }
2405
- // When no trusted origins configured, trust first message origin (trust-on-first-use).
2406
- // SECURITY WARNING: This creates a race condition - whichever iframe sends the first
2407
- // message establishes permanent trust. For production, always configure trustedOrigins.
2440
+ // Trust-on-first-use: trust first message origin.
2441
+ // SECURITY WARNING: For production, always configure trustedOrigins.
2408
2442
  if (!this.trustedOrigin) {
2443
+ // Guard against race condition where multiple messages arrive simultaneously
2444
+ if (this.originTrustPending) {
2445
+ return false;
2446
+ }
2409
2447
  if (window.parent !== window && origin) {
2448
+ this.originTrustPending = true;
2410
2449
  this.trustedOrigin = origin;
2450
+ this.originTrustPending = false; // Reset after successful trust establishment
2411
2451
  return true;
2412
2452
  }
2413
2453
  return false;
@@ -2477,6 +2517,34 @@ var ExtAppsAdapter = {
2477
2517
  },
2478
2518
  requestClose: function(context) {
2479
2519
  return this.sendRequest('ui/close', {});
2520
+ },
2521
+ // Extended ext-apps methods (full specification)
2522
+ updateModelContext: function(context, data, merge) {
2523
+ if (!this.hostCapabilities.modelContextUpdate) {
2524
+ return Promise.reject(new Error('Model context update not supported'));
2525
+ }
2526
+ return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
2527
+ },
2528
+ log: function(context, level, message, data) {
2529
+ if (!this.hostCapabilities.logging) {
2530
+ // Fallback to console logging if host doesn't support it
2531
+ var logFn = console[level] || console.log;
2532
+ logFn('[Widget] ' + message, data);
2533
+ return Promise.resolve();
2534
+ }
2535
+ return this.sendRequest('ui/log', { level: level, message: message, data: data });
2536
+ },
2537
+ registerTool: function(context, name, description, inputSchema) {
2538
+ if (!this.hostCapabilities.widgetTools) {
2539
+ return Promise.reject(new Error('Widget tool registration not supported'));
2540
+ }
2541
+ return this.sendRequest('ui/registerTool', { name: name, description: description, inputSchema: inputSchema });
2542
+ },
2543
+ unregisterTool: function(context, name) {
2544
+ if (!this.hostCapabilities.widgetTools) {
2545
+ return Promise.reject(new Error('Widget tool unregistration not supported'));
2546
+ }
2547
+ return this.sendRequest('ui/unregisterTool', { name: name });
2480
2548
  }
2481
2549
  };
2482
2550
  `.trim();
@@ -2496,12 +2564,26 @@ var ClaudeAdapter = {
2496
2564
  }),
2497
2565
  canHandle: function() {
2498
2566
  if (typeof window === 'undefined') return false;
2567
+
2568
+ // If MCP Apps is enabled, let ext-apps adapter handle it
2569
+ if (window.__mcpAppsEnabled) return false;
2570
+ if (window.__mcpPlatform === 'ext-apps') return false;
2571
+ if (window.__extAppsInitialized) return false;
2572
+
2573
+ // Legacy Claude detection
2499
2574
  if (window.__mcpPlatform === 'claude') return true;
2500
2575
  if (window.claude) return true;
2501
2576
  if (window.__claudeArtifact) return true;
2502
2577
  if (typeof location !== 'undefined') {
2503
- var href = location.href;
2504
- if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
2578
+ try {
2579
+ var url = new URL(location.href);
2580
+ var hostname = url.hostname.toLowerCase();
2581
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
2582
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
2583
+ if (isClaudeHost || isAnthropicHost) return true;
2584
+ } catch (e) {
2585
+ // If URL parsing fails, fall through
2586
+ }
2505
2587
  }
2506
2588
  return false;
2507
2589
  },
@@ -2846,6 +2928,42 @@ FrontMcpBridge.prototype.requestClose = function() {
2846
2928
  return this._adapter.requestClose(this._context);
2847
2929
  };
2848
2930
 
2931
+ // Extended ext-apps methods (full specification)
2932
+ FrontMcpBridge.prototype.updateModelContext = function(context, merge) {
2933
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2934
+ if (!this._adapter.updateModelContext) {
2935
+ return Promise.reject(new Error('updateModelContext not supported on this platform'));
2936
+ }
2937
+ return this._adapter.updateModelContext(this._context, context, merge);
2938
+ };
2939
+
2940
+ FrontMcpBridge.prototype.log = function(level, message, data) {
2941
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2942
+ if (!this._adapter.log) {
2943
+ // Fallback to console
2944
+ var logFn = console[level] || console.log;
2945
+ logFn('[Widget] ' + message, data);
2946
+ return Promise.resolve();
2947
+ }
2948
+ return this._adapter.log(this._context, level, message, data);
2949
+ };
2950
+
2951
+ FrontMcpBridge.prototype.registerTool = function(name, description, inputSchema) {
2952
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2953
+ if (!this._adapter.registerTool) {
2954
+ return Promise.reject(new Error('registerTool not supported on this platform'));
2955
+ }
2956
+ return this._adapter.registerTool(this._context, name, description, inputSchema);
2957
+ };
2958
+
2959
+ FrontMcpBridge.prototype.unregisterTool = function(name) {
2960
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2961
+ if (!this._adapter.unregisterTool) {
2962
+ return Promise.reject(new Error('unregisterTool not supported on this platform'));
2963
+ }
2964
+ return this._adapter.unregisterTool(this._context, name);
2965
+ };
2966
+
2849
2967
  FrontMcpBridge.prototype.setWidgetState = function(state) {
2850
2968
  Object.assign(this._context.widgetState, state);
2851
2969
  this._saveWidgetState();
@@ -4564,6 +4682,17 @@ async function renderToolTemplateAsync(options) {
4564
4682
  }
4565
4683
  }
4566
4684
 
4685
+ // libs/uipack/src/adapters/platform-meta.constants.ts
4686
+ var DISPLAY_MODE_MAP = {
4687
+ // Standard MCP Apps modes
4688
+ inline: "inline",
4689
+ fullscreen: "fullscreen",
4690
+ pip: "pip",
4691
+ // OpenAI-style aliases
4692
+ widget: "inline",
4693
+ panel: "fullscreen"
4694
+ };
4695
+
4567
4696
  // libs/uipack/src/adapters/platform-meta.ts
4568
4697
  function buildUIMeta(options) {
4569
4698
  const { uiConfig, platformType, html, token, directUrl, rendererType, contentHash, manifestUri } = options;
@@ -4635,6 +4764,21 @@ function buildClaudeMeta(meta, uiConfig) {
4635
4764
  if (uiConfig.prefersBorder !== void 0) {
4636
4765
  meta["claude/prefersBorder"] = uiConfig.prefersBorder;
4637
4766
  }
4767
+ if (uiConfig.resourceUri) {
4768
+ meta["ui/resourceUri"] = uiConfig.resourceUri;
4769
+ }
4770
+ if (uiConfig.csp) {
4771
+ const csp = {};
4772
+ if (uiConfig.csp.connectDomains?.length) {
4773
+ csp.connectDomains = uiConfig.csp.connectDomains;
4774
+ }
4775
+ if (uiConfig.csp.resourceDomains?.length) {
4776
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
4777
+ }
4778
+ if (Object.keys(csp).length > 0) {
4779
+ meta["ui/csp"] = csp;
4780
+ }
4781
+ }
4638
4782
  return meta;
4639
4783
  }
4640
4784
  function buildGeminiMeta(meta, uiConfig) {
@@ -4663,14 +4807,7 @@ function buildGenericMeta(meta, uiConfig) {
4663
4807
  }
4664
4808
  }
4665
4809
  if (uiConfig.displayMode) {
4666
- const displayModeMap = {
4667
- inline: "inline",
4668
- fullscreen: "fullscreen",
4669
- pip: "pip",
4670
- widget: "inline",
4671
- panel: "fullscreen"
4672
- };
4673
- const mappedMode = displayModeMap[uiConfig.displayMode];
4810
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4674
4811
  if (mappedMode) {
4675
4812
  meta["ui/displayMode"] = mappedMode;
4676
4813
  }
@@ -4698,15 +4835,7 @@ function buildExtAppsMeta(meta, uiConfig) {
4698
4835
  }
4699
4836
  }
4700
4837
  if (uiConfig.displayMode) {
4701
- const displayModeMap = {
4702
- inline: "inline",
4703
- fullscreen: "fullscreen",
4704
- pip: "pip",
4705
- // Map OpenAI-style values
4706
- widget: "inline",
4707
- panel: "fullscreen"
4708
- };
4709
- const mappedMode = displayModeMap[uiConfig.displayMode];
4838
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4710
4839
  if (mappedMode) {
4711
4840
  meta["ui/displayMode"] = mappedMode;
4712
4841
  }