Files
Cerbero-mcp/README.md
T
2026-05-01 08:51:40 +02:00

11 KiB

Cerbero MCP — V2.0.0

Server MCP unificato multi-exchange per la suite Cerbero. Distribuito come singola immagine Docker; testnet e mainnet sono raggiungibili contemporaneamente attraverso un meccanismo di routing per-request basato sul token bearer fornito dal client.

Caratteristiche

  • Una singola immagine Docker (cerbero-mcp) ospita tutti i router exchange in un unico processo FastAPI
  • Quattro exchange (Deribit, Bybit, Hyperliquid, Alpaca) e due data provider read-only (Macro, Sentiment)
  • Switch testnet/mainnet per-request tramite header Authorization: Bearer <TOKEN>: lo stesso container serve entrambi gli ambienti senza riavvii
  • Configurazione interamente in .env: nessun file JSON di credenziali separato; le URL upstream (live/testnet) di ciascun exchange sono override-abili tramite variabili dedicate (DERIBIT_URL_*, BYBIT_URL_*, HYPERLIQUID_URL_*, ALPACA_URL_*)
  • Documentazione interattiva OpenAPI/Swagger esposta a /apidocs
  • Qualità verificata: 259 test (unit + integration + smoke), mypy pulito, ruff pulito

Avvio rapido (sviluppo, senza Docker)

  1. Copiare il template di configurazione e compilarlo:
    cp .env.example .env
    # editare .env con le proprie credenziali e i due token
    
  2. Generare i token bearer:
    python -c 'import secrets; print("TESTNET_TOKEN=" + secrets.token_urlsafe(32))'
    python -c 'import secrets; print("MAINNET_TOKEN=" + secrets.token_urlsafe(32))'
    
  3. Installare le dipendenze e avviare:
    uv sync
    uv run cerbero-mcp
    
  4. Aprire la documentazione interattiva: http://localhost:9000/apidocs

Avvio con Docker

cp .env.example .env  # compilare valori
docker compose up -d

Il container espone la porta indicata da PORT in .env (default 9000).

Token bearer e ambienti

Token usato Ambiente upstream
Authorization: Bearer $TESTNET_TOKEN URL testnet di ciascun exchange
Authorization: Bearer $MAINNET_TOKEN URL mainnet (live)
Nessun token / token sconosciuto 401 Unauthorized

Le tool puramente read-only (/mcp-macro/* e /mcp-sentiment/*) richiedono comunque un bearer valido, ma il valore (testnet o mainnet) è indifferente perché non hanno endpoint testnet.

Header X-Bot-Tag (identificazione bot)

Tutte le chiamate a /mcp-* richiedono inoltre l'header X-Bot-Tag con una stringa identificativa del bot chiamante (massimo 64 caratteri). Il valore viene loggato negli audit record per tracciare quale bot ha eseguito ogni operazione write. Esempio di richiesta:

Authorization: Bearer $MAINNET_TOKEN
X-Bot-Tag: scanner-alpha-prod

Se l'header è assente o vuoto la risposta è 400 BAD_REQUEST. L'header non è richiesto sugli endpoint pubblici (/health, /apidocs, /openapi.json) né sull'endpoint admin /admin/audit.

Endpoint principali

Path Descrizione
GET /health Healthcheck (no auth)
GET /apidocs Swagger UI (no auth)
GET /openapi.json Schema OpenAPI 3.1 (no auth)
POST /mcp-deribit/tools/{tool} Tool exchange Deribit
POST /mcp-bybit/tools/{tool} Tool exchange Bybit
POST /mcp-hyperliquid/tools/{tool} Tool exchange Hyperliquid
POST /mcp-alpaca/tools/{tool} Tool exchange Alpaca
POST /mcp-macro/tools/{tool} Tool macro/market data
POST /mcp-sentiment/tools/{tool} Tool sentiment/news
GET /admin/audit Query dell'audit log JSONL (bearer richiesto, no X-Bot-Tag)

Audit query

GET /admin/audit legge il file JSONL puntato da AUDIT_LOG_FILE e restituisce i record filtrati. Richiede un bearer valido (testnet o mainnet); non richiede l'header X-Bot-Tag.

Parametri di query (tutti opzionali):

  • from, to: ISO 8601 datetime (es. 2026-05-01 o 2026-05-01T12:34:56Z)
  • actor: testnet | mainnet
  • exchange: nome dell'exchange (deribit, bybit, hyperliquid, alpaca)
  • action: nome del tool (es. place_order)
  • bot_tag: identificatore del bot
  • limit: massimo record restituiti, default 1000, massimo 10000

Esempio di chiamata:

curl -H "Authorization: Bearer $MAINNET_TOKEN" \
     "http://localhost:9000/admin/audit?from=2026-05-01&actor=mainnet&action=place_order&limit=100"

Se AUDIT_LOG_FILE non è configurata l'endpoint risponde count: 0 con un campo warning. Per abilitare il sink persistente impostare nel .env:

AUDIT_LOG_FILE=/var/log/cerbero-mcp/audit.jsonl
AUDIT_LOG_BACKUP_DAYS=30

Tool disponibili

Common (cerbero_mcp.common.indicators + options + microstructure + stats)

Tecnici (sma, rsi, macd, atr, adx), volatilità (vol_cone, garch11_forecast), statistici (hurst_exponent, half_life_mean_reversion, cointegration_test), risk (rolling_sharpe, var_cvar), microstructure (orderbook_imbalance), options (oi_weighted_skew, smile_asymmetry, dealer_gamma_profile, vanna_charm_aggregate).

Deribit

DVOL, GEX, P/C ratio, skew_25d, term_structure, iv_rank, realized_vol, indicatori tecnici, find_by_delta, calculate_spread_payoff, get_dealer_gamma_profile, get_vanna_charm, get_oi_weighted_skew, get_smile_asymmetry, get_atm_vs_wings_vol, get_orderbook_imbalance, place_combo_order.

Bybit

Ticker, orderbook, OHLCV, funding rate, open interest, basis spot/perp, indicatori tecnici, place_batch_order, get_orderbook_imbalance, get_basis_term_structure.

Hyperliquid

Account summary, positions, orderbook, historical, indicators, funding rate, basis spot/perp, place_order, set_stop_loss, set_take_profit.

Alpaca

Account, positions, bars, snapshot, option chain, place_order, amend_order, cancel_order, close_position.

Macro

Treasury yields, FRED indicators, equity futures, asset prices, calendar, get_yield_curve_slope, get_breakeven_inflation, get_cot_tff, get_cot_disaggregated, get_cot_extreme_positioning.

Sentiment

News (CryptoPanic/CoinDesk), social (LunarCrush), funding multi-exchange, OI history, get_funding_arb_spread, get_liquidation_heatmap, get_cointegration_pairs.

Deploy su VPS con Traefik

Sul VPS la rete pubblica (TLS, allowlist IP, rate limit) è gestita da Traefik esterno a questo repository. Il container cerbero-mcp non espone porte all'esterno: si registra alla rete docker di Traefik tramite label aggiunte da un override compose esterno (es. docker-compose.override.yml versionato fuori da questo repo). La policy di sicurezza pubblica (allowlist IP per gli endpoint write) è responsabilità di Traefik.

Esempio label minime per Traefik:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.cerbero.rule=Host(`cerbero-mcp.tielogic.xyz`)"
  - "traefik.http.routers.cerbero.entrypoints=websecure"
  - "traefik.http.routers.cerbero.tls.certresolver=letsencrypt"
  - "traefik.http.services.cerbero.loadbalancer.server.port=9000"

Build & deploy pipeline

Build dell'immagine eseguita sulla macchina di sviluppo:

export GITEA_PAT='<PAT con scope write:package>'
./scripts/build-push.sh

Lo script tagga :2.0.0, :latest e :sha-<short> per rollback puntuali e pubblica al registry Gitea. Sul VPS Watchtower polla :latest e aggiorna il container automaticamente.

Smoke test post-deploy:

PORT=9000 TESTNET_TOKEN="$TESTNET_TOKEN" bash tests/smoke/run.sh

Sviluppo

uv sync
uv run pytest                   # tutta la suite (259 test attesi)
uv run pytest tests/unit -v     # solo unit
uv run pytest tests/integration -v
uv run ruff check src/ tests/
uv run mypy src/cerbero_mcp

Tutti e quattro i comandi devono ritornare verde prima di committare.

Layout sorgenti

src/cerbero_mcp/
├── __main__.py            # entrypoint cerbero-mcp
├── settings.py            # Pydantic Settings (legge .env)
├── auth.py                # middleware bearer → request.state.environment
├── server.py              # build_app() + Swagger + middleware + handlers
├── client_registry.py     # cache lazy {(exchange, env): client}
├── routers/               # un file per exchange (deribit, bybit, ...)
├── exchanges/             # logica per-exchange: client + tools
└── common/                # indicators, options, microstructure, stats, ...

Migrazione da V1 (1.x → 2.0.0)

Per chi è in produzione su V1:

  1. Backup secrets/ (V2 non li userà ma servono come fonte di copia).

  2. Generare i due nuovi token bearer (vedi sopra).

  3. Compilare .env mappando i campi V1 ai campi V2:

    V1 (file JSON) V2 (variabile .env)
    secrets/deribit.json client_id / client_secret DERIBIT_CLIENT_ID / DERIBIT_CLIENT_SECRET
    secrets/bybit.json api_key / api_secret BYBIT_API_KEY / BYBIT_API_SECRET
    secrets/hyperliquid.json wallet_address / private_key HYPERLIQUID_WALLET_ADDRESS / HYPERLIQUID_PRIVATE_KEY
    secrets/alpaca.json api_key_id / secret_key ALPACA_API_KEY_ID / ALPACA_SECRET_KEY
    secrets/macro.json fred_api_key / finnhub_api_key FRED_API_KEY / FINNHUB_API_KEY
    secrets/sentiment.json cryptopanic_key / lunarcrush_key CRYPTOPANIC_KEY / LUNARCRUSH_KEY
  4. Aggiornare i client bot:

    • i path API restano identici (/mcp-{exchange}/tools/{tool})
    • sostituire core.token / observer.token con TESTNET_TOKEN o MAINNET_TOKEN a seconda dell'ambiente desiderato per la chiamata
  5. Spegnere V1 (docker compose -f <vecchio compose> down) e avviare V2 (docker compose up -d).

  6. Verificare /health e /apidocs.

In caso di necessità è possibile fare rollback pullando i tag immagine V1 (cerbero-mcp-*:1.x); si ricordi però che .env e secrets/ sono formati incompatibili tra V1 e V2 — tenere backup separati.

Architettura

Spec di progettazione e plan di implementazione completi in:

Riepilogo del flusso runtime:

Bot → Authorization: Bearer <TESTNET|MAINNET>_TOKEN
        ↓
   FastAPI middleware auth → request.state.environment ∈ {testnet, mainnet}
        ↓
   Router /mcp-{exchange}/tools/{tool}
        ↓
   ClientRegistry.get(exchange, env) → client cached lazy (HTTP/WS pool riusato)
        ↓
   Tool function (logica pura) → exchange API

Override URL upstream

L'override delle URL upstream da .env è completo per Deribit e Hyperliquid. Per Bybit funziona tramite l'attributo endpoint interno di pybit (workaround documentato nel client). Per Alpaca l'override è applicato al solo trading endpoint: gli endpoint dati (data.alpaca.markets) restano quelli predefiniti dell'SDK.

Licenza

Privato.