unhcr-refugees-mcp-server
Server Details
Query UNHCR refugee, IDP, and stateless populations, asylum decisions, returns, and resettlement.
- Status
- Healthy
- Uptime
- 99.9% over 25 days
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- Repository
- cyanheads/unhcr-refugees-mcp-server
- GitHub Stars
- 1
- Server Listing
- @cyanheads/unhcr-refugees-mcp-server
TDQS
Scored across 8 tools
Each unhcr_get_* tool targets a distinct UNHCR dataset (applications, decisions, demographics, population, solutions), and descriptions explicitly cross-reference to prevent confusion between similar-sounding pairs like population vs demographics and applications vs decisions. The two dataframe tools (describe vs query) are cleanly separated by read-schema vs run-SQL. Only minor residual overlap between the asylum applications/decisions and population/demographics pairs keeps it from a perfect score.
All tools share a consistent unhcr_ prefix followed by a predictable verb_noun pattern: unhcr_get_*, unhcr_list_*, and unhcr_dataframe_describe/query. The convention is uniform and readable throughout.
Eight tools is well-scoped for a domain-focused data retrieval server: five get_* dataset accessors, a reference decoder, and two dataframe helpers that generalize any dataset spanning them. Each tool earns its place without redundancy.
The surface covers the core UNHCR data domains (applications, decisions, demographics, population stocks, solutions), a reference/code-lookup tool for the controlled vocabularies, and a describe/query pair that lets agents work with any staged dataset. As a read-only data service, no create/update/delete operations are needed, so there are no obvious dead ends.
Available Tools
8 toolsunhcr_dataframe_describeDescribe staged dataframesARead-onlyIdempotentInspect
Describe a dataframe (df_XXXXX_XXXXX) staged by the unhcr_get_* tools — any response carrying a dataset handle staged its full result here — or list them all where this deployment allows listing. Each entry gives the source tool, query parameters, creation and expiry time, row count, whether the upstream fetch was complete, and the column schema. Read the columns here before writing SQL for unhcr_dataframe_query.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | One dataframe to describe, as df_XXXXX_XXXXX (uppercase letters and digits): the dataset.name a unhcr_get_* result returned, or a register_as name. Omit to list every staged dataframe; a deployment that serves unauthenticated callers over HTTP turns listing off, and there the name is required. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when nothing is staged or the named dataframe is gone. |
| dataframes | No | Staged dataframes, newest first. Empty when none are staged. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/openWorldHint=false, so the safety profile is covered. The description adds genuinely new behavior: staged results expire, listing can be disabled in unauthenticated HTTP deployments, and completeness of the upstream fetch is surfaced. It does not mention pagination or size limits, keeping it short of a 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?
Purpose is front-loaded and the two sentences are dense with useful content. The opening sentence is somewhat convoluted with its em-dash aside ('— any response carrying a dataset handle staged its full result here —'), but nothing is padding.
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 values needn't be explained, yet the description enumerates the entry fields (source tool, query params, creation/expiry, row count, completeness, column schema), which is slightly redundant but helps the agent know what it can rely on. The full describe→query workflow is covered.
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% and the single 'name' parameter is fully documented in the schema, including the pattern and the listing fallback. The description restates the staging origin (dataset.name / register_as name) and the listing-off case rather than adding new syntax or format detail, so the baseline 3 applies.
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 ('Describe a dataframe (df_XXXXX_XXXXX) staged by the unhcr_get_* tools') and explicitly names the sibling it feeds into (unhcr_dataframe_query). An agent can distinguish it from the query tool and from the unhcr_get_* producers without opening any 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?
Gives explicit routing: 'Read the columns here before writing SQL for unhcr_dataframe_query.' It also states the when-not condition — omit name to list, but name is required in deployments serving unauthenticated HTTP callers. Both the action and the exclusion are spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unhcr_dataframe_queryQuery staged dataframesARead-onlyIdempotentInspect
Run a single-statement SELECT against the dataframes staged by the unhcr_get_* tools. Check a dataframe’s columns with unhcr_dataframe_describe first. Writes, DDL, DROP, COPY, PRAGMA, ATTACH, file references, external-data functions, engine introspection, dynamic table lookup, and system catalogs are rejected; query() accepts only a single SQL string literal, checked by the same gate. Optional register_as saves the result as a new dataframe with a fresh TTL. Recompute rates from summed counts rather than averaging rate columns.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | One DuckDB SELECT against df_XXXXX_XXXXX tables, at most 20,000 characters — joins, aggregates, window functions, and CTEs work. SUM and COUNT results come back as JSON strings (BIGINT); CAST(… AS DOUBLE) for inline arithmetic. Staged tables add origin/asylum UNHCR and UN region columns for regional GROUP BY. | |
| preview | No | Requested inline rows, 0–1000000. Values above row_limit are clamped to row_limit with disclosure. Omit to return every row up to row_limit; the serialized byte ceiling may withhold whole rows or metadata, preserving the result as lossless chunks. | |
| row_limit | No | Most rows the query materializes (1–10000, default 1000). When more match, row_count_capped is true; use register_as to keep the full result. | |
| register_as | No | Save the result as a new dataframe under this name (df_ plus two groups of 5 uppercase letters or digits, e.g. df_ABCDE_12345) with a fresh TTL, to chain analyses. The name must not already be staged. The saved rows count toward the 1,000,000-row staging budget: the oldest other dataframes are evicted to make room, and a result larger than the budget is not saved. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The cap that bound: preview when lower than row_limit, otherwise row_limit. |
| rows | No | Result rows, one object per row keyed by column name, bounded by preview and row_limit. BIGINT values (SUM, COUNT) arrive as strings. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Rows returned inline. |
| notice | No | Guidance when the query returned no rows or rows were withheld. |
| columns | No | Column names in projection order. |
| evicted | No | Older dataframes dropped, oldest first, to keep the staged total within the 1,000,000-row budget once register_as saved this result; they can no longer be queried. Present only when any were evicted. |
| complete | No | False when any original source fetch was incomplete, independently of display truncation. |
| row_count | No | Rows the query produced, up to row_limit. When row_count_capped is true this is the cap, not the full size. |
| truncated | No | True when rows were withheld by a cap. |
| expires_at | No | ISO 8601 expiry of the new dataframe, when register_as was set. |
| attribution | No | Attribution UNHCR requires when these figures are reused. |
| registered_as | No | The new dataframe, when register_as was set. |
| result_chunks | No | Lossless materialized result snapshot when the byte ceiling withholds rows or metadata. |
| row_count_capped | No | True when more rows matched than row_limit allowed through. |
| metadata_withheld | No | True when oversized column names or incomplete-source metadata are withheld inline and retained in result_chunks. |
| incomplete_sources | No | Original incomplete upstream dataframe handles; retained through saved descendants. If metadata_withheld is true, consult the result snapshot for the full list. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/idempotentHint/openWorldHint, but the description adds substantial non-obvious behavior: the full list of rejected statements (writes, DDL, COPY, PRAGMA, ATTACH, external functions, catalogs), the single-string-literal gate, and register_as semantics including fresh TTL, 1M-row staging budget, eviction of the oldest dataframes, and non-save on overflow.
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 the core action, then the prerequisite, then constraints, then the optional save behavior. The enumeration of rejected constructs is long but each item carries real invocation-relevant information; only the trailing rate-recomputation sentence feels like an aside rather than core 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?
With an output schema present, return values need no explanation, and the description still covers constraints, prerequisites, staging-budget side effects, and the chaining path via register_as. An agent has everything needed to call this correctly and safely.
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 the parameter descriptions are already rich (clamping behavior, row_limit caps, register_as naming pattern). The description's mention that register_as 'saves the result as a new dataframe with a fresh TTL' largely restates the schema, so it adds only marginal meaning; the rate-averaging note is analytical advice rather than parameter semantics.
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 ('Run a single-statement SELECT against the dataframes staged by the unhcr_get_* tools') and scopes it to staged dataframes, which cleanly separates it from the unhcr_get_* staging tools and unhcr_dataframe_describe.
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 prescribes the workflow order explicitly ('Check a dataframe's columns with unhcr_dataframe_describe first') and names register_as as the path for chaining analyses. The rejection list tells the agent precisely which SQL constructs will fail, which is actionable when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unhcr_get_asylum_applicationsUNHCR asylum applicationsARead-onlyIdempotentInspect
Get asylum applications lodged per year (2000 to the latest year) by country of origin and/or asylum, split by default into application stage — new, repeat, appeal, and the combined stages some countries report — so new claims are not added to appeals of old ones. Counts given as cases are never added to counts of persons; each row states its unit. Decode stage, authority, and decision-level codes with unhcr_list_reference (topic asylum_codes).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled. | |
| stage | No | Stage the full result as a dataframe even when it fits inline, e.g. to join it with another unhcr_get_* result in unhcr_dataframe_query. Where dataframes are unavailable it is ignored, and the notice says so. | |
| asylum | No | Country of asylum — where people sought or hold protection; for returns, the country they returned from — as ISO3 codes (DEU, TUR), case-insensitive, same rules as origin. At most 50 codes. Omit to sum every asylum country into one row, or set expand to list each. | |
| expand | No | List every country, one row each, for a dimension that origin or asylum leaves unfiltered: origin, asylum, or both. Default none, where an unfiltered dimension is summed into one row. Expanding a dimension that origin or asylum already filters is rejected. | none |
| origin | No | Country of origin — where people fled from — as ISO3 codes (SYR, AFG), case-insensitive. ISO 3166 alpha-2 codes (SY) are rewritten to ISO3 and echoed in applied_scope. UNHCR's own codes (GFR) are rejected with the ISO3 to pass instead, and country names are rejected: resolve a name with unhcr_list_reference (topic countries, name_contains). Each listed code returns its own rows; codes are never summed together. At most 50 codes. Omit to sum every origin into one row, or set expand to list each. | |
| stages | No | Keep only these application stages before summing, case-insensitive: N new, R repeat, A appeal, NA new and appeal together, NR new and repeat together, FA first and appeal, J judiciary, BL backlog, SP subsidiary protection; V and RA appear in the data without a published definition. ["N"] gives new applications only, the basis of UNHCR's headline figure. Omit for every stage. | |
| sort_by | No | Order of the full result before the inline cut. year (default) orders by year, then origin ISO3, then asylum ISO3; applied orders largest first, nulls last. | year |
| year_to | No | Last year of the window. When omitted, the window runs to the latest published year (latest_year in the result). A year past the latest published year is clamped to it and the clamp is reported. | |
| split_by | No | Procedure dimensions kept as separate rows: authority (government, UNHCR, or joint), stage (new, repeat, appeal, …), decision_level. Every dimension left out is summed. Default ["stage"]; [] gives one total per year and scope for each unit. Unit is always kept separate. | |
| year_from | No | First year of the window. When omitted, the window starts at the dataset's first year. A year before the dataset's first year is clamped to it and the clamp is reported; a window entirely outside the dataset's years is rejected. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The limit that was applied. |
| rows | No | Inline rows, sorted, up to limit. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Rows returned inline. |
| notice | No | Guidance for this result: empty-result hints, year clamps, where the full set was staged, or how to narrow. |
| dataset | No | Present when the full result was staged as a dataframe; read its columns with unhcr_dataframe_describe, then query it with unhcr_dataframe_query. |
| measure | No | stock: people in a situation on 31 December; flow: events during the year. |
| complete | No | False when the server row cap stopped the upstream fetch; the notice says how many upstream rows were fetched and how to narrow. |
| truncated | No | True when rows were cut at limit. |
| data_notes | No | How to read the counts: stock or flow, rounding, the meaning of null, caveats. |
| total_rows | No | Rows in the full result before the inline cut. When complete is false, the result is built from only the upstream rows fetched before the cap, so rows and summed counts can fall short of the complete result. |
| attribution | No | Attribution UNHCR requires when these figures are reused. |
| latest_year | No | Newest year this dataset publishes. |
| applied_scope | No | The scope actually sent upstream, including clamped years and code rewrites. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly, idempotent, open-world, so safety is covered. The description adds real behavioral value beyond that: it warns that case counts are never added to person counts and that each row declares its unit, and that the stage split prevents new claims being merged into old appeals. It omits discussion of rate limits or response size behavior, which the schema covers.
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, front-loaded with purpose and scope, then the unit-mixing caveat, then the helper-tool pointer. Dense but every sentence carries a distinct payload; the first sentence is long but earns its length by packing scope, dimension, and default behavior.
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 10-parameter, all-optional aggregation tool with a full output schema and supporting annotations, the description supplies the key interpretive context (unit mixing, default stage split, code decoding). Nothing critical is missing, though it does not touch on the clamping/disclosure mechanics that an agent would only learn from the schema.
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 the schema already documents all ten parameters in detail (clamping, expand rejection rules, ISO3 rewriting, enum codes). The description reinforces the default stage split and code-decoding path but adds little syntax or format meaning beyond what the schema states; baseline 3 applies.
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 ('Get asylum applications lodged per year'), gives the temporal scope (2000 to latest), and names the two filterable dimensions (origin and/or asylum). An agent can distinguish this from the sibling unhcr_get_asylum_decisions purely from the description.
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?
Explains the default splitting behavior and routes to unhcr_list_reference (topic asylum_codes) for decoding stage/authority/decision-level codes, which is actionable context. It does not, however, explicitly say when to prefer this tool over unhcr_get_asylum_decisions or unhcr_get_demographics, so the guidance is clear but not exclusionary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unhcr_get_asylum_decisionsUNHCR asylum decisionsARead-onlyIdempotentInspect
Get asylum decisions per year (2000 to the latest year) by country of origin and/or asylum: recognized as refugees, complementary protection, rejected, and otherwise closed, with UNHCR's Refugee Recognition Rate and Total Protection Rate computed over substantive decisions (otherwise-closed cases excluded). By default all decision levels are summed and each row lists the levels it includes; appeal-stage decisions can concern people already decided at first instance, so split_by decision_level or filter decision_levels to FI for first-instance rates. Cases and persons are never added together.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled. | |
| stage | No | Stage the full result as a dataframe even when it fits inline, e.g. to join it with another unhcr_get_* result in unhcr_dataframe_query. Where dataframes are unavailable it is ignored, and the notice says so. | |
| asylum | No | Country of asylum — where people sought or hold protection; for returns, the country they returned from — as ISO3 codes (DEU, TUR), case-insensitive, same rules as origin. At most 50 codes. Omit to sum every asylum country into one row, or set expand to list each. | |
| expand | No | List every country, one row each, for a dimension that origin or asylum leaves unfiltered: origin, asylum, or both. Default none, where an unfiltered dimension is summed into one row. Expanding a dimension that origin or asylum already filters is rejected. | none |
| origin | No | Country of origin — where people fled from — as ISO3 codes (SYR, AFG), case-insensitive. ISO 3166 alpha-2 codes (SY) are rewritten to ISO3 and echoed in applied_scope. UNHCR's own codes (GFR) are rejected with the ISO3 to pass instead, and country names are rejected: resolve a name with unhcr_list_reference (topic countries, name_contains). Each listed code returns its own rows; codes are never summed together. At most 50 codes. Omit to sum every origin into one row, or set expand to list each. | |
| sort_by | No | Order of the full result before the inline cut. year (default) orders by year, then origin ISO3, then asylum ISO3; a count field orders largest first, nulls last. Rates are not sortable, since rates on small rounded counts would crowd the top. | year |
| year_to | No | Last year of the window. When omitted, the window runs to the latest published year (latest_year in the result). A year past the latest published year is clamped to it and the clamp is reported. | |
| split_by | No | Procedure dimensions kept as separate rows: authority (government, UNHCR, or joint) and decision_level (first instance, administrative review, …). Every dimension left out is summed. Default [] sums all authorities and levels, as UNHCR does for its rates. Unit is always kept separate. | |
| year_from | No | First year of the window. When omitted, the window starts at the dataset's first year. A year before the dataset's first year is clamped to it and the clamp is reported; a window entirely outside the dataset's years is rejected. | |
| decision_levels | No | Keep only these decision levels before summing, case-insensitive: NA new applications, FI first instance, AR administrative review, RA repeat/reopened, IN US Citizenship and Immigration Services, EO US Executive Office for Immigration Review, JR judicial review, SP subsidiary protection, FA first instance and appeal, TP temporary protection, TA temporary asylum, BL backlog, TR temporary leave to remain, CA cantonal regulations (Switzerland). ["FI"] gives first-instance decisions. Omit for every level. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The limit that was applied. |
| rows | No | Inline rows, sorted, up to limit. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Rows returned inline. |
| notice | No | Guidance for this result: empty-result hints, year clamps, where the full set was staged, or how to narrow. |
| dataset | No | Present when the full result was staged as a dataframe; read its columns with unhcr_dataframe_describe, then query it with unhcr_dataframe_query. |
| measure | No | stock: people in a situation on 31 December; flow: events during the year. |
| complete | No | False when the server row cap stopped the upstream fetch; the notice says how many upstream rows were fetched and how to narrow. |
| truncated | No | True when rows were cut at limit. |
| data_notes | No | How to read the counts: stock or flow, rounding, the meaning of null, caveats. |
| total_rows | No | Rows in the full result before the inline cut. When complete is false, the result is built from only the upstream rows fetched before the cap, so rows and summed counts can fall short of the complete result. |
| attribution | No | Attribution UNHCR requires when these figures are reused. |
| latest_year | No | Newest year this dataset publishes. |
| applied_scope | No | The scope actually sent upstream, including clamped years and code rewrites. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, openWorld). The description adds substantive behavioral context beyond them: rates are computed over substantive decisions with otherwise-closed cases excluded, all decision levels are summed by default with each row listing its levels, and cases vs persons are never combined. This materially helps correct interpretation.
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, front-loaded with the resource and scope, then defaults and the important caveat. Dense but every sentence earns its place; the multi-clause sentences are slightly heavy but not wasteful.
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 no explanation. The description supplies the defaults, the rate-computation caveat, and the splitting nuance an agent needs to call this correctly, though pagination/limit behavior is left entirely to the schema.
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 the schema does not: the default summing of decision levels, the risk that appeal-stage rows double-count, and the interplay of split_by/decision_levels for first-instance rates. It does not walk through each parameter, but it enriches the most consequential ones.
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 ('Get asylum decisions per year ... by country of origin and/or asylum') and enumerates the decision categories returned, plus the derived rates. An agent can tell this apart from siblings like unhcr_get_asylum_applications 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?
Gives concrete conditional guidance: use split_by decision_level or filter decision_levels to FI to get first-instance rates, and notes that appeal-stage decisions overlap with first-instance populations. Does not explicitly name sibling tools or exclusions, but the when-to-use context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unhcr_get_demographicsUNHCR demographicsARead-onlyIdempotentInspect
Get UNHCR year-end stocks (2001 to the latest year) broken down by population type, sex, and age band (0–4, 5–11, 12–17, 18–59, 60+, unknown age), by country of origin and/or asylum. Coverage is partial: each row gives the share of its total that UNHCR could disaggregate by sex, and age bands are null where no breakdown exists. These totals come from a separate collection and need not match unhcr_get_population.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled. | |
| stage | No | Stage the full result as a dataframe even when it fits inline, e.g. to join it with another unhcr_get_* result in unhcr_dataframe_query. Where dataframes are unavailable it is ignored, and the notice says so. | |
| asylum | No | Country of asylum — where people sought or hold protection; for returns, the country they returned from — as ISO3 codes (DEU, TUR), case-insensitive, same rules as origin. At most 50 codes. Omit to sum every asylum country into one row, or set expand to list each. | |
| expand | No | List every country, one row each, for a dimension that origin or asylum leaves unfiltered: origin, asylum, or both. Default none, where an unfiltered dimension is summed into one row. Expanding a dimension that origin or asylum already filters is rejected. | none |
| origin | No | Country of origin — where people fled from — as ISO3 codes (SYR, AFG), case-insensitive. ISO 3166 alpha-2 codes (SY) are rewritten to ISO3 and echoed in applied_scope. UNHCR's own codes (GFR) are rejected with the ISO3 to pass instead, and country names are rejected: resolve a name with unhcr_list_reference (topic countries, name_contains). Each listed code returns its own rows; codes are never summed together. At most 50 codes. Omit to sum every origin into one row, or set expand to list each. | |
| sort_by | No | Order of the full result before the inline cut. year (default) orders by year, then origin ISO3, then asylum ISO3, then population type; total orders largest first, nulls last. | year |
| year_to | No | Last year of the window. When omitted, the window runs to the latest published year (latest_year in the result). A year past the latest published year is clamped to it and the clamp is reported. | |
| year_from | No | First year of the window. When omitted, the window starts at the dataset's first year. A year before the dataset's first year is clamped to it and the clamp is reported; a window entirely outside the dataset's years is rejected. | |
| population_types | No | Keep only these population types, case-insensitive: REF refugees, ASY asylum-seekers, OIP other people in need of international protection, IDP internally displaced, STA stateless, OOC others of concern, HST host community, RET returned refugees, RDP returned IDPs. Decode them with unhcr_list_reference (topic population_types). Omit for every type. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The limit that was applied. |
| rows | No | Inline rows, sorted, up to limit. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Rows returned inline. |
| notice | No | Guidance for this result: empty-result hints, year clamps, where the full set was staged, or how to narrow. |
| dataset | No | Present when the full result was staged as a dataframe; read its columns with unhcr_dataframe_describe, then query it with unhcr_dataframe_query. |
| measure | No | stock: people in a situation on 31 December; flow: events during the year. |
| complete | No | False when the server row cap stopped the upstream fetch; the notice says how many upstream rows were fetched and how to narrow. |
| footnotes | No | UNHCR caveats matching rows of the full result, country-specific first; at most 20. |
| truncated | No | True when rows were cut at limit. |
| data_notes | No | How to read the counts: stock or flow, rounding, the meaning of null, caveats. |
| total_rows | No | Rows in the full result before the inline cut. When complete is false, the result is built from only the upstream rows fetched before the cap, so rows and summed counts can fall short of the complete result. |
| attribution | No | Attribution UNHCR requires when these figures are reused. |
| latest_year | No | Newest year this dataset publishes. |
| applied_scope | No | The scope actually sent upstream, including clamped years and code rewrites. |
| footnotes_total | No | Caveats matching the full result, before the cap. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations, the description discloses substantive behavioral traits: coverage is partial, each row carries the share of its total that UNHCR could disaggregate by sex, age bands are null where no breakdown exists, and the figures may diverge from a sibling data source. These caveats materially affect interpretation of results.
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 tightly packed sentences, front-loaded with what the tool returns, then coverage caveats, then the cross-tool reconciliation warning. No sentence is filler and each adds decision-relevant 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?
An output schema exists, so return-value explanation is not the description's burden. For a 9-parameter, all-optional read tool, the description supplies the critical interpretation context (partial disaggregation, null age bands, provenance mismatch) that an agent needs to use results 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 coverage is 100%, so the schema already documents every parameter including code formats, expand semantics, clamping, and sorting. The description adds the year-window origin (2001) and the age-band cohorts, but no syntax or behavior beyond the schema, so the baseline 3 is appropriate.
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 concrete verb and resource (year-end stocks) plus the exact disaggregation dimensions (population type, sex, age band) and the geographic axes (origin and/or asylum), with the time span (2001 to latest). It explicitly names the sibling unhcr_get_population as a distinct data source, so an agent can separate the two 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?
Gives clear context for what the tool is for and warns that its totals come from a separate collection and may not reconcile with unhcr_get_population, which helps an agent choose between them. It stops short of an explicit 'use X instead when you want whole-population totals' rule, so the alternative selection is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unhcr_get_populationUNHCR displacement stocksARead-onlyIdempotentInspect
Get UNHCR year-end displacement stocks (1951 to the latest year) by country of origin and/or asylum: refugees, asylum-seekers, other people in need of international protection, IDPs, stateless people, others of concern, and host communities, plus refugees and IDPs who returned during the year. Stocks count people in a situation on 31 December, not arrivals. Palestine refugees under UNRWA's mandate and IDMC's conflict-IDP estimate are separate series shown beside each row. Set include_nowcast for UNHCR's current-year estimate by asylum country; for sex and age breakdowns, use unhcr_get_demographics.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled. | |
| stage | No | Stage the full result as a dataframe even when it fits inline, e.g. to join it with another unhcr_get_* result in unhcr_dataframe_query. Where dataframes are unavailable it is ignored, and the notice says so. | |
| asylum | No | Country of asylum — where people sought or hold protection; for returns, the country they returned from — as ISO3 codes (DEU, TUR), case-insensitive, same rules as origin. At most 50 codes. Omit to sum every asylum country into one row, or set expand to list each. | |
| expand | No | List every country, one row each, for a dimension that origin or asylum leaves unfiltered: origin, asylum, or both. Default none, where an unfiltered dimension is summed into one row. Expanding a dimension that origin or asylum already filters is rejected. | none |
| origin | No | Country of origin — where people fled from — as ISO3 codes (SYR, AFG), case-insensitive. ISO 3166 alpha-2 codes (SY) are rewritten to ISO3 and echoed in applied_scope. UNHCR's own codes (GFR) are rejected with the ISO3 to pass instead, and country names are rejected: resolve a name with unhcr_list_reference (topic countries, name_contains). Each listed code returns its own rows; codes are never summed together. At most 50 codes. Omit to sum every origin into one row, or set expand to list each. | |
| sort_by | No | Order of the full result before the inline cut. year (default) orders by year, then origin ISO3, then asylum ISO3; a count field orders largest first, nulls last. | year |
| year_to | No | Last year of the window. When omitted, the window runs to the latest published year (latest_year in the result). A year past the latest published year is clamped to it and the clamp is reported. | |
| year_from | No | First year of the window. When omitted, the window starts at the dataset's first year. A year before the dataset's first year is clamped to it and the clamp is reported; a window entirely outside the dataset's years is rejected. | |
| include_nowcast | No | Append UNHCR's latest monthly estimate of refugees and asylum-seekers by asylum country: one current-year snapshot, independent of the year window. The nowcast has no origin dimension, so it is skipped when origin lists codes. When origin lists no codes, a window entirely after the latest published year returns the nowcast alone instead of failing. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The limit that was applied. |
| rows | No | Inline rows, sorted, up to limit. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Rows returned inline. |
| notice | No | Guidance for this result: empty-result hints, year clamps, where the full set was staged, or how to narrow. |
| dataset | No | Present when the full result was staged as a dataframe; read its columns with unhcr_dataframe_describe, then query it with unhcr_dataframe_query. |
| measure | No | stock: people in a situation on 31 December; flow: events during the year. |
| nowcast | No | Present when include_nowcast was set and origin lists no codes. |
| complete | No | False when the server row cap stopped the upstream fetch; the notice says how many upstream rows were fetched and how to narrow. |
| footnotes | No | UNHCR caveats matching rows of the full result, country-specific first; at most 20. |
| truncated | No | True when rows were cut at limit. |
| data_notes | No | How to read the counts: stock or flow, rounding, the meaning of null, caveats. |
| total_rows | No | Rows in the full result before the inline cut. When complete is false, the result is built from only the upstream rows fetched before the cap, so rows and summed counts can fall short of the complete result. |
| attribution | No | Attribution UNHCR requires when these figures are reused. |
| latest_year | No | Newest year this dataset publishes. |
| applied_scope | No | The scope actually sent upstream, including clamped years and code rewrites. |
| footnotes_total | No | Caveats matching the full result, before the cap. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only/idempotent, and the description adds genuinely non-obvious semantics: stocks count people in a situation on 31 December rather than arrivals, UNRWA and IDMC series appear as separate columns beside each row, and nowcast is a current-year snapshot independent of the year window. These are behaviors an agent could not infer from the schema.
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-loads the resource and its scope before moving to caveats and sibling routing. It is a dense paragraph rather than a list, but nearly every clause carries distinct semantic information (stocks-vs-flows, separate series, nowcast, demographics alternative).
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 9 optional parameters, an output schema, and full annotation coverage, the description supplies exactly the missing pieces: what the counts mean, which series are bundled in, and which sibling to use for demographic breakdowns. Return values are covered by the output schema, so nothing essential is absent.
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 baseline is 3; the description still adds meaning by explaining what include_nowcast produces ('UNHCR's current-year estimate by asylum country') and that an unfiltered dimension is summed unless expanded. It does not elaborate on limit/stage/sort_by beyond the schema text.
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?
Opens with a specific verb+resource ('Get UNHCR year-end displacement stocks') and enumerates exactly which series are covered (refugees, asylum-seekers, OIP, IDPs, stateless, OOC, host communities, returns). It also distinguishes itself from siblings by naming unhcr_get_demographics as the tool for sex/age breakdowns.
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 an explicit routing rule: 'for sex and age breakdowns, use unhcr_get_demographics', plus a conditional usage hint for include_nowcast. It does not address overlap with other count-oriented siblings like unhcr_get_solutions or unhcr_get_asylum_applications, so it falls short of full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unhcr_get_solutionsUNHCR durable solutionsARead-onlyIdempotentInspect
Get durable solutions per year (1959 to the latest year) by country of origin and/or asylum: refugees who returned home, refugees resettled to a third country, refugees naturalised, and IDPs who returned. The asylum country means something different per column: the country refugees returned from, the country they were resettled to, the country that naturalised them; IDP returns sit on the origin country itself. Null means the figure was not collected for that country and year, not zero.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled. | |
| stage | No | Stage the full result as a dataframe even when it fits inline, e.g. to join it with another unhcr_get_* result in unhcr_dataframe_query. Where dataframes are unavailable it is ignored, and the notice says so. | |
| asylum | No | Country of asylum — where people sought or hold protection; for returns, the country they returned from — as ISO3 codes (DEU, TUR), case-insensitive, same rules as origin. At most 50 codes. Omit to sum every asylum country into one row, or set expand to list each. | |
| expand | No | List every country, one row each, for a dimension that origin or asylum leaves unfiltered: origin, asylum, or both. Default none, where an unfiltered dimension is summed into one row. Expanding a dimension that origin or asylum already filters is rejected. | none |
| origin | No | Country of origin — where people fled from — as ISO3 codes (SYR, AFG), case-insensitive. ISO 3166 alpha-2 codes (SY) are rewritten to ISO3 and echoed in applied_scope. UNHCR's own codes (GFR) are rejected with the ISO3 to pass instead, and country names are rejected: resolve a name with unhcr_list_reference (topic countries, name_contains). Each listed code returns its own rows; codes are never summed together. At most 50 codes. Omit to sum every origin into one row, or set expand to list each. | |
| sort_by | No | Order of the full result before the inline cut. year (default) orders by year, then origin ISO3, then asylum ISO3; a count field orders largest first, nulls last. | year |
| year_to | No | Last year of the window. When omitted, the window runs to the latest published year (latest_year in the result). A year past the latest published year is clamped to it and the clamp is reported. | |
| year_from | No | First year of the window. When omitted, the window starts at the dataset's first year. A year before the dataset's first year is clamped to it and the clamp is reported; a window entirely outside the dataset's years is rejected. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The limit that was applied. |
| rows | No | Inline rows, sorted, up to limit. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Rows returned inline. |
| notice | No | Guidance for this result: empty-result hints, year clamps, where the full set was staged, or how to narrow. |
| dataset | No | Present when the full result was staged as a dataframe; read its columns with unhcr_dataframe_describe, then query it with unhcr_dataframe_query. |
| measure | No | stock: people in a situation on 31 December; flow: events during the year. |
| complete | No | False when the server row cap stopped the upstream fetch; the notice says how many upstream rows were fetched and how to narrow. |
| footnotes | No | UNHCR caveats matching rows of the full result, country-specific first; at most 20. |
| truncated | No | True when rows were cut at limit. |
| data_notes | No | How to read the counts: stock or flow, rounding, the meaning of null, caveats. |
| total_rows | No | Rows in the full result before the inline cut. When complete is false, the result is built from only the upstream rows fetched before the cap, so rows and summed counts can fall short of the complete result. |
| attribution | No | Attribution UNHCR requires when these figures are reused. |
| latest_year | No | Newest year this dataset publishes. |
| applied_scope | No | The scope actually sent upstream, including clamped years and code rewrites. |
| footnotes_total | No | Caveats matching the full result, before the cap. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly/idempotent/openWorld, so the safety profile is covered. The description adds genuinely useful behavioral context beyond that: the coverage window (1959 to latest), the per-column meaning of the asylum dimension, and the critical null-vs-zero semantics. It does not mention pagination/clamping or rate limits, but the important semantic traps are disclosed.
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, no filler; the scope and the four measures are front-loaded, followed by the two highest-risk clarifications (asylum column semantics, null vs zero). Every sentence carries information an agent would otherwise have to guess.
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, 100% parameter coverage, and read-only annotations, the description only needs to resolve domain traps — and it does: the per-column asylum semantics and null-not-zero convention are the two most likely sources of misinterpretation. Nothing essential for correct invocation 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 baseline is 3, but the description adds real meaning by explaining that 'asylum country' denotes a different country per measure (returned-from, resettlement destination, naturalising country, IDP return on origin) — a semantic distinction the schema only partially implies. It does not elaborate on expand/sort_by/stage behavior, which stays 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?
States a specific verb and resource ('Get durable solutions per year ... by country of origin and/or asylum') and enumerates the four measure columns (returned refugees, resettlement, naturalisation, returned IDPs), which implicitly but clearly separates it from siblings like unhcr_get_population, unhcr_get_asylum_applications, and unhcr_get_demographics. An agent can tell what data this returns without opening the 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?
The description never states when to choose this tool over alternatives or what conditions make it the right call. Routing hints (resolve names via unhcr_list_reference, join via unhcr_dataframe_query) exist only in the parameter schema, not the description, so the description itself offers no usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unhcr_list_referenceUNHCR reference vocabularyARead-onlyIdempotentInspect
Decode the vocabulary the unhcr_* tools take as input: countries (ISO3, ISO2, UNHCR code, names, UNHCR and UN regions), UNHCR’s regional bureaus, each dataset’s first and latest year, population-type definitions, and the asylum authority, stage, decision-level, and unit codes. Filter countries with name_contains to turn a country name into the ISO3 code that origin and asylum take.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | Yes | countries: every queryable country with its codes, names, and regions. regions: UNHCR's regional bureaus. coverage: first and latest year of each dataset, plus the current nowcast month. population_types: what each population type counts and which output column carries it. asylum_codes: asylum authority, application stage, decision level, and unit codes. | |
| name_contains | No | For topic countries only: keep countries whose names, in UNHCR’s English spelling, contain every word given, ignoring case, accents, and punctuation, or whose ISO3, ISO2, or UNHCR code equals a word ("syria", "turkiye", "britain", "deu"). Words match every name variant UNHCR records, including short and formal names the output does not list. No fuzzy matching. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| topic | No | The topic returned. |
| notice | No | Guidance when name_contains matched nothing or does not apply to the topic. |
| nowcast | No | Present for topic coverage: the single current-year estimate unhcr_get_population's include_nowcast returns. |
| regions | No | Present for topic regions. |
| coverage | No | Present for topic coverage. |
| countries | No | Present for topic countries, sorted by name. |
| totalCount | No | Countries returned, after any name_contains filter. |
| asylum_codes | No | Present for topic asylum_codes. |
| population_types | No | Present for topic population_types. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint, so the safety profile is covered. The description adds content-scope context (what vocabulary is decoded) but no behavioral detail such as caching, latency, or result completeness beyond the 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the purpose and followed by the practical name_contains use case. Dense but every clause maps to a covered topic or usage instruction; no filler.
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 unnecessary, and the description covers the topic space and the code-conversion use case. Adequate for a reference-lookup tool, though it could note whether results are static or refreshed.
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 the nested enum descriptions already document each topic exhaustively. The description echoes the topic list and mentions name_contains' role, adding only a light usage hint over what the schema provides, so baseline 3 applies.
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 ('Decode') and resource ('the vocabulary the unhcr_* tools take as input'), then enumerates exactly what it returns: countries/codes, regional bureaus, dataset year coverage, population types, and asylum codes. This clearly distinguishes it from the data-returning siblings like unhcr_get_population or unhcr_get_asylum_applications.
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 a concrete usage path: 'Filter countries with name_contains to turn a country name into the ISO3 code that origin and asylum take,' which tells the agent why it would call this before other tools. It stops short of explicitly stating when NOT to use it or naming a sibling as an alternative.
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.
7 tool updates
- Changed
unhcr_dataframe_describe2 fields changed- added
Output schema / properties / dataframes / items / properties / incomplete_sourcesAdded value: +{ + "description": "Original incomplete upstream handles retained through saved descendants, even after those ancestors expire or are evicted.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / dataframes / items / requiredPrevious value: -[ - "name", - "source_tool", - "query_params", - "created_at", - "expires_at", - "row_count", - "complete", - "providers", - "column_schema" -]New value: +[ + "name", + "source_tool", + "query_params", + "created_at", + "expires_at", + "row_count", + "complete", + "incomplete_sources", + "providers", + "column_schema" +]
- Changed
unhcr_dataframe_query9 fields changed- changed
Input schema / properties / preview / descriptionPrevious value: -"Rows to return inline when that should be fewer than the query materializes, e.g. a small sample while register_as keeps the whole result; a value above row_limit is treated as row_limit. Omit to return every row up to row_limit."New value: +"Requested inline rows, 0–1000000. Values above row_limit are clamped to row_limit with disclosure. Omit to return every row up to row_limit; the serialized byte ceiling may withhold whole rows or metadata, preserving the result as lossless chunks." - changed
Input schema / properties / preview / maximumPrevious value: -10000New value: +1000000 - changed
Output schema / anyOfPrevious value: -[ - { - "not": { - "required": [ - "error" - ] - }, - "required": [ - "columns", - "row_count", - "row_count_capped", - "rows", - "attribution" - ] - }, - { - "required": [ - "error" - ] - } -]New value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "complete", + "incomplete_sources", + "columns", + "row_count", + "row_count_capped", + "rows", + "attribution" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / completeAdded value: +{ + "description": "False when any original source fetch was incomplete, independently of display truncation.", + "type": "boolean" +} - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: Dataframes are turned off in this deployment, or the SQL engine behind them could not load. `missing_table`: A df_<id> the SQL names is not staged: it never existed, its TTL expired, or it was evicted to make room for newer dataframes. `invalid_sql`: A statement starting with SELECT, WITH, or FROM does not parse, or the SELECT fails to prepare: an unknown column, table, or function, or an invalid expression. `sql_execution_error`: The SELECT prepared but failed on the data it read: a cast or conversion that does not fit, an out-of-range value, or invalid input to a function. `register_as_clash`: The register_as name is already a staged dataframe. `register_as_too_large`: The register_as result alone holds more rows than the 1,000,000-row staging budget, so it was not saved. `non_select_statement`: The statement is not a read-only SELECT: an INSERT, UPDATE, DDL, PRAGMA, or other write the engine refuses, or SQL that fails to parse and does not start with SELECT, WITH, or FROM (such as a misspelled SELEC). `multi_statement`: The SQL holds more than one statement. `denied_function`: The SQL calls a file-reading or external-data table function such as read_csv, read_parquet, or glob. `plan_operator_not_allowed`: The query plan uses an operator outside the read-only allowlist, such as the range() or generate_series() table functions. `system_catalog_access`: The SQL references a system catalog (information_schema, pg_catalog, sqlite_master, duckdb_*). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `result_storage_unavailable`: The lossless result snapshot cannot be retained within the staging budget or storage fails. `canvas_unavailable`: Dataframes are turned off in this deployment, or the SQL engine behind them could not load. `missing_table`: A df_<id> the SQL names is not staged: it never existed, its TTL expired, or it was evicted to make room for newer dataframes. `invalid_sql`: A statement starting with SELECT, WITH, or FROM does not parse, or the SELECT fails to prepare: an unknown column, table, or function, or an invalid expression. `sql_execution_error`: The SELECT prepared but failed on the data it read: a cast or conversion that does not fit, an out-of-range value, or invalid input to a function. `register_as_clash`: The register_as name is already a staged dataframe. `register_as_too_large`: The register_as result alone holds more rows than the 1,000,000-row staging budget, so it was not saved. `non_select_statement`: The statement is not a read-only SELECT: an INSERT, UPDATE, DDL, PRAGMA, or other write the engine refuses, or SQL that fails to parse and does not start with SELECT, WITH, or FROM (such as a misspelled SELEC). `multi_statement`: The SQL holds more than one statement. `denied_function`: The SQL calls a denied external-data or introspection function, uses query_table, or passes query() anything other than a single SQL string literal. `plan_operator_not_allowed`: The query plan uses an operator outside the read-only allowlist, such as the range() or generate_series() table functions. `system_catalog_access`: The SQL references a system catalog (information_schema, pg_catalog, sqlite_master, duckdb_*). Other values are possible when a failure originates below the handler." - changed
Output schema / properties / error / properties / data / properties / reason / examplesPrevious value: -[ - "canvas_unavailable", - "missing_table", - "invalid_sql", - "sql_execution_error", - "register_as_clash", - "register_as_too_large", - "non_select_statement", - "multi_statement", - "denied_function", - "plan_operator_not_allowed", - "system_catalog_access" -]New value: +[ + "result_storage_unavailable", + "canvas_unavailable", + "missing_table", + "invalid_sql", + "sql_execution_error", + "register_as_clash", + "register_as_too_large", + "non_select_statement", + "multi_statement", + "denied_function", + "plan_operator_not_allowed", + "system_catalog_access" +] - added
Output schema / properties / incomplete_sourcesAdded value: +{ + "description": "Original incomplete upstream dataframe handles; retained through saved descendants. If metadata_withheld is true, consult the result snapshot for the full list.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / metadata_withheldAdded value: +{ + "description": "True when oversized column names or incomplete-source metadata are withheld inline and retained in result_chunks.", + "type": "boolean" +} - added
Output schema / properties / result_chunksAdded value: +{ + "additionalProperties": false, + "description": "Lossless materialized result snapshot when the byte ceiling withholds rows or metadata.", + "properties": { + "chunk_count": { + "description": "Chunks numbered from zero, with at most 6144 source bytes each.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "encoding": { + "const": "base64-json-utf8", + "description": "Decode each base64 chunk, concatenate bytes in index order, then decode UTF-8 and JSON.", + "type": "string" + }, + "expires_at": { + "description": "ISO 8601 expiry of the chunk dataframe.", + "type": "string" + }, + "name": { + "description": "Tenant-scoped dataframe holding chunk_index and base64 chunk columns.", + "type": "string" + } + }, + "required": [ + "name", + "encoding", + "chunk_count", + "expires_at" + ], + "type": "object" +}
- Changed
unhcr_get_asylum_applications2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Rows returned inline, 1–500 (default 100). Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled."New value: +"Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled." - changed
Input schema / properties / limit / maximumPrevious value: -500New value: +1000000
- Changed
unhcr_get_asylum_decisions2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Rows returned inline, 1–500 (default 100). Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled."New value: +"Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled." - changed
Input schema / properties / limit / maximumPrevious value: -500New value: +1000000
- Changed
unhcr_get_demographics2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Rows returned inline, 1–500 (default 100). Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled."New value: +"Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled." - changed
Input schema / properties / limit / maximumPrevious value: -500New value: +1000000
- Changed
unhcr_get_population2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Rows returned inline, 1–500 (default 100). Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled."New value: +"Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled." - changed
Input schema / properties / limit / maximumPrevious value: -500New value: +1000000
- Changed
unhcr_get_solutions2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Rows returned inline, 1–500 (default 100). Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled."New value: +"Requested inline rows, 1–1000000 (default 100); values above 500 are clamped to 500 with disclosure. Sorting runs over the full result first; a larger result is staged as a dataframe where dataframes are enabled." - changed
Input schema / properties / limit / maximumPrevious value: -500New value: +1000000
8 tool updates
- First observed
unhcr_dataframe_describe - First observed
unhcr_dataframe_query - First observed
unhcr_get_asylum_applications - First observed
unhcr_get_asylum_decisions - First observed
unhcr_get_demographics - First observed
unhcr_get_population - First observed
unhcr_get_solutions - First observed
unhcr_list_reference
Related MCP Connectors
Query UN population data: fertility, mortality, migration, life expectancy for 298 countries.
Query US Census Bureau data: demographics, economics, and housing statistics.
Query IMF SDMX 3.0 macroeconomic dataflows — WEO, BOP, CPI, exchange rates, 190 countries.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides access to UNHCR refugee statistics through a standardized interface, allowing AI agents to query data by country of origin, country of asylum, and year.5MIT
- AlicenseNot gradedqualityBmaintenanceProvides a unified interface to access UNHCR's open data across statistics, RDF, and IATI MCP servers, enabling aggregated queries, cross-domain analytics, and dataset discovery.MIT
- AlicenseAqualityBmaintenanceEnables querying and analyzing humanitarian data, such as refugee statistics, through semantic tools like country comparisons, trend analysis, and report generation, using the UNHCR API.2125 npm1MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to query keyless UNHCR statistics on forcibly displaced and stateless persons, including refugees, asylum seekers, IDPs, and returnees, plus asylum applications and decisions, filtered by year, country of origin, or country of asylum, with a country-code lookup for resolving UNHCR codes. It can be reached through the hosted Pipeworx gateway or run locally over stdio.7 npmMIT
Glama MCP Gateway
Add one secure layer between your agents and this server.