进阶 📋 6 个步骤 第 585 / 587 篇

用 mcp-use v2 把 MCP 服务器和交互界面一次写完:工具契约、Views 与 Inspector 实操

官方 MCP SDK 只负责协议,界面、类型、调试器都要自己拼。mcp-use v2 在官方 TypeScript SDK v2 之上补上了类型化输入输出、可绑定 React 视图的 Views、内置 Inspector 与截图命令行:一条脚手架命令起项目,改 Zod 契约就能让文本输出、结构化结果与界面 props 同步生效。本教程六步落地。

2026-11-01· 9 分钟上手· 约 2986 字· 🔌 mcp-use v2 / 🧩 类型化工具契约

给 Agent 造一个 MCP 服务器,最花时间的往往不是业务逻辑,而是它周围的那一圈:输入输出要手写校验、结构化结果和界面各写一份、想看一眼工具到底返回了什么还得自己搭个调试页。mcp-use v2 的思路是把这一圈补齐——它构建在官方 TypeScript SDK v2 之上,额外提供类型化的输入输出契约、可绑定到工具的 React 视图(Views)、开箱即用的 Inspector,以及能在终端里连接服务器、列工具、调工具、给界面截图的命令行。用 Zod 写好一份 schema,它会同时成为入参校验、工具返回的 structuredContent 和界面组件的 props 类型,改一处三处同步。本教程六步落地:起项目并跑通本地端点、用 Zod 定义工具契约、给工具挂一个界面、用命令行做无头验收、让 ChatGPT 与 Claude 连上本地服务、最后构建部署并说清版本迁移的坑。

🔌 本教程适合:会写 TypeScript、想给 ChatGPT、Claude 或自建 Agent 提供 MCP 工具的开发者。前置条件只有 Node.js 与 npm(能跑 npx 即可),本机开发不需要任何密钥;只有真正调用大模型时才用得到你自己的 API key。mcp-use 采用 MIT 许可,Python 侧另有同名包可构建客户端与 Agent,但创建服务器的完整能力在 TypeScript 侧。官方 TypeScript SDK 与 Python SDK 的协议一致性徽章是分开标注的,选型时留意自己用的那一侧。

Step 1:起项目:一条脚手架命令,端点与调试页同时就绪

1 不用自己拼调试器

项目用官方脚手架创建,它会把服务端框架、TypeScript 配置、开发脚本、Inspector 与 React 视图构建管线一并生成好。进入目录装依赖后运行开发脚本,本机同时起来两个东西:MCP 端点与挂在同一个端口下的 Inspector 页面。也就是说你不再需要另开一个调试站点,也不用在每次改动后手动重启去验证。

# 创建项目(交互式脚手架)
npx -y create-mcp-use-app@latest

# 进入项目并启动开发模式
cd my-mcp-project
npm install
npm run dev

# 开发模式默认提供
#   MCP 端点      http://localhost:3000/mcp
#   内置 Inspector http://localhost:3000/mcp/inspector

# 线上 Inspector(不想本地起也能用)
https://inspector.mcp-use.com/inspector

生成的项目里已经有示例工具与示例视图,建议先原样跑一遍,确认 Inspector 里能看到工具列表,再开始改。这个顺序能帮你把「环境问题」和「代码问题」分开。

💡 Inspector 不只是看工具列表:它可以直接调用工具、校验入参、渲染并查看交互界面。把它当成开发主界面,比在 Agent 里猜返回值高效得多。

Step 2:写工具:用 Zod 把输入、输出和界面 props 绑成一份契约

2 一份 schema 三处生效

服务端入口是 MCPServer 实例,工具通过 server.tool 注册:入参有两个,前一个是元信息对象(名称、标题、描述、输入 schema、输出 schema、视图绑定、注解),后一个是处理函数。处理函数收到的是已经过校验的输入,返回时必须同时给 content(给模型读的文本)与 structuredContent(给程序与界面用的结构化结果)。

import { MCPServer } from "mcp-use";
import { z } from "zod";

// 1) 服务端实例
const server = new MCPServer({
  name: "weather-app",
  title: "Weather App",
  version: "1.0.0",
});

// 2) 输入 / 输出契约(Zod)
const weatherInput = z.object({
  city: z.string().describe("City to look up"),
});

const weatherOutput = z.object({
  city: z.string(),
  temperature: z.number(),
  conditions: z.string(),
});

// 3) 注册工具:文本输出 + 结构化结果 + 视图绑定 + 注解
export const getWeather = server.tool(
  {
    name: "get-weather",
    title: "Get weather",
    description: "Get the current weather for a city",
    inputSchema: weatherInput,
    outputSchema: weatherOutput,
    view: { name: "weather-card" },
    annotations: {
      readOnlyHint: true,     // 只读:客户端更愿意自动执行
      destructiveHint: false, // 不是破坏性操作
      openWorldHint: true,    // 会访问外部世界(网络)
    },
  },
  async ({ city }) => {
    const weather = { city, temperature: 22, conditions: "Sunny" };
    return {
      content: [
        // 文本用普通字符串拼接,避免模板字符串与示例混淆
        { type: "text", text: "Weather in " + city + ": " + weather.conditions + ", " + weather.temperature + "°C" },
      ],
      structuredContent: weather,
    };
  },
);

export default server;

注解这一段别跳过:readOnlyHint、destructiveHint、openWorldHint 会被客户端拿来判断「要不要先问人」和「能不能自动跑」。把会写数据的工具标成只读,等于替 Agent 关掉了它本该有的确认环节。

v1 与 v2 的 API 不是一回事:网上能搜到的旧代码里常见 createMCPServer 加 parameters 的写法,那是 v1;v2 是 new MCPServer 配合 server.tool 与 inputSchema 加 outputSchema。照抄旧示例会直接报类型错误。项目里若已有 v1 代码,让编码 Agent 读官方迁移指南再改,不要手工逐行猜。

Step 3:加界面:视图目录名必须和 view.name 对上

3 工具返回的不只是文字

视图让工具结果以交互卡片的形式出现在对话里。绑定方式是工具元信息里的 view: { name: "weather-card" },对应的组件必须落在 views/weather-card/view.tsx——目录名与 name 逐个字母一致,否则界面不会渲染。组件从 mcp-use/react 取钩子:useToolContext 读输入、状态与输出,useCallTool 在界面里再调一次工具(比如刷新按钮)。

// views/weather-card/view.tsx
import { useCallTool, useToolContext } from "mcp-use/react";

export default function WeatherCard() {
  // 读当前工具调用的输入、状态与输出
  const { status, toolOutput, toolInput } = useToolContext<"get-weather">();
  // 在界面里再次调用工具(刷新)
  const refresh = useCallTool("get-weather");

  if (status === "pending") {
    return <p>Checking the weather in {toolInput?.city ?? "your city"}…</p>;
  }
  if (status === "error") return <p>Could not load the weather.</p>;

  const weather = refresh.data?.structuredContent ?? toolOutput;

  return (
    <main style={{ padding: 24 }}>
      <h2>{weather.city}</h2>
      <p>{weather.temperature}°C · {weather.conditions}</p>
      <button disabled={refresh.isPending} onClick={() => void refresh.callTool({ city: weather.city })}>
        {refresh.isPending ? "Refreshing…" : "Refresh"}
      </button>
      {refresh.error && <p>{refresh.error.message}</p>}
    </main>
  );
}

如果你的目标是 ChatGPT 插件形态,还有一个额外钩子 useModelContext,它可以把界面里选中的内容(比如一本书、一条分析结果)作为消息加回对话。这个能力属于 ChatGPT 插件扩展的用法,Claude 连接器侧不需要。

目录名对不上是静默失败:视图文件放错位置或目录名与 view.name 不一致时,工具照常返回文本,界面就是不出现,日志里也不一定有醒目报错。写完视图先在 Inspector 里看一眼,再去接客户端。

💡 界面的 props 类型来自 outputSchema,所以改 Zod 就够了:加一个字段,视图里就能直接读到,不用另写一份类型定义。这是把 schema 当单一真源最直接的好处。

Step 4:无头验收:连接、列工具、调工具、给界面截图

4 把 MCP 服务器当成命令行可测的东西

装 @mcp-use/client 之后,同一个服务器可以完全脱离浏览器来验证:连接本地端点、列出工具、按参数调用、把视图渲染成图片。这几条命令最适合放进 CI——不用起界面,也能确认工具还在、返回值结构没变、卡片还能画出来。

npm install --save-dev @mcp-use/client

# 连接本地服务器(起个别名,后续命令复用)
npx mcp-use client connect local http://localhost:3000/mcp

# 列出工具
npx mcp-use client local tools list

# 调用工具
npx mcp-use client local tools call get-weather city=Tokyo

# 把视图渲染成图片(做视觉回归很顺手)
npx mcp-use screenshot --server local --tool get-weather city=Tokyo --output weather-card.png

截图这条命令值得单独说:它产出的是真实渲染结果,团队评审时能直接看到卡片长什么样,而不是靠想象。把生成的图片作为构建产物留档,界面悄悄坏掉时你能立刻发现。

💡 调用命令里参数用的是 key=value 形式,值里有空格要加引号。先 tools list 确认工具名与参数名,再写调用,能省掉一轮无谓的排错。

Step 5:让 ChatGPT 与 Claude 连上:用隧道把本地服务临时暴露出去

5 临时公网地址,只为联调

本地 3000 端口对云端客户端不可见,官方给了两条出路:在 Inspector 界面里直接启动隧道,或在命令行用开发命令加隧道参数。两种方式都会给出一个公网 URL,把它填进 ChatGPT 或 Claude 的连接器配置即可联调。

# 命令行起隧道,拿到公网 URL
mcp-use dev --tunnel

# 或在 Inspector 界面里点开隧道开关
# 把得到的公网 URL 填进 ChatGPT / Claude 的连接器配置

联调通过后就要把它关掉。隧道的本质是把你本机的服务放到公网上,任何拿到地址的人都能调用你注册的工具——包括那些会写数据的工具。

隧道不是部署方式:它适合临时验证,不适合长期挂着跑业务,更不适合在生产数据上用。联调结束就停掉隧道;要给团队稳定使用,走 Step 6 的构建与部署,而不是长期依赖一个临时地址。

Step 6:构建与部署:一条命令上托管,或按官方指引自托管

6 两条路都要先跑构建

生产产物由构建命令产出,之后有两条选择:一是部署到官方托管平台,一次命令即可,附带可观测、分析、评测与基于 Git 的分支预览环境;二是按官方自托管指引落到自己的运行时上,适合有合规或内网要求的团队。两条路共用同一个构建产物,所以本地跑通的东西换环境不会变形。

# 生产构建
npm run build

# 部署到官方托管平台
npm run deploy

# 自托管:按官方 runtime patterns 指引部署构建产物
https://docs.mcp-use.com/typescript/server/deployment/runtime-patterns

生态里还有几个可单独安装的包:@mcp-use/client 负责从 Node、浏览器、React 与沙箱连接服务器,@mcp-use/agent 负责在客户端之上搭模型驱动的 Agent,@mcp-use/inspector 与 @mcp-use/tunnel 分别对应调试与隧道。按需装,不必一次全上。

💡 分支预览环境很适合多人协作:每个拉取请求一个地址,评审者直接连上去点两下,比读代码判断工具行为可靠得多。

托管不等于免运维:部署后仍然要关心谁在调用、调用频率与成本、以及工具里涉及的密钥。托管平台提供的是可观测与预览便利,不会替你决定一个工具该不该自动执行——那件事由 Step 2 里的注解说了算。

常见问题速查

现象原因与处理
开发模式起不来确认能跑 npx 与 npm;脚手架生成的项目需先 npm install
Inspector 打不开它在 /mcp/inspector,不是根路径;也可用线上 Inspector
界面不显示视图目录名必须与 view.name 一致,且文件叫 view.tsx
照抄示例报类型错示例可能是 v1 写法(createMCPServer),v2 用 new MCPServer 与 server.tool
视图读不到新字段字段要先加进 outputSchema,视图 props 由它推导
客户端总要先问我检查 annotations:只读与破坏性标注会影响客户端的自动执行判断
想在终端里验收装 @mcp-use/client,用 client connect / tools list / tools call
想留界面快照用 mcp-use screenshot 产出图片,可作为构建产物存档
ChatGPT 里想加内容回对话用 mcp-use/react 的 useModelContext(ChatGPT 插件扩展场景)
本地服务怎么给云端客户端用临时用 dev --tunnel,正式用构建加部署,别长期挂隧道
必须付费吗框架本身 MIT;托管平台有免费档,调用大模型的成本来自你自己的 key
Python 能造服务器吗Python 侧有同名的客户端与 Agent 能力,服务器完整能力在 TypeScript 侧
部署后怎么回滚托管平台按 Git 分支管理预览与发布,回滚走同一套流程
想让 Agent 直接调我的服务器用 @mcp-use/agent 在客户端之上搭模型驱动的 Agent
← 返回教程中心