Skip to content

Development Guide

Set up local development for Universal Crypto MCP.


Prerequisites


Quick Start

# Clone
git clone https://github.com/nirholas/universal-crypto-mcp
cd universal-crypto-mcp

# Install dependencies
bun install

# Start development server
bun dev:sse

Environment Variables

Create a .env file:

cp .env.example .env
Variable Description Required
PRIVATE_KEY Wallet private key (for write operations) For transactions
LOG_LEVEL DEBUG, INFO, WARN, ERROR Optional
PORT Server port (default: 3001) Optional
INFURA_API_KEY Infura API key for RPC Optional
ALCHEMY_API_KEY Alchemy API key for RPC Optional
ETHERSCAN_API_KEY Etherscan API key for verification Optional
CRYPTOPANIC_API_KEY CryptoPanic for news Optional

Project Structure

universal-crypto-mcp/
├── src/
│   ├── index.ts           # Entry point
│   ├── evm.ts             # EVM entry point
│   ├── lib.ts             # Library exports
│   ├── evm/
│   │   ├── chains.ts      # Chain configurations (50+ networks)
│   │   ├── index.ts       # EVM module registry
│   │   ├── modules/       # EVM-specific modules
│   │   │   ├── blocks/
│   │   │   ├── bridge/
│   │   │   ├── contracts/
│   │   │   ├── deployment/
│   │   │   ├── domains/     # ENS
│   │   │   ├── events/
│   │   │   ├── gas/
│   │   │   ├── governance/
│   │   │   ├── lending/
│   │   │   ├── mev/
│   │   │   ├── multicall/
│   │   │   ├── network/
│   │   │   ├── nft/
│   │   │   ├── portfolio/
│   │   │   ├── price-feeds/
│   │   │   ├── security/
│   │   │   ├── signatures/
│   │   │   ├── staking/
│   │   │   ├── swap/
│   │   │   ├── tokens/
│   │   │   ├── transactions/
│   │   │   └── wallet/
│   │   └── services/      # Core EVM services
│   ├── modules/           # General modules
│   │   ├── governance/
│   │   ├── news/
│   │   └── utils/
│   ├── server/            # MCP server (stdio/SSE)
│   └── utils/             # Utilities & logging
├── docs/                  # Documentation (MkDocs)
└── package.json

Adding a New Tool

Create a file in src/evm/modules/<module>/tools.ts:

import { z } from "zod"
import { getPublicClient } from "../../services/clients"

export const exampleTools = {
  example_tool: {
    description: "Example tool description",
    schema: z.object({
      network: z.string().default("mainnet").describe("Network name"),
      address: z.string().describe("Address to check")
    }),
    handler: async ({ network, address }) => {
      const client = getPublicClient(network)
      const balance = await client.getBalance({ address })
      return { 
        address,
        balance: balance.toString(),
        network 
      }
    }
  }
}

Register in src/evm/modules/<module>/index.ts:

import { exampleTools } from "./tools"

export const tools = { ...exampleTools }

export const registerModule = (server) => {
  Object.entries(tools).forEach(([name, tool]) => {
    server.tool(name, tool.description, tool.schema, tool.handler)
  })
}

Adding ABIs

For tools requiring contract interactions:

// Define ABI constants
const ERC20_ABI = [
  {
    name: "balanceOf",
    type: "function",
    stateMutability: "view",
    inputs: [{ name: "account", type: "address" }],
    outputs: [{ type: "uint256" }]
  }
] as const

// Use in handler
const balance = await client.readContract({
  address,
  abi: ERC20_ABI,
  functionName: "balanceOf",
  args: [walletAddress]
})

Adding Prompts

Create prompts in src/evm/modules/<module>/prompts.ts:

import { PromptMessage } from "@modelcontextprotocol/sdk/types.js"

export const analyzePrompts = {
  analyze_example: {
    name: "analyze_example",
    description: "Analyze an example",
    arguments: [
      { name: "address", description: "Address to analyze", required: true },
      { name: "network", description: "Network name", required: false }
    ],
    handler: async ({ address, network = "mainnet" }): Promise<PromptMessage[]> => {
      return [
        {
          role: "user",
          content: {
            type: "text",
            text: `Analyze the address ${address} on ${network}...`
          }
        }
      ]
    }
  }
}

Testing

# Run MCP Inspector
bun test

# Test specific module
bun test:evm

# Run E2E tests
bun e2e

Manual Testing

Use the MCP Inspector for interactive testing:

npx @anthropic/mcp-inspector

Scripts

Command Description
bun dev Start dev server (stdio)
bun dev:sse Start dev server (SSE)
bun build Build for production
bun test Launch MCP Inspector
bun format Format code
bun lint Lint code

Architecture

Module Pattern

Each module follows this pattern:

module/
├── index.ts       # Exports & registration
├── tools.ts       # Tool definitions
└── prompts.ts     # Prompt definitions (optional)

Service Layer

Services in src/evm/services/ provide reusable functionality:

  • clients.ts - RPC client management
  • balance.ts - Balance queries
  • tokens.ts - Token operations
  • transactions.ts - Transaction building
  • contracts.ts - Contract interactions
  • ens.ts - ENS resolution

Error Handling

Tools should return structured errors:

try {
  // operation
} catch (error) {
  return {
    error: error instanceof Error ? error.message : "Unknown error",
    success: false
  }
}

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tools with tests
  4. Update documentation
  5. Submit a pull request

Credits

Built by nich (github.com/nirholas)