Browse and query the U.S. Energy Information Administration API v2 — electricity, petroleum, natural gas, coal, forecasts, and more via MCP. STDIO or Streamable HTTP.
Strong tool definitions with comprehensive descriptions and well-structured schemas. All 6 tools have detailed, LLM-optimized descriptions (150-400 chars) explaining WHAT, WHEN, and HOW. Input schemas are complete with types and descriptions for all parameters. Tool names follow verb_noun convention (browse, describe, query, drop). Output schemas are documented inline. Key strengths: facet pagination guidance, error recovery hints, and clear chaining paths. Minor gaps: no explicit enum constraints for some parameters (e.g., frequency codes, facet IDs), and error handling lacks structured error classification (retryable vs fatal). Composition is excellent, tools chain naturally (browse→describe→query→dataframe). Security is solid: no secrets in params, read-only hints present.
Lists child routes under a given path in the EIA dataset taxonomy. Start with no path to get the 14 top-level categories (electricity, petroleum, natural-gas, steo, aeo, ieo, seds, etc.), then drill into subcategories. Each result includes an isLeaf flag — leaf routes are queryable endpoints; non-leaf routes have children to browse. When isLeaf is true on the browsed path itself, switch to eia_describe_route.
List canvas dataframes (df_<id>) materialized by eia_query_route calls that passed stage: true, with provenance, expiry, row count, and column schema. Nothing is staged until such a call runs, so an empty list on a fresh session means no query has staged yet, not that staging failed. Drops entries for dataframes the canvas no longer holds before responding, so the list is always current. Pass a specific name to inspect one dataframe; omit to list all active dataframes for this tenant. A name that is not staged comes back as found=false alongside the handles that are, never as an empty list. Listing is not use: only an eia_dataframe_query statement naming a dataframe extends its expiry, so a dataframe polled with this tool and never queried still lapses on schedule.
Drop a canvas dataframe by name. Idempotent — returns dropped=false when nothing matched. Use to free canvas resources ahead of the per-dataframe expiry when an analysis is complete. In normal operation, expiry cleanup (default 24 h, extended by every query that references the dataframe) is sufficient and this tool is unnecessary. Only available when EIA_DATAFRAME_DROP_ENABLED=true and a DataCanvas is configured.
Frequency and facet IDs not declared as enums. Descriptions reference 'frequency codes' and 'facet IDs' but do not constrain them to a closed set. LLMs may hallucinate invalid values like 'quarterly' or 'region' without explicit enum constraints.
Error handling lacks structured classification. Descriptions mention 'invalid input' and 'not found' but do not categorize errors as retryable, user-fixable, or fatal. LLMs cannot determine whether to retry, ask the user, or abort.
Output schema for eia_query_route mentions 'preview' and 'dataset' but does not formally document the structure of preview rows, column types, or the dataset handle format. LLMs must infer structure from description text.
Inferred effective spec: 2026-07-28+.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 64 | 2026-07-28+ | v2 |
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_<id> 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.
Returns metadata for a leaf route: available facets with their valid values, data column names and units, frequency options, and date range. Call this before eia_query_route to discover valid facet IDs, facet values, column IDs, and frequency codes. Each facet returns a capped window of its values with value_count and values_truncated alongside; pass facet and values_offset to page through the rest of one facet. A values_offset past the last value of a facet returns an empty window for it and a notice naming the count to page against. Facet values are fetched from separate EIA endpoints and merged — results are cached per-route for the process lifetime to minimize API calls.
Fetches data from a leaf route with optional facet filters, date range, frequency, and column selection. Use eia_describe_route first to discover valid facet IDs, facet values, column IDs, and frequency codes. Data values are strings in the response (EIA API returns all numeric values as strings, e.g. "9.13"); cast to DOUBLE in SQL when arithmetic is needed. Returns a preview inline and stages nothing by default — one upstream request, whatever total says. Pass stage: true to also page past the preview and stage the accumulated set as a DataCanvas table, then pass the returned dataset name to eia_dataframe_query for SQL. Every dataset a tenant stages lands in the same canvas, so tables from different routes cross-join by name with nothing to thread between calls.
eia_dataframe_drop is gated by EIA_DATAFRAME_DROP_ENABLED but the tool definition does not document this precondition. LLMs may attempt to call it and receive a 'not available' error without understanding why.
No confirmation or dry-run pattern for destructive operations. eia_dataframe_drop is marked destructive but offers no preview or confirmation step. Agents may accidentally drop active dataframes.