@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 "Install 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
Tools
Four route tools cover the two-phase EIA workflow — find the right dataset route, then pull the data. Three DataCanvas tools add SQL over staged results and are listed only where a canvas is configured (CANVAS_PROVIDER_TYPE=duckdb); eia_dataframe_drop needs its own opt-in on top. A default deployment therefore advertises four tools, all of them working:
Tool | Description |
| Lists child routes under a given path in the EIA dataset taxonomy. Start at root to see top-level categories, then drill into subcategories and leaf routes. |
| Returns metadata for a leaf route: available facets with valid values, data column names, frequency options, units, and date range. Call before |
| Fuzzy text search across route names, descriptions, category labels, STEO series names, and facet values. Resolves natural-language queries like "electricity retail sales by state" or a fuel type like "wind" to matching route paths. |
| Fetches data from a leaf route with optional facet filters, date range, frequency, and column selection. Returns a preview; pass |
| Lists active DataCanvas dataframes created by prior |
| Runs a read-only SQL SELECT across DataCanvas dataframes, referenced by their |
| Drops a DataCanvas dataframe, freeing its memory. Only exposed when a canvas is configured and |
eia_browse_routes
Walk the EIA dataset taxonomy from root to leaf.
Root call returns 14 top-level categories: electricity, petroleum, natural-gas, coal, international, total-energy, steo, aeo, ieo, seds, crude-oil-imports, nuclear-outages, densified-biomass, co2-emissions
Intermediate paths return subcategories; leaf routes are flagged so callers know when to switch to
eia_describe_routeSTEO(Short-Term Energy Outlook) is a flat leaf with 1,469 named series — no sub-routes
eia_describe_route
Full schema for a leaf route. Required before constructing facet filters.
Returns facets with valid values (fetched via per-facet API calls and cached in-process)
Returns data column names, units, frequency options, and date range
Each facet returns at most
EIA_FACET_VALUE_CAPvalues, alongsidevalue_countandvalues_truncated. Passfacetwithvalues_offsetto page one facet past the cap — the cap shapes this tool's response only, and the in-process cache keeps every valueThat window is the same on both client surfaces:
content[]renders every valuestructuredContentcarries, so both name the same next callvalues_offsetapplies to every facet in the response. One past a facet's last value empties that facet's window and returns anoticenaming the facet and itsvalue_count, so an overshoot never reads like a fully enumerated facetIn
content[]a value reads asid=name (alias), with the alias left off when it only restates the pair — EIA supplies(IN) IndianabesideIN=Indianaon most values. An alias that adds something, such asRegion: (MAT) Middle Atlantic, still prints, and thealiasfield itself is unchangedA value EIA sends without a
nameis labelled from itsalias, then from itsid— theidis what filters, so the value is kept. A value EIA sends without anidis dropped, having nothing to filter witheia_search_routesandeia_browse_routesresolve the route path; this tool provides the filter vocabulary
eia_search_routes
Fuzzy search across the in-memory route index.
Indexes route names, descriptions, and category labels — plus STEO's 1,469 series names and facet values
Resolves natural language ("natural gas spot prices", "ethanol net imports") to queryable route paths, and a fuel-type or sector term ("wind", "anthracite coal") to the route that exposes it, with a
filter_hintto pass straight toeia_query_routeA multi-term query is also matched term by term, so combining a commodity, a metric, and a sector ("electricity price residential", "coal generation industrial sector") reaches the route carrying that data even when no single entry reads like the whole phrase. Each result keeps the better of its whole-phrase and per-term score; a single-term query takes the phrase path alone
scoreruns 0 (exact) to 1 (no match), lower is better; above0.72the match is unreliable and the query is worth narrowing.bun run eval:searchscores a labelled query battery against a live corpus, which is where that number comes fromThe first call waits for the whole corpus to warm — 24–30 s measured against the live API from cold, and never more than 45 s, so a degraded upstream cannot hold the call past a client's request timeout. Every later search is served from the in-process Fuse.js index in tens of milliseconds, with no upstream cost
indexCompletereports whether the answer was ranked against the whole corpus; when it is false,indexGapsnames the routes and index passes that are missing, so a short result set is never mistaken for a settled one
eia_query_route
Pull data from a leaf route.
Facet filters keyed by facet ID (e.g.
{ "stateid": "TX", "sectorid": ["RES", "COM"] })Date range and frequency selection; valid values discoverable via
eia_describe_routePagination via
offset/length(max 5,000 rows per page); total row count in responseAll numeric values arrive as strings from the EIA API — units appear as inline
{col}-unitsfields per rowRoute paths accept leading, trailing, and doubled slashes — an EIA-doc spelling like
/electricity/retail-sales/resolves to the same route, and the response echoes the canonical form backDataCanvas staging is opt-in per call via
stage: true: further pages are fetched and the accumulated rows are staged as adataset(df_<id>) for SQL, bounded byEIA_CANVAS_MAX_ROWS. The response note names how many rows actually reached the table. Left off (the default), a query costs one upstream request however large the match is, and the note namesstage: trueas the way to reach the rest.
Related MCP server: @cyanheads/federal-reserve-mcp-server
Features
Built on @cyanheads/mcp-ts-core:
Declarative tool definitions — single file per tool, framework handles registration and validation
Unified error handling — handlers throw, framework catches, classifies, and formats
Pluggable auth:
none,jwt,oauthSwappable storage backends:
in-memory,filesystem,Supabase,Cloudflare KV/R2/D1Structured logging with optional OpenTelemetry tracing
STDIO and Streamable HTTP transports
EIA-specific:
Full coverage of EIA API v2 — all 14 top-level dataset categories
In-process route tree cache with Fuse.js fuzzy index — built once on first use at a paced request rate, no repeated upstream calls
Facet values are searchable: a bounded pass indexes the fuel-type, sector, and technology vocabulary named in the route tree, and every described route folds its own values in at no upstream cost
Warm gaps are tracked, not swallowed: a route whose metadata could not be fetched is held as an incomplete stub rather than passed off as a queryable leaf, reported through
eia_search_routes, and re-fetched by the nexteia_browse_routescall that reaches itPer-route facet cache via
Promise.allfan-out — valid filter values available without re-fetchingSTEO series names (1,469 entries) indexed for natural-language discovery
DataCanvas (DuckDB) opt-in for tabular staging — the three dataframe tools are gated at registration, so a canvas-less deployment lists no tool it cannot serve
Getting started
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.3.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. |
|
| 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. |
|
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
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 and pull requests 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 installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-quality-maintenanceProvides 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.
- Alicense-qualityAmaintenanceSearch and fetch ~800K Federal Reserve economic time-series from the FRED API via MCP, with STDIO or Streamable HTTP transport.651Apache 2.0
- Alicense-qualityAmaintenanceExposes the FBI Crime Data Explorer API — crime estimates, agency offense rates, and LEOKA officer safety data via MCP. Supports STDIO or Streamable HTTP transport.1171Apache 2.0
- FlicenseAqualityCmaintenanceAn 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
Related MCP Connectors
Query SEC EDGAR filings, XBRL financials, and company data through MCP. STDIO & Streamable HTTP.
Econdata MCP — wraps BLS (Bureau of Labor Statistics) public API v2
NREL MCP — wraps the US National Renewable Energy Laboratory developer API
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/cyanheads/eia-energy-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server