# 04 — MCP Integration Tutti i server MCP sono già configurati a livello di `CerberoSuite` (vedi `Cerbero_Office/.mcp.json`). Cerbero Bite vi si connette come **client MCP** usando l'SDK ufficiale `mcp` per Python. ## Configurazione di connessione Cerbero Bite legge `~/.config/cerbero-suite/mcp.json` (o, in dev, da `.mcp.json` locale puntato via env var `CERBERO_BITE_MCP_CONFIG`). I server vengono risolti **per nome** dichiarato nel file di config. ```python # clients/_base.py — abstract class McpClient: name: str # "cerbero-deribit", ecc. timeout_s: float = 8.0 retry_max: int = 3 retry_base_delay: float = 1.0 # esponenziale async def call(self, tool: str, **params) -> dict: ... ``` Ogni wrapper concreto eredita `McpClient` ed espone metodi tipizzati. La logica di retry e timeout è centralizzata. ## Server MCP usati ### `cerbero-deribit` Tool consumati: | Tool | Uso | Frequenza | |---|---|---| | `get_index_price(asset="ETH")` | Spot ETH per calcolo strike | Ogni ciclo entry + monitor | | `get_dvol()` | Volatilità implicita aggregata ETH | Ogni ciclo entry + monitor | | `get_options_chain(asset, expiry_window)` | Lista strumenti per dato DTE | Solo entry | | `get_instrument(instrument_name)` | Mid, bid, ask, greche su singolo strumento | Entry + monitor | | `get_orderbook(instrument_name, depth=5)` | Profondità per liquidity_gate e slippage | Solo entry | | `get_combo_mark(legs)` | Mark price del combo (debito di chiusura) | Solo monitor | | `get_account_summary(currency="USDC")` | Equity Deribit, margin libero | Periodico | Wrapper: ```python # clients/deribit.py class DeribitClient(McpClient): name = "cerbero-deribit" async def index_price(self, asset: str) -> Decimal: ... async def dvol(self) -> Decimal: ... async def options_chain(self, asset: str, dte_min: int, dte_max: int) -> list[InstrumentSnapshot]: ... async def instrument(self, name: str) -> InstrumentSnapshot: ... async def orderbook(self, name: str, depth: int = 5) -> OrderbookSnapshot: ... async def combo_mark(self, legs: list[OptionLeg]) -> Decimal: ... async def account_summary(self) -> AccountSummary: ... ``` **Note:** - Tutti i prezzi sono ricevuti come float dal MCP, convertiti in `Decimal` con `quantize` a 6 cifre nel wrapper. - Greche convertite con la stessa quantizzazione. - Se `mark_iv = 7%` o `300%` o `bid = 0` su orderbook ATM su tutti gli strumenti → wrapper solleva `DeribitDataAnomalyError` (probabile testnet o feed rotto). Il decision orchestrator cattura, alert, skippa il ciclo. ### `cerbero-hyperliquid` | Tool | Uso | |---|---| | `get_perp_funding_rate(asset="ETH")` | Filtro entry §2.6 | | `get_perp_summary(asset="ETH")` | Volume 24h, conferma liquidità correlata | | `get_account_summary()` | Solo per coerenza, non usato in decision loop | ```python class HyperliquidClient(McpClient): async def funding_rate_annualized(self, asset: str) -> Decimal: ... async def perp_summary(self, asset: str) -> PerpSummary: ... ``` ### `cerbero-sentiment` | Tool | Uso | |---|---| | `get_funding_cross_exchange(asset="ETH")` | Bias direzionale §3.1 (mediana 4 maggiori) | Le news qualitative **non sono usate** nel decision loop (no LLM). Vengono eventualmente lette da Adriano in occasione del report settimanale. ```python class SentimentClient(McpClient): async def funding_cross_median(self, asset: str) -> Decimal: ... ``` ### `cerbero-macro` | Tool | Uso | |---|---| | `get_calendar(days_ahead=18)` | Filtro eventi macro pre-entry | Eventi rilevanti (filtra per `severity = high` e `country in {US, EU}`): FOMC, FED minutes, CPI, NFP, ECB, GDP, Powell speech, Lagarde speech. ```python class MacroClient(McpClient): async def upcoming_events(self, days_ahead: int) -> list[MacroEvent]: ... async def first_high_severity_within(self, days: int) -> int | None: """Days until first high-severity event, None if none in window.""" ``` ### `cerbero-portfolio` | Tool | Uso | |---|---| | `get_holdings()` | Capitale corrente complessivo | | `get_holdings_by_asset()` | Filtro entry §2.7 (ETH < 30% portfolio) | | `get_correlation()` | Sanity check, non bloccante | ```python class PortfolioClient(McpClient): async def total_equity_usd(self) -> Decimal: ... async def asset_pct(self, asset: str) -> Decimal: ... ``` ### `cerbero-memory` | Tool | Uso | |---|---| | `push_user_instruction(payload, source="cerbero-bite")` | Invio istruzione apertura/chiusura a Cerbero core | | `get_pending(source="cerbero-bite")` | Verifica ack di Cerbero core | ```python class MemoryClient(McpClient): async def push_instruction(self, instruction: CerberoInstruction) -> str: """Returns instruction_id.""" async def is_acknowledged(self, instruction_id: str) -> bool: ... ``` **Payload `CerberoInstruction`** (schema condiviso con Cerbero core, documentato in `Cerbero/prompt.base v4`): ```json { "source": "cerbero-bite", "kind": "open_combo" | "close_combo", "exchange": "deribit", "asset": "ETH", "proposal_id": "uuid-...", "legs": [ {"instrument": "ETH-13MAY26-1900-P", "side": "SELL", "size": 2, "limit_price_eth": "0.0048"}, {"instrument": "ETH-13MAY26-1810-P", "side": "BUY", "size": 2, "limit_price_eth": "0.0021"} ], "limit_combo_eth": "0.0027", "tif": "GTC", "expires_at": "2026-04-27T16:00:00Z", "max_slippage_eth": "0.0005", "reason": "weekly_open" | "profit_take" | "stop_loss" | "vol_stop" | "time_stop" | "delta_breach" | "adverse_move", "milestone": "advisory_only" | "approved_by_user" } ``` Cerbero core deduplica per `proposal_id` (idempotenza in caso di retry). ### `cerbero-telegram` | Tool | Uso | |---|---| | `send_message(text, parse_mode="MarkdownV2")` | Report pre/post trade, alert | | `send_with_buttons(text, buttons)` | Conferma ad Adriano (yes/no) | Le conferme devono ritornare entro 60 minuti (entry) o 30 minuti (exit). Implementazione: l'engine si mette in `await` su una coda interna alimentata dal callback Telegram via webhook locale. ```python class TelegramClient(McpClient): async def send(self, text: str, parse_mode: str = "MarkdownV2") -> int: ... async def request_confirmation(self, text: str, timeout_s: int) -> bool: ... ``` ### `cerbero-brain-bridge` | Tool | Uso | |---|---| | `kb_search(query)` | Lookup pre-trade su pattern simili (consultivo) | | `kb_read(path)` | Lettura nota wiki specifica | | `kb_write(path, content)` | Salvataggio learning post-trade | **Importante:** il brain-bridge non partecipa al decision loop. Le chiamate `kb_search` sono **consultive** e i risultati allegati al report di Adriano per contesto, mai consumati come input ai filtri deterministici. ```python class BrainBridgeClient(McpClient): async def search(self, query: str, limit: int = 5) -> list[KbHit]: ... async def write_note(self, path: str, content: str) -> None: ... ``` ### `cerbero-scheduler` **Non usato** dal decision loop. Cerbero Bite ha il proprio scheduler APScheduler interno. Il MCP scheduler resta a disposizione del core Cerbero per altre routine. ## Errori e degradation | Server down | Comportamento | |---|---| | `cerbero-deribit` | Skip ciclo entry; per monitor → alert e marca posizione come `unknown_state` (non chiude alla cieca) | | `cerbero-hyperliquid` | Skip filtro funding §2.6 con warning; entry può proseguire se altre condizioni soddisfatte | | `cerbero-sentiment` | Bias §3.1 cade in `no_entry` per default (no funding cross → niente direzione) | | `cerbero-macro` | **Hard fail**: senza calendar non si apre. È un filtro irrinunciabile | | `cerbero-portfolio` | Skip filtro §2.7 con warning; sizing usa ultimo capitale noto da SQLite con warning | | `cerbero-memory` | Hard fail per esecuzione: senza push_user_instruction non si può aprire/chiudere | | `cerbero-telegram` | Skip ciclo: senza canale di conferma niente proposta | | `cerbero-brain-bridge` | Skip lookup, log warning. Mai bloccante | Ogni "hard fail" → alert sonoro su Telegram via canale di backup (BotPapà), kill switch armato fino al ripristino. ## Versioning Cerbero Bite verifica all'avvio la versione di ciascun MCP via `get_version()` (tool standard). Schema di versioning attesa: ```python EXPECTED_MCP_VERSIONS = { "cerbero-deribit": "^2.0.0", "cerbero-hyperliquid": "^1.5.0", "cerbero-memory": "^4.0.0", "cerbero-portfolio": "^1.2.0", ... } ``` Mismatch → kill switch e alert manuale. Mai partire con MCP a versione incompatibile.