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:
| 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
Manual Testing
Use the MCP Inspector for interactive testing:
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
- Fork the repository
- Create a feature branch
- Add tools with tests
- Update documentation
- Submit a pull request
Credits
Built by nich (github.com/nirholas)