server_fastmcp.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """FastMCP transport wrapper for crypto tools (compliance-first)."""
  2. from fastapi import FastAPI
  3. from mcp.server.fastmcp import FastMCP
  4. from mcp.server.transport_security import TransportSecuritySettings
  5. import services
  6. from mcp_tools import MCP_TOOLS
  7. from cache import get_cache_stats
  8. mcp = FastMCP(
  9. "crypto-mcp",
  10. transport_security=TransportSecuritySettings(
  11. enable_dns_rebinding_protection=False,
  12. ),
  13. )
  14. @mcp.tool(
  15. description=(
  16. "Return the latest USD spot price for a symbol (e.g., BTC, ETH). Uses Binance as primary data source "
  17. "with CoinGecko fallback; payload includes symbol, price, and provider timestamp."
  18. )
  19. )
  20. async def get_price(symbol: str):
  21. return await services.get_price(symbol)
  22. @mcp.tool(
  23. description=(
  24. "Fetch OHLCV candles for a symbol/timeframe (1m, 5m, 15m, 1h, 4h, 1d). "
  25. "Limit can range from 1-500 candles; candles are returned oldest→newest with [timestamp, open, high, low, close, volume]."
  26. )
  27. )
  28. async def get_ohlcv(symbol: str, timeframe: str = "1h", limit: int = 100):
  29. return await services.get_ohlcv(symbol, timeframe, limit)
  30. @mcp.tool(
  31. description=(
  32. "Compute a technical indicator for a symbol/timeframe. Supported names: rsi, ema, sma, macd, atr, bollinger, vwap. "
  33. "Pass params such as period, fast_period/slow_period/signal_period (MACD) or multiplier (Bollinger). Output echoes the indicator name and value object."
  34. )
  35. )
  36. async def get_indicator(symbol: str, indicator: str, timeframe: str = "1h", params: dict | None = None):
  37. return await services.get_indicator(symbol, indicator, timeframe, params or {})
  38. @mcp.tool(
  39. description=(
  40. "Return a lightweight 1h snapshot: price, RSI14, EMA20/50/200, MACD histogram, ATR (abs & %), Bollinger(20,2) bands, "
  41. "rolling VWAP, and a simple trend_bias derived from EMA20 vs EMA50. Ideal for dashboards needing core stats."
  42. )
  43. )
  44. async def get_market_snapshot(symbol: str):
  45. return await services.get_market_snapshot(symbol)
  46. @mcp.tool(
  47. description=(
  48. "List top 24h movers (gainers/losers) from the provider feed. Limit 1-50 entries; each item includes symbol, price change %, "
  49. "and supporting metadata for quick leaderboard views."
  50. )
  51. )
  52. async def get_top_movers(limit: int = 10):
  53. return await services.get_top_movers(limit)
  54. @mcp.tool(
  55. description=(
  56. "Describe what this server supports: indicator catalog (with params/defaults) plus allowed timeframes "
  57. "for OHLCV/indicator/regime calls. Use this to self-discover valid inputs before invoking other tools."
  58. )
  59. )
  60. async def get_capabilities():
  61. return await services.get_capabilities()
  62. @mcp.tool(
  63. description=(
  64. "Return a composite regime snapshot that merges trend (EMA20/50, SMA200), momentum (RSI, MACD histogram), "
  65. "volatility (ATR + % of price), Bollinger bands, and VWAP for the requested symbol/timeframe. "
  66. "Fields include derived bull/bear/range states for quick downstream logic."
  67. )
  68. )
  69. async def get_regime(symbol: str, timeframe: str = "1h"):
  70. return await services.get_regime(symbol, timeframe)
  71. app = FastAPI(title="Crypto MCP Server")
  72. app.mount("/mcp", mcp.sse_app())
  73. @app.get("/")
  74. def root():
  75. return {"status": "ok", "transport": "fastmcp+sse", "mount": "/mcp", "tools": [t["name"] for t in MCP_TOOLS]}
  76. @app.get("/health")
  77. def health():
  78. return {"status": "ok", "cache": get_cache_stats(), "tools": [t["name"] for t in MCP_TOOLS]}