@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/adapters/index.d.ts +1 -1
- package/adapters/index.d.ts.map +1 -1
- package/adapters/index.js +71 -24
- package/adapters/platform-meta.constants.d.ts +26 -0
- package/adapters/platform-meta.constants.d.ts.map +1 -0
- package/adapters/platform-meta.d.ts +39 -0
- package/adapters/platform-meta.d.ts.map +1 -1
- package/bridge-runtime/iife-generator.d.ts.map +1 -1
- package/bridge-runtime/index.js +125 -7
- package/build/index.js +125 -7
- package/esm/adapters/index.mjs +69 -24
- package/esm/bridge-runtime/index.mjs +125 -7
- package/esm/build/index.mjs +125 -7
- package/esm/index.mjs +180 -31
- package/esm/package.json +5 -2
- package/esm/registry/index.mjs +153 -24
- package/esm/runtime/index.mjs +125 -7
- package/esm/tool-template/index.mjs +125 -7
- package/index.js +180 -31
- package/package.json +5 -2
- package/registry/index.js +153 -24
- package/runtime/index.js +125 -7
- package/tool-template/index.js +125 -7
- package/types/ui-config.d.ts +23 -0
- package/types/ui-config.d.ts.map +1 -1
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
5546
|
-
// SECURITY WARNING:
|
|
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
|
-
|
|
5644
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
//
|
|
2442
|
-
// SECURITY WARNING:
|
|
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
|
-
|
|
2540
|
-
|
|
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
|
|
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
|
|
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
|
}
|