@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/index.js CHANGED
@@ -4852,6 +4852,17 @@ var DEFAULT_RENDERER_ASSETS = {
4852
4852
  }
4853
4853
  };
4854
4854
 
4855
+ // libs/uipack/src/adapters/platform-meta.constants.ts
4856
+ var DISPLAY_MODE_MAP = {
4857
+ // Standard MCP Apps modes
4858
+ inline: "inline",
4859
+ fullscreen: "fullscreen",
4860
+ pip: "pip",
4861
+ // OpenAI-style aliases
4862
+ widget: "inline",
4863
+ panel: "fullscreen"
4864
+ };
4865
+
4855
4866
  // libs/uipack/src/adapters/platform-meta.ts
4856
4867
  function buildUIMeta(options) {
4857
4868
  const { uiConfig, platformType, html, token, directUrl, rendererType, contentHash, manifestUri } = options;
@@ -4933,6 +4944,21 @@ function buildClaudeMeta(meta, uiConfig) {
4933
4944
  if (uiConfig.prefersBorder !== void 0) {
4934
4945
  meta["claude/prefersBorder"] = uiConfig.prefersBorder;
4935
4946
  }
4947
+ if (uiConfig.resourceUri) {
4948
+ meta["ui/resourceUri"] = uiConfig.resourceUri;
4949
+ }
4950
+ if (uiConfig.csp) {
4951
+ const csp = {};
4952
+ if (uiConfig.csp.connectDomains?.length) {
4953
+ csp.connectDomains = uiConfig.csp.connectDomains;
4954
+ }
4955
+ if (uiConfig.csp.resourceDomains?.length) {
4956
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
4957
+ }
4958
+ if (Object.keys(csp).length > 0) {
4959
+ meta["ui/csp"] = csp;
4960
+ }
4961
+ }
4936
4962
  return meta;
4937
4963
  }
4938
4964
  function buildGeminiMeta(meta, uiConfig) {
@@ -4961,14 +4987,7 @@ function buildGenericMeta(meta, uiConfig) {
4961
4987
  }
4962
4988
  }
4963
4989
  if (uiConfig.displayMode) {
4964
- const displayModeMap = {
4965
- inline: "inline",
4966
- fullscreen: "fullscreen",
4967
- pip: "pip",
4968
- widget: "inline",
4969
- panel: "fullscreen"
4970
- };
4971
- const mappedMode = displayModeMap[uiConfig.displayMode];
4990
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4972
4991
  if (mappedMode) {
4973
4992
  meta["ui/displayMode"] = mappedMode;
4974
4993
  }
@@ -4996,15 +5015,7 @@ function buildExtAppsMeta(meta, uiConfig) {
4996
5015
  }
4997
5016
  }
4998
5017
  if (uiConfig.displayMode) {
4999
- const displayModeMap = {
5000
- inline: "inline",
5001
- fullscreen: "fullscreen",
5002
- pip: "pip",
5003
- // Map OpenAI-style values
5004
- widget: "inline",
5005
- panel: "fullscreen"
5006
- };
5007
- const mappedMode = displayModeMap[uiConfig.displayMode];
5018
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
5008
5019
  if (mappedMode) {
5009
5020
  meta["ui/displayMode"] = mappedMode;
5010
5021
  }
@@ -5063,12 +5074,7 @@ function buildToolDiscoveryMeta(options) {
5063
5074
  }
5064
5075
  }
5065
5076
  if (uiConfig.displayMode) {
5066
- const displayModeMap = {
5067
- inline: "inline",
5068
- fullscreen: "fullscreen",
5069
- pip: "pip"
5070
- };
5071
- const mappedMode = displayModeMap[uiConfig.displayMode];
5077
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
5072
5078
  if (mappedMode) {
5073
5079
  meta["ui/displayMode"] = mappedMode;
5074
5080
  }
@@ -5080,7 +5086,32 @@ function buildToolDiscoveryMeta(options) {
5080
5086
  meta["ui/domain"] = uiConfig.sandboxDomain;
5081
5087
  }
5082
5088
  break;
5083
- // Claude, Gemini, IDEs don't need discovery metadata
5089
+ case "claude":
5090
+ meta["ui/resourceUri"] = staticWidgetUri;
5091
+ meta["ui/mimeType"] = "text/html+mcp";
5092
+ if (uiConfig.displayMode) {
5093
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
5094
+ if (mappedMode) {
5095
+ meta["ui/displayMode"] = mappedMode;
5096
+ }
5097
+ }
5098
+ if (uiConfig.prefersBorder !== void 0) {
5099
+ meta["ui/prefersBorder"] = uiConfig.prefersBorder;
5100
+ }
5101
+ if (uiConfig.csp) {
5102
+ const csp = {};
5103
+ if (uiConfig.csp.connectDomains?.length) {
5104
+ csp.connectDomains = uiConfig.csp.connectDomains;
5105
+ }
5106
+ if (uiConfig.csp.resourceDomains?.length) {
5107
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
5108
+ }
5109
+ if (Object.keys(csp).length > 0) {
5110
+ meta["ui/csp"] = csp;
5111
+ }
5112
+ }
5113
+ break;
5114
+ // Gemini, IDEs don't need discovery metadata
5084
5115
  // They use inline HTML at call time
5085
5116
  default:
5086
5117
  break;
@@ -5475,16 +5506,43 @@ var ExtAppsAdapter = {
5475
5506
  capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
5476
5507
  trustedOrigins: ${originsArray},
5477
5508
  trustedOrigin: null,
5509
+ originTrustPending: false,
5478
5510
  pendingRequests: {},
5479
5511
  requestId: 0,
5480
5512
  hostCapabilities: {},
5481
5513
  canHandle: function() {
5482
5514
  if (typeof window === 'undefined') return false;
5483
5515
  if (window.parent === window) return false;
5484
- // Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
5516
+
5517
+ // Check for OpenAI SDK - defer to OpenAIAdapter
5518
+ if (window.openai && window.openai.canvas) return false;
5485
5519
  if (window.openai && typeof window.openai.callTool === 'function') return false;
5520
+
5521
+ // Explicit ext-apps marker
5486
5522
  if (window.__mcpPlatform === 'ext-apps') return true;
5487
- return true;
5523
+ if (window.__extAppsInitialized) return true;
5524
+
5525
+ // Claude MCP Apps mode (2026+) - uses ext-apps protocol
5526
+ if (window.__mcpAppsEnabled) return true;
5527
+
5528
+ // Legacy Claude detection - defer to ClaudeAdapter
5529
+ if (window.claude) return false;
5530
+ if (window.__claudeArtifact) return false;
5531
+ if (window.__mcpPlatform === 'claude') return false;
5532
+ if (typeof location !== 'undefined') {
5533
+ try {
5534
+ var url = new URL(location.href);
5535
+ var hostname = url.hostname.toLowerCase();
5536
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
5537
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
5538
+ if (isClaudeHost || isAnthropicHost) return false;
5539
+ } catch (e) {
5540
+ // If URL parsing fails, fall through to other checks
5541
+ }
5542
+ }
5543
+
5544
+ // Do NOT default to true for any iframe
5545
+ return false;
5488
5546
  },
5489
5547
  initialize: function(context) {
5490
5548
  var self = this;
@@ -5526,6 +5584,11 @@ var ExtAppsAdapter = {
5526
5584
  context.toolInput = params.arguments || {};
5527
5585
  window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
5528
5586
  break;
5587
+ case 'ui/notifications/tool-input-partial':
5588
+ // Streaming: merge partial input with existing
5589
+ context.toolInput = Object.assign({}, context.toolInput, params.arguments || {});
5590
+ window.dispatchEvent(new CustomEvent('tool:input-partial', { detail: { arguments: context.toolInput } }));
5591
+ break;
5529
5592
  case 'ui/notifications/tool-result':
5530
5593
  context.toolOutput = params.content;
5531
5594
  context.structuredContent = params.structuredContent;
@@ -5536,18 +5599,26 @@ var ExtAppsAdapter = {
5536
5599
  Object.assign(context.hostContext, params);
5537
5600
  context.notifyContextChange(params);
5538
5601
  break;
5602
+ case 'ui/notifications/cancelled':
5603
+ window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
5604
+ break;
5539
5605
  }
5540
5606
  },
5541
5607
  isOriginTrusted: function(origin) {
5542
5608
  if (this.trustedOrigins.length > 0) {
5543
5609
  return this.trustedOrigins.indexOf(origin) !== -1;
5544
5610
  }
5545
- // When no trusted origins configured, trust first message origin (trust-on-first-use).
5546
- // SECURITY WARNING: This creates a race condition - whichever iframe sends the first
5547
- // message establishes permanent trust. For production, always configure trustedOrigins.
5611
+ // Trust-on-first-use: trust first message origin.
5612
+ // SECURITY WARNING: For production, always configure trustedOrigins.
5548
5613
  if (!this.trustedOrigin) {
5614
+ // Guard against race condition where multiple messages arrive simultaneously
5615
+ if (this.originTrustPending) {
5616
+ return false;
5617
+ }
5549
5618
  if (window.parent !== window && origin) {
5619
+ this.originTrustPending = true;
5550
5620
  this.trustedOrigin = origin;
5621
+ this.originTrustPending = false; // Reset after successful trust establishment
5551
5622
  return true;
5552
5623
  }
5553
5624
  return false;
@@ -5617,6 +5688,34 @@ var ExtAppsAdapter = {
5617
5688
  },
5618
5689
  requestClose: function(context) {
5619
5690
  return this.sendRequest('ui/close', {});
5691
+ },
5692
+ // Extended ext-apps methods (full specification)
5693
+ updateModelContext: function(context, data, merge) {
5694
+ if (!this.hostCapabilities.modelContextUpdate) {
5695
+ return Promise.reject(new Error('Model context update not supported'));
5696
+ }
5697
+ return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
5698
+ },
5699
+ log: function(context, level, message, data) {
5700
+ if (!this.hostCapabilities.logging) {
5701
+ // Fallback to console logging if host doesn't support it
5702
+ var logFn = console[level] || console.log;
5703
+ logFn('[Widget] ' + message, data);
5704
+ return Promise.resolve();
5705
+ }
5706
+ return this.sendRequest('ui/log', { level: level, message: message, data: data });
5707
+ },
5708
+ registerTool: function(context, name, description, inputSchema) {
5709
+ if (!this.hostCapabilities.widgetTools) {
5710
+ return Promise.reject(new Error('Widget tool registration not supported'));
5711
+ }
5712
+ return this.sendRequest('ui/registerTool', { name: name, description: description, inputSchema: inputSchema });
5713
+ },
5714
+ unregisterTool: function(context, name) {
5715
+ if (!this.hostCapabilities.widgetTools) {
5716
+ return Promise.reject(new Error('Widget tool unregistration not supported'));
5717
+ }
5718
+ return this.sendRequest('ui/unregisterTool', { name: name });
5620
5719
  }
5621
5720
  };
5622
5721
  `.trim();
@@ -5636,12 +5735,26 @@ var ClaudeAdapter = {
5636
5735
  }),
5637
5736
  canHandle: function() {
5638
5737
  if (typeof window === 'undefined') return false;
5738
+
5739
+ // If MCP Apps is enabled, let ext-apps adapter handle it
5740
+ if (window.__mcpAppsEnabled) return false;
5741
+ if (window.__mcpPlatform === 'ext-apps') return false;
5742
+ if (window.__extAppsInitialized) return false;
5743
+
5744
+ // Legacy Claude detection
5639
5745
  if (window.__mcpPlatform === 'claude') return true;
5640
5746
  if (window.claude) return true;
5641
5747
  if (window.__claudeArtifact) return true;
5642
5748
  if (typeof location !== 'undefined') {
5643
- var href = location.href;
5644
- if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
5749
+ try {
5750
+ var url = new URL(location.href);
5751
+ var hostname = url.hostname.toLowerCase();
5752
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
5753
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
5754
+ if (isClaudeHost || isAnthropicHost) return true;
5755
+ } catch (e) {
5756
+ // If URL parsing fails, fall through
5757
+ }
5645
5758
  }
5646
5759
  return false;
5647
5760
  },
@@ -5986,6 +6099,42 @@ FrontMcpBridge.prototype.requestClose = function() {
5986
6099
  return this._adapter.requestClose(this._context);
5987
6100
  };
5988
6101
 
6102
+ // Extended ext-apps methods (full specification)
6103
+ FrontMcpBridge.prototype.updateModelContext = function(context, merge) {
6104
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
6105
+ if (!this._adapter.updateModelContext) {
6106
+ return Promise.reject(new Error('updateModelContext not supported on this platform'));
6107
+ }
6108
+ return this._adapter.updateModelContext(this._context, context, merge);
6109
+ };
6110
+
6111
+ FrontMcpBridge.prototype.log = function(level, message, data) {
6112
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
6113
+ if (!this._adapter.log) {
6114
+ // Fallback to console
6115
+ var logFn = console[level] || console.log;
6116
+ logFn('[Widget] ' + message, data);
6117
+ return Promise.resolve();
6118
+ }
6119
+ return this._adapter.log(this._context, level, message, data);
6120
+ };
6121
+
6122
+ FrontMcpBridge.prototype.registerTool = function(name, description, inputSchema) {
6123
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
6124
+ if (!this._adapter.registerTool) {
6125
+ return Promise.reject(new Error('registerTool not supported on this platform'));
6126
+ }
6127
+ return this._adapter.registerTool(this._context, name, description, inputSchema);
6128
+ };
6129
+
6130
+ FrontMcpBridge.prototype.unregisterTool = function(name) {
6131
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
6132
+ if (!this._adapter.unregisterTool) {
6133
+ return Promise.reject(new Error('unregisterTool not supported on this platform'));
6134
+ }
6135
+ return this._adapter.unregisterTool(this._context, name);
6136
+ };
6137
+
5989
6138
  FrontMcpBridge.prototype.setWidgetState = function(state) {
5990
6139
  Object.assign(this._context.widgetState, state);
5991
6140
  this._saveWidgetState();
package/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",
package/registry/index.js CHANGED
@@ -2371,16 +2371,43 @@ var ExtAppsAdapter = {
2371
2371
  capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
2372
2372
  trustedOrigins: ${originsArray},
2373
2373
  trustedOrigin: null,
2374
+ originTrustPending: false,
2374
2375
  pendingRequests: {},
2375
2376
  requestId: 0,
2376
2377
  hostCapabilities: {},
2377
2378
  canHandle: function() {
2378
2379
  if (typeof window === 'undefined') return false;
2379
2380
  if (window.parent === window) return false;
2380
- // Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
2381
+
2382
+ // Check for OpenAI SDK - defer to OpenAIAdapter
2383
+ if (window.openai && window.openai.canvas) return false;
2381
2384
  if (window.openai && typeof window.openai.callTool === 'function') return false;
2385
+
2386
+ // Explicit ext-apps marker
2382
2387
  if (window.__mcpPlatform === 'ext-apps') return true;
2383
- return true;
2388
+ if (window.__extAppsInitialized) return true;
2389
+
2390
+ // Claude MCP Apps mode (2026+) - uses ext-apps protocol
2391
+ if (window.__mcpAppsEnabled) return true;
2392
+
2393
+ // Legacy Claude detection - defer to ClaudeAdapter
2394
+ if (window.claude) return false;
2395
+ if (window.__claudeArtifact) return false;
2396
+ if (window.__mcpPlatform === 'claude') return false;
2397
+ if (typeof location !== 'undefined') {
2398
+ try {
2399
+ var url = new URL(location.href);
2400
+ var hostname = url.hostname.toLowerCase();
2401
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
2402
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
2403
+ if (isClaudeHost || isAnthropicHost) return false;
2404
+ } catch (e) {
2405
+ // If URL parsing fails, fall through to other checks
2406
+ }
2407
+ }
2408
+
2409
+ // Do NOT default to true for any iframe
2410
+ return false;
2384
2411
  },
2385
2412
  initialize: function(context) {
2386
2413
  var self = this;
@@ -2422,6 +2449,11 @@ var ExtAppsAdapter = {
2422
2449
  context.toolInput = params.arguments || {};
2423
2450
  window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
2424
2451
  break;
2452
+ case 'ui/notifications/tool-input-partial':
2453
+ // Streaming: merge partial input with existing
2454
+ context.toolInput = Object.assign({}, context.toolInput, params.arguments || {});
2455
+ window.dispatchEvent(new CustomEvent('tool:input-partial', { detail: { arguments: context.toolInput } }));
2456
+ break;
2425
2457
  case 'ui/notifications/tool-result':
2426
2458
  context.toolOutput = params.content;
2427
2459
  context.structuredContent = params.structuredContent;
@@ -2432,18 +2464,26 @@ var ExtAppsAdapter = {
2432
2464
  Object.assign(context.hostContext, params);
2433
2465
  context.notifyContextChange(params);
2434
2466
  break;
2467
+ case 'ui/notifications/cancelled':
2468
+ window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
2469
+ break;
2435
2470
  }
2436
2471
  },
2437
2472
  isOriginTrusted: function(origin) {
2438
2473
  if (this.trustedOrigins.length > 0) {
2439
2474
  return this.trustedOrigins.indexOf(origin) !== -1;
2440
2475
  }
2441
- // When no trusted origins configured, trust first message origin (trust-on-first-use).
2442
- // SECURITY WARNING: This creates a race condition - whichever iframe sends the first
2443
- // message establishes permanent trust. For production, always configure trustedOrigins.
2476
+ // Trust-on-first-use: trust first message origin.
2477
+ // SECURITY WARNING: For production, always configure trustedOrigins.
2444
2478
  if (!this.trustedOrigin) {
2479
+ // Guard against race condition where multiple messages arrive simultaneously
2480
+ if (this.originTrustPending) {
2481
+ return false;
2482
+ }
2445
2483
  if (window.parent !== window && origin) {
2484
+ this.originTrustPending = true;
2446
2485
  this.trustedOrigin = origin;
2486
+ this.originTrustPending = false; // Reset after successful trust establishment
2447
2487
  return true;
2448
2488
  }
2449
2489
  return false;
@@ -2513,6 +2553,34 @@ var ExtAppsAdapter = {
2513
2553
  },
2514
2554
  requestClose: function(context) {
2515
2555
  return this.sendRequest('ui/close', {});
2556
+ },
2557
+ // Extended ext-apps methods (full specification)
2558
+ updateModelContext: function(context, data, merge) {
2559
+ if (!this.hostCapabilities.modelContextUpdate) {
2560
+ return Promise.reject(new Error('Model context update not supported'));
2561
+ }
2562
+ return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
2563
+ },
2564
+ log: function(context, level, message, data) {
2565
+ if (!this.hostCapabilities.logging) {
2566
+ // Fallback to console logging if host doesn't support it
2567
+ var logFn = console[level] || console.log;
2568
+ logFn('[Widget] ' + message, data);
2569
+ return Promise.resolve();
2570
+ }
2571
+ return this.sendRequest('ui/log', { level: level, message: message, data: data });
2572
+ },
2573
+ registerTool: function(context, name, description, inputSchema) {
2574
+ if (!this.hostCapabilities.widgetTools) {
2575
+ return Promise.reject(new Error('Widget tool registration not supported'));
2576
+ }
2577
+ return this.sendRequest('ui/registerTool', { name: name, description: description, inputSchema: inputSchema });
2578
+ },
2579
+ unregisterTool: function(context, name) {
2580
+ if (!this.hostCapabilities.widgetTools) {
2581
+ return Promise.reject(new Error('Widget tool unregistration not supported'));
2582
+ }
2583
+ return this.sendRequest('ui/unregisterTool', { name: name });
2516
2584
  }
2517
2585
  };
2518
2586
  `.trim();
@@ -2532,12 +2600,26 @@ var ClaudeAdapter = {
2532
2600
  }),
2533
2601
  canHandle: function() {
2534
2602
  if (typeof window === 'undefined') return false;
2603
+
2604
+ // If MCP Apps is enabled, let ext-apps adapter handle it
2605
+ if (window.__mcpAppsEnabled) return false;
2606
+ if (window.__mcpPlatform === 'ext-apps') return false;
2607
+ if (window.__extAppsInitialized) return false;
2608
+
2609
+ // Legacy Claude detection
2535
2610
  if (window.__mcpPlatform === 'claude') return true;
2536
2611
  if (window.claude) return true;
2537
2612
  if (window.__claudeArtifact) return true;
2538
2613
  if (typeof location !== 'undefined') {
2539
- var href = location.href;
2540
- if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
2614
+ try {
2615
+ var url = new URL(location.href);
2616
+ var hostname = url.hostname.toLowerCase();
2617
+ var isClaudeHost = hostname === 'claude.ai' || (hostname.length > 10 && hostname.slice(-10) === '.claude.ai');
2618
+ var isAnthropicHost = hostname === 'anthropic.com' || (hostname.length > 14 && hostname.slice(-14) === '.anthropic.com');
2619
+ if (isClaudeHost || isAnthropicHost) return true;
2620
+ } catch (e) {
2621
+ // If URL parsing fails, fall through
2622
+ }
2541
2623
  }
2542
2624
  return false;
2543
2625
  },
@@ -2882,6 +2964,42 @@ FrontMcpBridge.prototype.requestClose = function() {
2882
2964
  return this._adapter.requestClose(this._context);
2883
2965
  };
2884
2966
 
2967
+ // Extended ext-apps methods (full specification)
2968
+ FrontMcpBridge.prototype.updateModelContext = function(context, merge) {
2969
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2970
+ if (!this._adapter.updateModelContext) {
2971
+ return Promise.reject(new Error('updateModelContext not supported on this platform'));
2972
+ }
2973
+ return this._adapter.updateModelContext(this._context, context, merge);
2974
+ };
2975
+
2976
+ FrontMcpBridge.prototype.log = function(level, message, data) {
2977
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2978
+ if (!this._adapter.log) {
2979
+ // Fallback to console
2980
+ var logFn = console[level] || console.log;
2981
+ logFn('[Widget] ' + message, data);
2982
+ return Promise.resolve();
2983
+ }
2984
+ return this._adapter.log(this._context, level, message, data);
2985
+ };
2986
+
2987
+ FrontMcpBridge.prototype.registerTool = function(name, description, inputSchema) {
2988
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2989
+ if (!this._adapter.registerTool) {
2990
+ return Promise.reject(new Error('registerTool not supported on this platform'));
2991
+ }
2992
+ return this._adapter.registerTool(this._context, name, description, inputSchema);
2993
+ };
2994
+
2995
+ FrontMcpBridge.prototype.unregisterTool = function(name) {
2996
+ if (!this._adapter) return Promise.reject(new Error('Not initialized'));
2997
+ if (!this._adapter.unregisterTool) {
2998
+ return Promise.reject(new Error('unregisterTool not supported on this platform'));
2999
+ }
3000
+ return this._adapter.unregisterTool(this._context, name);
3001
+ };
3002
+
2885
3003
  FrontMcpBridge.prototype.setWidgetState = function(state) {
2886
3004
  Object.assign(this._context.widgetState, state);
2887
3005
  this._saveWidgetState();
@@ -4600,6 +4718,17 @@ async function renderToolTemplateAsync(options) {
4600
4718
  }
4601
4719
  }
4602
4720
 
4721
+ // libs/uipack/src/adapters/platform-meta.constants.ts
4722
+ var DISPLAY_MODE_MAP = {
4723
+ // Standard MCP Apps modes
4724
+ inline: "inline",
4725
+ fullscreen: "fullscreen",
4726
+ pip: "pip",
4727
+ // OpenAI-style aliases
4728
+ widget: "inline",
4729
+ panel: "fullscreen"
4730
+ };
4731
+
4603
4732
  // libs/uipack/src/adapters/platform-meta.ts
4604
4733
  function buildUIMeta(options) {
4605
4734
  const { uiConfig, platformType, html, token, directUrl, rendererType, contentHash, manifestUri } = options;
@@ -4671,6 +4800,21 @@ function buildClaudeMeta(meta, uiConfig) {
4671
4800
  if (uiConfig.prefersBorder !== void 0) {
4672
4801
  meta["claude/prefersBorder"] = uiConfig.prefersBorder;
4673
4802
  }
4803
+ if (uiConfig.resourceUri) {
4804
+ meta["ui/resourceUri"] = uiConfig.resourceUri;
4805
+ }
4806
+ if (uiConfig.csp) {
4807
+ const csp = {};
4808
+ if (uiConfig.csp.connectDomains?.length) {
4809
+ csp.connectDomains = uiConfig.csp.connectDomains;
4810
+ }
4811
+ if (uiConfig.csp.resourceDomains?.length) {
4812
+ csp.resourceDomains = uiConfig.csp.resourceDomains;
4813
+ }
4814
+ if (Object.keys(csp).length > 0) {
4815
+ meta["ui/csp"] = csp;
4816
+ }
4817
+ }
4674
4818
  return meta;
4675
4819
  }
4676
4820
  function buildGeminiMeta(meta, uiConfig) {
@@ -4699,14 +4843,7 @@ function buildGenericMeta(meta, uiConfig) {
4699
4843
  }
4700
4844
  }
4701
4845
  if (uiConfig.displayMode) {
4702
- const displayModeMap = {
4703
- inline: "inline",
4704
- fullscreen: "fullscreen",
4705
- pip: "pip",
4706
- widget: "inline",
4707
- panel: "fullscreen"
4708
- };
4709
- const mappedMode = displayModeMap[uiConfig.displayMode];
4846
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4710
4847
  if (mappedMode) {
4711
4848
  meta["ui/displayMode"] = mappedMode;
4712
4849
  }
@@ -4734,15 +4871,7 @@ function buildExtAppsMeta(meta, uiConfig) {
4734
4871
  }
4735
4872
  }
4736
4873
  if (uiConfig.displayMode) {
4737
- const displayModeMap = {
4738
- inline: "inline",
4739
- fullscreen: "fullscreen",
4740
- pip: "pip",
4741
- // Map OpenAI-style values
4742
- widget: "inline",
4743
- panel: "fullscreen"
4744
- };
4745
- const mappedMode = displayModeMap[uiConfig.displayMode];
4874
+ const mappedMode = DISPLAY_MODE_MAP[uiConfig.displayMode];
4746
4875
  if (mappedMode) {
4747
4876
  meta["ui/displayMode"] = mappedMode;
4748
4877
  }