Skip to main content
Glama
Broker Analysis MCP

Broker Analysis MCP

by chetto1983

broker-analysis-mcp

An MCP server for financial analysis for a broker's analysts, with skills that teach the agent how to read every number. Data from Yahoo Finance through yfinance.

It ships as one plugin, broker-analysis/, that Claude Code, Codex and Aura install as it is: the server and the five skills together.

Install

The plugin starts the server with uvx from an immutable full commit of this repository, so the machine that runs it needs uv and git, and network access on the first start, when uv fetches the package, its dependencies and Python 3.13 if it is missing.

Client

Install

Claude Code

/plugin marketplace add chetto1983/broker-analysis-mcp, then /plugin install broker-analysis@broker-analysis-mcp

Codex

codex plugin marketplace add chetto1983/broker-analysis-mcp, then codex plugin add broker-analysis@broker-analysis-mcp

Aura

Install chetto1983/broker-analysis-mcp/broker-analysis from the cockpit's Governance plugin board; upgrade an existing installation from the same board. The server runs in each identity's sandbox box.

Data source. yfinance reads Yahoo's unofficial endpoints, is meant for personal and research use, and can break without notice. Check the source's permitted use for your deployment. Nothing here places orders or gives personalised advice.

Operational controls, bounded execution, telemetry and archive recovery are described in OPERATIONS.md. Standalone MCP tests do not require Aura.

Related MCP server: YFinance MCP Server

Tools

Tool

What it returns

portfolio_risk

Fixed current positions in a base currency: FX-aware returns, correlations, historical and filtered-historical-simulation VaR/ES, component and scenario-frozen marginal ES, named historical/hypothetical stress scenarios. Saves a data snapshot and separate development, validation and monitoring reports.

model_status

Champion and EWMA fallback decision as of a saved portfolio run, validation and monitoring QLIKE, VaR coverage diagnostics and last validation date.

analysis_record

Hash-verified output, exact snapshot, manifest or one of the three model reports, by record ID.

market_snapshot

Acquires one immutable full-OHLCV dataset, optional benchmark, required directional FX and minimal ETF profiles. Returns a shared snapshot_id for provider-free replay across reports.

risk_metrics

CAGR, close-to-close and range volatility (Garman-Klass, Parkinson, Rogers-Satchell), Sharpe with standard error and t statistic, Probabilistic Sharpe Ratio, Sortino, Omega, max drawdown, CDaR, Calmar, historical and Cornish-Fisher VaR, expected shortfall; against a benchmark also beta and correlation, Bloomberg-style adjusted beta (two years weekly, 0.67 raw + 0.33), Jensen's alpha, Treynor, M², information ratio, tracking error and Morningstar-style up/down capture

technical_indicators

Snapshot of SMAs, RSI, MACD, Bollinger, ATR, 52-week range, all with adjustable parameters

fundamental_ratios / peer_comparison

Multiples, DuPont (ROE, ROA, margins, turnover, equity multiplier), ROIC, working-capital cycle, liquidity, leverage and coverage, payout, revenue CAGR; CAPM cost of equity, WACC, EVA and unlevered beta when the analyst gives the risk-free rate and the equity risk premium; Piotroski F-score, Altman Z and Z'', Beneish M-score with their components (not for banks and insurers); currency conversion between quote and statements

etf_analysis

TER, tracking difference and tracking error against a benchmark converted to the fund's currency

list_indicator_catalog

The parametric indicators (typed parameters with defaults and bounds), expression functions and syntax

custom_analysis

User-defined indicators, derived series, conditions and levels on one ticker; condition events with forward returns against all bars; PNG chart plus a QuantDinger-style JSON spec (plots, signals, layers)

create_hook / list_hooks / run_hooks / delete_hook

Saved conditions that deliver a JSON event (file or allow-listed webhook) when they fire on a new bar

Every metric carries its unit and the key of the formula it came from (references.py, with arXiv sources where one exists), plus the data window, source and warnings.

Analysis reports also include schema_version, retrieved_at, price_observations and return_observations. Counts describe the primary history, the common price calendar for portfolio/ETF reports, or are null for statement-based fundamentals/peer comparisons. Existing window.days, window.bars, window.returns and aligned_days fields keep their meanings. Unavailable capital metrics include a reason, missing_inputs and invalid_inputs; debt cost and unlevered beta do not require the analyst's CAPM rates. Technical warmup nulls name the required observation count. Public parameter bounds appear in the tools' input schemas.

Daily price reports default to completed sessions. Set include_incomplete_session=true to opt into an open/uncertain daily bar; future bars are always excluded. An omitted/null flag uses the completed-only live default. Reports name the aware daily last_bar_timestamp, last_bar_complete, completeness reason and excluded candidates separately from the latest trade time and scheduled session end. Equity completion uses provider session boundaries; crypto waits for the next UTC midnight. Live alert hooks explicitly retain incomplete-session evaluation. Current fundamental quotes and financial statements keep their existing behavior. When current metadata names another session, acquisition requests yfinance's existing historical regular-session schedule and matches its dated start/end to the bar. The captured session end belongs to the selected bar, or is null without matching evidence. If the historical schedule is also unavailable, the latest bar remains uncertain and can be excluded conservatively. Historical rows crossing exchange-local midnight do not prove the daily-bar association and remain ambiguous; their start-derived index is not used as a bar identifier. Previously stored snapshots retain their original metadata; acquire a new one for new evidence. For comparisons, comparison_window names the common calendar and session_sources preserves each asset/benchmark/FX bar's evidence. The aggregate last_bar_complete includes all series used by the calculation; a closed fund does not hide an incomplete benchmark or FX bar.

Shared datasets

Acquire once with market_snapshot, for example:

{"tickers": ["AAPL", "SPY"], "period": "3y", "benchmark": "SPY", "base_currency": "EUR"}

Pass its returned snapshot_id to risk_metrics, technical_indicators, custom_analysis, etf_analysis or portfolio_risk. Omit period and include_incomplete_session on replay to inherit the captured values. Explicit conflicts fail. Indicator parameters, confidence and CAPM rates may vary while prices stay fixed. A snapshot with period shorter than a report's supported range is rejected by that report.

Replay verifies the stored hash and schema, preserves original retrieval/session metadata, and never contacts Yahoo, even after an MCP process restart. Missing tickers, benchmark, directional FX or an ETF profile produce an error; explicitly acquire a broader dataset to extend coverage. Full OHLCV values, numeric dtypes and aware source labels are retained, including integer volumes above the exact floating-point integer range. Acquisition returns coverage and provenance without returning the full time series to the model.

market_snapshot makes an additive local write. Its snapshot_id identifies a reusable market dataset and is distinct from portfolio_risk's audit.record_id or archived snapshot_hash. The latter remain readable through the existing record tools.

portfolio_risk makes additive local audit writes. It is a mutating, non-destructive tool; repeated calls add fresh timestamped records, so it is not idempotent. Its Yahoo reads remain open-world interaction. custom_analysis declares local writes because its default chart option saves PNG/JSON files. Hook tools retain their file/webhook effects. No trading operations are provided.

With custom_analysis(chart=false), indicator/series computation has no chart-pane limit and does not write chart files. The four-lower-pane limit applies when a chart is requested. If a configured lookback exceeds the finite history of its source series, the report warns with the series ID, lookback and available observation count; the unavailable value stays null.

Portfolio risk and model records

Pass signed market values, not share counts, in each security's quote currency (GBP for London prices normalised from pence). portfolio_value is total positive NAV in base_currency, including cash. For example:

{
  "positions": [
    {"ticker": "AAPL", "market_value": 10000},
    {"ticker": "ENI.MI", "market_value": 8000}
  ],
  "portfolio_value": 25000,
  "base_currency": "EUR",
  "period": "5y",
  "confidence": 0.975,
  "scenarios": [{
    "name": "Equity and dollar decline",
    "asset_shocks": {"AAPL": -0.20, "ENI.MI": -0.15},
    "fx_shocks": {"USD": -0.10}
  }]
}

Use position values as of the common valuation date returned by the tool. Residual cash earns zero; negative residual cash represents financing with zero assumed financing cost. This is a fixed-exposure risk reconstruction, not actual historical portfolio performance. No options or other nonlinear instruments are repriced. Prices and FX are intersected before returns; no missing values are filled. The horizon is the next common observed session, not necessarily one calendar day. Closes in different time zones remain asynchronous. Historical stress scenarios exclude current UTC-date/future bars along with the rest of the analysis. Historical episodes are unavailable when their full dates are not covered; request 10y or max for older episodes.

The implemented registry contains EWMA (lambda 0.94) and a rolling 60-session mean-square challenger. It does not yet implement HAR, ARFIMA or GARCH. At least 435 returns are needed for 252 validation observations after a 60-observation warm-up and a separate 63-observation monitoring window. The challenger must improve QLIKE with a HAC z statistic above 1.96; subsequent monitoring deterioration or a VaR coverage alarm triggers EWMA. With less history, the fallback reason is explicit. This policy is evaluated on each portfolio_risk call; model_status(record_id) reads that saved decision and does not run a background scheduler.

Every successful run stores its exact price/FX inputs, alignment calendar, request, versions, source hashes, forecasts, output and three separate reports under the selected data home's records/ directory. Home precedence is BROKER_ANALYSIS_HOME, then Aura's identity-scoped PLUGIN_DATA, then ~/.broker-analysis. Charts and hooks use the same home; read-only record lookup creates no directories. When upgrading an existing Aura installation, explicitly copy old ~/.broker-analysis contents into its PLUGIN_DATA before recreating the box, verify hashes and preserve differing files rather than overwriting them. There is no automatic legacy lookup or migration process. audit.record_id identifies the manifest; use analysis_record with section set to output, snapshot, development, validation or monitoring. Objects use canonical JSON and SHA-256, are published atomically and are never overwritten by the application. Every lookup verifies all manifest-linked objects. Administrators can still delete or replace local files: this is tamper detection and application immutability, not certified WORM storage. Snapshots contain position values; secure this local directory accordingly. Older single-security tools do not create these portfolio records. See model governance for the formulas, validation policy, interpretation and operating workflow.

Parametric analysis

Indicators are data, not code: {"id": "bb", "fn": "bollinger", "params": {"n": 30, "k": 2.5}}. Derived series and conditions are expressions over the series by id:

close / bb.lower - 1
rsi14 < 30 and close > sma200
crosses_above(ema20, ema50)
volume > 2 * mean(volume, 20)

Expressions are parsed with ast and evaluated over a whitelist of node types and functions; nothing reaches eval. Conditions use edge semantics (an event when the rule becomes true), as QuantDinger's signal monitoring does.

Hooks

BROKER_ANALYSIS_HOME=~/.broker-analysis          # hooks.json, alerts/*.jsonl, charts/
BROKER_ANALYSIS_WEBHOOK_ALLOW=https://hooks.example.com/   # webhooks are off without it
*/30 9-18 * * 1-5  broker-analysis-hooks          # evaluate saved hooks during market hours

Skills

broker-analysis/skills/ holds five skills: risk-analysis, fundamental-analysis, technical-analysis, etf-selection, custom-analysis. The plugin installs them with the server. They are written in English and tell the agent to answer in the analyst's language.

Layout

Path

What it is

broker-analysis/

The plugin: .claude-plugin/plugin.json, .codex-plugin/plugin.json, .mcp.json, skills/

.claude-plugin/marketplace.json

The Claude Code marketplace

.agents/plugins/marketplace.json

The Codex marketplace

src/, tests/, e2e/, docs/

The server package, its tests and its measurements

Release

Both .mcp.json and mcp.json launch the server from the same full 40-character commit. A release first commits the tested server with its new version in pyproject.toml, uv.lock and all three plugin manifests, then a packaging commit pins both launchers to that server commit. Tag the tested packaging commit. A cached Git tag still requires remote resolution on each start; a full commit lets uv reuse the environment and works offline after warming. tests/test_plugin_manifest.py checks manifest versions and immutable, matching launchers; the installed MCP smoke verifies the pinned package version and behavior.

Run

uv sync
uv run broker-analysis-mcp                      # stdio MCP server
uv run broker-analysis-monitor --config examples/portfolio-monitor.json  # illustrative portfolio
uv run pytest                                   # offline tests
uv run pytest -m live -o addopts=""             # MCP end to end on live Yahoo data
uv run python e2e/agent_e2e.py                  # a real agent answers analyst questions, scored (needs the claude CLI)
uv run ruff check .                             # lint, pinned to ruff 0.17.1

e2e/tsfm_eval.py retains optional Chronos research adapters; it needs torch and chronos-forecasting, which this project does not install.

Timestral CPU/GPU adapters and the local GPU model/runtime were removed after the benchmark failed to establish an advantage. The historical evaluation and measured results remain as evidence for that decision. Portfolio models use EWMA and the rolling 60-session baseline, with no foundation-model runtime dependency.

The portfolio monitoring command supports cron and Windows Task Scheduler with strict configuration validation, saved records and explicit exit codes. Supply current holdings and NAV; the example is illustrative and no schedule is installed.

Measured, not assumed

docs/VERIFICA.md records what was measured, including the formulas checked against independent implementations and the forecasting models (Mistral Timestral, Amazon Chronos-Bolt, Chronos-2 and Kronos) that were dropped for not beating naive baselines. The Kronos adapter was removed afterwards; it is at commit b996f52 of the Aura repository, where this project started, with the runs it produced.

Available Tools

11 tools
create_hookA

Save a condition hook: when the condition fires on a new bar, a JSON event is delivered.

action: {"type": "file", "target": "alerts_name"} appends JSON lines on the server, or {"type": "webhook", "target": "https://..."} posts JSON (only to prefixes the operator allowed). Past events are not replayed. Hooks are evaluated by run_hooks or the broker-analysis-hooks command (schedule it with cron). Analysis alerts only: nothing is ever traded.

Inputs: name (required), action (required), ticker (required), condition (required), +3 more.

custom_analysisA
Read-only

User-defined technical analysis on one ticker, with an optional chart.

indicators: catalog indicators with parameters, e.g. [{"id": "ema20", "fn": "ema", "params": {"n": 20}}, {"id": "rsi14", "fn": "rsi"}, {"id": "bb", "fn": "bollinger", "params": {"n": 20, "k": 2}}] series: derived series by expression, e.g. [{"id": "dist", "expr": "close / bb.lower - 1"}] conditions: true/false rules evaluated on every bar, e.g. [{"name": "Oversold in uptrend", "when": "rsi14 < 30 and close > sma200"}, {"name": "Golden cross", "when": "crosses_above(sma50, sma200)"}] For each: active now, number and dates of events, and forward returns after events against all bars (5 and 20 sessions) with a t statistic. levels: horizontal lines, e.g. [{"price": 25.5, "label": "support"}]. Returns JSON with last values and condition reports, plus the PNG chart when chart is true. Call list_indicator_catalog first to see indicators, parameters and functions.

Inputs: ticker (required), indicators (required), chart, title, +4 more.

delete_hookC
DestructiveIdempotent

Delete a saved hook by id.

Inputs: hook_id (required).

etf_analysisA
Read-only

ETF cost and replication quality: TER, tracking difference (fund minus benchmark, annualised) and tracking error against a benchmark ticker, which is converted to the fund's currency. Prefer a total-return index or a reference ETF on the same index.

Inputs: ticker (required), benchmark (required), period.

fundamental_ratiosA
Read-only

Ratios from the latest annual statements (USMx accounting and financial ratios sheet): valuation (P/E, EV/EBITDA, EV/EBIT, P/B, P/S, FCF yield), profitability and DuPont (ROE = net margin x asset turnover x equity multiplier, ROA, ROIC, margins), turnover (receivables, inventory, days sales/inventory/payables, cash conversion cycle), liquidity (current, quick, cash ratios, operating cash flow coverage, working capital), leverage and coverage, payout. With risk_free_annual and equity_risk_premium (decimals, from the analyst) also CAPM cost of equity, WACC, EVA and unlevered beta; beta defaults to Yahoo's. Also the Piotroski F-score (financial strength, 0-9), Altman Z and Z'' (distress) and Beneish M-score (earnings manipulation), with their components in scores; not computed for banks and insurers. Ratios not meaningful (losses, banks without current assets) are null.

Inputs: ticker (required), beta, risk_free_annual, equity_risk_premium.

list_hooksB
Read-only

Saved condition hooks with their condition, action and last delivered event.

Inputs: no named parameters.

list_indicator_catalogA
Read-only

Parametric building blocks for custom_analysis and create_hook: every catalog indicator with its typed parameters (default, min, max), outputs and pane; the functions allowed in expressions and conditions; the expression syntax.

Inputs: no named parameters.

peer_comparisonA
Read-only

Fundamental ratios and scores for several companies side by side (2 to 10 tickers), for relative valuation within one sector. Failed tickers are reported, not dropped silently. Score components and their inputs are left out: call fundamental_ratios for one company.

Inputs: tickers (required).

risk_metricsA
Read-only

Risk and performance of one security or index from daily total-return prices.

Returns CAGR, close-to-close and range-based volatility (Garman-Klass, Parkinson, Rogers-Satchell), Sharpe with its standard error, Probabilistic Sharpe Ratio, Sortino, Omega, maximum drawdown, CDaR, Calmar, historical and Cornish-Fisher one-day VaR and expected shortfall. With a benchmark ticker (converted to the security's currency) also daily beta and correlation, Bloomberg-style beta (two years of weekly returns, adjusted 0.67 raw + 0.33), Jensen's alpha, Treynor, M2, information ratio and tracking error, and Morningstar-style up and down capture on monthly returns. period: 1y, 2y, 3y, 5y, 10y or max. risk_free_annual as a decimal (0.03 = 3%).

Inputs: ticker (required), period, benchmark, confidence, +1 more.

run_hooksA

Evaluate every saved hook now and deliver the events newer than each hook's last one.

Inputs: no named parameters.

technical_indicatorsA
Read-only

Snapshot of standard technical indicators with adjustable parameters: close, two SMAs with the price's distance from each, RSI, MACD, Bollinger bands, ATR in currency and as % of the close, 52-week high and low with distances. Keys carry the period (sma50, rsi14, atr14). Use a period long enough for the slow SMA. For custom indicators, expressions, conditions or a chart use custom_analysis.

Inputs: ticker (required), period, sma_fast, sma_slow, +7 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 11 tools

    Disambiguation4/5

    Most tools target a clearly distinct analysis domain (ETF cost quality, risk/performance, standard technicals, custom expressions, fundamentals, peer comparison) plus a self-contained hook lifecycle. The main fuzziness is technical_indicators vs custom_analysis, which is largely a subset relationship, and slight overlap between etf_analysis and risk_metrics on tracking error/beta; descriptions explicitly redirect between them, which mitigates it.

    Naming Consistency4/5

    Two predictable patterns coexist: noun_phrase report tools (etf_analysis, risk_metrics, technical_indicators, fundamental_ratios, peer_comparison, custom_analysis) and verb_noun hook tools (list_hooks, create_hook, run_hooks, delete_hook, list_indicator_catalog). The split is semantically motivated and readable, though not a single uniform verb_noun scheme.

    Tool Count5/5

    11 tools is well within the sweet spot and every tool maps to a distinct capability: six analysis endpoints, one catalog/reference tool, and four hook CRUD/execution tools. Nothing feels padded or missing at the count level.

    Completeness4/5

    The hook lifecycle (create, list, run, delete) plus a broad analysis surface covers the stated domain with no dead ends for typical workflows. Minor gaps: no update/edit hook (must delete and recreate) and no way to inspect delivered event history beyond each hook's last event.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      A Model Context Protocol (MCP) server that provides comprehensive access to Yahoo Finance data through 18 specialized tools for pricing, financials, options, holders, and news.
      18
      30 PyPI
      1
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      A comprehensive MCP server that provides seamless access to Yahoo Finance stock market data, enabling retrieval of real-time quotes, historical data, charts, financial summaries, and market searches.
      28 npm
      ISC
    • F
      license
      Not graded
      quality
      D
      maintenance
      Production-grade MCP server that enables AI assistants to access real-time financial market data with intelligent fallback mechanisms, supporting stock prices, comparisons, fundamentals, and market summaries via Yahoo Finance and CSV.
      -
    • A
      license
      A
      quality
      B
      maintenance
      MCP server that exposes Yahoo Finance data through tools for searching instruments, fetching quotes, history, company info, financials, dividends, news, recommendations, and options. Enables AI assistants to answer market-data questions using natural language.
      24
      2,315 PyPI
      2
      MIT