Skip to main content
Glama
server.py16.6 kB
""" DeBank MCP Server FastMCP server providing tools for interacting with the DeBank API. Enables AI assistants to access DeFi portfolio data, token information, and blockchain analytics. Usage: # Run with uvx uvx mcp-server-debank # Run directly python -m mcp_server_debank.server Environment Variables: DEBANK_ACCESS_KEY: DeBank API access key (required) """ import os import sys from typing import Optional from fastmcp import FastMCP from dotenv import load_dotenv from .client import DeBankClient from .portfolio_tools import register_portfolio_tools from .advanced_tools import register_advanced_tools # Load environment variables from .env file load_dotenv() # Initialize FastMCP server mcp = FastMCP( "DeBank API", dependencies=["httpx", "pydantic"] ) # Global client instance (will be initialized on first use) _client: Optional[DeBankClient] = None def get_client() -> DeBankClient: """ Get or create the DeBank API client instance. Returns: Initialized DeBankClient instance Raises: ValueError: If DEBANK_ACCESS_KEY environment variable is not set """ global _client if _client is None: access_key = os.getenv("DEBANK_ACCESS_KEY") if not access_key: raise ValueError( "DEBANK_ACCESS_KEY environment variable is required. " "Please set it in your environment or .env file." ) _client = DeBankClient(access_key=access_key) return _client # ============================================================================ # REGISTER PORTFOLIO TOOLS (Agent 3) # ============================================================================ # Portfolio & Data Tools: User tokens, NFTs, protocols, history, approvals register_portfolio_tools(mcp, get_client) # ============================================================================ # REGISTER ADVANCED TOOLS (Agent 4 + Agent 5) # ============================================================================ # Advanced Analytics: Net curve, pools, transactions, gas, API usage, social register_advanced_tools(mcp, get_client) # ============================================================================ # CORE DEBANK API TOOLS # ============================================================================ # Implemented by Agent 2: Core Tools Implementation # All tools provide comprehensive error handling and detailed documentation @mcp.tool() async def debank_get_chains(chain_id: str = None) -> dict: """Get blockchain network information from DeBank. This tool retrieves comprehensive information about blockchain networks supported by DeBank, including network details, native tokens, and protocol counts. Args: chain_id: Optional chain ID. If provided, returns single chain info. If None, returns all supported chains. Examples: "eth", "bsc", "polygon", "arbitrum", "optimism" Returns: Chain object(s) with the following structure: { "id": "eth", "name": "Ethereum", "logo_url": "https://...", "native_token_id": "eth", "wrapped_token_id": "0xc02...", "is_support_pre_exec": true } Examples: - Get all chains: debank_get_chains() - Get Ethereum: debank_get_chains(chain_id="eth") - Get Binance Smart Chain: debank_get_chains(chain_id="bsc") """ try: client = get_client() if chain_id: # Get specific chain result = await client.get(f"/v1/chain", params={"id": chain_id}) return {"chain": result} else: # Get all chains result = await client.get("/v1/chain/list") return {"chains": result, "count": len(result)} except ValueError as e: return { "error": "Validation error", "message": str(e), "type": "validation_error" } except Exception as e: return { "error": "Unexpected error", "message": str(e), "type": "unknown_error" } @mcp.tool() async def debank_get_protocols( protocol_id: str = None, chain_id: str = None, all_chains: bool = False ) -> dict: """Get DeFi protocol information from DeBank. This tool retrieves comprehensive information about DeFi protocols, including TVL (Total Value Locked), supported chains, and protocol metadata. Args: protocol_id: Optional specific protocol ID (e.g., 'uniswap', 'aave', 'compound', 'curve', 'lido', 'makerdao') chain_id: Optional filter by chain (e.g., 'eth', 'bsc', 'polygon') all_chains: If True, get all protocols across all chains Returns: Protocol object(s) with the following structure: { "id": "uniswap", "name": "Uniswap", "chain": "eth", "logo_url": "https://...", "tvl": 5234567890, "portfolio_item_list": [...] } Examples: - Get all protocols on Ethereum: debank_get_protocols(chain_id="eth") - Get specific protocol: debank_get_protocols(protocol_id="uniswap") - Get all protocols: debank_get_protocols(all_chains=True) - Get Aave on Polygon: debank_get_protocols(protocol_id="aave", chain_id="polygon") """ try: client = get_client() # Validate mutually exclusive parameters if protocol_id and all_chains: raise ValueError("Cannot specify both protocol_id and all_chains") if protocol_id: # Get specific protocol params = {"id": protocol_id} if chain_id: params["chain_id"] = chain_id result = await client.get("/v1/protocol", params=params) return {"protocol": result} elif all_chains: # Get all protocols across all chains result = await client.get("/v1/protocol/all_list") return {"protocols": result, "count": len(result) if isinstance(result, list) else 0} elif chain_id: # Get protocols on specific chain result = await client.get("/v1/protocol/list", params={"chain_id": chain_id}) return {"protocols": result, "count": len(result) if isinstance(result, list) else 0} else: # Default: get all protocols result = await client.get("/v1/protocol/all_list") return {"protocols": result, "count": len(result) if isinstance(result, list) else 0} except ValueError as e: return { "error": "Validation error", "message": str(e), "type": "validation_error" } except Exception as e: return { "error": "Unexpected error", "message": str(e), "type": "unknown_error" } @mcp.tool() async def debank_get_token_info( chain_id: str, token_id: str = None, token_ids: list = None, date: str = None ) -> dict: """Get token information and prices from DeBank. This tool retrieves comprehensive token data including current prices, historical prices, decimals, symbols, logos, and verification status. Args: chain_id: Blockchain ID (required). Examples: "eth", "bsc", "polygon" token_id: Single token contract address (0x prefixed) token_ids: List of token addresses (max 100, 0x prefixed) date: Historical date in YYYY-MM-DD format for price lookup Returns: Token object(s) with the following structure: { "id": "0xdac17f958d2ee523a2206206994597c13d831ec7", "chain": "eth", "name": "Tether USD", "symbol": "USDT", "price": 1.0, "decimals": 6, "logo_url": "https://...", "is_verified": true, "is_core": true } Examples: - Get USDT: debank_get_token_info( chain_id="eth", token_id="0xdac17f958d2ee523a2206206994597c13d831ec7" ) - Get multiple tokens: debank_get_token_info( chain_id="eth", token_ids=["0xdac17f958d2ee523a2206206994597c13d831ec7", "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"] ) - Get historical price: debank_get_token_info( chain_id="eth", token_id="0xdac17f958d2ee523a2206206994597c13d831ec7", date="2024-01-15" ) """ try: client = get_client() # Validate mutually exclusive parameters if token_id and token_ids: raise ValueError("Cannot specify both token_id and token_ids") if not token_id and not token_ids: raise ValueError("Must specify either token_id or token_ids") if date and token_id: # Get historical price for single token result = await client.get( "/v1/token/history_price", params={"chain_id": chain_id, "id": token_id, "date": date} ) return {"token": result, "historical": True, "date": date} elif token_ids: # Get multiple tokens (max 100) if len(token_ids) > 100: raise ValueError("token_ids list cannot exceed 100 addresses") ids_param = ",".join(token_ids) result = await client.get( "/v1/token/list_by_ids", params={"chain_id": chain_id, "ids": ids_param} ) if isinstance(result, list): return {"tokens": result, "count": len(result)} else: return {"tokens": result} else: # Get single token current info result = await client.get( "/v1/token", params={"chain_id": chain_id, "id": token_id} ) return {"token": result} except ValueError as e: return { "error": "Validation error", "message": str(e), "type": "validation_error" } except Exception as e: return { "error": "Unexpected error", "message": str(e), "type": "unknown_error" } @mcp.tool() async def debank_get_token_holders( chain_id: str, token_id: str, limit: int = 100, offset: int = 0 ) -> dict: """Get top holders of a token from DeBank. This tool retrieves the largest holders of a specific token, useful for analyzing token distribution and whale activity. Args: chain_id: Blockchain ID (required). Examples: "eth", "bsc", "polygon" token_id: Token contract address (required, must start with 0x) limit: Number of holders to return (max 100, default 100) offset: Starting position for pagination (max 10000, default 0) Returns: Object with holder data: { "holders": [ { "address": "0x5853ed4f26a3fcea565b3fbc698bb19cdf6deb85", "amount": "1234567890000000000", "usd_value": 1234567.89 }, ... ], "total_count": 50000 } Examples: - Get top 100 USDT holders: debank_get_token_holders( chain_id="eth", token_id="0xdac17f958d2ee523a2206206994597c13d831ec7" ) - Get next 50 holders: debank_get_token_holders( chain_id="eth", token_id="0xdac17f958d2ee523a2206206994597c13d831ec7", limit=50, offset=100 ) """ try: client = get_client() # Validate required parameters if not token_id: raise ValueError("token_id is required") if not token_id.startswith("0x"): raise ValueError("token_id must start with 0x") # Validate limit if limit < 1 or limit > 100: raise ValueError("limit must be between 1 and 100") # Validate offset if offset < 0 or offset > 10000: raise ValueError("offset must be between 0 and 10000") result = await client.get( "/v1/token/top_holders", params={ "chain_id": chain_id, "id": token_id, "limit": limit, "start": offset } ) # Wrap response if it's a list if isinstance(result, list): return {"holders": result, "count": len(result)} else: # API might return object with holders and total_count return result except ValueError as e: return { "error": "Validation error", "message": str(e), "type": "validation_error" } except Exception as e: return { "error": "Unexpected error", "message": str(e), "type": "unknown_error" } @mcp.tool() async def debank_get_user_balance( address: str, chain_id: str = None ) -> dict: """Get user's total balance or chain-specific balance from DeBank. This tool retrieves comprehensive portfolio data for a wallet address, including total balance across all chains or balance on a specific chain. Args: address: User's wallet address (required, must start with 0x) chain_id: Optional chain ID. If provided, returns balance on that chain. If None, returns total balance across all chains. Examples: "eth", "bsc", "polygon", "arbitrum" Returns: Balance information: - If chain_id is None: { "total_usd_value": 123456.78, "chain_list": [ { "id": "eth", "usd_value": 98765.43 }, ... ] } - If chain_id is specified: { "usd_value": 98765.43, "chain": "eth" } Examples: - Get total balance: debank_get_user_balance( address="0x5853ed4f26a3fcea565b3fbc698bb19cdf6deb85" ) - Get Ethereum balance: debank_get_user_balance( address="0x5853ed4f26a3fcea565b3fbc698bb19cdf6deb85", chain_id="eth" ) - Get BSC balance: debank_get_user_balance( address="0x5853ed4f26a3fcea565b3fbc698bb19cdf6deb85", chain_id="bsc" ) """ try: client = get_client() # Validate required parameters if not address: raise ValueError("address is required") if not address.startswith("0x"): raise ValueError("address must start with 0x") if chain_id: # Get balance on specific chain return await client.get( "/v1/user/chain_balance", params={"id": address, "chain_id": chain_id} ) else: # Get total balance across all chains return await client.get("/v1/user/total_balance", params={"id": address}) except ValueError as e: return { "error": "Validation error", "message": str(e), "type": "validation_error" } except Exception as e: return { "error": "Unexpected error", "message": str(e), "type": "unknown_error" } def main(): """ Entry point for running the MCP server. This function is called when running: - uvx mcp-server-debank - python -m mcp_server_debank.server """ try: # Verify access key is configured if not os.getenv("DEBANK_ACCESS_KEY"): print( "ERROR: DEBANK_ACCESS_KEY environment variable is not set.\n" "Please set it before running the server:\n\n" " export DEBANK_ACCESS_KEY='your_key_here'\n" " # or create a .env file with:\n" " DEBANK_ACCESS_KEY=your_key_here\n", file=sys.stderr ) sys.exit(1) # Run the MCP server mcp.run() except KeyboardInterrupt: print("\nShutting down DeBank MCP server...", file=sys.stderr) sys.exit(0) except Exception as e: print(f"ERROR: Failed to start server: {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/caiovicentino/debank-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server