ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

使用 mcp-builder Skill 构建生产级 MCP Server:mcp-use 框架下 Tools、Resources、Prompts 与交互式 Widget 完整实战指南 使用 mcp-builder Skill 构建生产级 MCP Servermcp-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 ProtocolMCP服务端。你将掌握从需求拆解、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原语角色对应 APITool工具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 中有完整展开确定要构建什么从用户请求中抽取核心动作只做用户要求的不要发明额外功能。例如weather app对应获取当前天气、获取预报todo list对应添加、列出、完成、删除recipe finder对应搜索食谱、获取食谱详情translator对应翻译文本、检测语言stock tracker对应获取股价、对比股票quiz app对应生成测验、检查答案。判断是否需要 Widget浏览/对比多个条目搜索结果、商品卡片、视觉数据能提升理解图表、地图、图片、仪表盘、可视化交互选择更方便座位选择器、日历、取色器→ 用 Widget纯文本输出翻译、计算、状态检查、输入天然适合对话日期、金额、描述、没有任何视觉元素能带来帮助 → 只用 Tool。文档给出的原则是犹豫时就上 Widget因为它通常能显著改善体验。设计 API工具与 Widget 命名以动词开头get-weather、search-recipes、add-todo、translate-text一个工具只做一个聚焦能力❌manage-todos→ ✅add-todo/list-todos/complete-todo/delete-todo一个流程只对应一个 Widget❌search-recipesWidget view-recipeWidget → ✅ 合并为一个search-recipesWidget列表与详情都在其中不做懒加载工具调用成本高一次性返回全部数据❌search-recipesget-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 ServerSkill 的 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 中服务端配置会显式传入更多元信息title、description、baseUrl、favicon、websiteUrl、icons并通过环境变量控制地址与端口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/svgxml, 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.3、react^19.2.4、zod4.3.5并通过npm run devmcp-use build --inline cross-env NODE_ENVproduction npx tsx index.ts在开发时自动重建 Widget 并重启服务。三、服务端实现详解Tools、Resources 与 Promptsreferences/tools-and-resources.md 完整讲解了服务端三类原语的实现模式。3.1 ToolAI 模型可调用的动作基础工具在配置对象中声明name、description、schemaZod 描述输入回调函数解构输入并返回响应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: string、color: string、facts: 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)文件路径形式是异步的需awaitaudio同理audio/wav、audio/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/下的文件名、invoking、invoked。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 构建交互式 UIreferences/widgets.md 是 Widget 开发的完整指南。其工作机制为在resources/下创建 React 组件 → 组件导出widgetMetadata描述 props 的 Zod schema和默认 React 组件 → mcp-use 自动注册为工具与资源 → 工具被调用时 Widget 以工具输出数据渲染。5.1 文件组织与命名单文件resources/weather-display.tsx→ Widget 名weather-display文件夹复杂 Widgetresources/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 文件必须导出widgetMetadata——含description展示内容说明与propsZod schema的对象默认 React 组件——UI 实现。WidgetMetadata完整字段字段类型必填说明descriptionstring是Widget 展示什么propsz.ZodObject是Widget 输入数据的 Zod schemaexposeAsToolboolean否是否自动注册为工具默认falsetoolOutputCallToolResult \| (params CallToolResult)否模型在自动注册工具被调用时看到的内容titlestring否展示标题annotationsobject否readOnlyHint、destructiveHint等metadataobject否CSP、边框、尺寸、调用状态文本等配置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.3useWidgetHookWidget 的数据与能力入口useWidget是访问 Widget 数据与能力的主 Hookconst { // 核心数据 props, // Widget 输入数据 isPending, // 工具仍在执行时为 trueprops 可能不完整 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简单场景可直接使用便捷 HooksuseWidgetPropsMyProps()只取 props、useWidgetTheme()只取主题、useWidgetStateMyState({ count: 0 })提供类似useState的体验。Widget 内容必须包裹在McpUseProvider中常用属性autoSize自动调整高度以适应内容默认false、viewControlsboolean | pip | fullscreen显示显示模式控制按钮默认false、debugger显示调试 inspector 覆盖层默认false。样式上内联样式与 Tailwind 均可div style{{ padding: 20, borderRadius: 12 }}或div classNamep-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.tsxWidgetimport { 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://settings、config://envuser://用户相关数据user://{id}/profiledocs://文档docs://api、docs://guidestats://统计/指标stats://current、stats://dailyfile://文件内容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.json、implementation.json、architecture.json、widgets.json覆盖需求拆解、服务端实现、架构设计与 Widget 开发四类场景可作为验证 Agent 行为是否符合上述规范的参考。八、从 Skill 到生产落地清单综合以上内容基于 mcp-use 构建一个生产级 MCP Server 的完整落地路径是拆解需求按工具 vs Widget vs 资源四类原语划分核心动作命名动词开头、一个工具一个能力、一个流程一个 Widget、不做懒加载。搭建骨架以 index.ts 为模板初始化MCPServer配置baseUrl、端口process.env.PORT ?? 3109、favicon 与图标用npm install npm run dev启动开发服务自动重建 Widget 并热重启。实现服务端按 tools-and-resources.md 编写带outputSchema与annotations的工具、静态与参数化资源、可复用 promptZod schema 全程.describe()。接入 Widget在resources/创建 React 组件导出widgetMetadata与默认组件用useWidget()获取 props/state牢记isPending加载态用McpUseProvider autoSize包裹通过callTool实现 Widget 内工具联动。统一响应按 response-helpers.md 选择text/object/markdown/html/image/audio/binary/error/mix/widget/resource用completable()提升参数体验。保护与部署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),仅供参考
返回列表