# Directory Structure ``` ├── .gitignore ├── index.js ├── LICENSE ├── package-lock.json ├── package.json └── README.md ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Dependency directories node_modules/ npm-debug.log yarn-debug.log yarn-error.log # Environment variables .env .env.local .env.development.local .env.test.local .env.production.local # Build outputs dist/ build/ # IDE and editor files .idea/ .vscode/ *.swp *.swo .DS_Store ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # Ethereum RPC MCP Server A Model Context Protocol (MCP) server for interacting with Ethereum blockchain. ## Overview This MCP server provides tools to query Ethereum blockchain data through standard JSON-RPC methods. It enables AI assistants and applications to interact with the Ethereum blockchain through a standardized protocol. ## Features This MCP server provides three key Ethereum RPC methods as tools: - **eth_getCode**: Retrieve the code at a specific Ethereum address - **eth_gasPrice**: Get the current gas price on the Ethereum network - **eth_getBalance**: Check the balance of an Ethereum account Note: More are coming ## Usage ### Adding to Cursor To add this MCP to Cursor: 1. First, clone this repository: ```bash git clone https://github.com/yourusername/eth-mpc.git ``` 2. Go to Cursor settings → MCP → Add new MCP server 3. Enter a name (e.g., "eth-mcp") 4. Select "command" as the type 5. Input the full path to the script: ``` node /path/to/eth-mpc/index.js ```  6. Click "Add" to enable the server Once added, the Ethereum RPC tools will be available to use within Cursor. The server uses stdio transport, making it compatible with MCP clients like Claude Desktop, Cursor, and others. ## Testing with MCP Inspector The MCP Inspector is a development tool for testing and debugging MCP servers. It provides an interactive interface to test your MCP server's functionality without needing a full AI client. ### Running the Inspector To test your Ethereum RPC MCP server with the Inspector: To run the Inspector: ```bash npx @modelcontextprotocol/inspector ``` 2. Input the command and path 3. The Inspector will connect to your running MCP server and display available tools. ### Testing Tools with Inspector The Inspector allows you to: - View available tools and their descriptions - Test each tool with different parameters - See the responses in a structured format - Debug any issues with your MCP server implementation For example, to test the `eth_getBalance` tool: 1. Select the tool in the Inspector interface 2. Enter a valid Ethereum address (e.g., `0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045` - Vitalik's address) 3. Use the default block parameter (`latest`) 4. Submit the request and view the response ## Integration with MCP Clients This MCP server can be integrated with any MCP-compatible client, including: - Claude Desktop - Claude Code - Cursor (instructions above) - Cline - Other MCP-compatible applications When integrated, the client application can use the tools provided by this server to query Ethereum blockchain data directly. ## Understanding MCP Model Context Protocol (MCP) is an open standard that allows AI models to interact with various tools and services. It provides a standardized way for developers to expose APIs, data sources, and functionality to AI assistants. ### Learn More About MCP MCP servers like this one form part of an ecosystem that allows AI assistants to perform complex tasks across multiple services without requiring custom integration for each service. 📚 **Official Documentation**: [Model Context Protocol Overview](https://modelcontextprotocol.io/sdk/java/mcp-overview) ## License MIT ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "eth-mcp", "version": "1.0.0", "description": "an mcp to interact with the evm", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.6.1", "axios": "^1.8.1", "zod": "^3.24.2" } } ``` -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- ```javascript const axios = require('axios'); const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { z } = require('zod'); // Redirect console.log to stderr to avoid breaking the MCP protocol const originalConsoleLog = console.log; console.log = function() { console.error.apply(console, arguments); }; // Ethereum RPC URL const ETH_RPC_URL = 'https://eth.llamarpc.com'; // Initialize the MCP server const server = new McpServer({ name: 'ethereum-rpc', version: '1.0.0' }); // Define prompts for common Ethereum interactions server.prompt( 'check-contract', { address: z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe('The Ethereum address to check') }, ({ address }) => ({ messages: [ { role: 'user', content: { type: 'text', text: `Please analyze the contract at address ${address}: 1. First, get the contract code 2. Check if it's a contract or regular address 3. If it's a contract, help me understand what type of contract it might be based on the code` } } ] }) ); server.prompt( 'check-wallet', { address: z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe('The Ethereum address to check') }, ({ address }) => ({ messages: [ { role: 'user', content: { type: 'text', text: `Please analyze the wallet at address ${address}: 1. Get the current balance 2. Format the balance in both Wei and ETH 3. Provide context about whether this is a significant amount` } } ] }) ); server.prompt( 'gas-analysis', {}, () => ({ messages: [ { role: 'user', content: { type: 'text', text: `Please analyze the current gas situation: 1. Get the current gas price 2. Convert it to Gwei for readability 3. Suggest whether this is a good time for transactions based on historical averages` } } ] }) ); // Helper function to make RPC calls async function makeRpcCall(method, params = []) { try { const response = await axios.post(ETH_RPC_URL, { jsonrpc: '2.0', id: 1, method, params }); if (response.data.error) { throw new Error(`RPC Error: ${response.data.error.message}`); } return response.data.result; } catch (error) { console.error(`Error making RPC call to ${method}:`, error.message); throw error; } } // Tool 1: eth_getCode - Gets the code at a specific address server.tool( 'eth_getCode', 'Retrieves the code at a given Ethereum address', { address: z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe('The Ethereum address to get code from'), blockParameter: z.string().default('latest').describe('Block parameter (default: "latest")') }, async (args) => { try { console.error(`Getting code for address: ${args.address} at block: ${args.blockParameter}`); const code = await makeRpcCall('eth_getCode', [args.address, args.blockParameter]); return { content: [{ type: "text", text: code === '0x' ? `No code found at address ${args.address} (this may be a regular wallet address, not a contract)` : `Contract code at ${args.address}:\n${code}` }] }; } catch (error) { return { content: [{ type: "text", text: `Error: Failed to get code. ${error.message}` }], isError: true }; } } ); // Tool 2: eth_gasPrice - Gets the current gas price server.tool( 'eth_gasPrice', 'Retrieves the current gas price in wei', {}, async () => { try { console.error('Getting current gas price'); const gasPrice = await makeRpcCall('eth_gasPrice'); // Convert hex gas price to decimal and then to Gwei for readability const gasPriceWei = parseInt(gasPrice, 16); const gasPriceGwei = gasPriceWei / 1e9; return { content: [{ type: "text", text: `Current Gas Price:\n${gasPriceWei} Wei\n${gasPriceGwei.toFixed(2)} Gwei` }] }; } catch (error) { return { content: [{ type: "text", text: `Error: Failed to get gas price. ${error.message}` }], isError: true }; } } ); // Tool 3: eth_getBalance - Gets the balance of an account server.tool( 'eth_getBalance', 'Retrieves the balance of a given Ethereum address', { address: z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe('The Ethereum address to check balance'), blockParameter: z.string().default('latest').describe('Block parameter (default: "latest")') }, async (args) => { try { console.error(`Getting balance for address: ${args.address} at block: ${args.blockParameter}`); const balance = await makeRpcCall('eth_getBalance', [args.address, args.blockParameter]); // Convert hex balance to decimal and then to ETH for readability const balanceWei = parseInt(balance, 16); const balanceEth = balanceWei / 1e18; return { content: [{ type: "text", text: `Balance for ${args.address}:\n${balanceWei} Wei\n${balanceEth.toFixed(6)} ETH` }] }; } catch (error) { return { content: [{ type: "text", text: `Error: Failed to get balance. ${error.message}` }], isError: true }; } } ); // Connect to the stdio transport and start the server server.connect(new StdioServerTransport()) .then(() => { console.error('Ethereum RPC MCP Server is running...'); }) .catch((err) => { console.error('Failed to start MCP server:', err); process.exit(1); }); ```