@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.
@@ -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/openLink', { url: url });
686
+ return this.sendRequest('ui/open-link', { url: url });
668
687
  },
669
688
  requestDisplayMode: function(context, mode) {
670
- return this.sendRequest('ui/setDisplayMode', { mode: mode });
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
- return this.sendRequest('ui/close', {});
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
- return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
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.sendRequest('ui/log', { level: level, message: message, data: data });
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
- self._context.hostContext.displayMode = mode;
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/openLink', { url: url });
937
+ return this.sendRequest('ui/open-link', { url: url });
919
938
  },
920
939
  requestDisplayMode: function(context, mode) {
921
- return this.sendRequest('ui/setDisplayMode', { mode: mode });
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
- return this.sendRequest('ui/close', {});
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
- return this.sendRequest('ui/updateModelContext', { context: data, merge: merge !== false });
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.sendRequest('ui/log', { level: level, message: message, data: data });
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
- self._context.hostContext.displayMode = mode;
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. FrontMCP 1.9 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}`
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.2",
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.2",
64
+ "@frontmcp/lazy-zod": "1.9.3",
65
65
  "esbuild": ">=0.27.0 <1"
66
66
  },
67
67
  "peerDependenciesMeta": {
@@ -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;AA8BtF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,WAAW,CA4F5E"}
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"}
@@ -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;;;;;;;GAOG;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;CACvB,GAAG,MAAM,CAgBT;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"}
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"}