crypto-feargreed-mcp
by kukapay
from mcp.server.fastmcp import FastMCP, Context
import httpx
import asyncio
from typing import Dict, List
from datetime import datetime
# Initialize the MCP server
mcp = FastMCP("CryptoFearGreed", dependencies=["httpx"])
# API endpoint for Crypto Fear & Greed Index
API_URL = "https://api.alternative.me/fng/"
# Resource to get current Fear & Greed Index
@mcp.resource("fng://current")
async def get_current_fng() -> str:
"""Get the current Crypto Fear & Greed Index"""
try:
async with httpx.AsyncClient() as client:
response = await client.get(API_URL, params={"limit": 1})
response.raise_for_status()
data = response.json()["data"][0]
timestamp = datetime.fromtimestamp(int(data["timestamp"]))
return (
f"Crypto Fear & Greed Index (as of {timestamp} UTC):\n"
f"Value: {data['value']}\n"
f"Classification: {data['value_classification']}"
)
except httpx.HTTPStatusError as e:
return f"Error fetching current FNG: {str(e)}"
except Exception as e:
return f"Unexpected error: {str(e)}"
# Resource to get historical Fear & Greed Index
@mcp.resource("fng://history/{days}")
async def get_historical_fng(days: str) -> str:
"""Get historical Crypto Fear & Greed Index for specified number of days"""
try:
days_int = int(days)
if days_int <= 0:
raise ValueError("Days must be a positive integer")
async with httpx.AsyncClient() as client:
response = await client.get(API_URL, params={"limit": days_int})
response.raise_for_status()
data = response.json()["data"]
result = ["Historical Crypto Fear & Greed Index:"]
for entry in reversed(data): # Latest first
timestamp = datetime.fromtimestamp(int(entry["timestamp"]))
result.append(
f"{timestamp} UTC: {entry['value']} ({entry['value_classification']})"
)
return "\n".join(result)
except ValueError as e:
return f"Error: {str(e)}"
except httpx.HTTPStatusError as e:
return f"Error fetching historical FNG: {str(e)}"
except Exception as e:
return f"Unexpected error: {str(e)}"
# Tool version of get_current_fng
@mcp.tool()
async def get_current_fng_tool(ctx: Context) -> str:
"""Get the current Crypto Fear & Greed Index as a tool."""
ctx.info("Fetching current Fear & Greed Index")
return await get_current_fng()
# Tool version of get_historical_fng
@mcp.tool()
async def get_historical_fng_tool(days: int, ctx: Context) -> str:
"""
Get historical Fear & Greed Index for specified number of days as a tool.
Parameters:
days (int): Number of days to retrieve (must be a positive integer).
Returns:
str: Historical Fear & Greed Index values for the specified period.
"""
ctx.info(f"Fetching historical Fear & Greed Index for {days} days")
return await get_historical_fng(str(days))
# Tool to analyze trends in the Fear & Greed Index
@mcp.tool()
async def analyze_fng_trend(days: int, ctx: Context) -> str:
"""
Analyze trends in Crypto Fear & Greed Index over specified days.
Parameters:
days (int): Number of days to analyze (must be a positive integer).
Returns:
str: A string containing the analysis results, including latest value,
average value, trend direction, and number of data points analyzed.
"""
if days <= 0:
return "Error: Days must be a positive integer"
ctx.info(f"Fetching {days} days of FNG data")
try:
async with httpx.AsyncClient() as client:
response = await client.get(API_URL, params={"limit": days})
response.raise_for_status()
data = response.json()["data"]
if not data:
return "Error: No data available"
values = [int(entry["value"]) for entry in data]
total_entries = len(values)
# Calculate statistics
avg = sum(values) / total_entries
trend = "rising" if values[0] > values[-1] else "falling" if values[0] < values[-1] else "stable"
latest = data[0] # Most recent entry
result = [
f"Fear & Greed Index Analysis ({days} days):",
f"Latest Value: {latest['value']} ({latest['value_classification']}) "
f"at {datetime.fromtimestamp(int(latest['timestamp']))} UTC",
f"Average Value: {avg:.1f}",
f"Trend: {trend}",
f"Data points analyzed: {total_entries}"
]
return "\n".join(result)
except httpx.HTTPStatusError as e:
return f"Error fetching data: {str(e)}"
except Exception as e:
return f"Unexpected error: {str(e)}"
# Prompt for interpreting the index
@mcp.prompt()
def interpret_fng(value: str) -> str:
"""Generate an interpretation of a Fear & Greed Index value"""
return (
f"Please interpret this Crypto Fear & Greed Index value and explain what it means "
f"for cryptocurrency markets (specifically Bitcoin):\n\n{value}"
)
# Run the server
if __name__ == "__main__":
mcp.run()