Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@frontmcp/uipack

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@frontmcp/uipack

FrontMCP UIpack - HTML shell builder, pluggable import resolver, and NPM component loader for MCP UI (React-free core)

latest
Source
npmnpm
Version
1.3.0
Version published
Maintainers
1
Created
Source

@frontmcp/uipack

HTML shell builder, pluggable import resolver, and NPM component loader for MCP UI development. React-free core.

NPM License

Install

npm install @frontmcp/uipack

No React required. The only runtime dependency is zod.

Quick Start

Build an HTML Shell

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

Render an NPM Component

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 },
);

Resolve Imports to CDN URLs

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' }

Generate Bridge IIFE

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 });

Fetch TypeScript Definitions

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

Features

  • Shell builder — Full HTML documents with CSP, data injection, and bridge runtime
  • Import resolver — Pluggable CDN resolution (esm.sh, Cloudflare, jsDelivr, unpkg)
  • Component loader — 4 source modes: npm, file, URL, inline function
  • Bridge IIFE — Platform-aware vanilla JS bridge (OpenAI, ExtApps, Claude, Gemini, Generic)
  • Type fetching — Download .d.ts from esm.sh with recursive dependency resolution
  • XSS utilitiesescapeHtml, safeJsonForScript, escapeScriptClose, and more
  • Custom shells — Load shell templates from npm, URL, or inline HTML
  • Standalone types — SDK-independent type definitions for widget configuration

Entry Points

PathPurpose
@frontmcp/uipackMain barrel (re-exports all modules)
@frontmcp/uipack/resolverPluggable import resolution
@frontmcp/uipack/shellHTML shell builder
@frontmcp/uipack/componentPolymorphic component loader
@frontmcp/uipack/bridge-runtimePlatform bridge IIFE generator
@frontmcp/uipack/typingsTypeScript .d.ts fetching engine
@frontmcp/uipack/typesStandalone type definitions
@frontmcp/uipack/utilsXSS-safe escaping utilities

Module Exports

Resolver

ExportTypePurpose
createEsmShResolver(opts?)functionDefault resolver using esm.sh with CDN registry fallback
DEFAULT_CDN_REGISTRYconstantPre-configured entries for react, react-dom, chart.js, d3, lodash
parseImports(source)functionParse source code for named, default, namespace, dynamic imports
rewriteImports(source, resolver, overrides?)functionRewrite bare specifiers to CDN URLs
createImportMap(overrides?)functionGenerate browser-standard import map
mergeImportMaps(...maps)functionDeep-merge multiple import maps
generateImportMapScriptTag(map)function<script type="importmap"> generation
lookupPackage(name)functionQuery the CDN registry for a package
getPackageCDNUrl(name, provider?)functionResolve package to best CDN URL
extractExternalPackages(code)functionExtract external package names from source

Shell

ExportTypePurpose
buildShell(content, config)functionMain entry point -> ShellResult
buildCSPMetaTag(config?)functionGenerate <meta> CSP tag
buildCSPDirectives(config?)functionMerge custom domains with defaults
buildDataInjectionScript(toolName, input?, output?)function<script> setting window.__mcp* globals
createTemplateHelpers(toolName)functionescapeHtml, formatDate, formatCurrency, uniqueId, jsonEmbed
resolveShellTemplate(opts)functionResolve custom shell from npm/URL/inline
applyShellTemplate(template, values)functionApply values to resolved template
validateShellTemplate(template)functionValidate a custom shell template
DEFAULT_CDN_DOMAINSconstantjsDelivr, Cloudflare, Google Fonts

Component

ExportTypePurpose
renderComponent(config, shell)functionTop-level compositor -> ShellResult
resolveUISource(source, opts)functionResolve any source to ResolvedComponent
generateMountScript(resolved, data)functionES module mount script for React/module components
isNpmSource, isFileSource, isImportSource, isFunctionSourceguardsType narrowing helpers

Bridge Runtime

ExportTypePurpose
generateBridgeIIFE(opts?)functionGenerate the bridge IIFE string
generatePlatformBundle(platform)functionPlatform-specific bundles (chatgpt, claude, gemini, universal)
UNIVERSAL_BRIDGE_SCRIPTconstantPre-generated script with all adapters
BRIDGE_SCRIPT_TAGSconstant{ universal, chatgpt, claude, gemini } pre-wrapped <script> tags

Typings

ExportTypePurpose
createTypeFetcher(opts?)functionFactory creating a TypeFetcher instance
TypeFetcherclassMain fetcher with caching and recursive dependency resolution
MemoryTypeCacheclassLRU cache with TTL support
globalTypeCacheconstantModule-level singleton cache (500 entries, 1h TTL)
parseDtsImports(content)functionParse .d.ts files for imports/references
combineDtsContents(results)functionCombine multiple .d.ts files into one

Utils

ExportTypePurpose
escapeHtml(str)functionEscape &, <, >, ", ', U+2028/2029
escapeHtmlAttr(str)functionLighter escape for attributes
escapeJsString(str)functionEscape for JS string literals
escapeScriptClose(str)functionReplace </ with <\/ to prevent </script> breakout
safeJsonForScript(value)functionJSON serialize safe for <script> embedding (handles BigInt, circular refs)
safeStringify(value, space?)functionJSON.stringify with circular reference detection

Shell HTML Structure

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>

Component Source Modes

ModeConfig ShapeDescription
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

Bridge API

The bridge IIFE exposes window.FrontMcpBridge with the following API:

MethodDescription
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

Contributing

Architecture

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

Module Dependency Graph

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

How @frontmcp/uipack works with @frontmcp/ui

@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

Development

# Build
nx build uipack

# Test
nx test uipack

# Lint
nx lint uipack

Testing

  • 95%+ coverage required across statements, branches, functions, lines
  • XSS tests are critical — test all escaping functions with malicious input
  • CSP tests — verify generated Content-Security-Policy headers are correct
  • Test all 4 component source modes (npm, file, import, inline)
  • Test bridge IIFE generation for each platform

Requirements

  • Node.js >= 22.0.0

License

Apache-2.0 — see LICENSE.

Keywords

mcp

FAQs

Package last updated on 27 May 2026

Did you know?

Socket

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.

Install

Related posts