
mcp-apps-builder
热门**所有MCP服务器工作的必读内容**——mcp-use框架最佳实践与模式。 **在进行任何MCP服务器工作之前,请先阅读本文**,包括: - 创建新的MCP服务器 - 修改现有的MCP服务器(添加/更新工具、资源、提示、组件) - 调试MCP服务器问题或错误 - 审查MCP服务器代码的质量、安全性或性能 - 回答关于MCP开发或mcp-use模式的问题 - 对server.tool()、server.resource()、server.prompt()或组件进行任何更改 本技能包含关键的架构决策、安全模式和常见陷阱。 在实现MCP功能之前,请务必查阅相关的参考文件。
**所有MCP服务器工作的必读内容**——mcp-use框架最佳实践与模式。 **在进行任何MCP服务器工作之前,请先阅读本文**,包括: - 创建新的MCP服务器 - 修改现有的MCP服务器(添加/更新工具、资源、提示、组件) - 调试MCP服务器问题或错误 - 审查MCP服务器代码的质量、安全性或性能 - 回答关于MCP开发或mcp-use模式的问题 - 对server.tool()、server.resource()、server.prompt()或组件进行任何更改 本技能包含关键的架构决策、安全模式和常见陷阱。 在实现MCP功能之前,请务必查阅相关的参考文件。
重要:如何使用本技能
本文件仅提供导航指南。在实现任何MCP服务器功能之前,您必须:
- 阅读此概述以了解哪些参考文件相关
- 始终阅读您要实现的功能的特定参考文件
- 将那些文件中的详细模式应用到您的实现中
不要仅依赖本文件中的快速参考示例——它们只是最小示例。参考文件包含关键的最佳实践、安全考虑和高级模式。
MCP服务器最佳实践
使用mcp-use构建生产就绪的MCP服务器(包含工具、资源、提示和组件)的综合指南。
⚠️ 首先:新项目还是现有项目?
在开始任何操作之前,请确定您是否在现有的mcp-use项目中。
检测: 检查工作区中是否有将"mcp-use"列为依赖项的package.json,或者是否有从"mcp-use/server"导入的.ts文件。
├─ 找到mcp-use项目 → 不要搭建脚手架。您已经在项目中。
│ └─ 跳转到下面的“快速导航”以添加功能。
│
├─ 没有mcp-use项目(空目录、不相关的项目或全新项目)
│ └─ 首先使用npx create-mcp-use-app搭建脚手架,然后添加功能。
│ 参见下面的“搭建新项目”。
│
└─ 在不相关的项目中(例如Next.js应用),用户想要一个MCP服务器
└─ 询问用户在哪里创建,然后在该目录中搭建脚手架。
不要在现有不相关项目的根目录内搭建脚手架。
永远不要手动创建MCPServer样板、package.json或项目结构。 CLI会设置TypeScript配置、开发脚本、检查器集成、热重载和组件编译,这些很难手动复制。
搭建新项目
npx create-mcp-use-app my-server
cd my-server
npm run dev
有关完整的搭建细节和CLI标志,请参见**quickstart.md**。
快速导航
根据您要构建的内容选择路径:
🚀 基础
何时: 在新对话中开始MCP工作时,始终先阅读这些。稍后参考以澄清架构/概念。
- concepts.md - MCP原语(工具、资源、提示、组件)以及何时使用每种
- architecture.md - 服务器结构(基于Hono)、中间件系统、server.use() vs server.app
- quickstart.md - 搭建、设置和第一个工具示例
- deployment.md - 部署到Manufact Cloud、自托管、Docker、管理部署
在深入工具/资源/组件部分之前,请先加载这些内容。
🔐 添加身份验证?
何时: 使用OAuth保护您的服务器(Auth0、Better Auth、Clerk、WorkOS、Supabase、Keycloak或任何其他提供商)
-
- 何时:首次添加身份验证、理解
ctx.auth或选择提供商/集成模式 - 涵盖:远程身份验证与OAuth代理、
oauth配置、ctx.auth形状、提供商比较、常见错误
- 何时:首次添加身份验证、理解
-
- 何时:使用Auth0——DCR(早期访问)或通过
oauthProxy的标准常规Web应用 - 涵盖:两种模式的设置、
extraAuthorizeParams.audience、通过rfc9068_profile_authz的权限
- 何时:使用Auth0——DCR(早期访问)或通过
-
- 何时:使用带有
@better-auth/oauth-provider插件的Better Auth(自托管OAuth 2.1) - 涵盖:
oauthBetterAuthProvider、身份验证URL/元数据路由、登录和同意流程
- 何时:使用带有
-
- 何时:使用Clerk(基于DCR的OAuth)
- 涵盖:
oauthClerkProvider、启用DCR、前端API URL、组织上下文
-
- 何时:使用WorkOS AuthKit(仅DCR)
- 涵盖:设置、环境变量、角色/权限、多租户组织过滤、WorkOS API调用
-
- 何时:使用Supabase的OAuth 2.1服务器
- 涵盖:设置、可发布密钥、ES256与HS256、托管同意UI、RLS感知的SDK调用
-
- 何时:通过原生DCR使用Keycloak
- 涵盖:DCR受信任主机+Web源、受众强制执行、领域与资源角色、用户信息
-
- 何时:任何其他提供商——通过
oauthCustomProvider支持DCR,或通过oauthProxy预注册(Google、GitHub、Okta、Azure AD) - 涵盖:
oauthCustomProvider、oauthProxy+jwksVerifier、提供商示例、不透明令牌验证
- 何时:任何其他提供商——通过
🔧 构建服务器后端(无UI)?
何时: 实现MCP功能(操作、数据、模板)。阅读您要构建的原语的特定文件。
-
- 何时:创建AI可以调用的后端操作(发送邮件、获取数据、创建用户)
- 涵盖:工具定义、模式、注释、上下文、错误处理
-
- 何时:公开客户端可以获取的只读数据(配置、用户配置文件、文档)
- 涵盖:静态资源、动态资源、参数化资源模板、URI补全
-
- 何时:创建用于AI交互的可重用消息模板(代码审查、总结)
- 涵盖:提示定义、参数化、参数补全、提示最佳实践
-
- 何时:格式化工具/资源的响应(文本、JSON、Markdown、图像、错误)
- 涵盖:
text()、object()、markdown()、image()、error()、mix()
-
- 何时:将多个MCP服务器组合成一个统一的聚合服务器
- 涵盖:
server.proxy()、配置API、显式会话、采样路由
-
- 何时:添加跨多个工具/资源的横切逻辑(日志记录、身份验证检查、速率限制、工具过滤)
- 涵盖:
server.use('mcp:...')中间件、MiddlewareContext(方法、参数、身份验证、状态)、模式匹配、HTTP与MCP中间件
🎨 构建可视化组件(交互式UI)?
何时: 创建基于React的可视化界面,用于浏览、比较或选择数据
-
- 何时:创建您的第一个组件或向现有工具添加UI
- 涵盖:组件设置、
useWidget()钩子、isPending检查、props处理
-
- 何时:管理组件内的UI状态(选择、过滤器、标签页)
- 涵盖:
useState、setState、状态持久化、何时使用工具与组件状态
-
- 何时:在组件内添加按钮、表单或调用工具
- 涵盖:
useCallTool()、表单处理、操作按钮、乐观更新
-
- 何时:为组件设置样式以支持主题、响应式布局或可访问性
- 涵盖:
useWidgetTheme()、亮/暗模式、autoSize、布局模式、CSS最佳实践
-
- 何时:构建具有异步数据、错误边界或性能优化的复杂组件
- 涵盖:加载状态、错误处理、记忆化、代码分割
-
- 何时:让AI模型了解用户当前看到的内容(活动标签页、悬停项、选中的产品),而无需工具调用
- 涵盖:
<ModelContext>组件、modelContext.set/remove命令式API、嵌套、树序列化、生命周期规则
-
- 何时:从组件内上传或下载文件(仅限ChatGPT Apps SDK)
- 涵盖:
useFiles()钩子、isSupported守卫、模型可见性(modelVisible)、存储fileId、临时下载URL
📚 需要完整示例?
何时: 您想查看常见用例的完整实现
- common-patterns.md
- 端到端示例:天气应用、待办事项列表、食谱浏览器
- 展示:服务器代码+组件代码+上下文中的最佳实践
🔁 从终端测试(代理反馈循环)
何时: 您想在没有检查器UI的情况下验证工具或组件——这是AI代理迭代MCP服务器的标准流程。
-
mcp-use client— 从终端驱动MCP服务器。在401时自动运行OAuth,将保存的服务器持久化到短名称下,一次性子命令干净退出,因此从框架中生成是安全的。npx mcp-use client connect dev http://localhost:3000/mcp npx mcp-use client dev tools list npx mcp-use client dev tools call get-weather city=Tokyo --screenshot每个服务器命令将保存的名称作为第一个位置参数(
mcp-use client <name> <scope> <action>)——没有“活动会话”。参数使用key=value(对于嵌套值使用key:='<json>')或单个JSON对象。当工具渲染组件时,传递--screenshot以同时保存PNG(默认./<view>-<timestamp>.png,或使用--screenshot-output <path>覆盖)。 -
mcp-use client screenshot— 将组件工具无头渲染为PNG。当您想在不打开检查器的情况下视觉验证组件更改时使用此命令,特别是在循环中调用工具、截图、检查输出并编辑时。两种形式:# 保存服务器形式——重用来自`mcp-use client connect`的身份验证 npx mcp-use client dev screenshot --tool get-weather city=Tokyo \ --width 800 --height 600 --theme light \ --output ./weather.png # 临时形式——内联连接(在需要身份验证的服务器上使用-H添加标头) npx mcp-use client screenshot --mcp http://localhost:3000/mcp \ --tool get-weather city=Tokyo添加
--device-scale-factor 2以获得Retina输出,或使用--cdp-url <ws>加上--inspector <publicly-reachable-url>从没有本地Chrome安装的沙箱驱动远程Chromium(例如Notte)。
两个命令的完整文档位于docs/typescript/client/cli。
决策树
您需要什么?
├─ 从头开始的新项目
│ └─> quickstart.md(搭建+设置)
│
├─ OAuth/用户身份验证
│ └─> authentication/overview.md → 特定提供商指南
│
├─ 简单的后端操作(无UI)
│ └─> 使用工具:server/tools.md
│
├─ 客户端的只读数据
│ └─> 使用资源:server/resources.md
│
├─ 可重用的提示模板
│ └─> 使用提示:server/prompts.md
│
├─ 横切逻辑(日志记录、身份验证检查、速率限制、工具过滤)
│ └─> 使用中间件:architecture.md#mcp-middleware
│
├─ 可视化/交互式UI
│ └─> 使用组件:widgets/basics.md
│
├─ 让模型了解用户在组件中看到的内容
│ └─> widgets/model-context.md
├─ 在组件中上传/下载文件
│ └─> widgets/files.md(仅限ChatGPT Apps SDK)
│
├─ 从终端验证工具或组件(代理反馈循环)
│ └─> 参见上面的“从终端测试”——`mcp-use client`用于工具运行,
│ `mcp-use client <server> screenshot --tool <tool>`用于无头组件PNG
│
└─ 部署到生产环境
└─> deployment.md(云部署、自托管、Docker)
核心原则
- 工具用于操作 - 具有输入/输出的后端操作
- 资源用于数据 - 客户端可以获取的只读数据
- 提示用于模板 - 可重用的消息模板
- 组件用于UI - 在需要时提供可视化界面
- 先使用模拟数据 - 快速原型,稍后连接API
❌ 常见错误
避免在生产MCP服务器中发现的这些反模式:
工具定义
- ❌ 返回原始对象而不是使用响应助手
- ✅ 使用
text()、object()、widget()、error()助手
- ✅ 使用
- ❌ 跳过每个字段的Zod模式
.describe()- ✅ 为所有模式字段添加描述,以便AI更好地理解
- ❌ 没有输入验证或清理
- ✅ 使用Zod验证输入,清理用户提供的数据
- ❌ 抛出错误而不是返回
error()助手- ✅ 使用
error("message")进行优雅的错误响应
- ✅ 使用
组件开发
- ❌ 在未检查
isPending的情况下访问props- ✅ 始终检查
if (isPending) return <Loading/>
- ✅ 始终检查
- ❌ 组件处理服务器状态(过滤器、选择)
- ✅ 组件使用
useState管理自己的UI状态
- ✅ 组件使用
- ❌ 缺少
McpUseProvider包装器或autoSize- ✅ 包装根组件:
<McpUseProvider autoSize>
- ✅ 包装根组件:
- ❌ 没有主题感知的内联样式
- ✅ 使用
useWidgetTheme()支持亮/暗模式
- ✅ 使用
安全与生产
- ❌ 在代码中硬编码API密钥或秘密
- ✅ 使用
process.env.API_KEY,在.env.example中记录
- ✅ 使用
- ❌ 工具处理程序中没有错误处理
- ✅ 包装在try/catch中,失败时返回
error()
- ✅ 包装在try/catch中,失败时返回
- ❌ 没有缓存的昂贵操作
- ✅ 使用TTL缓存API调用、计算
- ❌ 缺少CORS配置
- ✅ 为生产部署配置CORS
🔒 黄金法则
有主见的架构指南:
1. 一个工具 = 一个能力
将广泛的操作拆分为专注的工具:
- ❌
manage-users(太模糊) - ✅
create-user、delete-user、list-users
2. 一次性返回完整数据
工具调用代价高昂。避免懒加载:
- ❌
list-products+get-product-details(2次调用) - ✅
list-products返回包含详细信息的完整数据
3. 组件拥有自己的状态
UI状态存在于组件中,而不是单独的工具中:
- ❌
select-item工具、set-filter工具 - ✅ 组件使用
useState或setState管理
4. exposeAsTool默认为false
组件默认仅注册为资源。使用自定义工具(推荐)或设置exposeAsTool: true以向模型公开组件:
// ✅ 需要所有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. 仅在边界验证
- 信任内部代码和框架保证
- 验证用户输入、外部API响应
- 不要为不可能发生的场景添加错误处理
6. 优先使用组件进行浏览/比较
如有疑问,添加组件。可视化UI改进:
- 浏览多个项目
- 并排比较数据
- 交互式选择工作流
快速参考
最小服务器
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();
响应助手
| 助手 | 使用时机 | 示例 |
|---|---|---|
text() |
简单字符串响应 | text("Success!") |
object() |
结构化数据 | object({ status: "ok" }) |
markdown() |
格式化文本 | markdown("# Title\nContent") |
widget() |
可视化UI | widget({ props: {...}, output: text(...) }) |
mix() |
多个内容 | mix(text("Hi"), image(url)) |
error() |
错误响应 | error("Failed to fetch data") |
resource() |
嵌入资源引用 | resource("docs://guide", "text/markdown") |
服务器方法:
server.tool()- 定义可执行工具server.resource()- 定义静态/动态资源server.resourceTemplate()- 定义参数化资源server.prompt()- 定义提示模板server.proxy()- 组合/代理多个MCP服务器server.uiResource()- 定义组件资源server.listen()- 启动服务器server.use('mcp:tools/call', fn)- MCP中间件(工具、资源、提示、列表操作)server.use('mcp:*', fn)- 捕获所有MCP中间件server.use(fn)- HTTP中间件(Hono)





