如何给 Claude Code 或 Cursor 装上“手脚”?三步搞定 Node.js MCP 服务端开发与本地联调
在 AI 编程席卷开发者的今天,你是否也有过这样的困惑:虽然 Claude 3.5 Sonnet 和 GPT-4o 足够聪明,但它们依然被困在沙箱里,只能“纸上谈兵”。如果你想让 AI 自动读取你本地的某张图片并提取信息、或者直接查询你本地的 SQL 数据库,你只能手动复制粘贴,效率极低。
有没有办法给 AI 装上“手脚”,让它能够直接安全地调用你本地的工具、读取你的本地文件系统?
答案就是 MCP(Model Context Protocol,模型上下文协议)。这是 Anthropic 官方推出的一套开放协议,旨在标准化 AI Client 与本地/远程数据源及工具之间的通信。有了 MCP,你的 AI 助手(如 Claude Desktop、Claude Code、Cursor、Trae 等)就可以真正接入现实世界。
今天,我们就来手把手带大家使用 Node.js + TypeScript 从零开发、调试并发布一个专属的 MCP 服务,帮助你彻底释放 AI Agent 的本地生产力!
1. 什么是 MCP?为什么它是 AI 时代的“连接器”?
传统的 AI 交互范式中,客户端只负责把用户的 Prompts 发送给大模型,然后接收文本。即使大模型支持 Function Calling,开发者也必须针对每个客户端、每个工具编写私有的连接胶水代码,碎片化极其严重。
MCP 协议改变了这一切。它引入了客户端-服务器架构,将 AI 引擎与数据/工具解耦:
- MCP Host (客户端):如 Claude Desktop、Cursor、Claude Code,它们是协议的发起方,负责将用户的自然语言转化为具体的工具调用。
- MCP Server (服务端):暴露特定的 Tools、Resources 或 Prompts,为客户端提供实际的业务处理能力。
- 通信通道:支持标准的本地标准输入输出(Stdio)或远程的服务器发送事件(SSE)。

通过这种星型拓扑架构,你只需编写一次 MCP 服务,就可以无缝接入支持 MCP 的任意 AI 客户端中。
在接下来的部分中,我们将针对硬核开发者,介绍如何从零搭建一个属于自己的 MCP Server。
2. 第一步:环境与脚手架准备
在开始开发前,请确保你本地已安装 Node.js 环境(推荐 Node.js v18 或以上版本,建议使用 NVM 进行多版本管理)。
你可以通过以下命令检查环境:
node --version
npm --version
初始化项目的核心方法
初始化 MCP 项目主要有三种途径:
- 方法一(官方脚手架):使用 Anthropic 官方提供的
create-server工具一键生成 TypeScript 模板:bashnpx @modelcontextprotocol/create-server my-mcp-server --name "My MCP Server" --description "A custom MCP server" - 方法二(手动配置):如果你希望完全掌控依赖与项目结构,可以手动搭建项目。这也是我们本文重点演示的方案,能够让你彻底搞懂依赖关系。
- 方法三(Fork 官方生态):直接在官方的 Server 汇总仓库 中挑选一个现成的 Server(如 filesystem、git、postgres 等),在其基础上进行二次开发。
3. 第二步:手动搭建 Node.js TypeScript MCP 项目
为了深入理解 MCP 协议的工作原理,我们采用方法二(手动配置)来构建一个用于本地读取图片的 MCP 服务。
3.1 项目初始化与依赖安装
在控制台中执行以下命令,创建项目文件夹并初始化 package.json:
mkdir image-processor
cd image-processor
npm init -y
mkdir src
touch src/index.ts
接下来,我们需要编辑 package.json。MCP 服务的运行依赖官方的 @modelcontextprotocol/sdk,同时我们使用 zod 进行工具入参的强类型校验,使用 zod-to-json-schema 将 Zod 格式自动转换为大模型所需的 JSON Schema。
请将 package.json 替换为以下内容:
{
"name": "mcp-image-processor",
"version": "1.0.0",
"description": "A custom MCP server for image reading and processing",
"type": "module",
"bin": {
"mcp-image-processor": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod +x build/index.js",
"prepublishOnly": "npm run build"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.4",
"zod": "^3.24.1",
"zod-to-json-schema": "^3.24.1"
},
"devDependencies": {
"@types/node": "^22.10.2",
"typescript": "^5.7.2"
}
}
运行安装命令:
npm install
3.2 配置 TypeScript 编译环境
在项目根目录下创建 tsconfig.json 文件,并填入以下现代 Node.js 编译选项:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
3.3 编写 MCP 核心逻辑
打开 src/index.ts,写入如下完整的核心代码。这段代码主要做了三件事:
1. 实例化 Server 对象,定义服务名与版本。
2. 注册 ListToolsRequestSchema 处理器,向 AI 客户端声明支持的 Tool 列表(包含入参 JSON Schema)。
3. 注册 CallToolRequestSchema 处理器,实现具体的工具执行逻辑,即读取本地图片并转化为 Base64 返回。
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { zodToJsonSchema } from "zod-to-json-schema";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ToolSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import path from "path";
import fs from "fs/promises";
const ToolInputSchema = ToolSchema.shape.inputSchema;
type ToolInput = z.infer<typeof ToolInputSchema>;
// 1. 初始化 MCP Server
const server = new Server(
{
name: "image-processor",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 定义参数 Schema,限制输入路径为字符串
const ReadImageArgsSchema = z.object({
path: z.string().describe("本地图片的绝对路径"),
});
// 2. 声明暴露的工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "read-image",
description: "读取本地指定路径的图片,并返回 Base64 编码与 MIME 类型",
inputSchema: zodToJsonSchema(ReadImageArgsSchema) as ToolInput,
},
],
};
});
// 3. 处理工具具体调用的分发逻辑
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (name === "read-image") {
const parsed = ReadImageArgsSchema.safeParse(args);
if (!parsed.success) {
throw new Error(`参数校验失败: ${parsed.error.message}`);
}
const imagePath = path.resolve(parsed.data.path);
// 读取文件 Buffer
const imageBuffer = await fs.readFile(imagePath);
const base64String = imageBuffer.toString("base64");
// 提取后缀并识别对应的 MIME 类型
const ext = path.extname(imagePath).toLowerCase();
const mimeType =
{
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
}[ext] || "application/octet-stream";
return {
content: [
{
type: "image",
data: base64String,
mimeType: mimeType,
},
],
};
} else {
throw new Error(`未知的工具: ${name}`);
}
} catch (error: any) {
console.error("执行时捕获异常:", error);
return {
isError: true,
content: [
{
type: "text",
text: `处理图片失败: ${error.message}`,
},
],
};
}
});
// 4. 建立基于 Stdio (标准输入输出) 的进程通信通道
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Image Processor Server running on stdio");
}
main().catch((error) => {
console.error("启动失败:", error);
process.exit(1);
});
⚠️ 核心排坑点:为什么 Server 日志必须输出到
console.error? 因为本服务是通过命令行标准输入输出(Stdio)进行通信的,stdout是协议数据传输的专用通道。如果你在代码中误用了console.log()来输出普通调试日志,该信息会被客户端错误地当做 MCP 协议报文解析,导致协议直接崩溃!所有的调试与生命周期日志必须输出至stderr(即使用console.error)。
在开发完成后,执行以下命令进行本地 TypeScript 编译:
npm run build
编译成功后,你会在根目录下看到生成的 build/index.js 产物。
4. 第三步:本地联调与验证
由于 MCP 协议是无状态的、由客户端驱动的,我们通常需要借助外部工具或客户端来验证它是否正常工作。
4.1 方式一:接入 Cursor 进行真实场景测试
如果你使用的是 Cursor 编辑器,你可以通过图形界面直接接入:
1. 打开 Cursor,进入 Settings -> Models -> MCP。
2. 点击 + Add New MCP Server。
3. 配置如下参数:- Name: image-processor- Type: command- Command: node c:/你的路径/image-processor/build/index.js (请替换为实际绝对路径)

4. 点击保存后,看到状态显示为绿色的 Connected 即表示接入成功。
现在你就可以在 Cursor Chat 或 Composer 模式下直接对 AI 说:“帮我分析一下 d:/mock/test_image.png 这张架构图画了什么”,AI 就会自动调用 read-image 抓取该图片并完成分析!
4.2 方式二:使用官方 MCP Inspector 独立调试
如果你不想频繁重启 AI 客户端,或者需要检查协议的收发包报文,推荐使用官方的 MCP Inspector。这是一款功能极其强大、带 Web UI 的进程级联调工具。
1. 全局安装调试器:bashnpm install -g @modelcontextprotocol/inspector
2. 启动 Inspector(需要传入你的 server 入口绝对路径):bashnpx @modelcontextprotocol/inspector node c:/你的路径/image-processor/build/index.js
3. 启动成功后,控制台会输出一个本地 Web UI 地址(通常是 http://localhost:5173/)。
4. 在浏览器中打开该地址,点击左上角的 Connect 按钮建立会话,你就可以直观地在 UI 界面中看到注册的 read-image 工具。填入参数,点击 Run Tool 即可立即发起调用测试,还能实时查看到底层的 JSON-RPC 通信数据报文。
5. 第四步:打包发布到 npm
当你的 MCP 服务开发稳定,希望分享给团队或全球开发者使用时,最佳方式是将其发布为独立的 npm 包。
1. 配置 binary 入口:在 package.json 中配置 "bin" 字段指向构建出来的 index.js(已在 package.json 模板中配置完成)。
2. 构建打包:bashnpm run build
3. 发布 npm(请确保已登录 npm 账号):bashnpm publish
4. 共享接入:发布成功后,其他开发者无需将你的源码拉到本地,直接在 Cursor 或 Claude Desktop 的配置文件中将启动命令改为:json"image-processor": {"command": "npx","args": ["-y", "你的npm包名"]}即可一键完成拉取与启动!
总结:AI 工具革命的新起点
MCP 将大模型从“单兵作战”推进到了“万物互联”的新时代。从前端工程师的角度来看: - Claude Desktop / Cursor 扮演着类似浏览器的底座角色。 - MCP Server 则是各种提供专业数据的网站与微型 API 站点。 - 本地文件与远程接口就是底层的后端支撑。
通过极简的协议契约,我们用几十行 TypeScript 就可以将复杂的本地能力完美桥接到大模型中。赶紧动手编写一个属于你自己的 MCP Server 吧,给你的 AI 装上划时代的“手脚”!

长按二维码关注 “边学边练”