Skip to main content
Glama

unhcr-refugees-mcp-server

Query staged dataframes

unhcr_dataframe_query
Read-onlyIdempotent

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.

Input Schema

NameRequiredDescriptionDefault
sqlYesOne 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.
previewNoRequested 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_limitNoMost 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_asNoSave 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

NameRequiredDescriptionDefault
capNoThe cap that bound: preview when lower than row_limit, otherwise row_limit.
rowsNoResult rows, one object per row keyed by column name, bounded by preview and row_limit. BIGINT values (SUM, COUNT) arrive as strings.
errorNoPresent when the call failed. Absent on success.
shownNoRows returned inline.
noticeNoGuidance when the query returned no rows or rows were withheld.
columnsNoColumn names in projection order.
evictedNoOlder 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.
completeNoFalse when any original source fetch was incomplete, independently of display truncation.
row_countNoRows the query produced, up to row_limit. When row_count_capped is true this is the cap, not the full size.
truncatedNoTrue when rows were withheld by a cap.
expires_atNoISO 8601 expiry of the new dataframe, when register_as was set.
attributionNoAttribution UNHCR requires when these figures are reused.
registered_asNoThe new dataframe, when register_as was set.
result_chunksNoLossless materialized result snapshot when the byte ceiling withholds rows or metadata.
row_count_cappedNoTrue when more rows matched than row_limit allowed through.
metadata_withheldNoTrue when oversized column names or incomplete-source metadata are withheld inline and retained in result_chunks.
incomplete_sourcesNoOriginal incomplete upstream dataframe handles; retained through saved descendants. If metadata_withheld is true, consult the result snapshot for the full list.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed9 schema fields changed
    • changedInput schema / properties / preview / description
      Previous 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."
    • changedInput schema / properties / preview / maximum
      Previous value: -10000New value: +1000000
    • changedOutput schema / anyOf
      Previous 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"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / complete
      Added value: +{
      +  "description": "False when any original source fetch was incomplete, independently of display truncation.",
      +  "type": "boolean"
      +}
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous 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."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous 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"
      +]
    • addedOutput schema / properties / incomplete_sources
      Added 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"
      +}
    • addedOutput schema / properties / metadata_withheld
      Added value: +{
      +  "description": "True when oversized column names or incomplete-source metadata are withheld inline and retained in result_chunks.",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / result_chunks
      Added 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"
      +}
  2. First observed

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need no explanation, and the description still covers 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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.