@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/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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
5280
|
-
// SECURITY WARNING:
|
|
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
|
-
|
|
5378
|
-
|
|
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.
|
|
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/esm/registry/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
2406
|
-
// SECURITY WARNING:
|
|
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
|
-
|
|
2504
|
-
|
|
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
|
|
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
|
|
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
|
}
|