Signals. Infra
MCP server

Tool contract

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

Signals exposes 34 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.

Research workflows

Two of the thirty-four tools return no data at all. They serve Signals' research workflows — the methodology for combining the data tools into a piece of equity research.

ToolRequired inputsOptional inputsResult
list_skills—queryskills_version plus every workflow's id, name, description and recommended_for. query ranks them; nothing is filtered out
get_skillskill_id—One workflow: id, name, version, description, instructions, recommended_for

Call them only for multi-step research — an earnings review, a thesis update, a KPI study, a peer comparison, an evidence check, initiating coverage. A single datapoint ("what was Uber's Q2 revenue") should go straight to the narrowest data tool; retrieving a workflow to answer it costs two round trips and tells the model to call the tool it was already looking at.

list_skills never returns workflow bodies — fifteen of them is roughly 27KB the model will not follow. Fetch exactly the one you chose.

skill_id must be an exact id from list_skills. An unrecognised id returns a structured error rather than the nearest match, because silently serving a neighbouring workflow means the model follows a methodology it did not choose and cannot tell that it happened:

{
  "error": "unknown_skill",
  "skill_id": "earnings-deepdive",
  "valid_skill_ids": ["company-tearsheet", "earnings-flash", "..."]
}

The workflows carry a version each, and the set carries skills_version, so a change in methodology is visible without a change to this tool contract.

Company and coverage

ToolRequired inputsResultTypical next call
search_companiesquery (optional industry)Matching company identity records: every ticker of the company, per-class ISIN and FIGI (null with a reason when not held), primary template and template group, earliest and latest fiscal and calendar quarter, last_changed_at. Capital IQ ids are not served; FIGI is the cross-referenceget_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
list_seriessymbolkeyword, cursor, limitEvery served series of the company (KPIs as kpi:<metric_key>, statement lines as fin:<statement>.<field>) with label, unit, first and last period and last_changed_at
get_series_valuesitems (1–50 of ticker, key, period)version, include_unverifiedOne value per item with its src_id and citation, or a typed per-item error (not_covered, unknown_key, not_yet_disclosed, not_yet_verified, withheld, unavailable)

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 (quarterly, yearly and annually are accepted synonyms); 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.

Guidance

ToolRequired inputsOptional inputsResult
get_company_guidancesymbolhistory, include_reconciliation, include_directional, limitWhat management guided for a future period (revenue, EPS, margins, capex, tax rate, and operating metrics such as store openings, RevPAR or deliveries), read from earnings releases, calls and decks. guidance lists one row per guide; no_guidance_found lists report dates we read and found none

By default each series returns its current guide; history=true returns every issuance in order. Each guide carries:

FieldMeaning
target_periodThe period guided, e.g. FY2026-FY or FY2026-Q3
shapepoint, range, floor, ceiling or band. A band ("low single digits") has no number; never convert it to one
low, high, unitThe guided figure or range
direction, basisDirection of the guide; gaap or non_gaap
change, priorVersus the previous guide: new, raised, lowered, reaffirmed, narrowed or widened, computed by code
corroborationHow many documents stated it
citationsEvery source URL with its verbatim quote; cite one when you use a number
target_metric_keyThe series it forecasts (kpi:<key> for get_kpi_series, fin:<statement>.<line> for get_financial_statements), so guided can be compared with delivered

Forward-bridge lines (tax, D&A, share count) are hidden unless include_reconciliation=true; directional-only guides (a named metric and direction with no number) are hidden unless include_directional=true. An error not-covered means we have not read this company's guidance yet, not that it gives none. Street consensus is not served.

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.

ToolRequired inputsOptional inputsResult
get_pricessymbolstart_date, end_date, adjustedDaily open/high/low/close bars for one security (no volume), each tagged with source_provider and dataset, plus coverage segments naming any range that is not_covered or no_data and why, and corporate_actions: every split from start_date through today (type, ex_date, factor, ratio, source_url). adjusted=true divides earlier bars by later splits: split-adjusted only, not total return. Dividends are not served
search_documentskeywordssymbols, doc_types, source_kinds, start_date, end_date, limitHits that contain every keyword, each with the document, a snippet, its page or timestamp, and a link that opens it there
search_news—query, symbol, source, date_from, date_to, limit, cursorRecent business headlines: title, publisher, published_at, url, tickers. Headlines and links only, never article text; a word search with no symbol covers the last 30 days

Prices are served from 2018-05-01: databento_xnas to 2023-03-27, then databento_mini. Each bar names its dataset, and dates before 2018-05-01 are no_data. Prices are per share class and never combined across classes. A search whose index is unreachable returns error: "unavailable", which is not the same as no hits.

Economic data

Official statistics, about 576,000 series, to test a company's claims against independent data. Each series names its publisher and carries a citation. Pass a source key as source to narrow a search:

sourceWhat it holds
fred119 US national releases from BLS, BEA, Census, the Federal Reserve and other federal publishers (public-domain series only), refreshed on release day
blsProducer prices by industry and product, CPI items by region, employment, hours, pay, job openings and quits by industry, employment cost index
beaOutput and value added for about 400 industries, consumer spending by product, business investment by type, auto sales
eiaOil, gasoline, natural gas, electricity and coal: production, stocks, trade, prices and use; energy by country
treasuryFederal debt, the Treasury cash balance, interest rates and interest expense
tw_trade, cn_customsTaiwan's and China's monthly trade with every partner country
imf_portwatchDaily port calls and chokepoint transits (Suez, Panama, Hormuz, Malacca and 24 more)
rbiReserve Bank of India: rates, money, credit, inflation, external sector
nasa_firms, nasa_powerDaily fire detections by country; daily weather at crop and power-demand regions

Search ranks popular, national and current series first.

ToolRequired inputsOptional inputsResult
search_macro_seriesqueryfrequency, seasonal_adjustment, release_id, include_discontinued, source, geography, cursor, limitSeries whose titles match, each with series_id, title, units, frequency, seasonal_adjustment, publisher, release, first and last date and a citation
get_macro_seriesseries_ids (1–200)start, end, transform, summaryFull mode (up to 10 ids): every observation, newest 2,000 per series. summary: true (up to 200 ids): latest value, year-ago value and yoy, three-months-ago value and change_3m, 52-week high and low. An unknown id is a per-item error
list_macro_releases—release_id, cursor, limitNo id: every loaded release with its publisher and series count. With an id: that release's series

Comparisons are matched by date, never by position. The year-over-year base for an observation dated d is the observation dated d minus one year; daily and weekly series take the latest observation on or before that date within 4 and 6 days. No match is null, never a neighbouring value. transform is none, yoy_pct or yoy_diff; in summary mode the default is yoy_diff for rates and percents and yoy_pct for levels.

There is no SQL tool. To rank or combine many series, read them in summary mode and compute the result.

Federal Reserve communications

FOMC statements and minutes, the Chair's press-conference transcripts, and Board members' speeches and congressional testimony, from federalreserve.gov (public domain), since 2020.

ToolRequired inputsOptional inputsResult
search_central_bank_communicationsquerykind, speaker, date_from, date_to, order, limit, cursorMatching documents with kind, title, speaker, date, url and a snippet quoted from the stored text
get_central_bank_communicationidoffset, max_charsOne document's full text, in pages
list_fed_appearances—speaker, date_from, date_to, limit, cursorFed officials' scheduled speaking slots, linked to the text when we hold it

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_id—A 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. Filings have no stored blob. To read a filing, call get_filing_text — we serve the body from our own index.

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
list_filingsget_filing_textaccessionThe filing's own text, by item
search_call_qaget_transcripttranscript_idAnalyst questions from earnings-call Q&A, each with analyst, firm, topic, a verdict (answered, partial, deflected) and a one-line answer summary; inputs query, symbol, verdict, date_from, date_to. The topic and summary are paraphrases: quote from the transcript, which provenance.citation_url opens at the question
list_company_appearancesget_transcripttranscript_idThe company's conferences, investor and analyst days, capital markets and R&D days and fireside chats, newest first: kind, title, date (confirmed or estimated), webcast link, and transcript_id when we transcribed it. From the company's own event listings; podcasts and TV are not covered

Reading a filing

get_filing_text serves the filing body from our index, so you do not need to fetch sec.gov to read one. Cite provenance.source_url as before.

A 10-K averages 454,524 characters, which is more than most callers' context for one of its twenty-three items. So the tool is built to be read in two steps:

  1. Call it with only an accession. You get a sections outline — each item, its title and its exact size — plus the first max_chars of the body.
  2. Call again with section ("7" for MD&A, "1A" for risk factors, or a heading from the outline) to read one item.

Long items page rather than truncate. When truncated is true the reply carries next_offset; pass it back as offset to continue, and compare chars_returned with chars_total to see what remains. Sizes in the outline are the sizes the read returns, so you can budget from them.

Two answers that are not failures:

  • sections is empty for most 8-Ks and all proxies. Those carry no item headings and the whole body is the unit; an 8-K averages 5,332 characters.
  • text is null with text_unavailable_reason. not_extracted means we hold no indexed text for that filing. backend_unavailable means the index could not be reached, which is not evidence the filing is empty.

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. There is no cross-corpus text-search tool; 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