Skip to main content
Glama

ilostat-mcp-server

Query staged ILOSTAT dataframes

ilostat_dataframe_query
Read-onlyIdempotent

Run a single-statement SELECT against the df_ dataframes staged by ilostat_query_indicator and ilostat_compare_geographies or stored by an earlier register_as. Inspect a dataframe with ilostat_dataframe_describe first; its column schema is what the SQL has to match. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, file references, file-reading table functions, and functions that read engine settings or name a table from a string are rejected, and system catalogs (information_schema, pg_catalog, sqlite_master, duckdb_*) are denied. Breakdown versions overlap (AGE_YTHADULT_*, AGE_AGGREGATE_*, AGE_10YRBANDS_*, AGE_5YRBANDS_*; ECO_SECTOR_*, ECO_AGGREGATE_*, ECO_ISIC4_*, ECO_ISIC3_*), so filter to one version before summing. Optional register_as stores the result as a new dataframe with a fresh TTL.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "sql": "<sql>"
    },
    "name": "ilostat_dataframe_query"
  }
}

Input Schema

NameRequiredDescriptionDefault
sqlYesOne SELECT over df_<id> tables (DuckDB SQL: joins, aggregates, window functions, CTEs), at most 20,000 characters. BIGINT results such as COUNT or SUM of integers serialize as strings; CAST to DOUBLE for inline arithmetic.
previewNoRows returned inline; defaults to row_limit, and a larger value is applied as row_limit. Inline rows also stop at a size budget (applied_limits.preview_chars). Set lower when register_as keeps the full result.
row_limitNoCap on rows materialized; defaults to 1,000, or the per-call cap when that is lower. The per-call cap is 10,000, or the lower cap the deployment sets; a larger value, up to 1,000,000, is applied as that cap. A query matching more stops at the cap and row_count_capped is true; register_as keeps the full result.
register_asNoStore the result as a new dataframe under this name (df_XXXXX_XXXXX: letters and digits, five in each part; stored uppercased after df_) with a fresh TTL, to chain analyses. A result over 1,000,000 rows is refused, and storing one can evict the oldest dataframes, which evicted names.

Output Schema

NameRequiredDescriptionDefault
capNoThe row cap that bound: preview when lower than row_limit, else row_limit.
rowsNoResult rows keyed by the names in columns, bounded by preview, row_limit, and an inline size budget; rows stop before the first that would pass the budget, and a value is never clipped. BIGINT values arrive as strings.
errorNoPresent when the call failed. Absent on success.
shownNoRows returned inline.
noticeNoGuidance when the query returned no rows, a cap withheld some, or a limit was applied below the value sent.
columnsNoColumn names in projection order.
evictedNoDataframes dropped, oldest first, to keep this tenant within 1,000,000 staged rows and 100 dataframes; present only when storing the result evicted any.
row_countNoRows the query produced. With register_as this is the exact count of the stored dataframe, which row_limit does not bound; otherwise at most row_limit, and when row_count_capped is true it is the cap, not a total.
truncatedNoTrue when a cap withheld rows from this response.
expires_atNoISO 8601 expiry of the new dataframe.
registered_asNoThe new dataframe name, when register_as stored the result.
applied_limitsNoThe limits this call ran under, after any clamp.
row_count_cappedNoTrue when the query matched more rows than row_limit; never true with register_as, which stores every row.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas DuckDB engine cannot load in this deployment. `system_catalog_access`: The SQL references a system catalog. `missing_table`: A referenced df_<id> does not exist or has expired. `invalid_sql`: The SELECT fails to prepare: an unknown column or an invalid expression. `sql_execution_error`: The SELECT prepared but failed on the data. `register_as_clash`: register_as names an existing dataframe. `register_as_too_large`: The register_as result holds more than 1,000,000 rows. `non_select_statement`: The SQL is not a SELECT. `multi_statement`: The SQL holds more than one statement, or a PIVOT with no IN list, which DuckDB expands into several. `denied_function`: The SQL calls a file-reading or external table function, a function that reads engine settings (current_setting, current_schema), or one that runs SQL or names a table from a string (query_table, query() over anything but one string literal). `sql_read_only`: The SQL names a file in place of a table, or attempts a write the engine refuses. `plan_operator_not_allowed`: The plan uses an operator outside the read-only allowlist, such as range(). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas DuckDB engine cannot load in this deployment. `system_catalog_access`: The SQL references a system catalog. `missing_table`: A referenced df_<id> does not exist or has expired. `invalid_sql`: The SELECT fails to prepare: an unknown column or an invalid expression. `sql_execution_error`: The SELECT prepared but failed on the data. `register_as_clash`: register_as names an existing dataframe. `register_as_too_large`: The register_as result holds more than 1,000,000 rows. `non_select_statement`: The SQL is not a SELECT. `multi_statement`: The SQL holds more than one statement, or a PIVOT with no IN list, which DuckDB expands into several. `denied_function`: The SQL calls a file-reading or external table function, a function that reads engine settings (current_setting, current_schema, which an unqualified column of that name also reads as), or one that runs SQL or names a table from a string (query_table, query() over anything but one string literal, query() nested more than 4 deep). `sql_read_only`: The SQL names a file in place of a table, or attempts a write the engine refuses. `plan_operator_not_allowed`: The plan uses an operator outside the read-only allowlist, such as range(). Other values are possible when a failure originates below the handler."
  2. Changed10 schema fields changed
    • changedInput schema / properties / preview / description
      Previous value: -"Rows returned inline; defaults to row_limit. Set lower when register_as keeps the full result."New value: +"Rows returned inline; defaults to row_limit, and a larger value is applied as row_limit. Inline rows also stop at a size budget (applied_limits.preview_chars). Set lower when register_as keeps the full result."
    • changedInput schema / properties / preview / maximum
      Previous value: -10000New value: +9007199254740991
    • removedInput schema / properties / row_limit / default
      Removed value: -1000
    • changedInput schema / properties / row_limit / description
      Previous value: -"Hard cap on rows materialized (1–10,000). A query matching more stops at the cap and row_count_capped is true; register_as keeps the full result."New value: +"Cap on rows materialized; defaults to 1,000, or the per-call cap when that is lower. The per-call cap is 10,000, or the lower cap the deployment sets; a larger value, up to 1,000,000, is applied as that cap. A query matching more stops at the cap and row_count_capped is true; register_as keeps the full result."
    • changedInput schema / properties / row_limit / maximum
      Previous value: -10000New value: +1000000
    • addedOutput schema / properties / applied_limits
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "The limits this call ran under, after any clamp.",
      +  "properties": {
      +    "preview": {
      +      "description": "Inline rows applied: preview, at most the applied row_limit.",
      +      "type": "number"
      +    },
      +    "preview_chars": {
      +      "description": "Inline size budget applied, in characters; rows stop before the first that would pass it.",
      +      "type": "number"
      +    },
      +    "row_limit": {
      +      "description": "Row cap applied: row_limit (default 1,000), or the per-call cap when that is lower. With register_as it bounds the inline rows, never the stored result.",
      +      "type": "number"
      +    }
      +  },
      +  "required": [
      +    "row_limit",
      +    "preview",
      +    "preview_chars"
      +  ],
      +  "type": "object"
      +}
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas DuckDB engine cannot load in this deployment. `system_catalog_access`: The SQL references a system catalog. `missing_table`: A referenced df_<id> does not exist or has expired. `invalid_sql`: The SELECT fails to prepare: an unknown column or an invalid expression. `sql_execution_error`: The SELECT prepared but failed on the data. `register_as_clash`: register_as names an existing dataframe. `register_as_too_large`: The register_as result holds more than 1,000,000 rows. `non_select_statement`: The SQL is not a SELECT. `multi_statement`: The SQL holds more than one statement, or a PIVOT with no IN list, which DuckDB expands into several. `denied_function`: The SQL calls a file-reading or external table function. `plan_operator_not_allowed`: The plan uses an operator outside the read-only allowlist, such as range(). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas DuckDB engine cannot load in this deployment. `system_catalog_access`: The SQL references a system catalog. `missing_table`: A referenced df_<id> does not exist or has expired. `invalid_sql`: The SELECT fails to prepare: an unknown column or an invalid expression. `sql_execution_error`: The SELECT prepared but failed on the data. `register_as_clash`: register_as names an existing dataframe. `register_as_too_large`: The register_as result holds more than 1,000,000 rows. `non_select_statement`: The SQL is not a SELECT. `multi_statement`: The SQL holds more than one statement, or a PIVOT with no IN list, which DuckDB expands into several. `denied_function`: The SQL calls a file-reading or external table function, a function that reads engine settings (current_setting, current_schema), or one that runs SQL or names a table from a string (query_table, query() over anything but one string literal). `sql_read_only`: The SQL names a file in place of a table, or attempts a write the engine refuses. `plan_operator_not_allowed`: The plan uses an operator outside the read-only allowlist, such as range(). Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous value: -[
      -  "canvas_unavailable",
      -  "system_catalog_access",
      -  "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"
      -]New value: +[
      +  "canvas_unavailable",
      +  "system_catalog_access",
      +  "missing_table",
      +  "invalid_sql",
      +  "sql_execution_error",
      +  "register_as_clash",
      +  "register_as_too_large",
      +  "non_select_statement",
      +  "multi_statement",
      +  "denied_function",
      +  "sql_read_only",
      +  "plan_operator_not_allowed"
      +]
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when the query returned no rows or a cap withheld some."New value: +"Guidance when the query returned no rows, a cap withheld some, or a limit was applied below the value sent."
    • changedOutput schema / properties / rows / description
      Previous value: -"Result rows keyed by the names in columns, bounded by preview and row_limit; BIGINT values arrive as strings."New value: +"Result rows keyed by the names in columns, bounded by preview, row_limit, and an inline size budget; rows stop before the first that would pass the budget, and a value is never clipped. BIGINT values arrive as strings."
  3. First observed

TDQS

A4.4/5.0
Behavior5/5

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

Annotations only declare readOnly/idempotent/closed-world; the description goes far beyond by enumerating the rejected statement classes (writes, DDL, DROP, COPY, PRAGMA, ATTACH, file references, file-reading table functions, settings-reading functions, string-named tables) and the denied system catalogs. It also discloses the register_as side effect: fresh TTL and possible eviction of oldest dataframes.

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 and the staging provenance, then the guardrails. Dense but each clause carries operational weight; the long rejection enumeration is justified by the read-only contract, though it could be tightened.

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?

An output schema exists, so return-shape explanation is not needed. The description covers provenance of the queryable tables, the prerequisite describe step, enforcement boundaries, and the register_as side effect — everything an agent needs to call it correctly.

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 coverage is 100%, so the schema already documents sql, preview, row_limit and register_as in detail. The description only restates that register_as stores results with a fresh TTL, adding no syntax or behavioral detail beyond what the schema provides; baseline 3 is appropriate.

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

Purpose5/5

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

States a specific verb and resource (single-statement SELECT over df_<id> dataframes) and names exactly which sibling tools produce those dataframes (ilostat_query_indicator, ilostat_compare_geographies, or an earlier register_as). An agent can immediately distinguish this from the staging tools in the sibling list.

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

Usage Guidelines4/5

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

Gives a concrete prerequisite workflow: inspect the dataframe with ilostat_dataframe_describe first because its column schema must be matched by the SQL. Also adds a domain caution to filter to one breakdown version before summing. No explicit when-not-to-use statement, so it falls short of a 5.

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.