
在实际开发中我们经常需要让 AI 助手如 Cursor、Claude Desktop 等能够访问和处理项目内部或外部的特定数据源比如数据库、API 或本地文件。手动复制粘贴数据不仅低效而且难以保证上下文的一致性和准确性。Model Context ProtocolMCP正是为了解决这一问题而设计的开放协议它允许开发者构建标准的“服务器”MCP Server将任意数据源或工具的能力以结构化、安全的方式暴露给 AI 客户端。通过 MCPAI 助手可以直接“调用”这些能力就像调用一个函数一样从而获得实时、准确的信息。本文将以 TypeScript 为开发语言从零开始手把手教你构建一个功能完整的 MCP Server。我们将遵循 Matt Pocock 在其教程中强调的工程化实践通过 5 条核心的 Prompt 来驱动整个开发流程从项目初始化、协议理解、工具定义、资源暴露到最终的集成与测试。无论你是想为团队内部工具链增加 AI 能力还是希望探索 AI 代理Agent的更多可能性构建一个 MCP Server 都是极具价值的实践。通过本文你将掌握 MCP 的核心概念、TypeScript 开发 MCP Server 的完整流程并能够将其集成到 Cursor 等 IDE 中实现 AI 助手与你的专属数据或服务的无缝交互。1. 理解 MCP 协议AI 与工具之间的“通用插座”在开始编码之前我们必须先理解 MCP 要解决的根本问题以及它的工作模型。这决定了我们后续所有代码的结构和设计。1.1 MCP 是什么为什么需要它想象一下你的电脑有各种外设键盘、鼠标、打印机。它们通过 USB、蓝牙等标准接口与电脑通信。如果没有这些标准每个外设都需要专用的、复杂的驱动才能工作。MCP 就是 AI 世界里的“USB 协议”。它定义了一套标准让任何数据源如数据库、文件系统、API或工具如代码执行器、搜索引擎都能以统一的方式被 AI 模型“插拔”和使用。在没有 MCP 之前如果你想在 Cursor 里让 AI 查询公司内部的用户数据可能需要手动编写一个复杂的插件处理与 Cursor 的特定 API 集成。在 Prompt 里粘贴大量 JSON Schema 来描述你的 API。面临安全、权限控制和上下文管理的难题。MCP 通过标准化解决了这些问题标准化通信基于 JSON-RPC 协议服务器和客户端通过标准消息格式对话。能力声明服务器启动时主动向客户端声明“我能提供什么工具Tools和资源Resources”。结构化数据所有输入输出都是结构化的 JSON便于 AI 理解和处理。安全边界工具执行在独立的服务器进程中与 AI 模型本身隔离权限可控。1.2 MCP 的核心组件Server, Client, Tools Resources一个典型的 MCP 生态系统包含以下角色MCP Server我们将要构建的一个独立的进程它封装了对特定数据源或工具的操作。它向客户端宣告自己具备的能力。MCP Client如 Cursor, Claude Desktop集成在应用中的组件负责与一个或多个 MCP Server 通信并将 Server 提供的能力暴露给内部的 AI 模型。Tools工具这是 Server 提供的核心能力之一。一个 Tool 就像一个函数AI 可以调用它并传递参数。例如一个query_database工具AI 调用时传入 SQL 语句Server 执行并返回结果。Resources资源这是 Server 提供的另一种能力。Resource 代表一个可读的、内容可能变化的数据单元比如一个配置文件、一个 API 的实时状态页面。AI 可以“读取”这些资源来获取信息。资源通过 URI 标识。它们之间的关系如下图所示概念性描述[AI 模型在 Cursor 中] | v [Cursor 内置的 MCP Client] | (通过 stdio 或 SSE 通信) v [我们编写的 MCP Server] - [连接至真实数据源数据库/API/文件]我们的任务就是编写右下角的那个 MCP Server。1.3 开发前必须明确的技术栈和约束协议版本我们使用目前主流且稳定的MCP 协议。其核心通信基于 JSON-RPC。开发语言TypeScript。这是构建 MCP Server 最活跃的生态之一有官方和社区的良好支持。核心 SDK我们将使用modelcontextprotocol/sdk这个官方包它封装了协议细节让我们可以专注于业务逻辑。运行时Node.js建议版本 18。通信方式主要支持stdio标准输入输出这是与 Cursor 等客户端集成最简单的方式。也支持 SSEServer-Sent Events用于 HTTP 场景。项目初始化使用npm或yarn管理依赖用tsc或tsup进行构建。理解了这些我们就知道要构建的是一个运行在 Node.js 上、使用 TypeScript 编写、通过modelcontextprotocol/sdk与客户端通信的独立进程。2. 环境准备与项目初始化现在我们开始动手搭建开发环境并创建项目骨架。这是保证后续开发顺畅的基础。2.1 安装 Node.js 与包管理器首先确保你的系统已安装 Node.js。打开终端运行以下命令检查node --version npm --version # 或 yarn --version建议使用 Node.js 18 或更高版本。如果未安装请前往 Node.js 官网 下载 LTS 版本进行安装。2.2 创建项目并安装核心依赖我们创建一个全新的目录来开始我们的项目。# 1. 创建项目目录并进入 mkdir my-first-mcp-server cd my-first-mcp-server # 2. 初始化 package.json npm init -y # 3. 安装 TypeScript 和 Node.js 类型定义开发依赖 npm install -D typescript types/node # 4. 安装 MCP SDK生产依赖 npm install modelcontextprotocol/sdk # 5. 初始化 TypeScript 配置 npx tsc --init安装完成后你的package.json的dependencies和devDependencies应该类似这样{ name: my-first-mcp-server, version: 1.0.0, description: , main: dist/index.js, scripts: { build: tsc, start: node dist/index.js }, devDependencies: { types/node: ^20.0.0, typescript: ^5.0.0 }, dependencies: { modelcontextprotocol/sdk: ^1.0.0 } }2.3 配置 TypeScript 和项目结构默认的tsconfig.json配置可能不适合我们我们需要调整它以输出 CommonJS 模块到dist目录并包含必要的 ES 特性。打开tsconfig.json修改或确保包含以下关键配置{ compilerOptions: { target: ES2022, module: CommonJS, lib: [ES2022], outDir: ./dist, rootDir: ./src, strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, resolveJsonModule: true, declaration: true, declarationMap: true }, include: [src/**/*], exclude: [node_modules, dist] }然后创建项目源代码目录和入口文件mkdir src touch src/index.ts现在你的项目结构应该如下所示my-first-mcp-server/ ├── node_modules/ ├── src/ │ └── index.ts # 主入口文件 ├── package.json ├── package-lock.json ├── tsconfig.json └── .gitignore # 建议创建忽略 node_modules 和 dist2.4 验证基础环境在src/index.ts中写入最简单的代码验证环境是否正常。// src/index.ts console.log(MCP Server 环境检查正常);然后编译并运行npx tsc node dist/index.js如果终端成功输出MCP Server 环境检查正常说明 TypeScript 编译和 Node.js 运行环境都已就绪。3. 构建第一个 MCP Server实现工具Tools我们将从一个最简单的 Server 开始它只提供一个工具。这是理解 MCP SDK 工作流的最佳起点。3.1 理解 Server 生命周期与 SDK 使用模式使用modelcontextprotocol/sdk构建 Server 的核心步骤如下导入并创建 Server 实例传入 Server 的元信息名称、版本。定义并注册能力使用server.setRequestHandler()来处理客户端关于tools/list列出工具和tools/call调用工具的请求。启动 Server调用server.connect()并指定传输方式如 stdio。处理客户端请求在工具调用处理器中执行实际业务逻辑并返回结果。3.2 实现一个 “echo” 工具让我们实现一个最简单的工具它接收一个字符串并原样返回同时附上时间戳。这能帮助我们快速验证整个链路是否通畅。将src/index.ts的内容替换为以下代码// src/index.ts import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { CallToolRequestSchema, ListToolsRequestSchema, } from modelcontextprotocol/sdk/types.js; // 1. 创建 Server 实例 const server new Server( { name: my-first-mcp-server, version: 1.0.0, }, { capabilities: { tools: {}, // 声明本 Server 支持提供 tools }, } ); // 2. 处理客户端请求列出所有可用工具 server.setRequestHandler(ListToolsRequestSchema, async () { return { tools: [ { name: echo, description: 一个简单的回声工具返回输入的内容和当前时间戳。, inputSchema: { type: object, properties: { message: { type: string, description: 需要回声的消息内容, }, }, required: [message], }, }, ], }; }); // 3. 处理客户端请求调用特定工具 server.setRequestHandler(CallToolRequestSchema, async (request) { // 根据工具名分发处理逻辑 if (request.params.name echo) { const message request.params.arguments?.message as string; const timestamp new Date().toISOString(); // 这里是工具的核心逻辑 const result 回声${message}\n时间${timestamp}; // 返回结构化结果给客户端 return { content: [ { type: text, text: result, }, ], }; } // 如果请求的工具名未找到抛出错误 throw new Error(未知的工具: ${request.params.name}); }); // 4. 启动 Server使用 stdio 传输方式 async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(MCP Server 已启动并等待连接 (stdio)...); } main().catch((error) { console.error(Server 启动失败:, error); process.exit(1); });关键代码解释Server类MCP Server 的主类需要传入服务器信息和能力声明。StdioServerTransport这是与 Cursor 等客户端集成最常用的传输方式通过标准输入输出进行通信。ListToolsRequestSchema当客户端查询服务器有哪些工具时会发送此请求。我们的 handler 返回一个工具列表每个工具都需要定义name,description和inputSchema输入参数的 JSON Schema。CallToolRequestSchema当客户端调用某个工具时会发送此请求。我们的 handler 需要根据request.params.name识别是哪个工具从request.params.arguments中获取参数执行逻辑并返回指定格式的结果。结果必须包裹在content数组中通常我们返回type: text的文本内容。3.3 编译与独立运行测试在集成到 Cursor 之前我们可以先编译并直接运行这个 Server观察其输出。它会在启动后等待来自 stdio 的输入。# 编译 TypeScript npm run build # 直接运行 Server它会挂起等待连接 node dist/index.js此时程序会输出MCP Server 已启动并等待连接 (stdio)...到标准错误输出stderr然后等待。你可以按CtrlC终止它。目前我们无法手动测试工具调用因为需要一个 MCP 客户端来驱动。下一步我们将把它集成到 Cursor 中进行真实测试。4. 集成到 Cursor IDE 并进行测试Cursor 内置了 MCP Client 支持可以方便地加载本地开发的 MCP Server。这是验证我们 Server 是否工作的关键一步。4.1 配置 Cursor 以加载本地 MCP ServerCursor 通过一个全局配置文件来管理 MCP Server。配置文件的位置通常如下macOS/Linux:~/.cursor/mcp.jsonWindows:%USERPROFILE%\.cursor\mcp.json如果文件不存在请创建它。我们将把刚刚构建的 Server 添加到配置中。编辑mcp.json文件内容如下{ mcpServers: { my-first-server: { command: node, args: [ /ABSOLUTE/PATH/TO/your-project/dist/index.js ], env: {} } } }重要提示my-first-server是你给这个 Server 起的名字可以任意修改。args数组中的路径必须替换为你本地项目dist/index.js的绝对路径。例如在 macOS 上可能是/Users/yourname/Projects/my-first-mcp-server/dist/index.js。确保你已运行过npm run builddist/index.js文件确实存在。4.2 在 Cursor 中验证 Server 加载重启 Cursor修改配置文件后需要完全关闭并重新打开 Cursor 以使配置生效。打开 Cursor 设置在 Cursor 中进入Settings-Features-MCP Servers。你应该能看到你配置的my-first-server显示为已配置。检查日志打开一个项目或文件在 Cursor 的底部状态栏或输出面板中可能会看到 MCP 相关的日志表明 Server 正在被加载和连接。如果 Server 启动失败例如路径错误这里也会显示错误信息。验证工具可用性最直接的验证方式是使用 Cursor 的 Chat 功能。在 Chat 输入框中尝试输入“你能使用 echo 工具吗” 或者 “Call the echo tool with message ‘Hello MCP’”。如果配置成功Cursor 的 AI通常是 Claude应该能识别出echo工具并展示一个调用按钮或直接返回结果。4.3 通过 Prompt 驱动开发与测试这就是 Matt Pocock 教程中强调的“Prompt 驱动开发”的精髓。我们不需要手动编写复杂的测试脚本而是通过自然语言指令让 AI 来测试我们的 Server。你可以尝试在 Cursor Chat 中输入以下 Prompt 序列来测试Prompt 1 (列出工具):你现在可以使用哪些 MCP 工具预期响应AI 应该会列出echo工具及其描述。Prompt 2 (调用工具):使用 echo 工具发送消息 “测试一下 MCP 连接”。预期响应AI 会调用echo工具并返回类似“回声测试一下 MCP 连接\n时间2024-01-01T12:00:00.000Z”的结果。如果 AI 回复“我不知道如何使用这个工具”或没有反应请检查Cursor 是否已重启。mcp.json配置文件路径是否正确。终端中运行node dist/index.js是否报错可以单独运行查看。Cursor 的 MCP 设置界面是否有错误提示。4.4 调试技巧查看 Server 日志我们的 Server 将日志输出到stderr。在 Cursor 中这些日志可能不会直接显示。为了调试一个有效的方法是临时修改 Server 代码将传输方式改为简单的标准输入输出测试或者在 Cursor 之外手动模拟客户端进行测试。我们可以创建一个简单的测试脚本test-client.js// test-client.js - 这是一个非常简化的模拟测试 const { spawn } require(child_process); const serverProcess spawn(node, [dist/index.js]); serverProcess.stderr.on(data, (data) { console.error([Server STDERR]:, data.toString()); }); // 模拟一个简单的 JSON-RPC 请求 (列出工具) const listToolsRequest { jsonrpc: 2.0, id: 1, method: tools/list, params: {} }; serverProcess.stdin.write(JSON.stringify(listToolsRequest) \n); serverProcess.stdin.end(); serverProcess.stdout.on(data, (data) { console.log([Server STDOUT]:, data.toString()); }); serverProcess.on(close, (code) { console.log(子进程退出退出码 ${code}); });运行node test-client.js可以看到 Server 的原始输入输出帮助诊断协议层面的问题。但在大多数情况下通过 Cursor 的 Chat 进行功能测试已经足够。5. 扩展 Server添加资源Resources与复杂工具一个只会回声的 Server 实用价值有限。现在我们来扩展它添加更实用的“资源”和更复杂的“工具”构建一个模拟的“项目信息查询服务器”。5.1 设计 Server 能力项目信息查询假设我们想构建一个 Server让 AI 能读取资源获取一个固定的项目简介文档。使用工具get_file_info根据文件名获取该文件的模拟信息如大小、类型。search_code在模拟的代码库中搜索包含特定关键词的文件。5.2 实现资源Resources提供者资源是只读的、内容可能变化的 URI。我们需要处理resources/list列出资源和resources/read读取资源请求。更新src/index.ts在创建 Server 时声明支持资源能力并添加相应的请求处理器// 在文件顶部添加新的导入 import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, // 新增 ReadResourceRequestSchema, // 新增 } from modelcontextprotocol/sdk/types.js; // 更新 Server 的能力声明 const server new Server( { name: project-info-mcp-server, version: 1.0.0, }, { capabilities: { tools: {}, resources: {}, // 新增声明支持 resources }, } ); // ... (之前设置的 tools/list 和 tools/call 处理器保持不变) ... // 5. 处理客户端请求列出所有可用资源 server.setRequestHandler(ListResourcesRequestSchema, async () { return { resources: [ { uri: project://overview, name: 项目概览, description: 获取本项目的基本介绍信息。, mimeType: text/plain, }, ], }; }); // 6. 处理客户端请求读取特定资源 server.setRequestHandler(ReadResourceRequestSchema, async (request) { const uri request.params.uri; if (uri project://overview) { // 这里可以是从文件、数据库或API动态读取的内容 const overviewText 项目名称示例 MCP 服务器项目 项目描述这是一个演示 Model Context Protocol (MCP) 服务器功能的示例项目。 主要能力 1. 提供 echo 工具用于测试。 2. 提供项目概览资源。 3. 提供文件信息查询和代码搜索工具。 技术栈TypeScript, Node.js, modelcontextprotocol/sdk 状态开发中 最后更新${new Date().toLocaleDateString()}; return { contents: [ { uri: uri, mimeType: text/plain, text: overviewText, }, ], }; } throw new Error(未找到资源: ${uri}); });5.3 实现更复杂的工具现在在tools/call的处理器中添加对新工具的支持。我们更新之前的CallToolRequestSchema处理器// 替换或更新之前的 server.setRequestHandler(CallToolRequestSchema, ...) server.setRequestHandler(CallToolRequestSchema, async (request) { const toolName request.params.name; const args request.params.arguments || {}; if (toolName echo) { const message args.message as string; const timestamp new Date().toISOString(); const result 回声${message}\n时间${timestamp}; return { content: [ { type: text, text: result, }, ], }; } // 新增工具get_file_info if (toolName get_file_info) { const filename args.filename as string; if (!filename) { throw new Error(参数 filename 是必需的。); } // 模拟查询文件信息 const mockFileDatabase: Recordstring, { size: string; type: string } { index.ts: { size: 2.1 KB, type: TypeScript 源文件 }, package.json: { size: 0.5 KB, type: 项目配置文件 }, README.md: { size: 1.0 KB, type: Markdown 文档 }, }; const info mockFileDatabase[filename]; if (!info) { return { content: [ { type: text, text: 未找到文件 ${filename} 的信息。, }, ], }; } return { content: [ { type: text, text: 文件${filename}\n大小${info.size}\n类型${info.type}, }, ], }; } // 新增工具search_code if (toolName search_code) { const keyword args.keyword as string; if (!keyword) { throw new Error(参数 keyword 是必需的。); } // 模拟代码搜索 const mockCodeFiles [ { name: src/index.ts, content: import { Server } from modelcontextprotocol/sdk; }, { name: src/utils.ts, content: export function log(message: string): void { console.log(message); } }, { name: package.json, content: dependencies: { modelcontextprotocol/sdk: ^1.0.0 } }, ]; const results mockCodeFiles.filter(file file.content.toLowerCase().includes(keyword.toLowerCase()) ); if (results.length 0) { return { content: [ { type: text, text: 未找到包含关键词 ${keyword} 的代码文件。, }, ], }; } const resultText results.map(r - ${r.name}).join(\n); return { content: [ { type: text, text: 找到 ${results.length} 个包含 ${keyword} 的文件\n${resultText}, }, ], }; } throw new Error(未知的工具: ${toolName}); });别忘了更新tools/list处理器将新工具声明给客户端server.setRequestHandler(ListToolsRequestSchema, async () { return { tools: [ { name: echo, description: 一个简单的回声工具返回输入的内容和当前时间戳。, inputSchema: { type: object, properties: { message: { type: string, description: 需要回声的消息内容, }, }, required: [message], }, }, { name: get_file_info, description: 根据文件名获取模拟的文件信息大小、类型。, inputSchema: { type: object, properties: { filename: { type: string, description: 需要查询的文件名例如index.ts, }, }, required: [filename], }, }, { name: search_code, description: 在模拟的代码库中搜索包含特定关键词的文件。, inputSchema: { type: object, properties: { keyword: { type: string, description: 需要搜索的代码关键词, }, }, required: [keyword], }, }, ], }; });5.4 重新编译、配置与测试编译运行npm run build。更新 Cursor 配置如果需要如果 Server 名称或路径变了需要更新~/.cursor/mcp.json。这里我们只是更新了代码路径没变所以无需修改。重启 Cursor完全关闭再打开 Cursor或在其设置中尝试重新加载 MCP 配置。进行综合测试在 Cursor Chat 中尝试以下 PromptPrompt 3 (读取资源):读取一下项目概览资源project://overview。Prompt 4 (使用新工具查询文件):使用 get_file_info 工具查一下 index.ts 文件的信息。Prompt 5 (使用新工具搜索代码):使用 search_code 工具搜索包含 “dependencies” 关键词的文件。如果一切正常AI 应该能成功调用这些工具和资源并返回我们预设的模拟数据。这证明我们的 MCP Server 已经具备了提供多种结构化能力的功能。6. 生产环境考量与最佳实践到目前为止我们构建了一个用于学习和测试的 MCP Server。但要将其用于实际生产或团队共享还需要考虑更多因素。6.1 安全性权限与输入验证我们的示例 Server 非常简单但真实的 Server 可能连接数据库、调用内部 API 或执行系统命令。安全至关重要。输入验证与净化永远不要信任客户端传入的参数。即使有 JSON Schema 约束也要在业务逻辑中再次验证。// 不好的做法直接拼接 const query SELECT * FROM users WHERE name ${args.name}; // 好的做法使用参数化查询或严格验证 if (typeof args.name ! string || args.name.length 100) { throw new Error(无效的用户名参数); } // 然后使用参数化查询库权限控制MCP Server 进程本身运行在某个用户权限下。确保该用户只有执行必要操作的最小权限。不要在 Server 中以 root 或高级别权限运行。敏感信息数据库密码、API 密钥等不应硬编码在代码中。使用环境变量或安全的配置管理服务。# 启动时传入环境变量 MCP_DB_PASSWORDsecret123 node dist/index.js// 在代码中读取 const dbPassword process.env.MCP_DB_PASSWORD; if (!dbPassword) { throw new Error(数据库密码未配置); }6.2 错误处理与日志健壮的 Server 需要清晰的错误处理和日志记录方便排查问题。结构化错误返回在tools/call处理器中除了throw new Error更友好的做法是返回结构化的错误信息。try { // ... 业务逻辑 ... } catch (error: any) { console.error([工具 ${toolName} 执行失败], error); return { content: [{ type: text, text: 执行工具时发生错误${error.message} }], isError: true, // MCP 协议中表示这是一个错误响应 }; }分级日志使用console.error记录错误console.warn记录警告console.log或console.info记录一般信息。考虑使用winston或pino等日志库以便输出到文件或日志系统。6.3 性能与资源管理避免阻塞工具的执行应该是相对快速的。如果需要执行长时间运行的任务如处理大文件、复杂计算应考虑异步处理并可能通过其他机制如轮询另一个资源返回结果避免阻塞 MCP 的主通信线程。连接池与缓存如果工具需要连接数据库或外部 API使用连接池和适当的缓存机制来提升性能并减少负载。单例与状态MCP Server 通常是单例的会在客户端会话期间持续运行。谨慎管理全局状态避免内存泄漏。6.4 配置化与可扩展性外部化配置将工具列表、资源定义、模拟数据等抽取到配置文件如config.json或config.yaml中使 Server 更容易适配不同环境。插件化架构对于大型项目可以考虑将不同功能的工具和资源封装成独立的插件模块通过动态加载来扩展 Server 能力。6.5 部署与分发打包为可执行文件使用pkg或nexe将 Node.js 项目打包成单个可执行文件简化部署无需目标机器安装 Node.js。npx pkg . --targets node18-linux-x64,node18-macos-x64,node18-win-x64 -o dist/mcp-server发布到 NPM如果你构建的是通用性较强的 MCP Server例如连接某种特定数据库可以将其发布为 NPM 包方便他人通过npx安装运行。容器化使用 Docker 构建镜像确保运行环境一致。FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY dist/ ./dist/ CMD [node, dist/index.js]7. 常见问题排查清单在开发和集成 MCP Server 时你可能会遇到以下问题。请按此清单顺序排查。问题现象可能原因检查点与解决方案Cursor 中完全看不到 MCP 工具1. MCP 配置未加载或路径错误。2. Server 启动失败。3. Cursor 版本过旧不支持 MCP。1. 检查~/.cursor/mcp.json路径和内容确保 JSON 格式正确。2. 在终端直接运行node /your/absolute/path/dist/index.js看是否有报错。3. 确保 Cursor 已更新到最新版本。重启 Cursor。AI 无法识别或调用特定工具1. 工具未在tools/list中正确声明。2. 工具名拼写错误。3. 输入参数 Schema 不匹配。1. 检查 Server 代码中ListToolsRequestSchema处理器返回的tools数组是否包含该工具。2. 确保CallToolRequestSchema处理器中判断的工具名与声明的一致。3. 使用 Prompt 让 AI 列出所有可用工具进行确认。调用工具后返回错误或超时1. 工具处理器代码有 bug 抛出异常。2. 工具执行时间过长。3. 传输过程中出现错误。1. 查看 Server 进程的 stderr 输出如果直接运行或查看 Cursor 的 MCP 日志。2. 在工具代码中添加 try-catch返回更友好的错误信息。3. 简化工具逻辑确保快速返回。修改 Server 代码后Cursor 中无变化1. Cursor 缓存了旧的 Server 实例。2. 未重新编译 TypeScript。3. 配置文件指向了错误的路径。1. 完全关闭 Cursor 并重新打开。2. 运行npm run build重新编译。3. 确认mcp.json中的路径指向最新的dist/index.js。协议错误或通信失败1. Server 未按 JSON-RPC 协议格式返回数据。2. 传输层stdio被干扰。1. 使用test-client.js这类简单脚本测试原始协议通信。2. 确保 Server 代码没有向 stdout 输出无关的调试信息如console.log这会被坏 JSON-RPC 消息。所有日志应输出到stderr(console.error)。资源读取返回 “未找到资源”1. 资源 URI 拼写错误。2.resources/list未声明该资源。3.resources/read处理器逻辑错误。1. 检查 AI 请求的 URI 是否与ListResourcesRequestSchema处理器中声明的一致。2. 在ReadResourceRequestSchema处理器中添加详细的日志打印收到的 URI。遵循从零搭建一个 MCP Server 的完整路径核心在于理解协议角色、善用官方 SDK、并通过 Prompt 在真实的 AI 客户端如 Cursor中进行迭代测试。将你的本地数据、内部 API 或复杂操作封装成标准的 Tools 和 Resources就能极大地扩展 AI 助手在你日常工作流中的能力边界。下一步你可以尝试连接真实的数据库如通过pg库连接 PostgreSQL、调用第三方 Web API、或者与本地文件系统深度交互构建出真正赋能生产的 AI 增强型工具链。