从零构建MCP服务器:极简Node.js实现与Cursor IDE集成指南
1. 项目概述一个极简的MCP服务器起点如果你正在使用Cursor IDE并且对Model Context ProtocolMCP这个能让AI助手访问外部工具和数据的新协议感到好奇但又被官方示例的复杂度劝退那么这个名为“minimal-mcp-server”的项目就是你一直在找的完美起点。它本质上是一个用Node.js编写的、代码量极少的MCP服务器实现核心目标只有一个帮你用最快的方式理解MCP服务器是如何工作的并让你能立刻上手进行修改和实验。我自己在初次接触MCP时面对官方SDK和文档感觉就像拿到了一台精密仪器的所有零件却不知道从哪里开始组装。这个项目则直接给了你一个已经能跑起来的“最小可行产品”。它剥离了所有非必要的配置和抽象将MCP服务器的核心逻辑浓缩在了一个文件里。对于开发者来说这意味着你可以直接看到“骨骼”和“关节”而不是被“肌肉”和“皮肤”所干扰。无论你是想学习MCP协议的原理还是想快速为自己的工具比如内部API、数据库或文件系统构建一个MCP适配器这个项目都能为你节省大量搭建基础框架的时间。2. MCP核心概念与项目定位解析在深入代码之前我们有必要先厘清几个关键概念这能帮助你理解这个“极简”项目到底简在了哪里以及它为你承担了哪些工作。2.1 什么是Model Context Protocol (MCP)你可以把MCP想象成AI助手如Cursor里的AI Agent和外部世界之间的一个“标准化插座”。在没有MCP之前如果你想让你用的AI工具去查询数据库、读取特定文件或调用某个API通常需要依赖插件生态或者等待工具官方集成过程非常被动且碎片化。MCP定义了一套通用的通信协议任何符合MCP标准的服务器就像我们这个项目都可以将自己提供的“能力”称为Tools工具或Resources资源暴露出来。然后任何支持MCP的客户端如Cursor IDE就能自动发现并使用这些能力。举个例子你写了一个MCP服务器它提供了一个“查询本周GitHub提交记录”的工具。一旦在Cursor中配置好这个服务器你就可以直接在AI聊天框里说“帮我看看我这周提交了哪些代码”AI助手就能通过MCP调用你的服务器来获取数据并回答你。这极大地扩展了AI助手的能力边界。2.2 项目定位为什么“极简”至关重要官方提供的modelcontextprotocol/sdk包功能完整但同时也包含了许多高级特性如复杂的资源管理、工具输入验证Schema等。对于一个新手来说直接阅读官方示例可能会被大量的样板代码和配置选项淹没。这个“minimal-mcp-server”项目的聪明之处在于它做了一个极端但有效的取舍只实现MCP协议所要求的最基本通信框架并提供一个清晰无误的“工具”注册与响应示例。它没有数据库连接没有配置文件没有错误恢复机制甚至没有多个工具。它的存在就是为了回答一个问题“一个能跑起来的最简单的MCP服务器代码到底长什么样”这种设计带来了几个好处学习成本极低你可以在10分钟内通读并理解全部代码。修改风险小因为代码少结构清晰你想添加任何新功能比如连接你的数据库时都知道该在哪里动手不怕改崩。完美的调试模板当你的复杂MCP服务器出现通信问题时你可以回退到这个最小版本快速排查是协议层的问题还是你自己业务逻辑的问题。3. 项目结构与核心代码深度拆解让我们打开项目通常它只有一个核心文件例如server.js或index.js其代码结构直接映射了MCP服务器的生命周期。3.1 依赖与服务器初始化项目唯一的核心依赖就是官方的MCP SDK。在package.json中你会看到类似这样的配置{ dependencies: { modelcontextprotocol/sdk: ^0.4.0 } }使用官方SDK是绝对正确的选择它处理了所有与MCP协议版本兼容性、消息序列化/反序列化、传输层stdio通信等底层复杂问题让我们可以专注于业务逻辑。服务器初始化的代码通常如下import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; // 1. 创建Server实例 const server new Server( { name: minimal-mcp-server, version: 1.0.0, }, { capabilities: { tools: {}, }, } );这里有两个关键对象Server实例这是SDK的核心类。第一个参数是服务器的元信息名称、版本这些信息会被客户端获取并显示。第二个参数capabilities声明了本服务器提供的能力。这里tools: {}是一个占位符我们稍后会在这里注册具体的工具。StdioServerTransport这是传输层。MCP服务器通常通过标准输入输出stdio与客户端如Cursor通信这是一种简单、跨进程的通信方式。StdioServerTransport封装了这部分细节。注意在实际操作中务必确保name字段具有唯一性和描述性尤其是在你运行多个MCP服务器时这有助于在Cursor的设置界面中区分它们。3.2 工具Tool的定义与注册这是整个服务器的“心脏”。MCP中的“工具”类似于一个函数它有名字、描述、参数定义并且可以被AI客户端调用。在极简示例中通常会定义一个工具比如叫echo用来回显用户输入的消息以此演示完整的调用流程。// 2. 定义工具 server.setRequestHandler(tools/list, async () { return { tools: [ { name: echo, description: Echo back the input message. A simple demonstration tool., inputSchema: { type: object, properties: { message: { type: string, description: The message to echo back, }, }, required: [message], }, }, ], }; }); // 3. 处理工具调用 server.setRequestHandler(tools/call, async (request) { if (request.params.name echo) { const message request.params.arguments?.message; return { content: [ { type: text, text: Echo: ${message}, }, ], }; } throw new Error(Tool not found); });让我们拆解这段代码tools/list处理器当客户端Cursor查询服务器有哪些可用工具时会触发这个处理器。我们返回一个工具列表。每个工具必须定义name调用标识符、descriptionAI和用户看到的描述和inputSchema参数JSON Schema。定义清晰的Schema至关重要它指导AI如何构造调用参数。tools/call处理器当AI决定调用某个工具比如用户说“请echo一下hello world”时会触发这个处理器。request.params包含了工具名(name)和调用参数(arguments)。我们的逻辑就是检查工具名从参数中取出message然后构造返回内容。返回格式必须遵循MCP协议content数组中的text字段就是返回给AI的文本结果。实操心得在定义inputSchema时尽可能详细和准确。例如如果一个参数应该是邮箱格式就在Schema中通过format: email注明。这能显著提高AI调用工具的准确率减少无效请求。3.3 服务器启动与连接最后将服务器与传输层连接并启动。// 4. 连接传输层并启动 async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(Minimal MCP server running on stdio); } main().catch((error) { console.error(Server error:, error); process.exit(1); });这里有几个细节使用console.error输出日志因为MCP协议使用stdio进行通信标准输出(stdout)被用于传输协议消息。所以任何调试或状态日志都应该输出到标准错误(stderr)避免污染通信通道。错误处理用catch块捕获启动和运行过程中的错误并退出进程。这对于调试和确保服务器崩溃后能被进程管理器重启非常重要。4. 在Cursor IDE中配置与运行实战让这个服务器在Cursor里跑起来是整个过程中最具“魔法”的一步但其实非常简单。4.1 本地运行服务器首先确保你的环境有Node.js。然后在项目根目录下# 安装依赖 npm install # 运行服务器通常定义在package.json的scripts里如 npm start node server.js运行后你会看到Minimal MCP server running on stdio的输出然后程序看起来就“挂起”了。这是正常的它正在等待来自stdio的客户端连接。4.2 在Cursor中配置MCP服务器这是关键步骤Cursor需要知道如何找到并启动你的服务器。打开Cursor IDE。进入设置Settings。通常可以通过菜单或快捷键如Cmd ,打开。在设置中搜索“MCP”或“Model Context Protocol”。你会找到“MCP Servers”或类似的配置区域。点击“Add New Server”或“Configure”。配置方式通常有两种这里我们使用“Command”方式因为它最直接Name: 给你这个服务器起个名字比如“My Minimal Echo Server”。Command: 这里填写启动你服务器的命令。因为你的项目就在本地你需要提供完整的路径。例如node /Users/yourname/path/to/minimal-mcp-server/server.jsArgs: 通常留空除非你的脚本需要额外参数。Environment: 一般不需要设置。重要提示确保Command中的路径是绝对路径并且指向你刚刚运行的那个JavaScript文件。相对路径在Cursor的上下文中可能无法正确解析。4.3 验证与使用保存配置后Cursor通常会尝试连接你配置的服务器。如何验证是否成功查看Cursor界面配置成功后在AI聊天界面有时会有一个微小的提示或图标表明已连接的MCP服务器。进行测试最直接的方式就是问AI。在Cursor的AI聊天框中输入“你能调用echo工具吗给我回显一个‘Hello MCP’。” 如果配置成功AI会理解你的指令调用echo工具并返回“Echo: Hello MCP”的结果。如果AI没有反应或者报错请检查Cursor的设置界面是否有错误提示如“Connection failed”。返回终端查看运行服务器的命令行窗口是否有错误输出。确认Command中的路径和脚本名完全正确。5. 从“极简”到“实用”扩展你的MCP服务器理解了这个最小版本你就可以开始动手改造它把它变成真正有用的工具。扩展主要围绕两个核心添加更多工具和接入真实数据源。5.1 添加新的工具假设我们想添加一个计算器工具add用于两数相加。 你需要修改两个地方在tools/list处理器中注册新工具server.setRequestHandler(tools/list, async () { return { tools: [ { name: echo, description: Echo back the input message., inputSchema: { /* ... 原有schema ... */ }, }, { name: add, description: Add two numbers together., inputSchema: { type: object, properties: { a: { type: number, description: The first number }, b: { type: number, description: The second number }, }, required: [a, b], }, }, ], }; });在tools/call处理器中实现新工具的逻辑server.setRequestHandler(tools/call, async (request) { if (request.params.name echo) { // ... 原有echo逻辑 ... } if (request.params.name add) { const { a, b } request.params.arguments; const sum a b; return { content: [ { type: text, text: The sum of ${a} and ${b} is ${sum}., }, ], }; } throw new Error(Tool not found); });保存文件重启服务器在终端按CtrlC停止再重新运行node server.js。然后在Cursor里你就可以直接对AI说“请用add工具计算一下15加27等于多少。”5.2 接入真实数据源以文件系统为例一个更实用的例子是创建一个文件阅读工具。这需要用到Node.js的内置模块fs。导入模块并定义工具import fs from fs/promises; // ... 其他导入 ... // 在tools/list中新增 { name: read_file, description: Read the content of a file at a given path., inputSchema: { type: object, properties: { filepath: { type: string, description: The absolute path to the file to read., }, }, required: [filepath], }, }实现工具调用逻辑if (request.params.name read_file) { const { filepath } request.params.arguments; try { const content await fs.readFile(filepath, utf-8); return { content: [ { type: text, text: Content of ${filepath}:\n\\\\n${content}\n\\\, }, ], }; } catch (error) { // 错误处理非常重要需要以AI能理解的方式返回 return { content: [ { type: text, text: Failed to read file ${filepath}: ${error.message}, }, ], isError: true, // MCP协议中表示此次调用结果是一个错误 }; } }注意事项文件系统操作涉及安全。在实际项目中你绝对不应该允许任意路径读取。必须进行路径校验例如将可访问范围限制在项目目录内防止AI被诱导读取敏感系统文件如/etc/passwd。这是一个重要的安全考量。5.3 进阶提供资源Resources除了工具MCP另一个核心概念是“资源”Resources。工具是让AI“做某事”而资源是让AI“读某物”。例如你可以将项目下的README.md文件作为一个资源发布给AIAI在需要了解项目信息时会自动读取这个资源的内容而无需你显式调用工具。实现资源比工具稍复杂需要处理resources/list列出资源和resources/read读取资源内容等请求。官方SDK文档中有详细示例。当你需要将静态或动态生成的内容如数据库查询结果视图、API文档以更自然的方式提供给AI时资源是更好的选择。6. 开发、调试与问题排查实录在实际开发扩展功能时你肯定会遇到各种问题。以下是我在开发过程中总结的排查清单和技巧。6.1 常见问题速查表问题现象可能原因排查步骤Cursor中配置服务器后显示“连接失败”或“错误”。1. 启动命令路径错误。2. 服务器脚本本身有语法错误启动即崩溃。3. Node.js环境问题。1. 在终端直接运行配置的完整命令看能否启动。2. 检查终端是否有明显的错误输出如SyntaxError。3. 确认Node.js版本符合要求通常18。服务器在终端运行正常但Cursor里AI无法调用工具或说“找不到工具”。1.tools/list返回的格式不正确。2. 工具名拼写错误大小写敏感。3. Cursor缓存了旧的服务器信息。1. 确保tools/list返回的JSON结构完全正确工具名与tools/call中判断的一致。2. 重启Cursor或在其设置中禁用再重新启用该MCP服务器以刷新连接。AI调用了工具但返回结果不符合预期或参数错误。1.inputSchema定义不清晰导致AI误解参数。2.tools/call逻辑中对参数的处理有bug。3. 服务器逻辑抛出未捕获的异常。1. 在tools/call处理器开始处用console.error打印收到的request.params检查参数是否正确传入。2. 加强工具内部的错误处理确保任何错误都能被捕获并格式化成isError: true的响应返回。修改服务器代码后Cursor中的行为没有变化。Cursor没有重新启动你的服务器进程。它可能维持着旧的连接。在终端重启你的服务器进程CtrlC后重新运行。更可靠的方法是在Cursor设置里先禁用该服务器保存再启用这会触发Cursor重新建立连接。6.2 高效的调试技巧充分利用console.error这是你最好的朋友。在tools/list、tools/call等处理器的关键位置打印日志。例如server.setRequestHandler(tools/call, async (request) { console.error([CALL] Received request for tool:, request.params.name); console.error([CALL] Arguments:, JSON.stringify(request.params.arguments)); // ... 处理逻辑 ... });所有日志都会输出到你的终端让你清晰看到整个调用流程。结构化返回错误当工具执行失败时不要只是throw new Error。按照MCP协议返回带有isError: true的响应这样AI能理解这是一个工具执行错误而不是通信协议错误并能将错误信息友好地呈现给用户。从简单到复杂每次只添加或修改一个功能并立即测试。先确保echo工具工作再添加add最后集成复杂的文件操作或网络请求。这能帮你快速定位问题所在。参考官方文档与社区modelcontextprotocol.io是权威参考。遇到协议层面的问题先去查阅官方文档。此外Github上已经有许多开源的非官方MCP服务器如用于数据库、Jira、Notion等的服务器阅读它们的源码是学习最佳实践的绝佳途径。这个“minimal-mcp-server”项目就像一把钥匙为你打开了MCP世界的大门。它本身功能简单但其价值在于提供了一个无干扰、零认知负担的起点。通过解剖它你不仅学会了如何运行一个MCP服务器更重要的是理解了MCP服务器与客户端交互的基本范式。接下来你就可以将任何你熟悉的API、数据库查询或本地脚本封装成一个个MCP工具极大地提升你在Cursor等AI原生环境中的工作效率和创造力。真正的乐趣始于你动手将它改造成专属工具的那一刻。