Skip to content
Automation
Skill

/single-file-bundling

Configure Vite with vite-plugin-singlefile for mandatory single-file HTML bundling of MCP Apps. All assets (JS, CSS, images, fonts) must be inlined into a single HTML file for sandboxed iframe compatibility.

From plugin
babysitter
1.8k200 skills3 agents21 commands1 MCP
Install
$ npx -y skills add a5c-ai/babysitter --skill single-file-bundling --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/single-file-bundling

Context preview

The summary Claude sees to decide when to auto-load this skill.

Configure Vite with vite-plugin-singlefile for mandatory single-file HTML bundling of MCP Apps. All assets (JS, CSS, images, fonts) must be inlined into a single HTML file for sandboxed iframe compatibility.

SKILL.md

single-file-bundling.SKILL.md
name: single-file-bundling
description: Configure Vite with vite-plugin-singlefile for mandatory single-file HTML bundling of MCP Apps. All assets (JS, CSS, images, fonts) must be inlined into a single HTML file for sandboxed iframe compatibility.
allowed-tools: Read, Write, Edit, Bash
graph:
  domains: [domain:software-engineering]
  specializations: [specialization:ai-agents-conversational]
  skillAreas: [skill-area:mcp-server-implementation, skill-area:mcp-tool-design]
  roles: [role:backend-engineer, role:fullstack-engineer]
  workflows: [workflow:feature-development]

single-file-bundling

Configure Vite with vite-plugin-singlefile to produce a single self-contained HTML file for MCP Apps running in sandboxed iframes.

Overview

MCP Apps run in sandboxed iframes with no same-origin server. This means:

  • **No relative asset URLs** -- `<script src="./main.js">` will not resolve
  • **No CSS file imports** -- `<link href="./styles.css">` will not load
  • **No image/font paths** -- relative paths to assets will fail
  • The **entire UI must be a single HTML file** with all JS, CSS, images, and fonts inlined

`vite-plugin-singlefile` handles this by inlining all assets into one HTML file during the Vite build. This is **mandatory** for MCP Apps -- without it, the app will show a blank iframe.

Capabilities

Vite Configuration

  • Configure Vite with `vite-plugin-singlefile`
  • Set correct `base`, `build.outDir`, and entry point
  • Handle framework-specific Vite plugins (React, Vue, Svelte, Preact, Solid)

Single HTML File Output

  • All JavaScript inlined as `<script>` tags
  • All CSS inlined as `<style>` tags
  • All images converted to data URIs
  • All fonts converted to base64

Two-Phase Build Setup

  • Phase 1: Vite bundles UI into `dist/mcp-app.html`
  • Phase 2: TypeScript compiles server into `dist/server.js`
  • Combined build script orchestrates both phases

Hybrid Build Pipelines

  • Add MCP build alongside existing standalone build
  • Separate Vite configs for MCP and standalone if needed
  • Separate HTML entry points (`mcp-app.html` vs `index.html`)

Usage

Basic Vite Configuration

// vite.config.ts
import { defineConfig } from 'vite';
import { viteSingleFile } from 'vite-plugin-singlefile';

export default defineConfig({
  plugins: [viteSingleFile()],
  build: {
    outDir: 'dist',
    // Entry point for the MCP App UI
    rollupOptions: {
      input: 'mcp-app.html',
    },
  },
});

React Vite Configuration

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteSingleFile } from 'vite-plugin-singlefile';

export default defineConfig({
  plugins: [react(), viteSingleFile()],
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: 'mcp-app.html',
    },
  },
});

Vue Vite Configuration

// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { viteSingleFile } from 'vite-plugin-singlefile';

export default defineConfig({
  plugins: [vue(), viteSingleFile()],
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: 'mcp-app.html',
    },
  },
});

Svelte Vite Configuration

// vite.config.ts
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { viteSingleFile } from 'vite-plugin-singlefile';

export default defineConfig({
  plugins: [svelte(), viteSingleFile()],
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: 'mcp-app.html',
    },
  },
});

HTML Entry Point

<!-- mcp-app.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My MCP App</title>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/src/main.ts"></script>
</body>
</html>

For React (`.tsx` entry):

<!-- mcp-app.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My MCP App</title>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/src/main.tsx"></script>
</body>
</html>

Package.json Build Scripts

{
  "scripts": {
    "build:ui": "vite build",
    "build:server": "tsc --project tsconfig.server.json",
    "build": "npm run build:ui && npm run build:server",
    "dev": "concurrently \"vite\" \"tsx watch src/server.ts\"",
    "serve": "tsx src/server.ts"
  }
}

Server Reading the Bundled HTML

import fs from 'fs';
import path from 'path';
import { registerAppResource, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps';

// Read the single-file bundle produced by Vite
const bundledHtml = fs.readFileSync(
  path.join(__dirname, '../dist/mcp-app.html'),
  'utf-8'
);

registerAppResource(server, {
  uri: 'app:///my-app',
  name: 'My App',
  mimeType: RESOURCE_MIME_TYPE,
  async read() {
    return {
      contents: [{
        uri: 'app:///my-app',
        mimeType: RESOURCE_MIME_TYPE,
        text: bundledHtml,
      }],
    };
  },
});

Hybrid Build Pipeline (MCP + Standalone)

When converting a web app that already has its own build:

// vite.config.mcp.ts -- MCP-specific build config
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteSingleFile } from 'vite-plugin-singlefile';

export default defineConfig({
  plugins: [react(), viteSingleFile()],
  build: {
    outDir: 'dist/mcp',
    rollupOptions: {
      input: 'mcp-app.html',  // Separate entry from index.html
    },
  },
});
{
  "scripts": {
    "build:standalone": "vite build",
    "build:mcp:ui": "vite build --config vite.config.mcp.ts",
    "build:mcp:server": "tsc --project tsconfig.server.json",
    "build:mcp": "npm run build:mcp:ui && npm run build:mcp:server",
    "build:all": "npm run build
Read more
Ships withbabysitter

Enforce obedience on agentic workforces. Manage extremely complex workflows through deterministic, hallucination-free self-orchestration.

Get the whole plugin

Other skills on babysitter.