MCP协议Tool与Resource原语:AI应用外部系统集成实战指南
如果你正在构建AI应用可能会遇到这样的困境想要调用外部API、访问数据库或操作文件系统时却发现现有的AI框架要么封装过度缺乏灵活性要么需要大量胶水代码来桥接不同系统。这正是MCPModel Context Protocol原语中Tool和Resource设计要解决的核心问题。MCP不是另一个AI框架而是一套标准化协议它重新定义了AI模型与外部资源交互的方式。其中Tool和Resource作为核心原语分别解决了动作执行和状态管理两个维度的需求。与传统的单一工具调用方式不同MCP将资源声明与工具执行分离这种设计让AI应用具备了真正的生产级可靠性。本文将深入解析MCP中Tool和Resource的使用方式通过完整示例展示如何构建可维护、可扩展的AI应用架构。无论你是正在评估MCP的技术决策者还是需要具体实现的一线开发者都能找到对应的实践指导。1. MCP原语重新定义AI与外部系统的交互边界1.1 为什么需要MCP协议在传统AI应用开发中模型与外部系统的集成往往面临几个典型问题耦合过紧工具逻辑与业务代码混杂修改一个API端点可能影响整个系统状态管理混乱数据库连接、文件句柄等资源生命周期难以控制错误处理复杂网络超时、权限验证、重试机制等需要重复实现可观测性差工具调用链路追踪、性能监控缺乏标准化方案MCP协议通过定义清晰的边界和标准化的交互模式让AI模型能够以声明式的方式使用外部能力而无需关心具体的实现细节。1.2 Tool与Resource的核心区别理解Tool和Resource的区别是掌握MCP的关键Tool工具代表一个可执行的动作或操作具有明确的输入和输出。例如发送HTTP请求到外部API执行数据库查询调用本地系统命令Resource资源代表一个可被操作的状态或实体具有生命周期管理。例如数据库连接池文件系统路径API认证令牌网络套接字这种分离的设计理念类似于函数式编程中的纯函数Tool与副作用管理Resource让系统更容易推理和调试。1.3 MCP在实际项目中的价值体现从工程实践角度看MCP带来的核心价值包括开发效率提升标准化接口减少重复代码新工具接入成本显著降低系统可靠性增强资源生命周期管理避免内存泄漏和连接耗尽运维可观测性统一的日志、监控和调试接口团队协作优化前后端开发通过协议规范明确职责边界2. 环境准备与MCP开发栈搭建2.1 基础环境要求在开始MCP开发前需要确保以下环境就绪# 检查Node.js版本推荐18.x以上 node --version # 检查npm版本 npm --version # 或者使用yarn yarn --version2.2 MCP相关依赖安装MCP生态系统提供了多种语言的支持本文以TypeScript/JavaScript为例# 创建新项目 mkdir mcp-demo cd mcp-demo # 初始化package.json npm init -y # 安装核心依赖 npm install modelcontextprotocol/sdk npm install -D typescript types/node ts-node # 开发工具依赖 npm install -D eslint prettier2.3 项目结构规划合理的项目结构是MCP应用可维护性的基础mcp-demo/ ├── src/ │ ├── tools/ # Tool实现 │ ├── resources/ # Resource定义 │ ├── servers/ # MCP服务器 │ └── clients/ # MCP客户端 ├── tests/ # 测试用例 ├── package.json └── tsconfig.json2.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] }3. Tool原语定义可执行的操作能力3.1 基础Tool接口设计MCP中的Tool需要实现特定的接口规范// src/tools/base-tool.ts import { Tool } from modelcontextprotocol/sdk; export interface MCPTool extends Tool { // 工具名称在MCP协议中唯一标识 name: string; // 工具描述用于AI模型理解工具用途 description: string; // 输入参数JSON Schema定义 inputSchema: { type: object; properties: Recordstring, any; required?: string[]; }; // 工具执行逻辑 execute(params: any): Promiseany; }3.2 实现HTTP API调用Tool以下是一个完整的HTTP工具实现示例// src/tools/http-tool.ts import { MCPTool } from ./base-tool; export class HttpTool implements MCPTool { name http_request; description 发送HTTP请求到指定的API端点; inputSchema { type: object, properties: { url: { type: string, description: 请求的URL地址 }, method: { type: string, enum: [GET, POST, PUT, DELETE], default: GET }, headers: { type: object, description: 请求头信息 }, body: { type: object, description: 请求体数据仅POST/PUT } }, required: [url] }; async execute(params: any): Promiseany { const { url, method GET, headers {}, body } params; try { const response await fetch(url, { method, headers: { Content-Type: application/json, ...headers }, body: body ? JSON.stringify(body) : undefined }); if (!response.ok) { throw new Error(HTTP ${response.status}: ${response.statusText}); } const data await response.json(); return { success: true, status: response.status, data }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : Unknown error }; } } }3.3 实现数据库查询Tool数据库操作是AI应用的常见需求以下是SQL查询工具示例// src/tools/database-tool.ts import { MCPTool } from ./base-tool; export class DatabaseTool implements MCPTool { name database_query; description 执行SQL查询语句; inputSchema { type: object, properties: { query: { type: string, description: SQL查询语句 }, parameters: { type: array, description: 查询参数, default: [] } }, required: [query] }; // 假设已通过Resource获得数据库连接 private dbConnection: any; constructor(dbConnection: any) { this.dbConnection dbConnection; } async execute(params: any): Promiseany { const { query, parameters [] } params; try { // 实际项目中应使用参数化查询防止SQL注入 const result await this.dbConnection.query(query, parameters); return { success: true, rowCount: result.rowCount, rows: result.rows }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : Database error }; } } }3.4 Tool的注册与管理创建Tool管理器来统一管理所有工具// src/tools/tool-manager.ts import { MCPTool } from ./base-tool; export class ToolManager { private tools: Mapstring, MCPTool new Map(); registerTool(tool: MCPTool): void { if (this.tools.has(tool.name)) { throw new Error(Tool ${tool.name} already registered); } this.tools.set(tool.name, tool); } getTool(name: string): MCPTool | undefined { return this.tools.get(name); } listTools(): MCPTool[] { return Array.from(this.tools.values()); } async executeTool(name: string, params: any): Promiseany { const tool this.getTool(name); if (!tool) { throw new Error(Tool ${name} not found); } return await tool.execute(params); } }4. Resource原语管理有状态的外部资源4.1 Resource的生命周期管理Resource的核心价值在于其生命周期管理能力// src/resources/base-resource.ts export interface MCPResource { // 资源唯一标识 uri: string; // 资源类型描述 mimeType: string; // 资源名称可读 name: string; // 资源描述 description: string; // 初始化资源 initialize(): Promisevoid; // 清理资源 cleanup(): Promisevoid; // 检查资源状态 isHealthy(): boolean; }4.2 数据库连接Resource实现数据库连接是典型的需要生命周期管理的资源// src/resources/database-resource.ts import { MCPResource } from ./base-resource; import { Pool } from pg; // PostgreSQL客户端 export class DatabaseResource implements MCPResource { uri: string; name: string; description: string; mimeType application/x-postgresql-connection; private pool: Pool | null null; private config: any; constructor(config: any) { this.config config; this.uri postgresql://${config.host}:${config.port}/${config.database}; this.name Database: ${config.database}; this.description PostgreSQL数据库连接; } async initialize(): Promisevoid { this.pool new Pool(this.config); // 测试连接 const client await this.pool.connect(); try { await client.query(SELECT 1); } finally { client.release(); } } async cleanup(): Promisevoid { if (this.pool) { await this.pool.end(); this.pool null; } } isHealthy(): boolean { return this.pool ! null !this.pool.ended; } // 获取连接池供Tool使用 getPool(): Pool { if (!this.pool || !this.isHealthy()) { throw new Error(Database resource not available); } return this.pool; } }4.3 文件系统Resource实现文件系统访问也是常见需求// src/resources/filesystem-resource.ts import { MCPResource } from ./base-resource; import { promises as fs } from fs; import path from path; export class FilesystemResource implements MCPResource { uri: string; name: string; description: string; mimeType application/x-filesystem-directory; private basePath: string; private fileHandles: Mapstring, fs.FileHandle new Map(); constructor(basePath: string, name?: string) { this.basePath path.resolve(basePath); this.uri file://${this.basePath}; this.name name || Filesystem: ${this.basePath}; this.description 文件系统访问资源; } async initialize(): Promisevoid { // 验证目录存在且有访问权限 try { await fs.access(this.basePath); } catch { throw new Error(Directory ${this.basePath} does not exist or is not accessible); } } async cleanup(): Promisevoid { // 关闭所有打开的文件句柄 for (const [filePath, handle] of this.fileHandles) { try { await handle.close(); } catch (error) { console.error(Error closing file handle for ${filePath}:, error); } } this.fileHandles.clear(); } isHealthy(): boolean { return true; // 文件系统资源通常总是可用的 } // 文件操作相关方法 async readFile(filePath: string): Promisestring { const fullPath path.join(this.basePath, filePath); return await fs.readFile(fullPath, utf-8); } async writeFile(filePath: string, content: string): Promisevoid { const fullPath path.join(this.basePath, filePath); await fs.writeFile(fullPath, content, utf-8); } }4.4 API认证Token Resource实现对于需要认证的APIToken管理很重要// src/resources/token-resource.ts import { MCPResource } from ./base-resource; export class TokenResource implements MCPResource { uri: string; name: string; description: string; mimeType application/x-auth-token; private token: string; private expiryTime: number; private refreshCallback?: () Promisestring; constructor( initialToken: string, expiryTime: number, refreshCallback?: () Promisestring ) { this.token initialToken; this.expiryTime expiryTime; this.refreshCallback refreshCallback; this.uri token://${btoa(initialToken).slice(0, 10)}; this.name API认证令牌; this.description 用于访问受保护API的认证令牌; } async initialize(): Promisevoid { // 验证初始token是否有效 if (!this.token) { throw new Error(Initial token cannot be empty); } } async cleanup(): Promisevoid { // Token清理通常不需要特殊操作 this.token ; } isHealthy(): boolean { return Date.now() this.expiryTime !!this.token; } // 获取当前token必要时自动刷新 async getToken(): Promisestring { if (!this.isHealthy() this.refreshCallback) { this.token await this.refreshCallback(); // 更新过期时间假设新token有效期1小时 this.expiryTime Date.now() 3600000; } if (!this.isHealthy()) { throw new Error(Token is expired and no refresh mechanism available); } return this.token; } }5. MCP服务器集成连接Tool与Resource5.1 基础MCP服务器实现MCP服务器是Tool和Resource的协调中心// src/servers/mcp-server.ts import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { ToolManager } from ../tools/tool-manager; import { ResourceManager } from ../resources/resource-manager; export class MCPServer { private server: Server; private toolManager: ToolManager; private resourceManager: ResourceManager; constructor() { this.server new Server({ name: mcp-demo-server, version: 1.0.0 }, { capabilities: { tools: {}, resources: {} } }); this.toolManager new ToolManager(); this.resourceManager new ResourceManager(); this.setupHandlers(); } private setupHandlers(): void { // 处理工具列表请求 this.server.setRequestHandler( tools/list, async () ({ tools: this.toolManager.listTools().map(tool ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })) }) ); // 处理工具调用请求 this.server.setRequestHandler( tools/call, async (request) { const { name, arguments: params } request.params; try { const result await this.toolManager.executeTool(name, params); return { content: [ { type: text, text: JSON.stringify(result, null, 2) } ] }; } catch (error) { return { content: [ { type: text, text: Error executing tool ${name}: ${error} } ], isError: true }; } } ); // 处理资源列表请求 this.server.setRequestHandler( resources/list, async () ({ resources: this.resourceManager.listResources().map(resource ({ uri: resource.uri, name: resource.name, description: resource.description, mimeType: resource.mimeType })) }) ); } async run(): Promisevoid { const transport new StdioServerTransport(); await this.server.connect(transport); console.error(MCP Server running on stdio); } // 注册工具和资源的方法 registerTool(tool: any): void { this.toolManager.registerTool(tool); } registerResource(resource: any): void { this.resourceManager.registerResource(resource); } }5.2 Resource管理器实现Resource管理器负责协调多个资源的生命周期// src/resources/resource-manager.ts import { MCPResource } from ./base-resource; export class ResourceManager { private resources: Mapstring, MCPResource new Map(); async registerResource(resource: MCPResource): Promisevoid { if (this.resources.has(resource.uri)) { throw new Error(Resource ${resource.uri} already registered); } await resource.initialize(); this.resources.set(resource.uri, resource); } async unregisterResource(uri: string): Promisevoid { const resource this.resources.get(uri); if (resource) { await resource.cleanup(); this.resources.delete(uri); } } getResource(uri: string): MCPResource | undefined { return this.resources.get(uri); } listResources(): MCPResource[] { return Array.from(this.resources.values()); } // 健康检查所有资源 async healthCheck(): PromiseMapstring, boolean { const status new Map(); for (const [uri, resource] of this.resources) { status.set(uri, resource.isHealthy()); } return status; } // 清理所有资源 async cleanupAll(): Promisevoid { for (const [uri, resource] of this.resources) { try { await resource.cleanup(); } catch (error) { console.error(Error cleaning up resource ${uri}:, error); } } this.resources.clear(); } }6. 完整示例构建天气查询AI应用6.1 应用场景描述我们构建一个能够查询天气信息的AI应用展示Tool和Resource的协同工作使用HttpTool调用天气API使用DatabaseTool记录查询历史使用TokenResource管理API认证使用FilesystemResource缓存响应数据6.2 天气查询Tool实现// src/tools/weather-tool.ts import { MCPTool } from ./base-tool; export class WeatherTool implements MCPTool { name get_weather; description 获取指定城市的天气信息; inputSchema { type: object, properties: { city: { type: string, description: 城市名称 }, country: { type: string, description: 国家代码可选, default: CN } }, required: [city] }; private apiKey: string; constructor(apiKey: string) { this.apiKey apiKey; } async execute(params: any): Promiseany { const { city, country CN } params; const url https://api.weatherapi.com/v1/current.json?key${this.apiKey}q${city},${country}; try { const response await fetch(url); if (!response.ok) { throw new Error(Weather API error: ${response.status}); } const data await response.json(); return { success: true, city: data.location.name, country: data.location.country, temperature: data.current.temp_c, condition: data.current.condition.text, humidity: data.current.humidity, windSpeed: data.current.wind_kph }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : Weather API error }; } } }6.3 应用入口点实现// src/main.ts import { MCPServer } from ./servers/mcp-server; import { WeatherTool } from ./tools/weather-tool; import { DatabaseTool } from ./tools/database-tool; import { DatabaseResource } from ./resources/database-resource; import { TokenResource } from ./resources/token-resource; async function main() { const server new MCPServer(); // 初始化资源 const dbResource new DatabaseResource({ host: localhost, port: 5432, database: weather_app, user: postgres, password: password }); const tokenResource new TokenResource( your-weather-api-key, Date.now() 3600000, // 1小时过期 async () { // Token刷新逻辑 return new-refreshed-token; } ); // 注册资源 await server.registerResource(dbResource); await server.registerResource(tokenResource); // 初始化工具依赖资源 const weatherTool new WeatherTool(await tokenResource.getToken()); const dbTool new DatabaseTool(dbResource.getPool()); // 注册工具 server.registerTool(weatherTool); server.registerTool(dbTool); // 启动服务器 await server.run(); } main().catch(console.error);6.4 测试脚本创建测试脚本来验证功能// src/test/test-weather.ts import { WeatherTool } from ../tools/weather-tool; async function testWeatherTool() { const tool new WeatherTool(test-api-key); const result await tool.execute({ city: Beijing, country: CN }); console.log(Weather tool test result:, result); } // 模拟测试实际需要有效的API key testWeatherTool().catch(console.error);7. 运行验证与调试技巧7.1 启动MCP服务器创建启动脚本// src/start-server.ts import { main } from ./main; main().then(() { console.log(MCP Server started successfully); }).catch((error) { console.error(Failed to start MCP Server:, error); process.exit(1); });在package.json中添加启动脚本{ scripts: { start: ts-node src/start-server.ts, dev: ts-node --watch src/start-server.ts, test: ts-node src/test/test-weather.ts } }7.2 验证服务器功能使用curl或专门的MCP客户端测试服务器# 启动服务器 npm start # 在另一个终端测试工具列表 echo {jsonrpc:2.0,id:1,method:tools/list,params:{}} | nc localhost 80007.3 调试与日志记录添加详细的日志记录帮助调试// src/utils/logger.ts export class Logger { static info(message: string, data?: any): void { console.log([INFO] ${message}, data || ); } static error(message: string, error?: any): void { console.error([ERROR] ${message}, error || ); } static debug(message: string, data?: any): void { if (process.env.DEBUG) { console.debug([DEBUG] ${message}, data || ); } } }8. 常见问题与解决方案8.1 工具执行失败排查问题现象可能原因排查步骤解决方案工具调用返回错误参数格式不正确检查inputSchema定义验证参数是否符合JSON SchemaHTTP工具超时网络连接问题检查网络连通性增加超时设置添加重试机制数据库连接失败配置错误或服务未启动验证连接参数检查数据库服务状态和权限认证失败Token过期或无效检查Token有效期实现Token自动刷新机制8.2 资源管理问题问题现象可能原因排查步骤解决方案资源初始化失败依赖服务不可用检查依赖服务状态添加健康检查和重试逻辑内存泄漏资源未正确清理监控内存使用情况确保cleanup方法被调用资源竞争多个工具同时访问分析访问模式实现资源锁或队列机制8.3 性能优化建议连接池优化数据库连接池大小根据并发量调整缓存策略频繁访问的数据添加缓存层异步处理耗时操作使用异步非阻塞方式批量操作减少频繁的小规模操作9. 生产环境最佳实践9.1 安全考虑输入验证所有工具参数必须严格验证权限控制基于角色的工具访问权限敏感信息API密钥等使用环境变量或密钥管理服务审计日志记录所有工具调用和资源访问9.2 监控与可观测性实现完整的监控体系// src/monitoring/monitor.ts export class PerformanceMonitor { private metrics: Mapstring, number new Map(); recordToolCall(toolName: string, duration: number): void { const key tool.${toolName}.duration; this.metrics.set(key, duration); } recordResourceUsage(resourceUri: string, usage: number): void { const key resource.${resourceUri}.usage; this.metrics.set(key, usage); } getMetrics(): Mapstring, number { return new Map(this.metrics); } }9.3 错误处理与恢复实现健壮的错误处理机制重试策略网络请求失败时自动重试熔断机制连续失败时暂时禁用问题工具优雅降级主要功能不可用时提供备用方案事务回滚确保操作原子性9.4 部署与运维容器化部署使用Docker确保环境一致性配置管理不同环境使用不同配置健康检查实现 readiness/liveness 探针滚动更新避免服务中断的部署策略通过本文的详细讲解和完整示例你应该已经掌握了MCP中Tool和Resource的核心概念和使用方式。这种设计模式不仅适用于AI应用任何需要管理外部依赖和复杂状态的系统都可以从中受益。关键在于理解Tool和Resource的职责分离Tool关注动作执行Resource关注状态管理。在实际项目中建议先从简单的工具开始逐步构建资源管理体系。重视监控和日志这对后续的问题排查和性能优化至关重要。MCP协议仍在快速发展中保持对最新标准的关注及时调整实现方案。