mcp-apps-builder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseIMPORTANT: How to Use This Skill
重要提示:如何使用本技能
This file provides a NAVIGATION GUIDE ONLY. Before implementing any MCP server features, you MUST:
- Read this overview to understand which reference files are relevant
- ALWAYS read the specific reference file(s) for the features you're implementing
- Apply the detailed patterns from those files to your implementation
Do NOT rely solely on the quick reference examples in this file - they are minimal examples only. The reference files contain critical best practices, security considerations, and advanced patterns.
本文仅提供导航指引。在实现任何MCP服务器功能前,你必须:
- 阅读本概述以了解哪些参考文件与你的需求相关
- 务必阅读与你要实现的功能对应的具体参考文件
- 将这些文件中的详细模式应用到你的实现中
请勿仅依赖本文中的快速参考示例 - 这些只是最简示例。参考文件中包含关键的最佳实践、安全考量和高级模式。
MCP Server Best Practices
MCP服务器最佳实践
Comprehensive guide for building production-ready MCP servers with tools, resources, prompts, and widgets using mcp-use.
使用mcp-use构建生产级MCP服务器(包含工具、资源、提示词和Widgets)的综合指南。
Quick Navigation
快速导航
Choose your path based on what you're building:
根据你要构建的内容选择对应路径:
🚀 Foundations
🚀 基础部分
When: ALWAYS read these first when starting MCP work in a new conversation. Reference later for architecture/concept clarification.
- concepts.md - MCP primitives (Tool, Resource, Prompt, Widget) and when to use each
- architecture.md - Server structure (Hono-based), middleware system, server.use() vs server.app
- quickstart.md - Basic server setup patterns and first tool example
Load these before diving into tools/resources/widgets sections.
适用场景: 当你在新对话中开始MCP相关工作时,请务必先阅读这些内容。后续可作为架构/概念澄清的参考。
- concepts.md - MCP基础组件(Tool、Resource、Prompt、Widget)及各自的适用场景
- architecture.md - 服务器结构(基于Hono)、中间件系统、server.use()与server.app的区别
- quickstart.md - 基础服务器设置模式与首个Tool示例
在深入工具/资源/Widgets章节前,请先阅读这些内容。
🔧 Building Server Backend (No UI)?
🔧 构建服务器后端(无UI)?
When: Implementing MCP features (actions, data, templates). Read the specific file for the primitive you're building.
-
tools.md
- When: Creating backend actions the AI can call (send-email, fetch-data, create-user)
- Covers: Tool definition, schemas, annotations, context, error handling
-
resources.md
- When: Exposing read-only data clients can fetch (config, user profiles, documentation)
- Covers: Static resources, dynamic resources, parameterized resource templates, URI completion
-
prompts.md
- When: Creating reusable message templates for AI interactions (code-review, summarize)
- Covers: Prompt definition, parameterization, argument completion, prompt best practices
-
response-helpers.md
- When: Formatting responses from tools/resources (text, JSON, markdown, images, errors)
- Covers: ,
text(),object(),markdown(),image(),error()mix()
适用场景: 实现MCP功能(操作、数据、模板)时。请阅读与你要构建的基础组件对应的具体文件。
-
tools.md
- 适用场景:创建AI可调用的后端操作(发送邮件、获取数据、创建用户)
- 涵盖内容:Tool定义、模式、注解、上下文、错误处理
-
resources.md
- 适用场景:暴露客户端可获取的只读数据(配置、用户资料、文档)
- 涵盖内容:静态资源、动态资源、参数化资源模板、URI补全
-
prompts.md
- 适用场景:创建AI交互的可复用消息模板(代码审查、摘要生成)
- 涵盖内容:Prompt定义、参数化、参数补全、Prompt最佳实践
-
response-helpers.md
- 适用场景:格式化工具/资源的响应(文本、JSON、Markdown、图片、错误)
- 涵盖内容:、
text()、object()、markdown()、image()、error()mix()
🎨 Building Visual Widgets (Interactive UI)?
🎨 构建可视化Widgets(交互式UI)?
When: Creating React-based visual interfaces for browsing, comparing, or selecting data
-
basics.md
- When: Creating your first widget or adding UI to an existing tool
- Covers: Widget setup, hook,
useWidget()checks, props handlingisPending
-
state.md
- When: Managing UI state (selections, filters, tabs) within widgets
- Covers: ,
useState, state persistence, when to use tool vs widget statesetState
-
interactivity.md
- When: Adding buttons, forms, or calling tools from within widgets
- Covers: , form handling, action buttons, optimistic updates
callTool()
-
ui-guidelines.md
- When: Styling widgets to support themes, responsive layouts, or accessibility
- Covers: , light/dark mode,
useWidgetTheme(), layout patterns, CSS best practicesautoSize
-
advanced.md
- When: Building complex widgets with async data, error boundaries, or performance optimizations
- Covers: Loading states, error handling, memoization, code splitting
适用场景: 创建基于React的可视化界面,用于浏览、比较或选择数据
-
basics.md
- 适用场景:创建你的首个Widget或为现有工具添加UI
- 涵盖内容:Widget设置、钩子、
useWidget()检查、属性处理isPending
-
state.md
- 适用场景:在Widget内管理UI状态(选择项、过滤器、标签页)
- 涵盖内容:、
useState、状态持久化、何时使用工具状态vs Widget状态setState
-
interactivity.md
- 适用场景:在Widget内添加按钮、表单或调用工具
- 涵盖内容:、表单处理、操作按钮、乐观更新
callTool()
-
ui-guidelines.md
- 适用场景:对Widget进行样式设置以支持主题、响应式布局或无障碍访问
- 涵盖内容:、明暗模式、
useWidgetTheme()、布局模式、CSS最佳实践autoSize
-
advanced.md
- 适用场景:构建包含异步数据、错误边界或性能优化的复杂Widget
- 涵盖内容:加载状态、错误处理、记忆化、代码分割
📚 Need Complete Examples?
📚 需要完整示例?
When: You want to see full implementations of common use cases
- common-patterns.md
- End-to-end examples: weather app, todo list, recipe browser
- Shows: Server code + widget code + best practices in context
适用场景: 你想查看常见用例的完整实现
- common-patterns.md
- 端到端示例:天气应用、待办事项列表、食谱浏览器
- 展示内容:服务器代码 + Widget代码 + 上下文内的最佳实践
Decision Tree
决策树
What do you need to build?
├─ Simple backend action (no UI)
│ └─> Use Tool: server/tools.md
│
├─ Read-only data for clients
│ └─> Use Resource: server/resources.md
│
├─ Reusable prompt template
│ └─> Use Prompt: server/prompts.md
│
└─ Visual/interactive UI
└─> Use Widget: widgets/basics.mdWhat do you need to build?
├─ Simple backend action (no UI)
│ └─> Use Tool: server/tools.md
│
├─ Read-only data for clients
│ └─> Use Resource: server/resources.md
│
├─ Reusable prompt template
│ └─> Use Prompt: server/prompts.md
│
└─ Visual/interactive UI
└─> Use Widget: widgets/basics.mdCore Principles
核心原则
- Tools for actions - Backend operations with input/output
- Resources for data - Read-only data clients can fetch
- Prompts for templates - Reusable message templates
- Widgets for UI - Visual interfaces when helpful
- Mock data first - Prototype quickly, connect APIs later
- 工具用于操作 - 带有输入/输出的后端操作
- 资源用于数据 - 客户端可获取的只读数据
- 提示词用于模板 - 可复用的消息模板
- Widgets用于UI - 必要时使用可视化界面
- 优先使用模拟数据 - 快速原型开发,后续再连接API
❌ Common Mistakes
❌ 常见错误
Avoid these anti-patterns found in production MCP servers:
避免在生产环境MCP服务器中出现这些反模式:
Tool Definition
Tool定义
- ❌ Returning raw objects instead of using response helpers
- ✅ Use ,
text(),object(),widget()helperserror()
- ✅ Use
- ❌ Skipping Zod schema on every field
.describe()- ✅ Add descriptions to all schema fields for better AI understanding
- ❌ No input validation or sanitization
- ✅ Validate inputs with Zod, sanitize user-provided data
- ❌ Throwing errors instead of returning helper
error()- ✅ Use for graceful error responses
error("message")
- ✅ Use
- ❌ 返回原始对象而非使用响应助手
- ✅ 使用、
text()、object()、widget()助手error()
- ✅ 使用
- ❌ 跳过Zod模式中每个字段的
.describe()- ✅ 为所有模式字段添加描述,以便AI更好理解
- ❌ 未进行输入验证或清理
- ✅ 使用Zod验证输入,清理用户提供的数据
- ❌ 抛出错误而非返回助手
error()- ✅ 使用实现优雅的错误响应
error("message")
- ✅ 使用
Widget Development
Widget开发
- ❌ Accessing without checking
propsisPending- ✅ Always check
if (isPending) return <Loading/>
- ✅ Always check
- ❌ Widget handles server state (filters, selections)
- ✅ Widgets manage their own UI state with
useState
- ✅ Widgets manage their own UI state with
- ❌ Missing wrapper or
McpUseProviderautoSize- ✅ Wrap root component:
<McpUseProvider autoSize>
- ✅ Wrap root component:
- ❌ Inline styles without theme awareness
- ✅ Use for light/dark mode support
useWidgetTheme()
- ✅ Use
- ❌ 未检查就访问
isPendingprops- ✅ 始终先检查
if (isPending) return <Loading/>
- ✅ 始终先检查
- ❌ Widget处理服务器状态(过滤器、选择项)
- ✅ Widget使用管理自身UI状态
useState
- ✅ Widget使用
- ❌ 缺少包裹或
McpUseProviderautoSize- ✅ 包裹根组件:
<McpUseProvider autoSize>
- ✅ 包裹根组件:
- ❌ 使用内联样式但未考虑主题
- ✅ 使用支持明暗模式
useWidgetTheme()
- ✅ 使用
Security & Production
安全与生产环境
- ❌ Hardcoded API keys or secrets in code
- ✅ Use , document in
process.env.API_KEY.env.example
- ✅ Use
- ❌ No error handling in tool handlers
- ✅ Wrap in try/catch, return on failure
error()
- ✅ Wrap in try/catch, return
- ❌ Expensive operations without caching
- ✅ Cache API calls, computations with TTL
- ❌ Missing CORS configuration
- ✅ Configure CORS for production deployments
- ❌ 在代码中硬编码API密钥或机密信息
- ✅ 使用,在
process.env.API_KEY中记录.env.example
- ✅ 使用
- ❌ 工具处理函数中未添加错误处理
- ✅ 包裹在try/catch中,失败时返回
error()
- ✅ 包裹在try/catch中,失败时返回
- ❌ 未对耗时操作进行缓存
- ✅ 对API调用、计算结果添加TTL缓存
- ❌ 缺少CORS配置
- ✅ 为生产部署配置CORS
🔒 Golden Rules
🔒 黄金法则
Opinionated architectural guidelines:
带有倾向性的架构指南:
1. One Tool = One Capability
1. 一个工具对应一项能力
Split broad actions into focused tools:
- ❌ (too vague)
manage-users - ✅ ,
create-user,delete-userlist-users
将宽泛的操作拆分为专注的工具:
- ❌ (过于模糊)
manage-users - ✅ 、
create-user、delete-userlist-users
2. Return Complete Data Upfront
2. 提前返回完整数据
Tool calls are expensive. Avoid lazy-loading:
- ❌ +
list-products(2 calls)get-product-details - ✅ returns full data including details
list-products
工具调用成本较高,避免懒加载:
- ❌ +
list-products(两次调用)get-product-details - ✅ 返回包含详情的完整数据
list-products
3. Widgets Own Their State
3. Widget管理自身状态
UI state lives in the widget, not in separate tools:
- ❌ tool,
select-itemtoolset-filter - ✅ Widget manages with or
useStatesetState
UI状态存放在Widget中,而非单独的工具:
- ❌ 工具、
select-item工具set-filter - ✅ Widget使用或
useState管理setState
4. Use exposeAsTool: false
for Custom Widget Tools
exposeAsTool: false4. 为自定义Widget工具设置exposeAsTool: false
exposeAsTool: falsePrevent duplicate tool registration:
typescript
// ✅ ALL 4 STEPS REQUIRED for proper type inference:
// Step 1: Define schema separately
const propsSchema = z.object({
title: z.string(),
items: z.array(z.string())
});
// Step 2: Reference schema variable in metadata
export const widgetMetadata: WidgetMetadata = {
description: "...",
props: propsSchema, // ← NOT inline z.object()
exposeAsTool: false
};
// Step 3: Infer Props type from schema variable
type Props = z.infer<typeof propsSchema>;
// Step 4: Use typed Props with useWidget
export default function MyWidget() {
const { props, isPending } = useWidget<Props>(); // ← Add <Props>
// ...
}⚠️ Common mistake: Only doing steps 1-2 but skipping 3-4 (loses type safety)
避免重复注册工具:
typescript
// ✅ 正确类型推断所需的4个步骤:
// 步骤1:单独定义模式
const propsSchema = z.object({
title: z.string(),
items: z.array(z.string())
});
// 步骤2:在元数据中引用模式变量
export const widgetMetadata: WidgetMetadata = {
description: "...",
props: propsSchema, // ← 不要内联z.object()
exposeAsTool: false
};
// 步骤3:从模式变量推断Props类型
type Props = z.infer<typeof propsSchema>;
// 步骤4:将带类型的Props与useWidget一起使用
export default function MyWidget() {
const { props, isPending } = useWidget<Props>(); // ← 添加<Props>
// ...
}⚠️ 常见错误: 只完成步骤1-2但跳过步骤3-4(失去类型安全性)
5. Validate at Boundaries Only
5. 仅在边界处验证
- Trust internal code and framework guarantees
- Validate user input, external API responses
- Don't add error handling for scenarios that can't happen
- 信任内部代码和框架保证
- 验证用户输入、外部API响应
- 不要为不可能发生的场景添加错误处理
6. Prefer Widgets for Browsing/Comparing
6. 优先使用Widget进行浏览/比较
When in doubt, add a widget. Visual UI improves:
- Browsing multiple items
- Comparing data side-by-side
- Interactive selection workflows
如有疑问,添加Widget。可视化UI可提升:
- 多项目浏览体验
- 数据并排比较
- 交互式选择流程
Quick Reference
快速参考
Minimal Server
最简服务器
typescript
import { MCPServer, text } from "mcp-use/server";
import { z } from "zod";
const server = new MCPServer({
name: "my-server",
title: "My Server",
version: "1.0.0"
});
server.tool(
{
name: "greet",
description: "Greet a user",
schema: z.object({ name: z.string().describe("User's name") })
},
async ({ name }) => text("Hello " + name + "!"),
);
server.listen();typescript
import { MCPServer, text } from "mcp-use/server";
import { z } from "zod";
const server = new MCPServer({
name: "my-server",
title: "My Server",
version: "1.0.0"
});
server.tool(
{
name: "greet",
description: "Greet a user",
schema: z.object({ name: z.string().describe("User's name") })
},
async ({ name }) => text("Hello " + name + "!"),
);
server.listen();Response Helpers
响应助手
| Helper | Use When | Example |
|---|---|---|
| Simple string response | |
| Structured data | |
| Formatted text | |
| Visual UI | |
| Multiple contents | |
| Error responses | |
| Embed resource refs | |
| 助手函数 | 适用场景 | 示例 |
|---|---|---|
| 简单字符串响应 | |
| 结构化数据 | |
| 格式化文本 | |
| 可视化UI | |
| 多内容组合 | |
| 错误响应 | |
| 嵌入资源引用 | |