ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

基于MCP协议构建AI数据服务:以实时风险基金数据为例 在实际 AI 应用开发中让大语言模型LLM或 AI Agent 获取实时、结构化的外部数据一直是一个核心挑战。传统的做法往往需要开发者编写复杂的 API 调用代码、处理网络请求和解析响应这不仅增加了开发复杂度也使得 AI 难以灵活、动态地感知外部世界的变化。Model Context ProtocolMCP的出现为这个问题提供了一种标准化的解决方案。它定义了一套 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 架构包含三个核心角色其交互流程构成了数据流动的闭环。MCP Client客户端通常是集成了 MCP 协议的 AI 应用如 Claude Desktop、Cursor IDE 或你自己编写的 LLM 应用。Client 负责初始化连接、列出可用的工具和资源并发送执行请求。MCP Server服务器提供具体外部能力的服务端。它向 Client 宣告自己支持哪些 Tools 和 Resources并处理 Client 发来的调用请求。本文我们要构建的就是一个 Fund Momentum Data Server。JSON-RPC 2.0 协议Client 和 Server 之间通过此协议进行通信。所有请求和响应都是格式化的 JSON 消息通过标准输入输出stdio、HTTP 或 SSE 等传输层进行交换。交互的基本流程如下初始化Client 启动 Server 进程并发送initialize请求交换双方的能力信息。能力列表Client 发送tools/list和resources/list请求Server 返回其提供的所有工具和资源的元数据。工具调用当用户向 AI 提出需求如“查看最近活跃的基金”LLM 判断需要调用某个 ToolClient 就会向 Server 发送tools/call请求。结果返回Server 执行工具逻辑例如查询数据库并将结果通过tools/call响应返回给 Client。资源读取对于 ResourcesClient 可以通过resources/read请求获取其内容或者通过resources/subscribe进行订阅以获取更新。1.3 Tools 与 Resources 的区分与选型这是设计 MCP Server 时的第一个关键决策点。理解两者的区别至关重要。Tools工具代表一个动作或操作。它通常有明确的输入参数执行后会产生一个结果或副作用。例如“搜索基金”、“发送邮件”、“执行计算”。Tools 适合封装那些需要根据用户输入动态执行逻辑的场景。Resources资源代表一个静态或动态的数据实体可以通过 URI 来标识和访问。例如“fund://top10”前10基金列表、“file:///etc/config.yaml”配置文件。Resources 适合暴露结构化的数据源AI 可以像读取文件一样读取它们的内容。资源的内容可以是静态的也可以通过订阅Subscribe机制实现动态更新。对于“基金动量数据”这个场景我们可以这样设计提供一个 Toolget_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 -y2.2 安装核心依赖我们将使用modelcontextprotocol/sdk这个官方 SDK它极大地简化了 MCP Server 的开发。# 安装 MCP SDK npm install modelcontextprotocol/sdk # 安装 TypeScript 及相关类型定义推荐用于更好的开发体验 npm install --save-dev typescript types/node安装完成后你的package.json的dependencies和devDependencies应该类似这样{ 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并声明它支持tools和resources能力。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 输出便于阅读 }, ], }; });关键点解释工具 SchemainputSchema使用 JSON Schema 定义了工具的参数。enum限制了输入范围default提供了默认值。清晰的description能帮助 LLM 更好地理解如何使用这个工具。列表处理setRequestHandler用于处理ListToolsRequest当 Client 查询可用工具时返回我们定义的工具列表。调用处理setRequestHandler用于处理CallToolRequest。我们首先验证工具名然后从request.params.arguments中提取参数。核心业务逻辑此处为模拟数据过滤在此执行。返回格式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), }, ], }; });关键点解释URI 设计fund://momentum/global是一个自定义的 URI 方案。你可以设计自己的命名空间如vcdata://funds/top。URI 应具有唯一性和描述性。MIME 类型mimeType告诉 Client 如何解析内容。application/json是最通用的选择。列表与读取与 Tools 类似需要分别处理ListResourcesRequest和ReadResourceRequest。内容返回资源内容通过contents数组返回每个元素包含uri、mimeType和text或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现在启动 Servernpm 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 ServerClaude Desktop 允许通过配置文件添加本地 MCP Server。找到 Claude Desktop 配置目录macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.jsonLinux:~/.config/Claude/claude_desktop_config.json编辑配置文件如果文件不存在则创建它。添加以下内容{ 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 验证集成并开始对话重启 Claude Desktop修改配置后完全退出并重新启动 Claude Desktop 应用。检查连接在 Claude 的输入框里你可以尝试询问“你现在可以使用哪些工具”或者“列出你拥有的资源。” Claude 应该会回复它从我们的 Server 发现的get_fund_momentum工具和fund://momentum/global资源。发起数据查询现在你可以进行自然语言查询例如“帮我看看 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. 在终端中手动运行配置中的command和args看 Server 能否独立启动。2. 检查 Server 代码的uncaughtException和unhandledRejection处理并增加更详细的console.error日志。3. 确保node在系统 PATH 中或使用which node获取绝对路径替换command。调用工具时Claude 返回“无效参数”或“工具执行错误”。1. 工具 Schema 定义与处理函数逻辑不一致。2. 参数类型或枚举值不匹配。3. Server 处理函数抛出异常。1. 对照CallToolRequestSchema处理函数检查参数提取和类型转换。2. 确保工具 Schema 中的enum、type定义准确。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 生命周期结束时正确关闭。进程管理考虑使用pm2或systemd来管理 Server 进程实现自动重启、日志轮转和监控。6.3 安全性增强MCP 协议本身提供了基础的安全框架但 Server 实现者仍需注意认证与授权如果数据敏感Server 需要验证 Client 的身份。MCP 支持在初始化阶段交换令牌。可以在initialize请求处理中检查params.credentials。输入净化防止注入攻击。如果工具参数用于构建数据库查询或系统命令必须进行转义或使用参数化查询。输出过滤返回给 AI 的数据可能包含敏感信息。确保只暴露必要的字段。速率限制防止恶意或过度的调用拖垮 Server。可以在工具调用处理函数中加入简单的计数器或集成express-rate-limit等中间件如果使用 HTTP 传输。6.4 性能优化与可观测性传输层选择对于高频调用stdio可能不是最高效的。可以考虑使用HTTP或SSE传输层这需要 Server 和 Client 都支持。MCP SDK 也提供了相应的 Transport 类。日志记录使用winston或pino等日志库替代console.error将日志分级info, warn, error输出到文件或日志收集系统。记录关键事件工具调用、资源读取、错误、数据更新等。指标监控暴露关键指标如请求量、延迟、错误率。可以使用prom-client暴露 Prometheus 指标或直接上报到监控系统。资源序列化优化如果资源数据量很大考虑使用更高效的序列化格式如 MessagePack或分页读取。6.5 扩展性与架构演进多工具/多资源随着业务增长可以轻松添加新的 Tools 和 Resources。保持每个工具/资源的功能单一便于维护和测试。配置化将数据源 API 端点、缓存时间、认证密钥等抽离到环境变量或配置文件中。容器化部署使用 Docker 将 Server 及其依赖打包确保环境一致性便于在 Kubernetes 或云服务器上部署和扩展。通过以上步骤一个为 AI Agent 提供实时基金数据的 MCP Server 就从概念变成了一个可运行、可测试、可集成的服务并且具备了向生产环境演进的基础。这种模式可以推广到任何需要将专业领域数据或能力暴露给 LLM 的场景是构建复杂 AI 应用的关键基础设施。
返回列表