Files
Cerbero-mcp/README.md
T
AdrianoDev 9afd087152 docs(V2): aggiorna conteggio test 259 → 310 nel README
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:52:11 +02:00

288 lines
11 KiB
Markdown

# 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**: 310 test (unit + integration + smoke), mypy
pulito, ruff pulito
## Avvio rapido (sviluppo, senza Docker)
1. Copiare il template di configurazione e compilarlo:
```bash
cp .env.example .env
# editare .env con le proprie credenziali e i due token
```
2. Generare i token bearer:
```bash
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:
```bash
uv sync
uv run cerbero-mcp
```
4. Aprire la documentazione interattiva: <http://localhost:9000/apidocs>
## Avvio con Docker
```bash
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:
```yaml
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:
```bash
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:
```bash
PORT=9000 TESTNET_TOKEN="$TESTNET_TOKEN" bash tests/smoke/run.sh
```
## Sviluppo
```bash
uv sync
uv run pytest # tutta la suite (310 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:
- [`docs/superpowers/specs/2026-04-30-V2.0.0-unified-image-token-routing-design.md`](docs/superpowers/specs/2026-04-30-V2.0.0-unified-image-token-routing-design.md)
- [`docs/superpowers/plans/2026-04-30-V2.0.0-unified-image-token-routing.md`](docs/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.