# Token Info MCP Server Requirements
## Project Overview
This MCP (Model Context Protocol) server provides comprehensive token smart contract information by integrating with blockchain tools and APIs. Built in Python following the [official MCP documentation](https://modelcontextprotocol.io/quickstart/server#python), it focuses on two primary capabilities: proxy contract analysis and access control role discovery.
The server integrates with Claude Desktop and other MCP clients to provide blockchain analysis capabilities through natural language interactions.
## Core Functionality
### 1. Proxy Contract Analysis
- **Tool**: `get_proxy_admin`
- **Description**: Comprehensive proxy admin and ownership analysis for upgradeable proxy contracts
- **Implementation**: Uses Foundry's `cast` commands for multi-step analysis
- **Features**:
- Detects proxy admin address using `cast admin`
- Identifies if proxy admin is a ProxyAdmin contract using `cast call owner()`
- Discovers ultimate owner of ProxyAdmin contracts
- Maps complete ownership chain from proxy to ultimate controller
- **Input**: Contract address, network/RPC URL, optional custom RPC URL
- **Output**: Complete ownership analysis including proxy admin, ProxyAdmin contract detection, ultimate owner, and ownership chain
### 2. Access Control Role Discovery
- **Tool**: `get_contract_roles`
- **Description**: Discovers AccessControl roles by analyzing contract event logs
- **Implementation**: Uses Etherscan API to query logs filtered by AccessControl topics
- **Input**: Contract address, network, optional block range
- **Output**: List of roles with their identifiers, role admins, and assigned accounts
### 3. Address Type Analysis
- **Tool**: `check_address_type`
- **Description**: Determines if an address is an EOA, regular contract, or multisig wallet
- **Implementation**: Uses JSON-RPC to check for contract code and multisig-specific function calls
- **Features**:
- EOA detection (externally owned accounts)
- Contract detection with bytecode analysis
- Multisig identification (Gnosis Safe, MultiSigWallet)
- Threshold and owner discovery for multisigs
- **Input**: Address, network/RPC URL, optional custom RPC URL
- **Output**: Address type classification with multisig details if applicable
## Technical Requirements
### Dependencies
- **Python 3.10+**: Programming language (following MCP official documentation)
- **MCP SDK**: Python MCP SDK 1.2.0 or higher (`mcp[cli]`)
- **Foundry**: Required for `cast` command functionality
- **Etherscan API**: For querying contract logs and events
- **httpx**: Async HTTP client for API requests
- **asyncio**: Python's built-in async runtime
- **typing**: Type hints for better code documentation
### Network Support
- **Ethereum Mainnet**: Primary network with configurable RPC endpoint
- **Testnets**: Goerli, Sepolia with dedicated configurations
- **L2 Networks**: Polygon, Arbitrum with built-in support
- **Custom RPC**: Full support for custom RPC endpoints via:
- Environment variables for persistent network configurations
- Direct RPC URL parameters in tool calls for one-off queries
- Automatic fallback to reliable public endpoints (e.g., llamarpc.com)
- **Multi-Provider Support**: Compatible with Alchemy, Infura, QuickNode, and other RPC providers
### Configuration
- **Environment Variables**:
- `ETHERSCAN_API_KEY`: Required for log queries
- `DEFAULT_RPC_URL`: Default RPC endpoint (used for mainnet if not overridden)
- `RATE_LIMIT_REQUESTS_PER_SECOND`: API rate limiting (default: 5)
- **Network-specific RPC URLs** (optional):
- `MAINNET_RPC_URL`: Custom Ethereum mainnet RPC URL
- `GOERLI_RPC_URL`: Custom Goerli testnet RPC URL
- `SEPOLIA_RPC_URL`: Custom Sepolia testnet RPC URL
- `POLYGON_RPC_URL`: Custom Polygon mainnet RPC URL
- `ARBITRUM_RPC_URL`: Custom Arbitrum mainnet RPC URL
- **RPC URL Flexibility**: Users can pass custom RPC URLs directly as tool parameters
- **Network Configuration**: Automatic mapping of network names to RPC URLs and Etherscan API endpoints
- **MCP Server Configuration**: Following [official MCP documentation](https://modelcontextprotocol.io/quickstart/server#python)
## MCP Tools Specification
### Tool 1: `get_proxy_admin`
```json
{
"name": "get_proxy_admin",
"description": "Get comprehensive proxy admin and ownership analysis for an upgradeable proxy contract",
"inputSchema": {
"type": "object",
"properties": {
"contract_address": {
"type": "string",
"description": "The contract address to check"
},
"network": {
"type": "string",
"description": "Network name (mainnet, goerli, sepolia, polygon, arbitrum) or use 'custom' with rpc_url",
"default": "mainnet"
},
"rpc_url": {
"type": "string",
"description": "Custom RPC URL to use (optional, overrides network setting)"
}
},
"required": ["contract_address"]
}
}
```
### Tool 2: `get_contract_roles`
```json
{
"name": "get_contract_roles",
"description": "Discover AccessControl roles and their assignments for a contract",
"inputSchema": {
"type": "object",
"properties": {
"contract_address": {
"type": "string",
"description": "The contract address to analyze"
},
"network": {
"type": "string",
"description": "Network name (mainnet, goerli, sepolia, polygon, arbitrum)",
"default": "mainnet"
},
"from_block": {
"type": "string",
"description": "Starting block number (default: contract creation block)",
"default": "earliest"
},
"to_block": {
"type": "string",
"description": "Ending block number (default: latest)",
"default": "latest"
}
},
"required": ["contract_address"]
}
}
```
### Tool 3: `check_address_type`
```json
{
"name": "check_address_type",
"description": "Analyze an address to determine if it's an EOA, contract, or multisig with detailed information",
"inputSchema": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "The Ethereum address to analyze"
},
"network": {
"type": "string",
"description": "Network name (mainnet, goerli, sepolia, polygon, arbitrum) or use 'custom' with rpc_url",
"default": "mainnet"
},
"rpc_url": {
"type": "string",
"description": "Custom RPC URL to use (optional, overrides network setting)"
}
},
"required": ["address"]
}
}
```
## Implementation Details
### Python MCP Server Implementation
- **FastMCP**: Use FastMCP class for automatic tool definition generation
- **Type Hints**: Leverage Python type hints and docstrings for MCP tool definitions
- **Async/Await**: Use Python's asyncio for concurrent operations
- **Error Handling**: Implement robust error handling with custom exceptions
- **Subprocess Management**: Use Python's subprocess module for `cast` command execution
### ProxyAdmin Contract Analysis
- **Multi-step Analysis**: Performs comprehensive ownership chain discovery
- **Accurate Proxy Detection**: Uses EIP-1967 storage slot verification to distinguish real proxies from regular contracts
- **Zero Address Handling**: Properly handles cases where `cast admin` returns zero address for non-proxy contracts
- **ProxyAdmin Detection**: Identifies when proxy admin is a contract vs EOA
- **Owner Resolution**: Calls `owner()` function on ProxyAdmin contracts to find ultimate controller
- **Chain Mapping**: Creates complete ownership chain from proxy → ProxyAdmin → Ultimate Owner
- **Security Insights**: Provides full picture of who can upgrade proxy contracts
### Address Type & Multisig Detection
- **EOA Detection**: Uses `eth_getCode` to check if address has contract bytecode
- **Contract Classification**: Distinguishes between regular contracts and multisig wallets
- **Gnosis Safe Support**: Detects Gnosis Safe multisigs by calling:
- `getOwners()` (selector: `0xa0e67e2b`) to retrieve owner addresses
- `getThreshold()` (selector: `0xe75235b8`) to get signature threshold
- **MultiSigWallet Support**: Detects older multisig implementations
- **Data Parsing**: Properly decodes ABI-encoded responses from multisig contracts
- **Fallback Handling**: Gracefully handles contracts that don't respond to multisig calls
### AccessControl Role Detection
- **Target Events**:
- `RoleGranted(bytes32 indexed role, address indexed account, address indexed sender)`
- `RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender)`
- `RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole)`
- **Common Role Identifiers**:
- `DEFAULT_ADMIN_ROLE`: `0x0000000000000000000000000000000000000000000000000000000000000000`
- `MINTER_ROLE`: `keccak256("MINTER_ROLE")`
- `PAUSER_ROLE`: `keccak256("PAUSER_ROLE")`
- `UPGRADER_ROLE`: `keccak256("UPGRADER_ROLE")`
### Error Handling
- **Proxy Detection**: Handle cases where contract is not a proxy
- **Network Connectivity**: Graceful handling of RPC/API failures
- **Rate Limiting**: Implement exponential backoff for API rate limits
- **Invalid Addresses**: Validate Ethereum addresses before processing
### Performance Considerations
- **Async Operations**: Use Python's asyncio and async/await for parallel API calls
- **HTTP Client**: Use httpx for efficient async HTTP requests
- **Caching**: Cache role discoveries to reduce API calls (using in-memory cache or Redis)
- **Pagination**: Handle large log result sets with pagination
- **Timeouts**: Set reasonable timeouts for external API calls
- **FastMCP**: Use FastMCP class for automatic tool definition generation via type hints
## Output Format
### Proxy Admin Response
```json
{
"contract_address": "0x...",
"network": "mainnet",
"is_proxy": true,
"proxy_admin": "0x...",
"proxy_type": "TransparentUpgradeableProxy",
"proxy_admin_owner": "0x...",
"is_proxy_admin_contract": true,
"ownership_chain": [
"0x... (proxy contract)",
"0x... (proxy admin contract)",
"0x... (ultimate owner)"
]
}
```
### Contract Roles Response
```json
{
"contract_address": "0x...",
"network": "mainnet",
"roles": [
{
"role_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"role_name": "DEFAULT_ADMIN_ROLE",
"admin_role": "0x0000000000000000000000000000000000000000000000000000000000000000",
"members": ["0x..."],
"total_grants": 5,
"total_revokes": 1
}
],
"total_roles": 3,
"block_range": {
"from": "earliest",
"to": "latest"
}
}
```
### Address Type Response
```json
{
"address": "0x...",
"network": "mainnet",
"address_type": "multisig",
"is_contract": true,
"contract_type": "gnosis_safe",
"multisig_info": {
"type": "gnosis_safe",
"threshold": 3,
"owners": [
"0x...",
"0x...",
"0x..."
],
"owner_count": 5
}
}
```
## Testing Requirements
### Unit Tests
- Use `pytest` for Python testing framework
- `pytest-asyncio` for async test support
- Proxy admin detection with various proxy types
- Role parsing from event logs
- Address type detection (EOA, contract, multisig)
- Multisig threshold and owner parsing
- Error handling for invalid inputs
- Network configuration validation
### Integration Tests
- End-to-end testing with real contracts
- Etherscan API integration testing
- Cast command execution testing
- Rate limiting behavior validation
- MCP server integration testing with Claude Desktop
### Test Contracts
- Deploy test contracts with known proxy configurations
- Deploy contracts with AccessControl for role testing
- Test edge cases (non-proxy contracts, contracts without roles)
### MCP Testing
- Test server configuration with Claude Desktop
- Validate tool registration and execution
- Test error handling and edge cases through MCP protocol
## Security Considerations
- **API Key Management**: Secure storage and rotation of API keys
- **Input Validation**: Sanitize all user inputs
- **Rate Limiting**: Respect API provider rate limits
- **Error Information**: Avoid exposing sensitive system information in errors
- **Multisig Detection**: Verify multisig signatures and thresholds for security analysis
## Known Issues & Debugging
### Cast Command Dependency
- **Issue**: The `get_proxy_admin` tool requires Foundry's `cast` command to be installed and available in the environment
- **Symptoms**: "Cast command not found - ensure Foundry is installed" error
- **Root Cause**: Either Foundry is not installed or not available in the PATH when the MCP server runs
- **Debugging Steps**:
1. Verify Foundry installation: `cast --version`
2. Check PATH availability: `which cast`
3. For Claude Desktop: Verify the Python executable has access to system PATH
4. Test with full path: `/usr/local/bin/cast --version`
- **Solution**: Install Foundry following the [official installation guide](https://book.getfoundry.sh/getting-started/installation)
### Claude Desktop Environment PATH Issues
- **Issue**: Claude Desktop may not have access to system-installed tools like `cast`
- **Symptoms**: Commands work in terminal but fail in Claude Desktop
- **Debugging**: Add logging to check environment variables in MCP server
- **Solutions**:
- Use full path to Python executable in `claude_desktop_config.json`
- Set up virtual environment with proper PATH configuration
- Use shell wrapper script that sources proper environment
### False Proxy Detection
- **Issue**: Regular contracts incorrectly identified as proxies with zero admin
- **Symptoms**: Non-proxy contracts showing as "TransparentUpgradeableProxy with renounced admin"
- **Root Cause**: `cast admin` command returning zero address for non-proxy contracts
- **Fix**: Added EIP-1967 storage slot verification to distinguish real proxies from regular contracts
- **Example**: USDT contract (0xdAC17F958D2ee523a2206206994597C13D831ec7) correctly identified as non-proxy
## Future Enhancements
### Additional Tools
- **Token Metadata**: `get_token_metadata` for ERC-20/ERC-721 information
- **Ownership Info**: `get_ownership_info` for Ownable contracts
- **Upgrade History**: `get_upgrade_history` for proxy upgrade events
- **Token Holders**: `get_top_holders` for token distribution analysis
- **Enhanced Proxy Analysis**: Implement proxy admin detection using direct RPC calls in addition to cast commands
### Advanced Features
- **Multi-network Queries**: Query same contract across different networks
- **Historical Analysis**: Compare role assignments over time
- **Visualization**: Generate role hierarchy diagrams
- **Alerts**: Monitor role changes in real-time
- **Multisig Monitoring**: Track multisig threshold changes and owner updates
- **Enhanced Multisig Support**: Support for more multisig implementations (Coinbase, Argent, etc.)
## Project Structure
```
token-info-mcp/
├── README.md
├── requirements.txt
├── token_info_server.py # Main MCP server implementation
├── config.py # Configuration management
├── etherscan_client.py # Etherscan API client
├── cast_executor.py # Cast command wrapper
├── types.py # Data models and types
├── cache.py # Caching functionality
└── tests/
├── test_proxy_admin.py
├── test_contract_roles.py
└── test_integration.py
```
## Running the Server
### Development Setup
```bash
# Install dependencies
pip install -r requirements.txt
# Run server for testing
python token_info_server.py
```
### Claude Desktop Integration
Following the [official MCP documentation](https://modelcontextprotocol.io/quickstart/server#python), configure `claude_desktop_config.json`:
```json
{
"mcpServers": {
"token-info": {
"command": "python",
"args": ["/absolute/path/to/token_info_server.py"]
}
}
}
```
## Usage Examples
### Basic Address Type Check
```bash
# Check if an address is EOA, contract, or multisig
{
"tool": "check_address_type",
"arguments": {
"address": "0x...",
"network": "mainnet"
}
}
```
### Multisig Analysis
```bash
# Analyze a known multisig address
{
"tool": "check_address_type",
"arguments": {
"address": "0x...",
"network": "mainnet",
"rpc_url": "https://eth.llamarpc.com"
}
}
```
### Proxy Admin Analysis
```bash
# Get comprehensive proxy admin and ownership analysis
{
"tool": "get_proxy_admin",
"arguments": {
"contract_address": "0x259338656198eC7A76c729514D3CB45Dfbf768A1",
"network": "mainnet",
"rpc_url": "https://eth.llamarpc.com"
}
}
```
### Complete Ownership Analysis Workflow
```bash
# Step 1: Get proxy admin information
{
"tool": "get_proxy_admin",
"arguments": {
"contract_address": "0x...",
"network": "mainnet"
}
}
# Step 2: Analyze proxy admin address type (separate feature)
{
"tool": "check_address_type",
"arguments": {
"address": "0x...", # proxy admin address from step 1
"network": "mainnet"
}
}
# Step 3: Get contract roles (if applicable)
{
"tool": "get_contract_roles",
"arguments": {
"contract_address": "0x...",
"network": "mainnet"
}
}
```
## Success Criteria
1. **Accuracy**: 100% accurate proxy admin detection for supported proxy types
2. **Completeness**: Discover all AccessControl roles for contracts using standard OpenZeppelin implementation
3. **Address Analysis**: Correctly identify EOAs, contracts, and multisigs with >95% accuracy
4. **Multisig Detection**: Successfully detect Gnosis Safe and MultiSigWallet thresholds and owners
5. **Performance**: Sub-5 second response times for typical queries
6. **Reliability**: 99.9% uptime with proper error handling
7. **Usability**: Clear, structured output format suitable for both human and programmatic consumption
8. **MCP Compliance**: Full compatibility with MCP protocol and Claude Desktop integration