6rz6
Switch to FastAPI MCP server (remove Gradio)
38871a5
import json
import requests
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Any, Dict, Optional
import uvicorn
# Hyperliquid API base URL
HYPERLIQUID_API = "https://api.hyperliquid.xyz/info"
# FastAPI app
app = FastAPI(
title="Hyperliquid MCP Server",
description="MCP server providing real-time trading data from Hyperliquid decentralized exchange",
version="1.0.0"
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Tool definitions
TOOLS = [
{
"name": "get_all_mids",
"description": "Get all market prices from Hyperliquid",
"inputSchema": {"type": "object", "properties": {}}
},
{
"name": "get_user_state",
"description": "Get user account state and positions",
"inputSchema": {
"type": "object",
"properties": {
"address": {"type": "string", "description": "User wallet address"}
},
"required": ["address"]
}
},
{
"name": "get_recent_trades",
"description": "Get recent trades for a specific coin",
"inputSchema": {
"type": "object",
"properties": {
"coin": {"type": "string", "description": "Trading pair symbol (e.g., 'BTC')"},
"n": {"type": "number", "description": "Number of trades to retrieve (1-1000)", "default": 100}
},
"required": ["coin"]
}
},
{
"name": "get_l2_snapshot",
"description": "Get L2 order book snapshot",
"inputSchema": {
"type": "object",
"properties": {
"coin": {"type": "string", "description": "Trading pair symbol (e.g., 'BTC')"}
},
"required": ["coin"]
}
},
{
"name": "get_candles",
"description": "Get historical candlestick data",
"inputSchema": {
"type": "object",
"properties": {
"coin": {"type": "string", "description": "Trading pair symbol (e.g., 'BTC')"},
"interval": {"type": "string", "description": "Time interval (e.g., '1m', '5m', '1h', '1d')", "default": "1h"},
"limit": {"type": "number", "description": "Number of candles to retrieve (1-5000)", "default": 500}
},
"required": ["coin"]
}
},
{
"name": "get_meta",
"description": "Get market metadata",
"inputSchema": {"type": "object", "properties": {}}
},
{
"name": "get_funding_rates",
"description": "Get funding rates for perpetual contracts",
"inputSchema": {
"type": "object",
"properties": {
"coin": {"type": "string", "description": "Trading pair symbol (optional)"}
}
}
},
{
"name": "get_open_interest",
"description": "Get open interest data",
"inputSchema": {
"type": "object",
"properties": {
"coin": {"type": "string", "description": "Trading pair symbol"}
},
"required": ["coin"]
}
}
]
# Request models
class ToolCallRequest(BaseModel):
name: str
arguments: Optional[Dict[str, Any]] = {}
# Tool implementations
def get_all_mids():
"""Get all market prices from Hyperliquid"""
try:
response = requests.post(HYPERLIQUID_API, json={"type": "allMids"})
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
def get_user_state(address: str):
"""Get user account state and positions"""
try:
response = requests.post(HYPERLIQUID_API, json={
"type": "userState",
"user": address
})
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
def get_recent_trades(coin: str, n: int = 100):
"""Get recent trades for a specific coin"""
try:
response = requests.post(HYPERLIQUID_API, json={
"type": "trades",
"coin": coin,
"n": n
})
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
def get_l2_snapshot(coin: str):
"""Get L2 order book snapshot"""
try:
response = requests.post(HYPERLIQUID_API, json={
"type": "l2Book",
"coin": coin
})
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
def get_candles(coin: str, interval: str = "1h", limit: int = 500):
"""Get historical candlestick data"""
try:
response = requests.post(HYPERLIQUID_API, json={
"type": "candles",
"coin": coin,
"interval": interval,
"limit": limit
})
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
def get_meta():
"""Get market metadata"""
try:
response = requests.post(HYPERLIQUID_API, json={"type": "meta"})
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
def get_funding_rates(coin: str = None):
"""Get funding rates for perpetual contracts"""
try:
payload = {"type": "fundingRate"}
if coin:
payload["coin"] = coin
response = requests.post(HYPERLIQUID_API, json=payload)
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
def get_open_interest(coin: str):
"""Get open interest data"""
try:
response = requests.post(HYPERLIQUID_API, json={
"type": "openInterest",
"coin": coin
})
response.raise_for_status()
return {"success": True, "data": response.json()}
except Exception as e:
return {"success": False, "error": str(e)}
# API endpoints
@app.get("/")
async def root():
"""Root endpoint with API information"""
return {
"name": "Hyperliquid MCP Server",
"version": "1.0.0",
"description": "MCP server providing real-time trading data from Hyperliquid decentralized exchange",
"endpoints": {
"health": "/health",
"tools": "/mcp/tools",
"call_tool": "/mcp/call"
}
}
@app.get("/health")
async def health():
"""Health check endpoint"""
return {"status": "healthy", "service": "hyperliquid-mcp-server"}
@app.get("/mcp/tools")
async def list_tools():
"""List all available MCP tools"""
return {"tools": TOOLS}
@app.post("/mcp/call")
async def call_tool(request: ToolCallRequest):
"""Execute an MCP tool with provided arguments"""
tool_name = request.name
arguments = request.arguments or {}
# Map tool names to functions
tool_functions = {
"get_all_mids": get_all_mids,
"get_user_state": lambda: get_user_state(arguments.get("address", "")),
"get_recent_trades": lambda: get_recent_trades(
arguments.get("coin", ""),
arguments.get("n", 100)
),
"get_l2_snapshot": lambda: get_l2_snapshot(arguments.get("coin", "")),
"get_candles": lambda: get_candles(
arguments.get("coin", ""),
arguments.get("interval", "1h"),
arguments.get("limit", 500)
),
"get_meta": get_meta,
"get_funding_rates": lambda: get_funding_rates(arguments.get("coin")),
"get_open_interest": lambda: get_open_interest(arguments.get("coin", ""))
}
if tool_name not in tool_functions:
raise HTTPException(status_code=404, detail=f"Tool '{tool_name}' not found")
try:
# Execute the tool
if tool_name in ["get_user_state", "get_recent_trades", "get_l2_snapshot", "get_candles", "get_funding_rates", "get_open_interest"]:
result = tool_functions[tool_name]()
else:
result = tool_functions[tool_name]()
if result["success"]:
return {
"content": [
{
"type": "text",
"text": json.dumps(result["data"], indent=2)
}
]
}
else:
raise HTTPException(status_code=400, detail=result["error"])
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# Update requirements.txt for FastAPI
# fastapi
# uvicorn
# requests
# pydantic
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)