#
tokens: 2473/50000 4/4 files
lines: off (toggle) GitHub
raw markdown copy
# 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
   ```

![Adding Ethereum MCP to Cursor](image.png)

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);
  });

```