{
  "markdown": "# okama-mcp\n\n<!-- mcp-name: io.github.mbk-dev/okama-mcp -->\n\n[![PyPI](https://img.shields.io/pypi/v/okama-mcp)](https://pypi.org/project/okama-mcp/)\n[![CI](https://github.com/mbk-dev/okama-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/mbk-dev/okama-mcp/actions/workflows/ci.yml)\n[![Python](https://img.shields.io/pypi/pyversions/okama-mcp)](https://pypi.org/project/okama-mcp/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n![okama-mcp — investment analytics for AI assistants](https://raw.githubusercontent.com/mbk-dev/okama-mcp/main/docs/images/announce-en.png)\n\nMCP (Model Context Protocol) server that exposes the [okama](https://github.com/mbk-dev/okama)\ninvestment portfolio toolkit to AI assistants — Claude Desktop, Claude Code, Cursor, Codex,\nand any other MCP-compatible client.\n\nWith okama-mcp installed, you can ask an AI things like:\n\n> *\"Backtest a portfolio of 30% gold and 70% real estate over the last 15 years.\"*\n>\n> *\"Run a Monte Carlo retirement forecast on that portfolio, withdrawing $1,000/month\n> indexed to inflation, over 25 years.\"*\n>\n> *\"What's the tangency portfolio of SPY, BND, and GLD with a 3% risk-free rate?\"*\n\n…and the AI uses the MCP tools to call okama directly — no Python code needed.\n\nBuilt on [FastMCP](https://github.com/jlowin/fastmcp). Single codebase, two transports:\n`stdio` (for local clients) and `streamable-http` (for self-hosting).\nokama-mcp is free and open source — no hosted service, no registration; you run it\nyourself, locally or on your own server.\n\n## Install\n\nRequires Python ≥ 3.11 (same floor as okama itself); okama ≥ 2.2.0 is installed automatically.\n\nThe easiest way — no clone, no venv — is [uv](https://docs.astral.sh/uv/) or pipx:\n\n```bash\nuvx okama-mcp stdio          # run straight from PyPI\n# or\npipx install okama-mcp\n```\n\nPlain pip works too:\n\n```bash\npip install okama-mcp\n```\n\n> [!WARNING]\n> <sub>With pip, prefer a dedicated virtual environment: on most modern Linux distros the\n> system Python is marked externally managed (PEP 668), so `pip install` outside a venv\n> fails, and a shared environment risks dependency conflicts. In your MCP client config,\n> point `command` at the absolute path of the `okama-mcp` script inside the venv — GUI\n> clients don't see your shell `PATH`. `uvx` and `pipx` avoid all of this by isolating\n> the install automatically.</sub>\n\nTo work on the code, install from source instead:\n\n```bash\ngit clone https://github.com/mbk-dev/okama-mcp\ncd okama-mcp\npoetry install\n```\n\n## Run\n\n```bash\n# stdio — for Claude Desktop, Claude Code, Cursor (local IPC)\nokama-mcp stdio\n\n# streamable HTTP — for self-hosting on your own server\nokama-mcp http --host 127.0.0.1 --port 8765\n```\n\nWhen running from a source checkout, prefix each command with `poetry run`.\n\n## Connect a client\n\n### Claude Desktop\n\nEdit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or\n`%APPDATA%\\Claude\\claude_desktop_config.json` (Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"okama\": {\n      \"command\": \"uvx\",\n      \"args\": [\"okama-mcp\", \"stdio\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop; the server appears in the tools menu.\n\n### Claude Code\n\nTo make the server available in **every** project (works from any directory):\n\n```bash\nclaude mcp add --scope user okama -- uvx okama-mcp stdio\n```\n\nDevelopers running from a source checkout can use `claude mcp add okama -- poetry run okama-mcp stdio` from the project root instead.\n\nOr commit a `.mcp.json` at the project root so the whole team picks it up:\n\n```json\n{\n  \"mcpServers\": {\n    \"okama\": {\n      \"command\": \"uvx\",\n      \"args\": [\"okama-mcp\", \"stdio\"]\n    }\n  }\n}\n```\n\n### Cursor\n\nAdd the server to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` to make\nit global):\n\n```json\n{\n  \"mcpServers\": {\n    \"okama\": {\n      \"command\": \"uvx\",\n      \"args\": [\"okama-mcp\", \"stdio\"]\n    }\n  }\n}\n```\n\n### Codex (CLI & Desktop)\n\nAdd the server with one command:\n\n```bash\ncodex mcp add okama -- uvx okama-mcp stdio\n```\n\nOr declare it in `~/.codex/config.toml` (or a project-scoped `.codex/config.toml`\nin trusted projects):\n\n```toml\n[mcp_servers.okama]\ncommand = \"uvx\"\nargs = [\"okama-mcp\", \"stdio\"]\n```\n\nThe Codex CLI, desktop app, and IDE extension share this configuration — set it up\nonce and it works in all three.\n\n### Self-hosting (streamable HTTP)\n\nRun okama-mcp on your own server and share it across your MCP clients:\n\n```bash\nokama-mcp http --host 127.0.0.1 --port 8765 --path /mcp\n```\n\n(From source: `poetry run okama-mcp http ...`)\n\nThen point your MCP client at `http://<your-server>:8765/mcp`. For a production\nsetup put nginx + TLS in front; ready-made examples live in `deploy/`:\n\n- `deploy/systemd/okama-mcp.service` — systemd unit (hardened, runs as a dedicated user)\n- `deploy/nginx/self-hosted.conf` — nginx vhost: TLS, SSE-friendly proxying of `/mcp`\n\nThe server is open by design — free to run, no registration. If your instance must\nnot be public, restrict access at the nginx level (allow-list, VPN, or HTTP basic auth).\n\n## Tool catalog\n\nA multi-stage financial plan (contribute $1,000/month for 20 years into a 70/30\nSPY/AGG portfolio, then withdraw $6,000/month indexed to inflation for 25 years),\na Monte Carlo retirement forecast (30% gold / 70% real estate, withdrawing $1,000/month\nindexed to inflation over 25 years) and the efficient frontier of SPY/BND/GLD:\n\n![Financial-plan forecast fan — percentile bands with dashed stage boundaries](https://raw.githubusercontent.com/mbk-dev/okama-mcp/main/docs/images/finplan-forecast.png)\n\n![Monte Carlo forecast fan — percentile bands of future wealth](https://raw.githubusercontent.com/mbk-dev/okama-mcp/main/docs/images/monte-carlo-forecast.png)\n\n![Efficient frontier — SPY.US, BND.US, GLD.US (USD)](https://raw.githubusercontent.com/mbk-dev/okama-mcp/main/docs/images/efficient-frontier.png)\n\nAll tools are **stateless** — pass the full portfolio specification with every call.\nThe server caches expensive okama objects (`Portfolio`, `EfficientFrontier`) by content\nhash, so repeated calls on the same spec are fast.\n\n**Nested portfolios.** Wherever a list of assets is accepted — the `assets` field of\n`PortfolioSpec`/`FrontierSpec`, or the `portfolios` argument on the comparison tools —\nan entry may be a ticker string **or a nested portfolio object** (the same spec shape).\nThis lets you treat a whole portfolio as a single component: e.g. compare a 60/40\nportfolio against gold, or put a sub-portfolio on the efficient frontier.\n\n### Search & metadata\n\n| Tool | Purpose |\n|---|---|\n| `search_assets(query=\"\", namespace?, asset_type?, oldest_first?, limit=50)` | Free-text search by name / local name / ticker / ISIN. Filter by okama type and sort by `first_date`; for example, `namespace=\"MOEX\", asset_type=\"ETF\", oldest_first=true, limit=5` finds the five oldest MOEX-listed BPIFs. |\n| `list_namespaces(kind=\"all\"\\|\"assets\"\\|\"macro\")` | Show the available okama namespaces. |\n| `get_asset_info(symbol)` | Metadata for one symbol — name, country, currency, type, date range. |\n\n### Single asset & comparisons\n\n| Tool | Purpose |\n|---|---|\n| `get_asset_history(symbol, kind, first_date?, last_date?)` | Time series for one asset. `kind` ∈ {`close_monthly`, `close_daily`, `adj_close`, `ror`, `dividends`}. |\n| `compare_assets(symbols, ccy, ..., portfolios?, rf_return?, t_return?)` | Side-by-side statistics (`describe()` table: CAGR, risk, drawdowns by period) plus Sharpe & Sortino per asset. |\n| `get_correlations(symbols, ccy, ..., portfolios?)` | Correlation matrix of monthly returns. |\n| `get_rolling_risk(symbols, ccy, window_months=12, ..., portfolios?)` | Rolling annualized risk per asset. |\n| `get_asset_returns(symbols, ccy, ..., portfolios?, period?, real=False)` | Return metrics per asset: CAGR, cumulative return, mean / real mean return, monthly geometric mean, annual returns table. |\n| `get_rolling_returns(symbols, ccy, window_months=12, real=False, ..., portfolios?)` | Rolling CAGR and rolling cumulative return per asset. |\n| `get_benchmark_metrics(benchmark, symbols, ccy, ..., portfolios?, rolling_window?)` | Beta, correlation, annualized tracking difference and tracking error of each asset vs a benchmark/index. |\n| `get_dividend_info(symbols, ccy, ...)` | LTM dividend yield, 5y mean yield, paying/growing streaks per asset. |\n\n### Portfolio backtest\n\n| Tool | Purpose |\n|---|---|\n| `analyze_portfolio(portfolio, rf_return=0, t_return=0)` | Headline metrics (CAGR, annual mean/risk, **Sharpe, Sortino**) + full `describe()` for a `PortfolioSpec`. |\n| `get_portfolio_drawdowns(portfolio)` | Drawdown time series + max drawdown / recovery period. |\n| `get_portfolio_var_cvar(portfolio, time_frame=12, level=1)` | Historical Value at Risk and CVaR. |\n| `get_portfolio_wealth_index(portfolio, full=False)` | Wealth-index series (cumulative growth of 1000). |\n| `get_rolling_cagr(portfolio, window_months=12, real=False)` | Rolling CAGR time series (optionally inflation-adjusted). |\n| `get_cagr_probability(portfolio, years, cagr_target)` | Historical probability of CAGR below a target (e.g. of a loss) over N-year periods. |\n\n### Monte Carlo DCF\n\n| Tool | Purpose |\n|---|---|\n| `monte_carlo_forecast(portfolio, mc, cashflow)` | Forward simulation with one of five cash-flow strategies (`indexation`, `percentage`, `time_series`, `vanguard`, `cut_if_drawdown`). Returns percentile wealth bands, terminal-wealth stats, survival metrics. Includes the money-weighted IRR distribution (percentiles + mean). |\n| `get_portfolio_irr(portfolio, cashflow)` | Historical money-weighted return (IRR) for a contribution/withdrawal plan. |\n| `find_the_largest_withdrawals_size(portfolio, mc, cashflow, goal, ...)` | Largest sustainable withdrawal (Monte Carlo) for a `goal`: keep real purchasing power, keep nominal balance, or survive N years. |\n| `get_monte_carlo_cash_flow(portfolio, mc, cashflow, discounting?)` | Monte Carlo distribution of future cash flows over time (percentile bands). |\n\nThe `mc` argument accepts `distribution_parameters` to override the fitted distribution (e.g. a fixed Student-t `df`); see the MCSpec shape below.\n\n### Financial plan (multi-stage)\n\nA plan is an ordered sequence of stages, each with its own portfolio, horizon and\ncash-flow regime. Scenarios are chained: the balance a Monte Carlo scenario ends a\nstage with is the balance it starts the next one with, so the retirement stage is\nfunded by whatever the accumulation stage produced **in that same scenario** — not by\na percentile of it. Use these instead of `monte_carlo_forecast` whenever the portfolio\nor the contribution/withdrawal regime changes partway through the horizon.\n\n| Tool | Purpose |\n|---|---|\n| `finplan_forecast(plan, success_threshold=0)` | Monte Carlo forecast of the whole plan: percentile wealth bands, terminal-wealth stats, survival metrics, the share of scenarios finishing above `success_threshold`, the balance distribution at every stage boundary, and the IRR distribution. |\n| `finplan_backtest(plan, discounting?, first_date?)` | Replay the same plan over real history — a glide-path backtest. Requires the window covered by every stage portfolio to be at least as long as the plan. |\n\n\n### Distribution diagnostics\n\n| Tool | Purpose |\n|---|---|\n| `get_distribution_fit(portfolio, mc)` | Goodness-of-fit for the return distribution: fitted parameters, Jarque-Bera, Kolmogorov-Smirnov (chosen + all distributions), and backtesting error (theoretical vs empirical mean/VaR/CVaR). |\n| `get_return_moments(portfolio, mc, rolling_window?)` | Skewness & kurtosis time series — expanding, or rolling when a window (months) is given. |\n| `optimize_students_df(portfolio, mc, var_level?)` | Degrees of freedom for a Student-t that best matches empirical VaR/CVaR. |\n| `get_cagr_distribution(portfolio, mc, percentiles?, score?)` | Simulated CAGR at each percentile, plus the probability of a CAGR at/below `score` (e.g. `score=0` → probability of a loss). |\n\n### DCF (historical cash-flow analysis)\n\n| Tool | Purpose |\n|---|---|\n| `get_dcf_wealth_index(portfolio, cashflow, discounting?, include_negative_values?, discount_rate?)` | Historical wealth index with the cash-flow plan (FV nominal or PV discounted). |\n| `get_dcf_cash_flow_ts(portfolio, cashflow, discounting?, remove_if_wealth_index_negative?, discount_rate?)` | Historical contribution/withdrawal time series (FV or PV). |\n| `get_dcf_wealth_with_assets(portfolio, cashflow)` | Historical wealth index for the portfolio and each underlying asset. |\n| `get_survival_period(portfolio, cashflow, threshold?, discount_rate?)` | Historical longevity: survival period (years) and depletion date. |\n| `get_initial_investment_values(portfolio, cashflow, discount_rate?)` | Present value (PV) and future value (FV) of the initial investment. |\n\n### Efficient Frontier\n\n| Tool | Purpose |\n|---|---|\n| `build_efficient_frontier(frontier)` | Full EF point table (Risk / Mean return / CAGR + per-asset weights). |\n| `get_tangency_portfolio(frontier, rf_return, rate_of_return)` | Max-Sharpe portfolio on the EF. |\n| `get_min_variance_portfolio(frontier)` | Global Minimum Variance portfolio. |\n| `get_most_diversified_portfolio(frontier, target_return?)` | Most Diversified Portfolio (maximises the diversification ratio) on the EF. |\n\n### Macro\n\n| Tool | Purpose |\n|---|---|\n| `get_inflation(currency, first_date?, last_date?, include_cumulative?, include_rolling?, include_describe?)` | Inflation series for a currency (`USD`, `EUR`, `RUB`, …). Optional: cumulative inflation, 12-month rolling inflation, describe() table. |\n| `get_central_bank_rate(country, first_date?, last_date?, frequency=\"monthly\"\\|\"daily\", include_describe?)` | Central-bank policy rate (`US`→US_EFFR, `EU`/`ECB`→EU_MRO, `RUS`→RUS_CBR, `UK`/`GB`→UK_BR, `ISR`→ISR_IR, `CN`/`CHN`→CHN_LPR1, or full symbol). Monthly or daily series; optional describe() table. |\n| `get_indicator(symbol, first_date?, last_date?, include_describe?)` | Macro indicator from the RATIO namespace (e.g. `USA_CAPE10.RATIO`); bare country code defaults to that country's CAPE10. |\n\n### Charts\n\nEach tool renders a PNG (default 1500×900) and returns it as MCP image content —\nclients like Claude Desktop display it inline. Every chart tool also accepts\noptional `width` / `height` (pixels, 300–4000) for custom sizes and aspect ratios,\nand an optional `save_path` — the chart is then also written to that file and the\npath reported back. Use `save_path` in clients that don't render MCP images in\ntheir UI (e.g. Claude Code's terminal): ask for a chart \"saved to /tmp/chart.png\"\nand open the file reference. Note: in self-hosted (streamable-http) deployments\n`save_path` is written on the **server's** filesystem, not the client's machine.\n\n| Tool | Chart |\n|---|---|\n| `plot_wealth_index(portfolio)` | Portfolio wealth index (+ inflation line). |\n| `plot_drawdowns(portfolio)` | Drawdown depth over time. |\n| `plot_monte_carlo(portfolio, mc, cashflow)` | Monte Carlo forecast fan (percentile bands). |\n| `plot_finplan_forecast(plan)` | Financial-plan forecast fan: percentile bands with dashed stage boundaries and stage labels. |\n| `plot_irr_distribution(portfolio, mc, cashflow)` | Histogram of IRR across Monte Carlo scenarios (percentile markers). |\n| `plot_qq(portfolio, mc)` | Q-Q plot of historical returns against the fitted distribution (norm/lognorm/t). |\n| `plot_hist_fit(portfolio, mc, bins?)` | Histogram of historical returns with the fitted distribution PDF overlaid. |\n| `plot_efficient_frontier(frontier)` | EF curve with individual asset points. |\n| `plot_transition_map(frontier, x_axe=\"risk\")` | Transition map: asset weights along the efficient frontier (x-axis = risk or CAGR). |\n| `plot_assets(symbols, ccy, ..., portfolios?)` | Wealth-index comparison of individual assets. |\n| `plot_macro(symbols, first_date?, last_date?, frequency=\"monthly\"\\|\"daily\")` | Line chart of inflation / central-bank rate / CAPE10 series. Overlay multiple symbols (e.g. `[\"USA_CAPE10.RATIO\", \"EUR_CAPE10.RATIO\"]`). `frequency='daily'` valid only for `.RATE` symbols. |\n\n## Spec shapes\n\nThe complex tools take typed dicts validated by pydantic. The full schemas live in\n`src/okama_mcp/schemas.py`; here are the headline shapes:\n\n```jsonc\n// PortfolioSpec\n{\n  \"assets\":   [\"GLD.US\", \"VNQ.US\"],  // each entry: a ticker OR a nested PortfolioSpec\n  \"weights\":  [0.3, 0.7],            // optional, must sum to 1.0\n  \"ccy\":      \"USD\",\n  \"first_date\": \"2010-01\",\n  \"last_date\":  \"2024-12\",\n  \"rebalancing_strategy\": {            // mirrors okama.Rebalance\n    \"period\": \"year\",                  // month | quarter | half-year | year | none\n    \"abs_deviation\": 0.05,             // optional, |actual - target| threshold, 0 < x <= 1\n    \"rel_deviation\": 0.1               // optional, |actual / target - 1| threshold, > 0\n  },\n  \"inflation\": true\n}\n\n// MCSpec\n{\n  \"distribution\":  \"norm\",            // norm | lognorm | t\n  \"period_years\":  25,\n  \"scenarios\":     500,                // ≥ 1, no upper limit\n  \"percentiles\":   [5, 50, 95],\n  \"random_seed\":   42,                 // optional, for reproducibility\n  \"distribution_parameters\": null      // optional; null = fit from history (MLE). Lengths: norm [mu, sigma]; lognorm/t [shape|df, loc, scale]. Any element null = fit that one (e.g. [4, null, null])\n}\n\n// CashflowSpec — discriminated by `type`\n{ \"type\": \"indexation\",       \"initial_investment\": 1000000, \"frequency\": \"month\", \"amount\": -1000, \"indexation\": \"inflation\" }\n{ \"type\": \"percentage\",       \"initial_investment\": 1000000, \"frequency\": \"year\",  \"percentage\": -0.04 }\n{ \"type\": \"time_series\",      \"initial_investment\": 100000,  \"events\":    { \"2030-06\": -50000 }, \"time_series_discounted_values\": false }\n{ \"type\": \"vanguard\",         \"initial_investment\": 1000000, \"percentage\": -0.04, \"floor_ceiling\": [-0.025, 0.05], \"indexation\": \"inflation\" }\n{ \"type\": \"cut_if_drawdown\",  \"initial_investment\": 1000000, \"frequency\": \"year\",  \"amount\": -60000, \"indexation\": \"inflation\",\n  \"crash_threshold_reduction\": [[0.2, 0.4], [0.5, 1.0]] }\n\n// FinPlanSpec — a plan is a sequence of stages, chained per scenario\n{\n  \"stages\": [\n    { \"portfolio\": { \"assets\": [\"SPY.US\", \"AGG.US\"], \"weights\": [0.7, 0.3] },\n      \"period_years\": 20, \"name\": \"accumulation\",\n      \"cashflow\": { \"type\": \"indexation\", \"initial_investment\": 100000, \"frequency\": \"year\", \"amount\": 12000, \"indexation\": 0.03 } },\n    { \"portfolio\": { \"assets\": [\"SPY.US\", \"AGG.US\"], \"weights\": [0.3, 0.7] },\n      \"period_years\": 25, \"name\": \"retirement\",\n      \"distribution\": \"t\", \"distribution_parameters\": [5, null, null],\n      \"cashflow\": { \"type\": \"percentage\", \"initial_investment\": 100000, \"frequency\": \"year\", \"percentage\": -0.04 } }\n  ],\n  \"initial_investment\": 100000,       // balance the first stage starts with\n  \"discount_rate\": null,               // optional; null lets okama use inflation\n  \"scenarios\": 500,\n  \"random_seed\": 42,                   // optional\n  \"percentiles\": [10, 50, 90],\n  \"name\": \"retirement plan\"\n}\n\n// FrontierSpec\n{\n  \"assets\":   [\"SPY.US\", \"BND.US\", \"GLD.US\"],\n  \"ccy\":      \"USD\",\n  \"bounds\":   [[0.0, 0.7], [0.1, 1.0], [0.0, 0.3]],   // optional\n  \"n_points\": 20,\n  \"rebalancing_strategy\": { \"period\": \"year\" },\n  \"inflation\": false\n}\n\n// Nesting — a portfolio used as a single component (works in PortfolioSpec /\n// FrontierSpec `assets`, and the `portfolios` argument of the comparison tools):\n{\n  \"assets\": [\n    \"GLD.US\",\n    { \"assets\": [\"SPY.US\", \"AGG.US\"], \"weights\": [0.6, 0.4], \"symbol\": \"bench6040.PF\" }\n  ],\n  \"weights\": [0.3, 0.7]              // one weight per top-level entry\n}\n```\n\n## Development\n\nThe project follows TDD (see `AGENTS.md`). After every code change run:\n\n```bash\npoetry run pytest -q\npoetry run ruff check .\n```\n\nTo run the live-API integration test (hits `api.okama.io`):\n\n```bash\npoetry run pytest -m integration\n```\n\n## Project layout\n\n```\nsrc/okama_mcp/\n├── server.py          # FastMCP instance + registration entry point\n├── transport.py       # CLI: `okama-mcp stdio | http`\n├── schemas.py         # PortfolioSpec, MCSpec, CashflowSpec, FrontierSpec, FinPlanSpec\n├── cache.py           # TTL+LRU cache keyed by sha256 of canonical spec\n├── serialization.py   # pandas → JSON-safe with smart truncation\n├── errors.py          # Translate okama exceptions to actionable MCP errors\n└── tools/\n    ├── search.py, asset.py, asset_list.py\n    ├── portfolio.py, monte_carlo.py, finplan.py\n    ├── frontier.py, macro.py\n    └── plots.py\n```\n\n## License\n\n[MIT](LICENSE) — same license as okama itself.\n",
  "bytes": 20555,
  "sha": "d0bec8ca2c232b6820480c5551155b3eac2422adcc6711e8dbbf5171d936bc7d",
  "repo_slug": "mbk-dev/okama-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mbk_dev_okama_mcp_b01120d1/readme"
}