mcp-web3-wallet-tester

dennisonbertram/mcp-web3-wallet-tester

3.2

If you are the rightful owner of mcp-web3-wallet-tester and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

An MCP server that functions as an Ethereum wallet, enabling LLMs to manage Web3 dApp testing through Playwright.

Tools
13
Resources
0
Prompts
0

MCP Web3 Wallet Tester

An MCP server that acts as a programmable Ethereum wallet for automated Web3 dApp testing. LLMs can control wallet operations (approve transactions, sign messages, manage accounts) via MCP tools while Playwright automates the browser.

Features

  • Full EIP-1193 Provider - Injects a complete Ethereum provider into any dApp
  • EIP-6963 Support - Modern wallet discovery for multi-wallet environments
  • MCP Integration - Control wallet via MCP tools from any LLM
  • Multi-Account - Switch between 10 Anvil test accounts or use custom keys
  • Auto-Approve Mode - Enable fully automated testing when needed
  • Provider Serving - Fetch provider script via HTTP for easy injection

Quick Start

Prerequisites

  • Node.js 18+
  • Anvil (from Foundry): curl -L https://foundry.paradigm.xyz | bash && foundryup

Installation

npm install
npm run build

Start the Server

The easiest way to start everything (using the startup script):

# Start both Anvil and the wallet server
./server.sh --anvil

# Or start just the wallet server (if Anvil is already running)
./server.sh

# Custom ports
./server.sh --port=4000 --ws-port=9000 --anvil

# For mainnet fork testing
./server.sh --chain-id=1 --anvil

Or manually:

# Terminal 1: Start Anvil (local blockchain)
anvil

# Terminal 2: Start the wallet server
npm start

Or use the CLI:

npx web3-wallet-tester

Register with Claude Code

claude mcp add --transport http wallet-tester http://localhost:3001/mcp

Architecture

Browser (dApp)                     Wallet Server                    Blockchain
     │                                  │                               │
     │  window.ethereum.request()       │                               │
     ├─────────────────────────────────►│                               │
     │         WebSocket                │  wallet_approveRequest()      │
     │                                  │◄──────────────────────────────┤ LLM
     │                                  │         MCP Tools             │
     │                                  │                               │
     │                                  │  eth_sendTransaction          │
     │                                  ├──────────────────────────────►│
     │                                  │       JSON-RPC                │ Anvil
     │          result                  │                               │
     │◄─────────────────────────────────┤                               │

Usage

1. Inject the Provider (Playwright)

// Fetch provider from the wallet server
const providerScript = await fetch('http://localhost:3001/provider.js').then(r => r.text());

// Inject BEFORE navigating (critical for proper detection)
await page.addInitScript(providerScript);

// Navigate to dApp
await page.goto('https://app.uniswap.org');

2. Control via MCP Tools

// Check wallet status
const status = await wallet_getStatus();

// Wait for a wallet request
const request = await wallet_waitForRequest({ timeoutMs: 30000 });

// Approve the request
await wallet_approveRequest({ requestId: request.id });

// Or enable auto-approve for automated testing
await wallet_setAutoApprove({ enabled: true });

MCP Tools

ToolDescription
wallet_getStatusGet wallet status (address, chain, balance, pending count)
wallet_getAddressGet current wallet address
wallet_getBalanceGet ETH balance
wallet_getChainIdGet chain ID
wallet_getPendingRequestsList pending requests awaiting approval
wallet_approveRequestApprove a pending request
wallet_rejectRequestReject a pending request
wallet_waitForRequestWait for a request to arrive
wallet_setAutoApproveEnable/disable auto-approve mode
wallet_getTransactionReceiptGet transaction receipt by hash
wallet_listAccountsList all 10 Anvil test accounts
wallet_switchAccountSwitch to Anvil account by index (0-9)
wallet_setPrivateKeyUse a custom private key
wallet_getProviderScriptGet provider.js for injection

MCP Resources

The server exposes documentation as MCP resources:

Resource URIDescription
wallet://docs/instructionsLLM usage guide with workflow and examples
wallet://docs/testing-guideComplete testing documentation
wallet://docs/toolsTool reference with parameters

LLMs can read these resources to learn how to use the wallet tester.

HTTP Endpoints

EndpointDescription
GET /healthHealth check
GET /provider.jsGet the injectable provider script
POST /mcpMCP server endpoint

Configuration

Environment VariableDefaultDescription
MCP_PORT3001HTTP server port
WS_PORT8546WebSocket bridge port
ANVIL_RPC_URLhttp://127.0.0.1:8545Anvil RPC URL
CHAIN_ID(auto-detected)Chain ID to report
ACCOUNT_INDEX0Anvil account index (0-9)
PRIVATE_KEY(from index)Custom private key

Working with MetaMask

When MetaMask is installed, it locks window.ethereum. The wallet tester handles this by:

  1. EIP-6963 Announcements - Modern dApps discover wallets via events
  2. Repeated Announcements - Announces multiple times to catch late discovery
  3. Modal Detection - Re-announces when wallet selection UI opens

For reliable testing:

  • Use Playwright's addInitScript (injects before MetaMask)
  • Use incognito mode (extensions disabled)
  • Disable MetaMask temporarily in chrome://extensions

Example: Complete Test Flow

// 1. Setup
const providerScript = await fetch('http://localhost:3001/provider.js').then(r => r.text());
await page.addInitScript(providerScript);
await page.goto('https://example-dapp.com');

// 2. Connect wallet
await page.click('button:has-text("Connect Wallet")');
await new Promise(r => setTimeout(r, 500)); // Wait for requests

// 3. Approve connection
const pending = await wallet_getPendingRequests();
for (const req of pending) {
  await wallet_approveRequest({ requestId: req.id });
}

// 4. Send transaction
await page.fill('input[name="amount"]', '1.5');
await page.click('button:has-text("Send")');
await new Promise(r => setTimeout(r, 500));

// 5. Approve transaction
const txRequests = await wallet_getPendingRequests();
const txReq = txRequests.find(r => r.method === 'eth_sendTransaction');
const result = await wallet_approveRequest({ requestId: txReq.id });

// 6. Verify
const receipt = await wallet_getTransactionReceipt({ hash: result.result });
console.log('Transaction confirmed:', receipt.status);

Development

# Type check
npm run type-check

# Build everything
npm run build

# Build provider only
npm run build:provider

# Build with dev UI enabled
npm run build:provider:dev

# Watch mode
npm run dev

Project Structure

├── src/
│   ├── index.ts           # Entry point, starts all services
│   ├── mcp-server.ts      # MCP server with tools and resources
│   ├── ws-bridge.ts       # WebSocket bridge for browser
│   ├── queue.ts           # Request queue management
│   ├── wallet.ts          # Viem wallet wrapper
│   ├── types.ts           # TypeScript types
│   └── provider/
│       ├── injected.ts    # Browser provider (EIP-1193)
│       └── ui/            # Developer wallet UI (optional)
├── dist/
│   ├── index.js           # Compiled server
│   └── provider.js        # Compiled browser provider
├── docs/
│   ├── LLM_INSTRUCTIONS.md
│   ├── TESTING_GUIDE.md
│   └── ...
└── examples/
    ├── test-dapp.html     # Test dApp for development
    └── bookmarklet.html   # Manual injection bookmarklet

Documentation

  • - Step-by-step guide for AI agents
  • - Complete testing procedures
  • - How to inject the provider
  • - System design

License

MIT

Author

Dennison Bertram