{"protocolVersion":"0.3.0","name":"Oxford Ledge","description":"Financial research over U.S. public-filing data: a business-development-company private-credit corpus with loan-level borrower detail and cross-lender pricing, plus SEC EDGAR company fundamentals, Form 4 insider transactions, 13F institutional ownership, and intrinsic-value models computed from filed XBRL. Educational information, not investment advice.","version":"1.0.0","url":"https://www.oxfordledge.com/api/a2a/v1","preferredTransport":"JSONRPC","supportedInterfaces":[{"url":"https://www.oxfordledge.com/api/a2a/v1","protocolBinding":"JSONRPC","protocolVersion":"0.3.0"}],"provider":{"organization":"Oxford Ledge","url":"https://www.oxfordledge.com"},"documentationUrl":"https://www.oxfordledge.com/auth.md","capabilities":{"streaming":false,"pushNotifications":false,"stateTransitionHistory":false},"supportsAuthenticatedExtendedCard":false,"defaultInputModes":["application/json"],"defaultOutputModes":["application/json","text/plain"],"securitySchemes":{"oxfordLedgeApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Oxford Ledge API key, required for every skill. Obtain one per https://www.oxfordledge.com/auth.md; a 14-day trial key is available without a payment method."}},"security":[{"oxfordLedgeApiKey":[]}],"skills":[{"id":"company-fundamentals","name":"Company fundamentals (SEC XBRL)","description":"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 (up to 30 fiscal years; 17-20 served for AAPL/JNJ/DAC), and EVERY entry under `metrics`/`balanceSheet` is a PARALLEL ARRAY aligned to it by index, not a per-year object: revenue, grossProfit, ebit, netIncome, opCF, capex, da, epsDiluted, sharesOut, totalAssets, totalLiabilities, stockholdersEquity, currentAssets, currentLiabilities, totalDebt, cashAndShortTerm, goodwill, intangibleAssets, plus derived fcf (opCF minus capex), grossMarginPct, ebitMarginPct, currentRatio, quickRatio, debtToAssets (a PERCENT: 25.2 means 25.2%). `periods` is the ISO period-end list index-aligned with `years`, and each label is the filer's FISCAL year; a 52/53-week filer whose year ends Jan 1-3 keeps every year (JNJ FY2022, ended 2023-01-01, is served as 2022). Two periods never share a label: a transition period or an instant-only balance-sheet date that would collide is labelled by its full period end (ServiceNow serves '2011' beside '2011-12-31'). `quarterly` is the last 12 quarterly periods (10-Q facts only, newest first: period, endDate, revenue, grossProfit, ebit, netIncome, epsDiluted, margins) or null; fourth quarters are not tagged in 10-Qs and are absent, so 12 rows span about four years; a quarter ending in the first days of a month is labelled by the month it closes (JNJ's 2023-04-02 is Q1 2023). `basis` is the split-basis gate ({basisConsistent, basisChecked, withheldThrough, affectedMetrics, withheldValues, note, and splitRatio* when the filer's own tagged split withheld pre-split share counts}): per-share cells on a pre-split basis are WITHHELD (null) rather than rescaled and listed in withheldValues. basisConsistent is true (examined, one basis), false (a corroborated break; cells withheld) or null (unexamined, OR an implied-share jump with no issuer refiling -- listed in basisAdvisory, nothing withheld, nothing certified; basisNote says which). `as_of` is the newest fiscal period end and `source_period` its label (FY2025). UNITS: dollar figures are whole USD, epsDiluted is USD/share, every *Pct is a percentage number (12.5 means 12.5%). A year the filer did not tag is null, never 0. Returns {error, taxonomy, formsSeen, unitsSeen} when the ticker has no companyfacts or carries no annual us-gaap fact -- the sentence names what the filer DOES carry (an IFRS reporter; a Canadian MJDS filer whose us-gaap facts ride 6-K in CAD, like CNI). For cash deployment use get_capital_allocation; for per-share fair value use ol_intrinsic_value. Source: SEC EDGAR company-facts XBRL (~45-day post-quarter lag), 24h cache. Latency: 500ms-2s cold, <300ms cached. SEE ALSO ol_operating_kpis for the INDUSTRY operating metrics (same-store sales, RevPAR, load factor, NIM, ...) the income statement hides -- 26 configured industries, each live-gated by a deployment flag.","tags":["sec","edgar","xbrl","fundamentals","equities"],"examples":["Fundamentals for AAPL","Ten years of revenue and EPS for KO"],"inputModes":["application/json"],"outputModes":["application/json"]},{"id":"institutional-ownership-13f","name":"Institutional ownership (SEC 13F-HR)","description":"What one institutional filer owns: the largest positions in its latest SEC 13F-HR, plus quarter-over-quarter changes. Accepts a numeric CIK (preferred) or a ticker resolved via SEC's company map -- letters with at most one class suffix (BLK; BRK-B or BRK.B for Berkshire, which SEC lists as BRK-A / BRK-B, never bare BRK); any other shape is rejected as INVALID_PARAMS. Returns {cik, fundName, filingDate, periodOfReport, totalHoldings, totalValue, holdings}; each holding is {name (issuer name as filed), title_of_class, value, shares, type, position_type, lots} plus `cusip` ONLY for callers whose plan carries a CUSIP Global Services licence (none does today, so every channel -- anonymous or keyed -- receives rows with the CUSIP stripped; COUNSEL 2026-09-13) and there is NO ticker field on any channel, so a row carries no symbol: `title_of_class` is the share-class discriminator (Berkshire's two Alphabet rows are 'CAP STK CL A' and 'CAP STK CL C' under one `name`), and `lots` is Oxford Ledge's count of infotable rows merged into the position. `value` is whole USD (13F values are dollars despite the form's 'x$1000' wording); `position_type` is COM|PRN|PUT|CALL and options/notes are kept separate, so do NOT sum across types. `totalValue` and `totalHoldings` cover the WHOLE filing while `holdings` is truncated to max_holdings (default 50, cap 500), value-ranked. When a prior filing exists the response also carries prevFilingDate and changes:{new_positions, increased, decreased, closed} (rows with prevShares / sharesChange / pctChange), `changesTotals` (the full per-bucket counts) and `changesTruncated` when a bucket was cut; the `changes` key is ABSENT when only one filing was found. Inverse tools: get_institutional_holders (who holds X) and get_institutional_consensus (what is widely held); ol_13f_filer_analytics for this filer's concentration/turnover. Source: SEC EDGAR 13F-HR, ~45-day quarterly delay. Heavy operation, max 2 concurrent.","tags":["sec","13f","institutional","ownership","funds"],"examples":["Latest 13F positions for CIK 0001067983","Top holdings of a named institutional filer"],"inputModes":["application/json"],"outputModes":["application/json"]},{"id":"bdc-directory","name":"Business-development-company directory","description":"Roster of the ACTIVE BDCs tracked by Oxford Ledge, sorted by portfolio size -- this is our coverage, not the whole BDC universe; wound-down / de-BDC'd issuers are excluded by design, so it is not a historical universe. 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, plus the reconciliation triple reportedTotalFairValue (the filing's OWN stated grand total), fairValueBasis ('parsed-rows' or 'filing-reported') and parsedRowSumFairValue, and the refusal pair fairValueRefused / fairValueRefusalReason (true when the row breakdown is refused and only the filing's own total is served). READ fairValueBasis BEFORE using totalFairValue: when a parse over-counts, totalFairValue is swapped to the filing-reported figure and the raw row sum is preserved in parsedRowSumFairValue, so the two disagreeing numbers are both visible rather than silently reconciled. When the store does not answer, holdingCount is null (never a measured 0) and a top-level `notice` says the counts were not measured. holdingCount, totalFairValue and the arbitration are Oxford Ledge's parse, not filer-published figures; ticker / name / listed are the registry. Borrower-keyed counterpart: search_bdc_borrower. Source: SEC EDGAR BDC filings (Oxford Ledge parse). Latency: <300ms.","tags":["sec","bdc","private-credit","directory"],"examples":["Which BDCs does Oxford Ledge track, with portfolio fair value","BDC tickers and their latest filing dates"],"inputModes":["application/json"],"outputModes":["application/json"]},{"id":"bdc-borrower-search","name":"BDC borrower search (private-credit loan detail)","description":"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: canonical-name pick, borrower normalisation, group merge, current-holder aggregates), not filer-published data. Envelope: borrowerName, borrowerNorm (the canonical key the ol_bdc_* tools take), description (a compiled company profile from public sources, NOT filing text; `descriptionSource` names where it came from), industry, totalHolders, totalParAmount, totalFairValue, avgMarkedPrice/min/max, match_type, units, and `holders` -- one row per TRANCHE (bdcTicker, bdcName, filingDate, securityType, lienPosition, interestRate as filed, maturityDate, parAmount, fairValue, markedPrice, stale, staleBasis, holderStatus, successorTicker). `units` says it: USD; markedPrice is percent of par. ABOVE-PAR TRAP: markedPrice is fair value over FILED principal and filers differ on what principal tracks (capitalised PIK/OID may be excluded), so a mark above 100 is not a credit premium until fairValue is checked against cost. A borrower whose only rows are unfunded commitments may carry unfundedCommitments / unfundedRowCount / unfundedFairValueSum (deployment-flag dependent). `priceHistory` holds ONE quarter per BDC -- not a time series; use ol_bdc_loan_pricing_trend. AGGREGATES count CURRENT holders only (aggregatesBasis 'current_holders_only'): a row filed before that BDC's latest filing (staleBasis 'exited_position') or by a wound-down filer (staleBasis 'inactive_filer') stays in holders with stale=true but is excluded; staleRowCount/staleHolderCount count them. All rows stale -> totalParAmount/totalFairValue null (not 0) with aggregatesRefusalReason; marks stay in holders[].fairValue. RELATED KEYS: a hit is ONE borrower_norm key, not necessarily the whole obligor; relatedNorms lists other keys sharing its prefix with current holders ({borrowerNorm, borrowerName, holderCount, holdingRowCount, totalFv}; relatedNormsBasis 'prefix_of_resolved_key'; [] when none). relatedNormsStale lists prefix siblings with ZERO current holders (every row exited or filed by an inactive filer): holderCount 0, totalFv null (not measured on the current basis), holdingRowCount as stored; [] when none. Discovery is on key existence -- a fully-exited obligor still shows the other keys it is filed under. In the ambiguous `matches[]` list every candidate carries totalFvBasis 'current_holders_only', and a candidate with stored rows but no current holder carries totalFv null with totalFvRefusalReason -- never 0. A brand and its 'X Acquisition, LLC' vehicle can be separate keys: check it before reading totalHolders as the lender count, and query ol_bdc_borrower_dispersion per key. A MISS returns {found: false, match_type: null, message} -- a search miss, not a finding of no BDC exposure; ambiguous: {found: false, ambiguous: true, matches: [...], holders: []} -- re-call with a specific name (matches[].holderCount/totalFv are latest-filing-only). A hit carries match_type and no `found` key. Debt and equity included. PAGING (additive): `limit` / `offset` page the `holders` rows only -- every aggregate, holdingRowCount, priceHistory and relatedNorms stay computed over ALL rows; a call declaring either gets `page` {limit, offset, returned, total, hasMore} and a `completeness` block with total_available; an offset past the end is an empty page with total intact; out-of-range values are REFUSED, never clamped; a call declaring neither is unchanged (every row, up to the store's 5000-row backstop). Source: SEC EDGAR BDC schedules of investments (Oxford Ledge parse, ~45-60 day lag).","tags":["sec","bdc","private-credit","borrower","loans"],"examples":["Which BDCs lend to a named private company","Lien position and fair value of a borrower's loans"],"inputModes":["application/json"],"outputModes":["application/json"]},{"id":"intrinsic-value","name":"Intrinsic-value models from filed XBRL","description":"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, rounded to 2dp. `inputs` echoes fcf_latest, shares_out, eps_diluted, book_value_per_share, ebit_5y_avg, net_income_5y_avg, total_debt, cash_and_short_term, fiscal_year; `assumptions` echoes the FIXED, NOT-PER-NAME model constants (10% discount, 2.5% terminal growth, 10y horizon, 21% tax) plus the derived fcf_growth and the fcf_growth_basis string explaining how it was fitted. NO current price, market cap or margin-of-safety is returned by design -- fetch a price yourself and compare. Graham is null on negative EPS or book value, DCF null on negative FCF, and EPV null (null_reasons names the input) when total_debt or cash is null -- never computed as if debt-free; total_debt reads a six-rung ladder (LongTermDebtAndCapitalLeaseObligationsIncludingCurrentMaturities / LongTermDebtAndCapitalLeaseObligations / LongTermDebt / LongTermDebtNoncurrent / ConvertibleLongTermNotesPayable / SeniorNotes, earlier rung wins), so a converts-only filer such as NOW is served; available=false means all three failed; when it is a SEC fetch failure (unreachable, rate-limited, bad body) the payload carries reason:'fetch_error' and NO inputs block -- transient, never cached, retry -- while reason:'ticker_not_in_cik_map' means the symbol resolves to no registrant. The 5-year averages are over the years the filer tagged, not a fixed window. The DCF is FCFE-style (FCF = opCF minus capex, discounted straight to equity), so it is not comparable to an enterprise-value DCF. Source: SEC EDGAR company-facts XBRL. Typical latency: <2s on a cold EDGAR fetch.","tags":["sec","xbrl","valuation","dcf","epv"],"examples":["DCF, earnings-power and Graham values for MSFT from filed XBRL","The assumption block behind an intrinsic-value estimate"],"inputModes":["application/json"],"outputModes":["application/json"]},{"id":"insider-activity","name":"Insider transactions (SEC Form 4)","description":"Recent Form 4 insider transactions for one ticker. Returns {ticker, transactions}; THE 20 MOST RECENT TRANSACTION ROWS ONLY (one Form 4 can carry several), newest by filing date -- and the `days` argument is currently a NO-OP on this path, so it will not widen or narrow the window. Each row: ticker, filingDate, transactionDate, insiderName, position (the role, which is the load-bearing signal, not the name; an Oxford Ledge fallback label when the filing left it blank), title, transType (raw SEC transaction_code -- 'P' open-market buy, 'S' sale, 'A' grant, 'M' option exercise; there is no plain buy/sell field, so filter on this yourself), shares, pricePerShare, totalValue (USD with cents: |shares| x price computed by Oxford Ledge at ingest, null when the filed price failed the plausibility gate), sharesOwned (holding after the trade), securityTitle, isDerivative (true for an option / derivative row -- read it before treating `shares` as common stock), url (SEC filing link); keyed callers also see a row `id`. Dual-class siblings are unioned. On failure returns {error}. Market-wide open-market buying: ol_insider_recent_buys; multi-insider clusters: ol_insider_cluster_scan; >5% stakes: get_activist_stakes. Source: SEC EDGAR Form 4 (~2-day filing deadline). Requires Plus tier. Typical latency: 500ms-2s. Each row also carries formType (the Form 4 type as filed, '4' or '4/A'), accessionNumber (the filing), isAmendment (true for a 4/A; null when the store holds no form type), and supersedesAccession: a Form 4 and its 4/A that report the SAME line (identical filer, date, code, shares, price, shares-owned-after, security title and derivative flag) are served ONCE, as the amendment, with supersedesAccession naming the folded original -- so summing shares or value over the list no longer double-counts an amended filing. A 4/A that CORRECTED a value is a different line and is served beside its original; isAmendment says which is which.","tags":["sec","form4","insider","transactions"],"examples":["Recent Form 4 transactions for NVDA","Officer and director buys reported in the last quarter"],"inputModes":["application/json"],"outputModes":["application/json"],"security":[{"oxfordLedgeApiKey":["tier:plus"]}]},{"id":"bdc-top-borrowers","name":"Most widely syndicated private-credit borrowers","description":"MOAT / BDC discovery: the private-credit borrowers syndicated across the MOST BDCs, ranked by lender count then exposure -- the entrypoint for the BDC/private-credit category no generic MCP touches. Pairs with `ol_bdc_borrower_dispersion` (feed a returned `borrower_norm` into it to see cross-lender pricing). Returns {summary, count, borrowers}; each row is {borrower (display name), borrower_norm (the key other ol_bdc_* tools take), holder_count (distinct BDC lenders), total_fair_value (whole USD across lenders, latest filings; null when unpriced), industry}. Caps: limit default 25 / hard 100; min_holders default 2 (max 50). Parser mis-ingests (subtotals, maturity-date and instrument-descriptor rows, bare industry-taxonomy labels) are filtered out when the filter is available; the `borrower_filters` block says whether each half ran (nonborrower / industry_label, with industry_vocab_size) and `rows_removed` how many rows it took out. Cell-bleed names ('Acme, LLC, Diversified Financial Services') are deliberately NOT filtered. Source: SEC EDGAR BDC schedules of investments (Oxford Ledge parse); FREE.","tags":["sec","bdc","private-credit","syndication","borrower"],"examples":["Borrowers held by the largest number of BDC lenders","Lender count and aggregate fair value by borrower"],"inputModes":["application/json"],"outputModes":["application/json"]},{"id":"bdc-borrower-dispersion","name":"Cross-lender loan-pricing dispersion for one borrower","description":"MOAT: cross-lender loan-pricing DISPERSION for one private-credit borrower -- how N different BDCs each price the SAME loan (spread / mark / fair value). The credit-mispricing signal no generic MCP has: when one BDC marks a borrower S+550 @ 98 and another S+575 @ 99, the lenders disagree on the credit. Pass the borrower's canonical `borrower_norm` key (from `ol_bdc_top_borrowers` or `search_bdc_borrower`). Returns {summary, borrower_norm, count, lenders, spread_unit, base_rate, yield_disclosure, rows_above_par, as_of, completeness}, ordered widest-spread-first; each lender row is {bdc_ticker, bdc_name, filing_date, bdc_latest_filing, is_stale_vs_bdc_latest, filer_status, successor_ticker, security_type, lien_position, rate_type, maturity_date, spread, spread_bps, marked_price, mark_as_of, mark_above_par, mark_above_par_basis, fair_value, non_accrual, current_yield_pct, spread_to_maturity_bps, all_in_simple_yield_pct, yield_basis, yield_suppressed, pik_leg_excluded}. UNITS TRAP: `spread` is the RAW AS-FILED number and is MIXED-UNIT across filers -- one BDC files 5.75 (percent) for what another files as 575 (bps) -- so never average or diff `spread` blind; compare lenders on `spread_bps` (normalised basis points, the ranking key; `spread_unit` says so). marked_price is out of 100; fair_value is whole USD. STALENESS: `filing_date` is the filing the row came from and `bdc_latest_filing` that BDC's newest filing; `is_stale_vs_bdc_latest` true means the BDC has filed since without this borrower (an exited position), and `filer_status` 'inactive' with `successor_ticker` marks a wound-down lender whose frozen final filing still appears. YIELDS: current_yield_pct / spread_to_maturity_bps / all_in_simple_yield_pct are serve-time Oxford Ledge computations (SOFR read at serve time from `base_rate`, never stored); `yield_suppressed` names why a yield is null (non-accrual, pure PIK, unpriced floor) and `yield_disclosure` states the method; rows with mark_above_par carry no serve-time yields at all. ABOVE-PAR TRAP: marked_price is fair value over FILED principal, and filers differ on what principal tracks -- a row above 100 carries mark_above_par=true plus a mark_above_par_basis sentence (par may exclude capitalised PIK/OID accretion, or the filer's principal field may equal cost; FV/par and FV/cost differ) and rows_above_par counts them, so do NOT read such a row as a credit premium without checking fair_value against cost. Debt tranches only (equity excluded). Rows are each BDC's most recent filing THAT HOLDS this borrower, so vintages can differ across lenders. A single lender comes back as ONE row (count=1); an empty result means the key is not in the corpus as a funded debt position (equity-only, unfunded-only, or an unknown key). Default 25 rows, hard cap 100. Source: SEC EDGAR BDC schedules of investments (Oxford Ledge parse; ol-derived); FREE. maturity_date_precision ('day' | 'month' | 'year' | null) is the precision the filer's SOI printed; a month-precision maturity cannot anchor the day-count, so spread_to_maturity_bps is null with margin_suppressed_reason 'no_maturity' and margin_suppressed_note saying the maturity is partial, not missing (likewise when the served date is already past).","tags":["sec","bdc","private-credit","pricing","dispersion"],"examples":["How different BDCs mark the same borrower's loan","Spread and mark by lender for one private-credit borrower"],"inputModes":["application/json"],"outputModes":["application/json"]}],"_meta":{"com.oxfordledge":{"mcpServerCard":"https://www.oxfordledge.com/.well-known/mcp/server-card.json","toolCatalog":"https://www.oxfordledge.com/api/mcp/tools.json","openapi":"https://www.oxfordledge.com/.well-known/openapi.yaml","registration":"https://www.oxfordledge.com/auth.md","terms":"https://www.oxfordledge.com/terms#a2a-access","rateLimits":"Every skill requires an API key. Limits follow the account's site tier, not a key class: per caller per hour, Free 100, Researcher 500, Investor 2,000, Power User 5,000 skill calls (one bucket shared with POST /mcp); plus the /api/ ladder of 1,200 requests per minute per signed-in account. 429 carries Retry-After. A 14-day trial key is available at /auth.md.","dataSources":"SEC EDGAR (U.S. government public domain), U.S. Treasury, and Oxford Ledge first-party parses of public filings.","attribution":"Values derived by Oxford Ledge must be attributed to Oxford Ledge (oxfordledge.com) when restated to an end user.","disclaimer":"Educational information only. Not investment advice.","mcpRemoteEndpoint":"https://www.oxfordledge.com/mcp","skillCount":8,"anonymousSkillCount":0}}}