基于MCP协议构建AI数据服务:以实时风险基金数据为例

基于MCP协议构建AI数据服务:以实时风险基金数据为例

在实际 AI 应用开发中,让大语言模型(LLM)或 AI Agent 获取实时、结构化的外部数据一直是一个核心挑战。传统的做法往往需要开发者编写复杂的 API 调用代码、处理网络请求和解析响应,这不仅增加了开发复杂度,也使得 AI 难以灵活、动态地感知外部世界的变化。Model Context Protocol(MCP)的出现,为这个问题提供了一种标准化的解决方案。它定义了一套 LLM 与外部工具、数据源进行安全、高效交互的协议,使得 AI 能够像调用本地函数一样,便捷地获取外部能力。

本文将以一个具体的场景——“为 AI Agent 提供实时的风险投资基金数据”——为例,深入探讨如何基于 MCP 协议构建一个数据服务。我们将从理解 MCP 的核心概念和工作原理开始,逐步完成一个 MCP Server 的开发,该 Server 能够提供基金动量(Fund Momentum)数据。通过这个过程,你将掌握 MCP 的 JSON-RPC 通信机制、工具(Tools)与资源(Resources)的定义方法,以及如何将你的数据服务无缝集成到 Claude Desktop、Cursor 等支持 MCP 的 AI 客户端中。最终,你将拥有一个可运行、可扩展的 MCP 数据服务原型,并能理解在生产环境中部署此类服务需要考虑的关键因素。

1. 理解 MCP:连接 AI 与外部世界的标准化协议

在深入代码之前,必须厘清 MCP 要解决的根本问题以及它的设计哲学。这有助于我们在实现时做出正确的技术决策。

1.1 MCP 的核心目标与解决的问题

MCP 并非一个具体的框架或 SDK,而是一套开放协议。它的核心目标是标准化 LLM/AI Agent 与外部系统(数据源、工具、服务)之间的交互方式。在没有 MCP 之前,常见的集成模式是“硬编码”:开发者需要为特定的 LLM(如 OpenAI GPTs 的 Actions)编写特定的适配器,或者为每个外部 API 编写一段胶水代码。这种方式存在几个明显问题:

  • 耦合度高:AI 应用逻辑与具体的外部服务 API 深度绑定,更换数据源或 LLM 提供商成本巨大。
  • 能力发现困难:LLM 无法动态感知外部系统提供了哪些能力(工具、数据),需要开发者预先告知并编排。
  • 安全性挑战:每次调用外部 API 都需要处理认证、授权、输入验证和输出过滤,缺乏统一的安全层。
  • 开发体验碎片化:不同项目、不同团队可能采用完全不同的集成模式,难以复用和协作。

MCP 通过定义一套基于 JSON-RPC 的通用协议,将外部能力抽象为Tools(工具)Resources(资源)。LLM 客户端(如 Claude Desktop)通过 MCP 协议与 MCP Server 通信,动态发现并调用这些能力,而无需关心 Server 背后的具体实现是查询数据库、调用 REST API 还是执行一个本地脚本。

1.2 MCP 的核心组件与交互流程

一个典型的 MCP 架构包含三个核心角色,其交互流程构成了数据流动的闭环。

  1. MCP Client(客户端):通常是集成了 MCP 协议的 AI 应用,如 Claude Desktop、Cursor IDE 或你自己编写的 LLM 应用。Client 负责初始化连接、列出可用的工具和资源,并发送执行请求。
  2. MCP Server(服务器):提供具体外部能力的服务端。它向 Client 宣告自己支持哪些 Tools 和 Resources,并处理 Client 发来的调用请求。本文我们要构建的就是一个 Fund Momentum Data Server。
  3. JSON-RPC 2.0 协议:Client 和 Server 之间通过此协议进行通信。所有请求和响应都是格式化的 JSON 消息,通过标准输入输出(stdio)、HTTP 或 SSE 等传输层进行交换。

交互的基本流程如下:

  • 初始化:Client 启动 Server 进程,并发送initialize请求,交换双方的能力信息。
  • 能力列表:Client 发送tools/listresources/list请求,Server 返回其提供的所有工具和资源的元数据。
  • 工具调用:当用户向 AI 提出需求(如“查看最近活跃的基金”),LLM 判断需要调用某个 Tool,Client 就会向 Server 发送tools/call请求。
  • 结果返回:Server 执行工具逻辑(例如查询数据库),并将结果通过tools/call响应返回给 Client。
  • 资源读取:对于 Resources,Client 可以通过resources/read请求获取其内容,或者通过resources/subscribe进行订阅以获取更新。

1.3 Tools 与 Resources 的区分与选型

这是设计 MCP Server 时的第一个关键决策点。理解两者的区别至关重要。

  • Tools(工具):代表一个动作操作。它通常有明确的输入参数,执行后会产生一个结果或副作用。例如,“搜索基金”、“发送邮件”、“执行计算”。Tools 适合封装那些需要根据用户输入动态执行逻辑的场景。
  • Resources(资源):代表一个静态或动态的数据实体,可以通过 URI 来标识和访问。例如,“fund://top10”(前10基金列表)、“file:///etc/config.yaml”(配置文件)。Resources 适合暴露结构化的数据源,AI 可以像读取文件一样读取它们的内容。资源的内容可以是静态的,也可以通过订阅(Subscribe)机制实现动态更新。

对于“基金动量数据”这个场景,我们可以这样设计:

  • 提供一个 Tool:get_fund_momentum,接受sector(领域)或time_range(时间范围)等参数,返回筛选后的基金数据。这提供了灵活的查询能力。
  • 提供一个或多个 Resources:例如fund://momentum/global代表全球基金动量榜单。AI 可以直接“读取”这个资源来获取一份预设的数据视图。这提供了便捷的数据访问方式。

在实际项目中,通常根据数据的使用模式来决定。如果数据查询条件多变,用 Tool;如果数据是固定的报表或视图,用 Resource。

2. 构建 Fund Momentum MCP Server:环境与项目初始化

我们将使用 Node.js 来构建 MCP Server,因为它有成熟的 JSON-RPC 库和活跃的社区。我们将从零开始搭建项目,确保每一步都可复现。

2.1 环境准备与依赖确认

首先,确保你的开发环境满足以下要求:

组件要求检查命令说明
Node.js>= 18.0.0node --versionMCP 相关库通常需要较新的 Node 版本。
npm随 Node 安装npm --version用于包管理。
代码编辑器VS Code / Cursor 等-推荐使用支持 MCP 的编辑器以便后续测试。
Claude Desktop最新版(可选)-用于最终集成测试,非开发必需。

接下来,创建项目目录并初始化:

# 创建项目目录 mkdir fund-momentum-mcp-server cd fund-momentum-mcp-server # 初始化 npm 项目,生成 package.json npm init -y

2.2 安装核心依赖

我们将使用@modelcontextprotocol/sdk这个官方 SDK,它极大地简化了 MCP Server 的开发。

# 安装 MCP SDK npm install @modelcontextprotocol/sdk # 安装 TypeScript 及相关类型定义(推荐用于更好的开发体验) npm install --save-dev typescript @types/node

安装完成后,你的package.jsondependenciesdevDependencies应该类似这样:

{ "name": "fund-momentum-mcp-server", "version": "1.0.0", "description": "An MCP server providing live VC fund momentum data.", "main": "dist/index.js", "scripts": { "build": "tsc", "start": "node dist/index.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0" }, "devDependencies": { "@types/node": "^20.0.0", "typescript": "^5.0.0" } }

2.3 配置 TypeScript 编译器

在项目根目录创建tsconfig.json文件,配置 TypeScript 编译选项。

{ "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"] }

这个配置将src目录下的 TypeScript 文件编译到dist目录,并生成类型声明文件。

2.4 创建项目基础结构

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

mkdir src touch src/index.ts

现在,项目的基础结构已经搭建完成。接下来,我们将开始编写 MCP Server 的核心逻辑。

3. 实现 MCP Server 核心逻辑

我们将遵循 MCP SDK 的引导,逐步实现 Server 的初始化、工具定义和资源定义。

3.1 创建 Server 实例与初始化

编辑src/index.ts文件,首先导入必要的模块并创建 Server 实例。

// src/index.ts import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; // 1. 创建 Server 实例 // 第一个参数是 Server 的元信息,用于 Client 识别 const server = new Server( { name: 'fund-momentum-server', version: '1.0.0', }, { capabilities: { // 声明 Server 支持的能力:工具和资源 tools: {}, resources: {}, }, } );

这里创建了一个最基本的 Server,并声明它支持toolsresources能力。StdioServerTransport是用于标准输入输出的传输层,这是 MCP 最常见的一种通信方式,允许 Client 通过子进程启动 Server。

3.2 定义并注册 Tools(工具)

Tools 是 Server 提供的可调用函数。我们需要定义工具的 Schema(描述其输入参数和输出)以及对应的处理函数。

假设我们的get_fund_momentum工具支持按领域筛选,并返回基金列表。

// src/index.ts (续) // 2. 定义工具 (Tools) // 工具 Schema,描述输入参数 const getFundMomentumTool = { name: 'get_fund_momentum', description: '获取指定领域或全局的风险投资基金动量数据。动量数据可能包括基金名称、近期投资活跃度、关注领域等。', inputSchema: { type: 'object', properties: { sector: { type: 'string', description: '筛选的领域,例如:AI, FinTech, Biotech。留空则返回所有领域的数据。', enum: ['', 'AI', 'FinTech', 'Biotech', 'CleanTech', 'Enterprise'] // 示例领域 }, limit: { type: 'number', description: '返回结果的最大数量,默认 10。', default: 10 } } } }; // 3. 注册工具处理函数 server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [getFundMomentumTool], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { // 检查请求的工具名是否匹配 if (request.params.name !== getFundMomentumTool.name) { throw new Error(`Unknown tool: ${request.params.name}`); } const args = request.params.arguments as { sector?: string; limit?: number }; const sector = args.sector || ''; const limit = args.limit || 10; // 模拟数据获取逻辑 // 在实际项目中,这里会连接数据库、调用外部API等 const allFunds = [ { name: 'A16Z Bio Fund', sector: 'Biotech', momentumScore: 95, recentInvestments: 12 }, { name: 'Sequoia Capital AI', sector: 'AI', momentumScore: 92, recentInvestments: 15 }, { name: 'Tiger Global FinTech', sector: 'FinTech', momentumScore: 88, recentInvestments: 8 }, { name: 'Breakthrough Energy', sector: 'CleanTech', momentumScore: 85, recentInvestments: 10 }, { name: 'Insight Partners Enterprise', sector: 'Enterprise', momentumScore: 82, recentInvestments: 9 }, // ... 更多模拟数据 ]; // 根据参数过滤数据 let filteredFunds = allFunds; if (sector) { filteredFunds = allFunds.filter(fund => fund.sector === sector); } filteredFunds = filteredFunds.slice(0, limit); // 返回工具调用结果 return { content: [ { type: 'text', text: JSON.stringify({ funds: filteredFunds, count: filteredFunds.length, sectorFilter: sector || 'all', }, null, 2), // 格式化 JSON 输出,便于阅读 }, ], }; });

关键点解释:

  1. 工具 SchemainputSchema使用 JSON Schema 定义了工具的参数。enum限制了输入范围,default提供了默认值。清晰的description能帮助 LLM 更好地理解如何使用这个工具。
  2. 列表处理setRequestHandler用于处理ListToolsRequest,当 Client 查询可用工具时,返回我们定义的工具列表。
  3. 调用处理setRequestHandler用于处理CallToolRequest。我们首先验证工具名,然后从request.params.arguments中提取参数。核心业务逻辑(此处为模拟数据过滤)在此执行。
  4. 返回格式:MCP 要求工具调用结果放在content数组中,通常我们返回type: 'text'的文本内容。将数据序列化为 JSON 字符串是一种通用且 LLM 易于解析的格式。

3.3 定义并注册 Resources(资源)

Resources 通过 URI 标识。我们将定义一个资源,提供全球基金动量榜单。

// src/index.ts (续) // 4. 定义资源 (Resources) // 资源模板,描述资源的元数据 const globalMomentumResource = { uri: 'fund://momentum/global', name: '全球基金动量榜单', description: '展示全球范围内近期投资最活跃的风险投资基金排名。', mimeType: 'application/json', // 资源内容类型 }; // 5. 注册资源处理函数 server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [globalMomentumResource], }; }); server.setRequestHandler(ReadResourceRequestSchema, async (request) => { // 检查请求的 URI 是否匹配 if (request.params.uri !== globalMomentumResource.uri) { throw new Error(`Unknown resource: ${request.params.uri}`); } // 模拟资源内容 const resourceData = { lastUpdated: new Date().toISOString(), description: 'Top 5 funds by momentum score', funds: [ { rank: 1, name: 'Sequoia Capital AI', sector: 'AI', momentumScore: 92 }, { rank: 2, name: 'A16Z Bio Fund', sector: 'Biotech', momentumScore: 95 }, { rank: 3, name: 'Tiger Global FinTech', sector: 'FinTech', momentumScore: 88 }, { rank: 4, name: 'Lightspeed Venture Partners', sector: 'Consumer', momentumScore: 86 }, { rank: 5, name: 'Benchmark', sector: 'Enterprise', momentumScore: 84 }, ] }; // 返回资源内容 return { contents: [ { uri: request.params.uri, mimeType: globalMomentumResource.mimeType, text: JSON.stringify(resourceData, null, 2), }, ], }; });

关键点解释:

  1. URI 设计fund://momentum/global是一个自定义的 URI 方案。你可以设计自己的命名空间,如vcdata://funds/top。URI 应具有唯一性和描述性。
  2. MIME 类型mimeType告诉 Client 如何解析内容。application/json是最通用的选择。
  3. 列表与读取:与 Tools 类似,需要分别处理ListResourcesRequestReadResourceRequest
  4. 内容返回:资源内容通过contents数组返回,每个元素包含urimimeTypetext(或blob)。

3.4 启动 Server 并处理连接

最后,我们需要启动 Server,并建立与 Client 的传输连接。

// src/index.ts (续) // 6. 启动 Server async function runServer() { // 使用标准输入输出作为传输层 const transport = new StdioServerTransport(); await server.connect(transport); console.error('Fund Momentum MCP Server running on stdio'); // 使用 stderr 输出日志,避免干扰 JSON-RPC 通信 } // 捕获未处理的异常和拒绝,确保 Server 稳定 process.on('uncaughtException', (error) => { console.error('Uncaught exception:', error); }); process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled rejection at:', promise, 'reason:', reason); }); // 执行启动 runServer().catch((error) => { console.error('Failed to start server:', error); process.exit(1); });

注意:MCP 协议要求所有通信(JSON-RPC 消息)都通过标准输入输出进行。因此,任何非协议的输出(如调试日志)都应打印到stderr(使用console.error),以免污染stdout上的协议数据流,导致 Client 解析失败。

至此,一个完整的、提供 Tools 和 Resources 的 MCP Server 核心代码已经完成。

4. 编译、运行与基础测试

在集成到 AI 客户端之前,我们需要确保 Server 本身能正确启动和响应。

4.1 编译 TypeScript 代码

运行编译命令,将 TypeScript 代码转换为 JavaScript。

npm run build

如果一切顺利,会在dist目录下生成index.js和类型声明文件。

4.2 直接运行 Server 进行基础测试

我们可以直接运行 Server,并通过手动输入 JSON-RPC 请求来模拟 Client 进行测试。首先,在package.json中添加一个方便测试的脚本。

// package.json (scripts 部分新增) "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "ts-node src/index.ts" // 新增,用于开发时直接运行 ts 文件(需安装 ts-node) }

安装ts-node以便直接运行 TypeScript。

npm install --save-dev ts-node

现在,启动 Server:

npm run dev

你会看到Fund Momentum MCP Server running on stdio输出到控制台(stderr)。此时 Server 正在等待来自stdin的 JSON-RPC 请求。

4.3 模拟 Client 发送测试请求

我们需要在另一个终端或通过脚本发送请求。创建一个简单的测试脚本test_client.mjs(使用 ES 模块):

// test_client.mjs import { spawn } from 'child_process'; import { createInterface } from 'readline'; // 启动我们的 MCP Server 进程 const serverProcess = spawn('node', ['dist/index.js'], { stdio: ['pipe', 'pipe', 'inherit'] // 继承 stderr 以查看日志 }); const rl = createInterface({ input: process.stdin, output: process.stdout }); // 监听 Server 的输出 (stdout) serverProcess.stdout.on('data', (data) => { console.log('[Server Response]:', data.toString()); }); // 发送初始化请求 const initRequest = { jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '1.0', capabilities: {}, clientInfo: { name: 'TestClient', version: '1.0' } } }; serverProcess.stdin.write(JSON.stringify(initRequest) + '\n'); // 发送列出工具的请求 const listToolsRequest = { jsonrpc: '2.0', id: 2, method: 'tools/list', params: {} }; setTimeout(() => { serverProcess.stdin.write(JSON.stringify(listToolsRequest) + '\n'); }, 100); // 发送调用工具的请求 const callToolRequest = { jsonrpc: '2.0', id: 3, method: 'tools/call', params: { name: 'get_fund_momentum', arguments: { sector: 'AI', limit: 2 } } }; setTimeout(() => { serverProcess.stdin.write(JSON.stringify(callToolRequest) + '\n'); }, 200); // 5秒后退出测试 setTimeout(() => { serverProcess.kill(); process.exit(0); }, 5000);

运行测试脚本:

node test_client.mjs

你应该能看到 Server 返回的 JSON-RPC 响应,其中包含工具列表和调用get_fund_momentum后返回的 AI 领域基金数据。这验证了 Server 的基本通信和逻辑功能正常。

5. 集成到 AI 客户端:以 Claude Desktop 为例

真正的价值在于让 AI Agent 使用我们的服务。这里以 Anthropic 的 Claude Desktop 为例,展示集成步骤。其他支持 MCP 的客户端(如 Cursor)配置方式类似。

5.1 配置 Claude Desktop 加载本地 MCP Server

Claude Desktop 允许通过配置文件添加本地 MCP Server。

  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. 编辑配置文件:如果文件不存在,则创建它。添加以下内容:

{ "mcpServers": { "fund-momentum": { "command": "node", "args": [ "/ABSOLUTE/PATH/TO/YOUR/fund-momentum-mcp-server/dist/index.js" ] } } }

关键点

  • "fund-momentum"是给这个 Server 起的名字,可以自定义。
  • command是启动 Server 的命令,这里是node
  • args是命令的参数,必须提供编译后的 JS 文件的绝对路径。相对路径可能因工作目录问题导致启动失败。

5.2 验证集成并开始对话

  1. 重启 Claude Desktop:修改配置后,完全退出并重新启动 Claude Desktop 应用。
  2. 检查连接:在 Claude 的输入框里,你可以尝试询问:“你现在可以使用哪些工具?”或者“列出你拥有的资源。” Claude 应该会回复它从我们的 Server 发现的get_fund_momentum工具和fund://momentum/global资源。
  3. 发起数据查询:现在,你可以进行自然语言查询,例如:
    • “帮我看看 AI 领域最近比较活跃的基金。”
    • “读取全球基金动量榜单。”
    • “找出生物科技领域投资活跃度前 3 的基金。”

Claude 会理解你的意图,自动选择调用相应的 Tool 或读取 Resource,并将 Server 返回的结构化数据整合到它的回复中。你可能会看到类似“我调用get_fund_momentum工具查询了 AI 领域的基金...”的回复,后面跟着格式化的基金数据。

5.3 集成过程中的常见问题与排查

问题现象可能原因检查与解决步骤
Claude 提示“未找到 MCP 服务器”或配置未生效。1. 配置文件路径错误。
2. 配置文件格式错误(JSON 语法)。
3. Claude Desktop 未重启。
1. 确认配置文件在正确的操作系统路径下。
2. 使用 JSON 验证工具检查配置文件。
3. 彻底退出并重启 Claude Desktop。
Claude 能发现工具但调用失败,提示“连接错误”或“进程退出”。1. Server 启动命令或路径错误。
2. Server 代码存在未捕获异常,导致进程崩溃。
3. Node.js 环境问题。
1. 在终端中手动运行配置中的commandargs,看 Server 能否独立启动。
2. 检查 Server 代码的uncaughtExceptionunhandledRejection处理,并增加更详细的console.error日志。
3. 确保node在系统 PATH 中,或使用which node获取绝对路径替换command
调用工具时,Claude 返回“无效参数”或“工具执行错误”。1. 工具 Schema 定义与处理函数逻辑不一致。
2. 参数类型或枚举值不匹配。
3. Server 处理函数抛出异常。
1. 对照CallToolRequestSchema处理函数,检查参数提取和类型转换。
2. 确保工具 Schema 中的enumtype定义准确。
3. 在 Server 的处理函数中添加try-catch,并返回格式化的错误信息。
资源可以列出,但读取时内容为空或格式错误。1.ReadResourceRequestSchema处理函数未正确返回contents
2.mimeType与返回的text内容格式不匹配。
1. 检查处理函数返回值结构,确保是{ contents: [...] }
2. 确保返回的text是字符串,对于 JSON,使用JSON.stringify

6. 从原型到生产:关键考量与最佳实践

目前我们构建的是一个原型 Server,使用了模拟数据。要将其用于生产环境,为真实的 AI Agent 提供可靠的基金数据服务,需要考虑以下几个关键方面。

6.1 数据源集成与实时性

模拟数据必须替换为真实数据源。

  • 数据源选择:可以连接内部数据库、调用第三方金融数据 API(如 Crunchbase, PitchBook 的 API)、或聚合公开的募资新闻。
  • 数据更新策略
    • 定时拉取:使用node-cron等库定时从 API 拉取数据,更新内存或缓存数据库。
    • Webhook/消息队列:如果数据源支持,通过 Webhook 接收实时更新事件。
    • 增量更新:设计数据版本或时间戳,每次只获取变化部分,减少负载。
  • 数据缓存:在 Server 内存或 Redis 中缓存处理后的数据,避免每次工具调用都触发昂贵的查询。需要设置合理的缓存过期时间。
// 示例:简单的内存缓存与定时更新 import cron from 'node-cron'; let cachedFundData: FundData[] = []; async function updateFundData() { try { const response = await fetch('https://api.your-data-provider.com/vc-funds'); const data = await response.json(); // 处理数据... cachedFundData = processedData; console.error(`[${new Date().toISOString()}] Fund data updated.`); } catch (error) { console.error('Failed to update fund data:', error); } } // 每30分钟更新一次 cron.schedule('*/30 * * * *', updateFundData); // 启动时立即更新一次 updateFundData();

6.2 错误处理与健壮性

生产环境的 Server 必须具备完善的错误处理能力。

  • 输入验证:在工具处理函数中,严格校验arguments的参数,即使 Schema 已定义,Client 也可能发送非法值。
  • 外部依赖容错:数据库查询、API 调用必须放在try-catch中。对外部服务失败要有降级策略(如返回缓存旧数据、友好的错误信息)。
  • 资源清理:确保数据库连接、HTTP 代理等在 Server 生命周期结束时正确关闭。
  • 进程管理:考虑使用pm2systemd来管理 Server 进程,实现自动重启、日志轮转和监控。

6.3 安全性增强

MCP 协议本身提供了基础的安全框架,但 Server 实现者仍需注意:

  • 认证与授权:如果数据敏感,Server 需要验证 Client 的身份。MCP 支持在初始化阶段交换令牌。可以在initialize请求处理中检查params.credentials
  • 输入净化:防止注入攻击。如果工具参数用于构建数据库查询或系统命令,必须进行转义或使用参数化查询。
  • 输出过滤:返回给 AI 的数据可能包含敏感信息。确保只暴露必要的字段。
  • 速率限制:防止恶意或过度的调用拖垮 Server。可以在工具调用处理函数中加入简单的计数器或集成express-rate-limit等中间件(如果使用 HTTP 传输)。

6.4 性能优化与可观测性

  • 传输层选择:对于高频调用,stdio可能不是最高效的。可以考虑使用HTTPSSE传输层,这需要 Server 和 Client 都支持。MCP SDK 也提供了相应的 Transport 类。
  • 日志记录:使用winstonpino等日志库替代console.error,将日志分级(info, warn, error)输出到文件或日志收集系统。记录关键事件:工具调用、资源读取、错误、数据更新等。
  • 指标监控:暴露关键指标,如请求量、延迟、错误率。可以使用prom-client暴露 Prometheus 指标,或直接上报到监控系统。
  • 资源序列化优化:如果资源数据量很大,考虑使用更高效的序列化格式(如 MessagePack)或分页读取。

6.5 扩展性与架构演进

  • 多工具/多资源:随着业务增长,可以轻松添加新的 Tools 和 Resources。保持每个工具/资源的功能单一,便于维护和测试。
  • 配置化:将数据源 API 端点、缓存时间、认证密钥等抽离到环境变量或配置文件中。
  • 容器化部署:使用 Docker 将 Server 及其依赖打包,确保环境一致性,便于在 Kubernetes 或云服务器上部署和扩展。

通过以上步骤,一个为 AI Agent 提供实时基金数据的 MCP Server 就从概念变成了一个可运行、可测试、可集成的服务,并且具备了向生产环境演进的基础。这种模式可以推广到任何需要将专业领域数据或能力暴露给 LLM 的场景,是构建复杂 AI 应用的关键基础设施。