mcp-svg
v0.0.10
Published
A simple svg editor MCP with UI support
Readme
MCP Template - Todo List Example
A starter template for building MCP servers with UI Resources (MCP Apps).
Overview
This template demonstrates:
- MCP Server with tools that can be called by both the AI agent and the UI
- React UI that displays in Panel mode
- MCP Apps Protocol for Host-UI communication via the
useMcpHosthook - Theme Support via host context
Quick Start
# Install dependencies
npm install
# Build (compiles TypeScript and bundles React UI)
npm run build
# Start the server
npm startDevelopment
For active development with auto-rebuild:
# Watch mode - auto-rebuilds on file changes
npm run devWhen files change, the build updates automatically. To see the changes in the app:
- Click the ↻ refresh button on the panel title bar, OR
- Click the ↻ restart button in MCP Settings
The refresh button only reloads the UI content (faster), while restart reloads the entire MCP server.
Project Structure
├── src/
│ ├── server.ts # MCP server with todo tools
│ └── ui/
│ ├── index.html # Vite entry HTML
│ ├── main.tsx # React entry point
│ ├── App.tsx # Main App component
│ ├── App.css # Styles
│ ├── hooks/
│ │ └── useMcpHost.ts # MCP communication hook
│ └── components/
│ ├── TodoList.tsx # Todo list component
│ ├── TodoItem.tsx # Single todo item
│ └── AddTodoForm.tsx # Add todo form
├── dist/ # Built output
├── vite.config.ts # Vite configuration
└── package.jsonTools
| Tool | Description | Visibility |
|------|-------------|------------|
| todo_add | Add a new todo item | model, app |
| todo_list | List all todos | model, app |
| todo_toggle | Toggle todo completion | model, app |
| todo_remove | Delete a todo | model, app |
Customizing
Adding New Tools
- Edit
src/server.ts - Use
server.registerTool()to add your tool - Include
_meta.ui.resourceUrito link to a UI resource - Include
titleinstructuredContentfor dynamic panel titles
server.registerTool(
"my_tool",
{
description: "Does something useful",
inputSchema: {
param: z.string().describe("A parameter"),
},
_meta: {
ui: {
resourceUri: "ui://my-mcp/my-ui",
visibility: ["model", "app"],
},
},
},
async ({ param }) => {
return {
content: [{ type: "text", text: "Result for agent" }],
structuredContent: {
data: "Result for UI",
title: "My Dynamic Title", // Updates the panel title bar
},
};
}
);Dynamic Panel Titles
Include a title field in structuredContent to update the panel's title bar:
structuredContent: {
todos: allTodos,
title: `Todos (${allTodos.length})`, // Panel shows "Todos (3)"
}This helps users identify what's in each panel at a glance.
Modifying the UI
- Edit components in
src/ui/components/ - Edit styles in
src/ui/App.css - Use the
useMcpHosthook for MCP communication - Run
npm run buildto rebuild after changes - Use the refresh button in MCP Settings to reload the server
Using the useMcpHost Hook
The useMcpHost hook handles all MCP Apps protocol communication:
import { useMcpHost } from "./hooks/useMcpHost";
const MyComponent = () => {
const { callTool, isReady } = useMcpHost({
name: "My App",
version: "1.0.0",
onToolResult: (result) => {
// Handle tool results from server
console.log(result.structuredContent);
},
onThemeChange: (theme) => {
// Handle theme changes from host
console.log(`Theme: ${theme}`);
},
});
// Call a tool on the MCP server
const handleClick = async () => {
const result = await callTool("my_tool", { param: "value" });
};
return <button onClick={handleClick}>Call Tool</button>;
};MCP Apps Protocol
The UI communicates with the Host via JSON-RPC over postMessage. The useMcpHost hook abstracts this, but here's what happens under the hood:
- Initialize: Host sends
ui/initialize, UI responds with capabilities - Ready: UI sends
ui/notifications/initialized - Tool Input: Host sends
ui/notifications/tool-inputwith arguments - Tool Result: Host sends
ui/notifications/tool-resultwith data - Tool Calls: UI sends
tools/callrequests, Host forwards to server - Theme Changes: Host sends
ui/notifications/host-context-changed
Panel Reuse (Single-Session Default)
This template is a single-session MCP—the default and most common pattern:
- All tools share the same
resourceUri - Creature automatically reuses the existing panel
- When you call
todo_addmultiple times, they all update the same panel
No special configuration is needed for single-session MCPs. This is the recommended pattern for most apps.
For multi-session MCPs (like terminals where each session is independent), additional configuration is required. See Building MCP Apps - Multi-Session MCPs for the requirements.
Publishing Your MCP
To make your MCP installable via npx (from npm or GitHub), ensure your package.json is properly configured:
Required package.json fields
{
"name": "your-mcp-name",
"version": "1.0.0",
"type": "module",
"main": "dist/server.js",
"bin": {
"your-mcp-name": "dist/server.js"
},
"files": [
"dist"
]
}bin: Required fornpxto know which file to executefiles: Specifies which files to include when publishing (keeps package small)- Shebang: Your server entry file (
src/server.ts) must start with#!/usr/bin/env node
Publishing to npm
# Build first
npm run build
# Publish (requires npm account)
npm publishUsers can then install via:
npx your-mcp-namePublishing to GitHub
- Push your code to GitHub
- Ensure the
dist/folder is committed (or use a build step) - Users can install via:
npx github:username/repo-namePublishing to Creature Registry
In the Creature app:
- Open MCP Settings
- Click the publish icon next to your MCP
- Fill in the package details and install source (npm package name or GitHub URL)
- Click Publish
