Files
Cerbero-mcp/CLAUDE.md
T
Adriano 6faf21369d refactor(V2): get_historical & get_indicators are common-only on /mcp
Consolidate the cross-cutting data tools onto the unified interface and
remove their per-exchange duplicates.

- /mcp/tools/get_historical: single call, `exchange` now optional. Set →
  that venue's candles (native symbol); omitted → cross-exchange consensus
  (canonical symbol, median OHLC + div_pct). Folds in the former
  /mcp-cross consensus, whose router is deleted.
- /mcp/tools/get_indicators: new common tool computing sma/rsi/atr/macd/adx
  over the single-or-consensus series. Computation centralized in
  common/indicators.py::compute_indicators (was duplicated per exchange).
- Remove get_historical from /mcp-deribit and /mcp-hyperliquid; remove
  get_technical_indicators (deribit) and get_indicators (hyperliquid),
  including the now-dead client methods, Req schemas and tool wrappers.
  The client get_historical METHODS stay (used by the unified dispatch).

Tests: rewrite cross tests into test_unified (instruments, historical
single + consensus + failures, indicators single + consensus); drop the
old CrossClient test; tighten app-boot surface assertions (/mcp owns the
data tools; no /mcp-cross, no per-exchange historical/indicators).

Docs: API_REFERENCE / README / CLAUDE updated; smoke run_unified.sh now
covers consensus + indicators.

324 passed, ruff clean. Verified live against Deribit/Hyperliquid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 10:51:21 +00:00

93 lines
4.4 KiB
Markdown

# CLAUDE.md
Guida per Claude Code che lavora su **Cerbero MCP** — server MCP multi-exchange
(FastAPI) distribuito come singola immagine Docker, con routing testnet/mainnet
per-request basato sul token bearer.
## Comandi
Gestione progetto con `uv` (Python ≥ 3.11):
```bash
uv sync # installa dipendenze (incl. dev)
uv run cerbero-mcp # avvia il server (porta da .env, default 9000)
uv run pytest # suite completa (~323 test attesi)
uv run pytest tests/unit -v # solo unit
uv run pytest tests/unit/exchanges/cross/test_unified.py::test_get_historical_single_exchange # singolo test
uv run ruff check src tests # lint (E,F,I,W,UP,B,SIM; line-length 100)
uv run mypy src/cerbero_mcp # type check (non-strict)
```
Tutti e quattro (pytest, ruff, mypy) devono essere verdi prima di committare.
`testpaths = ["tests"]``old/` non viene mai raccolto dai test.
## Architettura
Flusso runtime:
```
Bot → Authorization: Bearer <TESTNET|MAINNET>_TOKEN (+ header X-Bot-Tag)
→ auth middleware → request.state.environment ∈ {testnet, mainnet}
→ Router (/mcp/... , /mcp-{exchange}/... , /mcp-cross/...)
→ ClientRegistry.get(exchange, env) → client cached lazy per (exchange, env)
→ tool function (logica pura) → exchange API
```
File chiave:
- `src/cerbero_mcp/__main__.py``_make_app()` registra tutti i router.
- `src/cerbero_mcp/server.py``build_app()`, Swagger, exception handler,
e `_TimestampInjectorMiddleware` che inietta `data_timestamp` (ISO UTC) nel
top level di **ogni** risposta JSON sotto `/tools/*` (+ header `X-Data-Timestamp`).
- `src/cerbero_mcp/auth.py` — bearer → env, e `X-Bot-Tag` obbligatorio (path
whitelisted: `/health`, `/health/ready`, `/apidocs`, `/openapi.json`).
- `src/cerbero_mcp/client_registry.py` — cache `{(exchange, env): client}`.
- `src/cerbero_mcp/exchanges/__init__.py``build_client()`: factory per exchange.
- `src/cerbero_mcp/settings.py` — Pydantic Settings da `.env`.
- `src/cerbero_mcp/routers/` — un file per exchange + `unified.py` (`/mcp`) +
`cross.py` (`/mcp-cross`).
- `src/cerbero_mcp/exchanges/<ex>/{client.py,tools.py}` — client HTTP + schemi
Pydantic / wrapper tool. `cross/` contiene aggregatore consensus, interfaccia
unificata, normalizzatori e symbol-map.
## Exchange attivi
Trading: **deribit, hyperliquid, ibkr**. Data-provider read-only: **macro,
sentiment**. **Bybit e Alpaca sono ritirati** dalla superficie API in V2:
codice archiviato in `old/` (fuori dal package, non importato, non testato).
Nota: nel modulo `sentiment` "bybit" resta come **fonte dati pubblica**
(funding/OI), non come client di trading.
## Interfacce dati
- **`/mcp` (comune)** — gli **unici** tool dati trasversali. `get_instruments`
(schema uniforme: `exchange`, `fees`, `history_start`, `type`, `tick_size`,
`native`); `get_historical` e `get_indicators` con `exchange` opzionale →
singolo venue se valorizzato, **consenso** multi-exchange (mediana OHLC +
`div_pct`) se omesso. Supporta deribit + hyperliquid (IBKR non integrato).
- **`/mcp-{exchange}`** — router per-exchange per il resto (ticker, ordini,
options analytics, …). **NON** espongono più `get_historical`/`get_indicators`.
Convenzione: `get_historical` ritorna la chiave uniforme
`candles: [{timestamp(ms), open, high, low, close, volume}]` (validatore in
`common/candles.py`). `fees`/`history_start` sono popolati **live dove
l'upstream li espone** (oggi: Deribit), altrimenti `null`. Il calcolo indicatori
è centralizzato in `common/indicators.py::compute_indicators`.
## Convenzioni
- Schemi richiesta = modelli Pydantic in `<ex>/tools.py`; il router fa solo
dependency-injection di env + client e chiama il wrapper del tool.
- Errori applicativi: sollevare `fastapi.HTTPException`; gli handler in
`server.py` li avvolgono in un envelope con `request_id`.
- Firme/crittografia: il signing L1 Hyperliquid usa `eth_utils.to_hex` come
l'SDK ufficiale → `r`/`s` hex possono essere < 66 char (byte alto a zero,
~1/256); non assumere lunghezza fissa.
- Branch di produzione: **`V2.0.0`** (non `main`). Deploy via
`scripts/deploy-vps.sh`.
## Documentazione
- `README.md` — overview, setup, deploy, migrazione V1→V2, IBKR OAuth.
- `docs/API_REFERENCE.md` — catalogo completo endpoint/tool. Aggiornarlo
quando si aggiungono/rimuovono tool o si cambiano gli schemi.