mcp-etf-holdings
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-etf-holdingsCompare SPY, VOO and IVV on expense ratio and 5-year return"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-etf-holdings
An MCP server for ETF data. Ask which funds hold a stock, compare fees, or pull top holdings. Data comes live from Yahoo Finance. No API key.
What you can ask
Ask Claude | Tool it calls |
"Which ETFs hold NVDA?" |
|
"What's the cheapest ETF for Nvidia exposure?" |
|
"Compare SPY, VOO, QQQ and SCHD" |
|
"What are SPY's top holdings?" |
|
"What is QQQ's expense ratio and AUM?" |
|
"Find me semiconductor ETFs" |
|
"What's the ticker for Berkshire Hathaway?" |
|
Search by company name or ticker symbol. "Which ETFs hold Nvidia?" works the same as "Which ETFs hold NVDA?". Ask about several funds at once and you get one comparison table.
Related MCP server: Stock Assistant MCP Server
Requirements
Python 3.11+
An MCP client: Claude Code, Claude Desktop, VS Code, or Cursor
uv, which runs the server without a manual install
Install uv:
curl -LsSf https://astral.sh/uv/install.sh | shSetup
You don't need to install the server first. Every config below launches it on demand with
uvx, in its own isolated environment.
To put it on your PATH instead, run pip install mcp-etf-holdings, then swap
uvx mcp-etf-holdings for a plain mcp-etf-holdings in any config below.
Claude Code
claude mcp add etf-holdings -- uvx mcp-etf-holdingsClaude Desktop
Open your config file:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
Add the server:
{ "mcpServers": { "etf-holdings": { "command": "uvx", "args": ["mcp-etf-holdings"] } } }Quit Claude Desktop and reopen it. Closing the window is not enough.
VS Code
Create
.vscode/mcp.jsonin your workspace. For a global config, run MCP: Open User Configuration instead.Add the server. VS Code uses the key
servers, notmcpServers:{ "servers": { "etf-holdings": { "type": "stdio", "command": "uvx", "args": ["mcp-etf-holdings"] } } }Restart VS Code.
Cursor
Use the same JSON as Claude Desktop. Put it in .cursor/mcp.json for one project, or
~/.cursor/mcp.json for all of them. Restart Cursor.
Verify it works
Ask your client each of these:
"Which ETFs hold NVDA?" — calls
find_etfs_holding_stock"Compare SPY, QQQ and VTI" — calls
compare_etfs, returns one table"Which ETFs hold Nvidia?" — resolves the name to
NVDAand says so
If you get real numbers back, you're done.
Example prompts
Copy any of these into a client that has the server connected.
Compare funds
Compare SPY, VOO, IVV and SPLG — they all track the S&P 500, so which is cheapest?
Compare QQQ, VGT, XLK and SMH on expense ratio and 5-year return.
Search by name or ticker
Which ETFs hold Nvidia?
What's the ticker for Berkshire Hathaway?
Find me the Vanguard total stock market fund and show its top holdings.
Find the cheapest exposure to a stock
What's the cheapest ETF to get exposure to Nvidia?
I want AMD exposure without buying the stock directly. What are my options, and what do they cost?
Check a portfolio for overlap
I own VOO, QQQ and VGT. Am I doubling up?
Show me every holding that appears in more than one of SPY, SCHD and DGRO.
Discover funds by theme
Find semiconductor ETFs and compare the three biggest.
What dividend ETFs exist, and which has the highest yield?
Research one fund
Give me a deep dive on SCHD.
How concentrated is QQQ? What share of it is the top 5 positions?
Prompt templates
The server registers five prompt templates. Claude Desktop and Claude Code show them as slash-commands, so you don't have to write the prompt yourself.
Template | Argument | What it does |
|
| Costs, size, returns, and a concentration read on one fund |
|
| Side-by-side table plus a cost and return comparison |
|
| Ranks the ETFs holding a stock by weight and cost |
|
| Finds positions duplicated across the funds you hold |
|
| Finds funds for a theme and compares the leaders |
Tool reference
Tools that return more than one row format the result as a markdown table.
etf_info(ticker)
Metadata for one ETF. For two or more funds, use compare_etfs.
ticker(string) — ETF symbol, e.g."SPY"
Returns name, category, AUM, expense ratio, dividend yield, NAV/price, and YTD / 3-yr / 5-yr returns.
compare_etfs(tickers)
Compares several ETFs in one table.
tickers(array of strings) — e.g.["SPY", "QQQ", "VTI", "SCHD"]. Case-insensitive and de-duplicated. Capped at 10 funds per call.
Returns Ticker, Name, Category, AUM, Expense, Yield, YTD, 3-Yr, 5-Yr. A ticker with no data still gets a row, plus a note naming it, so one typo doesn't discard the rest.
etf_holdings(ticker)
Top holdings of an ETF with portfolio weights.
ticker(string) — ETF symbol, e.g."QQQ"
Returns a ranked table of symbol, name, and weight %.
find_etfs_holding_stock(stock_ticker, limit, custom_etf_universe)
Reverse lookup. Finds which ETFs hold a given stock in their disclosed top positions.
stock_ticker(string) — a ticker like"NVDA"or a company name like"Nvidia". Names are resolved to a ticker, and the output says which one it used.limit(int, default 20) — capped at 50custom_etf_universe(JSON string, optional) — search a specific list instead of the default universe, e.g.'["SPY","QQQ","XLK"]'
Returns matching ETFs sorted by the stock's weight, highest first.
Only top holdings are checked, roughly 10–15 positions per ETF. A stock held outside those positions will not appear.
stock_exposure_summary(stock, limit)
The same reverse lookup, joined with each fund's cost and size. Use it for "what's the cheapest or largest way to hold this stock?".
stock(string) — ticker or company name, e.g."NVDA"or"Nvidia"limit(int, default 10) — capped at 25
Returns ETF, Name, Weight, Rank, Expense, AUM, sorted by weight in the stock.
lookup_symbol(query, limit, asset_type)
Resolves a company or fund name to its ticker symbol.
query(string) — e.g."Nvidia"or"Vanguard total stock market"limit(int, default 10) — capped at 25asset_type(string, default"any") —"any","stock", or"etf"
Returns Symbol, Name, Type, Exchange.
search_etfs(query, limit)
Finds ETFs by name, theme, or category.
query(string) — e.g."semiconductor"or"dividend"limit(int, default 10) — capped at 25
Returns matching ETF tickers with names and exchanges. To resolve a stock name instead
of finding funds, use lookup_symbol.
From source
For development, or to run without waiting on a release:
git clone https://github.com/vlearner/mcp-etf-holdings.git
cd mcp-etf-holdings
uv syncWithout uv:
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"The repo ships a .vscode/mcp.json pointed at your working tree, so VS Code picks up
local changes with no extra setup. Point other clients at:
{
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/mcp-etf-holdings", "mcp-etf-holdings"]
}Check that the server starts:
uv run mcp-etf-holdingsIt will sit silent, waiting for JSON-RPC on stdin. That is correct for a stdio MCP server.
To poke at the protocol by hand:
npx @modelcontextprotocol/inspector uv run mcp-etf-holdingsTests
uv run pytest -qThe suite is fully offline. Both yf.Ticker and yf.Search are mocked, the latter by an
autouse fixture, so no test can reach Yahoo by accident.
For coverage:
uv run pytest -q --cov=mcp_etf_holdings --cov-report=term-missingLayout
src/mcp_etf_holdings/
server.py ← FastMCP entry point, 7 tools
prompts.py ← prompt templates, exposed as client slash-commands
fetcher.py ← async wrappers around yfinance + 24h TTL cache
formatting.py ← markdown tables and shared number formatting
top_etfs.py ← ~365 ETF tickers scanned for reverse lookups
__main__.py ← enables `python -m mcp_etf_holdings`
tests/ ← pytest suite, fully offline
.vscode/mcp.json ← VS Code config pointing at the working tree
pyproject.tomlDependencies: mcp (protocol SDK), yfinance (data), pandas (holdings frames),
httpx (transport).
Troubleshooting
Symptom | Fix |
| Install uv, then restart your editor so it picks up the new |
Server missing from the client | Editors read MCP config only at startup. Fully quit and reopen. Check the client's MCP logs for the launch error. |
Works in a terminal, not in the editor | GUI apps often don't inherit your shell |
Tool calls return "No data found" | Check the symbol is real. Yahoo rate-limits sometimes, so wait and retry. |
Python version error on install | Needs 3.11+. Check with |
Limits
Worth knowing before you trust a number:
Only the top ~10–15 positions per ETF are published. Anything derived from holdings — reverse lookups, exposure summaries, overlap checks — is a floor, not the full picture.
The default universe is ~365 funds. A stock held only in small or niche ETFs may not turn up.
Responses are cached in memory for 24h. Override with
ETF_CACHE_TTL_SECONDS.Data is fetched live from Yahoo Finance. No API key, but Yahoo's terms and rate limits apply. See NOTICE for attribution.
Contributing
PRs welcome. The most useful contribution is expanding the ETF universe in top_etfs.py.
Wider coverage means better reverse-lookup results in niche sectors and international
markets.
Open an issue first for larger changes.
Disclaimer
Not affiliated with Yahoo Finance, yfinance, or any financial institution. Data is retrieved from Yahoo Finance at runtime and is subject to availability and their terms of service. This is not financial advice.
Available Tools
7 toolscompare_etfsA
Compare several ETFs side by side in a single table: name, category, AUM, expense ratio, dividend yield, and YTD / 3-yr / 5-yr returns.
Use this whenever the user mentions more than one ETF — it is one call instead of several etf_info calls, and the result is already tabular.
| Name | Required | Description | Default |
|---|---|---|---|
| tickers | Yes | ETF tickers to compare side by side, e.g. ['SPY', 'QQQ', 'VTI', 'SCHD'] |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses the return shape (a comparison table with defined columns), which is useful, but says nothing about whether tickers must exist, how invalid tickers are handled, or any rate/permission constraints. For a low-risk read-only lookup this is adequate but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short paragraphs with zero waste: the output contents are front-loaded, then the usage rule and the alternative. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so the description needn't explain return values, yet it helpfully previews the table columns. Combined with the clear usage routing, an agent has everything needed to invoke it correctly; only edge-case behavior (invalid/missing tickers) is unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single 'tickers' array parameter is documented in the schema with a concrete example (['SPY','QQQ','VTI','SCHD']). The description adds no format or cardinality guidance beyond what the schema already provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Compare') and resource ('ETFs'), and enumerates the exact output fields (name, category, AUM, expense ratio, dividend yield, returns), so the agent knows what it gets back. It is clearly distinguishable from the sibling etf_info, which it explicitly contrasts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ('whenever the user mentions more than one ETF') and names the alternative it replaces ('one call instead of several etf_info calls'), with the added rationale that the result is already tabular. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
etf_holdingsB
Return the top holdings of an ETF with their portfolio weight percentages.
| Name | Required | Description | Default |
|---|---|---|---|
| ticker | Yes | ETF ticker symbol, e.g. 'SPY' |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It does disclose that only 'top' holdings are returned with weights, implying a truncated result set, which is useful scoping context. But it never quantifies 'top', states data freshness, or notes any auth/rate constraints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence with the key output (weights) front-loaded alongside the resource. No filler or redundant restatement of the tool name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read tool with an output schema present, return-shape explanation is unnecessary. The only real gap is the undefined size of the 'top' list, which mildly affects expectations but not invocation correctness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single ticker parameter, so the schema already documents the argument including an example ('SPY'). The description adds no parameter-level meaning beyond that, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Return) and resource (top holdings of an ETF) plus the payload (portfolio weight percentages). However, it does not distinguish itself from siblings like etf_info or find_etfs_holding_stock, leaving the agent to infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance, no prerequisites, and no mention of alternatives such as etf_info for broader fund data or find_etfs_holding_stock for the reverse lookup. The agent must infer the appropriate context entirely.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
etf_infoA
Return metadata for a single ETF: name, category, AUM, expense ratio, NAV, and trailing returns.
For two or more ETFs, use compare_etfs instead — it returns one table.
| Name | Required | Description | Default |
|---|---|---|---|
| ticker | Yes | ETF ticker symbol, e.g. 'SPY' or 'QQQ' |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the full behavioral burden. It implies a pure read operation and lists what comes back, but never states read-only status, auth requirements, rate limits, or behavior for an unknown ticker. Adequate for a trivial one-param lookup, but thin for a tool with zero annotation coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the primary purpose front-loaded and the routing rule appended. Minor waste: enumerating the returned fields duplicates what the output schema already declares.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With one required, fully documented parameter and a companion output schema, the description supplies everything needed to select and call the tool correctly. Only the absence of any error/permission context keeps it short of a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter (ticker) and schema coverage is 100%, with the schema already supplying the 'SPY'/'QQQ' example. The description adds nothing beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb ('Return') plus resource ('metadata for a single ETF') and an explicit enumeration of the returned fields (name, category, AUM, expense ratio, NAV, trailing returns). It is immediately distinguishable from the sibling compare_etfs, which is named directly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The second sentence gives an explicit when-not rule: for two or more ETFs, use compare_etfs instead, with the rationale (it returns one table). This is a clear alternative routing rule rather than implied usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_etfs_holding_stockA
Reverse-lookup: find ETFs (from a ~365-ETF universe or a custom list) that hold a given stock in their disclosed top holdings.
Results are sorted by the stock's weight in each ETF, highest first. For a version that also reports each ETF's expense ratio and size, use stock_exposure_summary.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of ETFs to return (1-50, default 20) | |
| stock_ticker | Yes | Stock ticker or company name to search for, e.g. 'NVDA' or 'Nvidia' | |
| custom_etf_universe | No | Optional JSON array of ETF tickers to search instead of the default universe. Example: '["SPY","QQQ","XLK"]' |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does meaningful work: it discloses the default universe size (~365 ETFs), that results are sorted by weight descending, and that coverage is limited to 'disclosed top holdings' (i.e., partial data). It omits failure modes such as what happens for an unknown ticker or an empty result set, so it stops short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero filler, with the core purpose and the sorting/universe semantics front-loaded before the sibling routing at the end.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained. Between the description's scope, sorting, universe size, and explicit alternative, plus a fully covered parameter schema, an agent has everything needed to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents all three parameters and the baseline is 3. The description adds useful semantic context beyond the schema by stating the default universe is ~365 ETFs and that a custom list substitutes for it, clarifying the intent of custom_etf_universe.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Reverse-lookup: find ETFs that hold a given stock') plus the scope of the lookup ('disclosed top holdings'), which immediately separates it from etf_holdings and search_etfs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative sibling and the condition that selects it: 'For a version that also reports each ETF's expense ratio and size, use stock_exposure_summary.' This is exactly the when/when-not routing an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lookup_symbolA
Resolve a company or fund name to its ticker symbol.
Call this first whenever the user names a company instead of giving a ticker — "Which ETFs hold Nvidia?" needs NVDA. Covers stocks and ETFs; use asset_type='stock' to exclude funds from the results.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (1-25, default 10) | |
| query | Yes | Company or fund name to resolve, e.g. 'Nvidia' or 'Vanguard total stock market' | |
| asset_type | No | Filter results: 'any' (default), 'stock', or 'etf' | any |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It discloses scope (stocks and ETFs) and a filtering lever, but says nothing about ambiguous multi-match results, ranking, or the fact that this is a side-effect-free read (though the output schema covers the return shape).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core action, then the when-to-call rule, then the coverage caveat. Nothing is wasted and no sentence restates the title or name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and all three parameters documented in the schema, the description supplies the essential entry-point guidance. The only gap is behavior on ambiguous or no-match queries, which is minor for a lookup tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds intent beyond the schema by explaining why one would set asset_type='stock' — to exclude funds from results. That is real semantic value on top of the schema's mechanical enum listing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource — resolve a company or fund name to its ticker symbol — so the transformation is unambiguous. It also declares coverage (stocks and ETFs), which separates it from the ETF-only siblings like etf_info and search_etfs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger: 'Call this first whenever the user names a company instead of giving a ticker,' with a concrete example (Nvidia → NVDA). It does not state when-not-to-use or name a sibling alternative, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_etfsA
Search for ETFs by name, theme, or category using Yahoo Finance search.
Returns matching ETF tickers with their full names and exchanges. Useful for discovering ETFs to pass to etf_info, compare_etfs, etf_holdings, or find_etfs_holding_stock.
To resolve a stock or company name rather than find ETFs, use lookup_symbol.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of ETFs to return (1-25, default 10) | |
| query | Yes | Search term: fund name, theme, or category, e.g. 'semiconductor' or 'dividend' |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It discloses the data source and that results include ticker, name, and exchange, but says nothing about rate limits, failure/empty-result behavior, or whether results are cached or live. Adequate context for a simple read search, but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero waste, front-loaded with purpose, then return, then routing. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be spelled out further, and the description already covers purpose, discovery role, and the sibling alternative. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already documented, including the query examples ('semiconductor', 'dividend') that the description echoes. The description adds no syntax or format guidance beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search for ETFs') plus the scope of the search (name, theme, or category) and the backing source (Yahoo Finance). It also summarizes the return (matching tickers with names and exchanges), so an agent can distinguish it from siblings like etf_info without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the downstream consumers (etf_info, compare_etfs, etf_holdings, find_etfs_holding_stock), which tells the agent this is a discovery entry point. It also gives an explicit when-not with the alternative: use lookup_symbol to resolve a stock or company name instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stock_exposure_summaryA
Find the ETFs that give exposure to a stock, with each fund's weight in that stock alongside its expense ratio and AUM.
Answers "what is the cheapest / largest ETF for exposure to X?" — the weight column shows how much exposure you get, expense ratio what it costs.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of ETFs to return (1-25, default 10) | |
| stock | Yes | Stock ticker or company name to find ETF exposure for, e.g. 'NVDA' or 'Nvidia' |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It conveys the shape of the result (per-fund weight, expense ratio, AUM), which is useful, but says nothing about ordering of results, what happens with an unknown ticker, or that this is a read-only lookup. With an output schema present, the return-value gap is forgivable, but the behavioral coverage is thin for an unannotated tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the core action and outputs. The second sentence is partly redundant with the first but earns its place by naming the decision it supports. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a full output schema and 100% parameter coverage, the description need not explain return values or argument formats, and it correctly stays brief. The remaining omission is sibling differentiation in a crowded ETF-tool family, which is a meaningful but bounded gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: both 'stock' (ticker or name, with examples) and 'limit' (range and default) are fully documented in the schema. The description adds only the meaning of the returned metrics, not new parameter semantics, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('find the ETFs that give exposure to a stock') and enumerates the returned metrics (weight, expense ratio, AUM), so the agent knows exactly what this produces. It does not, however, differentiate itself from the near-identical sibling find_etfs_holding_stock or from compare_etfs, which leaves a real disambiguation gap.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The framing question 'what is the cheapest / largest ETF for exposure to X?' implies a use case and hints that this tool is for comparison-style selection. But it never states when to prefer it over find_etfs_holding_stock or compare_etfs, nor any exclusions, so routing must be inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v0.4.1- First observed
compare_etfs - First observed
etf_holdings - First observed
etf_info - First observed
find_etfs_holding_stock - First observed
lookup_symbol - First observed
search_etfs - First observed
stock_exposure_summary
TDQS
Scored across 7 tools
Most tools have clearly distinct purposes: etf_info vs compare_etfs is well-differentiated by the single-vs-multiple guidance, and search_etfs vs lookup_symbol target different lookup types. The main overlap is find_etfs_holding_stock and stock_exposure_summary, which both find ETFs holding a stock, but the descriptions explicitly clarify the difference (extra expense ratio/AUM and sorting).
All names use snake_case, but the patterns vary: etf_info (noun_noun), compare_etfs (verb_noun), etf_holdings (noun_noun), find_etfs_holding_stock (verb_noun_noun), stock_exposure_summary (noun_noun_noun), search_etfs (verb_noun), lookup_symbol (verb_noun). The inconsistency in verb-first vs noun-first and use of prefixes/suffixes makes it mixed but still readable.
Seven tools is well-scoped for an ETF holdings and exposure analysis server. Each tool covers a distinct operation (metadata, comparison, holdings, reverse lookup, exposure summary, search, symbol resolution) without obvious redundancy or bloat.
The surface covers the core workflows well: ETF discovery, metadata, comparison, holdings, reverse holdings lookup, and exposure analysis with cost/size. Minor gaps exist (e.g., no full holdings beyond top positions, no historical performance beyond trailing returns), but these are likely outside the server's stated scope and can be worked around.
Maintenance
Related MCP Connectors
Analyze stocks with summaries, price targets, and analyst recommendations. Track SEC filings, divi…
Independent US ETF data: holdings, fees, overlap, returns, each with its source and date.
Live market data, financial analysis, and portfolio research tools across 10,000+ tickers.
Holdings, overlap, correlation and look-through risk for US ETFs, from issuer files, with no key.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProvides real-time stock market data through Yahoo Finance without requiring an API key. Get quotes, historical data, company information, financial statements, and ticker search capabilities.-
- FlicenseNot gradedqualityDmaintenanceProvides real-time stock data, historical analysis, and stock comparisons using the Yahoo Finance API.-
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to perform live stock and portfolio research using Yahoo Finance data, including quotes, fundamentals, price history, news, portfolio analysis, and ticker comparisons.-
- AlicenseAqualityAmaintenanceFree stock, ETF & crypto analysis for AI agents: 10-point score, descriptive verdict and all key metrics for any ticker. Remote MCP server (Streamable HTTP, no auth, nothing to install) at https://www.stoxlyonline.com/api/mcp, plus a stdio server (server.js) for platforms that run MCP servers as a local process. Rate limit: 30 tool calls per IP per hour. Docs: https://www.stoxlyonline.com/mcp42MIT