使用 mcp-builder Skill 构建生产级 MCP Server:mcp-use 框架下 Tools、Resources、Prompts 与交互式 Widget 完整实战指南
2026/9/11 8:03:27 网站建设 项目流程

使用 mcp-builder Skill 构建生产级 MCP Server:mcp-use 框架下 Tools、Resources、Prompts 与交互式 Widget 完整实战指南

【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit

本指南以 CopilotKit 仓库中examples/showcases/open-mcp-client/apps/mcp-use-server示例应用所内置的mcp-builderAgent Skill 为核心,系统讲解如何基于mcp-use框架搭建生产可用的 Model Context Protocol(MCP)服务端。你将掌握从需求拆解、API 设计、server.tool()/server.resource()/server.prompt()服务端实现,到响应助手函数、React Widget 交互界面与参数化资源模板的完整技术栈,并能直接对照仓库内真实可运行的product-search示例落地自己的 MCP 应用。

说明:本文主体内容对应 Skill 文档examples/showcases/open-mcp-client/apps/mcp-use-server/.agent/skills/mcp-builder/SKILL.md,其底层细节来自同目录references/下的五份参考文档。该 Skill 的 frontmatter 标注为DEPRECATED(已废弃),官方建议改用mcp-app-builder(本仓库中的对应实现位于examples/showcases/open-mcp-client/apps/mcp-use-server/.agent/skills/mcp-apps-builder/),但其中关于 mcp-use 服务端开发的核心 API 与设计方法论仍然完全适用,因此本文仍以其为骨架展开。

一、Skill 定位:写代码之前先想清楚“拆什么”

mcp-builder是一条面向 MCP 服务端开发的 Agent Skill,它的核心主张是:先拆解用户需求,再写代码。它把 MCP 服务端的能力抽象为四类 MCP 原语(primitive):

原语角色对应 API
Tool(工具)AI 模型可调用的后端动作,接收输入并返回数据server.tool()
Widget tool(组件工具)返回可视化 UI 的工具,本质仍是server.tool(),但携带widget配置并指向resources/下的 React 组件server.tool()+widget()
Resource(资源)客户端可读取的只读数据server.resource()/server.resourceTemplate()
Prompt(提示词)可复用的消息模板server.prompt()

Skill 的三步拆解法在 design-and-architecture.md 中有完整展开:

  1. 确定要构建什么:从用户请求中抽取核心动作,只做用户要求的,不要发明额外功能。例如"weather app"对应"获取当前天气、获取预报";"todo list"对应"添加、列出、完成、删除";"recipe finder"对应"搜索食谱、获取食谱详情";"translator"对应"翻译文本、检测语言";"stock tracker"对应"获取股价、对比股票";"quiz app"对应"生成测验、检查答案"。
  2. 判断是否需要 Widget:浏览/对比多个条目(搜索结果、商品卡片)、视觉数据能提升理解(图表、地图、图片、仪表盘)、可视化交互选择更方便(座位选择器、日历、取色器)→ 用 Widget;纯文本输出(翻译、计算、状态检查)、输入天然适合对话(日期、金额、描述)、没有任何视觉元素能带来帮助 → 只用 Tool。文档给出的原则是"犹豫时就上 Widget",因为它通常能显著改善体验。
  3. 设计 API:工具与 Widget 命名以动词开头(get-weathersearch-recipesadd-todotranslate-text);一个工具只做一个聚焦能力(❌manage-todos→ ✅add-todo/list-todos/complete-todo/delete-todo);一个流程只对应一个 Widget(❌search-recipesWidget +view-recipeWidget → ✅ 合并为一个search-recipesWidget,列表与详情都在其中);不做懒加载(工具调用成本高,一次性返回全部数据,❌search-recipes+get-recipe-details→ ✅search-recipes直接返回完整食谱数据);Widget 自己管理状态(选择、筛选、UI 状态放在 Widget 内部,❌select-recipe/set-filter工具 → ✅ Widget 内部用useState管理)。

当用户没有指定真实 API 时,Skill 还给出了Mock 数据策略:使用真实名称(城市、食谱、产品名,而非 "Example 1")、加入轻微随机化让数据显得动态、按真实 API 的返回结构组织数据、并显式注释// Mock data - replace with real API

二、快速上手:最小可运行 MCP Server

Skill 的 Quick Reference 给出了一个覆盖 Tool、Resource、Prompt 三类原语的完整最小实现:

import { MCPServer, text, object, markdown, html, image, widget, error, } from "mcp-use/server"; import { z } from "zod"; const server = new MCPServer({ name: "my-server", version: "1.0.0" }); // Tool server.tool( { name: "my-tool", description: "...", schema: z.object({ param: z.string().describe("...") }), }, async ({ param }) => text("result"), ); // Resource server.resource( { uri: "config://settings", name: "Settings", mimeType: "application/json" }, async () => object({ key: "value" }), ); // Prompt server.prompt( { name: "my-prompt", description: "...", schema: z.object({ topic: z.string() }), }, async ({ topic }) => text(`Write about ${topic}`), ); server.listen();

关键 API 一览:

  • 响应助手text()object()markdown()html()image()audio()binary()error()mix()widget()
  • 服务端方法server.tool()server.resource()server.resourceTemplate()server.prompt()server.listen()

在仓库的真实示例 index.ts 中,服务端配置会显式传入更多元信息(titledescriptionbaseUrlfaviconwebsiteUrlicons),并通过环境变量控制地址与端口:

const server = new MCPServer({ name: "mcp-use-server", title: "mcp-use-server", version: "1.0.0", description: "MCP server with MCP Apps integration", baseUrl: process.env.MCP_URL || "http://localhost:3109", favicon: "favicon.ico", websiteUrl: "https://mcp-use.com", icons: [{ src: "icon.svg", mimeType: "image/svg+xml", sizes: ["512x512"] }], }); server.listen(parseInt(process.env.PORT ?? "3109", 10)).then(() => { console.log(`Server running on port ${process.env.PORT ?? "3109"}`); });

从 package.json 可以看到该示例基于mcp-use@^1.22.3react@^19.2.4zod@4.3.5,并通过npm run devmcp-use build --inline && cross-env NODE_ENV=production npx tsx index.ts)在开发时自动重建 Widget 并重启服务。

三、服务端实现详解:Tools、Resources 与 Prompts

references/tools-and-resources.md 完整讲解了服务端三类原语的实现模式。

3.1 Tool:AI 模型可调用的动作

基础工具在配置对象中声明namedescriptionschema(Zod 描述输入),回调函数解构输入并返回响应:

import { MCPServer, text, object, error } 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: "translate-text", description: "Translate text between languages", schema: z.object({ text: z.string().describe("Text to translate"), targetLanguage: z .string() .describe("Target language (e.g., 'Spanish', 'French')"), sourceLanguage: z .string() .optional() .describe("Source language (auto-detected if omitted)"), }), }, async ({ text: inputText, targetLanguage, sourceLanguage }) => { const translated = await translateAPI( inputText, targetLanguage, sourceLanguage, ); return text(`${translated}`); }, );
3.1.1 Tool 注释(Annotations)

通过annotations声明工具的性质,帮助客户端与模型判断调用风险:

server.tool( { name: "delete-item", description: "Delete an item permanently", schema: z.object({ id: z.string().describe("Item ID") }), annotations: { destructiveHint: true, // 删除或覆盖数据 readOnlyHint: false, // 有副作用 openWorldHint: false, // 不超出用户账户范围 }, }, async ({ id }) => { await deleteItem(id); return text(`Item ${id} deleted.`); }, );
3.1.2 Tool 上下文(Context)

回调的第二个参数ctx提供高级能力:ctx.reportProgress?.(0, 100, "Starting...")上报进度、ctx.log("info", ...)结构化日志、ctx.client.can("sampling")探测客户端能力并配合ctx.sample()请求 LLM 协助处理。

3.1.3 结构化输出(outputSchema)

outputSchema声明经过类型校验的输出,配合object()返回:

server.tool( { name: "get-stats", schema: z.object({ period: z.string() }), outputSchema: z.object({ total: z.number(), average: z.number(), trend: z.enum(["up", "down", "flat"]), }), }, async ({ period }) => { return object({ total: 150, average: 42.5, trend: "up" }); }, );

仓库示例 tools/product-search.ts 即采用此模式:search-tools是触发 Widget UI 的工具,get-fruit-details则声明了outputSchemafruit: stringcolor: stringfacts: string[]),作为 Widget 内部通过工具调用获取详情的"数据工具"。该文件还展示了_meta"ui/previewData"的用法——在尚无真实调用时为 UI Studio 提供预览数据。

3.2 Resource:客户端可读取的只读数据

静态资源通过固定uri暴露:

server.resource( { uri: "config://settings", name: "Application Settings", description: "Current server configuration", mimeType: "application/json", }, async () => object({ theme: "dark", version: "1.0.0", language: "en" }), ); server.resource( { uri: "docs://guide", name: "User Guide", mimeType: "text/markdown", }, async () => markdown("# User Guide\n\nWelcome to the app!"), );

动态资源则在回调中实时取数后返回(stats://current示例)。参数化资源使用server.resourceTemplate(),详见本文第六节。

3.3 Prompt:可复用的消息模板

server.prompt( { name: "code-review", description: "Generate a code review for the given language", schema: z.object({ language: z.string().describe("Programming language"), focusArea: z.string().optional().describe("Specific area to focus on"), }), }, async ({ language, focusArea }) => { const focus = focusArea ? ` Focus on ${focusArea}.` : ""; return text( `Please review this ${language} code for best practices and potential issues.${focus}`, ); }, );

3.4 Zod Schema 最佳实践

Zod schema 直接决定模型能拿到多好的输入描述,因此要求:

  • 每个字段都加.describe()——这是模型理解参数的唯一途径;
  • 非必填字段用.optional()
  • 适当添加校验(.min().max().enum());
  • 有固定取值集合时用z.enum()而非z.string()
// 好:描述清晰且有约束 const schema = z.object({ city: z.string().describe("City name (e.g., 'New York', 'Tokyo')"), units: z .enum(["celsius", "fahrenheit"]) .optional() .describe("Temperature units"), limit: z.number().min(1).max(50).optional().describe("Max results to return"), }); // 坏:无描述 const schema = z.object({ city: z.string(), units: z.string(), limit: z.number(), });

3.5 错误处理与环境变量

错误处理统一走error()响应(返回前捕获异常),例如fetchFromAPI返回空时return error(\No data found for ID: ${id}`),异常时return error(`Failed to fetch data: ${...}`)`。

需要 API Key 时从环境变量读取,未配置时直接返回明确错误提示;同时创建.env.example记录所有必需变量:

const API_KEY = process.env.WEATHER_API_KEY; server.tool( { name: "get-weather", schema: z.object({ city: z.string() }) }, async ({ city }) => { if (!API_KEY) { return error( "WEATHER_API_KEY not configured. Please set it in the Env tab.", ); } // ... }, );

3.6 自定义 HTTP 路由与启动

MCPServer继承自 Hono,因此可以直接挂自定义 HTTP 端点:

server.get("/api/health", (c) => c.json({ status: "ok" })); server.post("/api/webhook", async (c) => { const body = await c.req.json(); return c.json({ received: true }); });

启动逻辑:

const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000; server.listen(PORT);

四、响应助手 API 全参考

references/response-helpers.md 是响应助手的完整参考,所有助手均从mcp-use/server导入:

import { text, object, markdown, html, image, audio, binary, error, mix, widget, resource, } from "mcp-use/server";
助手返回类型适用场景
text(str)纯文本简单文本响应(支持多行模板字符串)
object(data)JSON结构化数据(支持嵌套对象)
markdown(str)Markdown带标题、列表、代码块的格式化文本
html(str)HTML富 HTML 内容
image(data, mime?)图片base64 数据、Buffer 或文件路径
audio(data, mime?)音频base64 数据、Buffer 或文件路径
binary(data, mime)二进制PDF、ZIP 等
error(msg)错误操作失败(会置isError: true通知模型)
resource(uri, content)资源在工具响应中内嵌资源引用
mix(...results)组合一个响应中包含多种内容类型
widget({ props, output })Widget交互式 UI + 提供给模型的内容

几个需要特别注意的用法:

  • 图片/音频:三种入参形式——image(base64Data, "image/png")image(imageBuffer, "image/jpeg")await image("/path/to/image.png")(文件路径形式是异步的,需await);audio同理(audio/wavaudio/mp3、文件路径)。
  • 内嵌资源:两参形式resource("report://analysis-123", text("Full report content here...")),三参形式resource("data://export", "application/json", '{"items": [1, 2, 3]}')
  • 混合响应mix(text("Analysis complete:"), object({ score: 95 }), markdown("## Recommendations\n- Optimize query")),也可以混合resource(...)内嵌资源。

4.1 Widget 响应

工具返回交互界面时使用widget(),注意区分props(传给 Widget UI,模型不可见)output(模型看到的文本)

server.tool( { name: "show-data", schema: z.object({ query: z.string() }), widget: { name: "data-display", // resources/ 下的 Widget 名 invoking: "Loading...", // 工具执行期间的提示文本 invoked: "Data loaded", // 工具完成后的提示文本 }, }, async ({ query }) => { const data = await fetchData(query); return widget({ props: { items: data.items, query, total: data.total }, output: text(`Found ${data.total} results for "${query}"`), message: `Displaying ${data.total} results`, // 可选 }); }, );

widget()的字段为props(传给组件的数据)、output(模型看到的内容)、message(可选文本);工具配置widget的字段为nameresources/下的文件名)、invokinginvoked

4.2 自动补全(completable)

对 prompt 参数与资源模板参数提供补全候选,支持静态列表与动态回调:

import { completable } from "mcp-use/server"; // 静态列表 server.prompt( { name: "code-review", schema: z.object({ language: completable(z.string(), ["python", "typescript", "go", "rust", "java"]), }), }, async ({ language }) => text(`Review this ${language} code.`), ); // 动态回调 server.prompt( { name: "get-user", schema: z.object({ username: completable(z.string(), async (value) => { const users = await searchUsers(value); return users.map((u) => u.name); }), }), }, async ({ username }) => text(`Get info for ${username}`), );

五、Widget 实战:用 React 构建交互式 UI

references/widgets.md 是 Widget 开发的完整指南。其工作机制为:在resources/下创建 React 组件 → 组件导出widgetMetadata(描述 + props 的 Zod schema)和默认 React 组件 → mcp-use 自动注册为工具与资源 → 工具被调用时 Widget 以工具输出数据渲染。

5.1 文件组织与命名

  • 单文件resources/weather-display.tsx→ Widget 名weather-display
  • 文件夹(复杂 Widget)resources/product-search/下必须含widget.tsx作为入口(组件名固定为widget.tsx),其余components/hooks/types.ts自由组织
  • 文件名/文件夹名即 Widget 名,统一使用 kebab-case

仓库的真实 Widget 位于 resources/product-search-result/widget.tsx,对应的工具注册在 tools/product-search.ts,其中widget.name必须与resources/下的文件夹名product-search-result完全一致。

5.2 Widget 文件的两个必导出项

每个 Widget 文件必须导出:

  1. widgetMetadata——含description(展示内容说明)与props(Zod schema)的对象;
  2. 默认 React 组件——UI 实现。

WidgetMetadata完整字段:

字段类型必填说明
descriptionstringWidget 展示什么
propsz.ZodObjectWidget 输入数据的 Zod schema
exposeAsToolboolean是否自动注册为工具(默认false
toolOutputCallToolResult \| (params => CallToolResult)模型在自动注册工具被调用时看到的内容
titlestring展示标题
annotationsobjectreadOnlyHintdestructiveHint
metadataobjectCSP、边框、尺寸、调用状态文本等配置
metadata.invokingstring工具运行期间的状态文本(inspector 中以 shimmer 展示,默认"Loading {name}..."
metadata.invokedstring工具完成后的状态文本(默认"{name} ready"

关于exposeAsTool的重要设计:Widget 默认只注册为 MCP 资源,不自动注册为工具。当你在工具配置中写了widget: { name: "my-widget" }时,省略exposeAsTool是正确的做法——由自定义工具负责使 Widget 可被调用;只有不写自定义工具、希望 Widget 被自动注册为工具时才设置exposeAsTool: true

toolOutput用于控制自动注册工具被调用时模型看到的内容:

export const widgetMetadata: WidgetMetadata = { description: "Recipe card", props: z.object({ name: z.string(), ingredients: z.array(z.string()) }), toolOutput: (params) => text( `Showing recipe: ${params.name} (${params.ingredients.length} ingredients)`, ), };

5.3useWidgetHook:Widget 的数据与能力入口

useWidget是访问 Widget 数据与能力的主 Hook:

const { // 核心数据 props, // Widget 输入数据 isPending, // 工具仍在执行时为 true(props 可能不完整) toolInput, // 原始工具输入参数 output, // 额外工具输出数据 metadata, // 响应元数据 // 持久状态 state, // 跨渲染持久化的 Widget 状态 setState, // 更新持久状态:setState(newState) 或 setState(prev => newState) // 宿主环境 theme, // 'light' | 'dark' displayMode, // 'inline' | 'pip' | 'fullscreen' safeArea, // { insets: { top, bottom, left, right } } maxHeight, // 可用最大高度(像素) userAgent, // { device: { type }, capabilities: { hover, touch } } locale, // 用户区域设置(如 'en-US') timeZone, // IANA 时区 // 动作 callTool, // 调用另一个 MCP 工具:callTool("tool-name", { args }) sendFollowUpMessage, // 触发 LLM 响应:sendFollowUpMessage("analyze this") openExternal, // 打开外部 URL requestDisplayMode, // 请求切换显示模式:requestDisplayMode("fullscreen") mcp_url, // MCP 服务端 base URL,用于自定义 API 请求 } = useWidget();

加载态处理是硬性要求:Widget 在工具执行完成前就会渲染,因此必须处理isPending,否则在props不完整时直接解构会出错。正确写法是先用McpUseProvider autoSize包裹渲染 Loading 占位,待isPending为 false 后再安全使用props

Widget 内调用其他工具(仓库product-search场景中 Widget 内调用get-fruit-details即此模式):

const { callTool } = useWidget(); const handleRefresh = async () => { try { const result = await callTool("get-weather", { city: "Tokyo" }); console.log(result.content); } catch (err) { console.error("Tool call failed:", err); } };

触发 LLM 继续对话

const { sendFollowUpMessage } = useWidget(); <button onClick={() => sendFollowUpMessage("Compare the weather in these cities")}> Ask AI to Compare </button>;

持久状态

const { state, setState } = useWidget(); await setState({ favorites: [...(state?.favorites || []), city] }); await setState((prev) => ({ ...prev, count: (prev?.count || 0) + 1 }));

5.4 便捷 Hooks 与 McpUseProvider

简单场景可直接使用便捷 Hooks:useWidgetProps<MyProps>()只取 props、useWidgetTheme()只取主题、useWidgetState<MyState>({ count: 0 })提供类似useState的体验。

Widget 内容必须包裹在McpUseProvider中,常用属性:autoSize(自动调整高度以适应内容,默认false)、viewControlsboolean | "pip" | "fullscreen",显示显示模式控制按钮,默认false)、debugger(显示调试 inspector 覆盖层,默认false)。

样式上内联样式与 Tailwind 均可:<div style={{ padding: 20, borderRadius: 12 }}><div className="p-5 rounded-xl bg-blue-50">

5.5 工具侧widget配置

server.tool({ name: "tool-name", schema: z.object({ ... }), widget: { name: "widget-name", // 必须与 resources/ 下文件名一致 invoking: "Loading...", invoked: "Ready", widgetAccessible: true, // Widget 能否调用其他工具(默认 true) }, }, async (input) => { ... });

5.6 完整端到端示例

index.ts(服务端):

import { MCPServer, widget, text, object } from "mcp-use/server"; import { z } from "zod"; const server = new MCPServer({ name: "recipe-finder", version: "1.0.0", baseUrl: process.env.MCP_URL || "http://localhost:3000", }); const mockRecipes = [ { id: "1", name: "Pasta Carbonara", cuisine: "Italian", time: 30, ingredients: ["pasta", "eggs", "bacon", "parmesan"] }, { id: "2", name: "Chicken Tikka", cuisine: "Indian", time: 45, ingredients: ["chicken", "yogurt", "spices", "rice"] }, { id: "3", name: "Sushi Rolls", cuisine: "Japanese", time: 60, ingredients: ["rice", "nori", "fish", "avocado"] }, ]; server.tool( { name: "search-recipes", description: "Search for recipes by query or cuisine", schema: z.object({ query: z.string().describe("Search query (e.g., 'pasta', 'chicken')"), cuisine: z.string().optional().describe("Filter by cuisine"), }), widget: { name: "recipe-list", invoking: "Searching recipes...", invoked: "Recipes found", }, }, async ({ query, cuisine }) => { const results = mockRecipes.filter( (r) => r.name.toLowerCase().includes(query.toLowerCase()) || (cuisine && r.cuisine.toLowerCase() === cuisine.toLowerCase()), ); return widget({ props: { recipes: results, query }, output: text(`Found ${results.length} recipes for "${query}"`), }); }, ); server.listen();

resources/recipe-list.tsx(Widget):

import { McpUseProvider, useWidget, type WidgetMetadata } from "mcp-use/react"; import { z } from "zod"; export const widgetMetadata: WidgetMetadata = { description: "Display recipe search results", props: z.object({ recipes: z.array( z.object({ id: z.string(), name: z.string(), cuisine: z.string(), time: z.number(), ingredients: z.array(z.string()), }), ), query: z.string(), }), exposeAsTool: false, }; export default function RecipeList() { const { props, isPending } = useWidget(); if (isPending) { return ( <McpUseProvider autoSize> <div style={{ padding: 16 }}>Searching...</div> </McpUseProvider> ); } return ( <McpUseProvider autoSize> <div style={{ padding: 16 }}> <h2 style={{ margin: "0 0 12px" }}>Recipes for "{props.query}"</h2> {props.recipes.length === 0 ? ( <p style={{ color: "#999" }}>No recipes found.</p> ) : ( <div style={{ display: "flex", flexDirection: "column", gap: 12 }}> {props.recipes.map((recipe) => ( <div key={recipe.id} style={{ padding: 16, borderRadius: 8, border: "1px solid #e5e7eb", background: "#fff" }} > <h3 style={{ margin: "0 0 4px" }}>{recipe.name}</h3> <p style={{ margin: 0, color: "#666", fontSize: 14 }}> {recipe.cuisine} · {recipe.time} min · {recipe.ingredients.join(", ")} </p> </div> ))} </div> )} </div> </McpUseProvider> ); }

六、参数化资源:URI 模板模式

references/resource-templates.md 讲解了server.resourceTemplate()的用法。它的回调签名支持直接解构路径参数,且支持多参数与查询参数:

// 单参数:user://123/profile server.resourceTemplate( { uriTemplate: "user://{userId}/profile", name: "User Profile", description: "Get user profile by ID", mimeType: "application/json", }, async ({ userId }) => { const user = await fetchUser(userId); return object(user); }, ); // 多参数:org://acme/team/engineering server.resourceTemplate( { uriTemplate: "org://{orgId}/team/{teamId}", name: "Team Details", }, async ({ orgId, teamId }) => object(await fetchTeam(orgId, teamId)), ); // 可选参数:第二个回调参数可访问 searchParams server.resourceTemplate( { uriTemplate: "file://{path}", name: "File Content", }, async ({ path }, { searchParams }) => { const format = searchParams?.get("format") || "text"; const content = await readFile(path); return format === "json" ? object(content) : text(content); }, );

Skill 推荐的 URI scheme 约定:

Scheme用途示例
config://配置数据config://settingsconfig://env
user://用户相关数据user://{id}/profile
docs://文档docs://apidocs://guide
stats://统计/指标stats://currentstats://daily
file://文件内容file://{path}
db://数据库记录db://users/{id}
api://API 端点api://weather/{city}
ui://UI 组件ui://widget/{name}.html

完整的资源服务器示例(静态资源 + 参数化模板 + 嵌套模板 + 文档资源)见 references/resource-templates.md 的 Complete Example 一节,其中嵌套模板user://{userId}/posts/{postId}展示了一个模板同时携带两个路径参数的写法。

七、迭代开发与工程化建议

Skill 对"在既有代码上做迭代"给出了明确流程:先读当前index.ts了解已有内容;保留所有既有 tools、resources、widgets;新功能与既有代码并列新增;优先更新既有 Widget 文件而非创建重复文件。这一原则在仓库示例中得到了严格执行——index.ts 通过register(server)模式将工具注册函数拆到tools/目录(如 tools/product-search.ts),并在文件头注释里清晰列出"新增一个 MCP App Widget"的三步流程:创建resources/<widget-name>/widget.tsx→ 创建tools/<tool-name>.ts并导出register(server)→ 在index.ts标记位置 import 并调用register()

此外,本 Skill 自身带有一组评估用例(evals/目录下的skill.jsonimplementation.jsonarchitecture.jsonwidgets.json),覆盖需求拆解、服务端实现、架构设计与 Widget 开发四类场景,可作为验证 Agent 行为是否符合上述规范的参考。

八、从 Skill 到生产:落地清单

综合以上内容,基于 mcp-use 构建一个生产级 MCP Server 的完整落地路径是:

  1. 拆解需求:按"工具 vs Widget vs 资源"四类原语划分核心动作,命名动词开头、一个工具一个能力、一个流程一个 Widget、不做懒加载。
  2. 搭建骨架:以 index.ts 为模板初始化MCPServer,配置baseUrl、端口(process.env.PORT ?? "3109")、favicon 与图标;用npm install && npm run dev启动开发服务(自动重建 Widget 并热重启)。
  3. 实现服务端:按 tools-and-resources.md 编写带outputSchemaannotations的工具、静态与参数化资源、可复用 prompt;Zod schema 全程.describe()
  4. 接入 Widget:在resources/创建 React 组件,导出widgetMetadata与默认组件,用useWidget()获取 props/state,牢记isPending加载态,用McpUseProvider autoSize包裹,通过callTool实现 Widget 内工具联动。
  5. 统一响应:按 response-helpers.md 选择text/object/markdown/html/image/audio/binary/error/mix/widget/resource,用completable()提升参数体验。
  6. 保护与部署:API Key 走环境变量并配套.env.example;必要时用 Hono 路由挂自定义 HTTP 端点;最后用mcp-use build/mcp-use deploy(或 Manufact Cloud 的npm run deploy)发布,并通过http://localhost:3109/inspector在浏览器中调试工具与 Widget 的交互效果。

通过本指南,你已具备在 mcp-use 框架下从零构建带交互式 UI 的 MCP Server 的完整知识,并可随时回到仓库中 SKILL.md 及其references/参考文档与 product-search 真实示例中对照验证。

【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询