# Reported issues for eia-mcp-server

Pod holds 19 of 60 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to [eia-mcp-server](/mcp/eia-mcp-server).

## Most discussed

### feat(config): eia_dataframe_describe and eia_dataframe_query stay listed when DataCanvas is off, so the default config advertises two unusable tools

### Problem

`eia_dataframe_drop` is gated at registration:

```ts
const dropTool = serverConfig.dataframeDropEnabled
  ? dataframeDropTool
  : disabledTool(dataframeDropTool, { reason: '...', hint: 'EIA_DATAFRAME_DROP_ENABLED=true' });
```

`eia_dataframe_describe` and `eia_dataframe_query` are not. They are registered unconditionally and only discover the canvas is absent inside the handler:

```ts
const bridge = getCanvasBridge();
if (!bridge) throw ctx.fail('canvas_unavailable', 'DataCanvas 

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/52) · 2026-08-06 · closed · 4 comments

### feat(eia_describe_route): cap high-cardinality facet values in structuredContent — STEO returns all 1,469 seriesId values (~130KB)

`eia_describe_route` returns every value of every facet in `structuredContent` with no cap. For most routes that's fine (~50 states, a handful of sectors), but high-cardinality facets blow up the payload — `eia_describe_route("steo")` returns all **1,469** `seriesId` values, ~130KB of JSON (~35–40K tokens) in a single describe call. `format()` correctly previews 5 and appends "(+N more)", but the truncation lives only in `content[]`; clients that consume `structuredContent` (e.g. Claude Code) re

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/29) · 2026-06-30 · closed · 4 comments

### bug(eia_dataframe_query): row_limit truncation is silent — totalRows reports the capped count and the notice never fires

### Server version

0.3.4

### mcp-ts-core version

^0.11.0

### Runtime

Bun

### Runtime version

Bun 1.3.14 / Node 26.5.0

### Transport

HTTP (transport-agnostic — defect is in the tool handler)

### OS

macOS (server-side logic, platform-independent)

### Description

`eia_dataframe_query` drops the framework's `QueryResult.truncated` signal, so a result cut by `row_limit` comes back indistinguishable from a complete one. The handler only discloses truncation when `result.rowCount > result.

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/40) · 2026-08-06 · closed · 3 comments

### feat(eia_search_routes): index facet values so fuel-type queries resolve — "wind" and "solar photovoltaic generation" return nothing

`eia_search_routes` indexes route names, descriptions, path segments, and STEO series names. It does not index **facet values**, so the vocabulary callers actually search with — fuel types, sectors, states — is invisible to the index. Queries naming a fuel return nothing even when a route exposes that fuel as a facet value.

`electricity/electric-power-operational-data` carries a `fueltypeid` facet with 45 values including `solar photovoltaic`, `solar thermal`, `wind`, `onshore wind turbine`, an

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/36) · 2026-07-31 · closed · 3 comments

### bug(eia_query_route): EIA's incomplete-return advisory still forwards on the canvas-off path, where canvas_preview_note has already accounted for the gap

### Server version

0.3.7

### mcp-ts-core version

0.11.1

### Runtime

Bun

### Runtime version

Bun 1.3.14

### Transport

http

### Description

`isIncompleteReturn` suppresses EIA's per-page advisory once the response itself states where the caller stands, and `gapAccountedFor` is set in two places: the row-less branch, and the canvas branch when the staged table reaches the last row. The third branch — canvas absent, `total > data.length` — writes a `canvas_preview_note` that accounts for 

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/54) · 2026-08-06 · closed · 2 comments

### feat(eia-service): normalize route paths — eia_browse_routes rejects the leading and trailing slashes eia_describe_route and eia_query_route accept

### Problem

The three route-taking tools disagree on whether a path may carry slashes.

`eia_browse_routes` resolves only against the in-process node map, whose keys are bare segments (`electricity/retail-sales`), so any variant misses and comes back `route_not_found`. `eia_describe_route` and `eia_query_route` miss the map too, but then fall through to a direct EIA fetch — and EIA normalizes the path server-side, so they succeed.

| Input | `eia_browse_routes` | `eia_describe_route` | `eia_que

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/53) · 2026-08-06 · closed · 2 comments

### bug(eia_query_route): a rejected sort column or date format is reported as invalid_facet, and the recovery hint sends the caller to the wrong field

### Server version

0.3.7

### mcp-ts-core version

0.11.1

### Runtime

Bun

### Runtime version

Bun 1.3.14

### Transport

http

### Description

`INVALID_PARAM_REASONS` in `eia-service.ts` maps EIA's 400 bodies to a typed reason, and matches two of them:

```ts
const INVALID_PARAM_REASONS = [
  { pattern: /invalid data\b/i,      reason: 'invalid_column',    hint: '... pick a column from data_columns[].id.' },
  { pattern: /invalid frequency\b/i, reason: 'invalid_frequency', hint: '... pick a

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/51) · 2026-08-06 · closed · 2 comments

### feat(eia_query_route): canvas staging pages 25,000 rows on every call with no opt-out — a 3-row preview costs 5-6x the latency

### Problem

`eia_query_route` sets `accumulate: bridge !== undefined`, so whenever DataCanvas is configured the service walks offset pages up to `EIA_CANVAS_MAX_ROWS` (25,000) on **every** call where `total` exceeds the inline preview. There is no input that turns it off. The caller who asks for 3 rows to see what a route looks like pays five extra 5,000-row upstream fetches plus the DuckDB insert before the response returns.

Measured against the live API, same route and same `length: 3`, on t

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/50) · 2026-08-06 · closed · 2 comments

## Most recent

### feat(eia_browse_routes): name the route path parameter consistently across route tools

### Description

`eia_browse_routes` names its route-path input `path`, while `eia_describe_route` and `eia_query_route` name the same concept `route`. One parameter name for one concept would remove a predictable wrong turn in the server's main workflow.

Since tool inputs became strict, the mismatch now fails loudly rather than silently: carrying `path` forward from a browse call into `eia_describe_route` is rejected outright.

### Steps to reproduce

1. `eia_browse_routes({ path: "electricity

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/63) · 2026-08-25 · open · 0 comments

### feat(eia_query_route): a staged dataset reaches structuredContent with no pointer to the dataframe tools

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

Any — the gap is in the tool definition, not the transport.

### Description

`eia_query_route` returns a `df_<id>` handle in `dataset` on a `stage: true` call, but the structured response never names a tool that can reach the staged rows. `eia_dataframe_describe` is named nowhere in the tool at all, and the only mention of `eia_dataframe_query` in the response is a line `format()` writes into `content[

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/62) · 2026-08-13 · open · 0 comments

### bug(formatting): escape HTML and Markdown in table cells

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

HTTP

### Description

The table formatters escape pipe characters but pass HTML and Markdown syntax through unchanged. Upstream strings and SQL result strings can therefore change how `content[]` renders, while `structuredContent` correctly retains the original value.

### Steps to reproduce

With DataCanvas enabled, call:

```sql
SELECT '<b>unsafe</b> | *em* [link](x)' AS text
```

through `eia_datafr

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/61) · 2026-08-11 · open · 0 comments

### bug(DataCanvas): staging and drop failures return successful calls

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

HTTP

### Description

Two DataCanvas failure paths return successful tool calls:

1. `CanvasBridge.registerDataframe()` catches registration errors and returns `undefined`. When `eia_query_route` was called with `stage: true`, the response contains no `dataset` and no notice that the requested stage failed.
2. `CanvasBridge.drop()` deletes provenance before contacting the canvas. If the canvas call thr

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/60) · 2026-08-11 · open · 0 comments

### bug(eia_describe_route): failed facet fetches look like empty facets

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

HTTP

### Description

`EiaApiService.fetchAndCacheMetadata()` catches each failed `/facet/{id}` request and substitutes `{ values: [] }`. The resulting metadata is cached for the process lifetime with no partial-result field or recovery hint.

This makes a failed facet leg indistinguishable from a facet that genuinely has no values. A later `eia_query_route` caller can conclude that the route has no fi

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/59) · 2026-08-11 · open · 0 comments

### bug(responses): cap inline query payloads below context-window scale

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

HTTP

### Description

The two row-returning tools allow inline responses large enough to consume a model's context window. Their input limits match upstream or provider limits rather than an MCP response budget.

### Steps to reproduce

1. Call `eia_query_route` with `{ "route": "electricity/retail-sales", "length": 5000 }`.
2. Stage a five-row dataframe, then call `eia_dataframe_query` with a six-way 

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/58) · 2026-08-11 · open · 0 comments

### feat(eia_search_routes): index named SEDS series for filter hints

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

HTTP

### Description

The State Energy Data System (`seds`) exposes 968 named `seriesId` values, but none are searchable. The general facet-indexing path skips every facet above 200 values. STEO already has a dedicated named-series pass; SEDS needs the same treatment so a state-energy workflow can obtain the opaque ID required by `eia_query_route`.

### Workflow gap

A caller looking for state carbon-d

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/57) · 2026-08-11 · open · 0 comments

### bug(eia_search_routes): exact-limit results report false truncation

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

HTTP

### Description

`eia_search_routes` reports `truncated: true` whenever the number of returned matches equals `limit`. The service has already sliced the search result to `limit`, so the handler cannot tell whether another match exists.

### Steps to reproduce

Call the tool twice with `query: "weekly crude oil stocks"`:

1. `limit: 3`
2. `limit: 4`

### Actual behavior

Both calls return the same

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/56) · 2026-08-11 · open · 0 comments

### bug(inputs): reject blank search terms and empty query collections

### Server version

0.4.0

### mcp-ts-core version

^0.11.1

### Runtime

Bun

### Transport

HTTP

### Description

Several optional query inputs accept values that contain no usable input, then silently change the request's meaning:

- `eia_search_routes({ query: "   " })` passes `z.string().min(1)` and returns arbitrary index entries.
- `eia_query_route({ filters: { stateid: [] } })` sends no `stateid` value upstream, so the call becomes unfiltered while `appliedFilters` still reports `statei

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/55) · 2026-08-11 · open · 0 comments

### bug(eia_query_route): format() stamps one unit on a column whose rows carry different units, so content[] mislabels the values

### Server version

0.3.7

### mcp-ts-core version

0.11.1

### Runtime

Bun

### Runtime version

Bun 1.3.14

### Transport

http

### Description

`format()` builds its unit annotations from the **first preview row only** and drops every `{col}-units` column from the table body:

```ts
const unitsMap: Record<string, string> = {};
for (const key of allKeys) {
  if (key.endsWith('-units')) {
    const col = key.slice(0, -6);
    const unit = firstRow[key];            // <- first row only
    if 

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/49) · 2026-08-06 · closed · 2 comments

### bug(eia_describe_route): a facet value whose id is a single space renders as an empty pair in content[], hiding a working filter

### Server version

0.3.6

### mcp-ts-core version

0.11.1

### Runtime

Node

### Runtime version

Node 24

### Transport

http

### Description

Related: #47

EIA sends one facet value in the taxonomy whose `id` is a single space: `electricity/facility-fuel`'s `primeMover` carries `{"id": " "}` alongside its 20 real prime-mover codes. It is a working filter — `facets[primeMover][]=%20` returns 14,955 rows — but `format()` renders it as an empty pair, so the `content[]` line reads `FW=FW,  = , 

[Read the thread](https://github.com/cyanheads/eia-energy-mcp-server/issues/48) · 2026-08-06 · closed · 2 comments

The remaining reports are on [the project's issue tracker](https://github.com/cyanheads/eia-mcp-server/issues).
