Skip to main content
Glama

ilostat-mcp-server

Query ILOSTAT observations

ilostat_query_indicator
Read-onlyIdempotent

Fetch observations for up to 3 ILOSTAT datasets per call, filtered by reference area or area group, sex, breakdown codes (classif1, classif2), source, and period; datasets past the first 3 come back as remaining_dataset_ids for another call. Rows keep their source, observation status, decoded notes, and a basis — reported, modelled_estimate, or projection — and the response echoes every filter applied, including the best-source default. Codes are checked against ILOSTAT's dictionaries before the request is sent: ilostat_list_reference lists valid codes and ilostat_describe_indicator lists the codes a dataset actually uses. A result larger than the inline preview is staged in full as a df_ dataframe for SQL through ilostat_dataframe_describe and ilostat_dataframe_query when this deployment enables dataframes. A request with no filters at all is refused when the dataset exceeds the row ceiling, and a filtered request that still exceeds it is refused with guidance to narrow it.

Input Schema

NameRequiredDescriptionDefault
sexNoSex codes SEX_T, SEX_M, SEX_F, SEX_O; T/M/F/O and total/both/male/female/other are accepted.
timeNoOne exact period: YYYY (on a quarterly or monthly dataset, every period of that year), YYYYQn, or YYYYMmm; 2024-Q2, 2024 Q2, and 2025-03 are normalized. Not combinable with time_from, time_to, or latest_only.
sourcesNoSource codes (e.g. BA:453); ilostat_list_reference topic sources with ref_area lists an area's sources. Without source_selection, setting sources switches it to all, since a secondary source matches nothing under best.
time_toNoLast year (YYYY), not before time_from.
classif1NoCodes of the first breakdown (e.g. AGE_YTHADULT_YGE15); case-insensitive. ilostat_describe_indicator lists the codes a dataset uses.
classif2NoCodes of the second breakdown, for datasets that have one; case-insensitive. ilostat_describe_indicator lists them.
ref_areasNoReference areas (up to 300): ISO3 country codes (USA) or X-coded aggregates (X01 World); case-insensitive, ILO_GEO_ forms accepted. Aggregates need a dataset with has_aggregates true. Omit for every area.
time_fromNoFirst year (YYYY); upstream filters by year only.
area_groupNoX01 for every country, an ILO region or subregion, or a World Bank income group (X06, X56, X02, …); expands to its member countries, unioned with ref_areas. ilostat_list_reference topic area_groups lists the codes.
dataset_idsYesDataset IDs, up to 100 — an indicator code plus _A, _Q, or _M (UNE_DEAP_SEX_AGE_RT_A), as ilostat_search_indicators returns them. Case-insensitive; a DF_ prefix (the SDMX dataflow form) is stripped, a bare indicator code resolves when it has one frequency, an element holding + or , joined IDs is split, and duplicates are dropped. Every ID is checked; the first 3 distinct datasets are queried and the rest come back as remaining_dataset_ids for another call.
latest_onlyNoOnly each series' latest period — a series is one dataset, reference area, sex, classif1, and classif2 code — with every row at that period (one per source under source_selection all or secondary; the latest quarter or month on sub-annual datasets); combines with time_from/time_to. When the request's full history is too large or too slow to read in one call, it falls back to the latest period per area and dataset, which can miss a series that ends earlier; applied_filters.latest_mode says which ran, and the notice says so. time_from or classif1 narrow it back under; sex or fewer ref_areas alone may still read too much and fall back.
source_selectionNobest (default): the preferred source per area and period; all: secondary sources too, each row flagged best_source; secondary: secondary sources only. Defaults to all when sources is set.

Output Schema

NameRequiredDescriptionDefault
capNoInline preview budget, in serialized characters.
rowsNoInline preview rows; the staged dataframe holds every row when the result is larger.
errorNoPresent when the call failed. Absent on success.
shownNoRows returned inline.
legendNoLabels for every code in rows.
noticeNoDatasets left for another call, a latest_only fallback to the latest period per area and dataset, filters that could not narrow a dataset, a unit the structure service could not supply, why nothing matched, where the full result is staged, or why reading stopped early.
summaryNoSummary over every row read, not just the preview.
datasetsNoThe queried datasets, in request order.
dataframeNoThe staged dataframe holding the full result; present only when staged.
row_countNoRows the request returned — exact unless reading stopped early at the inline preview (summary.complete false), when it counts the rows read.
truncatedNoTrue when reading stopped at the inline preview and more rows exist.
attributionNoCitation to keep with any use of the data.
applied_filtersNoEvery parameter sent upstream, defaults included.
remaining_dataset_idsNoRequested datasets past the first 3, as canonical IDs in request order; not fetched in this call. Pass them as dataset_ids with the same filters to get them. Absent when every requested dataset was queried.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed12 schema fields changed
    • changedInput schema / properties / dataset_ids / description
      Previous value: -"One to three dataset IDs — an indicator code plus _A, _Q, or _M (UNE_DEAP_SEX_AGE_RT_A), as ilostat_search_indicators returns them. Case-insensitive; a DF_ prefix (the SDMX dataflow form) is stripped, a bare indicator code resolves when it has one frequency, and an element holding + or , joined IDs is split."New value: +"Dataset IDs, up to 100 — an indicator code plus _A, _Q, or _M (UNE_DEAP_SEX_AGE_RT_A), as ilostat_search_indicators returns them. Case-insensitive; a DF_ prefix (the SDMX dataflow form) is stripped, a bare indicator code resolves when it has one frequency, an element holding + or , joined IDs is split, and duplicates are dropped. Every ID is checked; the first 3 distinct datasets are queried and the rest come back as remaining_dataset_ids for another call."
    • changedInput schema / properties / dataset_ids / maxItems
      Previous value: -3New value: +100
    • changedInput schema / properties / latest_only / description
      Previous value: -"Only the latest period per reference area and dataset (the latest quarter or month on sub-annual datasets); combines with time_from/time_to."New value: +"Only each series' latest period — a series is one dataset, reference area, sex, classif1, and classif2 code — with every row at that period (one per source under source_selection all or secondary; the latest quarter or month on sub-annual datasets); combines with time_from/time_to. When the request's full history is too large or too slow to read in one call, it falls back to the latest period per area and dataset, which can miss a series that ends earlier; applied_filters.latest_mode says which ran, and the notice says so. time_from or classif1 narrow it back under; sex or fewer ref_areas alone may still read too much and fall back."
    • changedOutput schema / properties / applied_filters / properties / dataset_ids / description
      Previous value: -"Datasets requested."New value: +"Datasets queried — at most 3."
    • addedOutput schema / properties / applied_filters / properties / latest_mode
      Added value: +{
      +  "description": "How latest_only was answered: per_series, each series' own latest period; latest_year, the fallback to the latest period per area and dataset, which can miss a series that ends earlier. Absent without latest_only.",
      +  "enum": [
      +    "per_series",
      +    "latest_year"
      +  ],
      +  "type": "string"
      +}
    • changedOutput schema / properties / applied_filters / properties / latest_only / description
      Previous value: -"Whether only the latest period per area was requested."New value: +"Whether only each series' latest period was requested."
    • changedOutput schema / properties / datasets / description
      Previous value: -"The requested datasets, in request order."New value: +"The queried datasets, in request order."
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `unknown_dataset`: A dataset ID is not in the catalog, or a bare indicator code has several frequencies. `unknown_code`: A ref_areas, sex, classif1, classif2, or sources value is not in the ILOSTAT dictionaries. `unknown_area_group`: area_group is not X01, an ILO region or subregion, or a World Bank income group. `aggregates_unavailable`: An X-coded aggregate was requested from a dataset that has no aggregate rows. `invalid_period`: A sub-annual time on a dataset of another frequency, time combined with a range or latest_only, or time_from after time_to. `request_too_broad`: No filters at all and the unfiltered size exceeds the row ceiling. `result_too_large`: The filtered result passed the row ceiling while streaming. `dataset_retired`: Upstream reports a catalog dataset ID as deprecated or invalid. `upstream_busy`: The server-side pacer shed the call, or the ILOSTAT API answered 429 or a challenge page. `catalog_unavailable`: No catalog snapshot has loaded and the load failed, or the first load has not finished within 30 s. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `unknown_dataset`: Any dataset ID in dataset_ids, queried or not, is not in the catalog, or a bare indicator code has several frequencies. `unknown_code`: A ref_areas, sex, classif1, classif2, or sources value is not in the ILOSTAT dictionaries. `unknown_area_group`: area_group is not X01, an ILO region or subregion, or a World Bank income group. `aggregates_unavailable`: An X-coded aggregate was requested from a dataset that has no aggregate rows. `invalid_period`: A sub-annual time on a dataset of another frequency, time combined with a range or latest_only, or time_from after time_to. `request_too_broad`: No filters at all and the unfiltered size exceeds the row ceiling. `result_too_large`: The filtered result passed the row ceiling while streaming. `dataset_retired`: Upstream reports a catalog dataset ID as deprecated or invalid. `upstream_busy`: The server-side pacer shed the call, or the ILOSTAT API answered 429 or a challenge page. `catalog_unavailable`: No catalog snapshot has loaded and the load failed, or the first load has not finished within 30 s. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / notice / description
      Previous value: -"Filters that could not narrow a dataset, a unit the structure service could not supply, why nothing matched, where the full result is staged, or why reading stopped early."New value: +"Datasets left for another call, a latest_only fallback to the latest period per area and dataset, filters that could not narrow a dataset, a unit the structure service could not supply, why nothing matched, where the full result is staged, or why reading stopped early."
    • addedOutput schema / properties / remaining_dataset_ids
      Added value: +{
      +  "description": "Requested datasets past the first 3, as canonical IDs in request order; not fetched in this call. Pass them as dataset_ids with the same filters to get them. Absent when every requested dataset was queried.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / properties / row_count / description
      Previous value: -"Rows the request returned — exact when the result is inline or staged; when reading stopped early (truncated), the rows read."New value: +"Rows the request returned — exact unless reading stopped early at the inline preview (summary.complete false), when it counts the rows read."
    • changedOutput schema / properties / summary / properties / complete / description
      Previous value: -"False when reading stopped early, so the counts cover only the rows read."New value: +"False when reading stopped early at the inline preview, so the counts cover only the rows read."
  2. First observed

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well past the readOnly/openWorld/idempotent annotations: discloses the 3-dataset ceiling, the df_<id> staging path, the row-ceiling refusal behavior for unfiltered vs filtered requests, and the latest_only fallback that can miss earlier-ending series. These are exactly the operational traits an agent cannot infer from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core operation before limits, validation, and staging behavior, and every clause carries operational information. It is a dense single block, but the density tracks the tool's real complexity rather than padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

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 no explanation, and the description still covers the parts that matter: dataset caps, filter echoing, code validation, refusal thresholds, and dataframe staging. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 12 parameters in depth. The description mostly restates the filter axes and the best-source default rather than adding syntax or semantics beyond the schema; baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource+scope ('Fetch observations for up to 3 ILOSTAT datasets per call') and enumerates the filter axes. It is clearly distinguishable from siblings, naming list_reference, describe_indicator, and the dataframe tools in their supporting roles.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete routing: use ilostat_list_reference for valid codes, ilostat_describe_indicator for codes a dataset actually uses, and dataframe tools when a result is staged as df_<id>. It also explains the 3-dataset cap and the remaining_dataset_ids continuation. It stops short of stating when to prefer ilostat_compare_geographies or ilostat_get_country_profile instead, so no explicit exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.