Skip to main content
Glama
README.md
# MCP Anvil Tools

Model Context Protocol (MCP) server providing Ethereum development and testing tools for AI agents. Built on Anvil (Foundry's local Ethereum node) and viem for robust blockchain interactions.

## Overview

MCP Anvil Tools enables AI agents to:
- Read Solidity source code and contract storage
- Simulate and execute transactions on local Anvil or testnets
- Manipulate blockchain state with snapshots and impersonation
- Query events and decode bytecode
- Test smart contracts in isolated environments

Perfect for AI-powered smart contract auditing, testing workflows, and blockchain development automation.

## Key Features

- **Dual Transport Modes**: Stateless HTTP via `/mcp` endpoint or stdio for CLI/desktop integration
- **Reading Tools (4)**: Source code, storage, bytecode, and event log access
- **Execution Tools (5)**: Transaction simulation, sending, and state manipulation
- **Tracing Tools (2)**: Transaction and call tracing with multiple tracer types
- **Anvil Integration**: Automatic Anvil process management with snapshot/revert support
- **State Persistence**: SQLite-backed deployment and session tracking
- **Type Safety**: Full TypeScript support with Zod validation

## Quick Start

### Installation

```bash
# Clone the repository
git clone https://github.com/yourusername/mcp-anvil-tools.git
cd mcp-anvil-tools

# Install dependencies
npm install

# Build the project
npm run build
```

### Configuration

```bash
# Copy example environment file
cp .env.example .env

# Edit .env with your configuration
nano .env
```

Required environment variables:
- `AUDIT_MCP_PORT` - Server port (default: 3000)
- `MAINNET_RPC_URL` - RPC endpoint for mainnet interactions
- `ETHERSCAN_API_KEY` - Optional, for source code verification

### Running the Server

**HTTP/SSE Mode** (for web clients, multi-agent systems):
```bash
npm start
# Server runs on http://localhost:3000
```

**stdio Mode** (for Claude Desktop, CLI tools):
```bash
npm run start:stdio
# Communicates via stdin/stdout
```

**Development Mode** (with hot reload):
```bash
npm run dev
```

## Transport Modes

### HTTP Mode

Use HTTP mode for:
- Web-based AI clients
- Multi-agent architectures
- Stateless MCP connections
- RESTful API interactions

**Endpoints:**
- `GET /health` - Health check (unauthenticated)
- `GET /metrics` - Deployment and instance statistics
- `POST /mcp` - MCP protocol endpoint (StreamableHTTPServerTransport)

**Example Connection:**
```bash
# Health check
curl http://localhost:3000/health

# Metrics
curl http://localhost:3000/metrics

# MCP protocol request
curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {
        "name": "test-client",
        "version": "1.0.0"
      }
    }
  }'
```

### stdio Mode

Use stdio for:
- Claude Desktop integration
- Command-line MCP clients
- Programmatic testing
- Shell script automation

**Example Test:**
```bash
# Simple echo test
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | npm run start:stdio

# Automated test suite
npx tsx test-stdio.ts

# Manual test script
./test-stdio-manual.sh
```

## Available Tools

### Reading Tools

#### 1. `read_source`
Read Solidity source code files from the v4-core repository.

**Input:**
- `path` (string): Relative path from `lib/v4-core/src/` (e.g., `PoolManager.sol`)

**Output:**
- `content` (string): Full source code
- `lines` (number): Total line count
- `path` (string): Absolute file path
- `size` (number): File size in bytes
- `lastModified` (string): ISO timestamp

**Example:**
```json
{
  "path": "PoolManager.sol"
}
```

#### 2. `read_storage`
Read contract storage slots (persistent storage only, not transient).

**Input:**
- `address` (string): Contract address
- `slot` (string): Storage slot (hex, e.g., `0x0`)
- `blockTag` (optional): `latest`, `earliest`, `pending`, block number, or block hash
- `rpc` (optional): RPC URL (default: `http://localhost:8545`)

**Output:**
- `value` (string): Raw 32-byte hex value
- `decoded` (optional): Best-effort interpretation (uint256, address, bool, bytes32)

**Example:**
```json
{
  "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "slot": "0x0",
  "blockTag": "latest"
}
```

#### 3. `read_bytecode`
Retrieve deployed bytecode from a contract address.

**Input:**
- `address` (string): Contract address
- `blockTag` (optional): Block identifier
- `rpc` (optional): RPC URL

**Output:**
- `bytecode` (string): Hex-encoded bytecode
- `size` (number): Bytecode size in bytes
- `codeHash` (string): Keccak256 hash
- `isEmpty` (boolean): True if no code deployed (EOA)

**Example:**
```json
{
  "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}
```

#### 4. `read_events`
Query and decode contract event logs.

**Input:**
- `address` (string): Contract address
- `eventSignature` (optional): Event signature (e.g., `Transfer(address,address,uint256)`)
- `topics` (optional): Indexed topics filter
- `fromBlock` (optional): Starting block (default: `earliest`)
- `toBlock` (optional): Ending block (default: `latest`)
- `rpc` (optional): RPC URL

**Output:**
- `events` (array): Event logs with block info
- `count` (number): Total events returned
- `fromBlock` (string): Actual starting block
- `toBlock` (string): Actual ending block

**Example:**
```json
{
  "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "eventSignature": "Transfer(address,address,uint256)",
  "fromBlock": 1000000,
  "toBlock": 1000100
}
```

### Execution Tools

#### 5. `simulate_tx`
Simulate transactions without sending them to the network.

**Input:**
- `to` (string): Target contract address
- `data` (string): Calldata (hex)
- `from` (optional): Sender address
- `gasLimit` (optional): Gas limit
- `value` (optional): ETH value in wei (hex)
- `abi` (optional): Contract ABI for decoding
- `functionName` (optional): Function name for decoding
- `blockNumber` (optional): Block to simulate at
- `stateOverrides` (optional): State overrides by address
- `rpc` (optional): RPC endpoint

**Output:**
- `result` (string): Return data (hex)
- `decoded` (optional): Decoded return value
- `reverted` (boolean): Whether call reverted
- `revertReason` (optional): Decoded revert reason
- `revertData` (optional): Raw revert data

**Example:**
```json
{
  "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "data": "0x70a08231000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266",
  "abi": [...],
  "functionName": "balanceOf"
}
```

#### 6. `send_tx`
Send actual transactions to the network.

**Input:**
- `to` (optional): Target address (omit for deployment)
- `data` (string): Transaction data / bytecode
- `from` (optional): Sender address
- `value` (optional): ETH value in wei (hex)
- `gasLimit` (optional): Gas limit (auto-estimated)
- `gasPrice` (optional): Legacy gas price
- `maxFeePerGas` (optional): EIP-1559 max fee
- `maxPriorityFeePerGas` (optional): EIP-1559 priority fee
- `nonce` (optional): Transaction nonce
- `privateKey` (optional): Private key for signing
- `confirmations` (optional): Confirmations to wait (default: 1)
- `rpc` (optional): RPC endpoint

**Output:**
- `txHash` (string): Transaction hash
- `blockNumber` (string): Block number
- `blockHash` (string): Block hash
- `gasUsed` (string): Gas consumed
- `effectiveGasPrice` (string): Actual gas price
- `status` (enum): `success` or `reverted`
- `logs` (array): Event logs
- `contractAddress` (optional): Deployed contract address
- `from` (string): Sender address
- `to` (optional): Recipient address

**Example:**
```json
{
  "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "data": "0xa9059cbb...",
  "value": "0x0",
  "privateKey": "0x..."
}
```

#### 7. `impersonate`
Impersonate any address on Anvil (testing only).

**Input:**
- `address` (string): Address to impersonate
- `stopImpersonating` (optional): Stop impersonation (default: false)
- `rpc` (optional): RPC endpoint (must be Anvil)

**Output:**
- `success` (boolean): Whether operation succeeded
- `address` (string): Impersonated address
- `active` (boolean): Current impersonation status
- `balance` (optional): Current balance

**Example:**
```json
{
  "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
}
```

#### 8. `create_snapshot`
Create Anvil state snapshot for later revert.

**Input:**
- `name` (optional): Human-readable snapshot name
- `description` (optional): Snapshot description
- `rpc` (optional): RPC endpoint (must be Anvil)

**Output:**
- `snapshotId` (string): Unique snapshot identifier
- `name` (optional): Snapshot name
- `blockNumber` (number): Block at snapshot
- `blockHash` (string): Block hash
- `timestamp` (number): Block timestamp
- `created` (number): Unix timestamp created

**Example:**
```json
{
  "name": "before-attack-simulation",
  "description": "State before testing exploit scenario"
}
```

#### 9. `revert_snapshot`
Revert blockchain state to a previous snapshot.

**Input:**
- `snapshotId` (string): Snapshot ID or name
- `rpc` (optional): RPC endpoint (must be Anvil)

**Output:**
- `success` (boolean): Whether revert succeeded
- `snapshotId` (string): Reverted snapshot ID
- `blockNumber` (number): Block after revert
- `blockHash` (string): Block hash after revert
- `timestamp` (number): Block timestamp
- `reverted` (boolean): State revert confirmation

**Example:**
```json
{
  "snapshotId": "before-attack-simulation"
}
```

### Tracing Tools

#### 10. `trace_transaction`
Trace an existing transaction by hash using debug_traceTransaction.

**Input:**
- `txHash` (string): Transaction hash to trace (64 hex characters)
- `tracer` (optional): Tracer type - `callTracer`, `prestateTracer`, `4byteTracer`, or omit for raw opcode trace
- `tracerConfig` (optional): Tracer-specific configuration object
  - For `callTracer`: `{ onlyTopCall: true }` to exclude subcalls
- `rpc` (optional): RPC URL (default: `http://localhost:8545`)

**Output:**
- `result`: Trace result (format depends on tracer type)
  - **callTracer**: Call tree with `type`, `from`, `to`, `value`, `gas`, `input`, `output`
  - **prestateTracer**: Pre-execution state of all touched accounts
  - **4byteTracer**: Map of function selectors to call counts
  - **No tracer**: Full opcode trace with `structLogs` array
- `txHash` (string): Transaction hash that was traced

**Example:**
```json
{
  "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  "tracer": "callTracer",
  "tracerConfig": {
    "onlyTopCall": true
  }
}
```

**Use Cases:**
- Debug failed transactions
- Analyze gas usage patterns
- Understand contract interactions
- Detect reentrancy or complex call paths

#### 11. `trace_call`
Trace a call without sending transaction using debug_traceCall.

**Input:**
- `to` (string): Target contract address
- `data` (string): Calldata (hex encoded)
- `from` (optional): Sender address
- `value` (optional): ETH value in wei (hex)
- `blockTag` (optional): Block to trace at - `latest`, `earliest`, `pending`, `safe`, `finalized`, or block number
- `tracer` (optional): Tracer type (same as `trace_transaction`)
- `tracerConfig` (optional): Tracer configuration
- `rpc` (optional): RPC URL

**Output:**
- `result`: Trace result (format depends on tracer type, same as `trace_transaction`)

**Example:**
```json
{
  "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "data": "0xa9059cbb000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000de0b6b3a7640000",
  "from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  "tracer": "callTracer"
}
```

**Use Cases:**
- Debug before sending actual transactions
- Analyze call behavior at specific blocks
- Test state override scenarios
- Investigate potential exploits safely

## Claude Desktop Integration

Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "anvil-tools": {
      "command": "node",
      "args": [
        "/absolute/path/to/mcp-anvil-tools/dist/index.js",
        "--stdio"
      ],
      "env": {
        "AUDIT_MCP_PORT": "3000",
        "MAINNET_RPC_URL": "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
        "ETHERSCAN_API_KEY": "your_etherscan_api_key"
      }
    }
  }
}
```

**Important**: Use absolute paths for the `command` args. Restart Claude Desktop after configuration changes.

## Configuration

### Alchemy Multi-Network Support

Set `ALCHEMY_API_KEY` to enable access to any Alchemy-supported network without additional configuration:

```bash
ALCHEMY_API_KEY=your_alchemy_api_key
```

Use any [Alchemy network slug](https://docs.alchemy.com/reference/supported-chains) directly:

| Network | Slug |
|---------|------|
| Ethereum | `eth-mainnet`, `eth-sepolia` |
| Arbitrum | `arb-mainnet`, `arb-sepolia` |
| Optimism | `opt-mainnet`, `opt-sepolia` |
| Polygon | `polygon-mainnet`, `polygon-amoy` |
| Base | `base-mainnet`, `base-sepolia` |

New networks are supported automatically as Alchemy adds them - no code changes needed.

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `AUDIT_MCP_PORT` | HTTP server port | `3000` |
| `AUDIT_MCP_HOST` | HTTP server host | `0.0.0.0` |
| `AUDIT_MCP_DB_PATH` | SQLite database path | `./audit-mcp.db` |
| `ANVIL_PORT_START` | Anvil port range start | `8545` |
| `ANVIL_PORT_END` | Anvil port range end | `8555` |
| `ANVIL_DEFAULT_CHAIN_ID` | Default chain ID | `31337` |
| `ALCHEMY_API_KEY` | Alchemy API key (enables multi-network) | - |
| `MAINNET_RPC_URL` | Mainnet RPC (overrides Alchemy) | - |
| `SEPOLIA_RPC_URL` | Sepolia RPC (overrides Alchemy) | - |
| `ETHERSCAN_API_KEY` | Etherscan API key | - |
| `ARBISCAN_API_KEY` | Arbiscan API key | - |
| `LOG_LEVEL` | Logging level | `info` |
| `LOG_FILE` | Log file path | `./audit-mcp.log` |
| `SLITHER_PATH` | Path to Slither binary | `/usr/local/bin/slither` |
| `SOLC_PATH` | Path to Solc binary | `/usr/local/bin/solc` |

## Architecture

### Project Structure

```
src/
├── index.ts              # Entry point (HTTP/stdio mode detection)
├── server.ts             # Express app + McpServer setup
├── config.ts             # Configuration management
├── anvil/
│   ├── manager.ts        # Anvil process lifecycle
│   └── types.ts          # Anvil-related types
├── state/
│   └── manager.ts        # SQLite state management
├── tools/
│   ├── index.ts          # Tool registration with McpServer.registerTool()
│   ├── reading.ts        # Reading tools (4): source, storage, bytecode, events
│   ├── execution.ts      # Execution tools (5): simulate, send, impersonate, snapshots
│   └── tracing.ts        # Tracing tools (2): trace_transaction, trace_call
└── utils/
    ├── errors.ts         # Error handling
    └── validation.ts     # Zod schemas
```

### Database Schema

SQLite tables for state persistence:

- **deployments** - Contract deployment records
- **anvil_instances** - Running Anvil instances
- **audit_sessions** - Audit session metadata
- **audit_findings** - Discovered vulnerabilities
- **audit_notes** - Session notes

### Transport Architecture

```
┌─────────────────┐
│  AI Agent/User  │
└────────┬────────┘
         │
    ┌────┴────┐
    │  HTTP   │  stdio
    │  /mcp   │  (stdin/stdout)
    └────┬────┘
         │
    ┌────┴──────────────────┐
    │  McpServer (stateless)│
    │  + registerTool API   │
    └────┬──────────────────┘
         │
    ┌────┴────────┐
    │   11 Tools  │
    │  Reading: 4 │
    │ Execution: 5│
    │  Tracing: 2 │
    └────┬────────┘
         │
    ┌────┴────────┐
    │    viem +   │
    │    Anvil    │
    └─────────────┘
```

## Testing

### Automated Tests

```bash
# Run stdio transport tests
npx tsx test-stdio.ts

# Run all tool tests
npm test
```

### Manual Testing

```bash
# Test stdio transport
./test-stdio-manual.sh

# Test specific tools
npm run test:tools
```

### Example Workflows

**1. Read and analyze contract:**
```bash
# Read source
read_source { "path": "PoolManager.sol" }

# Get bytecode
read_bytecode { "address": "0x..." }

# Read storage
read_storage { "address": "0x...", "slot": "0x0" }
```

**2. Simulate and execute transaction:**
```bash
# Simulate first
simulate_tx {
  "to": "0x...",
  "data": "0x...",
  "abi": [...]
}

# If successful, send
send_tx {
  "to": "0x...",
  "data": "0x...",
  "privateKey": "0x..."
}
```

**3. Test with snapshots:**
```bash
# Create snapshot
create_snapshot { "name": "clean-state" }

# Run test transactions
send_tx { ... }

# Revert to clean state
revert_snapshot { "snapshotId": "clean-state" }
```

## Development

### Building

```bash
# Build TypeScript
npm run build

# Watch mode
npm run dev
```

### Linting

```bash
# Run ESLint
npm run lint

# Format code
npm run format
```

### Adding New Tools

1. Define input/output schemas with Zod in `src/tools/`
2. Implement handler function
3. Export tool in tools object
4. Register in `src/tools/index.ts`
5. Add documentation to TOOLS.md

## Security Considerations

- **Impersonation**: Only works on Anvil, not production networks
- **Private Keys**: Never log or expose private keys
- **RPC Access**: Use secure RPC endpoints with authentication
- **State Overrides**: Validate carefully to prevent unintended behavior
- **Gas Limits**: Always set reasonable gas limits to prevent DoS
- **Input Validation**: All inputs validated with Zod schemas

## Troubleshooting

### Common Issues

**Server won't start:**
- Check port availability: `lsof -i :3000`
- Verify environment variables in `.env`
- Check database permissions for `AUDIT_MCP_DB_PATH`

**Anvil connection fails:**
- Ensure Anvil is installed: `which anvil`
- Check port range configuration
- Verify no port conflicts

**Tool execution errors:**
- Check RPC endpoint availability
- Verify contract address exists
- Ensure sufficient gas for transactions
- Check impersonation is only used on Anvil

**stdio mode issues:**
- Ensure one JSON-RPC message per line
- Check stderr for log messages (stdout is for responses)
- Verify MCP protocol version compatibility

## Performance

- **Connection Pooling**: Reuses viem clients across requests
- **State Caching**: SQLite for fast state retrieval
- **Snapshot Registry**: In-memory tracking for quick snapshot operations
- **Concurrent Requests**: Express handles multiple concurrent MCP connections

## Roadmap

- [ ] Advanced analysis tools (Slither integration)
- [ ] Call graph visualization
- [ ] AST parsing utilities
- [ ] Multi-chain support
- [x] Enhanced trace analysis (debug_traceTransaction, debug_traceCall)
- [ ] Gas optimization suggestions
- [ ] Security pattern detection

## Contributing

Contributions welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Update documentation
5. Submit a pull request

## License

MIT

## Resources

- [Model Context Protocol Documentation](https://modelcontextprotocol.io/)
- [viem Documentation](https://viem.sh/)
- [Foundry Anvil](https://book.getfoundry.sh/anvil/)
- [TOOLS.md](./TOOLS.md) - Detailed tool reference

## Support

- Issues: [GitHub Issues](https://github.com/yourusername/mcp-anvil-tools/issues)
- Documentation: [TOOLS.md](./TOOLS.md)
- Examples: See `test-tools.ts` for usage examples