将 Node.js REST API 改造为 AI 就绪的 MCP 服务器
了解如何将 Node.js REST API 升级为 AI 就绪的模型上下文协议(MCP)服务器,以支持智能的、由智能体驱动的交互能力。
大型语言模型(LLM)与智能体 AI 的发展,要求应用程序暴露能力的方式发生根本性转变。传统 REST API 专为软件对软件通信设计,需要开发者阅读文档并编写自定义集成代码。而模型上下文协议(MCP)作为一种开放标准,通过创建统一的机器可读接口解决了这一问题,使 AI 智能体能够动态发现并与之交互。
本文提供了一份全面指南,介绍如何使用官方 TypeScript SDK 将现有 Node.js REST API 转换为 MCP 服务器,重点解析转换带来的架构变化及解锁的关键应用场景。
一、范式转变:从 REST 到 MCP
REST API 的设计通常以人类开发者为核心,通过 HTTP 动词、路径变量和特定的请求/响应格式优化资源管理(CRUD 操作)。
相比之下,MCP 模型以 AI 为优先设计理念:
| 维度 | 传统 REST API | AI 优先 MCP 服务器 |
|---|---|---|
| 主要使用者 | 人类开发者、客户端应用 | AI 智能体、LLM、AI 驱动的 IDE |
| 接口形式 | HTTP 动词、路径、查询参数、自定义请求体 | 标准化 JSON-RPC 消息(工具、资源、提示词) |
| 发现方式 | 通过 OpenAPI/Swagger 文档手动发现 | 通过 list_tools()或 list_resources()协议动态发现 |
| 功能特性 | 细粒度、原子化端点(如 GET /users/{id}) | 高层级、语义化操作(如 manage_user_profile) |
这种转换并非直接移植,而是一种抽象过程:用 MCP 层包裹现有的 Node.js 业务逻辑,将标准化的 MCP 调用转换为 API 可理解的 REST 请求。
二、步骤 1:搭建 Node.js MCP 环境
官方模型上下文协议 TypeScript SDK 是实现这一转换的核心工具。
(一)初始化项目并安装依赖
假设已创建基础 Node.js 项目(v18 及以上版本),需安装 MCP SDK、请求验证工具(如 Zod)以及用于与现有 REST API 交互的 HTTP 客户端(如 axios 或 node-fetch)。
Shell
npm init -y
npm install @modelcontextprotocol/sdk zod node-fetch
npm install -D typescript @types/node ts-node
(二)实例化 MCP 服务器
创建文件(如 mcp-server.ts),用于设置服务器实例和传输层------本地测试可使用 StdioServerTransport,远程部署可使用 StreamableHttpServerTransport。
TypeScript
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服务器已在标准输入输出流上运行...");
}
startServer().catch(console.error);
三、步骤 2:设计并定义 MCP 工具
这是最关键的步骤。无需盲目暴露所有 REST 端点,而应将功能整合为高层级、适合智能体使用的"工具(Tools)"和"资源(Resources)"。
(一)设计适合 LLM 的工具
LLM 在处理语义化、基于意图的工具时表现更优,而非细粒度的底层 API 调用。
- 不推荐(REST 导向):get_user_by_id(通过 ID 获取用户)、update_user_name(更新用户名)、update_user_email(更新用户邮箱)
- 推荐(MCP 导向):manage_user_profile(userId, newName, newEmail)(管理用户档案)
MCP 工具处理器应协调多个必要的 REST 调用,以完成单个高层级操作。
(二)实现工具处理器
每个工具需包含描述性名称、面向 LLM 的详细自然语言描述,以及使用 Zod 定义的结构化输入/输出 schema。
TypeScript
// 定义工具输入参数的schema
const UpdateUserSchema = z.object({
userId: z.string().describe("待更新用户的唯一ID。"),
newEmail: z.string().email().optional().describe("用户的新邮箱地址(可选)。"),
newSubscriptionPlan: z.enum(['basic', 'premium', 'pro']).optional().describe("待应用的新订阅计划(可选,值为basic/premium/pro)。"),
});
// 注册MCP工具
server.registerTool(
"manage_subscription", // 工具唯一标识
{
title: "管理用户订阅及档案", // 工具标题
description: "更新用户邮箱地址和/或修改其订阅计划,需传入用户ID。", // 工具描述(供LLM理解功能)
argsSchema: UpdateUserSchema, // 输入参数校验schema
outputSchema: z.object({ // 输出结果schema
status: z.string(), // 操作状态(如Success/Error)
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; // 从环境变量获取原REST API地址
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 : ["未执行任何修改。"],
};
}
);
(三)创建用于提供上下文的资源
对于提供上下文的简单 GET 请求(只读数据),可使用 ResourceTemplates(资源模板)。这使 LLM 能够了解可用数据,无需实际调用工具。
TypeScript
server.registerResource(
"product_catalog_item", // 资源唯一标识
{
title: "产品目录项", // 资源标题
description: "产品目录中的单个商品信息,包含价格、库存和描述。", // 资源描述
uriTemplate: "api://my-node-api-mcp/products/{productId}", // 资源URI模板(包含参数占位符)
dataSchema: z.object({ // 数据结构schema
id: z.string(), // 商品ID
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:实现安全与错误处理
向自主智能体暴露能力时,安全性至关重要。
(一)认证集成
MCP 服务器充当代理角色,其内部 HTTP 客户端需处理原 REST API 的认证逻辑。通常需从环境变量中安全加载 API 密钥或 OAuth 令牌,并在工具处理器的 fetch/axios 调用中,将其包含在 Authorization 请求头中。
(二)健壮的错误响应
AI 智能体依赖结构化输出来判断操作成败。处理器必须捕获 REST API 返回的 HTTP 错误,并将其转换为清晰、结构化的 MCP 错误响应。
- 不推荐:直接抛出原始 HTTP 404 错误。
- 推荐:返回结构化 MCP 输出,如
{ status: "Error", message: "数据库中未找到ID为123的用户。" }
五、MCP 解锁的关键应用场景
转换为 MCP 是一项战略性举措,可支持新型 AI 驱动应用的开发。
(一)AI 驱动的开发工具("Cursor"场景)
许多现代 AI IDE 和代码助手(如 Cursor、GitHub Copilot)利用 MCP 实现 AI 与本地开发环境或内部服务的交互。
- 场景:开发者询问 AI:"为新的用户管理模块运行集成测试。"
- MCP 工具:
run_npm_script(scriptName: string)(执行 NPM 脚本) - Node.js API 逻辑:工具在用户明确授权后,安全执行
npm run test:user-management等 shell 命令。
(二)智能客户支持自动化
将核心 CRM(客户关系管理)、库存或计费 API 封装为 MCP 工具,提供给内部 AI 智能体使用。
- 场景:客服人员询问 AI:"客户 Alice 的订单状态如何?能否为其应用 10%的折扣?"
- MCP 工具 1(资源):
get_customer_order_history(customerId)(获取客户订单历史) - MCP 工具 2(工具):
apply_discount_to_order(orderId, percentage)(为订单应用折扣) - 优势:AI 自主串联多个调用,自动获取数据并执行操作,无需人工干预。
(三)动态工作流与微服务编排
MCP 服务器可作为抽象层部署在复杂的微服务架构之上,使 LLM 能通过单个语义化命令编排多步骤复杂工作流。
- 场景:LLM 收到指令:"为 Jane Doe 处理新客户入职流程。"
- MCP 工具:
onboard_new_customer(name, email)(新客户入职) - 编排逻辑:工具处理器内部调用用户微服务(REST POST)、计费服务(REST POST)和邮件服务(REST POST),确保整个业务流程正确完成。这使 LLM 集成更简单,且对后端复杂度具备弹性。
六、结语:标准化 AI 集成的未来
将 Node.js REST API 改造为支持 MCP 的服务器,是为自主 AI 智能体时代适配应用的前瞻性投资。虽然简单包裹所有端点是良好的起点,但 MCP 的真正价值在于主动整合功能------设计反映用户意图而非 API 结构的高层级语义化工具。这一过程将 API 从静态的数据交换服务,转变为动态的、可被 AI 调用的能力集合,大幅提升其在智能体生态系统中的实用性。