随着大型语言模型(LLM)和代理 AI 的快速发展,应用程序展示其功能的方式亟待从根本上变革。传统的 REST API 专为软件与软件之间的通信设计,开发人员需阅读大量文档并编写自定义集成代码才能使用。而模型上下文协议(MCP) 作为一种开放标准,通过创建统一的、机器可读的界面,让 AI 代理能够动态发现并与之交互,成功解决了这一痛点。
本文将提供一份全面指南,介绍如何使用官方 TypeScript SDK 将现有的 Node.js REST API 转换为 MCP 服务器,并重点剖析此次转换所带来的架构变革及关键应用场景。
一、范式转变:从 REST 到 MCP
REST API 在设计之初主要面向人类开发人员,围绕资源管理(CRUD 操作)进行优化,依赖 HTTP 谓词、路径变量及特定的请求 / 响应格式实现功能。
与之不同,MCP 模型秉持 “人工智能优先” 的理念,二者在核心特性上存在显著差异,具体对比如下表所示:
| 对比维度 | REST API(传统模式) | MCP 服务器(AI 优先模式) |
|---|---|---|
| 主要使用者 | 人类开发人员、客户端应用程序 | AI 代理、LLM、AI 驱动的 IDE |
| 接口形式 | HTTP 动词、路径、查询参数、自定义正文 | 标准化的 JSON-RPC 消息(工具、资源、提示) |
| 功能发现方式 | 通过 OpenAPI/Swagger 文档手动查阅 | 通过 list_tools() 或 list_resources() 协议动态发现 |
| 功能设计 | 细粒度的原子化端点(如 GET /users/{id}) | 高级语义化操作(如 manage_user_profile) |
需要注意的是,REST 到 MCP 的转换并非简单的端口迁移,而是一次抽象升级。我们可以通过 MCP 层包装现有的 Node.js 业务逻辑,将标准化的 MCP 调用转换为 REST API 能够理解的请求,从而实现功能适配。
二、第 1 步:搭建 Node.js MCP 环境
官方的模型上下文协议 TypeScript SDK 是完成此次转换的核心工具,以下是具体的环境搭建步骤:
1. 初始化项目并安装依赖
假设我们已拥有一个基础的 Node.js(v18+)项目,接下来需要安装 MCP SDK、用于请求验证的工具(如 Zod),以及用于与现有 REST API 交互的 HTTP 客户端(如 axios 或 node-fetch)。
打开终端,执行以下命令:
npm init -y
npm install @modelcontextprotocol/sdk zod node-fetch
npm install -D typescript @types/node ts-node
2. 实例化 MCP 服务器
创建一个名为 mcp-server.ts 的文件,用于设置服务器实例和传输层。传输层可根据需求选择,例如用于本地测试的 StdioServerTransport,或用于远程部署的 StreamableHttpServerTransport。
具体代码如下:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 实例化核心 MCP 服务器
const server = new McpServer({
name: "MyNodeAPIServer",
version: "1.0.0",
capabilities: { tools: {}, resources: {}, prompts: {} },
});
// 传输层负责与 LLM 客户端进行通信
const transport = new StdioServerTransport();
async function startServer() {
// 【工具和资源注册代码将在此处添加】
await server.connect(transport);
console.log("MCP 服务器已在标准 I/O 上运行...");
}
startServer().catch(console.error);
三、第 2 步:规划并定义 MCP 工具
这是转换过程中最关键的一步。我们不能盲目地将每个 REST 端点直接暴露,而应将功能规划为高级、适配代理的工具和资源。
1. 设计适配 LLM 的工具
LLM 对语义化、基于意图的工具的适配性,远优于细粒度的低级 API 调用。
- 不合理(以 REST 为核心) :设计
get_user_by_id、update_user_name、update_user_email等细分接口; - 合理(以 MCP 为核心) :设计
manage_user_profile(userId, newName, newEmail)这样的高级语义化操作接口。
MCP 工具处理程序需协调多个必要的 REST 调用,以完成单个高级操作。
2. 实现工具处理程序
每个工具都需要具备描述性名称、面向 LLM 的完整自然语言描述,以及基于 Zod 的结构化输入 / 输出模式。
示例代码如下:
// 定义工具输入参数的模式
const UpdateUserSchema = z.object({
userId: z.string().describe("需要更新信息的用户的唯一 ID。"),
newEmail: z.string().email().optional().describe("用户的新电子邮箱地址。"),
newSubscriptionPlan: z.enum(['basic', 'premium', 'pro']).optional().describe("需要为用户应用的新订阅计划。"),
});
server.registerTool(
"manage_subscription",
{
title: "管理用户订阅与个人资料",
description: "更新用户的电子邮箱地址和/或更改其订阅计划,操作需提供用户 ID。",
argsSchema: UpdateUserSchema,
outputSchema: z.object({
status: z.string(),
updatedFields: z.array(z.string()),
}),
},
async (args) => {
const { userId, newEmail, newSubscriptionPlan } = args;
const updatedFields: string[] = [];
// --- REST API 调用协调 ---
const REST_API_BASE = process.env.REST_API_URL;
if (newEmail) {
// 1. 调用 REST API 更新用户电子邮箱
await fetch(`${REST_API_BASE}/users/${userId}/email`, {
method: 'PUT',
body: JSON.stringify({ email: newEmail }),
headers: { 'Content-Type': 'application/json' },
});
updatedFields.push('email');
}
if (newSubscriptionPlan) {
// 2. 调用 REST API 更新用户订阅计划
await fetch(`${REST_API_BASE}/billing/${userId}/plan`, {
method: 'POST',
body: JSON.stringify({ plan: newSubscriptionPlan }),
headers: { 'Content-Type': 'application/json' },
});
updatedFields.push('subscriptionPlan');
}
// 为 LLM 返回结构化响应
return {
status: "Success",
updatedFields: updatedFields.length > 0 ? updatedFields : ["未进行任何更改。"],
};
}
);
3. 为上下文创建资源
对于仅提供上下文(只读数据)的简单 GET 请求,可使用 ResourceTemplates。这种方式能让 LLM 了解可用的数据信息,无需额外调用工具。
示例代码如下:
server.registerResource(
"product_catalog_item",
{
title: "产品目录项",
description: "产品目录中的单个产品信息,包含价格、库存和产品描述。",
uriTemplate: "api://my-node-api-mcp/products/{productId}",
dataSchema: z.object({
id: z.string(),
name: z.string(),
price: z.number(),
description: z.string(),
}),
},
async (uri) => {
// 从 URI 或参数中解析出 productId
const productId = uri.split('/').pop();
// 调用 REST API:GET /products/{productId}
const response = await fetch(`${process.env.REST_API_URL}/products/${productId}`);
return await response.json();
}
);
四、第 3 步:实现安全与错误处理
向自主代理开放功能时,安全性至关重要。同时,为了确保 AI 代理能准确判断操作结果,还需设计完善的错误处理机制。
1. 身份验证集成
MCP 服务器在此过程中扮演代理角色,其内部的 HTTP 客户端必须处理原始 REST API 的身份验证。通常的做法是:从环境变量中安全加载 API 密钥或 OAuth 令牌,并在工具处理程序的 fetch 或 axios 调用中,将这些凭证包含在 Authorization 标头里。
2. 稳健的错误响应
AI 代理依赖结构化输出来判断操作的成功或失败。因此,工具处理程序必须捕获 REST API 返回的 HTTP 错误,并将其转换为清晰、结构化的 MCP 错误响应。
- 不合理:直接抛出原始的 HTTP 404 错误;
- 合理:返回结构化响应,如
{ status: "Error", message: "在数据库中未找到 ID 为 123 的用户" }。
五、MCP 解锁的关键应用场景
将 REST API 转换为 MCP 服务器是一项具有战略意义的举措,它能赋能新型人工智能驱动的应用程序,以下是几个典型场景:
1. AI 驱动的开发者工具(“光标” 场景)
如今,许多现代 AI IDE 和代码助手(如 Cursor、GitHub Copilot)都采用 MCP 协议,让 AI 能够与本地开发环境或内部服务进行交互。
场景示例:开发人员向 AI 询问 “为新的用户管理模块运行集成测试”;
MCP 工具支撑:调用 run_npm_script(scriptName: string) 工具;
Node.js API 逻辑:该工具在获得用户明确批准后,安全执行 shell 命令(如 npm run test:user-management),完成集成测试任务。
2. 智能客户支持自动化
将核心的 CRM(客户关系管理)、库存管理或账单管理 API 作为 MCP 工具,开放给内部 AI 代理使用。
场景示例:客服人员向 AI 询问 “客户 Alice 的订单状态如何?我们能否为她的订单应用 10% 的折扣?”;
MCP 工具支撑:
-
调用资源工具
get_customer_order_history(customerId),获取 Alice 的订单历史; -
调用操作工具
apply_discount_to_order(orderId, percentage),为指定订单应用折扣;优势:AI 可自动串联多个工具调用,完成数据获取与操作执行,无需人工手动干预。
3. 动态工作流与微服务编排
MCP 服务器可作为庞大微服务架构的抽象层,让 LLM 能够通过单个语义化命令,编排复杂的多步骤工作流。
场景示例:LLM 收到指令 “处理 Jane Doe 的新客户入职流程”;
MCP 工具支撑:调用 onboard_new_customer(姓名、电子邮件) 工具;
编排逻辑:该工具的处理程序在内部依次调用用户微服务(REST POST)、计费服务(REST POST)和邮件服务(REST POST),确保整个新客户入职的业务流程完整、正确执行。这种方式不仅让 LLM 集成变得简单,还能灵活适配后端架构的复杂性。
六、结论:标准化 AI 集成的未来
将 Node.js REST API 转换为支持 MCP 的服务器,是一项面向未来的投资,能让应用程序更好地适应自主 AI 代理时代的需求。
虽然简单包装每个 REST 端点是一个不错的起点,但 MCP 的真正价值需要通过主动规划来实现 —— 即设计反映用户意图而非 API 结构的高级语义化工具。这一过程能将 API 从静态的数据交换服务,转变为动态的、可被 AI 调用的 “技能集合”,从而大幅提升其在代理生态系统中的实用性,为应用程序的智能化升级奠定坚实基础。
- 作者: 拉克希米·纳拉亚纳·拉萨莱
- 源文地址:dzone.com/articles/tr…