Complete US ETF holdings from SEC N-PORT filings + ticker/CUSIP/FIGI crosswalk. Primary source.
SEC-primary-source ETF holdings + identifier crosswalk for agents.
An MCP (Model Context Protocol) server that gives AI agents the complete holdings of any US-registered ETF straight from SEC EDGAR N-PORT filings — not a top-10 scrape — plus a set of identifier-crosswalk tools (ticker, CUSIP/SEDOL, FIGI → resolved symbol) backed by OpenFIGI and SEC reference data. Runs locally over stdio; no vendor market-data subscriptions required.
| Tool | What it does | Sources |
|---|---|---|
lookup_cik | Ticker → SEC CIK + entity metadata (name, SIC, exchanges, fiscal year end) | SEC company_tickers.json + submissions API |
resolve_ticker | Raw ticker + optional exchange code → Yahoo-convention symbol, verified | Exchange-suffix map (pure string mapping) + SEC + OpenFIGI |
resolve_cusip | CUSIP (or SEDOL) → symbol, with cross-source verification | OpenFIGI (one batched request) + SEC name/ticker agreement |
resolve_figi | Bloomberg FIGI → symbol, with cross-source verification | OpenFIGI + SEC name/ticker agreement |
get_etf_holdings | Any US ETF → complete holdings list from its latest monthly N-PORT filing (name, ticker, ISIN, FIGI, LEI, shares, USD value, % of net assets, country, currency) | SEC EDGAR N-PORT XML |
All tools are read-only.
Crosswalk results are verified without any market-data vendor:
company_tickers.json must agree with the OpenFIGI
security name. A gross name mismatch vetoes the mapping
(verification: "name_veto") rather than returning a plausible-looking
wrong symbol.verification: "openfigi" so you
can tell the confidence tiers apart.Yahoo Finance symbology (.SW, .T, .L, … suffixes) is applied as a
pure string convention — the default path makes no network calls to
Yahoo.
# with uv
uv pip install pequod-mcp
# or with pip
pip install pequod-mcp
Run it directly (stdio):
pequod-mcp
The server works without a key (OpenFIGI anonymous rate limits apply). For heavier use, get a free key at https://www.openfigi.com/api and set:
export API_OpenFIGI="your-key-here"
An opt-in extra adds a live Yahoo quote block to crosswalk results. It is off by default and never used otherwise; enabling it means you accept the Yahoo Finance terms of use yourself.
pip install 'pequod-mcp[yahoo]'
export PEQUOD_YAHOO_VERIFY=1
Claude Desktop (claude_desktop_config.json) or any MCP client with a
stdio transport:
{
"mcpServers": {
"pequod": {
"command": "pequod-mcp",
"env": {
"API_OpenFIGI": "your-key-here"
}
}
}
}
Claude Code:
claude mcp add pequod -e API_OpenFIGI=your-key-here -- pequod-mcp
1. "What does SCHD actually hold?"
Agent: lookup_cik("SCHD")
→ { cik: "0000884394", entity_name: "SCHWAB STRATEGIC TRUST", found: true }
Agent: get_etf_holdings("SCHD", cik="0000884394")
→ { fund_name, report_date, source_url (the actual EDGAR filing),
total_holdings: ~100, holdings: [ { name, ticker, isin, figi,
balance, value_usd, pct_val, ... } ] }
The agent gets every position with weights, plus the EDGAR URL for citation — auditable back to the primary source.
2. "I have a CUSIP from a 13F — what stock is this?"
Agent: resolve_cusip("594918104")
→ { yahoo_symbol: "MSFT", verified: true, verification: "openfigi+sec",
company_name: "MICROSOFT CORP", cik: "0000789019",
openfigi: { figi: "BBG000BPH459", ... } }
If OpenFIGI's mapping disagrees with SEC's registry on who the ticker
belongs to, the tool refuses (verification: "name_veto") instead of
handing back a wrong symbol.
3. "Compare two dividend ETFs' overlap."
Agent: get_etf_holdings("SCHD") → complete holdings A
Agent: get_etf_holdings("VYM") → complete holdings B
Agent: joins on isin (or ticker), computes overlap weight, reports the
shared names — all from two tool calls, both citing SEC filings.
company_tickers.json). Identifier
crosswalks come from the OpenFIGI API (FIGI is an open OMG standard)..cache/ next to the module, override with PEQUOD_CACHE_DIR) for
company_tickers.json (24 h) and N-PORT documents (30 days — filed
documents are immutable).report_date and source_url fields tell you exactly which
period you are looking at.| Variable | Default | Purpose |
|---|---|---|
API_OpenFIGI | (unset) | OpenFIGI API key (higher rate limits) |
PEQUOD_CACHE_DIR | .cache/ next to the module | On-disk cache location |
PEQUOD_YAHOO_VERIFY | (off) | Opt-in Yahoo enrichment (needs the [yahoo] extra) |
LOG_LEVEL | INFO | Server log verbosity |
pip install -e '.[dev]'
pytest # fully offline — fixture filings, no live network
Apache-2.0 — see LICENSE. Copyright 2026 AB Evergreen Analytics LLC.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
uvx pequod-mcpMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-abwerks-pequod-mcp": {
"command": "uvx",
"args": [
"pequod-mcp"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referencepequod-mcppypiio.github.ABWerks/pequod-mcp works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.