mcp-html-sync-server
The MCP HTML Sync Server is a real-time HTML syncing tool designed for AI agents to dynamically manage HTML pages. It supports real-time updates through WebSockets, script and stylesheet management, and has lightweight deployment options including Docker and NPX.
MCP HTML Sync Server

Code of Conduct | Contributing | Security
A real-time HTML syncing server with hot reload capabilities, built using the Model Context Protocol (MCP). This server enables AI agents to create, update, and destroy HTML pages dynamically, with all connected clients receiving updates in real-time.
Features
- Real-time HTML Syncing: Create and update HTML content with instant updates to all connected clients
- WebSocket Hot Reload: All connected browsers automatically refresh when content changes
- Script Management: Add JavaScript scripts to pages, either via CDN URLs or inline content
- Stylesheet Management: Add CSS stylesheets to pages via CDN URLs
- MCP Integration: Designed specifically for AI agents using the Model Context Protocol
- Page Lifecycle Management: Automatic expiration of pages after configurable time periods
- Connection Limits: Configurable maximum page count with automatic cleanup of oldest pages
- Simple API: Easy-to-use MCP tools for page creation, updating, and destruction
- Lightweight: Minimal dependencies and efficient resource usage
Architecture
flowchart LR
AI[AI Agent] -->|MCP Protocol| MCP[MCP HTML Sync Server]
MCP -->|Create/Update/Destroy/AddScripts/AddStylesheets| PM[Page Manager]
PM -->|Store| Pages[(HTML Pages, Scripts & Stylesheets)]
User[User Browser] -->|HTTP Request| HTTP[HTTP Server]
HTTP -->|Fetch Page, Scripts & Stylesheets| PM
User <-->|WebSocket| WS[WebSocket Server]
WS <-->|Real-time Updates| PM
Installation
Using Docker
docker pull yujiosaka/mcp-html-sync-server
docker run -p 3000:3000 yujiosaka/mcp-html-sync-server
Docker Environment Variables
Instead of using an .env
file, you can pass environment variables directly to the Docker container at runtime:
docker run -p 3000:3000 \
-e SERVER_PORT=3000 \
-e BASE_URL=http://localhost:3000/ \
-e PAGE_MAX_AGE=1h \
-e PAGE_MAX_COUNT=1000 \
yujiosaka/mcp-html-sync-server
This approach is recommended for production deployments as it allows you to configure the server without modifying the container image.
Using NPX
NODE_ENV=production npx mcp-html-sync-server
From Source
# Clone the repository
git clone https://github.com/yujiosaka/mcp-html-sync-server.git
cd mcp-html-sync-server
# Install dependencies
bun install
# Copy the example environment file
bun run config
# Start the server
bun run start
Configuration
Edit the .env
file to configure the server:
# Host address for the HTTP server (used for binding the server)
SERVER_HOST=localhost
# Port number for the HTTP server (used for binding the server)
SERVER_PORT=3000
# Base URL for the application (used for generating view URLs)
BASE_URL=http://localhost:3000/
# Maximum age of pages before they expire (e.g., 1h = 1 hour, uses ms library format)
PAGE_MAX_AGE=1h
# Maximum number of pages that can be stored (oldest pages are removed when limit is reached)
PAGE_MAX_COUNT=1000
Configuration Options
Option | Description | Default | Format |
---|---|---|---|
SERVER_HOST | Host address for binding the HTTP server | localhost | hostname |
SERVER_PORT | Port number for the HTTP server | 3000 | number |
BASE_URL | Base URL for generating view URLs | http://localhost:3000/ | URL |
PAGE_MAX_AGE | Maximum age of pages before expiration | 1h | time string (e.g., 1h, 30m, 1d) |
PAGE_MAX_COUNT | Maximum number of pages to keep | 1000 | number |
Usage with MCP
Integration with Claude Desktop
Add this to your claude_desktop_config.json
:
Docker
{
"mcpServers": {
"html-sync-server": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-p",
"3000:3000",
"-e", "SERVER_PORT=3000",
"-e", "BASE_URL=http://localhost:3000/",
"-e", "PAGE_MAX_AGE=1h",
"-e", "PAGE_MAX_COUNT=1000",
"yujiosaka/mcp-html-sync-server"
]
}
}
}
NPX
{
"mcpServers": {
"html-sync-server": {
"command": "npx",
"args": ["-y", "mcp-html-sync-server"],
"env": {
"NODE_ENV": "production"
}
}
}
}
Integration with VS Code
For quick installation, use one of the one-click install buttons below:
Manual Installation
For manual installation, add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing Ctrl + Shift + P
and typing Preferences: Open Settings (JSON)
.
Optionally, you can add it to a file called .vscode/mcp.json
in your workspace. This will allow you to share the configuration with others.
Note that the
mcp
key is not needed in the.vscode/mcp.json
file.
{
"mcp": {
"servers": {
"html-sync-server": {
"command": "npx",
"args": ["-y", "mcp-html-sync-server"],
"env": {
"NODE_ENV": "production"
}
}
}
}
}
For Docker installation:
{
"mcp": {
"servers": {
"html-sync-server": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-p", "3000:3000",
"-e", "SERVER_PORT=3000",
"-e", "BASE_URL=http://localhost:3000/",
"-e", "PAGE_MAX_AGE=1h",
"-e", "PAGE_MAX_COUNT=1000",
"yujiosaka/mcp-html-sync-server"
]
}
}
}
}
MCP Tools
The server provides the following MCP tools that can be used by AI agents:
1. Create a Page
Creates a new HTML page with the specified content.
Tool Name: create_page
Input Schema:
{
"type": "object",
"properties": {
"body": {
"type": "string",
"description": "HTML content for the page body (only the inner content)"
},
"scripts": {
"type": "array",
"description": "Optional array of JavaScript scripts to include in the page",
"items": {
"type": "object",
"properties": {
"src": {
"type": "string",
"description": "URL for external script"
},
"content": {
"type": "string",
"description": "Content for inline script"
}
},
"description": "Either src or content must be provided",
"oneOf": [
{ "required": ["src"] },
{ "required": ["content"] }
]
}
},
"stylesheets": {
"type": "array",
"description": "Optional array of CSS stylesheets to include in the page",
"items": {
"type": "object",
"properties": {
"href": {
"type": "string",
"description": "URL for external stylesheet"
}
},
"required": ["href"]
}
}
},
"required": ["body"]
}
Example Request:
{
"name": "create_page",
"arguments": {
"body": "<h1>Hello World</h1><p>This is my page content.</p><button id='confetti-btn'>Click for Confetti!</button>",
"scripts": [
{ "src": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" }
],
"stylesheets": [
{ "href": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" }
]
}
}
Example Response:
{
"content": [
{
"type": "text",
"text": "Page created successfully! A URL is provided below to view your page."
},
{
"type": "text",
"text": "View your HTML page in URL: http://localhost:3000/abc123"
},
{
"type": "text",
"text": "ID: abc123\nExpires at: 2023-04-15T12:34:56.789Z\n\nUse this ID for future updates before expiration."
}
],
"metadata": {
"id": "abc123",
"url": "http://localhost:3000/abc123",
"expires_at": "2023-04-15T12:34:56.789Z"
}
}
2. Update a Page
Updates an existing page with new content. All connected clients will see the updates in real-time. If the page does not exist (has been removed or expired), an error response will be returned.
Tool Name: update_page
Input Schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID of the page to update"
},
"body": {
"type": "string",
"description": "New HTML content for the page body (only the inner content)"
}
},
"required": ["id", "body"]
}
Example Request:
{
"name": "update_page",
"arguments": {
"id": "abc123",
"body": "<h1>Updated Content</h1><p>This content has been updated.</p>"
}
}
Example Response:
{
"content": [
{
"type": "text",
"text": "Page updated successfully! A URL is provided below to view your updated page."
},
{
"type": "text",
"text": "View your HTML page in URL: http://localhost:3000/abc123"
},
{
"type": "text",
"text": "ID: abc123\nExpires at: 2023-04-15T12:34:56.789Z\n\nUse this ID for future updates before expiration."
}
],
"metadata": {
"id": "abc123",
"url": "http://localhost:3000/abc123",
"expires_at": "2023-04-15T12:34:56.789Z"
}
}
3. Destroy a Page
Removes a page and disconnects all clients.
Tool Name: destroy_page
Input Schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID of the page to destroy"
}
},
"required": ["id"]
}
Example Request:
{
"name": "destroy_page",
"arguments": {
"id": "abc123"
}
}
Example Response:
{
"success": true
}
4. Add Scripts to a Page
Adds JavaScript scripts to an existing page. All connected clients will receive the scripts in real-time.
Tool Name: add_scripts
Input Schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID of the page to add scripts to"
},
"scripts": {
"type": "array",
"description": "Array of JavaScript scripts to add to the page",
"items": {
"type": "object",
"properties": {
"src": {
"type": "string",
"description": "URL for external script"
},
"content": {
"type": "string",
"description": "Content for inline script"
}
},
"description": "Either src or content must be provided",
"oneOf": [
{ "required": ["src"] },
{ "required": ["content"] }
]
}
}
},
"required": ["id", "scripts"]
}
Example Request:
{
"name": "add_scripts",
"arguments": {
"id": "abc123",
"scripts": [
{ "content": "alert('Hello World!')" }
]
}
}
Example Response:
{
"content": [
{
"type": "text",
"text": "Scripts added successfully! A URL is provided below to view your page."
},
{
"type": "text",
"text": "View your HTML page in URL: http://localhost:3000/abc123"
},
{
"type": "text",
"text": "ID: abc123\nExpires at: 2023-04-15T12:34:56.789Z\n\nUse this ID for future updates before expiration."
}
],
"metadata": {
"id": "abc123",
"url": "http://localhost:3000/abc123",
"expires_at": "2023-04-15T12:34:56.789Z"
}
}
5. Add Stylesheets to a Page
Adds CSS stylesheets to an existing page. All connected clients will receive the stylesheets in real-time.
Tool Name: add_stylesheets
Input Schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID of the page to add stylesheets to"
},
"stylesheets": {
"type": "array",
"description": "Array of CSS stylesheets to add to the page",
"items": {
"type": "object",
"properties": {
"href": {
"type": "string",
"description": "URL for external stylesheet"
}
},
"required": ["href"]
}
}
},
"required": ["id", "stylesheets"]
}
Example Request:
{
"name": "add_stylesheets",
"arguments": {
"id": "abc123",
"stylesheets": [
{ "href": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" }
]
}
}
Example Response:
{
"content": [
{
"type": "text",
"text": "Stylesheets added successfully! A URL is provided below to view your page."
},
{
"type": "text",
"text": "View your HTML page in URL: http://localhost:3000/abc123"
},
{
"type": "text",
"text": "ID: abc123\nExpires at: 2023-04-15T12:34:56.789Z\n\nUse this ID for future updates before expiration."
}
],
"metadata": {
"id": "abc123",
"url": "http://localhost:3000/abc123",
"expires_at": "2023-04-15T12:34:56.789Z"
}
}
How It Works
- AI agents interact with the server through the Model Context Protocol (MCP)
- The MCP server processes requests to create, update, or destroy HTML pages
- Users view these pages through URLs provided by the AI agent
- Real-time updates are delivered to all connected clients via WebSockets
Manual Testing
The project includes a manual test script (manual-test.ts
) that demonstrates how to use the MCP server. This script:
- Creates a new HTML page with canvas-confetti from CDN
- Updates the page content
- Adds stylesheets to trigger text animations
- Adds scripts to trigger confetti animations
- Deletes the page
To run the manual test:
# Run the test script
bun manual-test.ts
The script will provide URLs that you can open in your browser to see the page and observe real-time updates. It uses an interactive approach with confirmation prompts between each step, allowing you to verify the changes in your browser before proceeding to the next step.
This test script is particularly useful for:
- Verifying that the MCP server is working correctly
- Understanding the flow of creating, updating, and destroying pages
- Observing real-time WebSocket updates in the browser
Development
# Run in development mode with auto-reload
bun run dev
# Build the project
bun run build
# Run linting
bun run check
# Fix linting issues
bun run check:write
Troubleshooting
Port Already in Use
If you see an error like the following when starting the server:
[2025-04-28 18:28:48.370 +0900] ERROR: Error starting server
err: {
"type": "Error",
"message": "Failed to start server. Is port 3000 in use?",
"stack":
Error
at serve (unknown)
at [kRealListen] (node:http:565:41)
at listen (node:http:542:35)
at <anonymous> (/Users/yujiisobe/work/mcp-html-sync-server/node_modules/fastify/lib/server.js:265:12)
at processTicksAndRejections (unknown:7:39)
"code": "EADDRINUSE",
"syscall": "listen",
"errno": 0
}
This means that port 3000 (or whatever port you've configured) is already being used by another application. To resolve this:
-
Change the port: Edit the
.env
file and change theSERVER_PORT
to a different value (e.g., 3001, 8080, etc.) -
Stop the other application: Find and stop the application that's using port 3000. You can use one of these commands to find processes using the port:
# On macOS/Linux lsof -i :3000 # On Windows netstat -ano | findstr :3000
-
Restart your computer: In some cases, a restart can clear orphaned processes that might be holding onto the port.
When using the server with Claude Desktop or VS Code, you may also see JSON parsing errors if the port is already in use. These errors occur because the server can't start properly and is returning error messages instead of valid JSON responses.
Building Docker Image
docker build -t yujiosaka/mcp-html-sync-server .
License
This project is licensed under the MIT License. See for details.