
Research
/Security News
Mini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.
@frontmcp/uipack
Advanced tools
FrontMCP UIpack - HTML shell builder, pluggable import resolver, and NPM component loader for MCP UI (React-free core)
HTML shell builder, pluggable import resolver, and NPM component loader for MCP UI development. React-free core.
npm install @frontmcp/uipack
No React required. The only runtime dependency is zod.
import { buildShell } from '@frontmcp/uipack/shell';
const result = buildShell('<div id="root"></div>', {
toolName: 'get_weather',
input: { city: 'London' },
output: { temp: 18 },
includeBridge: true,
csp: { connectDomains: ['https://api.weather.com'] },
});
// result.html -> full HTML document with CSP, data injection, bridge IIFE
// result.hash -> fast 32-bit content hash
// result.size -> byte length
import { renderComponent } from '@frontmcp/uipack/component';
const result = await renderComponent(
{ source: { npm: 'my-weather-widget', exportName: 'WeatherCard' } },
{ toolName: 'get_weather', output: { temp: 18 }, includeBridge: true },
);
import { createEsmShResolver, createImportMap } from '@frontmcp/uipack/resolver';
const resolver = createEsmShResolver({ providerOrder: ['cloudflare', 'jsdelivr'] });
const result = resolver.resolve('chart.js');
// -> { value: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.min.js', type: 'url' }
import { generateBridgeIIFE, UNIVERSAL_BRIDGE_SCRIPT } from '@frontmcp/uipack/bridge-runtime';
// Use pre-generated universal script
const script = `<script>${UNIVERSAL_BRIDGE_SCRIPT}</script>`;
// Or generate with custom options
const iife = generateBridgeIIFE({ debug: true, minify: false });
import { createTypeFetcher } from '@frontmcp/uipack/typings';
const fetcher = createTypeFetcher({
allowedPackages: ['react', 'zod', '@frontmcp/*'],
maxDepth: 3,
});
const result = await fetcher.fetchBatch({
imports: ["import { z } from 'zod'"],
});
// result.results[0].files -> TypeFile[] with virtual node_modules/ paths
.d.ts from esm.sh with recursive dependency resolutionescapeHtml, safeJsonForScript, escapeScriptClose, and more| Path | Purpose |
|---|---|
@frontmcp/uipack | Main barrel (re-exports all modules) |
@frontmcp/uipack/resolver | Pluggable import resolution |
@frontmcp/uipack/shell | HTML shell builder |
@frontmcp/uipack/component | Polymorphic component loader |
@frontmcp/uipack/bridge-runtime | Platform bridge IIFE generator |
@frontmcp/uipack/typings | TypeScript .d.ts fetching engine |
@frontmcp/uipack/types | Standalone type definitions |
@frontmcp/uipack/utils | XSS-safe escaping utilities |
| Export | Type | Purpose |
|---|---|---|
createEsmShResolver(opts?) | function | Default resolver using esm.sh with CDN registry fallback |
DEFAULT_CDN_REGISTRY | constant | Pre-configured entries for react, react-dom, chart.js, d3, lodash |
parseImports(source) | function | Parse source code for named, default, namespace, dynamic imports |
rewriteImports(source, resolver, overrides?) | function | Rewrite bare specifiers to CDN URLs |
createImportMap(overrides?) | function | Generate browser-standard import map |
mergeImportMaps(...maps) | function | Deep-merge multiple import maps |
generateImportMapScriptTag(map) | function | <script type="importmap"> generation |
lookupPackage(name) | function | Query the CDN registry for a package |
getPackageCDNUrl(name, provider?) | function | Resolve package to best CDN URL |
extractExternalPackages(code) | function | Extract external package names from source |
| Export | Type | Purpose |
|---|---|---|
buildShell(content, config) | function | Main entry point -> ShellResult |
buildCSPMetaTag(config?) | function | Generate <meta> CSP tag |
buildCSPDirectives(config?) | function | Merge custom domains with defaults |
buildDataInjectionScript(toolName, input?, output?) | function | <script> setting window.__mcp* globals |
createTemplateHelpers(toolName) | function | escapeHtml, formatDate, formatCurrency, uniqueId, jsonEmbed |
resolveShellTemplate(opts) | function | Resolve custom shell from npm/URL/inline |
applyShellTemplate(template, values) | function | Apply values to resolved template |
validateShellTemplate(template) | function | Validate a custom shell template |
DEFAULT_CDN_DOMAINS | constant | jsDelivr, Cloudflare, Google Fonts |
| Export | Type | Purpose |
|---|---|---|
renderComponent(config, shell) | function | Top-level compositor -> ShellResult |
resolveUISource(source, opts) | function | Resolve any source to ResolvedComponent |
generateMountScript(resolved, data) | function | ES module mount script for React/module components |
isNpmSource, isFileSource, isImportSource, isFunctionSource | guards | Type narrowing helpers |
| Export | Type | Purpose |
|---|---|---|
generateBridgeIIFE(opts?) | function | Generate the bridge IIFE string |
generatePlatformBundle(platform) | function | Platform-specific bundles (chatgpt, claude, gemini, universal) |
UNIVERSAL_BRIDGE_SCRIPT | constant | Pre-generated script with all adapters |
BRIDGE_SCRIPT_TAGS | constant | { universal, chatgpt, claude, gemini } pre-wrapped <script> tags |
| Export | Type | Purpose |
|---|---|---|
createTypeFetcher(opts?) | function | Factory creating a TypeFetcher instance |
TypeFetcher | class | Main fetcher with caching and recursive dependency resolution |
MemoryTypeCache | class | LRU cache with TTL support |
globalTypeCache | constant | Module-level singleton cache (500 entries, 1h TTL) |
parseDtsImports(content) | function | Parse .d.ts files for imports/references |
combineDtsContents(results) | function | Combine multiple .d.ts files into one |
| Export | Type | Purpose |
|---|---|---|
escapeHtml(str) | function | Escape &, <, >, ", ', U+2028/2029 |
escapeHtmlAttr(str) | function | Lighter escape for attributes |
escapeJsString(str) | function | Escape for JS string literals |
escapeScriptClose(str) | function | Replace </ with <\/ to prevent </script> breakout |
safeJsonForScript(value) | function | JSON serialize safe for <script> embedding (handles BigInt, circular refs) |
safeStringify(value, space?) | function | JSON.stringify with circular reference detection |
Annotated output of buildShell with withShell: true and includeBridge: true:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>get_weather</title>
<!-- CSP: default CDN domains + custom connectDomains/resourceDomains -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net ..."
/>
<!-- Data injection: tool context as window globals -->
<script>
window.__mcpToolName = 'get_weather';
window.__mcpToolInput = { city: 'London' };
window.__mcpToolOutput = { temp: 18 };
window.__mcpStructuredContent = null;
</script>
<!-- Bridge IIFE: auto-detects platform, exposes window.FrontMcpBridge -->
<script>
(function () {
/* ... adapters, FrontMcpBridge class ... */
})();
</script>
</head>
<body>
<!-- Content from resolver + component -->
<script type="importmap">
{ "imports": { "react": "https://esm.sh/react@19" } }
</script>
<div id="root"></div>
<script type="module">
import { WeatherCard } from 'https://esm.sh/my-weather-widget';
/* mount logic */
</script>
</body>
</html>
| Mode | Config Shape | Description |
|---|---|---|
| npm | { npm: 'my-chart', exportName?, version? } | Resolved via CDN registry / esm.sh |
| file | { file: './widget.html', inline? } | Local file path |
| import | { import: 'https://cdn.example.com/widget.js' } | Direct URL passthrough |
| inline | (input, output) => '<div>...</div>' | Function returning HTML string |
The bridge IIFE exposes window.FrontMcpBridge with the following API:
| Method | Description |
|---|---|
callTool(name, args) | Invoke an MCP tool from the widget |
getToolOutput() | Read injected tool output data |
getToolInput() | Read injected tool input data |
sendMessage(content) | Send message to host |
openLink(url) | Open a URL via platform adapter |
requestDisplayMode(mode) | Switch inline / fullscreen / pip |
setWidgetState(state) | Persist widget state to localStorage |
onContextChange(callback) | Subscribe to host context changes |
onToolResult(callback) | Subscribe to tool result events |
hasCapability(cap) | Check adapter capabilities |
getTheme() | Get current theme from host |
getDisplayMode() | Get current display mode |
getHostContext() | Get host context metadata |
graph TD
subgraph "@frontmcp/uipack"
IDX["index.ts<br/><i>Main barrel exports</i>"]
subgraph bridge-runtime["bridge-runtime/"]
BR_IIFE["iife-generator.ts<br/>5 platform adapters:<br/>OpenAI, ExtApps, Claude,<br/>Gemini, Generic"]
end
subgraph component["component/"]
CO_TYPES["types.ts<br/>NpmSource, FileSource,<br/>ImportSource, FunctionSource"]
CO_LOAD["loader.ts<br/>resolveUISource(),<br/>generateMountScript()"]
CO_REND["renderer.ts<br/>renderComponent()"]
end
subgraph resolver["resolver/"]
RE_CDN["cdn-registry.ts<br/>DEFAULT_CDN_REGISTRY,<br/>lookupPackage()"]
RE_ESM["esm-sh.resolver.ts<br/>createEsmShResolver()"]
RE_PARSE["import-parser.ts<br/>parseImports()"]
RE_REWRITE["import-rewriter.ts<br/>rewriteImports()"]
RE_MAP["import-map.ts<br/>createImportMap(),<br/>mergeImportMaps()"]
end
subgraph shell["shell/"]
SH_BUILD["builder.ts<br/>buildShell()"]
SH_CSP["csp.ts<br/>CSP directives + meta tag"]
SH_DATA["data-injector.ts<br/>window.__mcp* globals"]
SH_CUSTOM["custom-shell-*.ts<br/>Resolve, validate, apply<br/>custom shell templates"]
end
subgraph types["types/"]
TY_CFG["ui-config.ts<br/>UITemplateConfig,<br/>WidgetServingMode"]
TY_RT["ui-runtime.ts<br/>UIType, WidgetManifest,<br/>WidgetConfig"]
end
subgraph typings["typings/"]
TP_FETCH["type-fetcher.ts<br/>TypeFetcher class"]
TP_DTS["dts-parser.ts<br/>parseDtsImports()"]
TP_SCHEMA["schemas.ts<br/>Zod validation"]
TP_CACHE["cache/<br/>MemoryTypeCache (LRU + TTL)"]
end
subgraph utils["utils/"]
UT["escapeHtml, safeJsonForScript,<br/>escapeScriptClose, safeStringify"]
end
end
graph BT
utils["utils<br/><i>foundation, zero deps</i>"]
resolver["resolver<br/><i>import resolution</i>"]
shell["shell<br/><i>HTML shell builder</i>"]
bridge["bridge-runtime<br/><i>IIFE generator, standalone</i>"]
component["component<br/><i>polymorphic loader</i>"]
types["types<br/><i>standalone, no internal deps</i>"]
typings["typings<br/><i>standalone, uses zod</i>"]
resolver --> utils
shell --> utils
shell --> resolver
shell --> bridge
component --> utils
component --> resolver
component --> shell
@frontmcp/uipack runs server-side (Node.js) and produces complete HTML documents. @frontmcp/ui runs browser-side and provides React components and hooks that mount inside those documents.
Bridge contract: uipack generates a vanilla JS IIFE that creates window.FrontMcpBridge. The @frontmcp/ui bridge module wraps this same API in a React-friendly class and hooks (useCallTool, etc.). The ui package re-exports bridge IIFE generators from uipack via @frontmcp/ui/bridge/runtime.
Dependency direction: @frontmcp/ui optionally depends on @frontmcp/uipack (optional dependency). They can be used independently — uipack alone for vanilla JS widgets, or together for React-based widgets.
End-to-end data flow:
sequenceDiagram
participant Dev as Developer
participant SDK as @frontmcp/sdk
participant UP as @frontmcp/uipack
participant Browser
participant UI as @frontmcp/ui
Dev->>SDK: @Tool({ ui: { npm: '...' } })
SDK->>UP: renderComponent(config, shellConfig)
UP->>UP: resolve deps (esm.sh)
UP->>UP: build shell (CSP + data injection)
UP->>UP: inject bridge IIFE
UP-->>SDK: ShellResult { html, hash, size }
Browser->>SDK: readResource("ui://widget/tool.html")
SDK-->>Browser: cached HTML
Browser->>Browser: Load @frontmcp/ui via esm.sh
Browser->>UI: Mount React components
UI->>UI: Bridge hooks read window.__mcp* globals
UI->>Browser: Interactive widget rendered
# Build
nx build uipack
# Test
nx test uipack
# Lint
nx lint uipack
@frontmcp/ui — React components, hooks, and renderers that consume uipack output@frontmcp/sdk — Core FrontMCP SDK with ToolUIRegistry@frontmcp/testing — UI test assertionsApache-2.0 — see LICENSE.
FAQs
FrontMCP UIpack - HTML shell builder, pluggable import resolver, and NPM component loader for MCP UI (React-free core)
We found that @frontmcp/uipack demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.

Research
/Security News
The North Korean malware loader hides in a Packagist-listed package and its GitHub branch to fetch and execute remote code in a likely Contagious Interview-style lure.

Security News
The Rust project is moving toward formal rules on LLM use in contributions after months of internal debate over maintainer burden, code quality, and contributor experience.