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, and external-file table functions 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.

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.
row_limitNoHard 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.
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
rowsNoMaterialized rows, bounded by preview / row_limit.
errorNoPresent when the call failed. Absent on success.
noticeNoGuidance when either cap bound the response — names the cap that applied and how to reach the rows it withheld.
columnsNoColumn names in projection order.
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.
returnedRowsNoRows included in this response.
registered_asNoSet when register_as was supplied and the new dataframe was materialized.

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already declare readOnlyHint and idempotentHint, and the description reinforces these by explicitly listing rejected operations. It adds behavior not in annotations: VARCHAR casting requirements, expiry extension for referenced dataframes, and the semantics of register_as (fresh expiry, name uniqueness). This goes beyond the annotation baseline, though it does not cover every edge case (e.g., performance or transaction behavior).

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?

The description is dense but efficient. It leads with the core purpose, then covers constraints, data type caveats, and optional chaining in a logical order. Every sentence conveys essential information without filler. It is longer than average, but the complexity of the tool justifies it — no word is wasted.

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?

For a tool of this complexity, the description covers all critical aspects: prerequisites, supported and rejected operations, data type handling, output truncation behavior, expiry semantics, and register_as usage. The output schema likely captures return format, so nothing critical is missing. This is a model of a complete tool description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already provides detailed descriptions for all four parameters (100% coverage), so the baseline is 3. The description adds value by explaining how the sql parameter must reference df_<id> handles, giving an example, and clarifying the interplay between preview and register_as (lower preview when chaining). It also deepens register_as's meaning with reuse rules. This is more than the schema alone offers.

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 opens with a specific verb and resource: 'Run a single-statement SELECT against canvas dataframes'. It clearly distinguishes this from sibling tools by referencing dataframes registered by eia_query_route and listing them via eia_dataframe_describe, and implies it is the querying counterpart to those route-management tools.

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?

Explicitly states the prerequisite: dataframes must have been staged (stage: true) or there is nothing to select. It enumerates supported SQL features and lists rejected operations (writes, DDL, DROP, etc.), provides a concrete example, and explains when register_as should be used for chaining. This leaves an agent with no ambiguity about when and how to use the tool.

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.

TDQS

A4.7/5.0
Disambiguation5/5

Each tool serves a clearly distinct function: taxonomy browsing, fuzzy search, route metadata discovery, data retrieval, dataframe inspection, and SQL querying. There is no overlap between the route-focused and dataframe-focused tools, and even the two describe/query pairs are separated by target (route vs. dataframe).

Naming Consistency4/5

All tools share the eia_ prefix and use descriptive verbs, but there's a minor inconsistency: route tools mix plural (browse_routes, search_routes) and singular (describe_route, query_route) forms, while dataframe tools place 'dataframe' before the verb (eia_dataframe_describe, eia_dataframe_query). This is readable and predictable overall, with only slight deviation from a uniform pattern.

Tool Count5/5

With 6 tools, the server is well-scoped for its purpose. It covers discovery, metadata, data access, and SQL analysis without unnecessary bloat or missing essentials. The number fits comfortably within the ideal range for a domain-specific data server.

Completeness5/5

The tool chain is complete for the domain: browse/search to locate routes, describe to get facet/column metadata, query to fetch data, and optionally stage to query with SQL. Dataframe management includes listing and querying, with automatic expiry, so there are no dead ends in the typical workflow. It's a read-only API, but that's appropriate for the energy data use case.