@frontmcp/uipack 1.8.6 → 1.9.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/esm/index.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
2
+ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
3
+ // __frontmcpCreateRequire
1
4
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
5
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
6
  }) : x)(function(x) {
@@ -398,12 +401,38 @@ function __applyHostTheme(theme) {
398
401
  } catch (e) {}
399
402
  }
400
403
 
404
+ function extractToolOutput(params) {
405
+ // A host sends a CallToolResult: prefer structuredContent, then parsed text, then the raw content.
406
+ if (!params) return undefined;
407
+ if (params.structuredContent !== undefined && params.structuredContent !== null) return params.structuredContent;
408
+ var content = params.content;
409
+ if (Array.isArray(content)) {
410
+ for (var i = 0; i < content.length; i++) {
411
+ var item = content[i];
412
+ if (item && item.type === 'text' && typeof item.text === 'string') {
413
+ try { return JSON.parse(item.text); } catch (e) { return item.text; }
414
+ }
415
+ }
416
+ }
417
+ return content;
418
+ }
419
+
401
420
  function readInjectedData() {
402
421
  var data = { toolInput: {}, toolOutput: undefined, structuredContent: undefined };
403
422
  if (typeof window !== 'undefined') {
404
423
  if (window.__mcpToolInput) data.toolInput = window.__mcpToolInput;
405
424
  if (window.__mcpToolOutput) data.toolOutput = window.__mcpToolOutput;
406
425
  if (window.__mcpStructuredContent) data.structuredContent = window.__mcpStructuredContent;
426
+ // The OpenAI Apps SDK hands the widget its data as window.openai.toolInput / toolOutput
427
+ // (toolOutput is the tool's structuredContent).
428
+ var oa = window.openai;
429
+ if (oa && typeof oa === 'object') {
430
+ if (oa.toolInput && !window.__mcpToolInput) data.toolInput = oa.toolInput;
431
+ if (oa.toolOutput !== undefined && oa.toolOutput !== null) {
432
+ if (data.toolOutput === undefined) data.toolOutput = oa.toolOutput;
433
+ if (data.structuredContent === undefined) data.structuredContent = oa.toolOutput;
434
+ }
435
+ }
407
436
  }
408
437
  return data;
409
438
  }
@@ -452,7 +481,24 @@ var OpenAIAdapter = {
452
481
  if (sdk.displayMode) {
453
482
  context.hostContext.displayMode = sdk.displayMode;
454
483
  }
455
- // Note: OpenAI SDK does not have an onContextChange equivalent
484
+ // The Apps SDK re-dispatches the changed globals in an 'openai:set_globals' event.
485
+ window.addEventListener('openai:set_globals', function(event) {
486
+ var globals = (event && event.detail && event.detail.globals) || window.openai || {};
487
+ if (globals.toolInput !== undefined && globals.toolInput !== null) {
488
+ context.toolInput = globals.toolInput;
489
+ window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
490
+ }
491
+ if (globals.toolOutput !== undefined && globals.toolOutput !== null) {
492
+ context.structuredContent = globals.toolOutput;
493
+ context.notifyToolResult(globals.toolOutput);
494
+ }
495
+ if (globals.theme && globals.theme !== context.hostContext.theme) {
496
+ context.notifyContextChange({ theme: globals.theme });
497
+ }
498
+ if (globals.displayMode && globals.displayMode !== context.hostContext.displayMode) {
499
+ context.notifyContextChange({ displayMode: globals.displayMode });
500
+ }
501
+ });
456
502
  return Promise.resolve();
457
503
  },
458
504
  callTool: function(context, name, args) {
@@ -599,9 +645,10 @@ var ExtAppsAdapter = {
599
645
  window.dispatchEvent(new CustomEvent('tool:input-partial', { detail: { arguments: context.toolInput } }));
600
646
  break;
601
647
  case 'ui/notifications/tool-result':
602
- context.toolOutput = params.content;
648
+ var resultOutput = extractToolOutput(params);
649
+ context.toolOutput = resultOutput;
603
650
  context.structuredContent = params.structuredContent;
604
- context.notifyToolResult(params.content);
651
+ context.notifyToolResult(resultOutput);
605
652
  window.dispatchEvent(new CustomEvent('tool:result', { detail: params }));
606
653
  break;
607
654
  case 'ui/notifications/host-context-changed':
@@ -655,6 +702,15 @@ var ExtAppsAdapter = {
655
702
  window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params }, targetOrigin);
656
703
  });
657
704
  },
705
+ sendNotification: function(method, params) {
706
+ // JSON-RPC notification: no id, no response. Same origin rule as sendRequest.
707
+ if (!this.trustedOrigin && this.trustedOrigins.length === 0) {
708
+ return Promise.reject(new Error('Cannot send notification: no trusted origin established'));
709
+ }
710
+ var targetOrigin = this.trustedOrigin || this.trustedOrigins[0];
711
+ window.parent.postMessage({ jsonrpc: '2.0', method: method, params: params }, targetOrigin);
712
+ return Promise.resolve();
713
+ },
658
714
  sendHandshake: function(context) {
659
715
  // Send ui/initialize using '*' as target origin since TOFU hasn't
660
716
  // been established yet. The response from the host will establish
@@ -720,7 +776,7 @@ var ExtAppsAdapter = {
720
776
  var pending = this.pendingSize;
721
777
  if (!pending) return;
722
778
  this.pendingSize = null;
723
- this.sendRequest('ui/setSize', pending.params).then(function(result) {
779
+ this.sendNotification('ui/notifications/size-changed', pending.params).then(function(result) {
724
780
  for (var i = 0; i < pending.waiters.length; i++) pending.waiters[i].resolve(result);
725
781
  }, function(err) {
726
782
  for (var i = 0; i < pending.waiters.length; i++) pending.waiters[i].reject(err);
@@ -747,13 +803,11 @@ var ExtAppsAdapter = {
747
803
  return this.sendRequest('ui/setDisplayMode', { mode: mode });
748
804
  },
749
805
  setSize: function(context, size) {
750
- // FrontMCP sizing channel \u2014 parallels ui/setDisplayMode. Reports the
751
- // measured/desired widget dimensions to the host.
752
- var params = {
753
- height: size && size.height,
754
- width: size && size.width,
755
- aspectRatio: size && size.aspectRatio
756
- };
806
+ // Standard ext-apps sizing: the view tells the host its dimensions with the
807
+ // ui/notifications/size-changed notification ({ width?, height? } in px).
808
+ var params = {};
809
+ if (size && typeof size.width === 'number') params.width = size.width;
810
+ if (size && typeof size.height === 'number') params.height = size.height;
757
811
  if (!this.handshakeSettled && !this.trustedOrigin && this.trustedOrigins.length === 0) {
758
812
  // No host origin yet: keep only the latest size and send it when the
759
813
  // handshake settles; every caller's promise follows that one request.
@@ -764,7 +818,7 @@ var ExtAppsAdapter = {
764
818
  self.pendingSize.waiters.push({ resolve: resolve, reject: reject });
765
819
  });
766
820
  }
767
- return this.sendRequest('ui/setSize', params);
821
+ return this.sendNotification('ui/notifications/size-changed', params);
768
822
  },
769
823
  requestClose: function(context) {
770
824
  return this.sendRequest('ui/close', {});
@@ -1187,7 +1241,7 @@ FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
1187
1241
 
1188
1242
  // Report a desired widget size to the host. \`size\` is { height?, width?, aspectRatio? }.
1189
1243
  // Per-adapter behaviour: Claude/generic no-op (host measures the DOM),
1190
- // ext-apps sends ui/setSize, OpenAI forwards to its SDK when available.
1244
+ // ext-apps sends ui/notifications/size-changed, OpenAI forwards to its SDK when available.
1191
1245
  FrontMcpBridge.prototype.setSize = function(size) {
1192
1246
  if (!this._adapter) return Promise.reject(new Error('Not initialized'));
1193
1247
  if (!this._adapter.setSize) return Promise.resolve();
@@ -1289,7 +1343,7 @@ FrontMcpBridge.prototype._setupDataToolCallHandler = function() {
1289
1343
  target.classList.add('opacity-50', 'cursor-not-allowed');
1290
1344
 
1291
1345
  // Add spinner for buttons
1292
- var spinner = '<svg class="animate-spin -ml-1 mr-2 h-4 w-4 inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>';
1346
+ var spinner = '<svg class="animate-spin -ml-1 mr-2 h-4 w-4 inline" style="width:1em;height:1em;vertical-align:-0.125em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>';
1293
1347
  if (target.tagName === 'BUTTON') {
1294
1348
  target.innerHTML = spinner + 'Loading...';
1295
1349
  }
@@ -1370,7 +1424,22 @@ function minifyJS(code) {
1370
1424
  if (code.length > MAX_MINIFY_CODE_LENGTH) {
1371
1425
  return code;
1372
1426
  }
1373
- return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1").replace(/\s+/g, " ").replace(/\s*([{};,:()[\]])\s*/g, "$1").replace(/;\}/g, "}").trim();
1427
+ const out = [];
1428
+ let inBlockComment = false;
1429
+ for (const raw of code.split("\n")) {
1430
+ const line = raw.trim();
1431
+ if (inBlockComment) {
1432
+ if (line.includes("*/")) inBlockComment = false;
1433
+ continue;
1434
+ }
1435
+ if (line === "" || line.startsWith("//")) continue;
1436
+ if (line.startsWith("/*")) {
1437
+ if (!line.includes("*/")) inBlockComment = true;
1438
+ continue;
1439
+ }
1440
+ out.push(line);
1441
+ }
1442
+ return out.join("\n");
1374
1443
  }
1375
1444
  function generatePlatformBundle(platform, options = {}) {
1376
1445
  const platformAdapters = {
@@ -2907,11 +2976,8 @@ function buildCSPDirectives(csp) {
2907
2976
  directives.push(`img-src ${imgSources.join(" ")}`);
2908
2977
  const fontSources = ["'self'", "data:", ...allResourceDomains];
2909
2978
  directives.push(`font-src ${fontSources.join(" ")}`);
2910
- if (validConnectDomains.length) {
2911
- directives.push(`connect-src ${validConnectDomains.join(" ")}`);
2912
- } else {
2913
- directives.push(`connect-src ${allResourceDomains.join(" ")}`);
2914
- }
2979
+ const connectSources = [.../* @__PURE__ */ new Set([...allResourceDomains, ...validConnectDomains])];
2980
+ directives.push(`connect-src ${connectSources.join(" ")}`);
2915
2981
  directives.push("object-src 'self' data:");
2916
2982
  return directives;
2917
2983
  }
@@ -2920,26 +2986,34 @@ function buildCSPMetaTag(csp) {
2920
2986
  const content = directives.join("; ");
2921
2987
  return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(content)}">`;
2922
2988
  }
2989
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
2923
2990
  function validateCSPDomain(domain) {
2924
- if (domain.startsWith("https://*.")) {
2925
- const rest = domain.slice(10);
2926
- return /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/.test(rest);
2991
+ if (typeof domain !== "string" || /[\s;,'"]/.test(domain)) return false;
2992
+ if (/[?#]/.test(domain)) return false;
2993
+ if (domain.includes("*")) {
2994
+ const wildcard = /^(?:https|wss):\/\/\*\.(.*)$/.exec(domain);
2995
+ return wildcard !== null && /^([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(wildcard[1]);
2927
2996
  }
2928
2997
  try {
2929
2998
  const url = new URL(domain);
2930
- return url.protocol === "https:";
2999
+ if (url.protocol === "https:" || url.protocol === "wss:") return true;
3000
+ return (url.protocol === "http:" || url.protocol === "ws:") && LOOPBACK_HOSTS.has(url.hostname);
2931
3001
  } catch {
2932
3002
  return false;
2933
3003
  }
2934
3004
  }
3005
+ var reportedInvalidDomains = /* @__PURE__ */ new Set();
2935
3006
  function sanitizeCSPDomains(domains) {
2936
3007
  if (!domains) return [];
2937
3008
  const valid = [];
2938
3009
  for (const domain of domains) {
2939
3010
  if (validateCSPDomain(domain)) {
2940
3011
  valid.push(domain);
2941
- } else {
2942
- console.warn(`Invalid CSP domain ignored: ${domain}`);
3012
+ } else if (!reportedInvalidDomains.has(domain)) {
3013
+ reportedInvalidDomains.add(domain);
3014
+ console.warn(
3015
+ `Invalid CSP domain ignored: ${domain} (expected an https:// or wss:// origin, or http:// / ws:// on localhost)`
3016
+ );
2943
3017
  }
2944
3018
  }
2945
3019
  return valid;
@@ -3476,8 +3550,8 @@ if (__root) {
3476
3550
  configurable: true, enumerable: true
3477
3551
  });
3478
3552
  }
3479
- // 2. Try injected data globals
3480
- if (__hasData(window.__mcpToolOutput)) { __render(window.__mcpToolOutput); }
3553
+ // 2. Try injected data globals (null: the page carries no call's data, e.g. compiled at startup)
3554
+ if (__hasData(window.__mcpToolOutput) && window.__mcpToolOutput !== null) { __render(window.__mcpToolOutput); }
3481
3555
  // 3. Listen for bridge tool-result (ext-apps / MCP Inspector)
3482
3556
  var __bridge = window.FrontMcpBridge;
3483
3557
  if (__bridge && typeof __bridge.onToolResult === 'function') {
@@ -3729,7 +3803,7 @@ createRoot(document.getElementById('root'))
3729
3803
  return `${componentImport}
3730
3804
  import React from 'react';
3731
3805
  import { createRoot } from 'react-dom/client';
3732
- const output = window.__mcpToolOutput;
3806
+ const output = window.__mcpToolOutput || {};
3733
3807
  const props = ${propsCode};
3734
3808
  createRoot(document.getElementById('root'))
3735
3809
  .render(React.createElement(Component, props));`;
@@ -4102,6 +4176,86 @@ function wrapDetectedContent(value) {
4102
4176
  }
4103
4177
  }
4104
4178
 
4179
+ // libs/uipack/src/adapters/markdown.ts
4180
+ var SAFE_HREF = /^(https?:|mailto:|\/|#)/i;
4181
+ function emphasis(text) {
4182
+ return text.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>");
4183
+ }
4184
+ function inline(text) {
4185
+ const stash = [];
4186
+ const hold = (html2) => `\uE000${stash.push(html2) - 1}\uE000`;
4187
+ let out = escapeHtml(text.replace(/\uE000/g, "")).replace(
4188
+ /`([^`]+)`/g,
4189
+ (_m, code) => hold(`<code>${code}</code>`)
4190
+ );
4191
+ out = out.replace(
4192
+ /\[([^\]]+)\]\(([^)\s]+)\)/g,
4193
+ (_m, label, href) => SAFE_HREF.test(href) ? hold(`<a href="${href}" rel="noopener noreferrer">${emphasis(label)}</a>`) : label
4194
+ );
4195
+ out = emphasis(out);
4196
+ for (let i = 0; i < 2; i++) out = out.replace(/\uE000(\d+)\uE000/g, (_m, n) => stash[Number(n)]);
4197
+ return out;
4198
+ }
4199
+ function markdownToHtml(source) {
4200
+ const lines = source.replace(/\r\n?/g, "\n").split("\n");
4201
+ const blocks = [];
4202
+ let paragraph = [];
4203
+ let list;
4204
+ const flushParagraph = () => {
4205
+ if (paragraph.length > 0) blocks.push(`<p>${inline(paragraph.join(" "))}</p>`);
4206
+ paragraph = [];
4207
+ };
4208
+ const flushList = () => {
4209
+ if (list)
4210
+ blocks.push(`<${list.tag}>${list.items.map((item) => `<li>${inline(item)}</li>`).join("")}</${list.tag}>`);
4211
+ list = void 0;
4212
+ };
4213
+ for (let i = 0; i < lines.length; i++) {
4214
+ const line = lines[i];
4215
+ const fence = /^\s*(`{3,})/.exec(line);
4216
+ if (fence) {
4217
+ flushParagraph();
4218
+ flushList();
4219
+ const fenceLength = fence[1].length;
4220
+ const isClosing = (l) => {
4221
+ const m = /^\s*(`{3,})\s*$/.exec(l);
4222
+ return m !== null && m[1].length >= fenceLength;
4223
+ };
4224
+ const code = [];
4225
+ for (i++; i < lines.length && !isClosing(lines[i]); i++) code.push(lines[i]);
4226
+ blocks.push(`<pre><code>${escapeHtml(code.join("\n"))}
4227
+ </code></pre>`);
4228
+ continue;
4229
+ }
4230
+ const heading = /^(#{1,6})\s+(.*)$/.exec(line);
4231
+ if (heading) {
4232
+ flushParagraph();
4233
+ flushList();
4234
+ blocks.push(`<h${heading[1].length}>${inline(heading[2].trim())}</h${heading[1].length}>`);
4235
+ continue;
4236
+ }
4237
+ const item = /^\s*([-*+]|\d+\.)\s+(.*)$/.exec(line);
4238
+ if (item) {
4239
+ flushParagraph();
4240
+ const tag = /\d/.test(item[1]) ? "ol" : "ul";
4241
+ if (list && list.tag !== tag) flushList();
4242
+ list ??= { tag, items: [] };
4243
+ list.items.push(item[2]);
4244
+ continue;
4245
+ }
4246
+ if (line.trim() === "") {
4247
+ flushParagraph();
4248
+ flushList();
4249
+ continue;
4250
+ }
4251
+ flushList();
4252
+ paragraph.push(line.trim());
4253
+ }
4254
+ flushParagraph();
4255
+ flushList();
4256
+ return blocks.join("\n");
4257
+ }
4258
+
4105
4259
  // libs/uipack/src/adapters/type-detector.ts
4106
4260
  function detectUIType(template) {
4107
4261
  if (template === null || template === void 0) {
@@ -4135,9 +4289,15 @@ function detectUIType(template) {
4135
4289
  }
4136
4290
 
4137
4291
  // libs/uipack/src/adapters/template-renderer.ts
4138
- function buildCspConfig(resolver) {
4292
+ function buildCspConfig(resolver, extra) {
4139
4293
  const cspResourceDomains = ["https://esm.sh"];
4140
4294
  const cspConnectDomains = ["https://esm.sh"];
4295
+ for (const domain of extra?.resourceDomains ?? []) {
4296
+ if (!cspResourceDomains.includes(domain)) cspResourceDomains.push(domain);
4297
+ }
4298
+ for (const domain of extra?.connectDomains ?? []) {
4299
+ if (!cspConnectDomains.includes(domain)) cspConnectDomains.push(domain);
4300
+ }
4141
4301
  if (resolver && "overrides" in resolver) {
4142
4302
  const overrides = resolver.overrides;
4143
4303
  if (overrides) {
@@ -4177,6 +4337,24 @@ function renderUnwrappedResult(rawResult, options) {
4177
4337
  }
4178
4338
  return rawResult;
4179
4339
  }
4340
+ var warnedComponentReferences = /* @__PURE__ */ new WeakMap();
4341
+ function warnComponentReference(template, toolName, logger) {
4342
+ const warned = warnedComponentReferences.get(template) ?? /* @__PURE__ */ new Set();
4343
+ if (warned.has(toolName)) return;
4344
+ warned.add(toolName);
4345
+ warnedComponentReferences.set(template, warned);
4346
+ logger.warn(
4347
+ `[FrontMCP] Tool "${toolName}": \`ui.template\` is a React component reference, which cannot be bundled (there is no source file to compile), so the widget would render an empty page. Use \`template: { file: './widget.tsx' }\` instead.`
4348
+ );
4349
+ }
4350
+ function isReactElement(value) {
4351
+ return typeof value === "object" && value !== null && "$$typeof" in value;
4352
+ }
4353
+ function isDefinitelyReactComponent(template) {
4354
+ if (typeof template !== "function") return false;
4355
+ const proto = template.prototype;
4356
+ return typeof proto?.render === "function" || template["$$typeof"] !== void 0;
4357
+ }
4180
4358
  function renderToolTemplate(options) {
4181
4359
  const { toolName, input, output, template, resolver, platformType, sizing } = options;
4182
4360
  const uiType = detectUIType(template);
@@ -4187,30 +4365,41 @@ function renderToolTemplate(options) {
4187
4365
  output,
4188
4366
  includeBridge: true,
4189
4367
  resolver,
4190
- sizing
4368
+ sizing,
4369
+ csp: options.csp ? buildCspConfig(resolver, options.csp) : void 0
4191
4370
  };
4192
4371
  let html2;
4193
4372
  let hash = "";
4194
4373
  let size = 0;
4195
4374
  if (typeof template === "object" && template !== null && "file" in template) {
4196
- const cspConfig = buildCspConfig(resolver);
4197
4375
  const result = renderComponent(
4198
4376
  { source: template, inlineReact: resourceMode === "inline" },
4199
- { ...shellConfig, csp: cspConfig }
4377
+ { ...shellConfig, csp: buildCspConfig(resolver, options.csp) }
4200
4378
  );
4201
4379
  html2 = result.html;
4202
4380
  hash = result.hash;
4203
4381
  size = result.size;
4204
4382
  } else if (typeof template === "function") {
4205
- if (uiType === "react") {
4383
+ const helpers = createTemplateHelpers();
4384
+ const ctx = { input: input ?? {}, output: output ?? {}, helpers };
4385
+ let rawResult;
4386
+ let isHtmlBuilder = uiType !== "react" || !isDefinitelyReactComponent(template);
4387
+ if (isHtmlBuilder) {
4388
+ try {
4389
+ rawResult = template(ctx);
4390
+ isHtmlBuilder = !isReactElement(rawResult);
4391
+ } catch (error) {
4392
+ if (uiType !== "react") throw error;
4393
+ isHtmlBuilder = false;
4394
+ }
4395
+ }
4396
+ if (!isHtmlBuilder) {
4397
+ warnComponentReference(template, toolName, options.logger ?? console);
4206
4398
  const shellResult = buildShell('<div id="root"></div>', shellConfig);
4207
4399
  html2 = shellResult.html;
4208
4400
  hash = shellResult.hash;
4209
4401
  size = shellResult.size;
4210
4402
  } else {
4211
- const helpers = createTemplateHelpers();
4212
- const ctx = { input, output, helpers };
4213
- const rawResult = template(ctx);
4214
4403
  const wrapped = isTrustedHtml(rawResult) ? void 0 : wrapDetectedContent(rawResult);
4215
4404
  if (wrapped) {
4216
4405
  html2 = wrapped;
@@ -4222,7 +4411,7 @@ function renderToolTemplate(options) {
4222
4411
  }
4223
4412
  }
4224
4413
  } else if (typeof template === "string") {
4225
- const shellResult = buildShell(template, shellConfig);
4414
+ const shellResult = buildShell(uiType === "markdown" ? markdownToHtml(template) : template, shellConfig);
4226
4415
  html2 = shellResult.html;
4227
4416
  hash = shellResult.hash;
4228
4417
  size = shellResult.size;
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/uipack",
3
- "version": "1.8.6",
3
+ "version": "1.9.0",
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.8.6",
64
+ "@frontmcp/lazy-zod": "1.9.0",
65
65
  "esbuild": ">=0.27.0 <1"
66
66
  },
67
67
  "peerDependenciesMeta": {
@@ -1,3 +1,6 @@
1
+ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
2
+ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
3
+ // __frontmcpCreateRequire
1
4
  // libs/uipack/src/resolver/cdn-registry.ts
2
5
  var DEFAULT_CDN_REGISTRY = {
3
6
  react: {