Signals. Infra
MCP server

Tool contract

Exact names, inputs, outputs, and citation rules for every Signals MCP tool.

Signals exposes 18 model-facing, read-only tools. Every descriptor includes a JSON Schema input contract, an output contract, and MCP annotations marking the operation read-only, non-destructive, idempotent, and closed-world.

Naming rules

Tool names describe cardinality and intent:

  • search_* finds candidates or evidence from a query.
  • list_* returns a bounded collection and usually supplies IDs for a fetch.
  • get_* returns one resource, one series, one bundle, or one coverage record.

This is why KPI access is a pair: list_company_kpis discovers valid metric_key values, while get_kpi_series fetches exactly one cited series. Names are exact: the server rejects any name not returned by tools/list. There are no hidden aliases.

Company and coverage

ToolRequired inputsResultTypical next call
search_companiesqueryMatching company identity recordsget_company or get_coverage
get_companysymbolOne company identity and profileget_coverage
get_coveragesymbolexists plus record counts by datasetA dataset tool with non-zero coverage

In get_coverage, a dataset count of null means the count could not be taken — not that the count is zero. The reason appears under unavailable, keyed by dataset. Treat null as unknown coverage and say so; treat 0 as genuinely no rows.

Use exchange-qualified symbols such as AAPL:US when the ticker could be ambiguous. A bare US ticker such as AAPL resolves to AAPL:US.

Financials and KPIs

ToolRequired inputsOptional inputsResult
get_financial_statementssymbolstatement, period, limitReported financials as a period series, each cited to its SEC filing
list_company_kpissymbollimit (1–50)KPI metadata in the company's judged order: metric_key, display name, unit, definition, an 8-point uncited preview, and — once the company is judged — tier, family, polarity, stale_quarters, plus a top-level headline list
get_kpi_seriessymbol, metric_keylimit (1–50)One KPI definition (with the same judgment fields) plus cited observations; a point may carry approximate: true

get_financial_statements is the tool for reported financials. Pass statement as one of:

statementCovers
income (default)Revenue, gross profit, operating expenses, EBITDA, operating income, tax, net income, EPS basic and diluted, share counts
balance_sheetCash, receivables, inventory, PP&E, goodwill, total assets, payables, short- and long-term debt, total liabilities, equity, net debt
cash_flowOperating, investing and financing cash flow, capex, buybacks, dividends, free cash flow
key_metricsMarket cap, enterprise value, P/E, P/S, P/B, EV/EBITDA, FCF yield, debt/equity, current ratio, margins, ROIC, ROE, ROA, working-capital days

period is annual (default) or quarter; limit is the number of periods, newest first. A line item that was not reported is null, never 0.

The two layers are intentionally different, and picking the wrong one is the most common mistake:

  • get_financial_statements — what the company reported on its face financials. This is where revenue, EPS, margins and ratios live.
  • list_company_kpis / get_kpi_series — issuer-specific operating measures that appear in no standard statement: gross bookings, NRR, deliveries, data-center revenue, segment revenue.

What the judgment fields mean

Every company's KPI list is laid out once by a model reading the company's own metric labels and definitions, and the verdict is cached. When that judgment exists the tools carry it; when it does not, the fields are simply absent — never a made-up tier.

fieldonmeaningwhat to do with it
headlinethe listingthe metric_keys the company is judged on, in orderthe answer to "what matters for this company"
tiereach metric1 = a headline metric the company leads its releases with; 2 = supporting; 3 = a minor line or a part of a breakdown; 4 = treasury / balance-sheet footnotelead with tier 1
familya metric that is one part of a breakdown{id, label, part} — e.g. Revenue by segment / Mobilitynever sum parts; never present a part as the total
polarityeach metricinverse when a rise is bad (costs, churn, losses)do not describe a rising cost as growth
stale_quarterseach metrichow many quarters the series trails the company's newest quartertreat > 1 as not current — usually our extraction gap, not the company's
approximatea pointthe issuer stated the figure as a rounding in prose ("surpassed 25 million")do not compute a growth rate from two of them

Rows already arrive in judged order with duplicate labels hidden; the fields say why. Metrics that pair a family share one unit and one measure by construction.

Real KPI response

Request:

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "get_kpi_series",
    "arguments": {
      "symbol": "UBER",
      "metric_key": "total_company_gross_bookings",
      "limit": 4
    }
  }
}

Structured result, shortened to one observation:

{
  "symbol": "UBER:US",
  "metric": {
    "metric_key": "total_company_gross_bookings",
    "display": "Total Company Gross Bookings",
    "unit": "USD"
  },
  "points": [
    {
      "period": {
        "calendar": "FY2026-Q1",
        "fiscal": null,
        "end_date": "2026-03-31",
        "kind": "quarter"
      },
      "value": {
        "raw": 53720000000,
        "normalized": 53720000000,
        "unit": "USD"
      },
      "provenance": {
        "src_id": "src_kpi2_be16edbfefe50d35",
        "source_url": "https://www.sec.gov/Archives/edgar/data/1543151/000154315126000019/0001543151-26-000019-index.htm",
        "quote": "Gross Bookings grew 25% YoY to $53.7 billion, and 21% on a constant currency basis."
      }
    }
  ]
}

The value, quote, and SEC URL above are from live corpus data verified on July 22, 2026. Treat metric_key as an opaque identifier returned by discovery; do not guess one from a display label.

Ownership and positioning

ToolRequired inputsOptional inputsResult
list_insider_transactionssymbollimitForm 4 transactions with SEC citations
list_congress_tradessymbollimitSTOCK Act trades with disclosure citations
list_institutional_holderssymbollimitLatest-quarter 13F holders and position values
get_short_interest_seriessymbollimitFINRA short position, volume, days-to-cover, and change history

These tools return public, impersonal disclosure data. They do not know a user's holdings and should not be used to imply personalized advice.

Investor-relations documents

ToolRequired inputsOptional inputsResult
list_ir_documentssymboldoc_type, fiscal_year, fiscal_period, limitEarnings decks, investor presentations, press releases, annual reports and supplements, each with the issuer's own document URL
get_ir_documentdocument_idinclude_text, include_outline, summaryOne document; page outline, and extracted text when include_text is true
get_ir_document_filedocument_idA 15-minute direct-download URL for the raw bytes

Pass the id field of a list_ir_documents row as document_id — the row names it id.

Every document carries a provenance object. Cite provenance.citation_url, a permanent Signals page that always resolves; provenance.source_url is the address on the issuer's own IR site, for a reviewer verifying against the original. Both are present whether or not we hold the document's text.

Extraction coverage is uneven and you must check it, not assume it. The list returns has_text per document and the fetch returns text_available; when text is absent, text is null (never "") and text_unavailable_reason says why. Roughly 83% of press releases carry extracted text versus about 15% of slide decks, so for decks the normal outcome is metadata plus the issuer's PDF link. Treat a missing text as "we have not extracted this document", never as "this document is empty".

Reading a document yourself

When the extracted text is not enough — a deck whose text_available is false, or a PDF you want to parse or view directly — get_ir_document_file returns a short-lived URL to the bytes as the issuer published them.

  • The URL expires in 15 minutes and carries no credentials. Anyone holding it can fetch that object until it expires, so fetch it promptly and do not store or forward it.
  • Decide before you fetch. The list rows and the response both carry mime_type and file_size_bytes. Sizes are long-tailed — half the corpus is under ~160 KB, but the 95th percentile is 3.7 MB and the largest document is 152 MB. Ranged GETs are supported, so you can read a header without pulling the whole file.
  • You can verify what you got. The SHA-256 of the fetched bytes equals the content_hash in the response.
  • Not for SEC filings. A filing's provenance.source_url is already a public sec.gov address you can fetch directly; filings have no stored blob.

Transcripts and filings

Discovery toolFetch toolJoin keyFetch result
list_transcriptsget_transcripttranscript_idFull, prepared-only, or Q&A transcript segments
list_filingsget_filingaccessionFiling metadata plus mirrored Ex-99.1/99.2 text when present

Fetch only after discovery. IDs are corpus identifiers, not values a model should invent.

Every tool is available on every plan. Plans differ only in how much you may call them — there is no dataset a paid plan unlocks. If a request is refused it is because you have reached a rate or quota limit, and the error says which; it is never because the data is withheld from your plan.

To assemble one quarter — the call, the deck and the press release together — filter list_transcripts and list_ir_documents by the same fiscal_year and fiscal_period. That is the join key, and it is the reliable one: the fiscal stamp is populated on 92% of transcripts.

The event-bundle tools were removed on 2026-08-26. Their cross-linked bundle carried both a document and a transcript on only 17% of earnings events, so the fiscal-stamp pairing above supersedes them. An event_id still rides out on IR documents and filings for anyone joining on it directly.

Company discovery is search_companies. Cross-corpus text search (search_corpus) is not currently exposed; reach evidence through the company-scoped list and fetch tools instead: resolve a symbol, then list_filings / list_ir_documents / list_transcripts and fetch by ID.

Citation contract

Financial observations and evidence rows are provenance-gated. A citation normally contains:

FieldMeaning
src_idStable Signals evidence identifier
citation_urlCite this. A permanent Signals page that always resolves
source_urlWhere the issuer or regulator published it, for verification
quoteVerbatim supporting text when the datum was extracted from prose
accessionSEC accession when applicable
content_hashsha256: digest of the source bytes we read

source_provider is not returned. It is an internal routing marker; the public citation is source_url / citation_url / accession, from which first-party versus regulatory origin is self-evident.

Which URL to cite

The two URLs answer different questions, and both are present on IR documents:

Datasetsource_urlcitation_url
IR documentsthe issuer's own copy on their sitehttps://signals.ai/d/{id}
Transcriptsthe issuer's webcast address where we can disclose it, otherwise nullhttps://signals.ai/t/{hash}
Filingsthe SEC EDGAR URLnot set — EDGAR is already permanent

Cite citation_url. It is permanent and it always resolves. source_url is the source of record and is the right link for a reviewer verifying against the company's own server, but roughly a third of issuer hosts refuse non-browser clients, so an agent that fetches it directly will sometimes fail. A null source_url on a transcript means we hold no disclosable issuer address for that call. It never means the quote cannot be cited.

Preserve citation_url and quote when presenting a claim. An empty data or points array means no matching cited row was returned. It does not prove that the real-world quantity is zero; check get_coverage and state the gap.

See protocol and errors for response envelopes and retry behavior.

On this page