unhcr-refugees-mcp-server

v0.1.1 pre-1.0

Query UNHCR refugee, IDP, and stateless populations, asylum decisions, returns, and resettlement via MCP. STDIO or Streamable HTTP.

unhcr-refugees.caseyjhand.com/mcp
claude mcp add --transport http unhcr-refugees-mcp-server https://unhcr-refugees.caseyjhand.com/mcp
codex mcp add unhcr-refugees-mcp-server --url https://unhcr-refugees.caseyjhand.com/mcp
{
  "mcpServers": {
    "unhcr-refugees-mcp-server": {
      "url": "https://unhcr-refugees.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http unhcr-refugees-mcp-server https://unhcr-refugees.caseyjhand.com/mcp
{
  "mcpServers": {
    "unhcr-refugees-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "https://unhcr-refugees.caseyjhand.com/mcp"
      ]
    }
  }
}
{
  "mcpServers": {
    "unhcr-refugees-mcp-server": {
      "type": "http",
      "url": "https://unhcr-refugees.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://unhcr-refugees.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

9

read 8

unhcr_list_reference

open-world

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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_list_reference",
    "arguments": {
      "topic": "<topic>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "topic": {
      "type": "string",
      "enum": [
        "countries",
        "regions",
        "coverage",
        "population_types",
        "asylum_codes"
      ],
      "description": "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": {
      "description": "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.",
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "topic"
  ],
  "additionalProperties": false
}
view source ↗

unhcr_get_population

open-world

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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_get_population",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "origin": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "asylum": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "expand": {
      "description": "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.",
      "default": "none",
      "type": "string",
      "enum": [
        "none",
        "origin",
        "asylum",
        "both"
      ]
    },
    "year_from": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "year_to": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "include_nowcast": {
      "description": "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.",
      "default": false,
      "type": "boolean"
    },
    "sort_by": {
      "description": "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.",
      "default": "year",
      "type": "string",
      "enum": [
        "year",
        "refugees",
        "asylum_seekers",
        "oip",
        "idps",
        "stateless",
        "ooc",
        "hst",
        "returned_refugees",
        "returned_idps"
      ]
    },
    "limit": {
      "description": "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.",
      "default": 100,
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "stage": {
      "description": "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.",
      "default": false,
      "type": "boolean"
    }
  },
  "required": [
    "expand",
    "include_nowcast",
    "sort_by",
    "limit",
    "stage"
  ],
  "additionalProperties": false
}
view source ↗

unhcr_get_demographics

open-world

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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_get_demographics",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "origin": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "asylum": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "expand": {
      "description": "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.",
      "default": "none",
      "type": "string",
      "enum": [
        "none",
        "origin",
        "asylum",
        "both"
      ]
    },
    "year_from": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "year_to": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "population_types": {
      "description": "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.",
      "maxItems": 9,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "REF",
          "ASY",
          "OIP",
          "IDP",
          "STA",
          "OOC",
          "HST",
          "RET",
          "RDP"
        ],
        "description": "A population-type code."
      }
    },
    "sort_by": {
      "description": "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.",
      "default": "year",
      "type": "string",
      "enum": [
        "year",
        "total"
      ]
    },
    "limit": {
      "description": "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.",
      "default": 100,
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "stage": {
      "description": "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.",
      "default": false,
      "type": "boolean"
    }
  },
  "required": [
    "expand",
    "sort_by",
    "limit",
    "stage"
  ],
  "additionalProperties": false
}
view source ↗

unhcr_get_asylum_applications

open-world

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).

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_get_asylum_applications",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "origin": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "asylum": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "expand": {
      "description": "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.",
      "default": "none",
      "type": "string",
      "enum": [
        "none",
        "origin",
        "asylum",
        "both"
      ]
    },
    "year_from": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "year_to": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "split_by": {
      "description": "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.",
      "default": [
        "stage"
      ],
      "maxItems": 3,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "authority",
          "stage",
          "decision_level"
        ],
        "description": "A procedure dimension to keep separate."
      }
    },
    "stages": {
      "description": "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.",
      "maxItems": 11,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "N",
          "R",
          "A",
          "NA",
          "NR",
          "FA",
          "J",
          "BL",
          "SP",
          "V",
          "RA"
        ],
        "description": "An application-stage code."
      }
    },
    "sort_by": {
      "description": "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.",
      "default": "year",
      "type": "string",
      "enum": [
        "year",
        "applied"
      ]
    },
    "limit": {
      "description": "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.",
      "default": 100,
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "stage": {
      "description": "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.",
      "default": false,
      "type": "boolean"
    }
  },
  "required": [
    "expand",
    "split_by",
    "sort_by",
    "limit",
    "stage"
  ],
  "additionalProperties": false
}
view source ↗

unhcr_get_asylum_decisions

open-world

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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_get_asylum_decisions",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "origin": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "asylum": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "expand": {
      "description": "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.",
      "default": "none",
      "type": "string",
      "enum": [
        "none",
        "origin",
        "asylum",
        "both"
      ]
    },
    "year_from": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "year_to": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "split_by": {
      "description": "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.",
      "default": [],
      "maxItems": 2,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "authority",
          "decision_level"
        ],
        "description": "A procedure dimension to keep separate."
      }
    },
    "decision_levels": {
      "description": "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.",
      "maxItems": 14,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "NA",
          "FI",
          "AR",
          "RA",
          "IN",
          "EO",
          "JR",
          "SP",
          "FA",
          "TP",
          "TA",
          "BL",
          "TR",
          "CA"
        ],
        "description": "A decision-level code."
      }
    },
    "sort_by": {
      "description": "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.",
      "default": "year",
      "type": "string",
      "enum": [
        "year",
        "total_decisions",
        "substantive_decisions",
        "recognized",
        "rejected"
      ]
    },
    "limit": {
      "description": "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.",
      "default": 100,
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "stage": {
      "description": "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.",
      "default": false,
      "type": "boolean"
    }
  },
  "required": [
    "expand",
    "split_by",
    "sort_by",
    "limit",
    "stage"
  ],
  "additionalProperties": false
}
view source ↗

unhcr_get_solutions

open-world

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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_get_solutions",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "origin": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "asylum": {
      "description": "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.",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 1000,
          "description": "One code, or several separated by commas or spaces, e.g. \"SYR\" or \"SYR, AFG\"."
        },
        {
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 100
          },
          "description": "Codes as a list, e.g. [\"SYR\", \"AFG\"]; at most 50."
        }
      ]
    },
    "expand": {
      "description": "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.",
      "default": "none",
      "type": "string",
      "enum": [
        "none",
        "origin",
        "asylum",
        "both"
      ]
    },
    "year_from": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "year_to": {
      "description": "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.",
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100
    },
    "sort_by": {
      "description": "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.",
      "default": "year",
      "type": "string",
      "enum": [
        "year",
        "returned_refugees",
        "resettlement",
        "naturalisation",
        "returned_idps"
      ]
    },
    "limit": {
      "description": "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.",
      "default": 100,
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "stage": {
      "description": "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.",
      "default": false,
      "type": "boolean"
    }
  },
  "required": [
    "expand",
    "sort_by",
    "limit",
    "stage"
  ],
  "additionalProperties": false
}
view source ↗

unhcr_dataframe_describe

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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_dataframe_describe",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "description": "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.",
      "type": "string",
      "maxLength": 14,
      "pattern": "^df_[A-Z0-9]{5}_[A-Z0-9]{5}$"
    }
  },
  "additionalProperties": false
}
view source ↗

unhcr_dataframe_query

Run a single-statement SELECT against the dataframes staged by the unhcr_get_* tools. Check a dataframe’s columns with unhcr_dataframe_describe first. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, external-file functions, and system catalogs (information_schema, pg_catalog, sqlite_master, duckdb_*) are rejected. Optional register_as saves the result as a new dataframe with a fresh TTL. Recompute rates from summed counts rather than averaging rate columns.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "unhcr_dataframe_query",
    "arguments": {
      "sql": "<sql>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "sql": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20000,
      "description": "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."
    },
    "register_as": {
      "description": "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.",
      "type": "string",
      "maxLength": 14,
      "pattern": "^df_[A-Z0-9]{5}_[A-Z0-9]{5}$"
    },
    "preview": {
      "description": "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.",
      "type": "integer",
      "minimum": 0,
      "maximum": 10000
    },
    "row_limit": {
      "description": "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.",
      "default": 1000,
      "type": "integer",
      "minimum": 1,
      "maximum": 10000
    }
  },
  "required": [
    "sql",
    "row_limit"
  ],
  "additionalProperties": false
}
view source ↗

disabled 1

unhcr_dataframe_drop

Drop a staged dataframe by name before its TTL expires. Idempotent: returns dropped=false when nothing matched. Re-running the unhcr_get_* call that staged it restores the rows under a new name.

disabledwould be write

Disabled. Dropping dataframes is turned off in this deployment; the per-table TTL reclaims staged tables on its own.

UNHCR_DATAFRAME_DROP_ENABLED=true
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 14,
      "pattern": "^df_[A-Z0-9]{5}_[A-Z0-9]{5}$",
      "description": "Dataframe to drop, as df_XXXXX_XXXXX (uppercase letters and digits): the dataset.name a unhcr_get_* result returned, or a register_as name."
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}
view source ↗