InvestMCP
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| KIS_CANO | No | 한국투자증권 계좌번호 (CANO) | |
| KIS_APP_KEY | No | 한국투자증권 앱 키 | |
| INVESTMCP_DEMO | No | 켜면 계좌·포트폴리오 도구가 합성 데모 포트폴리오를 쓴다 | |
| INVESTMCP_HOST | No | HTTP 바인드 주소 | 127.0.0.1 |
| INVESTMCP_PORT | No | HTTP 포트 | 18800 |
| KIS_APP_SECRET | No | 한국투자증권 앱 시크릿 | |
| MCP_AUTH_TOKEN | No | HTTP 베어러 토큰. HTTP 전송에 필수 | |
| MCP_PUBLIC_URL | No | 공개 HTTPS 오리진. OAuth 리소스 식별자로도 쓴다 | |
| SEC_USER_AGENT | No | SEC 요청 User-Agent (애플리케이션 식별자와 연락처) | investmcp/0.1.0 (contact: you@example.com) |
| TOSS_CLIENT_ID | No | 토스증권 Open API 클라이언트 ID | |
| BINANCE_API_KEY | No | Binance 읽기 전용 API 키 | |
| KIS_ACNT_PRDT_CD | No | 한국투자증권 계좌 상품 코드 | |
| KIS_ACCOUNTS_JSON | No | 여러 한국투자증권 계좌 설정 (JSON) | |
| MCP_OAUTH_ENABLED | No | OAuth 켜기 | |
| BINANCE_API_SECRET | No | Binance API 시크릿 | |
| INVESTMCP_DATA_DIR | No | 원장, NAV, OAuth, 토큰, 캐시를 저장하는 디렉터리 | ~/.investmcp |
| TOSS_ACCOUNTS_JSON | No | 여러 토스증권 계좌 설정 (JSON) | |
| TOSS_CLIENT_SECRET | No | 토스증권 Open API 클라이언트 시크릿 | |
| BINANCE_SIGNING_KEY | No | 시크릿 대신 쓰는 Binance RSA 서명 키 (PEM 텍스트) | |
| INVESTMCP_TRANSPORT | No | 기본 전송 방식 (`stdio` 또는 `streamable-http`) | stdio |
| MCP_OAUTH_CLIENT_ID | No | 수동 등록 OAuth 클라이언트 ID | |
| MCP_OAUTH_TOKEN_TTL | No | 액세스 토큰 수명 (초) | |
| MCP_OAUTH_CLIENT_CAP | No | 동적 등록 클라이언트 최대 수 | |
| KIS_HISTORY_MAX_PAGES | No | 이력 조회 한 번에 읽는 최대 페이지 수 | |
| KIS_RATE_LIMIT_RETRIES | No | 요청 제한 응답 뒤 재시도 횟수 (최대 2) | |
| MCP_OAUTH_APPROVAL_HASH | No | 동의 페이지 승인 암호의 scrypt 해시 | |
| MCP_OAUTH_CLIENT_SECRET | No | 수동 등록 OAuth 클라이언트 시크릿 | |
| KIS_MIN_REQUEST_INTERVAL | No | 한국투자증권 요청 사이 최소 간격 (초) | |
| TICKER_TECHNICAL_ENABLED | No | `get_ticker_research`의 기술적 지표 계산 켜기·끄기 | |
| MCP_OAUTH_REFRESH_TOKEN_TTL | No | 리프레시 토큰 수명 (초) | |
| CONSENT_GATE_REDIRECT_ORIGINS | No | 허용할 OAuth 리다이렉트 HTTPS 오리진의 JSON 목록 | |
| HYPERLIQUID_ACCOUNT_ADDRESSES | No | 조회할 본인 Hyperliquid 주소, 쉼표로 구분 | |
| INVESTMCP_LEDGER_FULL_BACKFILL | No | 켜면 요청마다 모든 이력 구간을 브로커에서 다시 읽는다 | |
| MCP_OAUTH_REFRESH_GRACE_SECONDS | No | 회전된 리프레시 토큰의 재사용 유예 시간 (초). 이후 재사용하면 토큰 계열 전체를 폐기한다 | |
| MCP_OAUTH_STATIC_CLIENT_ENABLED | No | 수동 등록 클라이언트 허용 여부 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_candlesA | Get candlestick chart data (OHLCV) for a stock (Toss). Args: symbol: Stock symbol (e.g., "AAPL", "005930") interval: Candle interval — "1d" for daily or "1m" for 1-minute count: Number of candles to retrieve (max 200, default 100) adjusted: Request provider adjustments (default True); actual basis unverified. before: Provider cursor; inclusive local timestamp filter, upstream boundary unverified. start_date/end_date: Inclusive YYYY-MM-DD candle-local labels. One page per call. Returns: Raw provider payload for existing calls. Paging calls add returned_range, next_cursor, complete (page chain only, not historical coverage), limit, result_status/reason_codes and source_metadata. Merge pages ascending by timestamp and deduplicate; adjusted prices are not verified total returns. |
| get_sec_financialsA | Get SEC financials for US stocks: up to five annual fiscal years, quarterly facts, derived TTM, earnings quality, and segment revenue when available. Missing or incomplete data is marked unavailable or carries warnings; quarters/TTM/segments are not guaranteed for every issuer. Uses CompanyFacts and filings (10-K/10-Q; applicable amended forms). No API key required. Uses SEC_USER_AGENT for fair access. Args: symbol: US stock ticker (e.g., "AAPL", "MSFT") Returns: Structured JSON envelope with annuals, quarters, TTM, quality and segment data. |
| get_ttm_financialsB | Get SEC 10-Q/FY-derived TTM financials for a US stock. |
| get_insider_tradesA | Get insider trading data from SEC Form 4 filings (US stocks only). Shows insider purchases (code P) and sales (code S), plus option exercises, grants, tax withholdings, etc. Each trade includes the insider name, title, shares, price, and whether it's a 10% operator. Args: symbol: US stock ticker (e.g., "AAPL") days_back: How many days back to search (default 180, max 365) Returns: Structured JSON envelope with insider trade records. |
| get_financialsA | Get comprehensive financial data from yfinance (US, Korea, Japan). Provides income statement, balance sheet, cash flow, valuation metrics, and profitability ratios from yfinance. Works with US stocks ("AAPL"), Korean stocks ("005930.KS"), and Japanese stocks ("7203.T"). Args: symbol: Stock ticker. US: "AAPL", Korean: "005930.KS", Japanese: "7203.T" Returns: Structured JSON envelope with financial statements and key metrics. |
| get_key_metricsA | Get key financial metrics summary from yfinance (US, Korea, Japan). Returns P/E, PEG, P/B, EV/EBITDA, margins, ROE, ROA, growth rates, etc. Args: symbol: Stock ticker. US: "AAPL", Korean: "005930.KS", Japanese: "7203.T" Returns: Structured JSON envelope with key financial metrics. |
| get_stock_snapshotB | Return a compact stock snapshot with quotes, financial metrics, provider analyst consensus, and insider data. |
| export_portfolio_datasetA | Freeze an auditable portfolio dataset locally, then return small pages. Only for an explicit request to export, save or freeze a dataset. A dated portfolio value series is get_portfolio_returns(include_series=true), which stores nothing. Transaction exports default to YTD (365 days with end_date alone); pass start_date (e.g. 2024-01-01) or period="ALL" for older history. Writes only local dataset copies and never places orders or uploads data. First call (no dataset_id) captures normalized inputs through existing engine paths. Later calls supply dataset_id and next_cursor as cursor; they read only the frozen copy, with no provider/FX refetch. Returned manifest identifies the original dates and filters; replay does not reapply collection parameters. IDs are content hashes. The latest 20 datasets are retained; expired/unknown IDs return DATASET_EXPIRED and never silently rebuild. Start a new first call explicitly to collect changed inputs. Missing money stays null with a status. JSON rows have record_type, row_id, data. CSV has those first two columns and data_json (canonical JSON preserving nested fields, null and statuses). Supporting holdings, cash, source coverage, cost FX, quantity breaks and NAV components are paged audit records. Manifest descriptors name their row types; read every page to reconstruct/hash the entire dataset. Totals sum frozen input money by type/currency; reporting summaries retain their stated period. Account IDs are opaque keyed hashes. Human account labels are retained only when already exposed, and raw broker payloads/credentials are excluded. Args: dataset: transactions, realized_pnl, holdings, or nav_history period: reporting period (default YTD); explicit ISO dates override it start_date: ISO YYYY-MM-DD; realized inputs include earlier basis history end_date: ISO YYYY-MM-DD. Holdings export is current, not historic backfill dataset_id: frozen ID for replay; omit only for a new collection cursor: opaque next_cursor from the preceding page; requires dataset_id format: json or csv; both carry the same typed audit rows page_size: 1–200, additionally limited to 64 KiB of row data per page account: existing account filter; nav_history requires all accounts symbol: optional existing symbol filter; not applicable to nav_history input_snapshot_ids: nav_history only, exact E-store versions; bounds use their dates when no explicit dates are supplied |
| snapshot_portfolio_navA | Store today's portfolio NAV snapshot for history and TWR (local write). Only on an explicit request to store a snapshot now: the scheduled 18:10 KST job stores
the daily NAV, and reading returns or history needs no snapshot.
Re-running appends an immutable version and moves that date's representative, unless the scheduled
18:10 capture with crypto evidence holds it (snapshot.representative false). Writes only derived
valuations to the local SQLite store; no broker state is modified. Intended for
a daily schedule (see Args: snapshot_date: ISO YYYY-MM-DD (default: today); other dates are refused because collection uses current prices, not historical NAV. Returns: Structured JSON envelope with the stored snapshot. |
| import_statementA | Import Toss stock BUY/SELL rows from a private inbox PDF statement into the local ledger. Two steps. 1) file=".pdf": parses it and returns the preview and the import plan (nothing is written); keep preview_id. 2) preview_id plus confirm=true stores the planned rows locally. A blocked plan (parse issues, conflicts, unmatched rows inside API coverage) cannot be confirmed. Never places orders; previews expire after 15 minutes. |
| get_statusA | Connector status: cached service health, live source probes, configured accounts, or the usage guide. mode="passive" reads cached status only (no upstream calls); "probe" calls each market-data source once; "accounts" lists the configured broker accounts and checks each one live; "help" returns the usage guide for this tool list (topic: overview, quotes, portfolio, financials, futures, futures_account, diagnostics, examples, all). |
| get_holdingsA | Current positions, merged per symbol in native currency and KRW, plus cash. account: "all", "toss", "kis", or one account label ("toss:primary", "kis:pension"). detail_level "summary" lists the top 15 positions; "full" lists every position with its accounts and the look-through exposure per underlying; "raw" returns each broker's own balance payloads unmerged. Fetched broker rows are recorded in the local append-only ledger (no broker writes). |
| get_portfolio_summaryA | Whole-portfolio totals across every broker account, by section. totals: total assets, stock value, cash, cost basis, unrealized P&L, per-account breakdown and the NAV bridge, in KRW at current spot. exposure: weights by currency and market, and look-through exposure per underlying (leveraged products at their daily multiple; open crypto futures at signed notional, never in totals). concentration: top positions, HHI and risk level. realized: year-to-date realized P&L, dividends, fees and taxes from the ledger (slower). Default: totals and realized. Never writes a snapshot. Fetched broker rows are recorded in the local append-only ledger (no broker writes). |
| get_portfolio_returnsB | Portfolio return for a period: simple return, TWR and XIRR side by side, P&L split and NAV risk statistics. Read from stored daily NAV snapshots and the ledger. include_series adds the daily NAV history of the same period with drawdown statistics. That is the dated value series; it stores nothing, and no export or snapshot is needed for it. Fetched broker rows are recorded in the local append-only ledger (no broker writes). |
| get_transactionsA | Your trades across every Toss and KIS account, newest first, one page at a time. start/end: ISO YYYY-MM-DD (default: the last 90 days). account: "all", "toss", "kis" or one label. The page header carries the totals and counts for the whole window and next_cursor for the following page (limit 1-500, default 100). detail_level "summary" returns compact rows (columns + rows), "full" every normalized field per row, "raw" the brokers' own rows, one source at a time. Fetched broker rows are recorded in the local append-only ledger (no broker writes). |
| get_trading_pnlA | Realized trading P&L under per-account moving-average cost basis. view "by_symbol": realized P&L, fees and taxes per symbol for the period. view "round_trips": closed buy-to-sell cycles, win rate and open-position P&L. start/end (ISO YYYY-MM-DD) override period. detail_level "summary" lists 20 by_symbol rows, incomplete data first, then the largest realized P&L (symbols_page has the total), or the top 10 round_trips symbols by |realized P&L| with reason groups (symbol_count, omitted_symbols); pass symbol or detail_level "full" for every symbol. Fetched broker rows are recorded in the local append-only ledger (no broker writes). |
| get_quoteA | Current stock quotes for up to 10 symbols (e.g. "AAPL", "005930"). provider "auto" uses yfinance without Toss keys, otherwise reads Toss with KIS fallback; "toss" or "kis" reads one; "both" compares the two with session timing. include adds per symbol: orderbook (bid/ask depth), trades (public market tape), limits (daily price limits), warnings (delisting/VI/overheated) and info (name, market, ISIN). KIS covers Japanese stocks (exchange TKSE). |
| get_market_infoB | Market sessions and holidays (KR or US; Toss), an ECB daily reference exchange rate, and the 10Y US Treasury yield (yfinance ^TNX). Each section's meta names its provider and as_of. |
| get_crypto_marketA | Public Binance USD-M futures market data for one symbol (e.g. "BTCUSDT"); no API key. Always returns the snapshot: last price, mark/index price, latest funding rate and open interest. include adds the public funding rate history, open interest history and OHLCV candles (interval; limit rows each, 1-500). |
| get_crypto_positionsA | Your open crypto futures positions and native account balances (Binance USD-M, Hyperliquid). Kept out of portfolio NAV and holdings. Unknown leverage, margin mode and mark prices stay null; inspect each venue's source_status and coverage. |
| get_crypto_account_historyA | Your own crypto futures fills (with fees) or funding paid/received, newest first, paged. start/end: inclusive UTC YYYY-MM-DD (default: the last 7 days). Values stay in their native asset. The page header carries per-venue coverage and next_cursor (limit 1-500, default 50). Fetched broker rows are recorded in the local append-only ledger (no broker writes). |
| get_ticker_researchA | Issuer facts and explainable factors for one US ticker; explicit refresh; no account access or orders. Returns {ok, data, error, meta} as structuredContent and the same compact JSON as text; ok=false is a tool error naming the problem. data.snapshot.sections: sec_financials and sec_insider (SEC EDGAR), yfinance_metrics and yfinance_bars (Yahoo Finance via yfinance, unofficial), and factors calculated from the bars (RSI/MACD/ATR, moving averages, levels, risk), each section with as_of, as_of_basis and source; data.freshness gives the last confirmed close and the latest trade (session, time, status). For prices now/today/intraday use freshness, not the factors' bar dates. detail: standard (default) gives values with provenance once per section and rounded numbers, summary headline values only, full every field with per-value provenance; meta.detail_note says how to read same_as, _each and {columns, rows} tables. standard keeps the last 20 daily bars as columns {t,o,h,l,c,v} with the full bar_range, and the markdown report only in full. |
| get_ticker_research_statusA | Passive configuration status; performs no provider calls. Returns {ok, data, error, meta} as structuredContent and the same compact JSON as text; ok=false is a tool error naming the problem. data: status, provider readiness, auth mode, planning_configured and the latest-price source health. |
| plan_entryA | Scaled entry calculation, not an order; the amount is your input; no account access. Symbols: US tickers (USD) and KRX listings by code, e.g. 000660, 005930 (tries .KS then .KQ) or 005930.KS (KRW); not indices or other markets. currency defaults to the listing's; amounts and prices stay in it (no FX conversion): USD with a KRX listing or KRW with a US one returns CURRENCY_MISMATCH. KRX listings: Yahoo daily bars, the latest trade and indicators only (no options levels, SEC filings, consensus or earnings date, stated in warnings), limit prices on the KRX tick grid in force since 2023-01-25 (호가단위, rounded down) and whole shares per tranche. Returns {ok, data, error, meta} as structuredContent and the same compact JSON as text; ok=false is a tool error naming the problem. data: attractiveness (score, band, components with their factor inputs), plan (tranches with price, percent, amount and touch probability, expected average cost and fill, comparison_vs_lump_sum, unfilled_remainder, reassess_below), freshness and technical_basis. Spot is the latest trade (any session) and RSI/MACD/ATR/MAs use the same bars as get_technical_indicators, including today's provisional bar (technical_basis, freshness); confirmed_only=true uses the last confirmed close and completed bars. detail: standard (default) gives values with provenance once per section and rounded numbers, summary headline values only, full every field with per-value provenance; meta.detail_note says how to read same_as, _each and {columns, rows} tables. Rejected candidate levels (dropped_levels): standard {reason: {level: price}}, full with their inputs; style constants (style_parameters) and method text only in full; summary drops inputs and levels. |
| compare_tickersA | Compare 1–8 tickers for entry-plan scoring and allocation: calculation, not an order; the amount is your input; no account access. Symbols: US tickers (USD) and KRX listings by code, e.g. 000660, 005930 (tries .KS then .KQ) or 005930.KS (KRW); not indices or other markets. currency defaults to the listing's; amounts and prices stay in it (no FX conversion): USD with a KRX listing or KRW with a US one returns CURRENCY_MISMATCH. KRX listings: Yahoo daily bars, the latest trade and indicators only (no options levels, SEC filings, consensus or earnings date, stated in warnings), limit prices on the KRX tick grid in force since 2023-01-25 (호가단위, rounded down) and whole shares per tranche. US and KRX tickers can be mixed: each keeps its own currency (data.currency_policy); total_amount then needs currency, the currency it is split in, and a ticker priced in the other one gets a percentage-only plan. An unsupported symbol is listed in data.unavailable with its reason_code and the rest are compared. For price-performance charts or relative strength (상대강도), use compare_price_performance. Returns {ok, data, error, meta} as structuredContent and the same compact JSON as text; ok=false is a tool error naming the problem. data.comparison: per ticker attractiveness and entry_plan (tranches), ranking and allocation_scenarios (with total_amount); data.freshness and technical_basis per ticker. Spots are latest trades with their times (freshness; a spread over 5 minutes is warned) and indicators match get_technical_indicators; confirmed_only=true uses confirmed closes. detail: standard (default) gives values with provenance once per section and rounded numbers, summary headline values only, full every field with per-value provenance; meta.detail_note says how to read same_as, _each and {columns, rows} tables. Per-ticker inputs_used and dropped_levels only in full. |
| compare_price_performanceA | Compare the daily price performance of 1-8 US or Korean stocks, ETFs or indices on one common trading calendar (상대강도, 기준일 100, 수익률 비교, 코스피와 비교). For 내 보유 종목을 코스피/S&P와 비교: get holdings from investmcp get_holdings, then call this once with their symbols (KR 6-digit codes and US tickers together) and benchmark KOSPI (or SPY/^GSPC). Prefer it for cross-market price history and index levels: it uses the KOSPI index itself (not an ETF proxy such as KODEX 200), aligns calendars, converts USD and KRW holdings into one currency and labels sources. metric: normalized_price (default; every series = 100 on the common base date), price_return_pct, relative_strength_ratio (symbol / benchmark, base 100) or excess_return_pp (return minus the benchmark's, %p); the last two need benchmark (e.g. SPY, QQQ, KOSPI, ^GSPC). If relative strength (상대강도) is asked without a benchmark, the result is normalized_price with a warning naming that choice; pass benchmark for a true relative-strength ratio. Window: period 1mo (default) | 3mo | 6mo | ytd | 1y | d calendar days, or start_date/end_date, or trading_days N (N common trading days ending at the latest one). Symbols: AVGO, BRK.B, 005930 (tries .KS then .KQ), 005930.KS, ^GSPC, aliases KOSPI, KOSDAQ, S&P500, SPX, NASDAQ, NDX; an ETF benchmark is labelled a proxy. currency native | USD | KRW converts with the Yahoo KRW=X close of the same date. Prices are Yahoo daily closes, split-adjusted, dividends excluded: price return, not total return. One symbol failing returns its entry in errors; the rest are still compared. Up to 260 points per series; long windows switch to weekly points (data_quality.sampling). trading_days N counts today's session when a provisional point ends the series. By default the last point is provisional: today's trades (regular session, or pre-market before the open) after the last confirmed close, with provisional=true, session and exchange-local time; the confirmed close replaces it after the close, so no date appears twice. After-hours trades appear in freshness and the summary, never as a point. confirmed_only=true: completed daily closes only. table: period_return_pct to the last point and confirmed_return_pct to the last confirmed close. Text: a one-line summary, then the full result as compact JSON (the same object as structuredContent). Show source_line and warnings under the chart. Read-only calculation; no account access or orders. |
| get_technical_indicatorsA | Daily technical indicators for one US or Korean stock, ETF or index (RSI/MACD/볼린저 밴드, 이동평균). For RSI/MACD/볼린저 of holdings, get symbols from investmcp get_holdings, then call this per symbol instead of computing indicators from raw candles. indicators: up to 6 of {name, params}: rsi {n:14}, macd {fast:12, slow:26, signal:9}, bb {n:20, k:2}, sma/ema/rma {n}, atr {n:14}, stdev, highest, lowest, roc {n}, volume; default rsi, macd and bb. Lengths 2-400; recursive ones (ema, rma, rsi, atr, macd) at most 252, as they need 10 x n warm-up bars. For SMA 20/50/200 pass three sma items. period 1mo | 3mo | 6mo (default) | ytd | 1y | d, at most 260 daily points; history before the window is fetched for warm-up, never reseeded. Returns the close series, one series per indicator output (unit, price or separate panel), a latest table and warnings, including possible split discontinuities. Yahoo daily closes, price return. By default a provisional last bar from today's trades (regular session, or pre-market before the open; never after-hours) ends every series: latest rows give the provisional value with session and time plus confirmed_value for the last confirmed bar (data_quality.bar_status partial). plan_entry and compare_tickers use the same bars and calculation, so their RSI/MACD/ATR match. confirmed_only=true: completed bars only; recursive indicators (RSI/MACD/EMA/ATR) can differ from earlier releases by about 0.001, as all three tools now share one warm-up window. Text: a one-line summary, then the full result as compact JSON (the same object as structuredContent). Show source_line and warnings under the chart. Read-only calculation; no account access or orders. |
| get_eps_scenariosA | For any EPS question on a US SEC filer (EPS 시나리오, 약세/기본/강세, bear/base/bull, EPS 전망·컨센서스, P/E 적정가) call this first, before web search; web news can be added after. Returns chart-ready tables: GAAP diluted EPS per fiscal year from SEC EDGAR companyfacts (10-K) with the P/E at each fiscal year end, Yahoo Finance consensus EPS for the current and next fiscal year (low/avg/high, analysts; via yfinance, unofficial) and bear/base/bull implied prices = EPS x P/E multiple. The historical GAAP P/E band is applied only when the EPS bases match; otherwise rows keep EPS with implied_price and implied_return_pct null, a needs_input_pe_multiples note asks for pe_multiples (a dict, e.g. {"bear":25,"base":32,"bull":40}), and pe_multiples_used lists the band and forward P/E per year as reference only, not applied. A calculation, not a forecast or price target. Fiscal years filed before a later stock split are left out, not adjusted. Korean listings and indices return UNSUPPORTED_MARKET. No dividends. implied_return_pct is measured from reference_price: by default the latest trade (regular, pre-market or after-hours, with its basis, session and time), else the last confirmed close (latest_close, kept separately); eps_as_of (10-K filing) and estimates_as_of are separate from the price time. confirmed_only=true: from the close. Text: a one-line summary, then the full result as compact JSON (the same object as structuredContent). Show source_line and warnings with the table. Read-only calculation; no account access or orders. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 28 tools
Several tool clusters have overlapping boundaries: get_financials, get_key_metrics, get_sec_financials, get_ttm_financials, get_stock_snapshot, and get_ticker_research all return related fundamental/quote/research data, so an agent may struggle to choose the right one. Portfolio tools also overlap (get_holdings, get_portfolio_summary, get_portfolio_returns, get_trading_pnl, export_portfolio_dataset), though the detailed descriptions mitigate some confusion.
All tool names use snake_case and follow a predictable verb_noun pattern (get_*, compare_*, export_*, import_*, snapshot_*, plan_entry). There are no mixed conventions or vague standalone names.
28 tools is heavy for a single server; the broad investment domain justifies some breadth, but the overlapping financial-data and portfolio-analysis tools suggest the surface could be consolidated. It sits at the borderline between appropriately comprehensive and overstuffed.
The surface covers portfolio holdings, transactions, NAV history, returns, P&L, market quotes, candles, fundamentals, SEC filings, insider data, crypto, technical indicators, planning, and comparisons. Order placement is intentionally excluded and some auxiliary areas (watchlists, alerts, news) are absent, but core read-only investment workflows are well covered.