Skip to Content
Provider SdkV1Bundling

What is bundling

Bundling turns your UI entrypoint into a single, self-contained index.html. Scripts, styles, and assets are inlined so you can ship one file instead of a small web app. That file can be embedded as raw HTML in a UIResource or served directly from disk by your server.

The result is a portable artifact that behaves like a miniature web app but deploys like a document.

Why bundle

You have three ways to deliver UI into an agent chat:

  1. Raw inline HTML. It’s the quickest way to get something on screen, but it caps complexity at “what you’re willing to hand‑write in one file.” As your UI grows, you end up re‑implementing build steps by hand or hitting maintainability walls.

  2. A remote URL. You get total freedom—routing, assets, origin storage, full frameworks—but you’ve also signed up for hosting, deployment pipelines, versioning, and an extra network round trip before anything renders. Great for full apps, heavy for embedded UI.

Bundling is the middle path. You build a real component (React/Vue/Svelte or even plain HTML), then collapse it into one file. Startup is fast (fewer round trips), and operations are light (no separate UI to deploy). You keep most of the flexibility of a real app without the operational overhead of one.

CLI usage

Bundle a component entrypoint (React/Vue/Svelte automatically detected):

npx @fractal-mcp/cli bundle --entrypoint=./ui/src/MyComponent.tsx --out=./dist/my-component

Bundle a standalone HTML entrypoint:

npx @fractal-mcp/cli bundle --entrypoint=./ui/index.html --out=./dist/my-static-ui

Output: a single index.html in the destination directory you provide.

CLI flags:

  • --entrypoint <path> component (.tsx/.jsx/.vue/.svelte) or .html
  • --out <path> output directory for the bundle

Register a tool that serves the bundle

Serve the bundled index.html as raw HTML in a UIResource:

import fs from 'node:fs'; import path from 'node:path'; import { createUIResource } from '@mcp-ui/server'; server.registerTool('hello_ui', { title: 'HelloUI', description: 'Renders the bundled Hello UI', inputSchema: {}, }, async () => { const html = fs.readFileSync(path.resolve('dist/my-component/index.html'), 'utf8'); const uiResource = createUIResource({ uri: 'ui://hello', content: { type: 'rawHtml', htmlString: html }, encoding: 'text', }); return { content: [uiResource] }; });

Point the file read at the --out directory you chose during bundling.

Limitations, tradeoffs, and when to use it

The right choice depends on the surface you’re building and the operational cost you want to carry. Here’s the landscape at a glance:

Tradeoffs at a glance

ApproachFlexibilitySecurityAccess to localStorageComplexity/OverheadLoading Speed
Raw inline HTMLLowHigh (no external code)Limited (depends on iframe/origin)MinimalFastest (no fetches)
Remote URLHighestStrong isolation via separate originFull (normal web origin)Highest (deploy, host, auth, versions)Slower (extra network, DNS, TLS)
Bundled HTMLHighGood (single file; can be sandboxed)Limited under srcdoc/no-originLow (one artifact, no hosting)Very fast (single fetch/inline)

Notes:

  • If you embed with iframe srcdoc, you either share the parent origin (less isolation) or run without an origin (tighter sandbox, but limited APIs like localStorage).
  • Bundling uses Vite under the hood and inherits its limitations (e.g., weaker Angular support without additional config).

Recommendations

For tiny, static widgets, inline HTML is hard to beat. For most rich components in chat, bundling gives you the best performance‑to‑ops ratio. Reach for a remote URL when you’re truly building a standalone application and you need origin capabilities (e.g., persistent storage, complex routing) and the full web runtime.

Under the hood

  • Vite + vite-plugin-singlefile produce a single self-contained HTML file.
  • Framework detection walks up from your entrypoint to find package.json and inspects dependencies (React, Vue, Svelte). If not found, falls back to file extensions.
  • For component entrypoints, a lightweight bootstrap is generated and rendered, then inlined.
  • For HTML entrypoints, assets referenced by the page are bundled and inlined.