@frontmcp/uipack 1.9.2 → 1.9.3
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.js +63 -17
- package/adapters/template-renderer.d.ts +4 -2
- package/adapters/template-renderer.d.ts.map +1 -1
- package/bridge-runtime/iife-generator.d.ts +7 -0
- package/bridge-runtime/iife-generator.d.ts.map +1 -1
- package/bridge-runtime/index.d.ts +1 -1
- package/bridge-runtime/index.d.ts.map +1 -1
- package/bridge-runtime/index.js +52 -11
- package/component/index.js +61 -16
- package/esm/adapters/index.mjs +63 -17
- package/esm/bridge-runtime/index.mjs +50 -11
- package/esm/component/index.mjs +61 -16
- package/esm/index.mjs +63 -17
- package/esm/package.json +2 -2
- package/esm/shell/index.mjs +61 -16
- package/index.js +63 -17
- package/package.json +2 -2
- package/shell/builder.d.ts.map +1 -1
- package/shell/data-injector.d.ts +3 -1
- package/shell/data-injector.d.ts.map +1 -1
- package/shell/index.js +61 -16
- package/shell/types.d.ts +2 -0
- package/shell/types.d.ts.map +1 -1
- package/types/ui-config.d.ts +18 -15
- package/types/ui-config.d.ts.map +1 -1
- package/types/ui-runtime.d.ts +1 -1
package/esm/shell/index.mjs
CHANGED
|
@@ -2,6 +2,8 @@ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuilt
|
|
|
2
2
|
const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
|
|
3
3
|
// __frontmcpCreateRequire
|
|
4
4
|
// libs/uipack/src/bridge-runtime/iife-generator.ts
|
|
5
|
+
var WIDGET_CALL_META_KEY = "frontmcp/widgetCall";
|
|
6
|
+
var MCP_APPS_DISPLAY_MODES = ["inline", "fullscreen", "pip"];
|
|
5
7
|
function generateBridgeIIFE(options = {}) {
|
|
6
8
|
const { debug = false, trustedOrigins = [], minify = false } = options;
|
|
7
9
|
const adapters = options.adapters || ["openai", "ext-apps", "claude", "gemini", "generic"];
|
|
@@ -49,6 +51,7 @@ function generateBridgeIIFE(options = {}) {
|
|
|
49
51
|
parts.push("bridge.initialize().then(function() {");
|
|
50
52
|
parts.push(' log("Bridge initialized with adapter: " + bridge.adapterId);');
|
|
51
53
|
parts.push(' window.dispatchEvent(new CustomEvent("bridge:ready", { detail: { adapter: bridge.adapterId } }));');
|
|
54
|
+
parts.push(" if (window.__mcpDisplayMode) bridge.requestDisplayMode(window.__mcpDisplayMode).catch(function() {});");
|
|
52
55
|
parts.push("}).catch(function(err) {");
|
|
53
56
|
parts.push(' console.error("[FrontMcpBridge] Init failed:", err);');
|
|
54
57
|
parts.push(' window.dispatchEvent(new CustomEvent("bridge:error", { detail: { error: err } }));');
|
|
@@ -382,6 +385,9 @@ var OpenAIAdapter = {
|
|
|
382
385
|
return Promise.resolve();
|
|
383
386
|
},
|
|
384
387
|
requestDisplayMode: function(context, mode) {
|
|
388
|
+
if (window.openai && typeof window.openai.requestDisplayMode === 'function') {
|
|
389
|
+
return Promise.resolve(window.openai.requestDisplayMode({ mode: mode }));
|
|
390
|
+
}
|
|
385
391
|
return Promise.resolve();
|
|
386
392
|
},
|
|
387
393
|
setSize: function(context, size) {
|
|
@@ -419,6 +425,7 @@ var ExtAppsAdapter = {
|
|
|
419
425
|
hostCapabilities: {},
|
|
420
426
|
handshakeSettled: false,
|
|
421
427
|
pendingSize: null,
|
|
428
|
+
modelContext: null,
|
|
422
429
|
canHandle: function() {
|
|
423
430
|
if (typeof window === 'undefined') return false;
|
|
424
431
|
if (window.parent === window) return false;
|
|
@@ -497,8 +504,19 @@ var ExtAppsAdapter = {
|
|
|
497
504
|
|
|
498
505
|
if ('method' in data && !('id' in data)) {
|
|
499
506
|
this.handleNotification(context, data);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if ('method' in data && 'id' in data) {
|
|
511
|
+
this.handleRequest(context, data, event.origin);
|
|
500
512
|
}
|
|
501
513
|
},
|
|
514
|
+
handleRequest: function(context, request, origin) {
|
|
515
|
+
// ui/resource-teardown: the host is about to unmount the view; reply once listeners have cleaned up.
|
|
516
|
+
if (request.method !== 'ui/resource-teardown') return;
|
|
517
|
+
window.dispatchEvent(new CustomEvent('bridge:teardown', { detail: {} }));
|
|
518
|
+
window.parent.postMessage({ jsonrpc: '2.0', id: request.id, result: {} }, origin);
|
|
519
|
+
},
|
|
502
520
|
handleNotification: function(context, notification) {
|
|
503
521
|
var params = notification.params || {};
|
|
504
522
|
switch (notification.method) {
|
|
@@ -522,6 +540,7 @@ var ExtAppsAdapter = {
|
|
|
522
540
|
Object.assign(context.hostContext, params);
|
|
523
541
|
context.notifyContextChange(params);
|
|
524
542
|
break;
|
|
543
|
+
case 'ui/notifications/tool-cancelled':
|
|
525
544
|
case 'ui/notifications/cancelled':
|
|
526
545
|
window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
|
|
527
546
|
break;
|
|
@@ -586,7 +605,7 @@ var ExtAppsAdapter = {
|
|
|
586
605
|
var id = ++this.requestId;
|
|
587
606
|
var params = {
|
|
588
607
|
appInfo: { name: 'FrontMCP Widget', version: '1.0.0' },
|
|
589
|
-
appCapabilities: { tools: { listChanged: false } },
|
|
608
|
+
appCapabilities: { tools: { listChanged: false }, availableDisplayModes: ${JSON.stringify(MCP_APPS_DISPLAY_MODES)} },
|
|
590
609
|
protocolVersion: '2024-11-05'
|
|
591
610
|
};
|
|
592
611
|
|
|
@@ -654,20 +673,25 @@ var ExtAppsAdapter = {
|
|
|
654
673
|
return Promise.reject(new Error('Server tool proxy not supported'));
|
|
655
674
|
}
|
|
656
675
|
// Per ext-apps spec: use standard MCP method 'tools/call' (not 'ui/callServerTool')
|
|
657
|
-
return this.sendRequest('tools/call', { name: name, arguments: args || {} });
|
|
676
|
+
return this.sendRequest('tools/call', { name: name, arguments: args || {}, _meta: { '${WIDGET_CALL_META_KEY}': true } });
|
|
658
677
|
},
|
|
659
678
|
sendMessage: function(context, content) {
|
|
660
679
|
return this.sendRequest('ui/message', { content: content });
|
|
661
680
|
},
|
|
662
681
|
openLink: function(context, url) {
|
|
663
|
-
if (!this.hostCapabilities.openLink) {
|
|
682
|
+
if (!this.hostCapabilities.openLinks && !this.hostCapabilities.openLink) {
|
|
664
683
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
665
684
|
return Promise.resolve();
|
|
666
685
|
}
|
|
667
|
-
return this.sendRequest('ui/
|
|
686
|
+
return this.sendRequest('ui/open-link', { url: url });
|
|
668
687
|
},
|
|
669
688
|
requestDisplayMode: function(context, mode) {
|
|
670
|
-
|
|
689
|
+
// Ask only for a mode the host offers; it answers with the mode it actually set.
|
|
690
|
+
var offered = context.hostContext.availableDisplayModes;
|
|
691
|
+
if (!Array.isArray(offered) || offered.indexOf(mode) === -1) {
|
|
692
|
+
return Promise.reject(new Error('Display mode "' + mode + '" is not available on this host'));
|
|
693
|
+
}
|
|
694
|
+
return this.sendRequest('ui/request-display-mode', { mode: mode });
|
|
671
695
|
},
|
|
672
696
|
setSize: function(context, size) {
|
|
673
697
|
// Standard ext-apps sizing: the view tells the host its dimensions with the
|
|
@@ -688,14 +712,22 @@ var ExtAppsAdapter = {
|
|
|
688
712
|
return this.sendNotification('ui/notifications/size-changed', params);
|
|
689
713
|
},
|
|
690
714
|
requestClose: function(context) {
|
|
691
|
-
|
|
715
|
+
// The host decides whether to tear the view down; it answers with ui/resource-teardown if it does.
|
|
716
|
+
return this.sendNotification('ui/notifications/request-teardown', {});
|
|
692
717
|
},
|
|
693
718
|
// Extended ext-apps methods (full specification)
|
|
694
719
|
updateModelContext: function(context, data, merge) {
|
|
695
|
-
if (!this.hostCapabilities.modelContextUpdate) {
|
|
720
|
+
if (!this.hostCapabilities.updateModelContext && !this.hostCapabilities.modelContextUpdate) {
|
|
696
721
|
return Promise.reject(new Error('Model context update not supported'));
|
|
697
722
|
}
|
|
698
|
-
|
|
723
|
+
// The host keeps only the latest update, so an object merges into the last one here.
|
|
724
|
+
var isObject = data !== null && typeof data === 'object' && !Array.isArray(data);
|
|
725
|
+
var modelContext = isObject && merge !== false ? Object.assign({}, this.modelContext, data) : data;
|
|
726
|
+
this.modelContext = isObject ? modelContext : null;
|
|
727
|
+
var text = typeof modelContext === 'string' ? modelContext : JSON.stringify(modelContext);
|
|
728
|
+
var params = { content: [{ type: 'text', text: text }] };
|
|
729
|
+
if (isObject) params.structuredContent = modelContext;
|
|
730
|
+
return this.sendRequest('ui/update-model-context', params);
|
|
699
731
|
},
|
|
700
732
|
log: function(context, level, message, data) {
|
|
701
733
|
if (!this.hostCapabilities.logging) {
|
|
@@ -704,7 +736,10 @@ var ExtAppsAdapter = {
|
|
|
704
736
|
logFn('[Widget] ' + message, data);
|
|
705
737
|
return Promise.resolve();
|
|
706
738
|
}
|
|
707
|
-
return this.
|
|
739
|
+
return this.sendNotification('notifications/message', {
|
|
740
|
+
level: level === 'warn' ? 'warning' : level,
|
|
741
|
+
data: data === undefined ? message : { message: message, data: data }
|
|
742
|
+
});
|
|
708
743
|
},
|
|
709
744
|
registerTool: function(context, name, description, inputSchema) {
|
|
710
745
|
if (!this.hostCapabilities.widgetTools) {
|
|
@@ -1101,8 +1136,10 @@ FrontMcpBridge.prototype.openLink = function(url) {
|
|
|
1101
1136
|
FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
1102
1137
|
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1103
1138
|
var self = this;
|
|
1104
|
-
return this._adapter.requestDisplayMode(this._context, mode).then(function() {
|
|
1105
|
-
|
|
1139
|
+
return this._adapter.requestDisplayMode(this._context, mode).then(function(result) {
|
|
1140
|
+
// The host may set another mode than the one asked for, e.g. when it refuses the change.
|
|
1141
|
+
var setMode = result && result.mode;
|
|
1142
|
+
if (${JSON.stringify(MCP_APPS_DISPLAY_MODES)}.indexOf(setMode) !== -1) self._context.hostContext.displayMode = setMode;
|
|
1106
1143
|
});
|
|
1107
1144
|
};
|
|
1108
1145
|
|
|
@@ -1527,7 +1564,7 @@ function hasSizing(sizing) {
|
|
|
1527
1564
|
return sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0 || sizing.autoResize !== void 0;
|
|
1528
1565
|
}
|
|
1529
1566
|
function buildDataInjectionScript(options) {
|
|
1530
|
-
const { toolName, input, output, structuredContent, sizing } = options;
|
|
1567
|
+
const { toolName, input, output, structuredContent, sizing, displayMode } = options;
|
|
1531
1568
|
const lines = [
|
|
1532
1569
|
`window.__mcpAppsEnabled = true;`,
|
|
1533
1570
|
`window.__mcpToolName = ${safeJsonForScript(toolName)};`,
|
|
@@ -1538,6 +1575,9 @@ function buildDataInjectionScript(options) {
|
|
|
1538
1575
|
if (hasSizing(sizing)) {
|
|
1539
1576
|
lines.push(`window.__mcpWidgetSizing = ${safeJsonForScript(sizing)};`);
|
|
1540
1577
|
}
|
|
1578
|
+
if (displayMode && displayMode !== "inline") {
|
|
1579
|
+
lines.push(`window.__mcpDisplayMode = ${safeJsonForScript(displayMode)};`);
|
|
1580
|
+
}
|
|
1541
1581
|
return `<script>
|
|
1542
1582
|
${lines.join("\n")}
|
|
1543
1583
|
</script>`;
|
|
@@ -1629,7 +1669,8 @@ function resolveDataInjectionScript(args) {
|
|
|
1629
1669
|
input: args.input,
|
|
1630
1670
|
output: args.output,
|
|
1631
1671
|
structuredContent: args.structuredContent,
|
|
1632
|
-
sizing: args.sizing
|
|
1672
|
+
sizing: args.sizing,
|
|
1673
|
+
displayMode: args.displayMode
|
|
1633
1674
|
});
|
|
1634
1675
|
}
|
|
1635
1676
|
function buildShell(content, config) {
|
|
@@ -1642,7 +1683,8 @@ function buildShell(content, config) {
|
|
|
1642
1683
|
structuredContent,
|
|
1643
1684
|
includeBridge = true,
|
|
1644
1685
|
title,
|
|
1645
|
-
sizing
|
|
1686
|
+
sizing,
|
|
1687
|
+
displayMode
|
|
1646
1688
|
} = config;
|
|
1647
1689
|
const { customShell, dataInjection } = config;
|
|
1648
1690
|
const dataScript = resolveDataInjectionScript({
|
|
@@ -1651,7 +1693,8 @@ function buildShell(content, config) {
|
|
|
1651
1693
|
input,
|
|
1652
1694
|
output,
|
|
1653
1695
|
structuredContent,
|
|
1654
|
-
sizing
|
|
1696
|
+
sizing,
|
|
1697
|
+
displayMode
|
|
1655
1698
|
});
|
|
1656
1699
|
if (!withShell) {
|
|
1657
1700
|
const html3 = `${dataScript}
|
|
@@ -1672,6 +1715,7 @@ ${content}`;
|
|
|
1672
1715
|
includeBridge,
|
|
1673
1716
|
title,
|
|
1674
1717
|
sizing,
|
|
1718
|
+
displayMode,
|
|
1675
1719
|
dataInjection
|
|
1676
1720
|
});
|
|
1677
1721
|
}
|
|
@@ -1727,7 +1771,8 @@ function buildCustomShell(content, customShell, ctx) {
|
|
|
1727
1771
|
input: ctx.input,
|
|
1728
1772
|
output: ctx.output,
|
|
1729
1773
|
structuredContent: ctx.structuredContent,
|
|
1730
|
-
sizing: ctx.sizing
|
|
1774
|
+
sizing: ctx.sizing,
|
|
1775
|
+
displayMode: ctx.displayMode
|
|
1731
1776
|
});
|
|
1732
1777
|
const sizingStyle = buildSizingStyleTag(ctx.sizing);
|
|
1733
1778
|
const dataWithSizing = sizingStyle ? `${dataScript}
|
package/index.js
CHANGED
|
@@ -253,6 +253,8 @@ function safeJsonForScript(value) {
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
// libs/uipack/src/bridge-runtime/iife-generator.ts
|
|
256
|
+
var WIDGET_CALL_META_KEY = "frontmcp/widgetCall";
|
|
257
|
+
var MCP_APPS_DISPLAY_MODES = ["inline", "fullscreen", "pip"];
|
|
256
258
|
function generateBridgeIIFE(options = {}) {
|
|
257
259
|
const { debug = false, trustedOrigins = [], minify = false } = options;
|
|
258
260
|
const adapters = options.adapters || ["openai", "ext-apps", "claude", "gemini", "generic"];
|
|
@@ -300,6 +302,7 @@ function generateBridgeIIFE(options = {}) {
|
|
|
300
302
|
parts.push("bridge.initialize().then(function() {");
|
|
301
303
|
parts.push(' log("Bridge initialized with adapter: " + bridge.adapterId);');
|
|
302
304
|
parts.push(' window.dispatchEvent(new CustomEvent("bridge:ready", { detail: { adapter: bridge.adapterId } }));');
|
|
305
|
+
parts.push(" if (window.__mcpDisplayMode) bridge.requestDisplayMode(window.__mcpDisplayMode).catch(function() {});");
|
|
303
306
|
parts.push("}).catch(function(err) {");
|
|
304
307
|
parts.push(' console.error("[FrontMcpBridge] Init failed:", err);');
|
|
305
308
|
parts.push(' window.dispatchEvent(new CustomEvent("bridge:error", { detail: { error: err } }));');
|
|
@@ -633,6 +636,9 @@ var OpenAIAdapter = {
|
|
|
633
636
|
return Promise.resolve();
|
|
634
637
|
},
|
|
635
638
|
requestDisplayMode: function(context, mode) {
|
|
639
|
+
if (window.openai && typeof window.openai.requestDisplayMode === 'function') {
|
|
640
|
+
return Promise.resolve(window.openai.requestDisplayMode({ mode: mode }));
|
|
641
|
+
}
|
|
636
642
|
return Promise.resolve();
|
|
637
643
|
},
|
|
638
644
|
setSize: function(context, size) {
|
|
@@ -670,6 +676,7 @@ var ExtAppsAdapter = {
|
|
|
670
676
|
hostCapabilities: {},
|
|
671
677
|
handshakeSettled: false,
|
|
672
678
|
pendingSize: null,
|
|
679
|
+
modelContext: null,
|
|
673
680
|
canHandle: function() {
|
|
674
681
|
if (typeof window === 'undefined') return false;
|
|
675
682
|
if (window.parent === window) return false;
|
|
@@ -748,7 +755,18 @@ var ExtAppsAdapter = {
|
|
|
748
755
|
|
|
749
756
|
if ('method' in data && !('id' in data)) {
|
|
750
757
|
this.handleNotification(context, data);
|
|
758
|
+
return;
|
|
751
759
|
}
|
|
760
|
+
|
|
761
|
+
if ('method' in data && 'id' in data) {
|
|
762
|
+
this.handleRequest(context, data, event.origin);
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
handleRequest: function(context, request, origin) {
|
|
766
|
+
// ui/resource-teardown: the host is about to unmount the view; reply once listeners have cleaned up.
|
|
767
|
+
if (request.method !== 'ui/resource-teardown') return;
|
|
768
|
+
window.dispatchEvent(new CustomEvent('bridge:teardown', { detail: {} }));
|
|
769
|
+
window.parent.postMessage({ jsonrpc: '2.0', id: request.id, result: {} }, origin);
|
|
752
770
|
},
|
|
753
771
|
handleNotification: function(context, notification) {
|
|
754
772
|
var params = notification.params || {};
|
|
@@ -773,6 +791,7 @@ var ExtAppsAdapter = {
|
|
|
773
791
|
Object.assign(context.hostContext, params);
|
|
774
792
|
context.notifyContextChange(params);
|
|
775
793
|
break;
|
|
794
|
+
case 'ui/notifications/tool-cancelled':
|
|
776
795
|
case 'ui/notifications/cancelled':
|
|
777
796
|
window.dispatchEvent(new CustomEvent('tool:cancelled', { detail: { reason: params.reason } }));
|
|
778
797
|
break;
|
|
@@ -837,7 +856,7 @@ var ExtAppsAdapter = {
|
|
|
837
856
|
var id = ++this.requestId;
|
|
838
857
|
var params = {
|
|
839
858
|
appInfo: { name: 'FrontMCP Widget', version: '1.0.0' },
|
|
840
|
-
appCapabilities: { tools: { listChanged: false } },
|
|
859
|
+
appCapabilities: { tools: { listChanged: false }, availableDisplayModes: ${JSON.stringify(MCP_APPS_DISPLAY_MODES)} },
|
|
841
860
|
protocolVersion: '2024-11-05'
|
|
842
861
|
};
|
|
843
862
|
|
|
@@ -905,20 +924,25 @@ var ExtAppsAdapter = {
|
|
|
905
924
|
return Promise.reject(new Error('Server tool proxy not supported'));
|
|
906
925
|
}
|
|
907
926
|
// Per ext-apps spec: use standard MCP method 'tools/call' (not 'ui/callServerTool')
|
|
908
|
-
return this.sendRequest('tools/call', { name: name, arguments: args || {} });
|
|
927
|
+
return this.sendRequest('tools/call', { name: name, arguments: args || {}, _meta: { '${WIDGET_CALL_META_KEY}': true } });
|
|
909
928
|
},
|
|
910
929
|
sendMessage: function(context, content) {
|
|
911
930
|
return this.sendRequest('ui/message', { content: content });
|
|
912
931
|
},
|
|
913
932
|
openLink: function(context, url) {
|
|
914
|
-
if (!this.hostCapabilities.openLink) {
|
|
933
|
+
if (!this.hostCapabilities.openLinks && !this.hostCapabilities.openLink) {
|
|
915
934
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
916
935
|
return Promise.resolve();
|
|
917
936
|
}
|
|
918
|
-
return this.sendRequest('ui/
|
|
937
|
+
return this.sendRequest('ui/open-link', { url: url });
|
|
919
938
|
},
|
|
920
939
|
requestDisplayMode: function(context, mode) {
|
|
921
|
-
|
|
940
|
+
// Ask only for a mode the host offers; it answers with the mode it actually set.
|
|
941
|
+
var offered = context.hostContext.availableDisplayModes;
|
|
942
|
+
if (!Array.isArray(offered) || offered.indexOf(mode) === -1) {
|
|
943
|
+
return Promise.reject(new Error('Display mode "' + mode + '" is not available on this host'));
|
|
944
|
+
}
|
|
945
|
+
return this.sendRequest('ui/request-display-mode', { mode: mode });
|
|
922
946
|
},
|
|
923
947
|
setSize: function(context, size) {
|
|
924
948
|
// Standard ext-apps sizing: the view tells the host its dimensions with the
|
|
@@ -939,14 +963,22 @@ var ExtAppsAdapter = {
|
|
|
939
963
|
return this.sendNotification('ui/notifications/size-changed', params);
|
|
940
964
|
},
|
|
941
965
|
requestClose: function(context) {
|
|
942
|
-
|
|
966
|
+
// The host decides whether to tear the view down; it answers with ui/resource-teardown if it does.
|
|
967
|
+
return this.sendNotification('ui/notifications/request-teardown', {});
|
|
943
968
|
},
|
|
944
969
|
// Extended ext-apps methods (full specification)
|
|
945
970
|
updateModelContext: function(context, data, merge) {
|
|
946
|
-
if (!this.hostCapabilities.modelContextUpdate) {
|
|
971
|
+
if (!this.hostCapabilities.updateModelContext && !this.hostCapabilities.modelContextUpdate) {
|
|
947
972
|
return Promise.reject(new Error('Model context update not supported'));
|
|
948
973
|
}
|
|
949
|
-
|
|
974
|
+
// The host keeps only the latest update, so an object merges into the last one here.
|
|
975
|
+
var isObject = data !== null && typeof data === 'object' && !Array.isArray(data);
|
|
976
|
+
var modelContext = isObject && merge !== false ? Object.assign({}, this.modelContext, data) : data;
|
|
977
|
+
this.modelContext = isObject ? modelContext : null;
|
|
978
|
+
var text = typeof modelContext === 'string' ? modelContext : JSON.stringify(modelContext);
|
|
979
|
+
var params = { content: [{ type: 'text', text: text }] };
|
|
980
|
+
if (isObject) params.structuredContent = modelContext;
|
|
981
|
+
return this.sendRequest('ui/update-model-context', params);
|
|
950
982
|
},
|
|
951
983
|
log: function(context, level, message, data) {
|
|
952
984
|
if (!this.hostCapabilities.logging) {
|
|
@@ -955,7 +987,10 @@ var ExtAppsAdapter = {
|
|
|
955
987
|
logFn('[Widget] ' + message, data);
|
|
956
988
|
return Promise.resolve();
|
|
957
989
|
}
|
|
958
|
-
return this.
|
|
990
|
+
return this.sendNotification('notifications/message', {
|
|
991
|
+
level: level === 'warn' ? 'warning' : level,
|
|
992
|
+
data: data === undefined ? message : { message: message, data: data }
|
|
993
|
+
});
|
|
959
994
|
},
|
|
960
995
|
registerTool: function(context, name, description, inputSchema) {
|
|
961
996
|
if (!this.hostCapabilities.widgetTools) {
|
|
@@ -1352,8 +1387,10 @@ FrontMcpBridge.prototype.openLink = function(url) {
|
|
|
1352
1387
|
FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
1353
1388
|
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1354
1389
|
var self = this;
|
|
1355
|
-
return this._adapter.requestDisplayMode(this._context, mode).then(function() {
|
|
1356
|
-
|
|
1390
|
+
return this._adapter.requestDisplayMode(this._context, mode).then(function(result) {
|
|
1391
|
+
// The host may set another mode than the one asked for, e.g. when it refuses the change.
|
|
1392
|
+
var setMode = result && result.mode;
|
|
1393
|
+
if (${JSON.stringify(MCP_APPS_DISPLAY_MODES)}.indexOf(setMode) !== -1) self._context.hostContext.displayMode = setMode;
|
|
1357
1394
|
});
|
|
1358
1395
|
};
|
|
1359
1396
|
|
|
@@ -3219,7 +3256,7 @@ function hasSizing(sizing) {
|
|
|
3219
3256
|
return sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0 || sizing.autoResize !== void 0;
|
|
3220
3257
|
}
|
|
3221
3258
|
function buildDataInjectionScript(options) {
|
|
3222
|
-
const { toolName, input, output, structuredContent, sizing } = options;
|
|
3259
|
+
const { toolName, input, output, structuredContent, sizing, displayMode } = options;
|
|
3223
3260
|
const lines = [
|
|
3224
3261
|
`window.__mcpAppsEnabled = true;`,
|
|
3225
3262
|
`window.__mcpToolName = ${safeJsonForScript(toolName)};`,
|
|
@@ -3230,6 +3267,9 @@ function buildDataInjectionScript(options) {
|
|
|
3230
3267
|
if (hasSizing(sizing)) {
|
|
3231
3268
|
lines.push(`window.__mcpWidgetSizing = ${safeJsonForScript(sizing)};`);
|
|
3232
3269
|
}
|
|
3270
|
+
if (displayMode && displayMode !== "inline") {
|
|
3271
|
+
lines.push(`window.__mcpDisplayMode = ${safeJsonForScript(displayMode)};`);
|
|
3272
|
+
}
|
|
3233
3273
|
return `<script>
|
|
3234
3274
|
${lines.join("\n")}
|
|
3235
3275
|
</script>`;
|
|
@@ -3321,7 +3361,8 @@ function resolveDataInjectionScript(args) {
|
|
|
3321
3361
|
input: args.input,
|
|
3322
3362
|
output: args.output,
|
|
3323
3363
|
structuredContent: args.structuredContent,
|
|
3324
|
-
sizing: args.sizing
|
|
3364
|
+
sizing: args.sizing,
|
|
3365
|
+
displayMode: args.displayMode
|
|
3325
3366
|
});
|
|
3326
3367
|
}
|
|
3327
3368
|
function buildShell(content, config) {
|
|
@@ -3334,7 +3375,8 @@ function buildShell(content, config) {
|
|
|
3334
3375
|
structuredContent,
|
|
3335
3376
|
includeBridge = true,
|
|
3336
3377
|
title,
|
|
3337
|
-
sizing
|
|
3378
|
+
sizing,
|
|
3379
|
+
displayMode
|
|
3338
3380
|
} = config;
|
|
3339
3381
|
const { customShell, dataInjection } = config;
|
|
3340
3382
|
const dataScript = resolveDataInjectionScript({
|
|
@@ -3343,7 +3385,8 @@ function buildShell(content, config) {
|
|
|
3343
3385
|
input,
|
|
3344
3386
|
output,
|
|
3345
3387
|
structuredContent,
|
|
3346
|
-
sizing
|
|
3388
|
+
sizing,
|
|
3389
|
+
displayMode
|
|
3347
3390
|
});
|
|
3348
3391
|
if (!withShell) {
|
|
3349
3392
|
const html3 = `${dataScript}
|
|
@@ -3364,6 +3407,7 @@ ${content}`;
|
|
|
3364
3407
|
includeBridge,
|
|
3365
3408
|
title,
|
|
3366
3409
|
sizing,
|
|
3410
|
+
displayMode,
|
|
3367
3411
|
dataInjection
|
|
3368
3412
|
});
|
|
3369
3413
|
}
|
|
@@ -3419,7 +3463,8 @@ function buildCustomShell(content, customShell, ctx) {
|
|
|
3419
3463
|
input: ctx.input,
|
|
3420
3464
|
output: ctx.output,
|
|
3421
3465
|
structuredContent: ctx.structuredContent,
|
|
3422
|
-
sizing: ctx.sizing
|
|
3466
|
+
sizing: ctx.sizing,
|
|
3467
|
+
displayMode: ctx.displayMode
|
|
3423
3468
|
});
|
|
3424
3469
|
const sizingStyle = buildSizingStyleTag(ctx.sizing);
|
|
3425
3470
|
const dataWithSizing = sizingStyle ? `${dataScript}
|
|
@@ -4437,7 +4482,7 @@ function noticeStringResult(toolName, logger) {
|
|
|
4437
4482
|
if (noticedStringResultTools.has(toolName)) return;
|
|
4438
4483
|
noticedStringResultTools.add(toolName);
|
|
4439
4484
|
logger.warn(
|
|
4440
|
-
`[frontmcp] The UI template of tool "${toolName}" returned a plain string containing markup.
|
|
4485
|
+
`[frontmcp] The UI template of tool "${toolName}" returned a plain string containing markup. Since 1.9.2, FrontMCP HTML-escapes plain string results by default, so it is shown as text. Build the markup with ctx.helpers.html\`\u2026\` (interpolated values are escaped) or wrap safe markup with ctx.helpers.trustedHtml(). Set ui.escapeStringResults: false to render plain strings as markup, or true to silence this notice. See ${STRING_RESULT_DOCS}`
|
|
4441
4486
|
);
|
|
4442
4487
|
}
|
|
4443
4488
|
function renderUnwrappedResult(rawResult, options) {
|
|
@@ -4496,6 +4541,7 @@ function renderToolTemplate(options) {
|
|
|
4496
4541
|
includeBridge: true,
|
|
4497
4542
|
resolver,
|
|
4498
4543
|
sizing,
|
|
4544
|
+
displayMode: options.displayMode,
|
|
4499
4545
|
csp: options.csp ? buildCspConfig(resolver, options.csp) : void 0
|
|
4500
4546
|
};
|
|
4501
4547
|
let html2;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/uipack",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"description": "FrontMCP UIpack - HTML shell builder, pluggable import resolver, and NPM component loader for MCP UI (React-free core)",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"homepage": "https://docs.agentfront.dev",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"typescript": "^5.9.3"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@frontmcp/lazy-zod": "1.9.
|
|
64
|
+
"@frontmcp/lazy-zod": "1.9.3",
|
|
65
65
|
"esbuild": ">=0.27.0 <1"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
package/shell/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/shell/builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,KAAK,EAAE,WAAW,EAAgC,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/shell/builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,KAAK,EAAE,WAAW,EAAgC,WAAW,EAAE,MAAM,SAAS,CAAC;AAgCtF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,WAAW,CA+F5E"}
|
package/shell/data-injector.d.ts
CHANGED
|
@@ -21,7 +21,8 @@ export declare function hasSizing(sizing?: WidgetSizing): sizing is WidgetSizing
|
|
|
21
21
|
* Sets `window.__mcpToolName`, `window.__mcpToolInput`,
|
|
22
22
|
* `window.__mcpToolOutput`, and `window.__mcpStructuredContent`. When `sizing`
|
|
23
23
|
* is provided it also sets `window.__mcpWidgetSizing` so the bridge runtime can
|
|
24
|
-
* apply CSS and drive auto-resize
|
|
24
|
+
* apply CSS and drive auto-resize, and a `displayMode` other than `'inline'` sets
|
|
25
|
+
* `window.__mcpDisplayMode`, which the bridge asks the host for once it connects.
|
|
25
26
|
*/
|
|
26
27
|
export declare function buildDataInjectionScript(options: {
|
|
27
28
|
toolName: string;
|
|
@@ -29,6 +30,7 @@ export declare function buildDataInjectionScript(options: {
|
|
|
29
30
|
output?: unknown;
|
|
30
31
|
structuredContent?: unknown;
|
|
31
32
|
sizing?: WidgetSizing;
|
|
33
|
+
displayMode?: string;
|
|
32
34
|
}): string;
|
|
33
35
|
/**
|
|
34
36
|
* Generic data-injection descriptor: override the injected window global.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-injector.d.ts","sourceRoot":"","sources":["../../src/shell/data-injector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,IAAI,YAAY,CASvE;AAED
|
|
1
|
+
{"version":3,"file":"data-injector.d.ts","sourceRoot":"","sources":["../../src/shell/data-injector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,IAAI,YAAY,CASvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAoBT;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,UAAU,EAAE;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAeT;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACrC,gCAAgC;IAChC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D,kCAAkC;IAClC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9D,4CAA4C;IAC5C,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,qCAAqC;IACrC,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC;IACrC,8FAA8F;IAC9F,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,WAAW,CAAC;IAC3E,gEAAgE;IAChE,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,WAAW,CAAC;CAC9C;AAID;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CA+BvD"}
|