mcp-builder

โดย mcp-use

สร้างเซิร์ฟเวอร์ MCP ที่พร้อมใช้งานจริงด้วยเฟรมเวิร์ก mcp-use และการลงทะเบียนวิดเจ็ตอัตโนมัติ เริ่มต้นด้วย npx create-mcp-use-app และเลือกจากสามเทมเพลต: starter (ฟีเจอร์ครบ), mcp-apps (ปรับให้เหมาะกับ ChatGPT), หรือ blank (ขั้นต่ำ) กำหนดเครื่องมือ ทรัพยากร และพร้อมต์โดยใช้สคีมา Zod พร้อมการตรวจสอบอัตโนมัติและคำอธิบายพารามิเตอร์ที่ชัดเจน ลงทะเบียนวิดเจ็ต React จากโฟลเดอร์ resources/ โดยอัตโนมัติเป็นเครื่องมือและทรัพยากร MCP พร้อมรองรับโปรโตคอลคู่สำหรับ MCP Apps และ...

npx skills add https://github.com/mcp-use/skills --skill mcp-builder

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:

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
# 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:

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:

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:

// 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:

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):

yarn dev

Production mode:

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:

mcp-use start --port 3000 --tunnel

Option 2 - Separate tunnel:

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):

# Login first (if not already)
npx mcp-use login

# Deploy
yarn deploy

If authentication error:

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:

// 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:

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:

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:

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:

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
  },
});

Project Structure

my-mcp-server/
├── resources/           # React widgets (apps-sdk)
│   └── widget.tsx
├── public/             # Static assets
├── index.ts            # Server entry point
├── package.json
├── tsconfig.json
└── README.md

Common Patterns

Tool with dual-protocol widget:

import { MCPServer, widget, text } from 'mcp-use/server';
import { z } from 'zod';

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  baseUrl: process.env.MCP_URL || 'http://localhost:3000',
});

server.tool(
  {
    name: "show-data",
    description: "Display data with visualization",
    schema: z.object({
      query: z.string()
    }),
    widget: {
      name: "data-display", // Must exist in resources/
      invoking: "Loading...",
      invoked: "Data loaded"
    }
  },
  async ({ query }) => {
    const data = await fetchData(query);
    return widget({
      props: { data },
      output: text(`Found ${data.length} results`)
    });
  }
);

Resource template (parameterized):

server.resourceTemplate(
  {
    uriTemplate: "user://{userId}/profile",
    name: "User Profile",
    description: "Get user by ID",
    mimeType: "application/json"
  },
  async ({ userId }) => {
    const user = await fetchUser(userId);
    return object(user);
  }
);

Error handling:

server.tool(
  {
    name: "divide",
    schema: z.object({
      a: z.number(),
      b: z.number()
    })
  },
  async ({ a, b }) => {
    if (b === 0) {
      return text("Error: Cannot divide by zero");
    }
    return text(`Result: ${a / b}`);
  }
);

Detailed Examples

For comprehensive examples and advanced patterns, connect to the mcp-use MCP server which provides:

  • Complete example resources for all primitives
  • Full working server examples
  • Detailed documentation
  • Interactive widgets showcase

Learn More

Quick Reference

Commands:

  • npx create-mcp-use-app my-server - Bootstrap
  • yarn dev - Development mode
  • yarn build - Build for production
  • yarn start - Run production server
  • mcp-use start --tunnel - Start with tunnel
  • npx mcp-use login - Authenticate
  • yarn deploy - Deploy to cloud

Response helpers:

  • text(str), object(data), markdown(str), html(str)
  • image(buf, mime), audio(buf, mime), binary(buf, mime)
  • mix(...) - Combine multiple content types
  • widget({ props, output }) - Return widget with data

Server methods:

  • server.tool() - Define executable tool
  • server.resource() - Define static/dynamic resource
  • server.resourceTemplate() - Define parameterized resource
  • server.prompt() - Define prompt template
  • server.uiResource() - Define widget resource
  • server.listen() - Start server

Widget metadata fields:

  • description - Widget description
  • props - Zod schema for widget props
  • metadata - Unified config (dual-protocol)
  • metadata.csp - Content Security Policy
  • appsSdkMetadata - ChatGPT-specific overrides

Available templates:

  • starter - Full-featured (tools, resources, prompts, widgets)
  • mcp-apps - ChatGPT-optimized with product example
  • blank - Minimal boilerplate

Skills เพิ่มเติมจาก mcp-use

chatgpt-app-builder
mcp-use
เลิกใช้งานแล้ว: ให้ใช้ mcp-app-builder แทน สร้างแอป ChatGPT ด้วยวิดเจ็ตแบบโต้ตอบและการพัฒนา React ที่ไม่ต้องกำหนดค่าใดๆ ทักษะนี้เลิกใช้งานแล้ว โปรดย้ายไปใช้ mcp-app-builder เพื่อรับการสนับสนุนและการอัปเดตอย่างต่อเนื่อง ช่วยให้ผู้ใช้มนุษย์และ LLM ทำงานร่วมกันผ่านวิดเจ็ตแบบโต้ตอบที่ใช้ร่วมกัน มีตัวจัดการเซิร์ฟเวอร์ โครงสร้างวิดเจ็ต React การจัดการสถานะ และฮุคในตัวสำหรับการเรียกใช้เครื่องมือและข้อความติดตามผล ครอบคลุมโหมดการแสดงผล (แบบอินไลน์, เต็มหน้าจอ, ภาพซ้อนภาพ), การปรับแต่งธีม,...
official
mcp-apps-builder
mcp-use
คู่มืออ้างอิงบังคับสำหรับการสร้างเซิร์ฟเวอร์ MCP ระดับโปรดักชันพร้อมเครื่องมือ ทรัพยากร พรอมต์ และวิดเจ็ต ครอบคลุมแนวคิดพื้นฐาน (Tool, Resource, Prompt, Widget primitives), สถาปัตยกรรมเซิร์ฟเวอร์, รูปแบบการตรวจสอบสิทธิ์ (OAuth, Supabase, แบบกำหนดเอง) และกลยุทธ์การปรับใช้ รวมถึงคำแนะนำโดยละเอียดสำหรับการนำเครื่องมือ ทรัพยากร พรอมต์ การจัดรูปแบบการตอบสนอง และ UI ที่ใช้วิดเจ็ตพร้อมการจัดการสถานะและการปรับแต่งธีม เอกสารรูปแบบต่อต้านทั่วไป (การตรวจสอบที่ขาดหาย การจัดการข้อผิดพลาดที่ไม่เหมาะสม...)
official
mcp-builder
mcp-use
เลิกใช้งานแล้ว: สร้างเซิร์ฟเวอร์ MCP ด้วยเครื่องมือ ทรัพยากร พรอมต์ และวิดเจ็ตแบบโต้ตอบโดยใช้ mcp-use ทักษะนี้เลิกใช้งานแล้ว ให้ใช้ mcp-app-builder แทน รองรับการกำหนดเครื่องมือ ทรัพยากร พรอมต์ และวิดเจ็ต React แบบโต้ตอบภายในเซิร์ฟเวอร์ MCP เดียว รวมถึงตัวช่วยตอบกลับสำหรับจัดรูปแบบผลลัพธ์เป็นข้อความ มาร์กดาวน์ HTML รูปภาพ วัตถุ และวิดเจ็ตที่กำหนดเอง มีการประกอบเซิร์ฟเวอร์และการพร็อกซีเพื่อรวมเซิร์ฟเวอร์ MCP หลายตัวเป็นอินเทอร์เฟซเดียว
official
chatgpt-app-builder
mcp-use
สร้างแอป ChatGPT ด้วยวิดเจ็ตแบบโต้ตอบโดยใช้ mcp-use และ OpenAI Apps SDK ใช้เมื่อสร้างแอป ChatGPT, สร้างเซิร์ฟเวอร์ MCP พร้อมวิดเจ็ต, กำหนด...
official