1. 先搞清楚 MCP Server 是什么,以及为什么需要它
如果你最近在关注 AI 辅助编程,尤其是使用 Cursor 这类工具,可能会频繁听到MCP Server这个词。它听起来很技术,但核心要解决的问题其实很直接:让 AI 助手(比如 Cursor 里的 AI 模型)能够安全、可控地访问和使用你本地的工具、数据或服务。
简单来说,你可以把 MCP Server 理解为一个“翻译官”或“适配器”。AI 模型本身无法直接操作你的文件系统、数据库或调用某个本地 API。MCP Server 就负责定义一套标准接口,告诉 AI:“你可以通过我,用这些特定的‘工具’(Tools)或‘资源’(Resources)来做事。” 然后,当你在 Cursor 里对 AI 说“帮我把当前目录下的所有 .ts 文件整理到一个列表里”时,AI 就能通过你写的 MCP Server,调用一个“读取目录”的工具来完成这个任务。
所以,这个实战教程的价值在于,它教你如何从零开始,用几条清晰的Prompt来引导 AI(比如 Claude 或 GPT-4),快速搭建一个能实际工作的 MCP Server。这比单纯看文档要高效得多,因为文档往往告诉你“是什么”,而 Prompt 工程则直接引导你“怎么做”,并在这个过程中让你理解背后的原理。
最适合看这篇教程的人:
- 正在使用 Cursor、Claude Desktop 等支持 MCP 协议的 AI 编程工具的开发者。
- 希望扩展 AI 助手能力,让其能安全接入自己内部工具、私有 API 或特定数据源的团队。
- 对 TypeScript/Node.js 有基本了解,想学习如何将 AI 能力与现有工作流结合的工程师。
最关键的一点:通过这个教程,你获得的不是仅仅一个服务器代码,而是一套“用 Prompt 驱动复杂开发任务”的方法论。你会发现,写好 Prompt 让 AI 帮你写代码,比自己从头吭哧吭哧写要快得多,而且 AI 还能帮你考虑一些你可能会忽略的边界情况。
2. 动手前的环境与概念准备
在开始跟着 Prompt 搭建之前,你需要确保环境就绪,并理解几个关键概念,这样 AI 生成的代码你才能看得懂、改得了。
2.1 核心环境配置
- Node.js 环境:MCP Server 标准实现目前主要基于 Node.js。你需要安装Node.js 18或更高版本。可以在终端运行
node -v和npm -v来确认。 - TypeScript:教程和社区示例大量使用 TypeScript,因为它能提供更好的类型安全和开发体验。确保已全局安装 TypeScript:
npm install -g typescript。 - 代码编辑器/IDE:强烈推荐使用Cursor或VS Code。本教程的 Prompt 思路在 Cursor 中实践效果最佳,因为它深度集成了 AI 能力。如果你用 VS Code,需要安装相应的 AI 插件(如 Continue、Claude for VS Code 等)。
- MCP 基础包:你需要安装
@modelcontextprotocol/sdk这个官方 SDK。这是构建任何 MCP Server 的基石。
2.2 必须理解的三个核心概念
在给 AI 下 Prompt 前,你自己得先明白要让 AI 做什么。MCP 协议主要围绕这三个概念展开:
- Server(服务器):就是你将要构建的这个程序。它启动后,会通过标准输入输出(stdio)或 HTTP 等方式,等待 AI 客户端(如 Cursor)的连接和指令。
- Tools(工具):这是 Server 向 AI 暴露的核心能力。每个 Tool 都有一个名字、描述、输入参数定义(JSON Schema)和一个执行函数。例如,一个
read_file工具,参数是file_path,执行函数就是读取该路径文件并返回内容。AI 只能调用你明确声明和提供的 Tools。 - Resources(资源):你可以理解为一种只读的“数据源”。AI 可以通过 URI 来请求(
read)这些资源的内容,但不能修改。例如,你可以将本地一个配置文件、一个数据库查询结果封装成 Resource 供 AI 参考。
为什么先理解这些?因为你的 Prompt 需要清晰地告诉 AI:“请帮我创建一个 MCP Server,它需要提供 A、B、C 这几个 Tools,以及 X、Y 这几个 Resources。” 如果你自己都搞不清要什么,AI 生成的代码就会偏离目标。
2.3 项目初始化
打开你的终端,创建一个新的目录并初始化项目:
mkdir my-first-mcp-server cd my-first-mcp-server npm init -y然后安装核心依赖:
npm install @modelcontextprotocol/sdk npm install -D typescript @types/node tsx在package.json中,添加一个启动脚本:
{ "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc", "start": "node dist/index.js" } }创建tsconfig.json文件:
{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules"] }最后,创建源代码目录和入口文件:
mkdir src touch src/index.ts现在,你的基础开发环境就准备好了。接下来,就是利用 Prompt 的力量,让 AI 帮你填充src/index.ts的内容。
3. 五条核心 Prompt 实战拆解
下面这五条 Prompt 是一个循序渐进的构建指南。我建议你在 Cursor 里新建一个 Chat,并将对话上下文关联到当前项目,然后一条一条地喂给 AI(比如 Claude 3.5 Sonnet 或 GPT-4)。
3.1 Prompt 1:创建服务器骨架与基础工具
你的输入(Prompt):“我将使用@modelcontextprotocol/sdk构建一个 MCP Server。请先为我创建一个最基本的服务器骨架。这个服务器需要通过 stdio 传输。同时,请先实现一个最简单的工具,叫做get_server_info,当被调用时,返回一个简单的 JSON 对象,包含服务器名称name和当前时间戳timestamp。请确保代码使用 TypeScript,并包含必要的注释。”
AI 会做什么及输出要点:AI 会生成src/index.ts的初始内容。它会:
- 导入
Server类和相关类型。 - 创建一个
Server实例。 - 使用
server.setRequestHandler来定义tools/list和tools/call的处理逻辑。 - 在
tools/list中返回get_server_info工具的定义(包括名称、描述和参数 schema——本例无参数)。 - 在
tools/call中,处理对get_server_info的调用,执行函数并返回结果。 - 最后启动服务器,监听 stdio。
你需要检查和理解的地方:
- 工具定义:看
get_server_info工具的inputSchema是否为{ type: “object”, properties: {} },这表示它不需要输入参数。 - 结果返回:看
tools/call处理函数中,是否正确返回{ content: [{ type: “text”, text: JSON.stringify({ name: “My First MCP Server”, timestamp: Date.now() }) }] }。MCP 协议要求返回的内容是特定格式的数组。 - 启动方式:最后应该是
server.connect(stdio).then(() => console.error(‘MCP Server running…’));。注意是console.error,因为 stdout 要留给协议通信。
实测感:跑通这个最简单的工具至关重要。它能验证你的环境、依赖和基础通信链路是否正常。先别急着加复杂功能。
3.2 Prompt 2:添加一个实用的文件读取工具
你的输入(Prompt):“很好。现在,请为这个服务器添加一个新的工具,叫做read_file。这个工具应该接受一个字符串参数file_path,描述是‘读取指定路径的文本文件内容’。在tools/call中实现其逻辑:使用 Node.js 的fs/promises读取文件,并以文本形式返回内容。请妥善处理错误,例如文件不存在时,返回一个用户友好的错误信息。同时,更新tools/list的返回,包含这个新工具。”
AI 会做什么及输出要点:AI 会修改代码,主要更新两个部分:
tools/list处理器:在返回的列表中添加第二个工具定义。inputSchema会包含file_path参数,类型为string。tools/call处理器:添加一个if (request.params.name === “read_file”)的分支。在这个分支里,使用await readFile(request.params.arguments?.file_path, ‘utf-8’),并将结果包装返回。它应该用try…catch包裹,在catch中返回错误信息。
你需要检查和理解的地方:
- 参数验证:AI 生成的代码可能不会深入验证
file_path是否为空或是否为绝对路径。这是一个潜在的改进点,但对于第一个版本,能处理基本错误即可。 - 错误格式:MCP 协议中,工具调用错误应该通过返回
{ content: […], isError: true }来标示。检查 AI 是否正确地设置了isError: true。 - 导入语句:确保文件顶部正确添加了
import { readFile } from ‘fs/promises’;。
边界感:这个工具很强大,但也危险。它允许 AI 读取你文件系统上的任何文件(在进程权限内)。在实际生产用途中,你必须严格限制可访问的路径范围,比如限制在项目目录内。这里为了学习,我们先保持其通用性,但心里要有这根弦。
3.3 Prompt 3:实现资源(Resources)列表与读取
你的输入(Prompt):“现在,我想引入 Resources 的概念。请让服务器在初始化时,扫描当前项目目录下的所有.md文件,并将它们作为 Resources 公布。每个 Resource 的 URI 可以设为file://${filePath}。同时,请实现resources/list和resources/read请求处理器。resources/list返回这些 .md 文件的列表(包含 URI 和名称)。resources/read则根据请求的 URI 读取对应的 .md 文件内容并返回。”
AI 会做什么及输出要点:这是一个关键升级,AI 需要:
- 添加
readdir导入,用于扫描目录。 - 在服务器启动时(或在一个函数中),获取当前目录下所有
.md文件列表,并缓存起来。 - 实现
server.setRequestHandler对resources/list的处理:返回一个Resources列表,每个元素包含uri和name(可以用文件名)。 - 实现
resources/read的处理:解析请求中的uri,提取文件路径,然后读取文件内容返回。同样需要错误处理。
你需要检查和理解的地方:
- 路径处理:AI 生成的 URI 可能类似
file://${path.join(__dirname, ‘..’, file)}。确保这个路径解析是正确的,并且resources/read时能反向解析出来。 - 初始扫描时机:代码可能在服务器启动时同步扫描,这对于小目录没问题。如果文件很多,要考虑异步初始化或懒加载。
- MIME 类型:在
resources/read返回时,可以指定mimeType: “text/markdown”。检查 AI 是否添加了这个细节,这有助于客户端更好地处理内容。
避坑感:这里最容易出问题的是 URI 的格式和解析。如果resources/list返回的 URI 和resources/read请求的 URI 对不上,就会读不到文件。第一次实现后,一定要让 AI 客户端(如 Cursor)去请求一下资源列表,看是否能正确显示。
3.4 Prompt 4:连接 Cursor 并进行集成测试
你的输入(Prompt):“服务器代码看起来差不多了。现在,请指导我如何将这个 MCP Server 连接到 Cursor IDE 中进行测试。我需要修改 Cursor 的哪些配置?请给出具体的配置步骤和示例。另外,请在我们的服务器代码中添加必要的日志输出,以便在调试时能看到连接和请求过程。”
AI 会做什么及输出要点:AI 会提供两种主要的连接方式指导:
- 通过 Cursor 设置界面(推荐给初学者):指导你打开 Cursor Settings -> MCP Servers -> Add New Server。配置方式选择 “Command”,然后填入运行命令,例如
node /absolute/path/to/your/project/dist/index.js。你需要先运行npm run build生成dist目录。 - 通过配置文件
cursor/mcp.json:AI 会告诉你可以在项目根目录或用户全局目录创建这个文件,内容类似:{ “mcpServers”: { “my-local-server”: { “command”: “node”, “args”: [“dist/index.js”], “cwd”: “/absolute/path/to/your/project” } } }
同时,AI 会在服务器代码的关键位置(如连接建立、收到list/call/read请求时)添加console.error日志,方便你观察通信流程。
你需要检查和理解的地方:
- 命令路径:确保配置中的路径是绝对路径。相对路径在 Cursor 的上下文中可能无法正确解析。
- 构建与运行:记住,每次修改
src/index.ts后,需要重新运行npm run build来编译 TypeScript,或者直接使用tsx在开发时运行源码(配置命令为npx tsx src/index.ts)。 - 日志观察:连接 Cursor 后,你需要打开 Cursor 的“开发者工具”或查看其日志输出位置(不同平台不同),才能看到你服务器通过
console.error打印的日志。这是排查连接问题的关键。
实测感:这一步是“临门一脚”。很多人在此卡住。最常见的问题是路径不对、命令执行权限问题,或者端口/stdio 冲突。如果连接失败,首先检查 Cursor 的错误日志,然后回到终端手动运行你的服务器命令,看是否能正常启动且不报错。
3.5 Prompt 5:功能增强与错误处理优化
你的输入(Prompt):“我们已经有了一个可工作的原型。现在,请帮我进行以下增强和优化:
- 安全性:修改
read_file工具,将其访问范围限制在当前项目目录(即process.cwd())及其子目录下,防止路径遍历攻击。 - 健壮性:为所有工具调用和资源读取添加更全面的错误处理。不仅处理文件不存在,还要处理无权限、读取错误等情况,返回结构化的错误信息。
- 可扩展性:将工具和资源的定义与处理逻辑拆分成独立的模块或类,让
index.ts主文件只负责服务器初始化和路由。请展示一个简单的重构思路。”
AI 会做什么及输出要点:这条 Prompt 引导 AI 从“能跑”到“好用、安全”。
- 路径安全:AI 会引入
path模块,在read_file和resources/read中,使用path.resolve和path.relative来判断请求路径是否在项目根目录内。如果..试图跳出范围,则拒绝请求。 - 错误处理:AI 会创建统一的错误处理函数,或是在每个
try…catch中更细致地判断错误类型(instanceof Error,检查code属性如’ENOENT’,’EACCES’),并返回更具描述性的错误文本。 - 代码重构:AI 可能会建议创建
tools.ts和resources.ts文件。tools.ts导出所有工具的定义(Tool对象)和对应的执行函数。resources.ts管理资源列表和读取逻辑。然后在index.ts中导入并注册它们。这使得添加新工具变得非常容易。
你需要检查和理解的地方:
- 安全边界:检查路径检查逻辑是否严密。最简单的办法是:
const resolvedPath = path.resolve(projectRoot, requestedPath); if (!resolvedPath.startsWith(projectRoot + path.sep)) { throw new Error(‘Access denied’); }。 - 错误信息有用性:返回给 AI 的错误信息应该能指导用户(或 AI 本身)下一步该做什么。例如,“文件不存在”比“读取错误”更好。
- 重构的清晰度:重构后的代码应该更易读。如果 AI 的重构让你感到更混乱,可以要求它用更简单的方式,或者先不进行这一步,保持原有结构。
经验注入:到这一步,你已经拥有了一个功能相对完整、有一定安全意识的 MCP Server。这个过程的核心收获不是代码本身,而是你通过精心设计的 Prompt,像项目经理一样,分阶段、有重点地引导 AI 完成了从骨架到血肉,再到安全加固的整个开发流程。这比单纯复制粘贴代码要深刻得多。
4. 在 Cursor 中实际使用与效果验证
服务器搭建好并成功连接到 Cursor 后,怎么验证它真的在工作?
4.1 验证工具调用
- 在 Cursor 的 Chat 界面中,直接输入:“请调用
get_server_info工具。” - Cursor 的 AI 应该识别到你连接的 MCP Server 提供了这个工具,并自动调用它。
- 你会在回复中看到类似
{“name”: “My First MCP Server”, “timestamp”: 172…}的结果。 - 同样,尝试:“读取
README.md文件的内容。” AI 应该会调用read_file工具并返回文件内容。
如果失败怎么办?
- AI 说“找不到工具”:检查 Cursor 的 MCP Server 配置是否正确,服务器进程是否在运行。查看 Cursor 日志,确认握手和工具列表交换是否成功。
- 调用出错:查看你的服务器日志(
console.error输出的内容),通常会有详细的错误堆栈。常见问题包括路径错误、权限不足、代码逻辑 bug。
4.2 验证资源访问
- 在 Chat 中输入:“列出所有可用的资源。” 或者更自然地说:“你有什么可参考的文档吗?”
- AI 应该会调用
resources/list,并返回你项目里所有.md文件的列表。 - 你可以接着说:“请给我看看
xxx.md的内容。” AI 会调用resources/read并返回该文件内容。
资源与工具的区别体验:你会发现,AI 在“思考”时,对 Resources 和 Tools 的使用方式略有不同。Tools 是 AI 主动“使用”的“能力”,而 Resources 更像是 AI 可以“查阅”的“资料库”。在设计你的 Server 时,可以根据这个特性来规划功能。
4.3 更复杂的场景测试
尝试一些组合指令,观察 AI 如何利用你的 Server:
- “帮我把
src目录下所有.ts文件的文件名列出来。”(这可能需要你新增一个list_directory工具,或者 AI 组合多次read_file?不,更好的方式是新增工具。你可以用 Prompt 让 AI 帮你添加这个工具。) - “根据
requirements.md里的描述,帮我规划一下项目结构。”(AI 会先读取该资源,获取上下文,然后再进行回答)。
这就是 MCP 的强大之处:你扩展了 AI 的感知和行动边界。它不再局限于对话历史,而是能实时、安全地与你本地环境交互。
5. 排查清单:当你的 MCP Server 不工作时
按照以下顺序检查,能解决 95% 的问题:
基础运行检查:
- 在项目目录下,能否直接运行
node dist/index.js或npx tsx src/index.ts并看到“MCP Server running…”日志,且进程不退出? - 如果启动失败,根据终端报错解决(通常是语法错误、依赖缺失)。
- 在项目目录下,能否直接运行
Cursor 连接配置检查:
- 配置中使用的命令和路径,是否能在终端中独立执行成功?
- Cursor 的 MCP Server 配置页面,该 Server 的状态是否是 “Connected” 或 “Ready”?如果是 “Error”,点击查看详情。
- 检查 Cursor 的日志(Help -> Toggle Developer Tools 打开控制台,或在日志文件中查找)。
协议通信检查:
- 在你的服务器代码中,是否在关键步骤(连接成功、收到请求)添加了日志?查看这些日志是否被打印。
- 如果根本没收到
tools/list请求,说明连接或握手可能有问题。 - 如果收到了
list请求但没收到call请求,可能是 AI 认为工具不适用当前问题,或者工具描述不够清晰。
工具/资源逻辑检查:
- 当 AI 调用工具失败时,服务器返回的错误信息是什么?是否遵循了 MCP 的错误格式(
isError: true)? - 手动模拟调用:你可以在代码中临时写一个测试脚本来调用你的工具函数,看逻辑是否正确。
- 路径问题:所有文件路径都处理成绝对路径了吗?路径权限对吗?
- 当 AI 调用工具失败时,服务器返回的错误信息是什么?是否遵循了 MCP 的错误格式(
依赖与版本检查:
@modelcontextprotocol/sdk的版本是否与 Cursor 兼容?查看 Cursor 文档或社区,了解其兼容的协议版本。- Node.js 版本是否满足要求?
一个很常见的坑:你的服务器代码使用了 ES Module (import/export),但package.json中没有设置“type”: “module”,或者启动命令不对。确保你的环境一致。使用tsx通常能避免很多模块问题。
6. 下一步:从玩具到生产力的思考
通过这 5 条 Prompt,你已经走完了从零到一的闭环。接下来,可以考虑如何让它真正产生价值:
- 连接内部系统:将 MCP Server 作为桥梁,让 AI 能安全查询公司内部数据库(只读)、调用内部 API(如创建 JIRA 工单、查询 CI/CD 状态)、读取监控图表。关键是做好权限控制和审计。
- 封装复杂工作流:比如一个“部署预览”工具,AI 调用后,Server 后端执行一系列 Git、Docker、kubectl 命令,最后将预览 URL 返回给 AI。这比让 AI 去生成一堆命令再让你复制粘贴要可靠得多。
- 动态资源:Resources 不一定非要是文件。它可以是一个动态生成的报告,比如“当前线上错误最多的 5 个服务”,Server 每次收到
resources/read请求时,实时调用内部系统获取数据并生成 Markdown 返回。 - 权限模型:实现一个简单的权限模型,不同的工具/资源对不同用户或不同上下文(项目)可见。这需要 Server 能识别调用上下文(MCP 协议支持部分元数据传递)。
最后一点经验:Prompt Engineering 对于开发 MCP Server 来说,其价值在于快速原型设计和逻辑描述。一旦核心流程跑通,后续的优化、测试、安全加固和部署,仍然需要你扎实的工程能力。不要指望单靠 Prompt 就能得到一个完美的生产级应用,但它绝对是帮你跨越“从想到做”这个鸿沟的超级杠杆。现在,你可以试着用同样的方法,去让 AI 帮你实现上面任何一个进阶想法了。