Skip to main content

mcp-builder

Build Model Context Protocol (MCP) servers with mcp-use framework. Use when creating MCP servers, defining tools/resources/prompts, working with mcp-use, bootstrapping MCP projects, deploying MCP servers, or when user mentions MCP development, MCP tools, MCP resources, or MCP prompts.

Source facts

Repository
mcp-use/skills
Last source activity
January 31, 2026 at 16:32
Detected SKILL.md language
English
Stars
1
Forks
2

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
mcp-builder
description
Build Model Context Protocol (MCP) servers with mcp-use framework. Use when creating MCP servers, defining tools/resources/prompts, working with mcp-use, bootstrapping MCP projects, deploying MCP servers, or when user mentions MCP development, MCP tools, MCP resources, or MCP prompts.
# MCP Server Builder Build production-ready MCP servers with the mcp-use framework. This Skill provides quick-start instructions and best practices for creating MCP servers. ## Quick Start **Always bootstrap with `npx create-mcp-use-app`:** ```bash npx create-mcp-use-app my-mcp-server cd my-mcp-server ``` **Choose template based on needs:** - `--template starter` - Full-featured with all MCP primitives (tools, resources, prompts) + example widgets - `--template mcp-apps` - Optimized for ChatGPT widgets with product search example - `--template blank` - Minimal starting point for custom implementation ```bash # Example: MCP Apps template npx create-mcp-use-app my-server --template mcp-apps cd my-server yarn install ``` **Template Details:** - **starter**: Best for learning - includes all MCP features plus widgets - **mcp-apps**: Best for ChatGPT apps - includes product carousel/accordion example - **blank**: Best for experts - minimal boilerplate ## MCP Apps Structure ### Automatic Widget Registration The mcp-apps and starter templates automatically discover and register React widgets from the `resources/` folder: **Single-file widget pattern:** ``` resources/ └── weather-display.tsx # Widget name becomes "weather-display" ``` **Folder-based widget pattern:** ``` resources/ └── product-search/ # Widget name becomes "product-search" ├── widget.tsx # Entry point (required name!) ├── components/ # Sub-components ├── hooks/ # Custom hooks ├── types.ts └── constants.ts ``` **What happens automatically:** 1. Server scans `resources/` folder at startup 2. Finds `.tsx` files or `widget.tsx` in folders 3. Extracts `widgetMetadata` from each component 4. Registers as MCP Tool (e.g., `weather-display`) 5. Registers as MCP Resource (e.g., `ui://widget/weather-display.html`) 6. Builds widget bundles with Vite **No manual registration needed!** Just export `widgetMetadata` and a default component. ## Defining Tools Tools are executable functions that AI models can call: ```typescript import { MCPServer, text, object } from "mcp-use/server"; import { z } from "zod"; const server = new MCPServer({ name: "my-server", version: "1.0.0", description: "My MCP server" }); // Simple tool server.tool( { name: "greet-user", description: "Greet a user by name", schema: z.object({ name: z.string().describe("The user's name"), formal: z.boolean().optional().describe("Use formal greeting") }) }, async ({ name, formal }) => { const greeting = formal ? `Good day, ${name}` : `Hey ${name}!`; return text(greeting); } ); ``` **Key points:** - Use Zod for schema validation - Add `.describe()` to all parameters - Return appropriate response types (text, object, widget) ## Defining Resources Resources expose data that clients can read: ```typescript import { object, text, markdown } from "mcp-use/server"; // Static resource server.resource( { uri: "config://settings", name: "Application Settings", description: "Current configuration", mimeType: "application/json" }, async () => { return object({ theme: "dark", version: "1.0.0" }); } ); // Dynamic resource server.resource( { uri: "stats://current", name: "Current Stats", description: "Real-time statistics", mimeType: "application/json" }, async () => { const stats = await getStats(); return object(stats); } ); // Markdown resource server.resource( { uri: "docs://guide", name: "User Guide", description: "Documentation", mimeType: "text/markdown" }, async () => { return markdown("# Guide\n\nWelcome!"); } ); ``` **Response helpers available:** - `text(string)` - Plain text - `object(data)` - JSON objects - `markdown(string)` - Markdown content - `html(string)` - HTML content - `image(buffer, mimeType)` - Binary images - `audio(buffer, mimeType)` - Audio files - `binary(buffer, mimeType)` - Binary data - `mix(...contents)` - Combine multiple content types **Advanced response examples:** ```typescript // Audio response import { audio } from 'mcp-use/server'; // From base64 data return audio(base64Data, "audio/wav"); // From file path (async) return await audio("/path/to/audio.mp3"); // Binary data (PDFs, etc.) import { binary } from 'mcp-use/server'; return binary(pdfBuffer, "application/pdf"); // Mix multiple content types import { mix, text, object, resource } from 'mcp-use/server'; return mix( text("Analysis complete:"), object({ score: 95, status: "pass" }), resource("report://analysis-123", text("Full report...")) ); ``` ## Defining Prompts Prompts are reusable templates for AI interactions: ```typescript server.prompt( { name: "code-review", description: "Generate a code review template", schema: z.object({ language: z.string().describe("Programming language"), focusArea: z.string().optional().describe("Specific focus area") }) }, async ({ language, focusArea }) => { const focus = focusArea ? ` with focus on ${focusArea}` : ""; return { messages: [ { role: "user", content: { type: "text", text: `Please review this ${language} code${focus}.` } } ] }; } ); ``` ## Testing Locally **Development mode (hot reload):** ```bash yarn dev ``` **Production mode:** ```bash yarn build yarn start ``` **Inspector UI:** Access at `http://localhost:3000/inspector` to test tools, view resources, and try prompts. **Tunneling (test with ChatGPT before deploying):** Option 1 - Auto-tunnel: ```bash mcp-use start --port 3000 --tunnel ``` Option 2 - Separate tunnel: ```bash yarn start # Terminal 1 npx @mcp-use/tunnel 3000 # Terminal 2 ``` You'll get a public URL like `https://happy-cat.local.mcp-use.run/mcp` **Tunnel details:** - Expires after 24 hours - Closes after 1 hour of inactivity - Rate limit: 10 creations/hour, max 5 active per IP Learn more: https://mcp-use.com/docs/tunneling ## Deployment **Deploy to mcp-use Cloud (recommended):** ```bash # Login first (if not already) npx mcp-use login # Deploy yarn deploy ``` **If authentication error:** ```bash npx mcp-use login yarn deploy ``` **After deployment:** - Public URL provided (e.g., `https://your-server.mcp-use.com/mcp`) - Auto-scaled and monitored - HTTPS enabled - Zero-downtime deployments ## Best Practices **Tool Design:** - ✅ One tool = one focused capability - ✅ Descriptive names and descriptions - ✅ Use `.describe()` on all Zod fields - ✅ Handle errors gracefully - ✅ Return helpful error messages **Resource Design:** - ✅ Use clear URI schemes (config://, docs://, stats://) - ✅ Choose appropriate MIME types - ✅ Use response helpers for cleaner code - ✅ Make resources dynamic when needed **Prompt Design:** - ✅ Keep prompts reusable - ✅ Use system messages for context - ✅ Parameterize with Zod schemas - ✅ Include clear instructions **Testing:** - ✅ Test with Inspector UI first - ✅ Use tunneling to test with real clients before deploying - ✅ Verify all tools, resources, and prompts work as expected **Deployment:** - ✅ Test locally and with tunneling first - ✅ Run `npx mcp-use login` if deploy fails - ✅ Version your server semantically - ✅ Document breaking changes ## Widget Support ### Automatic Widget Registration When using the `mcp-apps` or `starter` template, widgets in the `resources/` folder are automatically registered: ```tsx // resources/weather-display.tsx import { useWidget, McpUseProvider, type WidgetMetadata } from 'mcp-use/react'; import { z } from 'zod'; const propSchema = z.object({ city: z.string(), temperature: z.number() }); // Required: Export widget metadata export const widgetMetadata: WidgetMetadata = { description: "Display weather information", props: propSchema, // Use 'props', not 'schema'! }; // Required: Export default component export default function WeatherDisplay() { const { props, isPending } = useWidget<z.infer<typeof propSchema>>(); // Always handle loading state if (isPending) return <div>Loading...</div>; return ( <McpUseProvider autoSize> <div> <h2>{props.city}</h2> <p>{props.temperature}°C</p> </div> </McpUseProvider> ); } ``` **Widget automatically becomes available as:** - MCP Tool: `weather-display` - MCP Resource: `ui://widget/weather-display.html` ### Content Security Policy (CSP) Control what external resources widgets can access: ```typescript export const widgetMetadata: WidgetMetadata = { description: "Weather widget", props: z.object({ city: z.string() }), metadata: { csp: { // APIs to call connectDomains: ["https://api.weather.com"], // Static assets to load resourceDomains: ["https://cdn.weather.com"], // Iframes to embed frameDomains: ["https://embed.weather.com"], // Script directives scriptDirectives: ["'unsafe-inline'"], }, }, }; ``` Alternatively, set at server level: ```typescript server.uiResource({ type: "mcpApps", name: "my-widget", htmlTemplate: `...`, metadata: { csp: { connectDomains: ["https://api.example.com"], resourceDomains: ["https://cdn.example.com"], }, }, }); ``` ## Dual-Protocol Widget Support mcp-use supports the **MCP Apps standard** (SEP-1865) with automatic dual-protocol support: ```typescript import { MCPServer } from 'mcp-use/server'; const server = new MCPServer({ name: 'my-server', version: '1.0.0', baseUrl: process.env.MCP_URL || 'http://localhost:3000', // Required for widgets }); // Register a dual-protocol widget server.uiResource({ type: "mcpApps", // Works with BOTH MCP Apps clients AND ChatGPT name: "weather-display", htmlTemplate: `<!DOCTYPE html>...`, metadata: { csp: { connectDomains: ["https://api.weather.com"] }, prefersBorder: true, autoResize: true, }, }); ``` **What happens automatically:** - **MCP Apps clients** (Claude, Goose) receive: `text/html;profile=mcp-app` with `_meta.ui.*` - **ChatGPT** receives: `text/html+skybridge` with `_meta.openai/*` - Same widget code works everywhere! ### Custom OpenAI Metadata Need ChatGPT-specific features? Combine both metadata fields: ```typescript server.uiResource({ type: "mcpApps", name: "my-widget", htmlTemplate: `...`, // Unified metadata (dual-protocol) metadata: { csp: { connectDomains: ["https://api.example.com"] }, prefersBorder: true, }, // ChatGPT-specific overrides appsSdkMetadata: { "openai/widgetDescription": "ChatGPT-specific description", "openai/customFeature": "some-value", // Any custom OpenAI metadata }, }); ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub