ILO Statistics (ILOSTAT) MCP Server
This server lets you query the ILO's ILOSTAT labour statistics through MCP, with provenance on every answer.
Search indicators:
ilo_search_indicators— keyword search over ~1,200 ILOSTAT dataflows (e.g. unemployment, wages, informality), with pagination and ranked results.Inspect indicator structure:
ilo_get_indicator_metadata— dimensions, codelists, time dimension, default selection, and data vintage for a dataflow.List valid filter codes:
ilo_list_dimension_values— valid codes/labels for dimensions like REF_AREA (countries), SEX, AGE, with search and pagination.Get statistical data:
ilo_get_data— observations from a dataflow filtered by country/area (up to 30), sex, age, period, etc.; raw ILOSTAT values, no transformation.ChatGPT Deep Research contract:
searchandfetchtools expose the dataflow catalogue and dataflow details as Markdown for ChatGPT-style connectors.Resources and prompts: attachable guides (
ilostat://guide, key dataflows, provenance reference) and ready-made workflows for country labour profiles, country comparisons, and indicator trends.Provenance and attribution: every response includes source URL, data vintage, retrieval timestamp, license, and ILO citation; data is CC BY 4.0.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ILO Statistics (ILOSTAT) MCP Serverget unemployment rate data for Brazil"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ILO Labour Statistics (ILOSTAT) — MCP Server
A public, hosted, provenance-first MCP server for the International Labour Organization (ILO) statistics — the ILOSTAT database — no installation, no account, no API key. Point your MCP client at the hosted endpoint and ask about unemployment, employment, wages, working time and other labour indicators by country, year, sex and age. It runs on Cloudflare Workers over Streamable HTTP and talks to the official ILOSTAT SDMX REST API.
Independent project. This is an unofficial, community-built client of the ILO's public ILOSTAT API — not affiliated with or endorsed by the International Labour Organization. Data remain © ILO under CC BY 4.0; see Data license and attribution.
Every response carries a provenance block (source URL, data vintage, real retrieval timestamp, license, ILO citation) — exact figures with an audit trail, not numbers guessed from training data.
🇧🇷 Em português. Servidor MCP remoto e hospedado (nada para instalar, sem conta e sem chave) para as estatísticas de mercado de trabalho da OIT — desemprego, emprego, salários, jornada e informalidade por país, ano, sexo e idade, direto no Claude, no ChatGPT ou em qualquer cliente MCP, com proveniência e citação da fonte em cada resposta: README em português.
Questions it answers
In plain language, inside the MCP client — the assistant picks the tool and the filters:
"What has happened to unemployment in Brazil since 2015?" (
ilo_get_data)"Compare youth unemployment in Brazil, Mexico and South Africa." (
ilo_compare_countries)"How large is the gender pay gap, and where does the ILO publish it?" (
ilo_search_indicators→ilo_get_data)"What share of employment in India is informal?" (
ilo_search_indicators→ilo_get_data)"Which ILOSTAT dataflow has average monthly earnings by sex and economic activity?" (
ilo_search_indicators)"Which country, age and sex codes can I filter this indicator by?" (
ilo_list_dimension_values)"Give me a labour-market profile of Viet Nam." (
ilo_country_labour_profile)
Ask in your words, not the ILO's. ILOSTAT is worded in British statistical English, and the catalogue is matched on the dataflow name — so the everyday or US word used to return nothing at all. Measured over the 1,212 dataflows of the official catalogue (2026-09-13), and fixed since 0.6.0: the search translates the term and tells you it did.
you ask | hits before | ILOSTAT writes | hits |
| 0 | labour, labour force | 176, 122 |
| 0 | earnings | 107 |
| 0 | informal | 133 |
| 2 | sex | 1,131 |
| 0 | output per worker | 4 |
| 0 | unemployment | 108 |
Related MCP server: world-bank-economic-mcp
Comparison with the alternatives
Anyone who already works with ILOSTAT has good tools, and this server replaces none of them —
it sits somewhere else in the chain: it answers the question at the point where the question is
asked, inside the assistant, with source, vintage and licence attached to the answer. Detail,
side-by-side examples and the measured numbers in
docs/alternatives.md.
Tool | What it is | When to prefer it |
ilo-mcp-server (this) | Remote MCP server, hosted, nothing to install: 6 tools over the ~1,200 ILOSTAT dataflows, with a provenance block per answer | The question is asked in an assistant (Claude, ChatGPT, Cursor, Claude Code) and the answer has to be auditable |
Rilostat 2.5.0 (R, CRAN) | The ILO's own R package, written by ILO staff: bulk download, metadata, filtering and reshaping | You are in R and want the dataset in a data frame — a whole table, repeatedly, for analysis |
Generic SDMX clients; ILO is one of ~36 sources they know | Your pipeline is Python and you want SDMX objects, or the same code across several SDMX agencies | |
Aggregator that republishes 1,071 ILO datasets next to other providers, one API for all | You want ILO series alongside IMF, OECD, Eurostat in a single interface | |
The source itself, which this server calls | You are building your own client and want full control |
Do not use this server when you need a whole dataset rather than an answer (Rilostat's bulk download is the right tool), when the question is not labour statistics published by the ILO (education → UNESCO UIS, national accounts → IMF/World Bank), or when you need microdata: ILOSTAT publishes aggregates, and so does this server.
Sister servers, same design and same provenance block, for other official sources: IBGE (Brazilian statistics), BCB (Central Bank of Brazil), Senado (Brazilian Senate open data), SIH/SUS (Brazilian hospital admissions) and medical terminologies (ICD-11, ICD-10, LOINC, RxNorm, ATC, MeSH).
Use it (hosted — no setup)
Point any MCP client at the Streamable HTTP endpoint:
https://ilo.sidneybissoli.com/mcpClaude Desktop / Claude Code and other clients with native remote support:
{
"mcpServers": {
"ilostat": {
"url": "https://ilo.sidneybissoli.com/mcp"
}
}
}For clients that launch MCP servers as a command, use the
mcp-remote bridge:
{
"mcpServers": {
"ilostat": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://ilo.sidneybissoli.com/mcp"]
}
}
}The ilo-mcp-server.sidneybissoli.workers.dev hostname is also served, as a secondary.
ChatGPT (Deep Research)
ChatGPT deep research (and company knowledge, and research workflows over the Responses API) only uses an MCP server that exposes exactly search and fetch — this server does, on top of the ilo_* tools. Point the connector at the hosted endpoint, no key required:
https://ilo.sidneybissoli.com/mcpsearch ranks the query against the full ILOSTAT dataflow catalogue (~1,200 SDMX dataflows — employment, unemployment, wages, working time, informality, SDG labour indicators) and returns { id, title, url } (ind:<DATAFLOW_ID>, e.g. ind:DF_UNE_2EAP_SEX_AGE_RT); fetch returns the dataflow as readable Markdown — name, data vintage, dimensions and codelists, the ILO's default selection and how to query it with ilo_get_data — with the public ILOSTAT data explorer page as url, which is what ChatGPT cites. Both carry the same provenance block as every other tool, in structuredContent and _meta (the text channel is the contract's JSON). In ChatGPT's developer mode (Settings → Security and login → Developer mode) any tool is callable — the ilo_* tools remain the ones to use for data.
Run locally (stdio)
Prefer not to route queries through a third-party host? The same server also runs as a local stdio process that talks directly to the official ILOSTAT API — same 6 tools, resources and prompts, same limits, same provenance block, no Cloudflare in the loop.
No install needed — the package is on npm (ilo-mcp-server, Node ≥ 20):
{
"mcpServers": {
"ilostat": {
"command": "npx",
"args": ["-y", "ilo-mcp-server"]
}
}
}Or from source:
git clone https://github.com/SidneyBissoli/ilo-mcp-server
cd ilo-mcp-server
npm install
npm run build
node dist/cli.js # serves MCP over stdio (Ctrl+C to stop)(then point the client at node /path/to/ilo-mcp-server/dist/cli.js).
Differences from the hosted server, all due to the absence of Cloudflare bindings: the SDMX
cache lives in process memory (structures and codelists are reused within a session, not across
sessions); the search catalogue is downloaded from the official endpoint on the first search
(its real retrieved_at is reported in provenance); no usage metrics, rate limit or auth. Logs
go to stderr — stdout carries only the JSON-RPC stream. The repository Dockerfile builds
this runtime (used by the Glama registry).
Tools
Tool | What it does | Source |
| keyword search over ~1,210 dataflows (paginated by | local catalogue (no upstream call) |
| dimensions, codelists, vintage and default selection of a dataflow | cached structure (miss → upstream) |
| valid codes of one dimension (paginated by | cached codelist (miss → upstream) |
| observations filtered by dimension and period | 1 live REST call per query |
| ChatGPT Deep Research contract: ranks a query against the full dataflow catalogue, returns | in-memory index built from the local catalogue (24 h) |
| ChatGPT Deep Research contract: one dataflow as readable Markdown with the public data explorer page as | cached structure (miss → upstream) |
Typical flow: ilo_search_indicators → ilo_get_indicator_metadata / ilo_list_dimension_values
to discover valid filter codes → ilo_get_data with country and period filters.
Every response carries the provenance block v1.0
(@sbissoli/mcp-provenance, modes
concise/detailed via the provenance_mode parameter) on three channels:
structuredContent, namespaced _meta (com.sidneybissoli.ilostat/*) and a text footer.
Resources and prompts
Three resources (static, text/markdown, no upstream call) that a client can attach to the
context before calling tools — they save the 2–3 discovery calls most sessions spend on
"which dataflow, which codes":
URI | Content |
| tool workflow, stable code conventions ( |
| verified dataflow ids by topic (unemployment, employment, participation, wages, hours, informality, NEET, SDG 8, productivity) |
| meaning of every provenance field and how to cite the ILO |
Three prompts — ready-made workflows that chain the tools and end with the citation rules (arguments are strings; period arguments optional):
Prompt | Arguments | Result |
|
| labour-market profile of one country (unemployment, participation, employment ratio, informality, NEET, earnings, hours) |
|
| comparison table across countries/aggregates in one data call, flagging modelled estimates vs reported data |
|
| time series of one indicator with first/last, peak/trough and |
Every dataflow id quoted in the resources and prompts is checked against the catalogue seed by the test suite, so the documentation cannot point at an id the search would not find.
Behaviour and limits
REF_AREAis required inilo_get_data, up to 30 areas per call. The ILO gateway times out (HTTP 504) on unrestricted queries, so the server never issues one; for broad panels, split the areas into batches and/or paginate by period (start_period/end_period). The error message explains how.One live REST call per data query. Data is never cached — every
ilo_get_dataresult is fetched from ILOSTAT at request time. Dataflow structures (TTL 24 h) and codelists (TTL 7 days, shared across dataflows) are cached.data_vintageis the dataflow's last-update date as published by the ILO (LAST_UPDATEannotation, normalised to ISO).retrieved_atis always the real instant of extraction from ILOSTAT, preserved alongside any cached value — never the build or response time. Cached responses say so (served_from_cache: true).The indicator catalogue is a local snapshot (~1,210 dataflows), refreshed periodically; its own
retrieved_atis reported in the provenance ofilo_search_indicators, so its age is always visible.Every upstream call carries an identifiable User-Agent (service URL + contact), so ILO administrators can reach the operator.
Language: English; timezone: UTC (ILO data is published in English).
Provenance fields
derived—trueonly for real transformation (aggregation, server-computed rate, interpolation, harmonisation), always with aderivation_note; unit conversion and rounding do not count. This server does not transform values, soderivedis alwaysfalse.notices— reproduces the values ofOBS_STATUS(the SDMX status/disclaimer channel, e.g. "Break in series"), verbatim and with counts. Technical per-observation attributes (DECIMALSetc.) stay on the rows (rows[].attributes).
Data license and attribution
ILOSTAT data and metadata: CC BY 4.0 (since 2023-05-03; license verified 2026-08-04).
ILO attribution in every response (
citationfield):International Labour Organization, ILOSTAT, https://ilostat.ilo.org/data/, accessed <date>.The ILO logo is not used. This service is not endorsed by the ILO.
Self-hosting / development
Everything below is only needed to run your own instance — it is not required to use the public server.
npm install
npm run typecheck && npm test # offline suite (parsers, key, tools, output contract, resources/prompts, in-memory catalogue, vocabulary, eval fixtures)
npm run dev # http://localhost:8787/mcp (Worker)
npm run build && npm start # stdio runtime (dist/cli.js)
# Catalogue seed (D1) — required before first use:
node scripts/seed-catalog.mjs # downloads via curl and generates scripts/seed-catalog.sql
npx wrangler d1 execute ilostat-catalog --local --file=scripts/seed-catalog.sql
npx wrangler d1 execute ilostat-catalog --remote --file=scripts/seed-catalog.sql
npm run deploy
node scripts/smoke-mcp.mjs # smoke test against production (initialize → 6 tools → search → fetch → errors)
npm run manifest:lhm # regenerate tools/resources/prompts in lhm.plugin.json from the real server
# (the seed also writes tests/fixtures/catalog-ids.txt — the versioned id list the tests check resources/prompts against)Bindings (see wrangler.jsonc): KV SDMX_CACHE, D1 CATALOG_DB, Durable Object USAGE
(SQLite-backed usage counters), CF_VERSION_METADATA. Optional Bearer auth
(wrangler secret put API_KEY); token-bucket rate limit per IP.
Notes for operators:
ILOSTAT returns JSON only when negotiated via the
Acceptheader (application/vnd.sdmx.{structure,data}+json);?format=is ignored and returns XML.The ILO gateway answers HTTP 500 (
languageTag1) to theAccept-Language: *header that Node'sfetch(undici) sends by default; every upstream call therefore setsAccept-Language: enexplicitly (Cloudflare's runtime sends no such header, so the Worker was never affected). It also expects an identifiable User-Agent.Catalogue refresh is manual (no cron): quarterly, or immediately if a dataflow that exists upstream does not show up in search. Procedure: the three seed commands above. Data queries are always live, so only the search catalogue can age — and its age is exposed in provenance.
Evals
@sbissoli/mcp-evals: 24 fixtures in
evals/fixtures/queries.ts, validated offline in npm test. The run with a real model
(npm run eval) uses the Anthropic API and needs ANTHROPIC_API_KEY (without it, it exits with
instructions). Run of 2026-08-07: top-1 100% (24/24) — evals/results/.
End-to-end: 10 complex questions with a single verifiable answer in evals/e2e/evaluation.xml,
answers validated manually against production (evals/e2e/validacao-respostas.md). Run of
2026-08-07 (Sonnet): 9/10 exact string; 10/10 substantive — evals/results/2026-08-07-e2e.md.
Endpoints
Route | Purpose |
| landing page (service identity + contact — public) |
| liveness |
| version, tool/resource/prompt counts and names, provenance contract version, current deploy (feeds the README badges) |
| aggregated usage (MCP endpoint only; no IPs, no query content) |
| MCP Streamable HTTP |
Security
Snyk Agent Scan (2026-08-07): passed — report in
security/.
License
Code: MIT. Data: ILOSTAT, CC BY 4.0 (see "Data license and attribution" above).
Privacy
Privacy policy of the hosted service: PRIVACY.md.
Contact
Sidney da S. P. Bissoli — sbissoli76@gmail.com. This service is not endorsed by the ILO.
Available Tools
6 toolsfetchDeep Research DocumentARead-onlyIdempotentInspect
Returns the full document for an id obtained from search, as { id, title, text, url, metadata }: text is the readable content (Markdown) and url the canonical public page to cite.
Companion of search in the OpenAI Deep Research contract, over the ILOSTAT labour statistics (≈1,200 SDMX dataflows: employment, unemployment, wages, working time, informality, SDG labour indicators) catalog. Only ids returned by search are valid; an unknown id returns an error.
The ilo_* tools remain the tools for data queries.
Behavior: read-only and idempotent — a live GET against the public source when the document needs it.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Identifier of a document returned by `search` |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Unique identifier of the document on this server; what `fetch` takes |
| url | Yes | Canonical public URL of the document — ChatGPT's citation depends on it |
| text | Yes | Full readable content of the document (Markdown) |
| title | Yes | Human-readable title of the document |
| metadata | No | Additional key/value pairs about the document (kind, source, period…) |
| provenance | Yes | |
| attribution | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavior beyond the annotations: it is read-only and idempotent, performs a live GET against the public source when needed, and returns an error for unknown ids. It also discloses the return shape. This complements, rather than contradicts, the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: the core function and return shape come first, followed by context and behavior. It is concise enough that every sentence adds useful information, and it avoids repeating the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter tool with an output schema and safety annotations, the description covers everything needed: source of ids, return format, error behavior, read-only nature, and relationship to sibling tools. Nothing critical is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds extra semantic value by specifying that the id must come from `search`, that only such ids are valid, and that unknown ids produce errors—details not present in the schema's parameter description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific action ('Returns the full document') and names the resource (document identified by an id from `search`). It also distinguishes itself from siblings by explicitly separating the `ilo_*` data-query tools from this document-fetch contract.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use the tool: after obtaining an id from `search`, and only ids from `search` are valid. It also provides an exclusion by reserving `ilo_*` tools for data queries, so an agent knows this is not for statistical data retrieval.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ilo_get_dataGet ILOSTAT dataARead-onlyIdempotentInspect
Statistical observations from one ILOSTAT dataflow, filtered by dimension codes (filters, e.g. {"REF_AREA": ["BRA","ARG"], "SEX": "SEX_T"}) and period (start_period/end_period, e.g. "2015"/"2024"). REF_AREA is required, maximum 30 areas per call — for broad panels, split areas into batches and/or paginate by period. Unfiltered dimensions return all their categories. Does not aggregate, convert or otherwise transform values (raw ILOSTAT data only), and does not search indicators (use ilo_search_indicators).
| Name | Required | Description | Default |
|---|---|---|---|
| filters | Yes | Dimension id → code or list of codes (from ilo_list_dimension_values). REF_AREA is required (up to 30 area codes); any other dimension is optional and, left out, returns all of its categories. | |
| dataflow | Yes | Dataflow id from ilo_search_indicators (e.g. "DF_UNE_DEAP_SEX_AGE_RT") | |
| end_period | No | Last period, e.g. "2024" | |
| start_period | No | First period, e.g. "2015" | |
| provenance_mode | No | Provenance verbosity: 'concise' (default — source, url, vintage, retrieval date, citation, license) or 'detailed' (full canonical block with dataset, dimension key and notices) | |
| last_n_observations | No | Alternative to periods: only the latest N observations per series |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| columns | Yes | |
| dataflow | Yes | |
| provenance | Yes | |
| rows_count | Yes | |
| attribution | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, open-world), and the description adds real operational context beyond them: a 30-area cap per call, batching/pagination strategies, and the guarantee that values are never aggregated or transformed. It stops short of describing rate limits or failure modes beyond the schema's timeout note.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with the core purpose, then filters, then constraints, then exclusions — a logical progression with no filler. The sentences are long and dense, but each carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be explained, and the description covers everything else an agent needs: required filtering, size limits, period vs. last-N options, and scope exclusions. Nothing material is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description goes further by showing a worked filter example that illustrates the string-vs-array value shapes and by clarifying the period parameters' format. This adds genuine meaning beyond the schema field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource — returning statistical observations from one ILOSTAT dataflow — and scopes it by dimension codes and period. It also explicitly distinguishes itself from the sibling ilo_search_indicators, so an agent can route between them without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete usage conditions (filter by dimension codes and period, unfiltered dimensions return everything) and names an alternative for a different job (ilo_search_indicators for indicator lookup). It also advises batching/pagination for broad panels, though it does not discuss the other siblings (fetch, ilo_get_indicator_metadata).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ilo_get_indicator_metadataGet ILOSTAT indicator metadataARead-onlyIdempotentInspect
Structure of one ILOSTAT dataflow: dimensions (in SDMX key order), their codelists, the time dimension, the source's default selection and the data vintage (last update at the ILO). Use before ilo_get_data to know which filters exist. Does not return statistical values and does not list the codes themselves (use ilo_list_dimension_values).
| Name | Required | Description | Default |
|---|---|---|---|
| dataflow | Yes | Dataflow id from ilo_search_indicators (e.g. "DF_UNE_DEAP_SEX_AGE_RT") | |
| provenance_mode | No | Provenance verbosity: 'concise' (default — source, url, vintage, retrieval date, citation, license) or 'detailed' (full canonical block with dataset, dimension key and notices) |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| name | Yes | |
| version | Yes | |
| dimensions | Yes | |
| provenance | Yes | |
| attribution | Yes | |
| data_vintage | Yes | |
| time_dimension | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld, so the safety profile is covered. The description adds substantive context beyond them: it defines the shape of the response and explicitly states two negative behaviors (no statistical values, no code enumeration), which prevents misuse. It does not mention pagination or response size, but for a metadata lookup that is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each load-bearing: the return structure first, the usage ordering second, the exclusions and alternative third. No filler and the most important content (what it returns and when to call it) is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value detail is not the description's burden, and it still summarizes the payload structure. Combined with the explicit sibling routing, an agent has everything needed to select and invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters are documented there, including the dataflow id source and the provenance_mode enum values. The description adds no parameter-level detail, so the baseline 3 applies — the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Structure of one ILOSTAT dataflow') and enumerates exactly what is returned: dimensions in SDMX key order, codelists, time dimension, default selection, and data vintage. It explicitly distinguishes itself from ilo_get_data (values) and ilo_list_dimension_values (codes).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit sequencing guidance ('Use before ilo_get_data to know which filters exist') and two clear exclusions with the correct alternative named for each ('does not return statistical values', 'does not list the codes themselves (use ilo_list_dimension_values)'). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ilo_list_dimension_valuesList valid codes of a dimensionARead-onlyIdempotentInspect
Valid codes (id + label) of one dimension of an ILOSTAT dataflow — e.g. the country/area codes of REF_AREA (ISO 3166-1 alpha-3 such as BRA, plus X-codes for aggregates such as X01 World) or the categories of SEX (SEX_T/SEX_M/SEX_F) and AGE. Use search to resolve a name to a code (e.g. search "Brazil") instead of paging through hundreds of codes; codelists are shared across dataflows, so a code found here is valid wherever the same codelist is used. Use to build correct ilo_get_data filters. Does not return statistical values, does not say which codes actually have data for a given dataflow, and is not applicable to the time dimension (filter it via start_period/end_period in ilo_get_data).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum codes returned (default 200) | |
| offset | No | Codes to skip, for pagination (default 0) | |
| search | No | Case-insensitive filter on code id or label | |
| dataflow | Yes | Dataflow id the dimension belongs to | |
| dimension | Yes | Dimension id from ilo_get_indicator_metadata (e.g. "REF_AREA", "SEX") | |
| provenance_mode | No | Provenance verbosity: 'concise' (default — source, url, vintage, retrieval date, citation, license) or 'detailed' (full canonical block with dataset, dimension key and notices) |
Output Schema
| Name | Required | Description |
|---|---|---|
| offset | Yes | |
| values | Yes | |
| showing | Yes | |
| codelist | Yes | |
| dataflow | Yes | |
| has_more | Yes | |
| dimension | Yes | |
| provenance | Yes | |
| attribution | Yes | |
| next_offset | No | |
| total_codes | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so safety is covered. The description adds real behavioral scope beyond them: codelists are shared across dataflows, results exclude statistical values, and codes do not indicate which have data. It stops short of describing pagination defaults or the raw return shape, so a 4 rather than 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then usage, then exclusions, in a compact block with no filler sentences. It is dense and runs long in a single paragraph, which slightly reduces scanability, but each clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and annotations covering safety, the description only needed to supply scope and exclusions — which it does: shared codelists, absence of data-availability info, and the time-dimension carve-out. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so limit/offset/search/dataflow/dimension/provenance_mode are already documented in the schema; baseline 3 applies. The description adds the intent of `search` (name-to-code resolution) and implies paging, but no syntax or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Valid codes (id + label) of one dimension of an ILOSTAT dataflow') and grounds it with concrete examples (REF_AREA ISO 3166-1 alpha-3, X-codes, SEX_T/SEX_M/SEX_F, AGE). An agent can distinguish this from ilo_get_data and search without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use `search` to resolve a name to a code instead of paging, and use this to build ilo_get_data filters. It also names an exclusion ('not applicable to the time dimension — filter it via start_period/end_period in ilo_get_data'), which is exactly the when-not guidance the dimension calls for.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ilo_search_indicatorsSearch ILOSTAT indicatorsARead-onlyIdempotentInspect
Search the ILOSTAT catalogue of ~1,200 indicator dataflows by keywords in the name or id (e.g. "unemployment rate sex age"). All terms must match (AND, case-insensitive), so start with 2–3 English words and drop terms if you get 0 results. Everyday and US wording is resolved to the ILO's own (labor→labour, wages/salary→earnings, informality→informal, gender→sex, productivity→output per worker); when that happens the response says so in vocabulary_notes. Results are ranked by ILO relevance weight, not by match count. Reading the id tells you the shape: suffix _RT = rate/ratio, NB = number (usually thousands); dataflows whose second token starts with 2 (e.g. DF_UNE_2EAP…) are ILO modelled estimates with full country/year coverage, the others are reported national data. Returns dataflow ids to use with ilo_get_data / ilo_get_indicator_metadata. Searches the local catalogue only — it does not return statistical values (use ilo_get_data), does not search dimension codes such as countries (use ilo_list_dimension_values) and does not cover non-ILO sources.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results (default 20) | |
| query | Yes | Keywords, matched against dataflow name and id (AND between terms) | |
| offset | No | Results to skip, for pagination (default 0) | |
| provenance_mode | No | Provenance verbosity: 'concise' (default — source, url, vintage, retrieval date, citation, license) or 'detailed' (full canonical block with dataset, dimension key and notices) |
Output Schema
| Name | Required | Description |
|---|---|---|
| hint | No | |
| offset | Yes | |
| showing | Yes | |
| has_more | Yes | |
| indicators | Yes | |
| provenance | Yes | |
| attribution | Yes | |
| next_offset | No | |
| total_matches | Yes | |
| vocabulary_notes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
While annotations already establish readOnly/idempotent/non-destructive behavior, the description goes well beyond them by disclosing AND semantics, case-insensitivity, synonym resolution, relevance ranking, id-shape conventions (_RT, _NB, 2-token modelled estimates), and vocabulary_notes behavior. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place: purpose, query strategy, vocabulary behavior, ranking, id interpretation, and exclusions are all covered without redundancy. The core purpose is front-loaded before the detailed guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given output schema exists and the tool is a read-only search, the description covers everything an agent needs: what it searches, how to construct queries, how results are ordered, how to interpret ids, and which sibling tools to use for adjacent tasks. Nothing important is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema: it explains query semantics with examples, vocabulary mapping, ranking behavior, and how to interpret result ids. It doesn't add much about limit/offset/provenance_mode, but those are already fully described in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Search the ILOSTAT catalogue of ~1,200 indicator dataflows by keywords in the name or id.' It also differentiates itself from siblings by explicitly stating what it does not do: no statistical values, no dimension-code search, no non-ILO sources.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage guidance is explicit and actionable: start with 2–3 English words, drop terms on zero results, and use alternatives for other needs (ilo_get_data for values, ilo_list_dimension_values for countries). It also names sibling tools directly, so an agent knows exactly when to choose this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchDeep Research SearchARead-onlyIdempotentInspect
Searches the ILOSTAT labour statistics (≈1,200 SDMX dataflows: employment, unemployment, wages, working time, informality, SDG labour indicators) catalog and returns up to 10 matching documents as { id, title, url }, ordered by relevance (an empty list means nothing matched).
This tool exists for the OpenAI Deep Research contract: ChatGPT deep research, company knowledge and research workflows over the Responses API require exactly the tools search and fetch. Pass one of the returned ids to fetch to read the document.
For direct questions and for data (values, series, rankings) prefer the ilo_* tools, which return the actual data with provenance — this is a catalog index, not a data query.
Query: natural language or keywords, Portuguese or English; accents and case are ignored.
Behavior: read-only and idempotent — the catalog comes from the public source and is cached in memory.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search terms, natural language or keywords (accents and case are ignored) |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | Matching documents, in relevance order |
| provenance | Yes | |
| attribution | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavioral context beyond annotations: read-only and idempotent, backed by an in-memory cache of the public catalog, empty list means no match, and results are relevance-ordered. It also clarifies that this is a catalog index rather than a data-returning endpoint, which is essential for correct agent expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence carries useful information: purpose, output format, ordering, match behavior, tool routing, language/query semantics, and behavioral guarantees. It is detailed yet tightly organized, front-loading the core capability and then providing the operational context an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers what an agent needs to correctly select and invoke this tool: what it searches, what it returns, how results are ordered, how to consume results via fetch, when to prefer sibling tools, and how the query parameter behaves. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, and the description goes further by explaining query semantics: 'natural language or keywords, Portuguese or English; accents and case are ignored.' This adds practical information that the schema alone does not fully convey about acceptable input formats.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and resource: 'Searches the ILOSTAT labour statistics catalog and returns up to 10 matching documents as { id, title, url }, ordered by relevance.' It clearly distinguishes its role from the sibling fetch tool (which reads documents by id) and the ilo_* data tools, so an agent can tell them apart immediately.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage guidance is explicit: 'For direct questions and for data (values, series, rankings) prefer the ilo_* tools... this is a catalog index, not a data query.' It also states when to use fetch by passing the returned id, and mentions the Deep Research contract context. This is strong routing guidance with no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.6.0- Added
fetch - Changed
ilo_get_data8 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / filters / descriptionPrevious value: -"Dimension id → code or list of codes (from ilo_list_dimension_values). REF_AREA is required (up to 30 area codes)."New value: +"Dimension id → code or list of codes (from ilo_list_dimension_values). REF_AREA is required (up to 30 area codes); any other dimension is optional and, left out, returns all of its categories." - added
Input schema / properties / filters / propertiesAdded value: +{ + "REF_AREA": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + } + ], + "description": "Area codes — REQUIRED, at most 30 per call (e.g. [\"BRA\",\"ARG\"]). Without them the ILO gateway times out (HTTP 504). Discover codes with ilo_list_dimension_values (dimension REF_AREA)." + } +} - removed
Input schema / properties / filters / propertyNamesRemoved value: -{ - "type": "string" -} - added
Input schema / properties / filters / requiredAdded value: +[ + "REF_AREA" +] - changed
Input schema / requiredPrevious value: -[ - "dataflow" -]New value: +[ + "dataflow", + "filters" +] - removed
Output schema / properties / dataflow / properties / name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / dataflow / properties / name / typeAdded value: +[ + "string", + "null" +]
- Changed
ilo_get_indicator_metadata9 fields changed- added
Input schema / additionalPropertiesAdded value: +false - removed
Output schema / properties / data_vintage / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / data_vintage / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / dimensions / items / properties / codelist / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / dimensions / items / properties / codelist / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / time_dimension / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / time_dimension / typeAdded value: +[ + "string", + "null" +]
- Changed
ilo_list_dimension_values3 fields changed- added
Input schema / additionalPropertiesAdded value: +false - removed
Output schema / properties / values / items / properties / name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / values / items / properties / name / typeAdded value: +[ + "string", + "null" +]
- Changed
ilo_search_indicators3 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Output schema / properties / hintAdded value: +{ + "type": "string" +} - added
Output schema / properties / vocabulary_notesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +}
- Added
search
4 tool updates
v0.1.0- First observed
ilo_get_data - First observed
ilo_get_indicator_metadata - First observed
ilo_list_dimension_values - First observed
ilo_search_indicators
TDQS
Scored across 6 tools
The data tools (ilo_search_indicators, ilo_get_indicator_metadata, ilo_list_dimension_values, ilo_get_data) are clearly separated by function. The generic search and fetch tools are distinct from each other, but search and ilo_search_indicators both search the same catalog, creating mild overlap; their descriptions do clarify that one serves document retrieval and the other serves dataflow discovery.
The four ilo_* tools follow a consistent verb-based pattern with underscores, but the unprefixed generic tools search and fetch break the convention. The mixed prefixing is explainable but inconsistent across the set.
Six tools is a well-scoped size for the server's purpose: discovery, metadata inspection, dimension lookup, and data retrieval each have a dedicated tool. No tool feels redundant or unnecessary for the stated domain.
The tool set covers the full workflow needed to query ILOSTAT data: search for indicators, inspect metadata, list valid dimension codes, and retrieve data values. The search/fetch pair also enables document-level research, so there are no obvious dead ends in the intended use cases.
Maintenance
Related MCP Connectors
ILOSTAT (International Labour Organization statistics) MCP — global labour
DBnomics MCP — meta-aggregator over 80+ stats providers
Statistics Netherlands (CBS / StatLine) OData MCP.
MCP server for Statistics Sweden (SCB) - 1200+ tables with population, economy, environment data
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server for official statistics from Statistics Finland (Tilastokeskus) — the StatFin database, exposed through the PxWeb API. Search 3000+ tables, inspect their dimensions, and pull data as JSON-stat2.435 npmMIT
- FlicenseNot gradedqualityDmaintenanceProvides macro-economic data from World Bank, IMF, FRED, OECD, and RBI through a unified get_indicator interface.1-
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol (MCP) server that provides tools to query Eurostat APIs for European statistics data.10MIT
- AlicenseNot gradedqualityAmaintenanceSearch and query the Eurostat catalogue — EU economy, demography, trade, health, and NUTS regional data via MCP with 5 tools and 1 resource.421 npm6Apache 2.0