Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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)
- Copiare il template di configurazione e compilarlo:
cp .env.example .env # editare .env con le proprie credenziali e i due token - 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))' - Installare le dipendenze e avviare:
uv sync uv run cerbero-mcp - 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-01o2026-05-01T12:34:56Z)actor:testnet|mainnetexchange: nome dell'exchange (deribit,bybit,hyperliquid,alpaca)action: nome del tool (es.place_order)bot_tag: identificatore del botlimit: massimo record restituiti, default1000, massimo10000
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:
-
Backup
secrets/(V2 non li userà ma servono come fonte di copia). -
Generare i due nuovi token bearer (vedi sopra).
-
Compilare
.envmappando i campi V1 ai campi V2:V1 (file JSON) V2 (variabile .env)secrets/deribit.jsonclient_id/client_secretDERIBIT_CLIENT_ID/DERIBIT_CLIENT_SECRETsecrets/bybit.jsonapi_key/api_secretBYBIT_API_KEY/BYBIT_API_SECRETsecrets/hyperliquid.jsonwallet_address/private_keyHYPERLIQUID_WALLET_ADDRESS/HYPERLIQUID_PRIVATE_KEYsecrets/alpaca.jsonapi_key_id/secret_keyALPACA_API_KEY_ID/ALPACA_SECRET_KEYsecrets/macro.jsonfred_api_key/finnhub_api_keyFRED_API_KEY/FINNHUB_API_KEYsecrets/sentiment.jsoncryptopanic_key/lunarcrush_keyCRYPTOPANIC_KEY/LUNARCRUSH_KEY -
Aggiornare i client bot:
- i path API restano identici (
/mcp-{exchange}/tools/{tool}) - sostituire
core.token/observer.tokenconTESTNET_TOKENoMAINNET_TOKENa seconda dell'ambiente desiderato per la chiamata
- i path API restano identici (
-
Spegnere V1 (
docker compose -f <vecchio compose> down) e avviare V2 (docker compose up -d). -
Verificare
/healthe/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:
docs/superpowers/specs/2026-04-30-V2.0.0-unified-image-token-routing-design.mddocs/superpowers/plans/2026-04-30-V2.0.0-unified-image-token-routing.md
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.