KB / mcp
MCP tool: analyze_signals
Last verified
analyze_signals is the platform’s multi-aspect signal inspector. One MCP tool, 15 aspects, each routing to a typed JSON endpoint on the backend. Call this when market_pulse isn’t enough and you need structured data on a specific dimension — the breakdown of the health score, the per-category alignment grid, the cascade-stage state, historical analogues, or a per-metric sparkline.
The tool is a thin dispatcher (mcp/mcp_server/tools/public.py:analyze_signals). Every aspect maps to one backend route via the _ASPECT_MAP table. The mapping is the contract.
Signature
analyze_signals(
aspect: str, # one of the 15 aspects below
days: int | None = None, # used by: history, regime_log
days_back: int | None = None, # used by: diff
metrics: str | None = None, # used by: sparklines (comma-separated)
period: str | None = None, # used by: sparklines (e.g. "30d")
) -> str # JSON-serialised dict | list
Optional params are silently dropped for aspects that don’t consume them — the dispatcher only forwards the params declared in _ASPECT_MAP. Unknown aspects raise ValueError("Unknown aspect '<x>'. Valid: ...") before the round-trip.
Aspects
| Aspect | Backend route | What it returns |
|---|---|---|
score_breakdown | GET /api/v1/signals/score/breakdown | Per-component score detail with raw inputs, bands, weights, 5-day history, regime-override resolution |
score | GET /api/v1/signals/score | Composite 0–100 health score + component weights |
history | GET /api/v1/signals/history | Daily signal-row history (param: days) |
regime_log | GET /api/v1/signals/regime-log | Historical regime transitions with drivers + score context (param: days) |
alignment | GET /api/v1/signals/alignment | Signal-vs-price alignment grid: 11 (or 12 when flag-gated news_sentiment is on) categories’ implications vs SPY direction, with severity, intensity, velocity, freshness |
fragility | GET /api/v1/signals/fragility | Distance to nearest regime transition — how close any signal is to flipping its classification |
cascade | GET /api/v1/signals/cascade | 12-stage transmission-stress pipeline (Volatility Spike → Score Collapse), with stage 13 as the meta “Full Cascade” trigger when ≥10 of the 12 are active |
base_rates | GET /api/v1/signals/base-rates | Historical analogue matcher — forward 1d/3d/5d SPY return distributions for similar setups |
diff | GET /api/v1/signals/diff | Compact diff vs a prior snapshot, significant changes only (param: days_back) |
velocity | GET /api/v1/signals/velocity | Health score rate of change — momentum before the score itself moves |
sectors | GET /api/v1/signals/sectors | Per-GICS-sector breadth breakdown |
correlations | GET /api/v1/signals/correlations | Rolling 20d inter-market correlations (SPY/VIX, SPY/DXY, SPY/TNX, SPY/Oil) with per-pair classification |
gamma | GET /api/v1/signals/gamma | Dealer-gamma hedging levels — call/put walls (price magnets) |
fedwatch | GET /api/v1/signals/fedwatch | Fed funds rate + cut/hike probabilities |
sparklines | GET /api/v1/signals/sparklines | Per-metric time series (params: metrics, period) |
Returns
Shape varies by aspect. Every response carries the platform’s standard generated_at (ET ISO 8601 + offset, Law 1) and a schema_version where applicable. Two representative shapes:
aspect="score"
{
"score": 68,
"max": 100,
"regime": "RISK-ON",
"component_count": 18,
"weights_active_regime": { "dix": 8, "gex": 6, "hy_oas": 7, "breadth_50d": 7, ... },
"schema_version": "2026.04.3",
"generated_at": "2026-05-26T10:14:33-04:00"
}
aspect="alignment"
{
"schema_version": "2026.05.1",
"spy_direction": "BULLISH",
"baseline": "3d",
"severity": "STRONG_ALIGNMENT",
"aligned_count": 7,
"divergent_count": 2,
"weighted_alignment_pct": 71.4,
"categories": {
"dark_pool": { "implication": "BULLISH", "value": 0.46, "reason": "DIX 0.46 (elevated)", "legs": [...], "freshness": {...} },
"gamma": { "implication": "BULLISH", ... },
"credit": { "implication": "NEUTRAL", ... },
...
},
"generated_at": "2026-05-26T10:14:33-04:00"
}
Refer to the matching /api/v1/signals/<aspect> KB article (kb/api/get-signals-*) for the per-aspect shape contract.
Behaviour
- Cache TTL.
CACHE_TTL_SIGNALS = 15s(mcp/mcp_server/config.py). Cache key includes the aspect and any forwarded params, sohistory?days=7andhistory?days=30cache independently. - Param filtering. Optional params not in the aspect’s allowed list are dropped — passing
days=10toaspect="score"is silently ignored rather than an error. This keeps the call surface forgiving without surprising the backend. - Read-only, no auth. All 15 aspects are public — no
TRADING_TOKENrequired. - Source of truth. Each aspect route is the same one the dashboard and the rendered report consume. Drift between the agent’s read and the dashboard view is structurally impossible.
Examples
Regime fragility
Operator: "How close are we to a regime flip?"
Agent: analyze_signals(aspect="fragility")
VIX + breadth trends over 60 days
Agent: analyze_signals(
aspect="sparklines",
metrics="vix,breadth",
period="60d"
)
Compare today vs 5 days ago
Agent: analyze_signals(aspect="diff", days_back=5)
Drill into the health score
Agent: analyze_signals(aspect="score_breakdown")
-> per-component points + raw values + bands + active regime overrides
Where’s the dealer-gamma wall?
Operator: "Is there a gamma magnet above us?"
Agent: analyze_signals(aspect="gamma")
-> read call_wall + put_wall + the per-strike profile.
When to use
- The market_pulse summary isn’t specific enough for the question.
- Building a thesis around one signal dimension (alignment, cascade, fragility).
- Checking historical context (
history,regime_log,base_rates). - Sparkline rendering or rate-of-change reads (
sparklines,velocity,diff).
When NOT to use
- You just need the regime + score (use
market_pulse). - You need live prices (use
get_quote). - You’re probing hypothetical analogue scenarios —
base_rateshere matches today’s live row; usecompare_to_historyto pass in a custom signals dict.
See also
market_pulse— the lighter summary surface.compare_to_history— the analogue matcher with a hypothetical-scenario knob.read_report— the narrative cousin to the structured aspects here.- KB API articles for each route:
kb/api/get-signals-score,kb/api/get-signals-alignment,kb/api/get-signals-cascade, etc. - Implementation:
mcp/mcp_server/tools/public.py:analyze_signals(dispatcher) +_ASPECT_MAP(the aspect→route table),app/signals/cascade.py:CASCADE_STAGES(the 12 stage entries).