手把手构建MCP服务器:让Claude/ChatGPT安全调用内部API与数据库

📅 2026/7/31 9:00:14
手把手构建MCP服务器:让Claude/ChatGPT安全调用内部API与数据库
如果你还在用 Claude 或 ChatGPT 做重复性的手动操作比如查数据库、调 API、读文档那可能错过了 AI 开发中最关键的一环MCPModel Context Protocol服务器。最近很多开发者发现虽然大模型能写代码、回答问题但一到具体业务场景就断联——它无法直接访问你的内部系统、私有数据库或专属工具链。传统解决方案要么是让模型生成代码你再手动执行要么依赖复杂的插件开发。而 MCP 协议的出现真正让 AI 能够安全、标准化地调用你的基础设施。本文将从实际开发角度手把手演示如何为 Claude 和 ChatGPT 构建自定义 MCP 服务器。不止是概念介绍我会用一个真实的天气查询服务器示例带你完成从环境搭建、协议理解、代码实现到集成测试的全流程。无论你是想提升团队开发效率还是为内部工具添加 AI 能力这都是值得深入的技术方向。1. 为什么需要自定义 MCP 服务器在深入技术细节前先明确一个问题当 Claude 和 ChatGPT 已经具备强大的对话和代码能力时为什么还要折腾 MCP传统 AI 集成的三大痛点上下文隔离每次对话都是独立的模型无法记住你的业务规则和数据结构权限边界模糊直接给模型数据库访问权限风险极高但完全隔离又无法实现自动化开发成本高为每个业务场景开发定制插件需要重复处理认证、协议、错误处理MCP 协议的核心价值在于它定义了一套标准化的通信机制。你可以把 MCP 服务器理解为 AI 的驱动程序——它封装了具体的业务逻辑和权限控制向模型提供安全的工具调用接口。实际应用场景举例内部数据库查询销售团队让 Claude 直接查询客户数据而无需暴露数据库连接信息API 集成让 ChatGPT 调用公司内部审批流程自动生成周报文档处理模型可以读取特定格式的文档库回答领域相关问题工具链集成直接执行构建命令、部署流程减少人工干预关键是一旦建立 MCP 服务器你可以在不同的 AI 应用间复用同一套工具而不是为每个模型单独开发集成方案。2. MCP 协议基础理解核心概念MCP 协议建立在 JSON-RPC 2.0 之上定义了模型与服务器之间的标准交互方式。在开始编码前需要理解几个关键概念2.1 核心组件MCP 客户端Claude Desktop、ChatGPT 等应用负责与用户交互并调用工具MCP 服务器你将要开发的自定义服务提供具体的工具实现工具Tools服务器向客户端暴露的可调用功能每个工具都有明确的输入输出定义资源Resources服务器提供的可读数据源如文档、配置文件等2.2 通信流程用户请求 → MCP 客户端 → 调用工具 → MCP 服务器 → 执行逻辑 → 返回结果协议处理了所有底层细节序列化、错误处理、会话管理。你只需要关注业务逻辑的实现。2.3 与传统 API 集成的区别方面传统 API 集成MCP 服务器开发成本需要处理 HTTP 客户端、认证、重试等专注业务逻辑协议层已标准化安全性模型直接接触敏感信息权限控制在服务器端模型只看到结果复用性绑定特定模型或平台一次开发多模型通用维护性每个集成点单独维护集中化的工具管理理解这些基础后我们就可以开始实际的环境准备了。3. 环境准备与开发工具选择构建 MCP 服务器并不复杂但需要正确的工具链。以下是推荐的技术栈3.1 基础环境要求Node.js 18或Python 3.9本文以 Node.js 为例Python 版本类似Claude Desktop最新版或ChatGPT具有插件支持的环境代码编辑器VS Code 或其他现代 IDE3.2 开发依赖安装对于 Node.js 方案我们需要官方提供的 MCP SDK# 创建项目目录 mkdir weather-mcp-server cd weather-mcp-server # 初始化项目 npm init -y # 安装核心依赖 npm install modelcontextprotocol/sdk-server npm install axios # 用于 HTTP 请求 # 安装 TypeScript 相关可选但推荐 npm install -D typescript types/node ts-node3.3 项目结构规划创建以下基础文件结构weather-mcp-server/ ├── src/ │ ├── server.ts # 主服务器文件 │ └── tools/ │ └── weather.ts # 天气查询工具实现 ├── package.json ├── tsconfig.json # TypeScript 配置 └── README.md3.4 TypeScript 配置创建tsconfig.json确保类型检查{ compilerOptions: { target: ES2022, module: CommonJS, outDir: ./dist, rootDir: ./src, strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true }, include: [src/**/*], exclude: [node_modules, dist] }环境准备完成后我们就可以开始实现具体的业务逻辑了。4. 构建天气查询 MCP 服务器完整示例让我们通过一个实际的天气查询服务器来掌握 MCP 开发的全流程。这个示例虽然简单但包含了所有关键要素。4.1 定义工具接口首先在src/tools/weather.ts中定义天气查询工具import { Tool } from modelcontextprotocol/sdk-server/index.js; import axios from axios; export class WeatherTool { private apiKey: string; constructor(apiKey: string) { this.apiKey apiKey; } // 定义工具元数据 getTool(): Tool { return { name: get_weather, description: 获取指定城市的当前天气信息, inputSchema: { type: object, properties: { city: { type: string, description: 城市名称例如北京、上海、New York }, units: { type: string, enum: [metric, imperial], description: 温度单位metric为摄氏度imperial为华氏度, default: metric } }, required: [city] } }; } // 实现工具逻辑 async execute(input: { city: string; units?: string }): Promisestring { try { const response await axios.get( https://api.openweathermap.org/data/2.5/weather?q${encodeURIComponent(input.city)}units${input.units || metric}appid${this.apiKey} ); const data response.data; const temperature data.main.temp; const description data.weather[0].description; const humidity data.main.humidity; return 城市${data.name}\n温度${temperature}°${input.units imperial ? F : C}\n天气${description}\n湿度${humidity}%; } catch (error) { if (axios.isAxiosError(error) error.response?.status 404) { throw new Error(找不到城市 ${input.city}请检查城市名称是否正确); } throw new Error(天气查询失败${error.message}); } } }这个工具类定义了完整的输入输出规范并处理了错误情况。4.2 创建主服务器在src/server.ts中创建 MCP 服务器实例import { Server } from modelcontextprotocol/sdk-server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk-server/stdio.js; import { WeatherTool } from ./tools/weather.js; // 创建服务器实例 const server new Server( { name: weather-mcp-server, version: 1.0.0, }, { capabilities: { tools: {}, }, } ); // 初始化天气工具在实际项目中应从环境变量读取API密钥 const weatherTool new WeatherTool(your-openweathermap-api-key); // 注册工具 server.setRequestHandler(async (request) { if (request.method tools/list) { return { tools: [weatherTool.getTool()], }; } if (request.method tools/call) { if (request.params.name get_weather) { try { const result await weatherTool.execute(request.params.arguments as any); return { content: [ { type: text, text: result, }, ], }; } catch (error) { return { content: [ { type: text, text: 错误${error instanceof Error ? error.message : 未知错误}, }, ], isError: true, }; } } } throw new Error(Method ${request.method} not found); }); // 启动服务器 async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(Weather MCP Server 已启动); } main().catch(console.error);这个服务器处理了工具列表查询和工具调用两个核心方法。4.3 配置 Claude Desktop 集成要让 Claude Desktop 识别你的 MCP 服务器需要创建配置文件在 macOS 上的配置路径# 创建配置目录 mkdir -p ~/Library/Application\ Support/Claude/claude_desktop_config.json配置内容{ mcpServers: { weather: { command: node, args: [/path/to/your/weather-mcp-server/dist/server.js] } } }在 Windows 上的配置路径%APPDATA%/Claude/claude_desktop_config.json4.4 测试服务器功能在部署前先本地测试服务器功能。创建测试脚本test/server.test.tsimport { WeatherTool } from ../src/tools/weather.js; // 简单的单元测试 async function testWeatherTool() { const tool new WeatherTool(test-key); // 测试工具定义 const toolDef tool.getTool(); console.log(工具定义:, JSON.stringify(toolDef, null, 2)); // 注意实际API测试需要有效的API密钥 console.log(工具测试完成); } testWeatherTool().catch(console.error);运行测试确保基础逻辑正确npx ts-node test/server.test.ts5. 高级功能扩展资源管理与会话状态基础工具实现后我们可以扩展更复杂的 MCP 功能。资源管理让模型能够读取静态内容而会话状态支持多步交互。5.1 添加资源管理扩展服务器以支持文档资源读取// 在 src/tools/resources.ts 中添加 import { Resource } from modelcontextprotocol/sdk-server/index.js; export class DocumentationResource { getResources(): Resource[] { return [ { uri: file:///weather-api-docs, mimeType: text/plain, name: 天气API文档, description: 天气查询工具的详细使用说明 } ]; } async readResource(uri: string): Promisestring { if (uri file:///weather-api-docs) { return 天气查询工具使用说明 - 支持全球城市查询 - 温度单位可选摄氏度或华氏度 - 返回信息包括温度、天气状况、湿度 示例查询北京天气; } throw new Error(资源不存在: ${uri}); } }5.2 支持会话状态对于需要多步交互的场景可以维护会话状态// 在 src/tools/session.ts 中添加 interface WeatherSession { lastCity?: string; preferredUnits: string; queryCount: number; } export class SessionManager { private sessions: Mapstring, WeatherSession new Map(); getSession(sessionId: string): WeatherSession { if (!this.sessions.has(sessionId)) { this.sessions.set(sessionId, { preferredUnits: metric, queryCount: 0 }); } return this.sessions.get(sessionId)!; } updateSession(sessionId: string, updates: PartialWeatherSession) { const session this.getSession(sessionId); Object.assign(session, updates); } }这些高级功能让 MCP 服务器能够处理更复杂的业务场景。6. 安全最佳实践与生产环境部署MCP 服务器涉及系统集成安全性至关重要。以下是必须遵循的实践6.1 敏感信息管理永远不要在代码中硬编码 API 密钥或密码// 正确的做法从环境变量读取 const apiKey process.env.OPENWEATHER_API_KEY; if (!apiKey) { throw new Error(OPENWEATHER_API_KEY 环境变量未设置); } const weatherTool new WeatherTool(apiKey);6.2 输入验证与清理对所有输入进行严格验证// 增强输入验证 function validateCityInput(city: string): boolean { // 只允许字母、空格和常见标点 const validPattern /^[a-zA-Z\s\-,\.]$/; return validPattern.test(city) city.length 100; } async function execute(input: { city: string; units?: string }) { if (!validateCityInput(input.city)) { throw new Error(城市名称包含无效字符); } // ... 其余逻辑 }6.3 错误处理与日志记录实现完整的错误处理和日志import { createLogger, format, transports } from winston; const logger createLogger({ level: info, format: format.combine( format.timestamp(), format.errors({ stack: true }), format.json() ), transports: [new transports.Console()] }); // 在工具执行中记录日志 async function execute(input: any) { logger.info(天气查询请求, { city: input.city }); try { // ... 业务逻辑 logger.info(天气查询成功, { city: input.city }); return result; } catch (error) { logger.error(天气查询失败, { city: input.city, error: error.message }); throw error; } }6.4 生产环境部署配置创建 Docker 容器化部署# Dockerfile FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY dist/ ./dist/ USER node CMD [node, dist/server.js]对应的docker-compose.ymlversion: 3.8 services: weather-mcp: build: . environment: - OPENWEATHER_API_KEY${OPENWEATHER_API_KEY} restart: unless-stopped logging: driver: json-file options: max-size: 10m max-file: 37. 集成测试与验证流程服务器开发完成后需要系统性的测试验证。7.1 手动测试流程启动服务器并进行基础验证# 编译 TypeScript npm run build # 直接运行测试 node dist/server.js在另一个终端中测试 JSON-RPC 调用# 测试工具列表查询 echo {jsonrpc:2.0,id:1,method:tools/list} | node dist/server.js # 测试工具调用需要有效的API密钥 echo {jsonrpc:2.0,id:2,method:tools/call,params:{name:get_weather,arguments:{city:Beijing}}} | node dist/server.js7.2 Claude Desktop 集成验证确保配置文件正确放置重启 Claude Desktop在对话中测试工具调用用户请查询北京的天气情况 Claude我将使用天气查询工具为您获取北京的最新天气信息...7.3 自动化测试套件创建完整的测试覆盖// test/integration.test.ts import { Server } from modelcontextprotocol/sdk-server/index.js; import { WeatherTool } from ../src/tools/weather.js; import { describe, it, expect, beforeEach } from jest/globals; describe(Weather MCP Server, () { let server: Server; let weatherTool: WeatherTool; beforeEach(() { server new Server({ name: test, version: 1.0.0 }, { capabilities: { tools: {} } }); weatherTool new WeatherTool(test-key); }); it(应该正确返回工具列表, async () { const response await server.handleRequest({ jsonrpc: 2.0, id: 1, method: tools/list }); expect(response).toHaveProperty(tools); expect(response.tools).toHaveLength(1); expect(response.tools[0].name).toBe(get_weather); }); });8. 常见问题与排查指南在实际部署中你可能会遇到以下问题8.1 连接问题排查问题现象可能原因解决方案Claude 无法识别服务器配置文件路径错误检查claude_desktop_config.json路径和权限服务器启动失败依赖缺失或版本冲突删除node_modules重新npm install工具调用无响应JSON-RPC 格式错误检查输入参数是否符合 schema 定义8.2 权限与安全问题问题场景风险缓解措施API 密钥泄露未授权使用服务使用环境变量定期轮换密钥输入注入攻击服务器被恶意利用严格验证所有输入参数资源耗尽恶意频繁调用实现速率限制和配额管理8.3 性能优化建议// 添加缓存机制减少 API 调用 import NodeCache from node-cache; const cache new NodeCache({ stdTTL: 300 }); // 5分钟缓存 async function execute(input: { city: string; units?: string }) { const cacheKey ${input.city}-${input.units}; const cached cache.get(cacheKey); if (cached) { return cached as string; } // ... 正常API调用 cache.set(cacheKey, result); return result; }9. 扩展到其他应用场景掌握了天气查询服务器的开发后你可以将其模式应用到各种业务场景9.1 数据库查询服务器// 简化的数据库查询工具示例 export class DatabaseTool { getTool(): Tool { return { name: query_customers, description: 查询客户信息, inputSchema: { type: object, properties: { region: { type: string }, status: { type: string, enum: [active, inactive] } } } }; } async execute(input: any) { // 执行安全的数据库查询只返回必要信息 const results await db.query( SELECT name, email FROM customers WHERE region ? AND status ?, [input.region, input.status] ); return JSON.stringify(results); } }9.2 内部工具集成将公司内部系统如 CRM、ERP 通过 MCP 暴露给 AIexport class InternalTools { getTools(): Tool[] { return [ { name: create_support_ticket, description: 创建技术支持工单 }, { name: check_project_status, description: 检查项目进度状态 } ]; } }9.3 监控与告警集成让 AI 能够访问系统状态信息export class MonitoringTool { getTool(): Tool { return { name: system_health, description: 获取系统健康状态 }; } async execute() { const metrics await this.collectMetrics(); return this.formatHealthReport(metrics); } }构建自定义 MCP 服务器的核心价值在于将 AI 能力与你的具体业务环境无缝衔接。通过标准化的协议你可以在不同 AI 应用间复用同一套工具链显著提升开发效率。开始实践时建议从简单的查询类工具入手逐步扩展到更复杂的业务流程集成。每个工具都应该有明确的输入输出定义和完整的错误处理。最重要的是始终将安全性放在首位确保 MCP 服务器成为业务能力的增强器而非安全漏洞。随着 MCP 生态的成熟这类自定义服务器将成为企业 AI 集成的标准做法。现在投入学习将为未来的 AI 原生应用开发奠定坚实基础。