@cyanheads/eia-energy-mcp-server
Enables use of Cloudflare KV, R2, or D1 as a storage backend for persistent state.
Enables tabular data spillover and SQL querying via DuckDB-powered DataCanvas for large result sets.
Provides optional OpenTelemetry tracing for observability.
Enables use of Supabase as a storage backend for persistent state.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@@cyanheads/eia-energy-mcp-serverFind electricity net generation data for California in 2022"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Public Hosted Server: https://eia-energy.caseyjhand.com/mcp
Overview
Energy data from the U.S. Energy Information Administration (EIA) API v2 — electricity, petroleum, natural gas, coal, and forecasts. Browse the dataset taxonomy, search it by natural language, and query time-series data with facet filters, then stage large result sets as a SQL-queryable DataCanvas table. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
Tools
Tool | Description |
| Lists child routes under a path in the EIA dataset taxonomy; omit |
| Returns a leaf route's facets, valid values, data columns, frequencies, and date range. |
| Fuzzy text search across route names, descriptions, STEO series names, and facet values. |
| Fetches data from a leaf route with facet filters, date range, and column selection; optionally stages results for SQL. |
| Lists active DataCanvas dataframes staged by |
| Runs a read-only SQL SELECT against staged DataCanvas dataframes. |
| Drops a DataCanvas dataframe, freeing its memory. |
The three eia_dataframe_* tools are callable and listed by MCP only when CANVAS_PROVIDER_TYPE=duckdb is set; eia_dataframe_drop additionally requires EIA_DATAFRAME_DROP_ENABLED=true. Disabled definitions remain in the server manifest with their enable hints. A default deployment lists the first four tools.
Related MCP server: @cyanheads/federal-reserve-mcp-server
Capability reference
Browse accepts route as an alias for path; describe and query accept path as an alias for route. Supplying both spellings is rejected. Leading, trailing, and repeated slashes are normalized.
eia_browse_routes tool
Omit
pathfor the 14 top-level categories; pass a path to browse its children.Each child carries
isLeaf: describe and query leaves, or browse categories further. A missing path returnsroute_not_found.
eia_describe_route tool
Pass a leaf
routefor facets, data columns, frequencies, and date range. Each facet window is capped atEIA_FACET_VALUE_CAP(default 50); page withfacetandvalues_offset.value_countandvalues_truncateddescribe each window. Errors distinguishroute_not_found,route_not_queryable,facet_not_found, and retryablerate_limited.
eia_search_routes tool
Search route names, descriptions, STEO's 1,469 series names, and facet values;
limitdefaults to 10 and caps at 30.Matches carry
score(0 exact, 1 no match) and may carry afilter_hintforeia_query_route. Scores above 0.72 are weak matches; checkindexCompleteandindexGapsbefore trusting a short result set.The first search waits for the index to warm (normally 24–30 s, at most 45 s); later calls use the in-process index.
eia_query_route tool
Pass a leaf
route, with optional facet-IDfilters,columns,frequency,start/end, andsort. Page withoffsetandlength(default 100, max 5000).totalreports the full match count; values remain strings and units appear as{col}-units. Errors distinguish invalid route, facet, column, frequency, sort, or period, inverted ranges (no_data), and retryablerate_limited.stage: trueaccumulates up toEIA_CANVAS_MAX_ROWS(default 25000) as adatasethandle foreia_dataframe_query; omitted, the call fetches one page.
eia_dataframe_describe tool
Omit
nameto list active staged dataframes; pass a handle to inspect one. A miss returnsfound: falsewithactive_names.Entries carry
source_tool,query_params,created_at,expires_at,row_count,truncated/max_rows, andcolumn_schema. Listing does not extend expiry.
eia_dataframe_query tool
Run one read-only SQL SELECT against staged handles.
row_limitdefaults to 1000 and caps at 10000;previewnarrows the inline slice.truncated: truemeanstotalRowsis the materialization cap, not a full count.register_asstages the complete result under an unused name with an exact count and fresh expiry.Cast EIA VARCHAR values for arithmetic. Writes and system catalogs are rejected; declared failures include
missing_table,invalid_sql,non_select_statement,system_catalog_access, andregister_as_clash.
eia_dataframe_drop tool
Pass
nameto remove a staged dataframe; requiresEIA_DATAFRAME_DROP_ENABLED=true.Returns
dropped: falsewhen nothing matched. Manual cleanup is optional: the default 24 h expiry extends whenever a query references the dataframe.
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
EIA-specific:
Full coverage of EIA API v2's 14 top-level dataset categories, via an in-process route tree cache built once on first use
Fuzzy search index (Fuse.js) covers route names/descriptions, all 1,469 STEO series names, and facet values for natural-language discovery
Per-route facet metadata is fetched by fan-out (
Promise.all) and cached, soeia_query_routefilters are validated without re-fetchingA route whose metadata could not be fetched is held as an incomplete stub — reported through
eia_search_routesrather than silently dropped — and re-fetched on the nexteia_browse_routescall that reaches itDataCanvas (DuckDB) staging is opt-in per call; a deployment without canvas omits the three dataframe tools from its callable MCP list
Agent-friendly output:
Provenance —
eia_query_routeechoes the canonical, slash-normalized route rather than the caller's spelling, and every staged dataframe records itssource_toolandquery_paramsCapped-window disclosure — every truncatable response (facet values, row previews, SQL row limits) reports the count against its cap and a
noticenaming the exact next call to page past itDiscriminated failure — typed error
reasonvalues (e.g.route_not_queryable,invalid_facet,missing_table) each carry arecoveryhint naming the next tool call
Getting started
Public Hosted Instance
A public instance is available at https://eia-energy.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"eia-energy-mcp-server": {
"type": "streamable-http",
"url": "https://eia-energy.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Get a free API key at api.eia.gov, then add the following to your MCP client configuration file.
{
"mcpServers": {
"eia-energy-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/eia-energy-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"EIA_API_KEY": "your-api-key"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"eia-energy-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/eia-energy-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"EIA_API_KEY": "your-api-key"
}
}
}
}Or with Docker:
{
"mcpServers": {
"eia-energy-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "EIA_API_KEY=your-api-key",
"ghcr.io/cyanheads/eia-energy-mcp-server:latest"
]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 EIA_API_KEY=your-key bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+).
A free EIA API key from api.eia.gov. The
DEMO_KEYhits rate limits quickly; a real key is required for sustained use.
Installation
Clone the repository:
git clone https://github.com/cyanheads/eia-energy-mcp-server.gitNavigate into the directory:
cd eia-energy-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# edit .env and set required vars (at minimum, EIA_API_KEY)Configuration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
Variable | Description | Default |
| Required. Free API key from api.eia.gov — appended as | — |
| EIA API base URL. |
|
| Sliding per-dataframe TTL in seconds. The window is extended every time an |
|
| Set to |
|
| Cumulative row ceiling for |
|
| Facet values |
|
| Set to | — |
| Transport: |
|
| HTTP server port. |
|
| HTTP endpoint path. |
|
| HTTP sessions: |
|
| Public origin override for TLS-terminating reverse-proxy deployments. | — |
| Auth mode: |
|
| Log level (RFC 5424). |
|
| Directory for log files (Node.js only). |
|
| Storage backend: |
|
| Enable OpenTelemetry instrumentation. |
|
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:httpRun checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t eia-energy-mcp-server .
docker run --rm -e EIA_API_KEY=your-key -p 3010:3010 eia-energy-mcp-serverThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/eia-energy-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Directory | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod. |
| Tool definitions ( |
| EIA API v2 service — route tree cache, Fuse.js index, facet fan-out, HTTP client. |
| DataCanvas bridge — registers EIA query results as DuckDB dataframes, routes SQL queries. |
| Unit and integration tests mirroring |
| Design documents ( |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storageAlways call
eia_describe_routebeforeeia_query_route— facet values require a separate API fan-out and are not embedded in route metadataWrap EIA responses: validate raw → normalize to domain type → return output schema; data values are strings — never coerce silently
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
Query SEC EDGAR filings, XBRL financials, and company data through MCP. STDIO & Streamable HTTP.
EIA MCP — US Energy Information Administration API v2
Econdata MCP — wraps BLS (Bureau of Labor Statistics) public API v2
NREL MCP — wraps the US National Renewable Energy Laboratory developer API
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceProvides access to comprehensive U.S. and international energy data from the EIA API, including electricity, natural gas, petroleum, coal, renewables, CO2 emissions, and energy forecasts.MIT
- AlicenseNot gradedqualityAmaintenanceSearch and fetch ~800K Federal Reserve economic time-series from the FRED API via MCP, with STDIO or Streamable HTTP transport.81 npm1Apache 2.0
- AlicenseNot gradedqualityAmaintenanceExposes the FBI Crime Data Explorer API — crime estimates, agency offense rates, and LEOKA officer safety data via MCP. Supports STDIO or Streamable HTTP transport.95 npm1Apache 2.0
- FlicenseAqualityDmaintenanceAn MCP server that exposes the U.S. Energy Information Administration (EIA) Open Data API, enabling LLMs to browse and query energy data across 17 datasets with generic, composable tools.4-