前面三篇文章分别解决了两个问题:第一篇讲清楚了 Vercel AI SDK 里的 Tool 是什么,以及为什么它适合在一个 Next.js 或 Node.js 项目里快速把业务函数暴露给模型;第二篇和第三篇则把视角抬高,解释了 MCP 为什么会出现,它解决的不是“怎么在一个项目里定义工具”,而是“怎么让不同 AI 宿主共享同一套工具能力”。
到了这一篇,终于轮到最关键的一步:把两者串起来。如果说 MCP Server 负责“对外标准化地提供工具”,那么 Vercel AI SDK 则可以在应用内部把这些远端工具重新变成模型可直接使用的本地 Tool。
这篇文章只做一件事:用一个最简单的 HTTP 汇率查询例子,把“纯 Tool 实现”和“MCP Server + AI SDK Host 实现”并排讲清楚,让你真正理解它们的区别、联系,以及迁移路径。
为什么需要这一层转换
如果只看 Vercel AI SDK 的开发体验,最简单的方式当然是直接在项目里写一个 tool(...),在 execute 里发 HTTP 请求,完成后把结果返回给模型。
这种方式的优点非常明显:
• 所有逻辑都在一个项目里,理解成本低。
• TypeScript 类型、参数校验、前端集成都在同一套开发体验里完成。
但一旦这个汇率查询能力不只给一个 Web 应用使用,而是还想给 IDE 插件、内部 Agent、另一个团队的 AI 工具复用,就会遇到老问题:每个宿主都要重复封装一层自己的 Tool。
MCP 的价值就在这里:它把“汇率查询”这件事抽成一个独立的 MCP Tool,由 MCP Server 统一暴露;而 Vercel AI SDK 则通过 MCP Client 把远端工具拿回来,再作为本地 Tool 交给模型使用。
换句话说:
• MCP 负责“工具如何被别的系统发现和调用”。
• Vercel AI SDK 负责“模型如何在当前应用里使用这些工具”。
这一层“变成本地 Tool”的动作,正是两者结合后最有价值的地方。
先看方案 A:纯 Vercel AI SDK Tool
先从最简单的方案开始。假设已经有一个普通的 HTTP API:
•GET /api/exchange-rate?from=USD&to=CNY
• 返回:{ "from": "USD", "to": "CNY", "rate": 6.80}
如果只想在自己的 Next.js 应用里使用这个能力,完全可以直接写一个本地 Tool:
import { tool } from 'ai';import { z } from 'zod';export const getExchangeRate = tool({ description: '查询两种货币之间的实时汇率', inputSchema: z.object({ from: z.string().describe('源货币代码,例如 USD'), to: z.string().describe('目标货币代码,例如 CNY'), }), execute: async ({ from, to }) => { const res = await fetch( `https://example.com/api/exchange-rate?from=${from}&to=${to}` ); if (!res.ok) { throw new Error('failed to fetch exchange rate'); } return await res.json(); },});然后在 generateText 或 streamText 里把它作为 tools 传给模型即可。
const result = await generateText({ model: openai('gpt-4o'), prompt: '100 美元大约等于多少人民币?', tools: { getExchangeRate, },});这一套写法的核心特点是:
• 工具定义、HTTP 请求、模型调用都在同一个代码库里。
• 模型看到的是一个本地 Tool,而不是某个“远端协议对象”。
• 如果以后别的宿主也需要这个能力,就得在别的项目里再写一遍类似封装。
所以方案 A 很适合:
• 只有一个 Web 应用要用这个工具。
• 工具逻辑比较轻,不打算对外提供标准接口。
• 当前阶段更重视开发速度,而不是跨宿主复用。
再看方案 B:MCP Server + Vercel AI SDK Host
现在把同一个需求换一种拆法:
• 汇率查询能力不再直接写成某个项目里的本地 Tool。
• 它先被放进一个 MCP Server,作为标准化的 MCP Tool 对外暴露。
• 你的 Next.js / Node.js 应用不再亲自实现汇率查询,而是作为 MCP Host 去连接这个 Server,再把它转换成本地 Tool 给模型。
第一步:在 MCP Server 中暴露工具
按照 MCP 的工具规范,Server 至少要提供工具名、描述和输入 schema,并支持 tools/list 与 tools/call 这两类操作。
概念上,这个汇率工具长这样:
{ "name": "get_exchange_rate", "description": "查询两种货币之间的实时汇率", "inputSchema": { "type": "object", "properties": { "from": { "type": "string", "description": "源货币代码,例如 USD" }, "to": { "type": "string", "description": "目标货币代码,例如 CNY" } }, "required": ["from", "to"] }}当 Host 发送 tools/call 时,MCP Server 在内部再去调用原来的 HTTP API。
也就是说,MCP Server 才是真正知道“汇率数据从哪来”的那一层;Host 只知道“这里有个叫 get_exchange_rate 的工具可以调用”。
第二步:在 Vercel AI SDK 中创建 MCP Client
AI SDK 从 4.2 开始支持 MCP Client,并提供了专门的 createMCPClient 能力,用来连接 MCP Server。
Cookbook 中给出的模式非常直接:创建客户端、连接远端 MCP Server、拉取工具列表、把它们交给 generateText 或 streamText。
代码大致如下:
import { createMCPClient } from '@ai-sdk/mcp';import { generateText } from 'ai';import { openai } from '@ai-sdk/openai';const mcpClient = await createMCPClient({ transport: { type: 'sse', url: 'https://example.com/mcp/sse', },});const result = await generateText({ model: openai('gpt-4o'), prompt: '100 美元大约等于多少日元?', tools: await mcpClient.tools(),});await mcpClient.close();这里最值得注意的一点是:mcpClient.tools() 返回的是已经兼容 AI SDK Tool 形态的对象,因此可以直接注入模型配置。
这正是本文标题所说的“把远端 MCP 工具变成本地 Tool”。它不要求你手写一层巨大的适配器;AI SDK 的 MCP Client 本身就是为“工具转换”这件事设计的。
第三步:模型看来,它们几乎一样
到了模型那一层,方案 A 和方案 B 的体验其实非常接近:
• 模型都会看到一个带名字、描述和参数 schema 的工具。
• 模型都会自己判断要不要调用、该传什么参数。
• 最终都会拿到结构化结果,再生成自然语言回复。
不同之处在于:
• 在方案 A 中,这个工具的执行逻辑就在你的项目里。
• 在方案 B 中,这个工具背后其实是一次 tools/call 到远端 MCP Server 的协议交互。
对业务代码而言,方案 B 相当于给当前应用加了一层“远端工具市场”。
两种实现放在一起比较
把同一个汇率查询能力放到两种方案中,对比会很清楚:
|
如果用一句话概括:
• 纯 Tool 更像“在本项目里原地写函数”。
• MCP + AI SDK 更像“先把能力发布成标准服务,再在本项目里按 Tool 的方式消费它”。
一个更真实的架构图应该怎么理解
如果把这一层组合关系画成一条链路,可以这样理解:
1. 用户在 Web 界面里问:“100 美元等于多少人民币?”
2. Next.js 后端收到消息,并调用 generateText。
3. AI SDK 发现当前可用工具来自 mcpClient.tools()。
4. 模型决定调用 get_exchange_rate。
5. AI SDK 通过 MCP Client 向远端 MCP Server 发起 tools/call。
6. MCP Server 内部再去请求汇率 HTTP API,拿到结果后返回。
7. AI SDK 把结果回填给模型,模型生成最终回复。
这里最值得体会的是“分层”:
• 汇率查询是工具层能力,由 MCP Server 管理。
• 聊天会话、模型选择、UI 流式输出是应用层能力,由 Vercel AI SDK 所在应用管理。
这意味着未来如果还有别的宿主——比如 IDE 插件或内部自动化 Agent——它们也可以直接连接这个 MCP Server,而不用复制当前 Web 应用里的 Tool 代码。
什么时候值得把 Tool 迁移到 MCP
不是所有 Tool 都值得立刻搬到 MCP。更现实的做法,是把它当成一种架构升级,而不是“新技术来了就全量重写”。
下面这几个信号通常说明:你已经接近需要 MCP 的阶段了。
信号一:同一个工具要被多个宿主使用
如果一个工具不只给 Web 用,还要给 Cursor、Claude Desktop、自研 CLI Agent 或公司内部总控台使用,那么继续在每个项目里各写一份 Tool 封装,维护成本会越来越高。
这时,把它提升成 MCP Tool 会更自然:
• 工具定义只维护一次。
• 不同 Host 通过协议统一接入。
• 权限、监控、审计更容易收敛在 Server 一层处理。
信号二:工具开始脱离某个具体应用
如果某个能力本质上已经不是“这个页面的一个函数”,而更像“公司内部的一项基础设施能力”,那它就不该继续深埋在某个 Next.js 项目里。
例如:
• 统一订单查询
• 通用知识库搜索
• Git 仓库操作
• 文件系统访问
这些能力越独立、越通用,就越适合放到 MCP Server 中,变成被多个应用消费的通用工具。
信号三:希望工具层与前端/模型层解耦
在纯 Tool 模式下,工具层和模型层天然耦合在同一个项目里:
• 你改工具,往往要顺手改聊天服务代码。
• 你换前端宿主,也要迁移相同的工具定义。
而在 MCP 模式下,工具层和宿主层可以独立演进:
• MCP Server 负责能力暴露。
• Web 应用、IDE 插件、桌面端各自负责自己的体验。
• AI SDK 则成为消费这些工具的其中一种宿主实现。
渐进式迁移:不必一步到位
对多数团队来说,最合理的路线并不是“从第一天就把所有工具做成 MCP”,而是先从纯 Tool 起步,再把真正值得复用的能力抽出来。
一个比较稳妥的演进方式是:
1. 先在当前 Next.js 项目里用纯 Tool 把业务跑通。
2. 观察哪些 Tool 开始被多个项目反复复制。
3. 把这些重复出现的能力抽成 MCP Server。
4. 在原项目里改为通过 mcpClient.tools() 接回这些工具。
5. 保留一部分强耦合的本地 Tool,只把“共享能力”协议化。
这也是 Vercel AI SDK + MCP 很实际的地方:它不要求你在“纯 Tool”和“纯 MCP”之间二选一,而是允许你在一个项目里同时使用两类工具。
例如:
• 本地 Tool 负责当前会话特有的操作,如读取用户当前页面状态。
• MCP Tool 负责通用能力,如汇率、工单、知识库、Git 操作。这种混合模式,通常比“一刀切重构”更适合真实团队。
最后
到这里,Vercel AI SDK Tool 和 MCP 的关系其实已经非常清晰了:
• Vercel AI SDK Tool 是模型调用工具时的应用内抽象。
• MCP Tool 是跨宿主、跨项目共享工具时的协议级抽象。
• AI SDK 的 MCP Client 则充当了一座桥:把远端 MCP 工具转换成当前应用可直接使用的 Tool。
所以,这一篇真正要传达的并不是“用了 MCP 就不用 Tool”,而是:
MCP 负责把工具带到你的应用门口,Vercel AI SDK 负责把这些工具顺手递给模型。
如果只做一个应用,纯 Tool 往往就够了。如果要做多宿主复用、统一工具层、平台化演进,那么 MCP + AI SDK 的组合会更自然。
从函数到 Tool:用 Vercel AI SDK 打开工具调用的大门
夜雨聆风