mcp-apps-builder by mcp-use/mcp-use
npx skills add https://github.com/mcp-use/mcp-use --skill mcp-apps-builder本文档仅提供导航指南。在实现任何 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 服务器
└─ 询问用户在何处创建,然后在该目录下搭建脚手架。
请勿在现有不相关项目的根目录内搭建脚手架。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
切勿手动创建 MCPServer 样板代码、package.json 或项目结构。CLI 会设置 TypeScript 配置、开发脚本、检查器集成、热重载和小部件编译,这些很难手动复制。
npx create-mcp-use-app my-server
cd my-server
npm run dev
有关完整的脚手架详情和 CLI 标志,请参阅 quickstart.md。
根据您要构建的内容选择路径:
时机: 在新对话中开始 MCP 工作时,始终先阅读这些内容。之后可参考以澄清架构/概念。
在深入工具/资源/小组件部分之前,请先加载这些内容。
时机: 使用 OAuth(WorkOS、Supabase 或自定义)保护您的服务器
ctx.auth 或选择提供商时oauth 配置、用户上下文结构、提供商比较、常见错误时机: 实现 MCP 功能(操作、数据、模板)。请阅读您要构建的原语对应的具体文件。
text()、object()、markdown()、image()、error()、mix()server.proxy()、配置 API、显式会话、采样路由server.use('mcp:...') 中间件、MiddlewareContext(方法、参数、身份验证、状态)、模式匹配、HTTP 与 MCP 中间件时机: 创建基于 React 的可视化界面,用于浏览、比较或选择数据
useWidget() 钩子、isPending 检查、props 处理useState、setState、状态持久化、何时使用工具状态与小组件状态useCallTool()、表单处理、操作按钮、乐观更新useWidgetTheme()、浅色/深色模式、autoSize、布局模式、CSS 最佳实践<ModelContext> 组件、modelContext.set/remove 命令式 API、嵌套、树序列化、生命周期规则useFiles() 钩子、isSupported 守卫、模型可见性(modelVisible)、存储 fileId、临时下载 URL时机: 您希望查看常见用例的完整实现
您需要什么?
├─ 从零开始的新项目
│ └─> 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)
│
└─ 部署到生产环境
└─> deployment.md(云部署、自托管、Docker)
避免在生产 MCP 服务器中发现的这些反模式:
text()、object()、widget()、error() 助手.describe()
error() 助手
error("message") 进行优雅的错误响应props 而不检查 isPending
if (isPending) return <Loading/>useState 管理自己的 UI 状态McpUseProvider 包装器或 autoSize
<McpUseProvider autoSize>useWidgetTheme() 支持浅色/深色模式process.env.API_KEY,在 .env.example 中记录error()固执己见的架构指南:
将宽泛的操作拆分为专注的工具:
manage-users(太模糊)create-user、delete-user、list-users工具调用成本高昂。避免延迟加载:
list-products + get-product-details(2 次调用)list-products 返回包括详情在内的完整数据UI 状态存在于小组件中,而不是单独的工具中:
select-item 工具、set-filter 工具useState 或 setState 管理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(失去类型安全)
如有疑问,添加一个小组件。可视化 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)每周安装量
6.2K
代码仓库
GitHub 星标
9.5K
首次出现
2026 年 2 月 17 日
安全审计
安装于
codex6.2K
cursor6.2K
claude-code6.2K
opencode209
gemini-cli208
github-copilot205
This file provides a NAVIGATION GUIDE ONLY. Before implementing any MCP server features, you MUST:
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.
Comprehensive guide for building production-ready MCP servers with tools, resources, prompts, and widgets using mcp-use.
Before doing anything else, determine whether you are inside an existing mcp-use project.
Detection: Check the workspace for a package.json that lists "mcp-use" as a dependency, OR any .ts file that imports from "mcp-use/server".
├─ mcp-use project FOUND → Do NOT scaffold. You are already in a project.
│ └─ Skip to "Quick Navigation" below to add features.
│
├─ NO mcp-use project (empty dir, unrelated project, or greenfield)
│ └─ Scaffold first with npx create-mcp-use-app, then add features.
│ See "Scaffolding a New Project" below.
│
└─ Inside an UNRELATED project (e.g. Next.js app) and user wants an MCP server
└─ Ask the user where to create it, then scaffold in that directory.
Do NOT scaffold inside an existing unrelated project root.
NEVER manually createMCPServer boilerplate, package.json, or project structure by hand. The CLI sets up TypeScript config, dev scripts, inspector integration, hot reload, and widget compilation that are difficult to replicate manually.
npx create-mcp-use-app my-server
cd my-server
npm run dev
For full scaffolding details and CLI flags, see quickstart.md.
Choose your path based on what you're building:
When: ALWAYS read these first when starting MCP work in a new conversation. Reference later for architecture/concept clarification.
Load these before diving into tools/resources/widgets sections.
When: Protecting your server with OAuth (WorkOS, Supabase, or custom)
ctx.auth, or choosing a provideroauth config, user context shape, provider comparison, common mistakesWhen: Implementing MCP features (actions, data, templates). Read the specific file for the primitive you're building.
text(), object(), , , , When: Creating React-based visual interfaces for browsing, comparing, or selecting data
useWidget() hook, isPending checks, props handlinguseState, setState, state persistence, when to use tool vs widget stateuseCallTool(), form handling, action buttons, optimistic updatesWhen: You want to see full implementations of common use cases
What do you need?
├─ New project from scratch
│ └─> quickstart.md (scaffolding + setup)
│
├─ OAuth / user authentication
│ └─> authentication/overview.md → provider-specific guide
│
├─ 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
│
├─ Cross-cutting logic (logging, auth checks, rate limiting, tool filtering)
│ └─> Use Middleware: architecture.md#mcp-middleware
│
├─ Visual/interactive UI
│ └─> Use Widget: widgets/basics.md
│
├─ Keep model aware of what user is seeing in widget
│ └─> widgets/model-context.md
├─ Upload/download files in a widget
│ └─> widgets/files.md (ChatGPT Apps SDK only)
│
└─ Deploy to production
└─> deployment.md (cloud deploy, self-hosting, Docker)
Avoid these anti-patterns found in production MCP servers:
text(), object(), widget(), error() helpers.describe() on every field
error() helper
error("message") for graceful error responsesprops without checking isPending
if (isPending) return <Loading/>useStateMcpUseProvider wrapper or autoSize
<McpUseProvider autoSize>useWidgetTheme() for light/dark mode supportprocess.env.API_KEY, document in .env.exampleerror() on failureOpinionated architectural guidelines:
Split broad actions into focused tools:
manage-users (too vague)create-user, delete-user, list-usersTool calls are expensive. Avoid lazy-loading:
list-products + get-product-details (2 calls)list-products returns full data including detailsUI state lives in the widget, not in separate tools:
select-item tool, set-filter tooluseState or setStateexposeAsTool Defaults to falseWidgets are registered as resources only by default. Use a custom tool (recommended) or set exposeAsTool: true to expose a widget to the model:
// ✅ 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)
When in doubt, add a widget. Visual UI improves:
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();
| Helper | Use When | Example |
|---|---|---|
text() | Simple string response | text("Success!") |
object() | Structured data | object({ status: "ok" }) |
markdown() | Formatted text | markdown("# Title\nContent") |
widget() |
Server methods:
server.tool() - Define executable toolserver.resource() - Define static/dynamic resourceserver.resourceTemplate() - Define parameterized resourceserver.prompt() - Define prompt templateserver.proxy() - Compose/Proxy multiple MCP serversserver.uiResource() - Define widget resourceserver.listen() - Start serverserver.use('mcp:tools/call', fn) - MCP middleware (tools, resources, prompts, list ops)server.use('mcp:*', fn) - Catch-all MCP middlewareWeekly Installs
6.2K
Repository
GitHub Stars
9.5K
First Seen
Feb 17, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
codex6.2K
cursor6.2K
claude-code6.2K
opencode209
gemini-cli208
github-copilot205
99,500 周安装
markdown()image()error()mix()server.proxy(), config API, explicit sessions, sampling routingserver.use('mcp:...') middleware, MiddlewareContext (method, params, auth, state), pattern matching, HTTP vs MCP middlewareuseWidgetTheme(), light/dark mode, autoSize, layout patterns, CSS best practices<ModelContext> component, modelContext.set/remove imperative API, nesting, tree serialization, lifecycle rulesuseFiles() hook, isSupported guard, model visibility (modelVisible), storing fileId, temporary download URLs| Visual UI |
widget({ props: {...}, output: text(...) }) |
mix() | Multiple contents | mix(text("Hi"), image(url)) |
error() | Error responses | error("Failed to fetch data") |
resource() | Embed resource refs | resource("docs://guide", "text/markdown") |
server.use(fn) - HTTP middleware (Hono)