Metadata-Version: 2.5
Name: pequod-mcp
Version: 0.2.2
Summary: SEC-primary-source ETF holdings + identifier crosswalk MCP server (ticker/CUSIP/FIGI resolution, complete N-PORT holdings)
Author-email: AB Analytics <info@abanalytics.dev>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: crosswalk,edgar,etf,figi,holdings,isin,mcp,model-context-protocol,nport,openfigi,sec,ticker
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.10
Requires-Dist: fastmcp>=2.0.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: yahoo
Requires-Dist: yfinance>=0.2.0; extra == 'yahoo'
Description-Content-Type: text/markdown

# pequod-mcp — "the Gam"
<!-- mcp-name: io.github.ABWerks/pequod-mcp -->

**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.

## Tools

| 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.

### How verification works

Crosswalk results are verified without any market-data vendor:

1. **SEC registry check** — if the mapped ticker is a US SEC registrant, the
   entity title in `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.
2. **OpenFIGI mapping** — non-US symbols (outside SEC's registry) verify on
   the OpenFIGI mapping itself, labeled `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**.

## Install

```bash
# with uv
uv pip install pequod-mcp

# or with pip
pip install pequod-mcp
```

Run it directly (stdio):

```bash
pequod-mcp
```

### Optional: OpenFIGI API key

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:

```bash
export API_OpenFIGI="your-key-here"
```

### Optional: Yahoo enrichment extra

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.

```bash
pip install 'pequod-mcp[yahoo]'
export PEQUOD_YAHOO_VERIFY=1
```

## Configure in Claude

**Claude Desktop** (`claude_desktop_config.json`) or any MCP client with a
stdio transport:

```json
{
  "mcpServers": {
    "pequod": {
      "command": "pequod-mcp",
      "env": {
        "API_OpenFIGI": "your-key-here"
      }
    }
  }
}
```

**Claude Code:**

```bash
claude mcp add pequod -e API_OpenFIGI=your-key-here -- pequod-mcp
```

## Agent workflow examples

**1. "What does SCHD actually hold?"**

```text
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?"**

```text
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."**

```text
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.
```

## Data provenance

- **All holdings data comes from SEC EDGAR primary sources** (Form N-PORT
  filings, the submissions API, and `company_tickers.json`). Identifier
  crosswalks come from the **OpenFIGI API** (FIGI is an open OMG standard).
- **No CUSIPs in output.** Tool outputs carry ticker, ISIN, FIGI, and LEI;
  CUSIP fields present in the underlying filings are deliberately stripped.
- **SEC fair access respected:** a declared User-Agent on every request, a
  throttle well under EDGAR's published rate limit, and an on-disk cache
  (`.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).
- **Fail-loud policy:** if EDGAR is unreachable or a filing cannot be
  parsed, tools return an error. There is no degraded or partial fallback
  source — you either get filing-backed data or a clear failure.
- N-PORT filings are published on a lag (holdings are typically ~1–2 months
  behind). The `report_date` and `source_url` fields tell you exactly which
  period you are looking at.
- **Not investment advice.** This server relays public regulatory filings
  and open identifier mappings; verify independently before making
  investment decisions.

## Environment variables

| 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 |

## Development

```bash
pip install -e '.[dev]'
pytest            # fully offline — fixture filings, no live network
```

## License

Apache-2.0 — see [LICENSE](LICENSE). Copyright 2026 AB Evergreen Analytics LLC.
