Skip to main content
Glama

eia-energy-mcp-server

Query EIA Dataframes

eia_dataframe_query
Read-onlyIdempotent

Run a single-statement SELECT against canvas dataframes registered by eia_query_route calls that passed stage: true — a query that staged nothing leaves no table to select from. Standard DuckDB SQL — joins, aggregates, window functions, CTEs all supported. Reference dataframes by the df_ handles returned by eia_query_route or listed by eia_dataframe_describe. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, file reads (read_* functions or a quoted file path), engine-setting functions (current_setting), and SQL built from a string (query_table) are rejected. System catalogs (information_schema, pg_catalog, sqlite_master, duckdb_*) are denied. EIA data values are VARCHAR — use CAST(col AS DOUBLE) for arithmetic and aggregation. Optional register_as chains results as a new dataframe with a fresh expiry. Every dataframe named in the statement has its expiry extended by the query. Inline rows stop at a 64,000-byte budget per response surface; a result cut short says how to page the rest from a register_as table with ORDER BY … LIMIT/OFFSET.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sqlYesSingle-statement SELECT against df_<id> tables. EIA data columns are VARCHAR — use CAST(col AS DOUBLE) for arithmetic. Example: SELECT period, CAST(value AS DOUBLE) AS val FROM df_XXXXX ORDER BY period
previewNoRows to include in the immediate response. Defaults to row_limit. Set lower when chaining via register_as and only a sample is needed inline. The 64,000-byte response budget can end the inline rows sooner.
row_limitNoHard cap on rows materialized (default 1000, max 10000). Rows past the cap are dropped without being counted — the response then carries truncated: true and a totalRows equal to the cap rather than a true total. Pass register_as to materialize the whole result instead and get an exact count. Of the rows materialized, only as many go inline as fit the 64,000-byte response budget.
register_asNoWhen set, persist the result as a new dataframe with a fresh expiry. Use to chain analyses without re-running upstream queries. The name must be unused — reusing a staged name is rejected, and the fix is a different name, not dropping the existing dataframe. eia_dataframe_describe lists the names already taken.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
rowsNoInline rows, bounded by preview, row_limit, and a 64,000-byte budget on each response surface — whole rows only; a row too large for the budget alone is withheld.
errorNoPresent when the call failed. Absent on success.
noticeNoGuidance when row_limit, preview, or the 64,000-byte response budget ended the inline rows — names each bound that applied and how to reach the rows it withheld (raise the cap, or page a register_as table with ORDER BY … LIMIT/OFFSET).
columnsNoColumn names in projection order. When no row is inline and the names pass 32,000 bytes, the list ends early with an entry counting the columns left out.
totalRowsNoRows the query materialized. Exact when truncated is false — including on the register_as path, which stages and counts the whole result past row_limit. Equal to row_limit when truncated is true: a floor on the real match count, not a total.
truncatedNoTrue when row_limit cut the result: more rows matched than the cap and the remainder was dropped without being counted. False when every matching row was materialized, including on the register_as path, which counts the new dataframe exactly.
expires_atNoISO 8601 expiry for the newly registered dataframe, when applicable. Extended each time a later query references it.
executedSqlNoEcho of the SQL statement that was executed — confirms the exact query that ran. A statement past 4,000 bytes is echoed cut, ending with a marker that gives its full size; the whole statement ran.
returnedRowsNoRows included in this response. Below totalRows when preview or the response budget ended the inline rows; the notice names how to page the rest.
registered_asNoSet when register_as was supplied and the new dataframe was materialized.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed7 schema fields changed
    • changedInput schema / properties / preview / description
      Previous value: -"Rows to include in the immediate response. Defaults to row_limit. Set lower when chaining via register_as and only a sample is needed inline."New value: +"Rows to include in the immediate response. Defaults to row_limit. Set lower when chaining via register_as and only a sample is needed inline. The 64,000-byte response budget can end the inline rows sooner."
    • changedInput schema / properties / row_limit / description
      Previous value: -"Hard cap on rows materialized in the response (default 1000, max 10000). Rows past the cap are dropped without being counted — the response then carries truncated: true and a totalRows equal to the cap rather than a true total. Pass register_as to materialize the whole result instead and get an exact count."New value: +"Hard cap on rows materialized (default 1000, max 10000). Rows past the cap are dropped without being counted — the response then carries truncated: true and a totalRows equal to the cap rather than a true total. Pass register_as to materialize the whole result instead and get an exact count. Of the rows materialized, only as many go inline as fit the 64,000-byte response budget."
    • changedOutput schema / properties / columns / description
      Previous value: -"Column names in projection order."New value: +"Column names in projection order. When no row is inline and the names pass 32,000 bytes, the list ends early with an entry counting the columns left out."
    • changedOutput schema / properties / executedSql / description
      Previous value: -"Echo of the SQL statement that was executed — confirms the exact query that ran."New value: +"Echo of the SQL statement that was executed — confirms the exact query that ran. A statement past 4,000 bytes is echoed cut, ending with a marker that gives its full size; the whole statement ran."
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when either cap bound the response — names the cap that applied and how to reach the rows it withheld."New value: +"Guidance when row_limit, preview, or the 64,000-byte response budget ended the inline rows — names each bound that applied and how to reach the rows it withheld (raise the cap, or page a register_as table with ORDER BY … LIMIT/OFFSET)."
    • changedOutput schema / properties / returnedRows / description
      Previous value: -"Rows included in this response."New value: +"Rows included in this response. Below totalRows when preview or the response budget ended the inline rows; the notice names how to page the rest."
    • changedOutput schema / properties / rows / description
      Previous value: -"Materialized rows, bounded by preview / row_limit."New value: +"Inline rows, bounded by preview, row_limit, and a 64,000-byte budget on each response surface — whole rows only; a row too large for the budget alone is withheld."
  2. Changed6 schema fields changed
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedInput schema / additionalProperties
      Added value: +false
    • changedOutput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedOutput schema / anyOf
      Added value: +[
      +  {
      +    "not": {
      +      "required": [
      +        "error"
      +      ]
      +    },
      +    "required": [
      +      "columns",
      +      "rows",
      +      "totalRows",
      +      "returnedRows",
      +      "truncated",
      +      "executedSql"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / error
      Added value: +{
      +  "additionalProperties": {},
      +  "description": "Present when the call failed. Absent on success.",
      +  "properties": {
      +    "code": {
      +      "description": "JSON-RPC error code for this failure.",
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "data": {
      +      "additionalProperties": {},
      +      "properties": {
      +        "reason": {
      +          "description": "Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: DataCanvas service is not configured for this deployment. `system_catalog_access`: SQL references a denied system catalog (information_schema, pg_catalog, sqlite_master, duckdb_*). `missing_table`: SQL references a df_<id> table that is not staged — mistyped, already dropped, or past its expiry. `non_select_statement`: The statement is not a single read-only SELECT — writes, DDL, DROP, COPY, PRAGMA, and ATTACH are rejected. `invalid_sql`: DuckDB could not parse or bind the statement — a syntax error, or a column or alias that does not exist on the referenced dataframe. `register_as_clash`: register_as names a dataframe that is already staged for this tenant. Other values are possible when a failure originates below the handler.",
      +          "examples": [
      +            "canvas_unavailable",
      +            "system_catalog_access",
      +            "missing_table",
      +            "non_select_statement",
      +            "invalid_sql",
      +            "register_as_clash"
      +          ],
      +          "type": "string"
      +        },
      +        "recovery": {
      +          "additionalProperties": {},
      +          "description": "Actionable next step for the caller.",
      +          "properties": {
      +            "hint": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "hint"
      +          ],
      +          "type": "object"
      +        },
      +        "retryable": {
      +          "description": "Whether retrying may succeed.",
      +          "type": "boolean"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "message": {
      +      "description": "Human-readable description of what went wrong.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "code",
      +    "message"
      +  ],
      +  "type": "object"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "columns",
      -  "rows",
      -  "totalRows",
      -  "returnedRows",
      -  "truncated",
      -  "executedSql"
      -]
  3. Changed5 schema fields changed
    • changedInput schema / properties / row_limit / description
      Previous value: -"Hard cap on rows materialized in the response (default 1000, max 10000)."New value: +"Hard cap on rows materialized in the response (default 1000, max 10000). Rows past the cap are dropped without being counted — the response then carries truncated: true and a totalRows equal to the cap rather than a true total. Pass register_as to materialize the whole result instead and get an exact count."
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when results are capped — shows how many rows were omitted."New value: +"Guidance when either cap bound the response — names the cap that applied and how to reach the rows it withheld."
    • changedOutput schema / properties / totalRows / description
      Previous value: -"Total rows the query produced (may exceed rows.length when capped by row_limit)."New value: +"Rows the query materialized. Exact when truncated is false — including on the register_as path, which stages and counts the whole result past row_limit. Equal to row_limit when truncated is true: a floor on the real match count, not a total."
    • addedOutput schema / properties / truncated
      Added value: +{
      +  "description": "True when row_limit cut the result: more rows matched than the cap and the remainder was dropped without being counted. False when every matching row was materialized, including on the register_as path, which counts the new dataframe exactly.",
      +  "type": "boolean"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "columns",
      -  "rows",
      -  "totalRows",
      -  "returnedRows",
      -  "executedSql"
      -]New value: +[
      +  "columns",
      +  "rows",
      +  "totalRows",
      +  "returnedRows",
      +  "truncated",
      +  "executedSql"
      +]
  4. Changed3 schema fields changed
    • changedInput schema / properties / register_as / description
      Previous value: -"When set, persist the result as a new dataframe with a fresh TTL. Use to chain analyses without re-running upstream queries. Conflicts with an existing name throw Conflict."New value: +"When set, persist the result as a new dataframe with a fresh expiry. Use to chain analyses without re-running upstream queries. The name must be unused — reusing a staged name is rejected, and the fix is a different name, not dropping the existing dataframe. eia_dataframe_describe lists the names already taken."
    • addedInput schema / properties / register_as / minLength
      Added value: +1
    • changedOutput schema / properties / expires_at / description
      Previous value: -"ISO 8601 expiry for the newly registered dataframe, when applicable."New value: +"ISO 8601 expiry for the newly registered dataframe, when applicable. Extended each time a later query references it."
  5. Changed2 schema fields changed
    • addedOutput schema / properties / executedSql
      Added value: +{
      +  "description": "Echo of the SQL statement that was executed — confirms the exact query that ran.",
      +  "type": "string"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "columns",
      -  "rows",
      -  "totalRows",
      -  "returnedRows"
      -]New value: +[
      +  "columns",
      +  "rows",
      +  "totalRows",
      +  "returnedRows",
      +  "executedSql"
      +]
  6. Changed8 schema fields changed
    • addedOutput schema / properties / notice
      Added value: +{
      +  "description": "Guidance when results are capped — shows how many rows were omitted.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / returnedRows
      Added value: +{
      +  "description": "Rows included in this response.",
      +  "type": "number"
      +}
    • removedOutput schema / properties / row_count
      Removed value: -{
      -  "description": "Total rows the query produced (may exceed rows.length when capped).",
      -  "type": "number"
      -}
    • addedOutput schema / properties / rows / items / description
      Added value: +"A result row with dynamic keys matching the SQL projection columns."
    • addedOutput schema / properties / rows / items / properties
      Added value: +{}
    • removedOutput schema / properties / rows / items / propertyNames
      Removed value: -{
      -  "type": "string"
      -}
    • addedOutput schema / properties / totalRows
      Added value: +{
      +  "description": "Total rows the query produced (may exceed rows.length when capped by row_limit).",
      +  "type": "number"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "columns",
      -  "row_count",
      -  "rows"
      -]New value: +[
      +  "columns",
      +  "rows",
      +  "totalRows",
      +  "returnedRows"
      +]
  7. First observed

TDQS

Score is being calculated.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.