Skip to main content

MCP server · user manual

Give your assistant the Ledge.

Fundamentals, insider filings, BDC holdings, bonds, and SEC documents — the same public-filings data behind the site, as typed tool calls for Claude Code, Claude Desktop, Codex, or any Model Context Protocol client.

On this page

Which client?

ClientInstallWhere the key livesVerify
Claude Code Nothing ~/.claude.json (%USERPROFILE%\.claude.json on Windows) claude mcp list
Claude Desktop pip install oxford-ledge-mcp claude_desktop_config.json Quit fully → reopen → Settings → Developer
Codex Nothing Shell env OXFORD_LEDGE_API_KEY codex mcp list
Other MCP client Nothing Client's own config POST /mcp with X-API-Key

Get your key

Every client below needs one key, created once in the YOUR API KEYS panel of your account.

  1. Sign in to the Ledge (if you do not have an account yet, create a free account first), then press K or click the key icon in the bottom bar (bottom-right), or open Settings and choose API keys. Signed in already? Open YOUR API KEYS.
  2. Under YOUR API KEYS choose + Create key.
  3. The key is shown once — copy it now into your client's own configuration file (never into a chat); the steps for each client below show exactly where.
  4. Come back to this page for your client's steps.

The key lives in your client's config — never in a chat. An assistant that asks for it is misconfigured. Treat it like a password: if it leaks, delete it under YOUR API KEYS and create another. Hosted transport reads X-API-Key; Authorization: Bearer is not supported.

A trial key (ol_trial_...) works the same way.

Claude Code

command line · nothing to install

  1. Add the server

    SHELL
    claude mcp add --transport http --scope user oxford-ledge https://www.oxfordledge.com/mcp --header "X-API-Key: ol_live_YOUR_KEY_HERE"

    Contains ol_live_YOUR_KEY_HERE — replace it with your key before you run this.

  2. Put the key in

    The command stores it for you in ~/.claude.json. Nothing else to do.

  3. Verify

    claude mcp list — expect oxford-ledge · connected.

Notes & variants — scope, Windows paths, shared projects
  • With --scope user the entry lives in ~/.claude.json for every project; drop --scope user and it is stored for the current folder only. A server added at project scope shows Pending approval in claude mcp list until you approve it in an interactive claude session.
  • The command line is recorded in your shell history and the value is stored in plaintext in ~/.claude.json.
  • On Windows, ~/.claude.json is %USERPROFILE%\.claude.json.
  • Inside a session, type /mcp to see the server's tools.

For a project you share. Keep the key out of the file: add this to the project's .mcp.json and export OXFORD_LEDGE_API_KEY in your shell. Only ever the ${OXFORD_LEDGE_API_KEY} form there, never the key itself.

JSON
{
  "mcpServers": {
    "oxford-ledge": {
      "type": "http",
      "url": "https://www.oxfordledge.com/mcp",
      "headers": { "X-API-Key": "${OXFORD_LEDGE_API_KEY}" }
    }
  }
}

Holds no key: it names the variable OXFORD_LEDGE_API_KEY, which you set in your shell.

Claude Desktop

desktop app · pip package

  1. Add the server

    First: pip install oxford-ledge-mcp (Python 3.9+). Then Settings → Developer → Edit Config, and paste this block into claude_desktop_config.json:

    JSON
    {
      "mcpServers": {
        "oxford-ledge": {
          "command": "oxford-ledge-mcp",
          "env": {
            "OXFORD_LEDGE_URL": "https://www.oxfordledge.com",
            "OXFORD_LEDGE_API_KEY": "ol_live_YOUR_KEY_HERE"
          }
        }
      }
    }

    Contains ol_live_YOUR_KEY_HERE — replace it with your key before you run this.

  2. Put the key in

    Replace the key in the block and save. Keep the OXFORD_LEDGE_URL line — without it the package runs standalone.

  3. Verify

    Quit fully → reopen → Settings → Developer — expect oxford-ledge listed under Developer.

Notes & variants — Windows paths, FRED tools, the pip subset
  • Quit from the system tray or menu bar, not the window close button; Claude Desktop reads its config only at launch.
  • On Windows, use the full path to oxford-ledge-mcp.exe as command (Claude Desktop does not inherit your shell PATH; find it with where oxford-ledge-mcp).
  • Microsoft-Store Claude Desktop reads its config from %LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\ — edits to %APPDATA%\Claude\ are silently ignored on Store installs.
  • Optional: the two FRED macro tools. Only get_yield_curve and get_fred_data call FRED directly, with a key you hold. To use them, add "FRED_API_KEY" to the env block with a real key from fred.stlouisfed.org as its value. JSON has no comments, so a sentence pasted as the value is sent to FRED as the key and every FRED call fails.
  • Source and install notes: github.com/hs902/oxford-ledge-mcp.

The pip package carries a subset of the hosted tools; the hosted endpoint always has the full catalog and the package catches up on its next release. Your client shows the package version when it connects — that version, not a number on any page, is what you are running. The cards below tag the hosted tools the package also carries as PIP; the contract states the split.

Codex

OpenAI's coding agent · nothing to install

  1. Add the server

    In ~/.codex/config.toml (create it if it is not there), add:

    TOML
    [mcp_servers.oxford-ledge]
    url = "https://www.oxfordledge.com/mcp"
    env_http_headers = { "X-API-Key" = "OXFORD_LEDGE_API_KEY" }

    Holds no key: it names the variable OXFORD_LEDGE_API_KEY, which you set in the next step.

  2. Put the key in

    export OXFORD_LEDGE_API_KEY=ol_live_YOUR_KEY_HERE in your shell profile, then open a new command line so Codex starts with it.

  3. Verify

    codex mcp list — expect oxford-ledge listed.

Notes & variants — Windows, the key in the file itself
  • On Windows, ~ is your user folder (%USERPROFILE%), and the shell command is setx OXFORD_LEDGE_API_KEY ol_live_YOUR_KEY_HERE.
  • After the list shows oxford-ledge, start a new Codex session and ask it what tools it can reach from oxford-ledge.

Prefer the key in the file itself. Use http_headers instead of env_http_headers:

TOML
[mcp_servers.oxford-ledge]
url = "https://www.oxfordledge.com/mcp"
http_headers = { "X-API-Key" = "ol_live_YOUR_KEY_HERE" }

Contains ol_live_YOUR_KEY_HERE — replace it with your key before you run this.

This file then holds a secret in plaintext: keep it readable only by you.

Other MCP clients

any client that speaks streamable-HTTP MCP · nothing to install

Endpoint: POST https://www.oxfordledge.com/mcp. Header: X-API-Key, set once in the client's own config. Any client that speaks streamable-HTTP MCP works; use the contract below for the auth boundary and the limits.

Troubleshooting

SymptomCauseFix
oxford-ledge not listed after add wrong scope or URL typo claude mcp get oxford-ledge — URL must be exactly https://www.oxfordledge.com/mcp, header X-API-Key
Shows “Pending approval” project-scope server awaiting approval approve inside an interactive claude session
Tools answer authentication_required key deleted / mistyped / env not loaded create a fresh key under YOUR API KEYS; Codex: open a new shell before starting
403 before reaching /mcp generic User-Agent (curl/, python-requests, Go-http-client) send a descriptive User-Agent, or the X-API-Key header (bypasses the check on /mcp and /api/mcp/*)
Package runs but tools are missing OXFORD_LEDGE_URL unset → standalone mode add the OXFORD_LEDGE_URL line to the config block
Client shows an old version stale pip install pip install -U oxford-ledge-mcp; restart the client
Desktop config won't load invalid JSON (missing comma/brace) run oxford-ledge-mcp in a shell — it prints a banner if installed; validate the JSON

For agents

The contract

Everything a planner needs to call correctly, without opening a single card. Same data as tools/list.

Endpoint & auth

POST https://www.oxfordledge.com/mcp
Header: X-API-Key (Bearer unsupported)
User-Agent: descriptive — generic → 403

Limits

Free tier 100 calls / hr per caller (API key, OAuth subject, or session; IP for anonymous) · heavy tools (tagged HEAVY in the catalog; 13F holdings among them) max 2 concurrent · catalog cached 5 min · tools/list rows carry requires_key and min_tier. For volume, a Developer plan carries 100,000 or 1,000,000 data calls a month across every key on the account; it is bought in YOUR API KEYS (/?panel=api-keys). Developer and Developer Pro include everything in Power User except real-time quotes, so their keys run at 5,000 MCP calls an hour, which makes the monthly allowance reachable.

Who answers what

45

answer an anonymous POST /mcp

3

anonymous, but Researcher tier at execution

get_capital_allocationget_debt_maturitiesget_insider_activity

14

refuse anonymous calls — 12 free with a key + 2 write

get_anomaly_flagsget_bond_dataget_economic_calendarget_fred_dataget_newsget_portfolio_positionsol_bdc_borrower_news_todayol_bond_directory_screenol_filing_searchol_paper_portfoliool_paper_tradereading_list_annotatesearch_bondssearch_news_archive

Hosted vs pip package

62 hosted · 24 also in the pip package (the package carries 29: 5 are package-only). The handshake prints oxford-ledge-mcp <version> — plan from that, not from this page.

At source the package dispatches a 29-tool subset: 4 standalone tools that need no Oxford Ledge account (2 SEC-EDGAR keyless + 2 FRED-keyed), 23 that answer keyless once OXFORD_LEDGE_URL is set (14 name-proxies of the /mcp transport + 9 unauthenticated REST proxies), and 2 Researcher-tier tools (get_debt_maturities, get_capital_allocation) that need OXFORD_LEDGE_API_KEY — an agent planning calls against a pip install should plan from those 29, not from every card below. The hosted tools the package also carries are tagged PIP in the catalog.

Machine-readable

GET /api/mcp/tools.json
<link rel="alternate" type="application/json"> in this page's head
/llms.txt

Write tools

reading_list_annotate and ol_paper_trade are listed; dispatch is refused unless the deployment sets OL_MCP_WRITE_ENABLED=1 (default: off). Every write defaults to a dry run, replays by _idempotency_key, is hourly-capped, and lands on your append-only account activity log. Writes need an X-API-Key or a linked OAuth token — a browser session cookie is refused.

Paper portfolio

ol_paper_portfolio reads YOUR practice portfolio (the /practice/ sandbox: cash, positions marked to a delayed quote, P&L, optional recent fills); ol_paper_trade places a buy or sell in it through the same engine the sandbox uses. Both are free for any signed-in key holder; the trade is capped at 20 executed fills per user per UTC day and the dry run shows the quote and cost first. No create or reset over MCP — start a portfolio in the sandbox. A fill placed this way is badged placed via API in the sandbox. Practice money only; every response says so in _meta.disclaimer.

Tools · 62

Company fundamentals

10 toolsSOURCE: SEC EDGAR COMPANY-FACTS XBRL · 10-K/10-Q/8-K EXHIBITS · ~45-DAY POST-QUARTER LAG · VENDOR QUOTE LEG (ANOMALY FLAGS ONLY)

get_anomaly_flags

KEY REQUIRED

Fifteen red-flag screens on one ticker (short interest, Altman Z, leverage, FCF, payout); red or amber

Full contract & input schema

Run 15 red-flag screens on a ticker (short interest >12%, Altman Z distress, net leverage >4x, negative FCF, P/E >50, dividend yield >6%, insider net selling, revenue decline, payout >90%, goodwill >50% of assets, SBC >10% of revenue, declining ROIC and others). Returns {ticker, flags, count}; each flag is {severity, label, detail, scrollTo}: SEVERITY IS ONLY 'red' OR 'amber', red first. A flag is a question to investigate, NOT a sell signal, and an empty list means the checks did not fire OR the inputs came back empty -- absence is not an all-clear. Pair with get_fundamentals and ol_intrinsic_value. Source: vendor quote/fundamentals snapshot plus computed heuristics. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    }
  },
  "required": [
    "ticker"
  ]
}

get_capital_allocation

RESEARCHER+PIP ✓

Where the cash went, up to 30 fiscal years: dividends, net buybacks, net debt change, acquisitions

Full contract & input schema

Where a company sent its cash, up to 30 annual labels. Returns {ticker, capitalAllocation:{years, periods, dividends, netBuybacks, grossRepurchases, netDebtChange, acquisitions, sharesOut, isDilutive, basis, summary}} as PARALLEL ARRAYS aligned to `years` (newest first, fiscal labels). netBuybacks is a NET, DERIVED DILUTION PROXY, not a buyback figure (repurchases minus issuance, IPO proceeds and SBC); grossRepurchases is the filed line. Share-count cells on a pre-split basis are withheld, never shown as a phantom buyback. An IFRS reporter is REFUSED, never served zeros. Complements get_fundamentals and get_debt_maturities. Source: SEC EDGAR XBRL cash-flow tags, 24h cache. Requires Plus tier. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    }
  },
  "required": [
    "ticker"
  ]
}

HEAVY · MAX 2 CONCURRENT

get_debt_maturities

RESEARCHER+PIP ✓

Forward debt maturity ladder from the latest 10-K/20-F footnote, in USD millions, with parser confidence

Full contract & input schema

Forward debt maturity ladder parsed from the latest 10-K/20-F footnote. Returns {ticker, maturities:[{year, amount}], thereafter, confidence, confidence_score, source, validation}. AMOUNTS ARE IN MILLIONS OF USD, not raw dollars -- 400 means $400M. THE LADDER IS AS OF the filing date, not today: the current-year bucket may already be repaid or refinanced. Check `validation` (the balance-sheet cross-check) before quoting a total. Every empty answer carries `refusal_reason` and a readable `refusal_note`. For historical issuance/repayment use get_capital_allocation. Source: SEC EDGAR 10-K note extraction. Requires Plus tier. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    }
  },
  "required": [
    "ticker"
  ]
}

HEAVY · MAX 2 CONCURRENT

get_fundamentals

ANONYMOUS OKPIP ✓

Up to 30 fiscal years of XBRL income, balance-sheet and cash-flow metrics, plus the last 12 quarters

Full contract & input schema

Full XBRL financial history for one ticker. Returns {ticker, fundamentals:{years, periods, metrics, balanceSheet, quarterly, basis, as_of}, source_period, as_of}: `years` is newest-first, UP TO 30 annual labels, and every entry under `metrics` / `balanceSheet` is a PARALLEL ARRAY aligned to it by index (revenue, netIncome, opCF, capex, epsDiluted, totalDebt, cash, derived fcf and margins ...). Dollar figures are whole USD; *Pct values are percentage numbers. A year the filer did not tag is null, never 0. Per-share cells on a pre-split basis are WITHHELD (null) rather than rescaled -- read `basis`. An IFRS reporter or a filer with no annual us-gaap fact returns {error, taxonomy, formsSeen, unitsSeen}. For cash deployment use get_capital_allocation; for per-share fair value ol_intrinsic_value; for industry operating metrics ol_operating_kpis. Source: SEC EDGAR company-facts XBRL (~45-day post-quarter lag), 24h cache. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    }
  },
  "required": [
    "ticker"
  ]
}

HEAVY · MAX 2 CONCURRENT

ol_fundamentals_screen

ANONYMOUS OK

Pass/fail threshold screen over up to 10 supplied tickers on latest-year XBRL fundamentals

Full contract & input schema

Threshold screen over a caller-supplied ticker set (the FIRST 10 unique tickers, silently) on latest-year SEC EDGAR XBRL fundamentals. Optional criteria: min_revenue, min_free_cash_flow, min_ebit_margin_pct, min_gross_margin_pct, max_debt_to_assets_pct, min_current_ratio, min_net_income (whole USD; *_pct as percentage numbers). Returns {summary, count, passing_count, criteria, results, as_of}; each result is {ticker, available, fiscal_year, passes, failed_criteria, metrics}. A MISSING METRIC FAILS its criterion, so a fail can mean 'no data', not 'bad company'. It never sweeps a universe and is not a discovery screen; a fundamentals filter, NOT a buy signal. Source: SEC EDGAR company-facts XBRL. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "tickers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "2-10 candidate ticker symbols to screen (hard-capped at 10)."
    },
    "min_revenue": {
      "type": "number",
      "description": "Min latest-year revenue (USD)."
    },
    "min_free_cash_flow": {
      "type": "number",
      "description": "Min latest-year free cash flow (USD)."
    },
    "min_ebit_margin_pct": {
      "type": "number",
      "description": "Min EBIT margin (percent)."
    },
    "min_gross_margin_pct": {
      "type": "number",
      "description": "Min gross margin (percent)."
    },
    "max_debt_to_assets_pct": {
      "type": "number",
      "description": "Max debt-to-assets (percent)."
    },
    "min_current_ratio": {
      "type": "number",
      "description": "Min current ratio."
    },
    "min_net_income": {
      "type": "number",
      "description": "Min latest-year net income (USD)."
    }
  },
  "required": [
    "tickers"
  ]
}

ol_intrinsic_value

ANONYMOUS OK

Per-share DCF, earnings-power value and Graham number from XBRL; no price or margin of safety

Full contract & input schema

Per-share intrinsic value from SEC EDGAR XBRL -- three textbook models: a levered-FCF DCF, a Greenwald Earnings-Power-Value, and the Graham number. Returns {summary, ticker, available, dcf_per_share, epv_per_share, graham_number, inputs, assumptions, as_of}; all three values are USD PER SHARE. `assumptions` are FIXED model constants, not per-name (10% discount, 2.5% terminal growth, 10y horizon, 21% tax). NO current price, market cap or margin of safety is returned -- fetch a price yourself and compare. A model is null when its inputs are negative or missing (null_reasons names the input), never computed as if debt-free. reason 'fetch_error' is transient: retry. The DCF is FCFE-style, not comparable to an enterprise-value DCF. Source: SEC EDGAR company-facts XBRL. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)."
    }
  },
  "required": [
    "ticker"
  ]
}

ol_issuer_kpi_panel

ANONYMOUS OK

Curated per-issuer KPI rows (ARR, NRR, RPO, DAU/MAU) from filing exhibits, with definitions and absences

Full contract & input schema

Tier-3 KPI PANEL for ONE issuer -- curated per-issuer metric rows (ARR, RPO/cRPO, NRR, logo retention, DAU/MAU, capacity utilization, ...) extracted from SEC filing exhibits, WITH the issuer's own metric definitions and full provenance. ABSENCE IS FIRST-CLASS: a row with value=null and is_absence=true is an answer (metric_availability says why), not a gap to fill from memory. Values are confidence-gated; needs_review rows are EXCLUDED. Optional filters: metric, period, industry. Per-issuer by design. For broad operating KPIs across 26 configured industries use ol_operating_kpis. Source: SEC 10-K/10-Q/8-K/6-K exhibits (Oxford Ledge parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Issuer symbol (single issuer, mandatory). There is deliberately NO cross-issuer panel read (SF-TIER3 D2) -- one metric across many issuers is not an answerable question on this surface."
    },
    "metric": {
      "type": "string",
      "description": "Optional metric key filter, e.g. arr, crpo, nrr."
    },
    "period": {
      "type": "string",
      "description": "Optional fiscal-period label, e.g. 2026-Q2."
    },
    "industry": {
      "type": "string",
      "description": "Optional panel tag filter, e.g. enterprise_saas."
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 800,
      "description": "Max rows (default 200, hard cap 800)."
    }
  },
  "required": [
    "ticker"
  ]
}

ol_operating_kpis

ANONYMOUS OK

Industry operating KPIs for one issuer (same-store sales, RevPAR, load factor, NIM) from SEC filings

Full contract & input schema

Industry OPERATING KPIs for ONE issuer -- the operational numbers the income statement hides (same-store sales, RevPAR, load factor, medical-loss ratio, net interest margin, TEU, rig count ...) extracted from SEC filings across 26 configured industries, each switched on by a deployment flag, so live coverage is a subset. Returns the issuer's panel {ticker, industry, metrics, available, partial, total_metrics, period, as_of, provenance, caveat, ...}. Anonymous callers get at most 3 rows (partial, total_metrics says how many); keyed callers get the full panel. Values are AS-REPORTED and confidence-gated. available=false cannot tell an uncovered industry from a covered issuer with no rows or an outage: do not retry with variations. Per-issuer by design. Source: SEC 10-K/10-Q/8-K exhibits (Oxford Ledge parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Issuer symbol, e.g. DAL, MAR, HCA, JPM, DAC (single issuer, mandatory). There is deliberately no cross-issuer KPI screen (SF-TIER3 D2): panels are per-issuer by design."
    }
  },
  "required": [
    "ticker"
  ]
}

ol_peer_fundamentals

ANONYMOUS OK

Latest-fiscal-year fundamentals side by side for up to 6 tickers; one year, no multiples

Full contract & input schema

Side-by-side latest-fiscal-year fundamentals for a peer set (the FIRST 6 unique tickers; extras are dropped silently). Returns {summary, count, peers, as_of}; each peer is {ticker, available, fiscal_year, revenue, net_income, eps_diluted, free_cash_flow, gross_margin_pct, ebit_margin_pct, debt_to_assets_pct, current_ratio, stockholders_equity}. Dollar figures are whole USD; *_pct are percentage numbers. ONE YEAR ONLY -- no history (use get_fundamentals). Fiscal years are each filer's own, so peers are NOT calendar-aligned. A ticker without XBRL coverage returns {ticker, available: false} (no metric keys), never dropped. No price leg, so no P/E or EV multiples. Source: SEC EDGAR company-facts XBRL; one cold EDGAR fetch per ticker.

{
  "type": "object",
  "properties": {
    "tickers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "2-6 ticker symbols to compare (hard-capped at 6)."
    }
  },
  "required": [
    "tickers"
  ]
}

search_company

ANONYMOUS OK

Resolve a company name, ticker or CIK to its SEC identity; up to 20 matches, substring not fuzzy

Full contract & input schema

Resolve a company name, ticker or CIK to its SEC identity. Returns {results, count}, up to 20 matches (hard cap); each result carries SEC-EDGAR-derivable identity fields only: ticker, companyName, cik, sicCode, exchange, fiscalYearEnd, tickerStatus (check it before treating a match as a live listing). A null field is OMITTED from the row, so never assume `cik` is present. Matching is a literal substring on ticker / companyName (plus the other class-share punctuation, and a digit-only CIK); former names and misspellings do NOT resolve. Once resolved, call get_fundamentals or get_business_summary. Source: Oxford Ledge company_profiles, restricted to the SEC-submissions column allowlist. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Search query (company name, ticker, or industry)"
    }
  },
  "required": [
    "query"
  ]
}

Filings & disclosure

5 toolsSOURCE: SEC EDGAR 10-K/20-F · FORM D DATA SETS · USASPENDING.GOV · USPTO ODP · FILING SEARCH: RANK FUSION OVER AN EMBEDDED CORPUS (METERED)

get_business_summary

ANONYMOUS OK

Curated business overview from the 10-K Item 1 (20-F Item 4), with source form, period and URL

Full contract & input schema

First-party business summary for a ticker from its SEC annual report (10-K Item 1; 20-F Item 4 for a foreign private issuer): a curated, verifier-gated overview of what the company does. Returns `summary` (a one-line status line, NOT the content), `available`, `business_summary` (prose truncated at ~2400 chars), `short_summary`, and provenance (source_form, source_period, source_url, generated_at). available=false means the store answered with no verified row and the provenance keys are ABSENT; an unreachable store is REFUSED (DATA_UNAVAILABLE). Rows generated before 2026-09-13 carry '10-K' and an empty `source_period` regardless of filer until the provenance restamp (tools/backfill_company_descriptions.py --restamp-provenance --apply) has run on prod. For semantic passage retrieval over the full filing corpus use ol_filing_search. Source: SEC EDGAR annual report (Oxford Ledge first-party summary); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    }
  },
  "required": [
    "ticker"
  ]
}

ol_federal_contracts

ANONYMOUS OKPIP ✓

Federal-contract obligations by ticker and fiscal year from USAspending, or the fiscal-year leaderboard

Full contract & input schema

Federal-contract obligation history for a ticker, OR the fiscal-year leaderboard -- for government-revenue-dependence diligence. TWO SHAPES: one of `ticker` or `fiscal_year` is REQUIRED (if both, `ticker` wins). With `ticker`: per-fiscal-year obligations (USD, the ten largest recipients, dropped_unresolved), NEWEST FY FIRST; with fiscal_year only: a leaderboard of the public companies on Oxford Ledge's USAspending crosswalk, largest first -- not of all federal contractors. A fiscal year still in progress is PARTIAL (period_complete false) and year-to-date -- never compare it to a full year. Obligations are federal awards, not company-reported revenue. `limit` default 20, hard cap 100. An unreachable store is REFUSED (DATA_UNAVAILABLE). Source: USAspending.gov (public domain; OL ticker-crosswalked); FREE. Attributing awards to a ticker is an Oxford Ledge curated crosswalk. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker (omit for the FY leaderboard)."
    },
    "fiscal_year": {
      "type": "integer",
      "description": "Fiscal year for the top-contractors leaderboard."
    },
    "limit": {
      "type": "integer",
      "description": "Max rows (default 20, hard cap 100).",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": []
}

ol_form_d_raises

ANONYMOUS OKPIP ✓

Recent SEC Form D private-placement filings, newest first, by industry group or trailing window

Full contract & input schema

Recent SEC Form D private-placement filings from the SEC's QUARTERLY Form D data set -- newest first, optionally scoped to an industry group and/or a trailing filing-date window. Returns {summary, count, days, industry, offerings}. Amounts are whole USD as disclosed; total_offering_amount is a ceiling, not money raised. The data set lands about one quarter after quarter-end, so a short `days` window is EMPTY BY CONSTRUCTION -- not evidence that nothing was filed. `limit` default 50, hard cap 200. Source: SEC EDGAR Form D data sets (public domain); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "industry": {
      "type": "string",
      "description": "Industry group filter (optional)."
    },
    "days": {
      "type": "integer",
      "description": "Trailing filing-date window in days (optional). The data set is quarterly with ~1 quarter of posting lag: a window shorter than that is empty by construction.",
      "minimum": 1,
      "maximum": 1825
    },
    "limit": {
      "type": "integer",
      "description": "Max offerings (default 50, hard cap 200).",
      "minimum": 1,
      "maximum": 200
    }
  },
  "required": []
}

ol_patents

ANONYMOUS OKPIP ✓

Recent USPTO patent filings for a ticker, newest first; a slice of up to 200, not the whole estate

Full contract & input schema

Recent USPTO patent filings for a ticker (innovation-intensity diligence). Returns {summary, ticker, count, filings}, NEWEST FIRST; `limit` default 50, hard cap 200, so this is a recent slice, never a full portfolio, and `count` is the number RETURNED. TRAP: a non-empty patent_number marks a continuation of an already-granted PARENT, NOT a grant of this application -- read the status field. Filings reflect Oxford Ledge's last on-demand USPTO ingest for the ticker, not a schedule; an unreachable store is REFUSED (DATA_UNAVAILABLE), never served as count 0. Source: USPTO (public domain); FREE. ATTRIBUTION: every filing field is USPTO ODP verbatim EXCEPT `ticker`, which is an Oxford Ledge applicant-name resolution, not a USPTO field. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker (e.g. GOOGL)."
    },
    "limit": {
      "type": "integer",
      "description": "Max filings (default 50, hard cap 200).",
      "minimum": 1,
      "maximum": 200
    }
  },
  "required": [
    "ticker"
  ]
}

Insiders & institutions

14 toolsSOURCE: SEC EDGAR 13F-HR · FORM 4 · SCHEDULE 13D/G · N-PORT · SEC FAILS-TO-DELIVER · FINRA SHORT INTEREST · ~45-DAY 13F DELAY

get_13f_holdings

ANONYMOUS OKPIP ✓

Largest positions in one institutional filer's latest 13F-HR, with quarter-over-quarter changes

Full contract & input schema

What one institutional filer owns: the largest positions in its latest SEC 13F-HR, plus quarter-over-quarter changes. Accepts a numeric CIK (preferred), a ticker (BRK-B or BRK.B for Berkshire), or a filer NAME (3-80 characters): ONE name-prefix match in the curated 13F filer universe resolves to its CIK (`resolved_from` says so); an ambiguous name is refused with candidates, never guessed. Returns {cik, fundName, filingDate, periodOfReport, totalHoldings, totalValue, holdings}; rows carry name, title_of_class, value (whole USD), shares, type, position_type, lots. There is NO ticker field and `cusip` is stripped on every channel (no plan carries a CUSIP Global Services licence today); `title_of_class` is the share-class discriminator. `position_type` is COM|PRN|PUT|CALL: do NOT sum across types. Source: SEC EDGAR 13F-HR, ~45-day quarterly delay. Heavy operation, max 2 concurrent. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "fund": {
      "type": "string",
      "description": "Fund CIK number (e.g. 1067983 for Berkshire Hathaway) -- preferred. A ticker is resolved via SEC's company map: letters with at most one class suffix (e.g. BLK, or BRK-B / BRK.B -- SEC lists Berkshire as BRK-A / BRK-B, so bare 'BRK' does not resolve). A filer NAME (3-80 chars) resolves when exactly one curated filer name starts with it; otherwise the error names ol_13f_filer_search. Any other shape is rejected as INVALID_PARAMS."
    },
    "max_holdings": {
      "type": "integer",
      "description": "Maximum number of holdings to return (default 50)",
      "minimum": 1,
      "maximum": 500
    }
  },
  "required": [
    "fund"
  ]
}

HEAVY · MAX 2 CONCURRENT

get_activist_stakes

ANONYMOUS OKPIP ✓

Schedule 13D/13G >5% beneficial-owner filings for a ticker, newest first, with staleness flags

Full contract & input schema

Schedule 13D/13G >5% beneficial-owner filings for a ticker -- event-driven stake-building, unlike quarterly 13F. Returns {ticker, count, filings}, newest first, limit default 50 (hard cap 200); rows carry filer_name, filing_date, form_type, shares, percent_of_class, accession_number and is_activist (a FORM-TYPE label: true iff the form is a 13D, not a judgement). `reports_zero` is the filer's own statement that it no longer owns more than 5% -- an exit OR a reporting realignment, so cross-check get_institutional_holders before reading it as a sale. FRESHNESS: keyless callers are served STORED rows and never trigger the EDGAR refresh -- read `stale` and `stale_basis`. Complements get_institutional_consensus. Source: SEC EDGAR. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    },
    "limit": {
      "type": "integer",
      "description": "Max filings to return (default 50)",
      "minimum": 1,
      "maximum": 200
    }
  },
  "required": [
    "ticker"
  ]
}

HEAVY · MAX 2 CONCURRENT

get_fails_to_deliver

ANONYMOUS OKPIP ✓

SEC fails-to-deliver history for one ticker, oldest first, anchored to the latest loaded date (~3-week lag)

Full contract & input schema

SEC fails-to-deliver history for one ticker -- the settlement-failure side of short pressure. Returns {ticker, days, history, count, window, coverage, as_of, summary}; each row is {date (settlement date), fails (SHARES, not dollars), price (USD), description}, OLDEST-FIRST. `days` (default 180, hard cap 730) is anchored to the latest LOADED settlement date, not to today; `end_date` ends it elsewhere. An empty or thin history describes the loaded SEC files, not an absence of fails: read `coverage` and `summary` before reading a gap. Prices are NOT split-adjusted. An unreachable store REFUSES (DATA_UNAVAILABLE). Pair with ol_short_interest_trend for the other half. Source: SEC Fails-to-Deliver dataset (published twice monthly, ~3-week lag). Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. GME)"
    },
    "days": {
      "type": "integer",
      "description": "Trailing window in days (default 180, max 730), ending at `as_of` unless end_date is given",
      "minimum": 1,
      "maximum": 730
    },
    "end_date": {
      "type": "string",
      "description": "ISO date (YYYY-MM-DD) the window ends on; default = the latest loaded settlement date. Pass today's date to measure against the calendar."
    }
  },
  "required": [
    "ticker"
  ]
}

get_insider_activity

RESEARCHER+

The 20 most recent Form 4 transactions for one ticker, with role, code, price and amendment folding

Full contract & input schema

Recent Form 4 insider transactions for one ticker. Returns {ticker, transactions}: THE 20 MOST RECENT TRANSACTION ROWS ONLY, newest by filing date -- the `days` argument is currently a NO-OP on this path. Rows carry insiderName, position (the role, the load-bearing signal), transType (the raw SEC code: 'P' open-market buy, 'S' sale, 'A' grant, 'M' option exercise; filter on it yourself), shares, pricePerShare, totalValue (USD, computed by Oxford Ledge at ingest), isDerivative (read it before treating `shares` as common stock) and url. A 4/A that repeats its original line is served once. Leave issuerSelfFiled rows out of any total. Market-wide buys: ol_insider_recent_buys; clusters: ol_insider_cluster_scan; >5% stakes: get_activist_stakes. Source: SEC EDGAR Form 4 (~2-day filing deadline). Requires Plus tier. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    },
    "days": {
      "type": "integer",
      "description": "Number of days of history (default 365)",
      "minimum": 1,
      "maximum": 1825
    }
  },
  "required": [
    "ticker"
  ]
}

get_institutional_consensus

ANONYMOUS OK

Tickers the tracked 13F filers most widely hold in common, ranked by fund count then value

Full contract & input schema

Which tickers the tracked 13F filers most widely hold in common. Returns {as_of_quarter, tickers_found, min_funds, consensus}; each row is {ticker, issuer_name, fund_count, total_value_usd, total_shares, funds}, ranked by fund_count then value. `consensus` is truncated to top_n (default 50, cap 200) while `tickers_found` is the FULL count meeting min_funds (default 2, cap 20). COMMON STOCK ONLY: an ownership tally, not exposure. Each fund contributes its own latest quarter, which may differ across funds. Answers 'who is buying X?'; get_13f_holdings answers 'what does fund X own?'. Source: SEC EDGAR 13F-HR, ~45-day quarterly delay. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "top_n": {
      "type": "integer",
      "description": "Maximum number of consensus tickers to return (default 50, max 200).",
      "minimum": 1,
      "maximum": 200
    },
    "min_funds": {
      "type": "integer",
      "description": "Minimum number of notable funds that must hold a ticker for it to appear (default 2, max 20).",
      "minimum": 1,
      "maximum": 20
    }
  },
  "required": []
}

get_institutional_holders

ANONYMOUS OK

Which institutions hold one ticker and which way they moved; top 100 by value, with coverage stats

Full contract & input schema

Which institutions hold one ticker, and which way they moved. Returns {ticker, holders, total_holders, as_of_quarter, coverage}. `holders` IS CAPPED AT THE TOP 100 BY VALUE while `total_holders` is the TRUE filer count. Each holder carries fund_name, fund_cik, shares, value_usd (whole USD), quarter, change_type and pct_change vs that fund's prior quarter. Check `coverage.basis` ('universe' vs 'top_display') before quoting an aggregate. Common stock only; funds file off-cadence, so quarters can differ per row. Rows are CUSIP-resolved: a resolution failure drops or mis-attributes a row. Direction-only view: ol_ownership_changes; fund-keyed inverse: get_13f_holdings. Source: SEC EDGAR 13F-HR, ~45-day quarterly delay. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    }
  },
  "required": [
    "ticker"
  ]
}

ol_13f_filer_analytics

ANONYMOUS OK

Concentration, turnover and holder persistence for one 13F filer by CIK; no position list

Full contract & input schema

Institutional-filer behavior derived purely from 13F holdings: concentration (top-N %, normalized HHI), turnover, and persistence (long-term holder vs fast money) for one filer. Pass a NUMERIC CIK (a ticker will not resolve here; ol_13f_filer_search finds it). Returns {summary, fund, quarter, n_positions, reported_long_equity_value, analytics}. The denominator is the filer's REPORTED 13F long-equity value, explicitly NOT AUM. NO position list -- use get_13f_holdings. A filer with one stored quarter gets no fabricated turnover rate; an unreachable store is REFUSED (DATA_UNAVAILABLE). Source: SEC EDGAR 13F-HR (public domain; Oxford Ledge derived); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "fund": {
      "type": "string",
      "description": "13F filer CIK (e.g. 1067983)."
    }
  },
  "required": [
    "fund"
  ]
}

ol_etf_lookthrough

ANONYMOUS OK

Underlying holdings of up to 25 ETFs as filed on Form N-PORT, value-ranked, with a ticker crosswalk

Full contract & input schema

ETF look-through: expand up to 25 ETF tickers into the underlying holdings the fund ITSELF filed on Form N-PORT. Returns {summary, count, etfs, units}; per ETF {etf, name, category, net_assets, in_catalog, series_id, opaque, reason, period_date, holdings_available, top_holdings}. `top_holdings` is value-ranked (default 10, hard cap 50), each with underlying_ticker, issuer_name, value_usd (whole USD), pct_of_nav (as filed) and weight_pct (Oxford Ledge derived; the shown rows will not sum to 100). HOLDINGS-ONLY: no yield, NAV or premium. An ETF with no filed holdings comes back opaque=true with `reason`; a store outage is DATA_UNAVAILABLE, never opaque. Source: SEC EDGAR N-PORT, latest filed period per series (~60-day lag); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "tickers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "ETF ticker symbols to expand (e.g. ['VOO','QQQ']). Max 25.",
      "maxItems": 25
    },
    "top_holdings": {
      "type": "integer",
      "description": "Max underlying holdings per ETF (default 10, hard cap 50).",
      "minimum": 1,
      "maximum": 50
    }
  },
  "required": [
    "tickers"
  ]
}

HEAVY · MAX 2 CONCURRENT

ol_insider_cluster_scan

ANONYMOUS OK

Active multi-insider Form 4 buy or sell clusters for a watchlist, one ticker, or market-wide with no ticker

Full contract & input schema

FLAGSHIP MOAT: scan for ACTIVE multi-insider cluster-buy/sell signals (distinct-actor Form 4 clusters with statistical strength). THREE GRAINS: pass `tickers` for a watchlist, a single `ticker` for that name's recent fired clusters, or NEITHER for the MARKET-WIDE scan (top fired clusters across every ticker, one row per ticker and direction). Returns {summary, count, events, grain, since_days, limit}; each event is {ticker, direction, insider_count, z_score, sector_z_score, percentile, window_start, window_end}. No fired clusters returns events=[] -- absence is not a signal; an unreachable store is REFUSED. Source: SEC EDGAR Form 4, windowed on FILING date so it is look-ahead-safe; FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "tickers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Watchlist of ticker symbols (e.g. ['AAPL','MSFT']). Max 100. Use this OR `ticker`.",
      "maxItems": 100
    },
    "ticker": {
      "type": "string",
      "description": "Single ticker for its recent fired clusters (alternative to `tickers`). Omit BOTH for the market-wide scan."
    },
    "since_days": {
      "type": "integer",
      "description": "Window in days: watchlist default 7 (max 365); market-wide default 30 (clamped to 90); ignored for the single-ticker grain.",
      "minimum": 1,
      "maximum": 365
    },
    "min_insider_count": {
      "type": "integer",
      "description": "Minimum distinct insiders for a fired cluster (default 3).",
      "minimum": 1,
      "maximum": 50
    },
    "limit": {
      "type": "integer",
      "description": "Max events to return (default 25, hard cap 100; market-wide grain: default 10, clamped to 25).",
      "minimum": 1,
      "maximum": 100
    }
  }
}

ol_insider_recent_buys

ANONYMOUS OKPIP ✓

Open-market insider purchases across ~5.3k tickers in a trailing window, newest first

Full contract & input schema

Recent OPEN-MARKET insider PURCHASES (SEC code 'P') across the Oxford Ledge issuer catalog (~5.3k tickers) -- a daily insider screen. Returns {summary, since_days, count, buys}, NEWEST FIRST; each buy carries ticker, filing and transaction dates, insiderName, position, shares, pricePerShare, totalValue (USD), url. since_days default 30 (hard cap 180), limit default 25 (hard cap 100). SAMPLING TRAP: when the window holds more purchases than `limit`, you get the NEWEST N filings, not the whole window -- never total these rows and call it the period's insider buying. totalValue and the fallback `position` label are the two derived fields. Source: SEC EDGAR Form 4 (public domain); FREE. Pairs with ol_insider_cluster_scan and get_insider_activity. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "since_days": {
      "type": "integer",
      "description": "Trailing window in days (default 30, hard cap 180).",
      "minimum": 1,
      "maximum": 180
    },
    "limit": {
      "type": "integer",
      "description": "Max purchases (default 25, hard cap 100).",
      "minimum": 1,
      "maximum": 100
    },
    "common_only": {
      "type": "boolean",
      "description": "Common stock only (default true): drops derivative rows and any security title naming preferred / pfd / warrant / debenture / note. Series-named common classes (\"Series A Common Stock\") are kept. false restores every 'P' row."
    },
    "min_value": {
      "type": "number",
      "minimum": 0,
      "description": "Only purchases with totalValue >= this many USD (optional). Rows whose totalValue is null -- the filed price failed the plausibility gate -- are dropped when this is set."
    }
  },
  "required": []
}

ol_institutional_confluence

ANONYMOUS OK

One verdict per ticker fusing 13F accumulation, Form 4 net buying and cluster confirmation

Full contract & input schema

Quarter-aligned institutional-confluence read for one ticker: 13F accumulation x insider Form 4 net buying x buy-cluster confirmation, fused on the ticker's reference 13F quarter. Returns a `verdict` (confluence_accumulation / confluence_distribution / partial_bullish / partial_bearish / neutral, or insufficient_13f_coverage / insufficient_history, never coerced to neutral), plus `institutional`, `insider`, `cluster`, `coverage` and a one-line `summary`. A DERIVED verdict, not raw data: no per-fund rows. Pairs with get_institutional_holders and ol_insider_cluster_scan. Source: SEC EDGAR 13F-HR + Form 4 (Oxford Ledge derived fusion); FREE, no tier gate and no AI metering. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol, e.g. AAPL."
    }
  },
  "required": [
    "ticker"
  ]
}

ol_ownership_changes

ANONYMOUS OK

Each fund's position in one ticker classified new, increased, decreased or exited; 50 rows per bucket

Full contract & input schema

Quarter-over-quarter institutional accumulation/distribution for one ticker: each fund's position classified new / increased / decreased / exited over the last ~4 quarters -- the 'who is moving on X?' flow read. Returns {summary, ticker, quarters_analyzed, counts, new_positions, increased, decreased, exited, split_suspect, ...}. `counts` is always the COMPLETE tally; `limit` bounds EACH row list (default 50, hard cap 250). Pairs whose delta looks like an unconfirmed corporate action are withheld into `split_suspect` with their as-filed counts. Share classes are never summed or netted. Rows are CUSIP-resolved, so treat share figures as close approximations. Complements get_institutional_holders (snapshot). Source: SEC EDGAR 13F-HR (public domain; OL derived); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker (e.g. AAPL)."
    },
    "limit": {
      "type": "integer",
      "description": "Max rows per bucket (default 50, hard cap 250); counts are complete regardless.",
      "minimum": 1,
      "maximum": 250
    }
  },
  "required": [
    "ticker"
  ]
}

ol_short_interest_trend

ANONYMOUS OK

FINRA biweekly short-percent series for one ticker, oldest first, up to 26 readings

Full contract & input schema

FINRA short-interest trend for one ticker: the biweekly settlement-date short-percent series, OLDEST-FIRST. Returns {summary, ticker, count, as_of, trend}; each point is EXACTLY {date (settlement date), shortPct (a PERCENTAGE number)} -- no share counts and no days-to-cover. `points` default 6, max 26 (about one year). A fortnightly SNAPSHOT with a reporting lag, not a live short-float figure; `as_of` is the settlement date, not today. Honest-empty for a ticker FINRA does not publish; an unreachable store is REFUSED (DATA_UNAVAILABLE). Source: FINRA (public); FREE.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker (e.g. GME)."
    },
    "points": {
      "type": "integer",
      "minimum": 1,
      "maximum": 26,
      "description": "Readings to return (default 6, max 26)."
    }
  },
  "required": [
    "ticker"
  ]
}

BDC & private credit

10 toolsSOURCE: SEC EDGAR BDC 10-K/10-Q SCHEDULES OF INVESTMENTS (OXFORD LEDGE PARSE) · ~45-60 DAY LAG · OL-DERIVED KEYS AND MARKS

get_bdc_list

ANONYMOUS OKPIP ✓

Roster of the active BDCs Oxford Ledge tracks, by portfolio size, with reconciled fair-value totals

Full contract & input schema

Roster of the ACTIVE BDCs tracked by Oxford Ledge, sorted by portfolio size -- our coverage, not the whole BDC universe; wound-down issuers are excluded by design. No arguments. Returns {bdcs, count}; per BDC: ticker, name, listed (false for a non-traded BDC carried under a pseudo-ticker such as AGTC), holdingCount, totalFairValue (whole USD, latest filing), filingDate, lastParsed and a reconciliation block (reportedTotalFairValue, fairValueBasis, parsedRowSumFairValue, fairValueRefused, fairValueGap, fairValueGapNote). READ fairValueBasis BEFORE using totalFairValue, and fairValueGap with it: an under-counting parse keeps the parsed-row sum, which UNDERSTATES the book. holdingCount, totalFairValue and the arbitration are Oxford Ledge's parse, not filer-published figures. Borrower-keyed counterpart: search_bdc_borrower. Source: SEC EDGAR BDC filings (Oxford Ledge parse). Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {}
}

ol_bdc_borrower_dispersion

ANONYMOUS OKPIP ✓

How each BDC lender prices the same private-credit loan: spread, mark and fair value, widest first

Full contract & input schema

MOAT: cross-lender loan-pricing DISPERSION for one private-credit borrower -- how N different BDCs each price the SAME loan (spread / mark / fair value); when one BDC marks a borrower S+550 @ 98 and another S+575 @ 99, the lenders disagree on the credit. Pass the canonical `borrower_norm` (from ol_bdc_top_borrowers or search_bdc_borrower). Returns {summary, borrower_norm, count, lender_count, tranche_count, lenders, ...}: ONE row per BDC lender, widest spread first, tranches nested. UNITS TRAP: `spread` is the raw as-filed number and mixes percent and bps across filers -- compare lenders on `spread_bps` only. Exited positions are excluded by default (include_stale=true shows them). Default 25 lenders, hard cap 100. Source: SEC EDGAR BDC schedules of investments (Oxford Ledge parse; ol-derived); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "borrower_norm": {
      "type": "string",
      "description": "Canonical normalized borrower key (from ol_bdc_top_borrowers or borrower search)."
    },
    "limit": {
      "type": "integer",
      "description": "Max LENDERS to return (default 25, hard cap 100); each lender's tranches ride nested.",
      "minimum": 1,
      "maximum": 100
    },
    "include_stale": {
      "type": "boolean",
      "description": "Include lenders whose newest filing no longer names this borrower (stale marks). Default false."
    }
  },
  "required": [
    "borrower_norm"
  ]
}

HEAVY · MAX 2 CONCURRENT

ol_bdc_common_borrowers

ANONYMOUS OKPIP ✓

Borrowers held in common by a given set of BDCs, most widely held first; debt positions only

Full contract & input schema

Borrowers common to a GIVEN SET of BDCs -- the cross-portfolio set question ('what do ARCC, OBDC and AGTC all lend to?') in ONE call. Returns per borrower: borrower, borrower_norm, holder_count, holder_tickers, holders ([{ticker, name}]), total_fair_value and total_par_amount in USD, and as_of_oldest/as_of_newest. Each BDC is read at ITS most recent filing, so rows MIX filing dates -- read as_of_range before treating the marks as contemporaneous. Debt positions only. Caps: bdc_tickers truncated at 25, limit 50/200, min_holders max 50. Feed a borrower_norm to ol_bdc_borrower_dispersion for cross-lender pricing. Source: SEC EDGAR BDC schedules of investments (Oxford Ledge parse -- ol-derived); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "bdc_tickers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "BDC symbols to intersect, e.g. [\"ARCC\",\"OBDC\",\"AGTC\"] (max 25)"
    },
    "min_holders": {
      "type": "integer",
      "description": "Minimum number of the supplied BDCs that must hold the borrower (default 2, min 2 -- this answers what is SHARED; for one BDC's book use ol_bdc_top_borrowers)",
      "minimum": 2,
      "maximum": 50
    },
    "limit": {
      "type": "integer",
      "description": "Max borrowers to return (default 50, max 200)",
      "minimum": 1,
      "maximum": 200
    }
  },
  "required": [
    "bdc_tickers"
  ]
}

ol_bdc_credit_quality

ANONYMOUS OKPIP ✓

Share of a BDC's debt book on non-accrual, latest filing plus the trailing-quarter trend

Full contract & input schema

BDC non-accrual credit-deterioration signal: the share of debt fair value on non-accrual (loans that stopped paying) in the latest filing, plus the trailing-quarter trend. Returns {summary, ticker, latest, trend}; `trend` is OLDEST-FIRST. flagged_pct is a percentage over the DETERMINATE-flag denominator, never total_debt_fv, and it is deliberately NULL (withheld, never zero) when determinate coverage is under 90% of debt fair value or the flag rate looks like a parse misread -- read `coverage_state` before reading flagged_pct. `quarters` default 12, hard cap 24. Pairs with ol_bdc_borrower_dispersion and ol_bdc_top_borrowers. Source: SEC EDGAR BDC schedule-of-investments non-accrual flags (Oxford Ledge parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "BDC ticker (e.g. ARCC, ORCC, FSK)."
    },
    "quarters": {
      "type": "integer",
      "description": "Trailing quarters of trend (default 12, hard cap 24).",
      "minimum": 1,
      "maximum": 24
    }
  },
  "required": [
    "ticker"
  ]
}

ol_bdc_fee_load

ANONYMOUS OK

One BDC's base and incentive fees for a fiscal year, with NII-based dividend coverage

Full contract & input schema

BDC fee load + NII-based dividend coverage for ONE fiscal year (not a series). Returns {summary, ticker, fiscal_year, fees, dividend_coverage}; `fees` holds base and incentive fees and net investment income in whole USD; `dividend_coverage` is NII divided by dividends paid, a ratio (0.92 means 0.92x) -- under 1.0 means the dividend is funded partly from capital. Fee RATE percentages and the dividend amount are not returned. A missing input is served as null and named, never as zero; an unreachable store is REFUSED (DATA_UNAVAILABLE). An internally-managed BDC carries no fee lines by construction. Source: SEC EDGAR 10-K/10-Q Statement-of-Operations XBRL, NOT a vendor fundamentals feed; FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "BDC ticker (e.g. ARCC, MAIN, HTGC)."
    }
  },
  "required": [
    "ticker"
  ]
}

ol_bdc_loan_pricing_trend

ANONYMOUS OK

Per-quarter weighted average spread, mark and position counts for one BDC's loan book

Full contract & input schema

Per-quarter loan-pricing trend for ONE BDC, from the parsed schedule of investments: fair-value-weighted average credit spread, average mark and position counts, OLDEST-FIRST. Returns {summary, ticker, count, spread_unit, trend}. `weighted_avg_spread` is in BASIS POINTS, normalized at the store (the as-filed column mixes percent and bps, so never average a raw spread yourself). Prefer `priced_borrowers` over `debt_positions` across quarters: position counts are parser-grain-dependent. `quarters` default 16, max 24. Honest-empty (trend=[]) for an unparsed BDC. Pairs with ol_bdc_borrower_dispersion. Source: SEC EDGAR BDC 10-Q/10-K schedules of investments (Oxford Ledge parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "BDC ticker (e.g. ARCC)."
    },
    "quarters": {
      "type": "integer",
      "minimum": 1,
      "maximum": 24,
      "description": "Quarters to return (default 16, max 24)."
    }
  },
  "required": [
    "ticker"
  ]
}

ol_bdc_mark_changes

ANONYMOUS OKPIP ✓

Largest quarter-over-quarter mark moves across a set of BDC portfolios, up and down, by borrower

Full contract & input schema

MOAT / private credit: the largest quarter-over-quarter MARK moves across a SET of BDC portfolios -- 'which borrowers got marked up or down the most last quarter, and by whom' in ONE deterministic call. Returns {increases, decreases}, each a global ranking; each row is {borrower, portfolio, prior_mark, latest_mark, mark_delta, prior_filing, latest_filing}. Marks are percent of par, fair-value-weighted across the BDC's tranches; mark_delta is in points. A borrower enters only when |mark_delta| >= 1.0 point and its fair value is >= $500k. Implausible moves are held in `suspect_moves` rather than ranked. `coverage` names every BDC that was NOT read and why. bdc_tickers capped at 25; limit default 10 / hard 50 per direction. Source: SEC 10-K/10-Q schedules of investments (Oxford Ledge parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "bdc_tickers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "BDC symbols to compare, e.g. [\"ARCC\",\"FSK\",\"OBDC\"] (max 25; the excess is reported in `coverage.not_covered_detail` rather than dropped)"
    },
    "limit": {
      "type": "integer",
      "description": "Rows per direction (default 10, hard cap 50). Ranking is GLOBAL across the supplied portfolios, not per BDC.",
      "minimum": 1,
      "maximum": 50
    }
  },
  "required": [
    "bdc_tickers"
  ]
}

HEAVY · MAX 2 CONCURRENT

ol_bdc_top_borrowers

ANONYMOUS OKPIP ✓

Private-credit borrowers syndicated across the most BDCs, ranked by active lender count then exposure

Full contract & input schema

MOAT / BDC discovery: the private-credit borrowers syndicated across the MOST BDCs, ranked by ACTIVE lender count (holder_count_active), then holder_count, then exposure -- the entrypoint for the BDC/private-credit category. Returns {summary, count, borrowers}; each row is {borrower, borrower_norm (the key other ol_bdc_* tools take), holder_count, total_fair_value (whole USD, latest filings), industry}. Feed a borrower_norm into ol_bdc_borrower_dispersion for cross-lender pricing. Caps: limit default 25 / hard 100; min_holders default 2 (max 50). Parser mis-ingests are filtered out when the filter is available (`borrower_filters` says whether it ran). Source: SEC EDGAR BDC schedules of investments (Oxford Ledge parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "min_holders": {
      "type": "integer",
      "description": "Minimum number of BDC lenders a borrower must appear in (default 2).",
      "minimum": 1,
      "maximum": 50
    },
    "limit": {
      "type": "integer",
      "description": "Max borrowers to return (default 25, hard cap 100).",
      "minimum": 1,
      "maximum": 100
    }
  }
}

HEAVY · MAX 2 CONCURRENT

ol_borrower_profile

ANONYMOUS OK

Profile, verified M&A history and public-market footprint for one private-credit borrower by key

Full contract & input schema

Profile + verified M&A/ownership history for one private-credit borrower. Pass the canonical `borrower_norm` (from ol_bdc_top_borrowers). Returns {summary, borrower_norm, borrower, profile, acquisitions, count, public_footprint}: the `profile` (description, industry, known_lenders, lien_position, estimated_size, notes, source), VERIFIED-only `acquisitions` (unaudited or refuted events never surface), and `public_footprint` (patents, federal contracts, corporate events) matched by exact normalized name, with `matched_entities` provenance -- a same-name match is never a guaranteed identity, and an empty footprint is normal for a private borrower. Honest-empty when the key is unknown. Source: SEC EDGAR BDC Schedule-of-Investments + attributed public M&A events (Oxford Ledge proprietary parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "borrower_norm": {
      "type": "string",
      "description": "Canonical normalized borrower key (from ol_bdc_top_borrowers)."
    },
    "limit": {
      "type": "integer",
      "description": "Max M&A/ownership events to return (default 20, hard cap 100).",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": [
    "borrower_norm"
  ]
}

search_bdc_borrower

ANONYMOUS OKPIP ✓

Which BDCs lend to one private-credit borrower, fuzzy-matched on name, with per-tranche marks

Full contract & input schema

Which BDCs lend to one private-credit borrower, matched fuzzily on name -- Oxford Ledge's parse of SEC EDGAR BDC schedules of investments (ol-derived), not filer-published data. Returns borrowerName, borrowerNorm (the key the ol_bdc_* tools take), description, descriptionSource, industry, aggregates over CURRENT holders (totalHolders, totalParAmount, totalFairValue, avgMarkedPrice) and `holders`, one row per TRANCHE. ABOVE-PAR TRAP: a mark above 100 is not a credit premium until fairValue is checked against cost. `description` is a compiled company profile from public sources, NOT filing text. A miss is {found: false} -- a search miss, not a finding of no BDC exposure. `query` needs 3+ characters; `limit` / `offset` page `holders` only. Source: SEC EDGAR BDC schedules of investments (~45-60 day lag). Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Borrower/company name to search (e.g. Finastra, Medline); at least 3 characters."
    },
    "limit": {
      "type": "integer",
      "description": "Max `holders` rows to return (default 5000 = every row; hard cap 5000, refused above). Pages the tranche rows only.",
      "minimum": 1,
      "maximum": 5000
    },
    "offset": {
      "type": "integer",
      "description": "Rows to skip before the page (default 0; max 5000). Past the end returns an empty page with `page.total` intact.",
      "minimum": 0,
      "maximum": 5000
    }
  },
  "required": [
    "query"
  ]
}

HEAVY · MAX 2 CONCURRENT

Bonds & rates

5 toolsSOURCE: TREASURY.GOV PAR YIELDS VIA FRED · SEC EDGAR DEBT SCHEDULES · LQD/HYG DIRECTORY (REFERENCE ONLY) · FINRA TRACE (RETIRED PATH)

get_bond_data

KEY REQUIRED

Single-bond trade detail by CUSIP from FINRA TRACE; the host was retired in 2026-07, expect nulls

Full contract & input schema

RETIRED (2026-09-13). This was single-bond trade detail by CUSIP over FINRA's public TRACE bond page; FINRA auth-walled that host in 2026-07 and Oxford Ledge holds no licence to redistribute TRACE trade data, so no CUSIP can be priced here. It makes NO network call and returns, in under a millisecond, the same FIXED-KEY envelope it always did -- cusip, type, issuer, coupon, maturity, debtType, is144A, lastPrice, lastYield, ytm, high, low, volume, tradeDate, source, fetchedAt, ytw, callable, duration, modifiedDuration, moodysRating, spRating, fitchRating, error -- with every price field null, plus {status: 'retired', unavailable: true, note, use_instead: 'ol_bond_directory_screen'}; `error` carries the retirement note, or 'CUSIP must be at least 6 characters' when the input itself is malformed, so a caller can tell their input from our outage. The name is kept because the wheel and the licence-class registries pin it. Use ol_bond_directory_screen (the persisted LQD/HYG corporate-bond directory: issuer, grade, coupon and maturity -- reference data, no prices) or get_debt_maturities for one issuer's own maturity schedule. Source: none (retired FINRA TRACE endpoint).

{
  "type": "object",
  "properties": {
    "cusip": {
      "type": "string",
      "description": "9-character CUSIP identifier (e.g. 037833AK6 for Apple)"
    }
  },
  "required": [
    "cusip"
  ]
}

get_yield_curve

ANONYMOUS OKPIP ✓

US Treasury par yield curve over 11 tenors with 3-month and 1-year history, or the wider macro snapshot

Full contract & input schema

US Treasury yield curve, plus (by default) the wider macro dashboard. TWO DIFFERENT SHAPES. include_history=true returns PARALLEL ARRAYS over 11 tenors (1M..30Y) -- today, ~91 days ago and ~1 year ago -- for steepening/inversion work. include_history false (the DEFAULT) returns {data: [...]}, a flat LIST of latest-value rows that mixes the Treasury tenors WITH CPI, unemployment, GDP, mortgage-rate and national-debt series. UNITS: yields are PERCENT numbers (4.25 means 4.25%). The credit-spread OAS series and UMCSENT were removed 2026-07-21 (licensed data) and are NOT in the list. Source: Treasury.gov daily par yields (FRED fallback) plus FRED series; cached 4h. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "include_history": {
      "type": "boolean",
      "description": "Include yield curve from 1 year ago for comparison (default false)"
    }
  }
}

ol_bond_directory_screen

KEY REQUIRED

Screen the LQD/HYG corporate-bond directory by issuer, grade, coupon and maturity; reference only, no identifiers

Full contract & input schema

Screen the corporate-bond browse directory by issuer name, grade (IG or HY), and optional coupon and maturity bands. Returns {summary, count, total_matching, bonds}; each bond is {key, issuer_name, coupon (percent number), coupon_type, maturity_date, grade, source_etf}. REFERENCE DATA ONLY -- no price, yield, spread or trade activity, and no security identifier. `grade` is the coarse SOURCE BUCKET (IG=LQD, HY=HYG), NOT a credit rating. limit default 25, hard cap 100. NOTE ON PROVENANCE: the directory is sourced from the LQD (investment-grade) and HYG (high-yield) ETF holdings. Its ingest-source redistribution posture is unresolved (COUNSEL memo 2026-07-07), so this is a FIRST-PARTY-only tool and is off the third-party redistribution surface until cleared. FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "issuer": {
      "type": "string",
      "description": "Issuer-name substring to match (optional). Identifier lookups are not supported."
    },
    "grade": {
      "type": "string",
      "description": "Source bucket: 'IG' (LQD universe) or 'HY' (HYG universe). Not a rating."
    },
    "coupon_min": {
      "type": "number",
      "description": "Minimum coupon percent, applied in-tool over the fetched page (optional)."
    },
    "coupon_max": {
      "type": "number",
      "description": "Maximum coupon percent, applied in-tool over the fetched page (optional)."
    },
    "maturity_after": {
      "type": "string",
      "description": "Only bonds maturing on/after this ISO date (YYYY-MM-DD), optional."
    },
    "maturity_before": {
      "type": "string",
      "description": "Only bonds maturing on/before this ISO date (YYYY-MM-DD), optional."
    },
    "limit": {
      "type": "integer",
      "description": "Max bonds to return (default 25, hard cap 100).",
      "minimum": 1,
      "maximum": 100
    },
    "offset": {
      "type": "integer",
      "description": "Pagination offset (used only when no coupon/maturity band is set).",
      "minimum": 0,
      "maximum": 100000
    }
  }
}

ol_maturity_wall

ANONYMOUS OK

Corporate debt maturities aggregated by year, market-wide or for up to 50 tickers, in USD millions

Full contract & input schema

The corporate DEBT MATURITY WALL: scheduled bond/loan maturities aggregated by year -- total_by_year, by_rating, top_issuers_by_year, peak_year, concentration. Amounts are USD MILLIONS (see `units`). `by_rating` splits IG vs HY by an Oxford Ledge LEVERAGE HEURISTIC, NOT agency ratings. Market-wide when `tickers` is omitted, or scoped to up to 50 tickers. Coverage is companies with PARSED maturity schedules only; an issuer with debt and no parsed schedule is NAMED under `unscheduled`, never estimated. LATENCY: a scoped call can fetch each issuer's annual report LIVE from SEC EDGAR -- seconds per issuer, minutes for a cold list. Source: SEC filing debt schedules + balance sheets (Oxford Ledge parse); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "tickers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Optional issuer symbols (max 50); omit for market-wide."
    },
    "by_sector": {
      "type": "boolean",
      "description": "Include the by-sector split (default false)."
    }
  },
  "required": []
}

HEAVY · MAX 2 CONCURRENT

search_bonds

KEY REQUIRED

Retired: FINRA TRACE issuer search is auth-walled; answers status 'retired' and points at ol_bond_directory_screen

Full contract & input schema

RETIRED (2026-09-13). This was a bond search by issuer name over FINRA's public TRACE issuer-search host; FINRA auth-walled that path behind its Federated Identity Platform in 2026-07 and Oxford Ledge holds no licence to redistribute TRACE data, so the tool answers no query. It makes NO network call and returns, in under a millisecond, {status: 'retired', note, use_instead: 'ol_bond_directory_screen', query, issuers: [], totalBonds: 0, error} -- the empty lists are the retired shape, never a search result. The name is kept because the wheel and the licence-class registries pin it. Use ol_bond_directory_screen (the persisted LQD/HYG corporate-bond directory: issuer, grade, coupon and maturity filters) for corporate-bond discovery, or get_debt_maturities for one issuer's own maturity schedule. Source: none (retired FINRA TRACE endpoint).

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Issuer name to search (e.g. Apple, Goldman Sachs)"
    }
  },
  "required": [
    "query"
  ]
}

Macro & FRED

6 toolsSOURCE: FRED (ST. LOUIS FED) · FRED RELEASE CALENDAR · TREASURY MSPD · CFTC COT · FDIC BANKFIND

get_economic_calendar

KEY REQUIRED

Upcoming US macro release dates for nine tracked series (CPI, jobs, GDP, PCE); dates only, no estimates

Full contract & input schema

Upcoming US macro DATA-RELEASE DATES (a schedule, not estimates) for the 9 tracked releases (CPI, Core CPI, Employment Situation, GDP, Jobless Claims, PCE, PPI, Industrial Production, Retail Sales). Returns {days, events, count, source_status, error}; each event is only {date, event, release_id, release_name} -- NO prior/consensus/actual values, and FOMC meeting dates are NOT included. `days` look-ahead default 90; above 180 is REFUSED. ALWAYS CHECK source_status: every failure mode returns events=[], otherwise indistinguishable from a quiet calendar. Cached 6h. Source: FRED releases/dates. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "days": {
      "type": "integer",
      "description": "Number of days to look ahead (default 90)",
      "minimum": 1,
      "maximum": 180
    }
  }
}

get_fred_data

KEY REQUIREDPIP ✓

One FRED or Treasury series from the ~41-series macro snapshot, latest observation only

Full contract & input schema

One series from Oxford Ledge's macro SNAPSHOT (about 41 FRED / Treasury series, newest observation each) by FRED id. Returns {series, data: {series, name, value, date, ytdChange, _source}, observation: 'latest_only', note} -- ONE observation, never a history. A series outside the snapshot returns {series, error, available}. THIRD-PARTY LICENSED SERIES ARE REFUSED WITH THE REASON (2026-09-13): the ICE BofA OAS credit spreads (BAMLH0A0HYM2, BAMLC0A0CM, BAMLC0A1CAAA, BAMLH0A1HYBB, BAMLH0A2HYB, BAMLH0A3HYC), VIXCLS and UMCSENT are licensed content FRED redistributes under its own terms and Oxford Ledge does not; the answer is {series, data: null, refusal_reason: 'third_party_licence', licensor, error, note, directional, directional_unavailable_reason}. RESTRICTED posture: a keyed tool on the hosted channel. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "series": {
      "type": "string",
      "description": "FRED series ID (e.g. GDP, UNRATE, CPIAUCSL, DFF)"
    }
  },
  "required": [
    "series"
  ]
}

ol_bank_structure_events

ANONYMOUS OK

FDIC structural events for one bank by cert or name: failures, mergers, acquisitions, charter changes

Full contract & input schema

FDIC STRUCTURAL EVENTS for one bank -- failures, assisted resolutions, mergers, acquisitions, charter changes -- newest first, with is_failure flagged from the FDIC CHANGECODE taxonomy. Pass `cert` (FDIC certificate) or `name`; a name resolves against active institutions AND closed charters, so a FAILED or merged-away bank (Signature Bank, First Republic) IS found, and `resolved_from` says which charter matched. Ambiguous names return an `ambiguous` candidate list instead of guessing -- call again with the `cert`. An unreachable FDIC store is DATA_UNAVAILABLE, never an empty list. Complements ol_fdic_bank (active institutions only). Source: FDIC public data; FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "cert": {
      "type": "integer",
      "description": "FDIC certificate number."
    },
    "name": {
      "type": "string",
      "description": "Bank name to resolve when cert is unknown."
    },
    "limit": {
      "type": "integer",
      "description": "Max events (default 25, cap 100).",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": []
}

ol_cftc_cot

ANONYMOUS OKPIP ✓

CFTC Commitments of Traders positioning for gold, crude oil and S&P 500: weekly history or latest

Full contract & input schema

CFTC Commitments-of-Traders positioning for THREE curated markets (keys gold, crude_oil, sp500). Pass `market` for its weekly history, NEWEST FIRST (names are normalised: 'wti', 'GOLD', 'e-mini s&p' resolve), or omit it for the latest report across the three. Returns {summary, market, matched_market, rows}. mm_* and open_interest are CONTRACT counts, not dollars, and mm_* cover ONE speculative category per report family (Managed Money for gold and crude_oil, Leveraged Funds for sp500). `limit` (series only) default 52, hard cap 156. An unreachable store is REFUSED (DATA_UNAVAILABLE). Source: CFTC.gov (public domain); FREE. ATTRIBUTION: every field is CFTC verbatim EXCEPT market_key and market_label (Oxford Ledge's curated market catalog) and mm_net (mm_long minus mm_short, computed by Oxford Ledge). Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "market": {
      "type": "string",
      "description": "Market key, CFTC report name or desk symbol -- gold / GC / XAU, crude_oil / CL / WTI, sp500 / ES / S&P (case-insensitive; omit for the latest all-market snapshot)."
    },
    "limit": {
      "type": "integer",
      "description": "Weekly reports for a market series (default 52, hard cap 156).",
      "minimum": 1,
      "maximum": 156
    }
  },
  "required": []
}

ol_fdic_bank

ANONYMOUS OKPIP ✓

FDIC-insured banks and thrifts by name prefix, largest first, with assets, deposits and report date

Full contract & input schema

FDIC-insured banks and thrifts. Pass `query` for a name PREFIX search against ACTIVE institutions (largest-asset first), or omit it for the largest active institutions. Returns {summary, query?, found?, institutions}. UNITS: `asset` and `dep` are THOUSANDS of dollars. The store is loaded ACTIVE-ONLY, so a bank that merged away or failed is absent by construction; a name miss says whether a structure event explains it (`structure_event`). An unreachable store is REFUSED (DATA_UNAVAILABLE). `limit` default 25, hard cap 100. Source: FDIC.gov (public domain); FREE. ATTRIBUTION: every field is FDIC BankFind verbatim EXCEPT `ticker`, which is an Oxford Ledge-verified CERT-to-ticker mapping, not an FDIC-published field. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Institution name prefix (omit for the top-by-assets list)."
    },
    "limit": {
      "type": "integer",
      "description": "Max institutions (default 25, hard cap 100).",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": []
}

ol_treasury_debt

ANONYMOUS OKPIP ✓

US Treasury debt composition from the MSPD: latest month by class, or one class's monthly history

Full contract & input schema

US Treasury debt composition from the Monthly Statement of the Public Debt. Omit BOTH args for the newest month's full class breakdown; pass security_type AND security_class together for that one class's monthly history (passing only one of them is IGNORED). Returns {summary, rows}. AMOUNTS ARE IN MILLIONS OF USD -- a 28,000,000 value means $28 trillion. The breakdown holds component AND Total rows, so summing a column double-counts. `limit` (series only) default 120 months, hard cap 360. An unreachable store is REFUSED (DATA_UNAVAILABLE). Sibling of get_yield_curve. Source: Treasury.gov MSPD (public domain); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "security_type": {
      "type": "string",
      "description": "e.g. 'Total Public Debt Outstanding' (with security_class for a series)."
    },
    "security_class": {
      "type": "string",
      "description": "Class within the type; '_' for Total rows."
    },
    "limit": {
      "type": "integer",
      "description": "Monthly points for a series (default 120, hard cap 360).",
      "minimum": 1,
      "maximum": 360
    }
  },
  "required": []
}

News & events

5 toolsSOURCE: SEC EDGAR 8-K INDEX · ISSUER IR CALENDARS · NEWS ARCHIVE: MULTI-PUBLISHER VENDOR FEEDS · BORROWER NEWS: GOOGLE NEWS

get_corporate_events

ANONYMOUS OKPIP ✓

The 20 most recent 8-K item events for a ticker, newest first, categorised by item

Full contract & input schema

The 20 most recent 8-K item events for a ticker from Oxford Ledge's 8-K index of SEC EDGAR, NEWEST FIRST -- a hard cap with no caller-settable limit, so a recent-events feed, not a history. Returns {ticker, events, count, coverage, summary}; each event is {ticker, eventDate, eventType, headline, description, counterparty, counterpartyTicker, sourceUrl, source}. `eventType` is Oxford Ledge's 8-K item-to-category map. The event_type filter matches STORED CATEGORIES ONLY; for M&A use acquisition_disposition. Dual-class siblings (GOOG/GOOGL) are unioned. An unreachable store REFUSES (DATA_UNAVAILABLE) rather than serving events=[]. For Form 4 insider transactions use get_insider_activity. Source: SEC EDGAR 8-K index (Oxford Ledge parse), re-indexed about every 7 days. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Stock ticker symbol (e.g. AAPL)"
    },
    "event_type": {
      "type": "string",
      "description": "Optional filter, case-insensitive; one category, a comma-separated list, or ALL (the default). The STORED vocabulary is the 17 8-K item categories: material_agreement, material_agreement_termination, bankruptcy, acquisition_disposition, earnings, new_debt_obligation, debt_obligation_trigger, material_impairment, delisting, auditor_change, financial_restatement, change_of_control, executive_change, bylaw_amendment, shareholder_vote, reg_fd_disclosure, other_event. Anything else (acquisition, divestiture, restructuring, dividend, split, merger) matches no stored row and returns events=[]; for M&A use acquisition_disposition."
    }
  },
  "required": [
    "ticker"
  ]
}

get_news

KEY REQUIRED

Oxford Ledge news archive by ticker and/or keyword, newest first; two response shapes, up to 100 rows

Full contract & input schema

Read the Oxford Ledge news archive by ticker and/or keyword, newest first. Default limit 25, hard cap 100; no pagination offset. THE RESPONSE SHAPE DEPENDS ON THE ARGUMENTS: ticker only returns {articles, total}; any free-text query returns {articles, count} with richer rows (publisher, snippet, sentimentScore, tickers, tags). Handle both. `sentiment` is a LABEL string, NOT a number. Query matching is prefix-word, not semantic. Source: Oxford Ledge news archive (multi-provider, cron-populated). Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Search query (e.g. 'tariff', 'earnings beat')"
    },
    "ticker": {
      "type": "string",
      "description": "Filter to a specific ticker (e.g. AAPL)"
    },
    "limit": {
      "type": "integer",
      "description": "Max results to return (default 25, max 100)",
      "minimum": 1,
      "maximum": 100
    }
  }
}

ol_bdc_borrower_news_today

KEY REQUIRED

Recent verified headlines for borrowers held across multiple BDCs; headline and link only

Full contract & input schema

MOAT / daily pulse: recent VERIFIED news for the private-credit borrowers held across MULTIPLE BDCs -- 'what broke recently for the cross-BDC borrowers I should watch?'. Returns {summary, count, items}; each item is {borrower, headline, source, url, date}. Matcher-VERIFIED only (>=0.9 confidence) and headline + link only, NO provider summary, so a false attribution cannot surface. Caps: limit default 25 / hard 100; since_days default 7 / hard 90; min_holders default 2 (max 50). No recent headlines returns items=[] -- absence is not a signal; an unreachable store is REFUSED (DATA_UNAVAILABLE). Source: Google News, matched to SEC EDGAR BDC borrowers; FREE.

{
  "type": "object",
  "properties": {
    "since_days": {
      "type": "integer",
      "description": "How many days back to look (default 7, hard cap 90).",
      "minimum": 1,
      "maximum": 90
    },
    "min_holders": {
      "type": "integer",
      "description": "Minimum number of BDC lenders a borrower must appear in (default 2).",
      "minimum": 1,
      "maximum": 50
    },
    "limit": {
      "type": "integer",
      "description": "Max news items to return (default 25, hard cap 100).",
      "minimum": 1,
      "maximum": 100
    }
  }
}

ol_earnings_calendar

ANONYMOUS OK

Upcoming earnings dates for one issuer: IR-published dates beside a filing-cadence estimate

Full contract & input schema

Upcoming EARNINGS DATES for one issuer, provenance-labelled: published_events are dates the ISSUER posted on its own IR calendar; estimate is an Oxford Ledge projection from the issuer's SEC filing cadence, with its confidence. Returns {ticker, published_events, published_events_degraded, estimate, provenance_rule, note}. The two are NOT merged: prefer a published date yourself, and never present the estimate as a company announcement. SCOPE: US domestic 8-K filers only. Read published_events_degraded before reading the note as an issuer fact. Distinct from get_economic_calendar (macro releases). Source: issuer IR pages + SEC filing cadence (Oxford Ledge); FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "ticker": {
      "type": "string",
      "description": "Issuer symbol."
    }
  },
  "required": [
    "ticker"
  ]
}

search_news_archive

KEY REQUIRED

TF-IDF full-text search over archived headlines and the pulse-fact corpus; lexical, not semantic

Full contract & input schema

TF-IDF full-text search over the archived news headlines + the pulse-fact corpus (a lexical index, not embeddings). Returns {query, count, results, index}; each result is {type ('news' | 'fact'), text, score (0-1 cosine), metadata}. `limit` default 20, hard cap 50. The first call after a restart pays the index build (seconds); later calls are fast. RESTRICTED posture: a keyed tool (refuses anonymous callers). For per-ticker archive reads use get_news; for semantic retrieval over SEC filings use ol_filing_search. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Search query (e.g. 'oil prices', 'Fed rate cut')"
    },
    "limit": {
      "type": "integer",
      "description": "Max results to return (default 20)",
      "minimum": 1,
      "maximum": 50
    }
  },
  "required": [
    "query"
  ]
}

Portfolio

4 toolsSOURCE: YOUR ACCOUNT DATA (OXFORD LEDGE POSTGRES) · KEY REQUIRED · NO EXTERNAL SOURCE

get_portfolio_positions

KEY REQUIRED

Stored positions of one saved portfolio: ticker, shares, cost basis; no price leg

Full contract & input schema

Stored positions of one saved portfolio. Returns {portfolio_id, positions, error} on EVERY path (error is null on success, a string on failure, and positions is [] either way). Each position is ONLY {ticker, shares, costBasis (whole USD, may be null)}, ticker-sorted. THERE IS NO PRICE LEG: no price, market value, gain/loss or weight -- value the positions yourself. An unknown portfolio_id yields positions=[] with error=null, so an empty list does not prove the portfolio exists. For the catalog-wide sector mix use get_sector_breakdown. Source: PG portfolio_positions.

{
  "type": "object",
  "properties": {
    "portfolio_id": {
      "type": "string",
      "description": "Portfolio identifier (default: 'default')"
    }
  }
}

ol_paper_portfolio

KEY REQUIRED

Your paper (practice) portfolio: cash, positions marked to a delayed quote, P&L, optional recent fills

Full contract & input schema

YOUR paper (practice) portfolio on Oxford Ledge -- the same snapshot the web sandbox at /practice/ renders: {portfolio, positions, totals, position_theses, summary, has_portfolio}. include_history: true adds recent_trades (history_limit 1..50, default 20). A user with NO portfolio gets {has_portfolio: false, how_to_start} -- an honest empty, not an error. Positions are marked to the platform's DELAYED quote at read time. Requires an authenticated Oxford Ledge caller (API key or linked OAuth token) and serves the caller's own portfolio only; the local stdio server gets AUTH_REQUIRED. Never cached. Practice money: _meta.disclaimer says so on every response. Source: Oxford Ledge paper sandbox; FREE. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "include_history": {
      "type": "boolean",
      "default": false,
      "description": "Add recent_trades (the last history_limit fills)."
    },
    "history_limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 50,
      "default": 20,
      "description": "How many recent fills to include when include_history is true (1..50)."
    }
  },
  "required": []
}

ol_paper_trade

WRITE · OFF BY DEFAULT

Place a buy or sell in your paper portfolio; dry run by default, key-only, 20 fills a day; a write tool

Full contract & input schema

WRITE (opt-in): place a market order in YOUR paper (practice) portfolio -- the same engine, quote, fills and rejections as the web sandbox; no real money, no real account. One of TWO mutating tools here (the other is reading_list_annotate). DEFAULTS TO A DRY RUN: with _dry_run omitted or true nothing is placed and you get {dry_run: true, tool, args, idempotency_key, preview, message}. Re-call with _dry_run: false AND that same _idempotency_key to execute; a rejection is a normal (non-error) response you must read and relay, and a repeat of the same key returns {replay: true, ...} without placing twice. shares is whole shares, 1..1,000,000; side is buy or sell; thesis (optional, max 250 chars) is the USER's own reason. Requires an API KEY or a linked OAuth token (a browser session cookie is refused). Capped at 20 executed fills per user per UTC day. Practice money: _meta.disclaimer says so on every response. Caveats ride the response's tool_notes.

{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "ticker",
    "side",
    "shares"
  ],
  "properties": {
    "ticker": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20,
      "description": "Symbol to trade in YOUR paper portfolio (e.g. DAC)."
    },
    "side": {
      "type": "string",
      "enum": [
        "buy",
        "sell"
      ],
      "description": "buy or sell (market order at the platform's delayed quote)."
    },
    "shares": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000000,
      "description": "Whole shares, 1..1,000,000 per order (the engine's per-order cap)."
    },
    "thesis": {
      "type": "string",
      "maxLength": 250,
      "description": "Optional: why (max 250 chars). Stored on the fill and shown back to the user beside the position; write it as the USER's reason."
    },
    "_dry_run": {
      "type": "boolean",
      "default": true,
      "description": "Default true: preview the fill (quote, cost, cash after) without placing it. Re-call with false and the returned _idempotency_key to execute."
    },
    "_idempotency_key": {
      "type": "string",
      "maxLength": 128,
      "description": "Optional replay-safety key; auto-derived if omitted. A repeat of the same key returns the first result and places nothing."
    }
  }
}

reading_list_annotate

WRITE · OFF BY DEFAULT

Save or update your private note on a reading-list entry; dry run by default; a write tool

Full contract & input schema

WRITE (opt-in): save or update YOUR private note on an Oxford Ledge reading-list entry. One of TWO mutating tools here (the other is ol_paper_trade). DEFAULTS TO A DRY RUN: with _dry_run omitted or true you get a proposal {dry_run: true, tool, args, idempotency_key, message}, not a save. Re-call with _dry_run: false AND that same _idempotency_key to execute; a repeat of the same key returns {replay: true, ...} without writing twice. Requires Plus tier AND an authenticated Oxford Ledge caller -- the local stdio server has no account context and raises AUTH_REQUIRED. `slug` must already exist on /reading-list; `body` is capped at 500 chars. Notes stay PRIVATE; this tool can never publish one. Caveats ride the response's tool_notes.

{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "slug": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "Reading-list entry slug (as on /reading-list)."
    },
    "body": {
      "type": "string",
      "minLength": 1,
      "maxLength": 500,
      "description": "The note text (max 500 chars). Notes are PRIVATE; publishing requires the dashboard flow (age attestation)."
    },
    "_dry_run": {
      "type": "boolean",
      "default": true,
      "description": "Default true: propose without persisting. Re-call with false to execute."
    },
    "_idempotency_key": {
      "type": "string",
      "maxLength": 128,
      "description": "Optional replay-safety key; auto-derived if omitted."
    }
  },
  "required": [
    "slug",
    "body"
  ]
}

Learn & reference

3 toolsSOURCE: OXFORD LEDGE EDITORIAL (PROFESSOR-REVIEWED GLOSSARY · CURATED QUOTATION CORPUS) · CATALOG SECTOR MIX FROM COMPANY PROFILES

get_sector_breakdown

ANONYMOUS OK

How the covered universe splits by sector and industry; ticker counts only, one row per pair

Full contract & input schema

How Oxford Ledge's covered universe splits across sectors and industries (the whole catalog, NOT a portfolio). No arguments. Returns a BARE LIST of {sector, industry, tickerCount, totalMarketCap}, one row per sector+industry PAIR -- aggregate rows to get a sector total. totalMarketCap IS ALWAYS NULL: a count-only breakdown that cannot answer 'how much of the market is tech'. Rows with a blank sector are excluded. Returns {error} (a dict, not a list) when Postgres is unavailable. For a named portfolio use get_portfolio_positions. Source: PG company_profiles, ~5,200 sectored tickers.

{
  "type": "object",
  "properties": {}
}

get_value_investing_fact

ANONYMOUS OKPIP ✓

A curated value-investing principle, paraphrase or fact from a ~2,000-entry corpus; by query, category or random

Full contract & input schema

Curated value-investing principle, historical fact or attributed paraphrase from Oxford Ledge's corpus (~2,000 entries about Buffett, Graham, Munger, Klarman and others). THE WORDING IS NOT VERIFIED against the primary source: none is a verbatim quotation unless `verbatim` is true -- so never present the text as the author's exact words, and credit it with the `attribution` line. THE SHAPE DEPENDS ON THE ARGUMENTS: a `query` returns {facts, count, total_facts} (facts capped at 10); a bare `category` or no arguments returns {fact, total_facts} (one random pick). Pedagogical content only -- never a market signal. Cached 24h per argument set. Caveats ride the response's tool_notes.

{
  "type": "object",
  "properties": {
    "category": {
      "type": "string",
      "description": "Optional category filter. The vocabulary is EXACTLY: principle, historical_fact, psychology, quote, case_study, contrarian, mistake. Matched case-insensitively; leave empty for random. An unknown value returns {error, available_categories, total_facts} where available_categories is read from the store, so one retry always lands."
    },
    "query": {
      "type": "string",
      "description": "Optional search query to find facts by keyword (e.g. 'moat', 'fear')"
    }
  }
}

ol_glossary_term

ANONYMOUS OK

Look up one of ~1,500 editorial glossary terms: definition, worked example, etymology, related terms

Full contract & input schema

Oxford Ledge's editorial GLOSSARY: ~1,500 finance and investing terms written for lifelong students of the market -- definition, worked example, etymology, pronunciation, related terms (see-also), and the learning paths that teach the concept. Look up one term by name (case and punctuation tolerant: 'first lien', 'convexity', 'duration'). Use it to ground explanations in the SAME definitions the platform teaches from, instead of improvising one. Source: Oxford Ledge editorial (PROFESSOR-reviewed); FREE.

{
  "type": "object",
  "properties": {
    "term": {
      "type": "string",
      "description": "The term to look up, e.g. 'duration'."
    }
  },
  "required": [
    "term"
  ]
}

Full descriptions are preserved verbatim inside each card's fold — they are the contract; the summary is the index.

Notes

  • Cached server-side for 5 minutes; refresh after a new tool registers.
  • Source of truth: mcp_package/oxford_ledge_mcp/tools_manifest.py (generated) — this page renders from it; do not import server.py to enumerate tools.
  • JSON Schemas mirror what the MCP list_tools response sends to clients — copy-paste them into your agent config.