Read-only MCP server: expose the validated portfolio core as stdio tools. Provides deterministic holdings, returns, and drawdown/risk analysis over local stdio. Every figure comes from the validated core (derive/returns/risk), read-only + offline after a one-time core warm.
14 tools with consistent naming patterns (verb_noun style), comprehensive descriptions (~200-400 chars each), and properly typed JSON schemas with enums. All tools are read-only with clear risk labeling. Strong parameter documentation with regex patterns and enum constraints. Output schemas are inferred from context but not explicitly documented in the code sample. Error handling focuses on graceful degradation (null/'n/a' values) rather than actionable recovery guidance. Tool descriptions are repetitive (all include the identical ~180-char offline/cache disclaimer) which adds bulk without discriminatory value for LLM selection. Schemas are present but validation and per-tool output structure documentation could be more explicit.
Suggest a target allocation by rule: equal-weight (1/N), inverse-volatility (each holding contributes ~equal risk), or a strategic preset (conservative / moderate / aggressive — fixed risk-posture buckets by role). Returns the target weights and a brief summary of the rule. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Historical simulation of an allocation rule (equal-weight, inverse-volatility, or strategic preset): rebalanced-to-target vs buy-and-hold over your entire history. Returns start/end dates, initial capital, rebalance schedule, final values, annualized returns, drawdown depth/duration/recovery, Ulcer, CDaR, Sharpe, volatility for each leg. Includes confidence intervals (95%) on drawdown metrics. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Historical simulation: how your portfolio compares to a reference (60-40, all-weather, permanent, or none=no benchmark). Returns start/end, initial capital, final values, annualized returns, drawdown depth/duration/recovery, Ulcer, CDaR, Sharpe, volatility for your portfolio and the benchmark. Includes confidence intervals (95%) on drawdown metrics and 'improved'/'worsened'/'inconclusive' verdicts on risk vs the benchmark. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Description bloat: all 14 tools share an identical ~180-character offline/cache disclaimer tacked to the end, consuming token budget without discriminatory signal. Moves signal-to-noise ratio down for LLM tool selection.
Output schemas not explicitly documented in source code. Tools like 'returns', 'risk', 'backtest_allocate' return complex nested structures (confidence intervals, per-leg metrics) but no visible schema definition for what fields the LLM should expect. Inferred from context only.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 59 | <=2025-11-25 | v2 |
Screen new candidate tickers for your portfolio: diversification (correlation check), cost (expense ratio), liquidity (trading volume), fund age, concentration impact, fund structure, overlap with holdings. Returns pass/fail and a brief reason for each check. Requires candidates passed as comma-separated tickers. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Guidance on your emergency cash reserve: recommended buffer in dollars and months of expenses based on risk horizon and loss response you choose. Helps you decide how much to keep in cash vs invested. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Suggest new tickers for roles your portfolio is light in, from the curated universe (bundled app/data/universe.csv). Each candidate is screened by the same checks as the candidates tool (diversification, cost, liquidity, age, concentration, structure, overlap). Bare call shows every gap except the sector-equity satellite; pass 'treasury' for a specific role's menu, or 'treasury:long' for one shelf within that role. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Your current holdings: ticker, shares, current price, market value, cost basis, unrealized gain, gain %, and fee drag. Sorted by market value (largest first). Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Holdings acquired on or after a date (YYYY-MM-DD), with current value and realized gain since acquisition. Sorted by market value. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Fund metadata: expense ratio, AUM, liquidity (daily volume), fund age, ETF/mutual fund/stock type, category (asset class, strategy). Source: yfinance fund facts, cached 7 days. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
One holding in detail: ticker, current shares/price/value, cost basis, unrealized gain, gain %, TWR since acquisition, drawdown since acquisition (depth/duration/recovery), volatility. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Current price for a ticker: the on-disk cache value if available; otherwise fetches live (a one-time online hit if the cache is cold). Returns source (cache vs ticker), and last-update timestamp. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Check current allocation vs your target (if set): how far each holding has drifted (as a % of total, both absolute and relative), and a count of holdings over/under target. Requires ASSET_TARGET (set in .env, the extension config, or pass via --target). Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Portfolio returns: time-weighted return (TWR) annualized, total return dollars, CAGR since inception, alpha vs 60-40 benchmark, best/worst day. Includes confidence intervals (95%) on TWR and alpha. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Portfolio risk (drawdown-first): maximum drawdown depth/duration/recovery, Ulcer Index, Conditional Drawdown at Risk (CDaR), Sharpe ratio, volatility annualized. Includes confidence intervals (95%) on depth/Ulcer/CDaR. Read-only: figures are derived from your transaction log and the on-disk price cache. Uncached prices are fetched online on demand — a one-time core warm on the first cold call, plus any new ticker you ask about (set ASSET_MCP_OFFLINE=1 to keep it strictly offline); a value still unavailable shows null (n/a), never a guess. This is a view, not financial advice.
Error handling lacks recovery guidance. Tools return null/'n/a' for unavailable prices, but descriptions do not tell the LLM what action to take: 'Price unavailable. Try calling price() directly, or set ASSET_MCP_OFFLINE=0 to enable auto-fetch.' Missing recovery paths.
Parameter 'roles' in 'discover' tool has a string type with description but no enum constraint or length limit. Description says 'optional role name or role:shelf', but LLM could pass invalid values like 'invalidrole' or 'role:shelf:extra'. Needs formal enum or pattern constraint.
Tools returning lists ('candidates', 'discover') lack pagination parameters (limit, offset). If a candidate universe contains hundreds of tickers, all are returned at once, bloating context. No documented result limit.
Tool 'backtest_allocate' and 'benchmark_compare' return per-leg or portfolio+benchmark metrics (Sharpe, volatility, drawdown with 95% CI). No schema documents what structure the LLM should parse, e.g., is it {portfolio: {...}, benchmark: {...}} or {legs: [{name, metrics}]} or flat? LLM must infer.