Browse documentation

MCP server reference

Every tool and resource the FactIQ MCP server offers: what each one does, the arguments it takes, what it returns, and the limits that apply.

On this page

Start here

The FactIQ MCP server is at https://api.factiq.com/mcp. It gives an AI assistant tools that find and read data. The assistant does the analysis; the server runs no model of its own.

Reads only

The data tools read. They cannot change stored data or your account. The one exception is send_feedback, which sends a report to the FactIQ team.

50 rows per call

A tool returns at most 50 rows. For a larger result, aggregate in SQL and fetch the aggregate.

Described by the server

The server publishes the full description of each tool in its answer to tools/list. This page is a summary of that answer.

Connect

The server uses the Streamable HTTP transport. Each request is independent, so a client does not open a session and does not keep a session id.
https://api.factiq.com/mcp

The server accepts two kinds of credential.

  1. 01

    Browser sign-in

    Claude, ChatGPT, Claude Code and Codex CLI register themselves with the server and open a FactIQ sign-in page in the browser (OAuth 2.1). The client receives the read-only factiq:read scope. An access token lasts one hour and the client renews it without a new sign-in. Follow the setup guide for your client.
  2. 02

    API key

    A script, a service, or a coding agent on a machine with no browser sends a FactIQ API key as a bearer token. See Programmatic access for how to create the key and for curl and Python examples.

Read the tool list from the server

This request returns the name, the full description and the JSON schema of the arguments for every tool. When this page and that answer differ, the answer from the server is correct.

curl -X POST https://api.factiq.com/mcp \
  -H "Authorization: Bearer $FACTIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'

Order of calls

The server sends these instructions to the assistant when it connects. A client you write yourself gets the best results from the same order.
  1. 01

    Read the catalog

    Call get_data_catalog once. Ignore the schemas listed under schemas_without_data.
  2. 02

    Find the series

    Use search_datasets and search_series first. When they find nothing, query the series and dimensions tables with run_sql.
  3. 03

    Fetch the numbers

    Use get_series for one known series. Use run_sql for several series or for an aggregate.
  4. 04

    Add other evidence

    Use the company, earnings-call, media, news, market and satellite tools when the question needs them. Each tool has a coverage option or a catalog that shows what it holds.
  5. 05

    Write it up

    Call get_style_guides before you build a chart or a report. Put the returned source_url next to each figure or quote you use.

All tools

Select a tool name to go to its parameters.
ToolPurpose
get_data_catalogLists every data source the account can read, plus the table structure that all sources share. Call it once at the start of a session.
search_datasetsKeyword search over dataset titles and topics in every schema. The ranking is by keyword, not by meaning.
describe_datasetFull metadata for one dataset. Call it after search_datasets has identified the dataset.
search_seriesFinds series in one schema by words in the title. The match is a substring match, so short word stems work best ("rare" finds more than "rare earth").
get_seriesReturns one series with its metadata. It accepts ordinary time series, tabular series and COMPOUND:: series.
run_sqlRuns one read-only SELECT statement against one schema. Use it for joins, pivots, aggregation, and catalog searches that the search tools cannot express.
get_market_dataRecent prices and company profiles from a market-data provider. Use it for quotes, currencies and commodities that the stored series do not yet cover.
get_geo_dataSatellite-derived indicators for a country, one state or province, or a bounding box. FactIQ computes the result from the provider at the time of the call, so a call can take 30 seconds or more.
search_company_filingsFigures and statements from the reports one company filed: US SEC filers, and companies listed in Germany, the UK and France.
search_earnings_transcriptsWhat management and analysts said on earnings calls, stored as separate claims with an exact quote each. It never returns a whole transcript.
search_media_appearancesClaims made on podcasts, in television interviews and at conferences, and in official documents such as Federal Reserve speeches, testimony, statements and minutes.
search_newsHeadlines and short publisher summaries from the news feeds FactIQ collects. It holds recent news, not an archive, and it never returns the body of an article.
get_style_guidesReturns FactIQ’s written guides for charts, reports, SQL and earnings-call work as Markdown text.
render_chartDraws an interactive line, bar or area chart inside the conversation from numbers the assistant has already fetched. The server lists this tool only for ChatGPT and Codex, which can display the chart component.
send_feedbackSends a report about wrong data or a failing tool to the FactIQ team. It is the only tool that does more than read.

Find data

Four tools locate a dataset or a series. All four read the catalog only; none of them returns observations.

get_data_catalog

Lists every data source the account can read, plus the table structure that all sources share. Call it once at the start of a session.

ParameterMeaning
schemasstringComma-separated list of schema names to return, for example "bls,bea". Omit it to get every schema.
fullbooleanReturn the long description of every dataset instead of the compact index. Default: false

Returns. The visible schemas with their organization and country, a list named schemas_without_data for schemas that hold no rows yet, a dataset index per schema, and the definitions of the series, data_points, dimensions and compound_series tables.

Example arguments

{
  "schemas": "bls,bea"
}

search_datasets

Keyword search over dataset titles and topics in every schema. The ranking is by keyword, not by meaning.

ParameterMeaning
queryRequiredstringSearch words.
schemasstring[]Restrict the search to these schemas.
limitintegerMaximum number of datasets to return. Default: 15

Returns. Ranked rows of schema, dataset_code, title and topic.

Example arguments

{
  "query": "consumer prices",
  "schemas": [
    "bls"
  ]
}

describe_dataset

Full metadata for one dataset. Call it after search_datasets has identified the dataset.

ParameterMeaning
schemaRequiredstringSchema that holds the dataset.
dataset_codeRequiredstringDataset code from search_datasets or the catalog.

Returns. Topic, methodology, known breaks in the series, any notice of a changed base year, the dimensions that exist in the data, and example series.

Example arguments

{
  "schema": "bls",
  "dataset_code": "cu"
}

search_series

Finds series in one schema by words in the title. The match is a substring match, so short word stems work best ("rare" finds more than "rare earth").

ParameterMeaning
schemaRequiredstringSchema to search.
termsRequiredstring[]Title words. The tool first looks for titles that contain every term. If none match, it returns titles that contain any term.
limitintegerMaximum number of series to return. Default: 15
include_compoundbooleanInclude series that FactIQ computes from other series. Their ids start with COMPOUND::. Default: true

Returns. Matching series with series_id, title, dataset_code, frequency, units, seasonal adjustment, begin_time and end_time, plus a note that says whether all terms or any term matched.

  • When this tool finds nothing, query the series and dimensions tables with run_sql.

Example arguments

{
  "schema": "bls",
  "terms": [
    "unemployment",
    "rate"
  ]
}

Fetch data

These tools return observations. Each call returns at most 50 rows; see Limits for what to do with larger results.

get_series

Returns one series with its metadata. It accepts ordinary time series, tabular series and COMPOUND:: series.

ParameterMeaning
schemaRequiredstringSchema that holds the series.
series_idRequiredstringSeries id from search_series or run_sql.
from_yearintegerFirst year to return.
to_yearintegerLast year to return.
transformstring"yoy_pct" adds the percent change from the same period one year earlier. "yoy_diff" adds the difference, which is the right choice for a rate. Periods are matched by calendar date, so a missing month does not shift later comparisons.

Returns. Title, units, frequency, notes, source metadata and the observations. A coverage_note and a missing_periods list appear when the series has periods with no observation. Series built from company filings also carry row_sources, which names the filing behind each row.

Example arguments

{
  "schema": "bls",
  "series_id": "LNS14000000",
  "from_year": 2024,
  "transform": "yoy_diff"
}

run_sql

Runs one read-only SELECT statement against one schema. Use it for joins, pivots, aggregation, and catalog searches that the search tools cannot express.

ParameterMeaning
schemaRequiredstringSchema the statement reads.
sqlRequiredstringOne SELECT statement. Qualify table names with the schema, for example bls.data_points.
questionstringThe question the statement answers. It is stored with the call and helps FactIQ diagnose a failed query.
explorebooleanSet it to true for a statement that only looks for which data exists. The result is then marked exclude_from_data_panel. Default: false
auto_retrybooleanWhen the statement returns zero rows, let the server revise it once and run it again. Default: false
pageintegerPage number. It works only on the nasa_fires schema, where each row is one fire detection and cannot be aggregated into a series. Default: 1

Returns. row_count, columns and results. When the statement matches more than 50 rows, truncated is true and a note says how many rows exist. On nasa_fires the result carries page and has_more instead.

  • The database role is read-only. INSERT, UPDATE, DELETE and DDL statements are rejected.
  • A statement is cancelled after 30 seconds.
  • For a year-over-year comparison, join on the calendar period with date_trunc. Do not use LAG(value, 12): one missing month makes every later comparison wrong.

Example arguments

{
  "schema": "bls",
  "sql": "SELECT date_trunc('year', time) AS year, avg(value) AS unemployment_rate FROM bls.data_points WHERE series_id = 'LNS14000000' AND time >= '2015-01-01' GROUP BY 1 ORDER BY 1"
}

get_market_data

Recent prices and company profiles from a market-data provider. Use it for quotes, currencies and commodities that the stored series do not yet cover.

ParameterMeaning
assetRequiredstringTicker, currency pair, commodity name, or company search text. Examples: "AAPL", "EUR/USD", "WTI", "NVIDIA".
data_typestringOne of price_history, quote, company_profile, etf_profile, symbol_search. Default: "price_history"
frequencystringFor price_history: daily, weekly or monthly. Default: "daily"
limitintegerMaximum number of price rows, from 1 to 5,000. Default: 100

Returns. Price rows with open, high, low, close and volume, or the requested quote or profile.

  • Financial statements and reported company figures come from search_company_filings, not from this tool.

Example arguments

{
  "asset": "EUR/USD",
  "data_type": "price_history",
  "frequency": "weekly",
  "limit": 52
}

get_geo_data

Satellite-derived indicators for a country, one state or province, or a bounding box. FactIQ computes the result from the provider at the time of the call, so a call can take 30 seconds or more.

ParameterMeaning
datasetRequiredstringOne of fires_viirs, no2_tropomi, so2_tropomi, co_tropomi, aerosol_index_tropomi, ndvi_s2, precip_chirps, precip_imerg, temperature_power, soil_moisture_power.
regionRequiredstringA country name or ISO3 code ("India", "IND"); "Country/State" for a state or province ("India/Punjab", "CHN/Guangdong"); or "bbox:west,south,east,north" in degrees.
start_dateRequiredstringFirst day, as YYYY-MM-DD.
end_dateRequiredstringLast day, as YYYY-MM-DD.
aggregationstring"monthly" or "daily" returns a time series. fires_viirs, ndvi_s2 and the *_tropomi datasets also accept "grid", which returns one row per latitude and longitude cell. fires_viirs also accepts "seasons" and "points". Default: "monthly"
resolutionnumberGrid cell size in degrees. Only for fires_viirs with aggregation "grid".
include_flaresbooleanOnly for fires_viirs. Set it to true to count gas flares, industrial heat sources, volcanoes and offshore detections. Default: false

Returns. Rows for each interval or grid cell, with units, the source attribution to cite, and caveats for the dataset.

  • State and province boundaries exist for IND, CHN, IDN, VNM, THA, MYS, PHL, PAK, BGD, LKA, MMR, KHM, NPL, KOR, JPN, TWN and USA.
  • Most datasets allow at most 50 intervals per call. fires_viirs allows 200.

Example arguments

{
  "dataset": "fires_viirs",
  "region": "India/Punjab",
  "start_date": "2025-10-01",
  "end_date": "2025-11-30",
  "aggregation": "daily"
}

Research companies and news

These tools search text and figures that FactIQ extracted in advance. The search matches words; it does not interpret meaning. If a search finds nothing, try the words the company itself uses.

search_company_filings

Figures and statements from the reports one company filed: US SEC filers, and companies listed in Germany, the UK and France.

ParameterMeaning
companyRequiredstringAn exact ticker is best. A full company name or an LEI also works when it identifies one company.
search_targetstringcoverage lists the reports and dates held. filings lists report records and their source URLs. metrics lists the available measures. facts returns reported numbers. commentary returns management statements with exact quotes. risk_changes returns year-over-year changes to the stated risk factors. Default: "facts"
conceptstringOne measure, for example "revenue". The tool selects the best stored match and returns it across periods. Only for metrics and facts.
querystringWord search across measure names, labels and segment names. For commentary and risk_changes it searches the statements and quotes.
report_typestringannual, quarterly or half_year, or a form name such as 10-K or 10-Q.
fiscal_yearintegerCompany fiscal year, for example 2026.
fiscal_periodstringQ1, Q2, Q3, Q4 or FY.
metric_classstringClass of measure, for example financial, ifrs, segment, kpi or apm.
segmentstringBusiness segment, product or region.
date_fromdateEarliest period-end date, inclusive.
date_todateLatest period-end date, inclusive.
formatstring"json" returns the result as a JSON tree. "pretty" returns the same tree as readable text. A call returns one form, never both. Default: "json"
limitintegerMaximum matched rows, from 1 to 50. Each reported number is one row. Default: 20

Returns. A tree grouped by filing or by measure. Each filing and each figure carries source_url and a source_link object that points to the original document.

  • commentary and risk_changes cover US SEC filers only.
  • When an exact ticker has no stored figure for a metrics or facts request, the tool answers from a market-data provider’s standardized statements. Those values carry no link to a filing.

Example arguments

{
  "company": "NVDA",
  "search_target": "facts",
  "concept": "revenue",
  "report_type": "annual"
}

search_earnings_transcripts

What management and analysts said on earnings calls, stored as separate claims with an exact quote each. It never returns a whole transcript.

ParameterMeaning
search_targetstringclaims returns statements by management and analysts. pressure_points returns what analysts asked for and whether management answered. disclosure_profile returns the recorded disclosure habits of one company. coverage lists the calls held. Default: "claims"
querystringSearch words. An empty query returns the most recent rows.
tickerstringOne ticker or a comma-separated list.
company_namestringOne company name or a comma-separated list. Pass ticker or company_name, not both.
quarter_filterstringExact fiscal period, for example FY2026Q3.
claim_familystringClaim category code, for example pricing_mechanics or capital_allocation. The tool description lists all codes.
sectionstringprepared_remarks or qa. Only for claims.
detailbooleanAdd the structured fields of each claim, such as period and time horizon. Default: false
limitintegerMaximum rows, from 1 to 50. Default: 15

Returns. Rows with the speaker, role, fiscal period, a normalized statement, the exact quote, and a source_link. Coverage rows give the number of calls and the first and last period for each company.

  • Only verbatim_quote is a quotation. canonical_statement is a rewritten summary and must not be placed in quotation marks.
  • An empty result does not prove that management said nothing on the subject. Check coverage first, then try other words.

Example arguments

{
  "search_target": "claims",
  "ticker": "MU",
  "query": "capital expenditure",
  "detail": true
}

search_media_appearances

Claims made on podcasts, in television interviews and at conferences, and in official documents such as Federal Reserve speeches, testimony, statements and minutes.

ParameterMeaning
search_targetstringsearch returns claims and passage summaries together. claims, passages and pressure_points each return one kind. appearances lists the recordings and documents held. coverage counts them per company. Default: "search"
querystringSearch words. An empty query returns recent rows.
companystringTickers and names in one comma-separated list, for example "NVDA,OpenAI,Federal Reserve".
personstringPart of a speaker name.
appearance_typestringpodcast, tv_interview, conference or other for interviews; speech, testimony, minutes, statement, press_conference or hearing for official documents.
institutionstringPart of the name of the institution that published an official document, for example "Federal Reserve".
countrystringISO country code of that institution, for example "US".
claim_familystringClaim category code. It cannot be combined with search_target "passages".
date_fromstringEarliest publication date, as YYYY-MM-DD.
date_tostringLatest publication date, as YYYY-MM-DD.
sortstringrelevance or newest. Default: "relevance"
detailbooleanAdd the structured claim fields and the fields that say how the recording was linked to a company. Default: false
limitintegerMaximum rows, from 1 to 50. Default: 15

Returns. Rows with the speaker, a summary of what was said, topic labels, the channel and publication date, and a source_url that opens the video at the right time or the document at the right page.

  • The text in canonical_paraphrase is a summary, not a quotation. Open source_url and check the wording before you quote it.
  • A company value that matches nothing is listed under company_unmatched with up to five possible matches. It returns no rows and no error.

Example arguments

{
  "search_target": "claims",
  "company": "OpenAI",
  "query": "compute",
  "sort": "newest"
}

search_news

Headlines and short publisher summaries from the news feeds FactIQ collects. It holds recent news, not an archive, and it never returns the body of an article.

ParameterMeaning
querystringSearch words over headline and summary. Every word must match, so start with one distinctive word. Quoted phrases and -exclusion work.
tickersstring[]Return articles that name any of these companies, for example ["NVDA", "RELIANCE"].
topicstringOne of markets, economics, companies, technology, politics, world, energy, health, india, opinion.
sourcesstring[]Publisher codes, for example bloomberg, ft, wsj, who.
start_datestringEarliest publication date, as YYYY-MM-DD.
end_datestringLatest publication date, as YYYY-MM-DD.
sortstringlatest or relevance. relevance needs a query. Default: "latest"
limitintegerMaximum rows, from 1 to 50. Default: 20

Returns. rows with published_at, source, title, summary, url, the tickers named in the text, and an analysis block with keywords, a geography and one sentence on why the story matters. meta gives the number matched and returned.

Example arguments

{
  "query": "copper",
  "topic": "markets",
  "limit": 10
}

Write up results and report problems

These tools do not return data.

get_style_guides

Returns FactIQ’s written guides for charts, reports, SQL and earnings-call work as Markdown text.

ParameterMeaning
guidesRequiredstring[]Any of "chart", "report", "sql", "earnings", or "all".

Returns. One Markdown document per requested guide, keyed by guide name.

Example arguments

{
  "guides": [
    "chart",
    "sql"
  ]
}

render_chart

Draws an interactive line, bar or area chart inside the conversation from numbers the assistant has already fetched. The server lists this tool only for ChatGPT and Codex, which can display the chart component.

ParameterMeaning
titleRequiredstringChart title.
x_valuesRequiredstring[]Horizontal-axis positions in order: ISO dates such as "2024-01-01", or category names.
seriesRequiredobject[]One or more objects with a label and a values list. Each values list has one entry per x value; use null for a gap.
chart_typestringline, bar or area. Default: "line"
unitsstringVertical-axis units, for example "Percent".
subtitlestringText under the title.
x_tick_formatstringDate label form: auto, year, quarter, month or date. Default: "auto"
x_labelsstring[]Custom label text, one per x value, for labels such as fiscal quarters.

Returns. The chart specification, which the client draws. The tool does not create a public page or a link.

Example arguments

{
  "title": "US unemployment rate held near 4.1% through mid-2026",
  "x_values": [
    "2026-06-01",
    "2026-07-01",
    "2026-08-01"
  ],
  "series": [
    {
      "label": "Unemployment rate",
      "values": [
        4.2,
        4.1,
        4.1
      ]
    }
  ],
  "units": "Percent"
}

send_feedback

Sends a report about wrong data or a failing tool to the FactIQ team. It is the only tool that does more than read.

ParameterMeaning
messageRequiredstringUp to 4,000 characters. Name the schema, the series or the SQL, and state the expected and the observed result. Do not include personal details.
categorystringdata_issue, tool_error, missing_data or other. Default: "other"

Returns. A confirmation that the report was stored. No reply comes back through the connector.

Example arguments

{
  "category": "data_issue",
  "message": "bls LNS14000000: get_series returns no row for 2026-05-01, but the BLS website publishes a value for May 2026."
}

Resources

The server also publishes five MCP resources. A client that supports resources can read them without a tool call.
URITypeContent
factiq://catalogapplication/jsonThe same content as get_data_catalog with no arguments.
factiq://guide/chart-styletext/markdownChart type choice, titles, colours and sourcing.
factiq://guide/report-styletext/markdownReport structure, narrative style and per-chart sources.
factiq://guide/sqltext/markdownTable structure, query patterns and common mistakes.
factiq://guide/earnings-styletext/markdownQuotation rules and the difference between spoken and filed figures.

The four guides are the same documents that get_style_guides returns. The tool exists because many clients do not read resources.

Limits

The limits keep results small enough for an assistant to read.
  • Rows. get_series, run_sql and the search tools return at most 50 rows. A longer result is sampled at even intervals and carries truncated: true. There is no option that returns every row.
  • SQL time. A statement is cancelled after 30 seconds.
  • Rate. Up to 10 calls per second for each account. A faster caller gets a 429 tool error.
  • Market prices. get_market_data is the exception to the row limit. It returns up to 5,000 price rows when limit asks for them.

When a result is truncated

Change the query so that the answer fits in 50 rows: group by month, quarter or year with date_trunc, compute the sum, average, rank or ratio in SQL, or pass from_year and to_year to get_series.

{
  "row_count": 1284,
  "columns": ["time", "value"],
  "results": [ ...50 rows... ],
  "truncated": true,
  "note": "Showing 50 of 1284 rows. To use the rest, aggregate or compute it in SQL ..."
}

Errors and retired tools

A failed call comes back as a normal MCP tool result with isError set to true. The text starts with the HTTP status that the same request gets from the REST endpoint.
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{ "type": "text", "text": "429: Too many requests. Please wait a moment before asking another question." }],
    "isError": true
  }
}

When search_company_filings cannot identify the company, the error text lists the closest stored companies, so that the next call can use one of them.

Tool names that changed

An older client can still call a tool name that the server no longer lists. The server does not answer with “unknown tool”. It returns a result that names the replacement, so the assistant can call the new name in the same conversation.

  • search_earnings is now search_earnings_transcripts.
  • search_company_filings_tree is now search_company_filings.
  • share_chart, share_report and list_my_artifacts were removed and have no replacement.

What the server does not do

These are design decisions, not faults. Do not report them with send_feedback.
  • It does not change stored data. Every SQL statement runs under a read-only database role.
  • It does not return a complete earnings-call transcript. It returns separate claims, each with a quote.
  • It does not publish a chart or a report to a public page. render_chart draws a chart inside the conversation only.
  • It does not answer questions. It returns data, and the assistant that called it does the analysis.

For how to ask good research questions with these tools, read Get better results. For what each data source covers, read the coverage overview.