1. 项目概述一个为AI与区块链交互而生的共享工具库如果你正在尝试让Claude、Cursor这类AI助手直接与区块链网络对话或者你正在构建一个需要集成多种区块链数据的AI应用那么你很可能已经听说过Model Context Protocol。而今天要拆解的这个cryptoapis-io/mcp-shared正是这个生态中一个至关重要的“基础设施”包。简单来说它不是一个独立运行的应用而是一个被设计用来被其他工具“复用”的共享代码库专门为那些基于Crypto APIs服务来构建MCP服务器的开发者服务。想象一下你要为团队开发一套内部工具让AI能查询以太坊上的交易、获取比特币的区块信息或者监控Polygon上的智能合约事件。如果每个工具都从头开始写HTTP请求、处理API密钥、定义错误类型那将是巨大的重复劳动。mcp-shared的核心价值就在于它把这些脏活累活都打包好了。它提供了经过实战检验的HTTP客户端、统一的错误处理机制、完整的区块链网络常量定义以及符合MCP规范的工具成本计算函数。这意味着当你基于它来开发一个具体的MCP服务器比如专门查以太坊余额的mcp-ethereum时你可以把全部精力集中在业务逻辑上而不是反复调试网络请求或纠结于该用chainId还是network来标识一条链。这个包背后对应的Crypto APIs服务版本是2024-12-12这是一个重要的信息因为它意味着库中定义的网络列表、接口路径等都与该特定版本的API保持兼容。对于使用者而言最大的好处是“开箱即用”和“一致性”。所有基于这个共享库构建的MCP服务器都会以相同的方式处理认证、分页和错误给最终用户带来统一、稳定的体验。无论你是想快速验证一个区块链AI集成的想法还是计划开发一套企业级的链上数据查询工具链理解并善用这个共享库都能让你的开发效率提升好几个档次。2. 核心模块深度解析与设计哲学2.1 HTTP客户端不仅仅是封装fetchCryptoApisHttpClient是这个库的基石。表面上看它只是一个封装了fetch或axios的HTTP客户端但它的设计远不止于此。首先它内置了完整的认证流程。Crypto APIs通常使用API Key进行认证密钥一般放在请求头中。这个客户端会自动从配置中加载密钥并以正确格式如X-API-Key附加到每一个发出的请求上。这避免了开发者在每个API调用处手动添加头信息的繁琐和可能出现的遗漏。更重要的是它统一了错误处理。区块链API调用失败的原因五花八门网络超时、API密钥无效、请求频率超限、查询的地址不存在等等。一个粗糙的客户端可能只是把原始的HTTP错误抛给上层迫使每个使用它的工具都要写一遍复杂的错误解析逻辑。而CryptoApisHttpClient会将服务端返回的错误响应统一封装成CryptoApisError对象。这个对象至少包含code错误码、statusHTTP状态码和details错误详情三个字段。例如当查询一个不存在的交易哈希时你可能会收到一个结构化的错误对象而不是一段难以解析的HTML或纯文本。这种设计让上层工具能够以一致的方式向AI助手或最终用户报告错误比如“未找到该交易哈希错误码404”。此外这个客户端很可能还内置了重试逻辑和请求限流的基础设施。对于区块链API来说偶尔的网络波动或节点暂时不可用是很常见的。一个健壮的客户端应该具备在遇到可重试错误如5xx服务器错误或网络超时时自动重试几次的能力。同时为了遵守Crypto APIs的速率限制客户端可能需要实现一个简单的令牌桶算法防止突发的大量请求导致整个API Key被临时封禁。虽然源码中没有明说但一个成熟的共享库必然会考虑这些生产环境下的细节。2.2 区块链常量构建清晰的网络地图BLOCKCHAIN_NETWORKS、EVM_BLOCKCHAINS、UTXO_BLOCKCHAINS这些常量对象构成了这个库的“世界观”。它们不仅仅是一堆字符串数组更是一份精心维护的、机器可读的区块链网络清单。这对于MCP服务器至关重要因为AI助手需要明确知道它能和哪些“链”对话。EVM_BLOCKCHAINS很可能列出了所有支持的以太坊虚拟机兼容链如ethereum、polygon、arbitrum、optimism、base等。而UTXO_BLOCKCHAINS则对应比特币及其分叉链如bitcoin、bitcoin-cash、litecoin等。BLOCKCHAIN_NETWORKS可能是一个更全面的映射将区块链名称映射到其支持的网络列表例如{ ethereum: [mainnet, goerli, sepolia], polygon: [mainnet, mumbai] }。这些常量与Zod模式EvmBlockchainSchema,UtxoBlockchainSchema是紧密配合的。Zod是一个TypeScript模式验证库。这些模式定义了“一个合法的EVM区块链标识符应该是什么样子”。当MCP服务器收到一个来自AI的请求比如“查询以太坊主网余额”它可以用EvmBlockchainSchema来验证“ethereum”这个字符串是否在预定义的合法列表中。这相当于在数据流入系统的最外层加了一道防火墙防止因拼写错误或输入了不支持的链而导致的底层API调用失败。这种“常量定义模式验证”的组合是构建鲁棒性系统的经典模式。2.3 配置管理灵活性与安全性的平衡loadSharedConfig()函数是配置的入口。它通常会从多个来源读取配置并合并成一个最终配置对象优先级顺序往往是环境变量 配置文件 默认值。一个典型的配置可能包括API_KEY: 你的Crypto APIs主密钥。API_BASE_URL: API服务的基地址可能用于指向不同的环境如开发、生产。REQUEST_TIMEOUT: 请求超时时间。MAX_RETRIES: 最大重试次数。而runWithApiKey()函数则体现了对灵活性和安全性的高级考量。在复杂的AI-Agent工作流中一个MCP服务器可能同时为多个用户或组织服务每个用户可能都有自己的Crypto APIs订阅和API Key。如果所有请求都使用服务器全局配置中的一个密钥不仅无法区分用量还存在密钥泄露的风险。runWithApiKey()允许在单次请求或一个短暂的上下文环境中临时使用一个特定的API Key。其内部实现可能利用了类似AsyncLocalStorage的机制在当前异步调用链中注入一个临时的配置覆盖。这样上层工具在处理来自不同用户的请求时可以动态地传入对应用户的API Key而共享的CryptoApisHttpClient会智能地使用这个临时密钥去发起请求。这为构建多租户的、安全的MCP服务提供了基础能力。2.4 工具成本与确认流程MCP生态的“货币”与“保险”formatCreditsForDescription()和相关的信用类型定义是MCP协议中“工具成本”元数据的具体实现。在MCP模型中AI模型如Claude调用一个工具如查询区块链余额是需要消耗“信用”的这类似于云计算中的API调用费用。这个函数的作用就是将一次API调用预计消耗的信用点数格式化成一段人类和AI都能理解的描述例如“消耗 5 信用点”。这段描述会被嵌入到MCP服务器向AI模型注册工具时的元数据中。AI模型在决定是否调用该工具前可以评估成本这对于控制使用开销非常重要。Confirmation确认流程模块则是为“危险操作”设计的“二次确认”机制。在区块链上下文中某些操作一旦执行就无法撤回或者会消耗大量资源。例如一个“同步所有交易”的工具可能会触发成千上万的API调用迅速耗尽信用额度一个“删除监控地址”的工具则会导致数据丢失。对于这类工具MCP协议支持一个确认流程。这个模块很可能实现了一个基于令牌的确认流。当AI尝试调用一个危险工具时服务器不会立即执行而是先返回一个特殊的“确认请求”响应其中包含一个唯一的确认令牌和对操作风险的描述。AI需要将这个令牌通常需要用户明确授权再次发送给服务器服务器验证令牌有效后才会真正执行操作。这相当于在AI和 irreversible action 之间加了一道需要人工或二次确认的保险丝极大地增强了系统的安全性。3. 实战应用从零构建一个自定义MCP服务器虽然mcp-shared主要被其他官方mcp-*包使用但理解如何直接用它来构建一个自定义MCP服务器能让你彻底掌握其精髓。假设我们要构建一个简单的MCP服务器提供一个工具来获取任意EVM链上指定区块的详细信息。3.1 项目初始化与依赖安装首先创建一个新的Node.js项目目录并初始化。mkdir mcp-custom-block-explorer cd mcp-custom-block-explorer npm init -y接下来安装核心依赖。我们需要cryptoapis-io/mcp-shared来获得所有基础设施还需要modelcontextprotocol/sdk这是实现MCP服务器协议的官方SDK。同时我们使用TypeScript以获得更好的类型安全。npm install cryptoapis-io/mcp-shared modelcontextprotocol/sdk npm install -D typescript tsx types/node npx tsc --init在tsconfig.json中确保设置module: ESNext和target: ES2022以使用现代JavaScript特性。3.2 构建核心工具获取区块信息现在我们创建服务器的核心文件src/server.ts。首先导入必要的模块。import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { CryptoApisHttpClient, loadSharedConfig, EVM_BLOCKCHAINS, EvmBlockchainSchema, formatCreditsForDescription, CryptoApisError } from cryptoapis-io/mcp-shared;接下来初始化MCP服务器和我们的共享配置与HTTP客户端。// 初始化MCP服务器 const server new Server( { name: custom-block-explorer, version: 0.1.0, }, { capabilities: { tools: {}, // 我们将在这里注册工具 }, } ); // 加载配置并创建HTTP客户端 // 默认会从环境变量CRYPTOAPIS_API_KEY读取密钥 const config loadSharedConfig(); const client new CryptoApisHttpClient(config);现在定义并注册我们的核心工具get_block_info。这个工具需要两个参数区块链标识符和区块号或哈希。server.setRequestHandler(tools/list, async () { return { tools: [ { name: get_block_info, description: 获取指定EVM区块链上某个区块的详细信息。支持以下链${EVM_BLOCKCHAINS.join(, )}。 ${formatCreditsForDescription(3)}, // 假设调用一次消耗3信用点 inputSchema: { type: object, properties: { blockchain: { type: string, description: EVM区块链标识符例如 ethereum, polygon, arbitrum., enum: EVM_BLOCKCHAINS // 使用共享常量限制可选值 }, blockIdentifier: { type: string, description: 区块号十进制数字或区块哈希0x开头。, }, }, required: [blockchain, blockIdentifier], }, }, ], }; });最关键的部分是工具的执行逻辑。我们使用CryptoApisHttpClient来调用Crypto APIs的相应接口。server.setRequestHandler(tools/call, async (request) { if (request.params.name ! get_block_info) { throw new Error(未知工具: ${request.params.name}); } const args request.params.arguments as { blockchain: string; blockIdentifier: string; }; // 1. 输入验证使用共享库的Zod模式验证区块链参数 const validationResult EvmBlockchainSchema.safeParse(args.blockchain); if (!validationResult.success) { return { content: [{ type: text, text: 错误不支持的区块链 ${args.blockchain}。请使用以下之一${EVM_BLOCKCHAINS.join(, )}。 }] }; } const blockchain validationResult.data; // 假设我们只查询主网实际可根据需要扩展 const network mainnet; // 2. 构建API路径并发起请求 const apiPath /blockchain-data/${blockchain}/${network}/blocks/${args.blockIdentifier}; try { const response await client.request(GET, apiPath); const blockData response.data; // 假设客户端返回的数据在.data字段 // 3. 格式化响应提取关键信息给AI const formattedText 区块查询成功 - 区块链: ${blockchain} ${network} - 区块号: ${blockData.blockNumber || blockData.number} - 哈希: ${blockData.hash} - 时间戳: ${new Date(blockData.timestamp * 1000).toISOString()} - 交易数量: ${blockData.transactions?.length || 0} - 矿工: ${blockData.miner} - 难度: ${blockData.difficulty} - 总难度: ${blockData.totalDifficulty} - 大小: ${blockData.size} bytes - 燃料限制: ${blockData.gasLimit} - 已用燃料: ${blockData.gasUsed} .trim(); return { content: [{ type: text, text: formattedText }] }; } catch (error) { // 4. 统一的错误处理 let errorMessage 查询区块信息时发生未知错误。; if (error instanceof CryptoApisError) { errorMessage Crypto APIs 错误 (${error.code}): ${error.details || error.message}; } else if (error instanceof Error) { errorMessage 请求失败: ${error.message}; } return { content: [{ type: text, text: errorMessage }], isError: true }; } });最后启动服务器使用标准输入输出作为传输层这是与Claude Desktop、Cursor等客户端通信的标准方式。async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(Custom Block Explorer MCP server running on stdio...); } main().catch((error) { console.error(Server fatal error:, error); process.exit(1); });3.3 配置与运行在运行前需要设置环境变量。创建一个.env文件确保.gitignore它CRYPTOAPIS_API_KEYyour_api_key_here然后在package.json中添加启动脚本{ scripts: { start: node --loader tsx src/server.ts } }现在你可以使用npm start来运行服务器。为了在Claude Desktop中使用它你需要在Claude的MCP配置文件中添加这个服务器。配置方式因客户端而异通常需要指定服务器启动命令的路径。通过这个实战例子你可以看到mcp-shared如何将HTTP调用、错误处理、输入验证这些通用且复杂的部分抽象掉让我们能专注于工具本身的业务逻辑构建API路径、解析和格式化响应。这正是共享库的价值所在。4. 高级特性与最佳实践剖析4.1 分页模式游标与偏移量的抉择在处理区块链数据时分页是绕不开的话题。一次查询可能返回成千上万笔交易或日志不可能一次性全部拉取。mcp-shared通过CursorPaginationSchema和OffsetPaginationSchema提供了两种主流分页模式的支持这背后是两种不同的设计哲学。游标分页通常用于数据持续、频繁更新的场景比如区块链交易。游标是一个指向数据集中特定位置的标记通常是一个时间戳、ID或哈希。当你请求下一页时你提供上一页返回的游标服务器则返回该游标之后的数据。它的最大优点是稳定性在数据不断新增的过程中使用游标分页不会出现“重复”或“遗漏”记录。例如你基于区块高度1000的游标获取了10条交易在此期间即使新区块1001产生了你下次用游标1010请求时仍然能准确拿到1000之后的下一页不会受到新增数据的干扰。Crypto APIs的许多列表接口如获取地址交易很可能采用这种方式。偏移量分页则更传统通过limit和offset参数来指定“跳过前N条取M条”。它简单直观适用于数据相对静态或总量不大的查询。但其缺点是在数据增删时页面内容可能会“漂移”。例如你第一次请求offset0, limit10在请求第二页offset10, limit10之前如果最前面新增了一条数据那么你第二页得到的结果实际上会包含原第一页的最后一条并丢失原第二页的最后一条。最佳实践提示在构建MCP工具时如果底层API支持游标分页应优先使用它尤其是对于交易、事件这类实时性强的数据。mcp-shared提供的模式可以帮助你验证和生成正确的分页参数。你应该在工具描述中明确说明该工具使用的分页方式并设计清晰的参数如nextPageToken或page和pageSize让AI助手能够理解如何遍历所有数据。4.2 系统信息工具服务的“自述文件”system_info工具是一个巧妙的设计。它不是一个面向终端用户查询数据的工具而是一个面向AI助手或管理员的“元工具”。当AI助手如Claude首次连接到一个MCP服务器时它可以调用这个内置工具来了解这个服务器能做什么。这个工具返回的信息可能包括支持的区块链和网络直接引用BLOCKCHAIN_NETWORKS常量列出所有可用的选项。信用成本说明解释本服务器中各个工具的信用消耗模型可能是引用formatCreditsForDescription的示例。API文档链接指向Crypto APIs官方文档的URL供需要深入了解的开发者或AI参考。服务器版本与状态例如基于的mcp-shared版本、服务器运行时间等。实现这样一个工具非常简单但它极大地提升了服务器的可发现性和可调试性。对于AI来说这就像读取一个驱动程序的INF文件对于开发者来说这是快速验证服务器是否正常工作的好方法。在你的自定义服务器中强烈建议也实现一个类似的工具哪怕只是返回一个简单的支持功能列表。4.3 环境隔离与多密钥管理策略在2.3中我们提到了runWithApiKey。在实际生产部署中这引出了一个重要的架构模式环境隔离。你可能同时有开发、测试、生产三个环境它们对应不同的Crypto APIs项目甚至不同的套餐等级自然需要使用不同的API Key。一种推荐的做法是利用环境变量来区分。你的loadSharedConfig()可以增强逻辑根据NODE_ENV环境变量加载不同的配置文件或环境变量前缀。例如NODE_ENVproduction- 使用CRYPTOAPIS_API_KEY。NODE_ENVdevelopment- 使用CRYPTOAPIS_DEV_API_KEY。NODE_ENVtest- 使用CRYPTOAPIS_TEST_API_KEY。对于多租户场景一个服务器实例服务多个客户runWithApiKey是核心。你需要建立一个租户信息与API Key的映射关系可以来自数据库或配置中心。当收到一个请求时先从请求上下文中识别租户例如通过HTTP头中的API Token然后查表得到对应的Crypto APIs Key最后用runWithApiKey来执行本次调用。安全警告绝对不要将不同租户的API Key硬编码在代码中或通过不安全的通道传递。映射关系应存储在安全的、有访问控制的后端服务中。MCP服务器本身应该通过一个安全的令牌如JWT来认证租户请求然后由服务器后端去获取对应的Crypto APIs Key。永远遵循“最小权限原则”每个租户的Key最好只拥有其所需数据的最小访问权限。4.4 错误处理与用户友好反馈虽然CryptoApisError提供了结构化的错误信息但直接将这些技术细节抛给AI或最终用户并不友好。AI可能无法理解ERR_RATE_LIMIT或ERR_INVALID_ADDRESS的具体含义。因此在MCP服务器的工具调用层你需要进行第二层的错误翻译和丰富。例如将ERR_RATE_LIMIT转换为更易懂的描述“请求过于频繁请稍后再试。每个API密钥每分钟最多允许X次请求。”将ERR_INVALID_ADDRESS与具体的输入值一起返回“您提供的地址 ‘0x123…’ 格式不正确请检查是否为有效的以太坊地址。”对于网络超时错误可以提示“区块链节点响应超时这可能是网络拥堵导致的建议重试一次。”此外可以设计一个错误分类机制将错误分为“用户输入错误”、“服务器端错误”、“临时性错误”等。对于“临时性错误”如超时、5xx错误MCP服务器可以在返回错误信息的同时建议AI助手是否自动重试。这种精细化的错误处理能极大提升AI使用工具的体验和成功率。5. 常见问题、性能优化与避坑指南5.1 依赖管理与版本锁定由于cryptoapis-io/mcp-shared是一个底层共享库它的版本升级可能会引入不兼容的更改。而你的MCP服务器以及可能依赖它的其他工具都需要保持稳定。问题直接使用npm install cryptoapis-io/mcp-shared会安装最新版本可能导致某次部署后因为共享库的更新而出现意外行为。解决方案在package.json中精确锁定版本号。不要使用模糊的版本范围如^1.0.0而应该使用精确版本如1.2.3。定期有意识地更新依赖并在测试环境中充分验证后再部署到生产环境。考虑使用npm shrinkwrap或package-lock.json来确保整个依赖树的一致性。5.2 处理API速率限制与配额Crypto APIs的所有套餐都有速率限制和月度信用点配额。不加控制地调用很容易触发限流或快速耗尽配额。问题AI助手可能在短时间内发起大量重复或类似的请求例如循环查询多个地址的余额。优化策略缓存对于不经常变化或允许短暂延迟的数据实施缓存。例如一个区块的信息在确认后是 immutable 的可以缓存很长时间。一个地址的余额虽然会变但可以缓存5-10秒。使用内存缓存如node-cache或Redis。请求合并如果AI要查询10个地址的余额而Crypto APIs恰好有批量查询接口那么应该设计一个get_multiple_balances工具将10次请求合并为1次大幅减少调用次数。信用点预算提示在工具描述中利用formatCreditsForDescription()清晰标明每次调用的成本。甚至可以设计一个get_remaining_credits工具让AI能主动查询当前剩余配额从而调整其行为。在MCP服务器层实现限流即使单个工具调用成本低也要防止被滥用。可以使用express-rate-limit类似的中间件思想在服务器入口处对每个客户端IP或API Key进行频率限制。5.3 网络与超时处理区块链RPC调用本身具有不确定性受网络状况和节点负载影响很大。问题默认的HTTP超时设置可能不适合区块链查询一个复杂的trace_filter请求可能需要几十秒。配置建议通过loadSharedConfig()加载的配置应该允许自定义超时时间。为不同类型的操作设置不同的超时简单查询余额、区块号5-10秒。复杂查询交易追踪、事件日志过滤30-60秒。广播交易15-30秒需要等待节点接收。在代码中对于可能长时间运行的操作要使用try...catch妥善处理超时错误并返回明确的提示如“查询超时目标区块链节点可能正忙请简化查询条件或稍后重试”。5.4 数据类型与精度陷阱区块链数据中充满了大整数BigInt例如Wei单位的余额、Gas价格等。JavaScript的普通Number类型无法安全地表示这些大数会导致精度丢失。问题直接从API响应中拿到一个表示ETH余额的字符串12345678901234567890单位Wei如果直接用Number()转换会变成科学计数法或失去精度。解决方案CryptoApisHttpClient在返回数据前可能已经对数字字符串进行了处理。但为了安全起见在你的MCP服务器工具逻辑中对于任何表示金额、数量的大数字都应该将其视为字符串处理直到需要展示或计算。如果需要计算务必使用专门的大数库如BigIntES2020原生支持或bignumber.js。在向AI返回文本结果时记得将Wei转换为可读的ETH单位除以1e18并格式化显示。5.5 日志记录与监控一个运行在生产环境的MCP服务器必须有完善的日志记录。要记录什么INFO级别工具被调用记录工具名、参数哈希或脱敏后的参数、调用成功。WARN级别遇到可恢复的错误如输入验证失败、API速率限制警告。ERROR级别遇到不可恢复的错误如网络中断、API密钥失效、意外的程序异常。DEBUG级别详细的请求/响应数据注意不要记录敏感信息如私钥、完整的API密钥。工具使用结构化的日志库如winston或pino。它们支持将日志输出为JSON格式方便后续被日志收集系统如ELK Stack、Loki抓取和分析。监控关键指标如各工具调用频率、平均响应时间、错误率、信用点消耗速度。这些指标能帮助你了解AI的使用模式优化工具设计并在出现问题时快速定位。5.6 安全性加固最后也是最重要的是安全。输入验证永远不要信任来自AI助手的输入。即使有前端的模式验证服务器端也必须用EvmBlockchainSchema这样的Zod模式重新验证。防止SQL注入、命令注入等攻击虽然MCP协议本身限制较大但仍需警惕。API密钥保护确保CRYPTOAPIS_API_KEY等环境变量不会通过日志、错误信息或任何API响应泄露出去。权限最小化你为MCP服务器申请的Crypto APIs密钥应该只授予它完成功能所必需的最小权限。如果只是查询数据就不要赋予它发送交易的权限。依赖安全定期使用npm audit或yarn audit检查依赖库的安全漏洞并及时更新。构建一个基于cryptoapis-io/mcp-shared的MCP服务器就像组装一台精密的仪器。共享库提供了高质量的标准化零件HTTP客户端、常量、模式而你的任务是理解这些零件的特性按照最佳实践将它们组装起来并处理好仪器运行时的所有边界情况——网络、错误、安全、性能。当你把这些都考虑周全你构建的就不再只是一个简单的数据查询工具而是一个可靠、高效、安全的区块链AI网关。