feat(V2): migrazione sentiment completa (read-only, env ignored)
- exchanges/sentiment/{client,fetchers,tools}.py: SentimentClient wrapper stateless (cryptopanic_key, lunarcrush_key)
- routers/sentiment.py: 9 tool POST sotto /mcp-sentiment (news, social, funding, OI, liquidations, cointegration)
- exchanges/__init__.py: branch builder per sentiment (env ignored)
- tests/unit/exchanges/sentiment: migrato test_fetchers, scartato test_server_acl V1-only
- tests/unit/test_exchanges_builder.py: aggiunto test_build_client_sentiment_no_env_distinction
- fetchers.py: env var lookup allineato a LUNARCRUSH_KEY (con fallback LUNARCRUSH_API_KEY)
241 test passano.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"""Router /mcp-sentiment/* — read-only data provider.
|
||||
|
||||
Sentiment non distingue testnet/mainnet (CryptoPanic, LunarCrush e gli endpoint
|
||||
pubblici di funding/OI multi-exchange sono unici), ma manteniamo la signature
|
||||
`Environment` per uniformità con gli altri router. Tutti i tool sono READ —
|
||||
niente write, niente leverage_cap.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
|
||||
from cerbero_mcp.client_registry import ClientRegistry
|
||||
from cerbero_mcp.exchanges.sentiment import tools as t
|
||||
from cerbero_mcp.exchanges.sentiment.client import SentimentClient
|
||||
|
||||
Environment = Literal["testnet", "mainnet"]
|
||||
|
||||
|
||||
def get_environment(request: Request) -> Environment:
|
||||
return request.state.environment
|
||||
|
||||
|
||||
async def get_sentiment_client(
|
||||
request: Request, env: Environment = Depends(get_environment)
|
||||
) -> SentimentClient:
|
||||
registry: ClientRegistry = request.app.state.registry
|
||||
return await registry.get("sentiment", env)
|
||||
|
||||
|
||||
def make_router() -> APIRouter:
|
||||
r = APIRouter(prefix="/mcp-sentiment", tags=["sentiment"])
|
||||
|
||||
@r.post("/tools/get_crypto_news")
|
||||
async def _get_crypto_news(
|
||||
params: t.GetCryptoNewsReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_crypto_news(client, params)
|
||||
|
||||
@r.post("/tools/get_world_news")
|
||||
async def _get_world_news(
|
||||
params: t.GetWorldNewsReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_world_news(client, params)
|
||||
|
||||
@r.post("/tools/get_social_sentiment")
|
||||
async def _get_social_sentiment(
|
||||
params: t.GetSocialSentimentReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_social_sentiment(client, params)
|
||||
|
||||
@r.post("/tools/get_funding_rates")
|
||||
async def _get_funding_rates(
|
||||
params: t.GetFundingRatesReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_funding_rates(client, params)
|
||||
|
||||
@r.post("/tools/get_funding_arb_spread")
|
||||
async def _get_funding_arb_spread(
|
||||
params: t.GetFundingArbSpreadReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_funding_arb_spread(client, params)
|
||||
|
||||
@r.post("/tools/get_cross_exchange_funding")
|
||||
async def _get_cross_exchange_funding(
|
||||
params: t.GetCrossExchangeFundingReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_cross_exchange_funding(client, params)
|
||||
|
||||
@r.post("/tools/get_oi_history")
|
||||
async def _get_oi_history(
|
||||
params: t.GetOiHistoryReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_oi_history(client, params)
|
||||
|
||||
@r.post("/tools/get_liquidation_heatmap")
|
||||
async def _get_liquidation_heatmap(
|
||||
params: t.GetLiquidationHeatmapReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_liquidation_heatmap(client, params)
|
||||
|
||||
@r.post("/tools/get_cointegration_pairs")
|
||||
async def _get_cointegration_pairs(
|
||||
params: t.GetCointegrationPairsReq,
|
||||
client: SentimentClient = Depends(get_sentiment_client),
|
||||
):
|
||||
return await t.get_cointegration_pairs(client, params)
|
||||
|
||||
return r
|
||||
Reference in New Issue
Block a user