5步构建MCP Server:让AI助手安全访问本地资源与工具
2026/9/18 23:57:11 网站建设 项目流程

最近在尝试将 AI 助手(如 Claude、Cursor)深度集成到我的本地开发工作流中时,遇到了一个核心痛点:AI 助手无法直接访问我本地的项目文件、数据库或内部 API,导致很多自动化想法无法落地。直到我接触到了MCP(Model Context Protocol),这个由 Anthropic 提出的开放协议,它像一座桥梁,让 AI 助手能够安全、可控地“看见”并使用外部工具和数据源。

本文将带你从零开始,跟随 TypeScript 专家 Matt Pocock 的实战思路,仅用 5 条核心 Prompt,一步步构建一个属于自己的 MCP Server。无论你是想集成公司内部系统,还是想为个人项目打造专属 AI 工具链,这篇教程都将提供完整的代码、配置和避坑指南。学完后,你将掌握 MCP Server 的核心原理与构建方法,并能将其应用于实际开发场景。

1. MCP 核心概念:为什么它是 AI 集成的未来?

在深入代码之前,我们必须理解 MCP 解决了什么问题。简单来说,MCP 是一个标准化的通信协议,它定义了 AI 应用(如 Claude Desktop、Cursor)如何发现、调用外部服务器(MCP Server)提供的工具(Tools)和资源(Resources)。

传统 AI 助手的局限:通常,AI 模型的知识截止于其训练数据,无法实时获取外部信息(如最新的股票价格、你本地的待办事项列表),也无法执行具体操作(如创建文件、查询数据库)。虽然可以通过长篇的上下文传递文件内容,但这不仅低效,而且有 token 限制和安全风险。

MCP 带来的变革

  1. 标准化接口:MCP 为“工具调用”提供了统一协议。开发者只需按照协议实现一个 Server,任何兼容 MCP 的 AI 客户端都能立即使用其提供的功能。
  2. 安全与可控:AI 客户端(如 Claude Desktop)在首次连接时会明确告知用户正在加载哪些 MCP Server 及其提供的工具,用户拥有完全的知情权和选择权。Server 运行在用户指定的环境(通常是本地),数据不必上传到云端。
  3. 能力扩展:通过 MCP Server,AI 助手可以:
    • 读取资源:获取本地文件系统、数据库、内部 API 的数据作为上下文。
    • 调用工具:执行创建文件、运行脚本、发送 HTTP 请求等具体操作。

核心组件关系图

[AI 客户端 (Claude Desktop, Cursor)] | | 通过 MCP 协议通信 (stdin/stdout 或 SSE) | [MCP Server (你将要构建的)] | | 调用本地能力 | [本地文件系统 / 数据库 / 内部 API / 其他服务]

理解了 MCP 的价值,我们接下来就准备构建环境,亲手搭建这座“桥梁”。

2. 环境准备:构建 TypeScript MCP Server 的基石

我们将使用 TypeScript 来构建 MCP Server,这是目前最主流和高效的方式。Matt Pocock 的教程也基于此。请确保你的开发环境满足以下要求。

2.1 基础环境要求

  • 操作系统:macOS, Linux, 或 Windows (WSL 2 推荐)。
  • Node.js:版本 18 或更高。这是运行 TypeScript 和 MCP 库的基础。可以通过node --version检查。
  • 包管理器:npm 或 yarn 或 pnpm。本文使用npm进行演示。
  • 代码编辑器:强烈推荐使用Cursor或 VS Code。Cursor 本身是优秀的 AI 编程 IDE,并且原生支持 MCP,方便我们后续测试。

2.2 初始化项目与安装核心依赖

首先,创建一个新的项目目录并初始化。

# 创建项目文件夹并进入 mkdir my-first-mcp-server cd my-first-mcp-server # 初始化 npm 项目,生成 package.json npm init -y # 初始化 TypeScript 配置 npx tsc --init

接下来,安装构建 MCP Server 所必需的依赖。

# 安装 MCP 核心 SDK 和 TypeScript 类型定义 npm install @modelcontextprotocol/sdk # 安装 TypeScript 编译器和 Node.js 类型(开发依赖) npm install --save-dev typescript @types/node # 安装一个用于测试的轻量级 HTTP 客户端(可选,用于后续示例) npm install axios

2.3 配置 TypeScript

打开生成的tsconfig.json文件,进行如下修改,以适配现代 Node.js 模块和 MCP 开发。

{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": true, "declarationMap": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] }

2.4 创建项目基础结构

创建源代码目录和入口文件。

# 创建源代码目录 mkdir src # 创建主服务器文件 touch src/index.ts # 创建 package.json 中的启动脚本

编辑package.json,在scripts部分添加构建和运行脚本。

{ "name": "my-first-mcp-server", "version": "1.0.0", "description": "A simple MCP server built with TypeScript", "main": "dist/index.js", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsc --watch & nodemon dist/index.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", "axios": "^1.6.0" }, "devDependencies": { "@types/node": "^20.0.0", "typescript": "^5.0.0", "nodemon": "^3.0.0" } }

注意:nodemon需要额外安装 (npm install --save-dev nodemon),用于开发时监听文件变化自动重启。

环境搭建完毕,我们已经拥有了一个结构清晰、配置完善的 TypeScript 项目。接下来,我们将进入核心环节:运用 Prompt 驱动开发,一步步实现 Server 功能。

3. 核心 Prompt 驱动开发:5 步构建功能完整的 Server

这是本文的核心方法。我们将模仿与一位资深 AI 工程师(如 Matt Pocock)对话的场景,通过 5 条结构化的 Prompt,引导 AI(或我们自己)完成 MCP Server 的构建。你可以直接在 Cursor 或 Claude 中应用这些 Prompt。

3.1 Prompt 1:创建 MCP Server 骨架与标准输入输出处理

目标:初始化一个最基本的 MCP Server,它能够启动并处理来自客户端的连接。

Prompt 内容: “请帮我创建一个基于@modelcontextprotocol/sdk的 TypeScript MCP Server 骨架代码。代码应包含:

  1. 导入必要的ServerStdioServerTransport类。
  2. 创建一个新的Server实例,并为其指定一个名称(如my-tools-server)和版本。
  3. 设置StdioServerTransport来处理标准输入/输出通信(这是 MCP 客户端最常见的连接方式)。
  4. 在 Server 实例上调用connect()方法建立连接。
  5. 添加基本的错误处理,确保 Server 崩溃时能输出错误信息。
  6. 将完整代码写入src/index.ts。”

预期代码实现: 根据上述 Prompt,我们可以在src/index.ts中编写如下代码:

// src/index.ts import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; // 1. 创建 Server 实例 const server = new Server( { name: 'my-tools-server', version: '1.0.0', }, { // 可选的 Server 能力声明,初始为空 capabilities: {} } ); // 2. 创建传输层(使用标准输入/输出) const transport = new StdioServerTransport(); // 3. 连接并启动 Server server.connect(transport).catch((error) => { console.error('[MCP Server] Failed to start:', error); process.exit(1); }); console.error('[MCP Server] Started and listening on stdio');

解释:这段代码创建了一个最小化的 MCP Server。StdioServerTransport意味着 Server 通过命令行标准输入(stdin)接收请求,通过标准输出(stdout)发送响应,这是与 Claude Desktop 等客户端集成的最简单方式。console.error用于输出日志,因为stdout需要留给协议通信。

3.2 Prompt 2:实现第一个工具(Tool)—— 获取系统时间

目标:为 Server 添加一个具体的工具(Tool),让 AI 客户端可以调用它来获取当前服务器时间。

Prompt 内容: “现在,请为上面创建的 MCP Server 添加一个工具(Tool)。

  1. 这个工具的名称(name)叫get_current_time
  2. 描述(description)为 ‘获取当前的系统日期和时间’。
  3. 它不需要任何输入参数(inputSchema为空)。
  4. 在 Server 实例上注册这个工具。
  5. 实现这个工具的处理函数,当被调用时,返回一个包含当前 ISO 格式时间字符串的结果。
  6. 更新 Server 的capabilities声明,包含tools能力。
  7. 展示更新后的完整src/index.ts代码。”

预期代码实现

// src/index.ts (更新后) import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; const server = new Server( { name: 'my-tools-server', version: '1.0.0', }, { // 声明 Server 具备提供工具的能力 capabilities: { tools: {} } } ); // 注册工具:获取当前时间 server.setRequestHandler( // 处理 `tools/call` 请求 'tools/call', async (request) => { // 根据工具名称执行不同逻辑 if (request.params.name === 'get_current_time') { return { content: [ { type: 'text', text: `当前系统时间是:${new Date().toISOString()}`, }, ], }; } // 如果收到未知工具请求,抛出错误 throw new Error(`未知的工具:${request.params.name}`); } ); // 注册工具列表:告知客户端本 Server 提供了哪些工具 server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'get_current_time', description: '获取当前的系统日期和时间', inputSchema: { type: 'object', properties: {}, // 无输入参数 }, }, ], }; }); const transport = new StdioServerTransport(); server.connect(transport).catch((error) => { console.error('[MCP Server] Failed to start:', error); process.exit(1); }); console.error('[MCP Server] Started with \'get_current_time\' tool');

解释:我们做了两件关键事:1. 在capabilities中声明支持tools。2. 注册了两个请求处理器:tools/list用于返回工具清单;tools/call用于执行具体的工具逻辑。现在,这个 Server 已经具备了一个可调用的功能。

3.3 Prompt 3:实现带参数的工具 —— 计算 MD5 哈希

目标:创建一个更复杂的工具,它需要接收用户输入的参数(一个字符串),并返回其 MD5 哈希值。

Prompt 内容: “请为 MCP Server 添加第二个工具,用于计算字符串的 MD5 哈希值。

  1. 工具名称:calculate_md5
  2. 工具描述:‘计算给定字符串的 MD5 哈希值’。
  3. 输入参数:需要一个名为text的字符串参数,描述为 ‘要计算哈希的文本’。
  4. 使用 Node.js 内置的crypto模块实现 MD5 计算。
  5. 更新tools/list处理器,将新工具添加到返回列表中。
  6. 更新tools/call处理器,添加对calculate_md5的判断和逻辑。
  7. 展示整合后的代码。”

预期代码实现

// src/index.ts (再次更新) import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import crypto from 'crypto'; // 导入 Node.js 加密模块 const server = new Server( { name: 'my-tools-server', version: '1.0.0', }, { capabilities: { tools: {} } } ); // 工具列表处理器 server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'get_current_time', description: '获取当前的系统日期和时间', inputSchema: { type: 'object', properties: {}, }, }, { name: 'calculate_md5', description: '计算给定字符串的 MD5 哈希值', inputSchema: { type: 'object', properties: { text: { type: 'string', description: '要计算哈希的文本', }, }, required: ['text'], // 标记 text 为必填参数 }, }, ], }; }); // 工具调用处理器 server.setRequestHandler( 'tools/call', async (request) => { const { name, arguments: args } = request.params; if (name === 'get_current_time') { return { content: [ { type: 'text', text: `当前系统时间是:${new Date().toISOString()}`, }, ], }; } if (name === 'calculate_md5') { // 1. 验证参数是否存在 if (!args || typeof args.text !== 'string') { throw new Error('必须提供有效的 `text` 字符串参数'); } // 2. 计算 MD5 const hash = crypto.createHash('md5').update(args.text).digest('hex'); return { content: [ { type: 'text', text: `文本 "${args.text.substring(0, 20)}${args.text.length > 20 ? '...' : ''}" 的 MD5 哈希值是:${hash}`, }, ], }; } throw new Error(`未知的工具:${name}`); } ); const transport = new StdioServerTransport(); server.connect(transport).catch((error) => { console.error('[MCP Server] Failed to start:', error); process.exit(1); }); console.error('[MCP Server] Started with time and MD5 tools');

关键点:1. 在inputSchema中定义了参数的类型和必要性。2. 在tools/call处理器中,通过request.params.arguments获取客户端传递的参数。3. 加入了基本的参数校验。这演示了如何构建一个交互式工具。

3.4 Prompt 4:实现资源(Resource)读取 —— 列出目录文件

目标:让 Server 不仅能提供“工具”,还能提供“资源”。资源是 AI 客户端可以读取的数据源。我们将实现一个列出指定目录下文件的资源。

Prompt 内容: “现在,请为 MCP Server 添加资源(Resources)能力。

  1. 声明 Server 支持resources能力。
  2. 实现resources/list处理器,返回一个资源列表。我们提供一个资源:file:///home/user/documents(请根据你的系统调整为一个真实存在的目录路径),其 MIME 类型为application/json
  3. 实现resources/read处理器,当读取上述资源时,使用 Node.jsfs模块列出该目录下的所有文件和文件夹,并以 JSON 数组的形式返回。
  4. 注意处理路径不存在等错误情况。
  5. 展示更新后的完整代码,需包含fs模块导入。”

预期代码实现

// src/index.ts (添加资源能力) import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import crypto from 'crypto'; import fs from 'fs/promises'; // 使用 Promise-based FS API import path from 'path'; const server = new Server( { name: 'my-tools-server', version: '1.0.0', }, { capabilities: { tools: {}, resources: {} // 声明支持资源能力 } } ); // 假设我们提供一个展示用户文档目录的资源 const DOCS_DIRECTORY_URI = 'file:///Users/你的用户名/Documents'; // !!!请修改为你的真实路径 // 工具列表(保持不变) server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'get_current_time', description: '获取当前的系统日期和时间', inputSchema: { type: 'object', properties: {} }, }, { name: 'calculate_md5', description: '计算给定字符串的 MD5 哈希值', inputSchema: { type: 'object', properties: { text: { type: 'string', description: '要计算哈希的文本' }, }, required: ['text'], }, }, ], }; }); // 资源列表处理器 server.setRequestHandler('resources/list', async () => { return { resources: [ { uri: DOCS_DIRECTORY_URI, name: '我的文档目录列表', description: '列出指定文档目录下的文件和文件夹', mimeType: 'application/json', }, ], }; }); // 资源读取处理器 server.setRequestHandler('resources/read', async (request) => { const { uri } = request.params; if (uri === DOCS_DIRECTORY_URI) { try { // 将 file:// URI 转换为本地文件系统路径 const localPath = new URL(uri).pathname; const items = await fs.readdir(localPath, { withFileTypes: true }); const list = items.map((item) => ({ name: item.name, type: item.isDirectory() ? 'directory' : 'file', })); return { contents: [ { uri: uri, mimeType: 'application/json', text: JSON.stringify(list, null, 2), // 美化输出 JSON }, ], }; } catch (error: any) { // 处理错误,例如目录不存在 return { contents: [ { uri: uri, mimeType: 'text/plain', text: `无法读取资源 ${uri}: ${error.message}`, }, ], }; } } throw new Error(`未找到资源:${uri}`); }); // 工具调用处理器(保持不变) server.setRequestHandler( 'tools/call', async (request) => { const { name, arguments: args } = request.params; // ... (get_current_time 和 calculate_md5 的逻辑保持不变) // 为节省篇幅,此处省略重复代码,实际文件中需保留 if (name === 'get_current_time') { /* ... */ } if (name === 'calculate_md5') { /* ... */ } throw new Error(`未知的工具:${name}`); } ); const transport = new StdioServerTransport(); server.connect(transport).catch((error) => { console.error('[MCP Server] Failed to start:', error); process.exit(1); }); console.error('[MCP Server] Started with tools and resources support');

重要提示请务必将DOCS_DIRECTORY_URI的值替换为你电脑上真实存在的目录路径(例如file:///C:/Users/YourName/Documentsfile:///home/yourname/Documents)。资源(Resource)机制使得 AI 客户端可以在需要时主动获取这些数据作为上下文,而无需用户手动上传文件。

3.5 Prompt 5:添加配置化与错误处理优化

目标:让 Server 更健壮、更专业。通过环境变量配置资源路径,并优化全局错误处理。

Prompt 内容: “最后,请优化我们的 MCP Server。

  1. 让资源目录的 URI 可通过环境变量MCP_DOCS_PATH进行配置。如果未设置,则使用一个默认的回退路径。
  2. 在 Server 级别添加一个全局的未捕获错误处理器,使用server.onerror来捕获并打印未被特定处理器捕获的错误,避免 Server 静默崩溃。
  3. resources/read处理器中,增加对路径遍历攻击的简单防护(例如,检查请求的 URI 是否在我们允许的基准路径下)。
  4. 展示最终的、优化后的完整src/index.ts代码。”

预期代码实现

// src/index.ts (最终优化版) import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import crypto from 'crypto'; import fs from 'fs/promises'; import path from 'path'; const server = new Server( { name: 'my-tools-server', version: '1.0.0', }, { capabilities: { tools: {}, resources: {}, }, } ); // 1. 通过环境变量配置资源路径 const DEFAULT_DOCS_PATH = path.join(process.env.HOME || process.env.USERPROFILE || '', 'Documents'); const CONFIGURED_DOCS_PATH = process.env.MCP_DOCS_PATH || DEFAULT_DOCS_PATH; // 确保路径是绝对路径,并格式化为 file:// URI const RESOURCE_URI = `file://${path.resolve(CONFIGURED_DOCS_PATH)}`; console.error(`[MCP Server] 资源目录配置为: ${RESOURCE_URI}`); // 2. 全局错误处理 server.onerror = (error) => { console.error('[MCP Server Global Error]', error); }; // 工具列表 server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'get_current_time', description: '获取当前的系统日期和时间', inputSchema: { type: 'object', properties: {} }, }, { name: 'calculate_md5', description: '计算给定字符串的 MD5 哈希值', inputSchema: { type: 'object', properties: { text: { type: 'string', description: '要计算哈希的文本' }, }, required: ['text'], }, }, ], }; }); // 资源列表 server.setRequestHandler('resources/list', async () => { return { resources: [ { uri: RESOURCE_URI, name: '配置的文档目录列表', description: `列出目录 ${CONFIGURED_DOCS_PATH} 下的内容`, mimeType: 'application/json', }, ], }; }); // 资源读取(带安全检查) server.setRequestHandler('resources/read', async (request) => { const { uri } = request.params; if (uri === RESOURCE_URI) { try { const localPath = new URL(uri).pathname; // 3. 简单的路径安全检查:确保请求的路径在配置的目录下 const resolvedRequestPath = path.resolve(decodeURIComponent(localPath)); const resolvedBasePath = path.resolve(new URL(RESOURCE_URI).pathname); if (!resolvedRequestPath.startsWith(resolvedBasePath)) { throw new Error('访问路径越界'); } const items = await fs.readdir(resolvedRequestPath, { withFileTypes: true }); const list = items.map((item) => ({ name: item.name, type: item.isDirectory() ? 'directory' : 'file', // 可选:添加大小或修改时间 // size: item.isFile() ? (await fs.stat(path.join(resolvedRequestPath, item.name))).size : null, })); return { contents: [ { uri: uri, mimeType: 'application/json', text: JSON.stringify(list, null, 2), }, ], }; } catch (error: any) { return { contents: [ { uri: uri, mimeType: 'text/plain', text: `错误: ${error.message}`, }, ], }; } } throw new Error(`未找到资源:${uri}`); }); // 工具调用 server.setRequestHandler('tools/call', async (request) => { const { name, arguments: args } = request.params; if (name === 'get_current_time') { return { content: [ { type: 'text', text: `当前系统时间是:${new Date().toISOString()}`, }, ], }; } if (name === 'calculate_md5') { if (!args || typeof args.text !== 'string') { throw new Error('必须提供有效的 `text` 字符串参数'); } const hash = crypto.createHash('md5').update(args.text).digest('hex'); return { content: [ { type: 'text', text: `文本 "${args.text.substring(0, 20)}${args.text.length > 20 ? '...' : ''}" 的 MD5 哈希值是:${hash}`, }, ], }; } throw new Error(`未知的工具:${name}`); }); const transport = new StdioServerTransport(); server.connect(transport).catch((error) => { console.error('[MCP Server] 启动失败:', error); process.exit(1); }); console.error('[MCP Server] 启动成功,等待客户端连接...');

至此,一个功能相对完整、具备工具和资源能力、且经过基本优化的 MCP Server 就构建完成了。接下来,我们需要编译并测试它。

4. 编译、运行与客户端集成测试

理论已经完备,代码也已就绪,现在是让 Server 跑起来并与 AI 客户端联调的时候了。

4.1 编译与运行 Server

首先,将 TypeScript 代码编译为 JavaScript。

# 在项目根目录执行编译 npm run build

如果tsconfig.json配置正确,这将在dist/目录下生成index.js文件。

你可以直接运行它:

node dist/index.js

运行后,程序会挂起,等待来自stdin的 MCP 协议消息。这正是我们期望的状态——Server 已就绪。

4.2 集成到 Claude Desktop(推荐测试方式)

Claude Desktop 是目前对 MCP 支持最友好、测试最方便的工具。

  1. 找到 Claude Desktop 配置

    • macOS:~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows:%APPDATA%\Claude\claude_desktop_config.json
    • Linux:~/.config/Claude/claude_desktop_config.json
  2. 编辑配置文件:如果文件不存在,则创建它。添加以下配置,将path/to/your/project替换为你的项目绝对路径。

{ "mcpServers": { "my-tools-server": { "command": "node", "args": [ "/absolute/path/to/your/my-first-mcp-server/dist/index.js" ], "env": { "MCP_DOCS_PATH": "/absolute/path/to/your/test_directory" // 可选,覆盖资源路径 } } } }
  1. 重启 Claude Desktop:完全退出并重新启动 Claude Desktop 应用。

  2. 验证连接:重启后,在 Claude 的输入框里,你可以尝试说:“请调用get_current_time工具” 或 “你能访问哪些资源?”。Claude 应该能识别出你的 Server 并调用相应的工具或读取资源。

4.3 在 Cursor 中集成

Cursor 也支持 MCP,但配置方式可能随版本更新而变化。通常可以在 Cursor 的设置(Settings)中搜索 “MCP” 进行配置,原理与 Claude Desktop 类似,也是通过 JSON 配置指定 Server 的启动命令和参数。

4.4 手动测试(调试)

对于深度调试,你可以编写一个简单的测试脚本,模拟 MCP 客户端向 Server 发送协议消息。但这相对复杂。更简单的方式是使用stdio直接交互(不推荐新手),或者在代码中添加详细的console.error日志来观察流程。

5. 常见问题与排查思路

在构建和集成 MCP Server 时,你可能会遇到以下典型问题。

问题现象可能原因排查步骤与解决方案
Server 启动后立即退出1. TypeScript 编译错误。
2. 依赖未安装。
3. 代码中存在同步错误导致进程崩溃。
1. 运行npm run build检查编译输出。
2. 运行npm install确保依赖完整。
3. 在server.connect()前后添加try-catch,并检查全局错误处理器server.onerror是否设置。
Claude Desktop 未加载 Server1. 配置文件路径错误。
2. 配置文件格式错误(JSON 语法)。
3. Claude Desktop 未重启。
4. Server 命令路径错误或不可执行。
1. 确认配置文件路径完全正确。
2. 使用 JSON 验证工具检查配置文件。
3. 彻底退出并重启 Claude Desktop。
4. 在终端中手动运行配置中的commandargs,确保能启动 Server。
AI 助手无法识别工具/资源1. Server 的capabilities未正确声明。
2.tools/listresources/list处理器未正确返回数据。
3. 协议通信失败。
1. 检查new Server()时的capabilities对象。
2. 在list处理器中添加console.error打印返回值,确保结构正确。
3. 查看 Claude Desktop 或 Cursor 的日志(如果有),或 Server 的stderr输出。
工具调用返回错误1. 工具名称不匹配。
2. 输入参数格式不符合inputSchema
3. 工具处理函数内部抛出异常。
1. 确认tools/call处理器中判断的nametools/list返回的一致。
2. 在tools/call开始时打印request.params检查输入。
3. 在工具逻辑内部添加细致的错误处理和日志。
资源读取返回空或错误1.file://URI 路径格式错误或不存在。
2. 文件系统权限不足。
3.resources/read处理器中的路径解析逻辑有误。
1. 确保RESOURCE_URI是绝对路径,并以file://开头。
2. 手动检查 Node.js 进程是否有权读取目标目录。
3. 在resources/read中添加日志,打印解析后的localPath
StdioServerTransport相关问题1. 在非命令行环境下错误使用。
2. 多个 Server 实例冲突。
1.StdioServerTransport专为命令行集成设计,确保通过 Claude/Cursor 配置调用,而非直接双击运行。
2. 确保同一时间只有一个 Server 实例占用stdio

6. 最佳实践与进阶工程建议

当你掌握了基础构建流程后,以下建议能帮助你打造更健壮、更实用的 MCP Server。

6.1 项目组织与代码结构

  • 拆分模块:不要将所有代码堆在index.ts中。将工具定义、资源定义、处理器逻辑拆分到不同的文件(如src/tools/,src/resources/)中,便于维护和扩展。
  • 使用配置管理:将 Server 名称、版本、资源路径等配置项集中到config.ts或使用dotenv.env文件加载。
  • 添加日志系统:使用winstonpino等日志库替代console.error,可以按级别(info, debug, error)输出日志,并支持输出到文件,方便生产环境调试。

6.2 安全性与可靠性

  • 输入验证与消毒:对于任何来自客户端的输入(如工具参数、资源 URI),都必须进行严格的验证和消毒,防止命令注入、路径遍历等攻击。上面的示例中已包含简单的路径检查。
  • 权限最小化:Server 运行时应仅拥有完成其功能所需的最小权限。避免以高权限(如 root)运行。
  • 错误边界:每个工具和资源处理器都应包裹在try-catch中,并返回用户友好的错误信息,而不是泄露内部堆栈跟踪。
  • 资源访问控制:仔细设计resources/list返回的 URI。不要暴露敏感目录。可以考虑实现动态资源列表,根据会话或身份验证返回不同的资源。

6.3 性能与可扩展性

  • 异步操作:所有 I/O 操作(如文件读写、网络请求)都必须使用异步模式(async/await),避免阻塞主线程。
  • 工具与资源缓存:对于频繁访问且变化不频繁的资源,可以考虑在 Server 端实现缓存机制,减少重复开销。
  • 状态管理:MCP Server 本质上是无状态的。如果需要维护会话状态,需要设计巧妙的机制(例如通过加密令牌在客户端和 Server 间传递状态)。

6.4 生产环境部署

  • 进程管理:使用pm2systemd来管理 Server 进程,确保其崩溃后能自动重启。
  • 健康检查:可以暴露一个简单的 HTTP 健康检查端点(如果 Server 同时运行 HTTP 服务),或通过信号机制检查进程是否存活。
  • 版本化:清晰定义 Server 的版本号,并在Server构造函数中声明。当协议或功能更新时,便于客户端兼容性处理。

6.5 扩展想法

你的 MCP Server 可以无限扩展:

  • 数据库工具:提供查询、更新本地数据库的工具。
  • 内部 API 网关:封装公司内部 REST 或 GraphQL API,让 AI 安全调用。
  • 代码库分析:提供读取、分析特定 Git 仓库代码结构的资源。
  • 系统监控:提供获取服务器 CPU、内存使用情况的工具。
  • 自定义工作流:将一系列复杂操作(如构建、部署、测试)封装成一个工具。

通过这 5 条结构化的 Prompt,我们不仅构建了一个可运行的 MCP Server,更掌握了一种“Prompt 驱动开发”的思维模式。这种模式将复杂的工程任务分解为清晰的、可执行的指令,无论是与 AI 协作还是自我规划,都极具效率。记住,MCP 的核心价值在于将你的本地能力安全、标准化地暴露给 AI。现在,你可以基于这个模板,去创造真正能提升你工作效率的 AI 增强工具了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询