使用Claude AI创建一个EVM MCP服务器

本文介绍了如何使用Model Context Protocol(MCP)构建一个EVM MCP服务器,使大型语言模型(LLM)能够与多个EVM兼容区块链进行交互。该服务器允许LLM访问链上数据,从而为Web3自动化和分析开辟了新的可能性。文章详细说明了服务器的搭建步骤、关键代码,以及如何配置Claude桌面应用来测试该服务器,最后探讨了未来扩展EVM功能、构建AI特定增强功能的方向。

概述

模型上下文协议 (MCP) 使得大型语言模型 (LLM) 能够使用标准化的基于消息的协议与外部工具(如 HTTP API、文件,甚至区块链)进行交互。可以将其视为 Claude 或 Cursor 等 agent 可以接入的“函数调用”接口,从而将脚本或服务转变为 AI 原生扩展。

在本指南中,你将学习如何构建和部署一个 MCP 服务器,该服务器使 LLM agent 能够跨多个 EVM 兼容网络访问区块链数据。这种强大的集成使 Claude 等 AI 模型能够直接与区块链数据交互,从而为 Web3 自动化和分析开辟新的可能性。

你将做什么

  • 构建一个与多个 EVM 链交互的 MCP 服务器
  • 使 LLM 能够使用 Viem 和 QuickNode 的 多链 RPC 获取链上数据
  • 注册工具、提示和资源以指导 LLM 的行为
  • Claude Desktop 中运行服务器并使用自然语言请求进行测试

你需要的

什么是 MCP?

模型上下文协议(MCP)是一个开放标准,允许 AI agent 与外部工具和数据源进行交互。它在语言模型和工具之间创建了一个结构化的通信通道。

如果没有 MCP,AI 模型将仅限于:

  • 它们接受过训练的信息
  • 它们当前正在进行的对话
  • 无法检查外部数据源
  • 无法在现实世界中执行操作

MCP 通过创建 AI 访问外部工具和数据的标准化方式来弥补这一差距。

High Level Architecture来源(针对我们正在构建的服务器进行了修改):Model Context Protocol

关键概念

MCP 服务器如何工作

MCP 服务器负责处理来自客户端的传入请求并返回适当的数据。它们使用 MCP 协议与 LLM 通信,并且可以使用任何编程语言或框架来实现。

每个服务器都通过标准通道(stdio、HTTP 或 sockets)与 LLM 通信,并返回结构良好的输出。这允许 LLM 理解请求的上下文和意图,并做出适当的响应。MCP 协议被设计为可扩展的,允许开发人员根据需要添加新的工具、资源和提示。

工具:AI 可以调用以执行特定操作的函数。 它们可以是简单的 API 调用,也可以是复杂的任务。

  • 示例:eth_getBalance 用于检查钱包的余额
  • 示例:weather_forecast 用于获取当前天气数据

资源:AI 可以引用的静态知识。 这些会影响模型的依据和假设——例如,添加 gas-reference 资源可以帮助 Claude 了解特定链的 gas 价格是低、中等还是高,而无需调用任何工具。

  • 示例:有关特定链上 gas 价格的详细信息
  • 示例:有关 API 参数的文档

提示:指导 AI 的预先编写的指令。 它们就像可重复使用的思考模板。 每个提示定义了 LLM 应如何使用工具和结构化推理来处理任务(例如,“分析此钱包”)。

  • 示例:用于分析钱包活动的模板
  • 示例:用于执行复杂任务的逐步指南

当你将 MCP 服务器连接到像 Claude 这样的 AI 时:

  1. AI 发现有哪些工具、资源和提示可用
  2. 在需要时,AI 可以使用特定参数调用这些工具
  3. 服务器处理这些请求并返回结构化数据
  4. AI 解释结果并将其纳入其响应中

以下是相同用户问题在使用和不使用 MCP 服务器的情况下的表现:

简单示例:检查钱包余额

没有 MCP 有 MCP
用户 “钱包 0x123... 的余额是多少?” “钱包 0x123... 的余额是多少?”
AI 进程 无法访问外部数据 [通过 MCP 调用 eth_getBalance 工具]
AI 响应 “我无法访问当前的区块链数据,因此无法检查该钱包的余额。” “该钱包目前在以太坊上持有 0.45 ETH。”
结果 ❌ 无法实现用户请求 ✅ 提供实时区块链数据

MCP 服务器中的请求生命周期

以下是 agent 通过提示发出请求时发生的情况:

  1. 解析请求以确定操作和参数
  2. 验证参数
  3. 选择适当的链客户端
  4. 通过 Viem 到 QuickNode 执行请求
  5. 格式化响应并将其返回给 agent

MCP Server Flow

我们了解了 MCP 服务器的工作原理。 现在,让我们构建一个!

项目结构

项目结构如下:

evm-mcp-server/
├── index.ts              # 入口点,设置 MCP 服务器
├── chains.ts             # 链配置 + QuickNode 端点映射
├── clients.ts            # Viem public client 创建器
├── package.json          # 依赖项和脚本
├── prompts.ts            # LLM 提示定义
├── resources.ts          # 外部参考(gas 价格,浏览器)
├── tools.ts              # MCP 工具:getCode,getBalance,gasPrice
└── tsconfig.json         # TypeScript 配置

首先,让我们看一下每个文件及其用途。 但是,如果你想直接跳转到代码,请随时跳到 构建你的 EVM MCP 服务器 部分。

项目代码

本指南的所有代码都可以在 QuickNode GitHub 存储库 中找到。 我们在这里通过提供代码的高级概述来解释事物的工作原理,但是我们将在 构建你的 MCP 服务器 部分中使用 GitHub 存储库。

入口点:index.ts

此文件是初始化并启动 MCP 服务器的入口点。 它注册工具、提示和资源:

index.ts 文件的一部分

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { registerTools } from './tools'
import { registerPrompts } from './prompts'
import { registerResources } from './resources'

async function main() {
    try {
        // 创建 MCP 服务器
        const server = new McpServer({
            name: 'EVM MCP Server',
            version: '0.1.0',
            description: 'A server for LLM agent to access EVM blockchain data', // 用于LLM agent 访问 EVM 区块链数据的服务器
        })

        // 注册所有工具、提示和资源
        registerTools(server)
        registerPrompts(server)
        registerResources(server)

        // 启动 MCP 服务器
        const transport = new StdioServerTransport()
        await server.connect(transport)
    } catch (error) {
        console.error('❌ Failed to start server:', error) //  启动服务器失败
        process.exit(1)
    }
}

// 运行 main 函数
main().catch(error => {
    console.error('❌ Unhandled error:', error) // 未处理的错误
    process.exit(1)
})

链配置:chains.ts

此文件定义区块链配置,并基于 QuickNode 的多链格式构建 RPC URL。

chains.ts 文件的一部分

// 从环境变量获取端点名称和Token ID
const QN_ENDPOINT_NAME = validateEnvVar('QN_ENDPOINT_NAME')
const QN_TOKEN_ID = validateEnvVar('QN_TOKEN_ID')

// 用于基于网络名称构建 QuickNode RPC URL 的函数
const buildRpcUrl = (networkName: string): string => {
    // 以太坊主网的特殊情况
    if (networkName === 'mainnet') {
        return `https://${QN_ENDPOINT_NAME}.quiknode.pro/${QN_TOKEN_ID}/`
    }

    // Avalanche 主网的特殊情况
    if (networkName === 'avalanche-mainnet') {
        return `https://${QN_ENDPOINT_NAME}.${networkName}.quiknode.pro/${QN_TOKEN_ID}/ext/bc/C/rpc`
    }

    // 对于其他网络,在 URL 中包含网络名称
    return `https://${QN_ENDPOINT_NAME}.${networkName}.quiknode.pro/${QN_TOKEN_ID}/`
}

export const CHAINS = {
    ethereum: {
        network: 'mainnet',
        rpc: buildRpcUrl('mainnet'),
        name: 'Ethereum',
        symbol: 'ETH',
        decimals: 18,
    },
    // 其他链...
}

// 代码的其余部分...

客户端创建器:clients.ts

此文件使用 Viem 库中的 createPublicClient 函数为每个链创建客户端,以使用 QuickNode RPC 端点建立与链的连接。

clients.ts 文件的一部分

import { createPublicClient, http } from 'viem'
import { ChainId, getChain } from './chains'

// viem 客户端的缓存,以避免创建重复的客户端
const clientCache = new Map<ChainId, ReturnType<typeof createPublicClient>>()

export const getPublicClient = (chainId: ChainId) => {
    // 如果存在则从缓存返回
    if (clientCache.has(chainId)) {
        return clientCache.get(chainId)!
    }

    // 获取链配置
    const chain = getChain(chainId)

    // 创建新的 public client
    const client = createPublicClient({
        transport: http(chain.rpc),
    })

    // 缓存以供将来使用
    clientCache.set(chainId, client)

    return client
}

提示:prompts.ts

此文件定义 LLM agent 要使用的提示。 提示对象包含 descriptionmessages 属性,MCP 服务器使用这些属性来生成提示。 提示包括用于调用工具、解释结果和格式化响应的说明。

prompts.ts 文件的一部分

// 注册 check-wallet 提示
server.prompt(
    'check-wallet',
    checkWalletSchema.shape,
    ({ address, chain }: { address: string; chain: string }) => ({
        description: "Guide for analyzing a wallet's balance and context", // 用于分析钱包余额和上下文的指南
        messages: [
            {
                role: 'user',
                content: {
                    type: 'text',
                    text: `Please analyze this Ethereum wallet address: ${address} on ${chain} chain.

You need to analyze a wallet address on an EVM blockchain.

First, use the eth_getBalance tool to check the wallet's balance.
Next, use the eth_getCode tool to verify if it's a regular wallet or a contract.

Once you have this information, provide a summary of:
1. The wallet's address
2. The chain it's on
3. Its balance in the native token
4. Whether it's a regular wallet (EOA) or a contract
5. Any relevant observations about the balance (e.g., if it's empty, has significant funds, etc.)

Aim to be concise but informative in your analysis.`, // 请分析以太坊钱包地址:${address} 在 ${chain} 链上。 你需要在 EVM 区块链上分析钱包地址。 首先,使用 eth_getBalance 工具检查钱包的余额。 接下来,使用 eth_getCode 工具验证它是否是常规钱包或合约。 获得此信息后,请提供以下摘要: 1. 钱包的地址 2. 它所在的链 3. 其在本机Token中的余额 4. 它是常规钱包 (EOA) 还是合约 5. 关于余额的任何相关观察(例如,如果它是空的、有大量资金等) 旨在在你的分析中保持简洁但内容丰富。
                },
            },
        ],
    })
)

// check-wallet 提示的 Schema
const checkWalletSchema = z.object({
    address: z.string().refine(isAddress, {
        message: 'Invalid Ethereum address format', // 以太坊地址格式无效
    }),
    chain: z
        .string()
        .refine((val): val is ChainId => Object.keys(CHAINS).includes(val), {
            message:
                'Unsupported chain. Use one of: ethereum, base, arbitrum, avalanche, bsc', // 不支持的链。 请使用以下链之一:ethereum、base、arbitrum、avalanche、bsc
        }),
})

// 代码的其余部分...

资源:resources.ts

此文件定义可由 LLM agent 使用的外部引用。 在这种情况下,我们让他们可以访问每个链的 gas 价格水平、每个链的区块浏览器链接以及有关链本身的一些详细信息。

resources.ts 文件的一部分

export const registerResources = (server: any) => {
    // 注册 gas 参考资源
    server.resource(
        'gas-reference',
        'evm://docs/gas-reference',
        async (uri: URL) => {
            return {
                contents: [
                    {
                        uri: uri.href,
                        text: JSON.stringify(gasReferencePoints, null, 2),
                    },
                ],
            }
        }
    )

    // 其他资源...
}

// 每个链的 Gas 参考点
const gasReferencePoints = {
    ethereum: {
        low: 20,
        average: 40,
        high: 100,
        veryHigh: 200,
    },
    base: {
        low: 0.05,
        average: 0.1,
        high: 0.3,
        veryHigh: 0.5,
    },
    // 其他链...
}

// 代码的其余部分...

工具:tools.ts

此文件定义服务器可以调用的工具。 在这种情况下,我们使用 eth_getBalanceeth_getCodeeth_gasPrice 工具。 每个工具都是一个异步函数,通过 Viem 查询区块链,并返回 LLM 使用的结构化数据。

tools.ts 文件的一部分

// 向 MCP 服务器注册工具
export const registerTools = (server: any) => {
    // 注册 eth_getBalance 工具
    server.tool(
        'eth_getBalance',
        balanceSchema.shape,
        async (args: z.infer<typeof balanceSchema>) => {
            try {
                const result = await getBalance(args)
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify(result, null, 2),
                        },
                    ],
                }
            } catch (error) {
                // 处理错误
            }
        }
    )
    // 其他工具...
}

// eth_getBalance 工具的 Schema
const balanceSchema = z.object({
    address: z.string().refine(isAddress, {
        message: 'Invalid Ethereum address format', // 以太坊地址格式无效
    }),
    chain: z
        .string()
        .refine((val): val is ChainId => Object.keys(CHAINS).includes(val), {
            message:
                'Unsupported chain. Use one of: ethereum, base, arbitrum, avalanche, bsc', // 不支持的链。 请使用以下链之一:ethereum、base、arbitrum、avalanche、bsc
        }),
})

// 其他工具 Schema...

/**
 * 获取指定链上以太坊地址的余额
 */
export const getBalance = async (params: z.infer<typeof balanceSchema>) => {
    const { address, chain } = balanceSchema.parse(params)

    try {
        const client = getPublicClient(chain as ChainId)
        const chainInfo = getChain(chain as ChainId)

        // 获取 wei 中的余额
        const balanceWei = await client.getBalance({ address })

        // 将余额格式化为 ETH/本机Token
        const balanceFormatted = formatEther(balanceWei)

        return {
            address,
            chain: chainInfo.name,
            balanceWei: balanceWei.toString(),
            balanceFormatted: `${balanceFormatted} ${chainInfo.symbol}`,
            symbol: chainInfo.symbol,
            decimals: chainInfo.decimals,
        }
    } catch (error) {
        return {
            error: `Failed to get balance: ${(error as Error).message}`, // 无法获取余额
        }
    }
}

// 代码的其余部分...

构建你的 EVM MCP 服务器

我们了解了每个文件如何在 MCP 服务器中发挥作用。 现在,让我们构建并运行我们的服务器。

获取多链端点

我们正在构建的 MCP 服务器将支持多个 EVM 链(以太坊、Base、Arbitrum、Avalanche 和 BSC)。 通过利用 QuickNode 的多链格式,我们可以使用单个端点轻松连接到这些链。 如果你没有 QuickNode 帐户,可以在 此处 免费获取一个。

  1. 登录到你的 QuickNode 帐户。
  2. 转到“端点”选项卡。
  3. 单击“创建端点”。
  4. 选择以太坊主网或其中一个其他 EVM 链。
  5. 创建端点后,激活多链格式。
  6. 记下你的端点 URL,它看起来像 https://{endpoint_name}.quiknode.pro/{token_id}/https://{endpoint_name}.{chain_name}.quiknode.pro/{token_id}
  7. 从 URL 中提取端点名称Token ID

由于有了多链格式,我们现在可以使用单个端点连接到任何 EVM 链。 端点名称和Token ID 用于标识链及其配置。

QuickNode Multichain Endpoint

安装说明

现在我们有了端点,让我们设置我们的 MCP 服务器。

步骤 1:克隆存储库

git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/AI/evm-mcp-server

步骤 2:安装依赖项

npm install

这将安装项目所需的依赖项:

  • @modelcontextprotocol/sdk:用于构建 MCP 服务器的 TypeScript SDK。
  • viem:用于与 EVM 区块链交互的 TypeScript 库。
  • zod:用于定义 Schema 和验证数据的 TypeScript 库。
  • typescript:TypeScript 编译器。
  • @types/node:Node.js 的 TypeScript 类型定义。

步骤 3:构建项目

npm run build

这将生成包含已编译的 index.js 文件的 build/ 目录,该文件用作服务器的入口点。

步骤 4:配置 Claude Desktop

服务器使用在 Claude Desktop 的配置文件 (claude_desktop_config.json) 中定义的环境变量来连接到 QuickNode 并运行服务器。

  • 打开 Claude Desktop,导航到 Claude > Settings > Developer
  • 编辑 claude_desktop_config.json 以包含以下内容(如果存在其他配置,请在 mcpServers 下添加):
{
    "mcpServers": {
        "evm": {
            "command": "node",
            "args": ["/absolute-path-to/build/index.js"],
            "env": {
                "QN_ENDPOINT_NAME": "your-quicknode-endpoint-name",
                "QN_TOKEN_ID": "your-quicknode-token-id"
            }
        }
    }
}
  • your-quicknode-endpoint-name 替换为你的 QuickNode 端点名称。
  • your-quicknode-token-id 替换为你的 QuickNode Token ID。
  • /absolute-path-to 替换为项目目录的绝对路径(例如,/Users/username/qn-guide-examples/AI/evm-mcp-server)。

保存文件并重新启动 Claude Desktop。 你的 MCP 服务器的工具、资源和提示现在应该在 Claude 中可用。

Tools

测试你的 MCP 服务器

让我们开始提问,看看服务器是如何工作的。

钱包分析

使用以下提示或使用我们预先构建的提示来指导 Claude 分析钱包的余额和上下文:

Give the balance of 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 on Ethereum

或者,要求 Claude 分析所有受支持链上的钱包余额:

Give the balance of 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 address across all networks you support.

Vitalik Balance - All Networks

克劳德会:

  • 调用 eth_getBalance 工具
  • 根据提示返回响应

请注意,由于 supported-chains 资源,Claude 将仅针对其支持的链调用 eth_getBalance 工具。

合约检测

使用以下提示或使用我们预先构建的提示来指导 Claude 检测钱包是否是合约:

What kind of contract is 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2?

克劳德会:

  • 检测到它是一个合约(使用字节码存在)
  • 解释它是否是已知的合约(使用资源或历史数据)

Gas 评估

使用以下提示或使用我们预先构建的提示来指导 Claude 评估以太坊网络上的 gas 价格:

Analyze current gas price on Ethereum. Is now a good time to transact?

克劳德会:

  • 调用 eth_gasPrice 工具以从 QuickNode 获取当前 gas 价格
  • 引用你提供的 gas-reference 资源,该资源包括每个链的低、中、高和非常高的 gas 价格的阈值
  • 将实时 gas 数据与这些参考点进行比较
  • 传递一条上下文感知的响应,指示现在是否是交易的好时机

Gas Price

恭喜! 你已经构建了你自己的 LLM 驱动的区块链数据分析工具。 现在,你可以使用 Claude 分析区块链数据并与之交互,使你能够构建智能应用程序并自动执行流程。

接下来是什么?

一旦你的 EVM MCP 服务器启动并运行,就可以通过多种方式扩展其功能并加深其与 AI 工作流程的集成。 以下是几个需要探索的领域:

扩展 EVM 功能

添加更多本机 EVM 方法以增加服务器可以执行的操作:

  • eth_getLogs:监视合约事件,例如Token转移或 DAO 投票
  • eth_call:读取智能合约数据(例如,Token余额、配置)
  • eth_blockNumber:获取最新的区块以进行链健康/状态跟踪
  • eth_getTransactionByHash:分析特定交易

构建 AI 特定增强功能

当 LLM agent 在特定于领域的推理和上下文的指导下,会变得更加有用。 考虑:

  • 专用提示:为 DeFi 分析、合约审计、钱包分析等设计特定于任务的说明。
  • ENS 支持:解析 .eth 域名以获得更好的用户体验
  • ERC20 Token和 NFT 数据探索:使用 Token和 NFT API v2 捆绑包 来获取Token余额、NFT 元数据等
  • 实时价格数据:使用 QuickNode Marketplace 插件(例如 DEX Aggregator Trading API)集成Token价格
  • 交易功能:如果你想超越只读查询,请查看我们的 在 Base 上构建电报交易机器人 指南。 想象一下将 MCP 提示与交易操作相结合以创建自主交易 agent。
  • 自定义 agent:将 MCP 与 LangChain、AutoGen 或 CrewAI 相结合以创建具有内存、计划和区块链访问权限的完全自主的 agent
  • 探索多链扩展:有兴趣支持 EVM 链旁边的 Solana 吗? 请按照我们的 如何构建用于 LLM 集成的 Solana MCP 服务器 指南来扩展你的 agent 在生态系统中的影响力。

添加缓存和性能优化

对于经常查询的数据,缓存可以减少延迟并避免重复的 RPC 调用:

  • 使用 RedisSQLite 或简单的基于文件的缓存来存储 eth_getBalanceeth_gasPrice
  • 为每个链/方法设置 TTL(生存时间)值,以确保新鲜度
  • 使用 memoization 模式或自定义包装器来避免每次运行重复的 Viem 调用

跟踪分析和使用情况

了解你的工具是如何使用的:

  • 记录传入的工具调用和提示使用情况
  • 监视 哪些链 查询最多
  • 添加基本使用指标(例如,每个链的交易量、每个工具的延迟)

此数据可以帮助指导未来的改进,例如优化提示模板或扩展基础架构。

加强安全性和稳定性

由于 MCP 服务器接受来自 agent 的输入,因此保护表面积非常重要:

  • 使用 Zod Schema 来严格验证和清理所有输入
  • 阅读 MCP 安全注意事项 以防止提示注入或滥用
  • 如果公开 MCP 服务器,请考虑限制速率或添加身份验证层

结论

EVM MCP 服务器在 LLM agent 和区块链数据之间提供了一个强大的桥梁。 通过实施模型上下文协议,我们创建了一个标准化接口,该接口允许 AI 模型访问和分析多个 EVM 兼容网络上的链上信息。

这个技术基础为 AI 驱动的区块链应用程序开辟了广阔的前景,从自动化监控到智能分析和推荐系统。 无论你是为分析师、dApp 还是内部自动化构建 agent,这都是你的基础——构建一次,然后在任何地方扩展它。

如果你有任何疑问或需要帮助,请随时在我们的 DiscordTwitter 上与我们联系。

附加资源

我们❤️反馈!

如果你对新主题有任何反馈或请求,请告诉我们。 我们很乐意听到你的声音。

  • 原文链接: quicknode.com/guides/ai/...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。