mcp2web

Build MCP servers that serve HTML/CSS web UIs via the mcp2web protocol

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "mcp2web" with this command: npx skills add wvides/mcp2web-skill/wvides-mcp2web-skill-mcp2web

mcp2web — Building Web UI MCP Servers

Protocol Overview

mcp2web is a protocol standard for MCP servers that serve HTML/CSS web UIs. An Electron mini-browser connects to these servers and renders their HTML content.

URI Schemes

  • Resources: mcp2web://<server-name>/<path> — HTML pages served as MCP resources with mimeType: "text/html"
  • Tool Actions: mcp2web-tool://<tool-name> — used as <form action> attributes to trigger MCP tool calls

How It Works

  1. MCP server registers resources (HTML pages) and tools (form handlers)
  2. Electron browser connects via stdio, lists resources, renders them in a sandboxed iframe
  3. Links (<a href="mcp2web://...">) trigger resource reads
  4. Forms (<form action="mcp2web-tool://...">) trigger tool calls
  5. Tool responses containing HTML are rendered as new pages

Project Scaffolding

When creating a new mcp2web server, use this structure:

my-server/
├── package.json
├── tsconfig.json
└── src/
    ├── pages.ts    # HTML page template functions
    └── server.ts   # MCP server setup, resources, tools

package.json

{
  "type": "module",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "zod": "^3.24.4"
  },
  "devDependencies": {
    "typescript": "^5.8.3"
  },
  "scripts": {
    "build": "tsc",
    "start": "node dist/server.js"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

Resource Pattern

Register HTML pages as MCP resources:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({ name: "my-server", version: "1.0.0" });

server.resource("page-name", "mcp2web://my-server/page-name", async (uri) => ({
  contents: [{
    uri: uri.href,
    mimeType: "text/html",
    text: myPageFunction()
  }]
}));

Tool Pattern

Register form handlers as MCP tools:

import { z } from "zod";

server.tool("submit-form", {
  field1: z.string(),
  field2: z.string()
}, async ({ field1, field2 }) => ({
  content: [{
    type: "text",
    text: successPageFunction(field1, field2)
  }]
}));

HTML/CSS Guidelines

MUST follow:

  • All CSS must be inline (<style> tags) — no external stylesheets
  • All pages must be complete HTML documents (<!DOCTYPE html>, <html>, <head>, <body>)
  • Navigation links: <a href="mcp2web://server-name/page-name">
  • Form actions: <form action="mcp2web-tool://tool-name" method="POST">
  • Form inputs must have name attributes matching tool parameter names
  • No external JavaScript dependencies
  • No external images or fonts (use inline SVG or CSS-only graphics)

Recommended:

  • Dark theme for consistency with the mcp2web browser
  • Responsive design with max-width containers
  • Color scheme: bg #1a1a2e, text #e0e0e0, accent #0f3460, links #16c79a
  • Clean typography with system fonts

Server Entry Point Pattern

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

// ... register resources and tools ...

const transport = new StdioServerTransport();
await server.connect(transport);

Security Considerations

  • Content is rendered in a sandboxed iframe (allow-scripts allow-forms, no allow-same-origin)
  • Never include sensitive data in HTML responses
  • Validate all tool inputs with Zod schemas
  • Pages cannot access the parent window or Node.js APIs

Security Best Practices

Always escape user input in HTML

User-provided strings must be HTML-escaped before embedding in templates to prevent XSS:

function escapeHtml(str: string): string {
  return str
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

// Use it when rendering user data
`<p>${escapeHtml(userInput)}</p>`

Add constraints to Zod schemas

Always set max lengths on string inputs and use format validators where applicable:

server.tool("example", {
  name: z.string().max(100),
  email: z.string().email().max(254),
  description: z.string().max(1000),
}, async ({ name, email, description }) => { /* ... */ });

Use non-guessable IDs

Use crypto.randomUUID() (built-in to Node.js) instead of sequential integers for resource identifiers:

const item = {
  id: crypto.randomUUID(), // e.g. "a1b2c3d4-..."
  // ...
};

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

terraform-best-practices

No summary provided by upstream source.

Repository SourceNeeds Review
General

M3U8 Downloader

Download encrypted m3u8/HLS videos using parallel downloads. Use when given an m3u8 URL to download a video, especially encrypted HLS streams with AES-128.

Registry SourceRecently Updated
General

Data Analyst Cn

数据分析助手 - 数据清洗、统计分析、可视化建议。适合:数据分析师、产品经理、运营。

Registry SourceRecently Updated