@cyanheads/eia-energy-mcp-server 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CLAUDE.md +351 -0
  2. package/Dockerfile +99 -0
  3. package/LICENSE +195 -0
  4. package/README.md +274 -0
  5. package/changelog/0.1.x/0.1.0.md +18 -0
  6. package/changelog/0.1.x/0.1.1.md +42 -0
  7. package/changelog/0.1.x/0.1.2.md +22 -0
  8. package/changelog/0.1.x/0.1.3.md +17 -0
  9. package/changelog/0.1.x/0.1.4.md +17 -0
  10. package/changelog/0.1.x/0.1.5.md +19 -0
  11. package/changelog/0.1.x/0.1.6.md +19 -0
  12. package/changelog/0.1.x/0.1.7.md +11 -0
  13. package/changelog/0.2.x/0.2.0.md +22 -0
  14. package/changelog/template.md +93 -0
  15. package/dist/config/server-config.d.ts +18 -0
  16. package/dist/config/server-config.d.ts.map +1 -0
  17. package/dist/config/server-config.js +36 -0
  18. package/dist/config/server-config.js.map +1 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +39 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/mcp-server/tools/definitions/browse-routes.tool.d.ts +28 -0
  24. package/dist/mcp-server/tools/definitions/browse-routes.tool.d.ts.map +1 -0
  25. package/dist/mcp-server/tools/definitions/browse-routes.tool.js +72 -0
  26. package/dist/mcp-server/tools/definitions/browse-routes.tool.js.map +1 -0
  27. package/dist/mcp-server/tools/definitions/dataframe-describe.tool.d.ts +34 -0
  28. package/dist/mcp-server/tools/definitions/dataframe-describe.tool.d.ts.map +1 -0
  29. package/dist/mcp-server/tools/definitions/dataframe-describe.tool.js +114 -0
  30. package/dist/mcp-server/tools/definitions/dataframe-describe.tool.js.map +1 -0
  31. package/dist/mcp-server/tools/definitions/dataframe-drop.tool.d.ts +22 -0
  32. package/dist/mcp-server/tools/definitions/dataframe-drop.tool.d.ts.map +1 -0
  33. package/dist/mcp-server/tools/definitions/dataframe-drop.tool.js +56 -0
  34. package/dist/mcp-server/tools/definitions/dataframe-drop.tool.js.map +1 -0
  35. package/dist/mcp-server/tools/definitions/dataframe-query.tool.d.ts +28 -0
  36. package/dist/mcp-server/tools/definitions/dataframe-query.tool.d.ts.map +1 -0
  37. package/dist/mcp-server/tools/definitions/dataframe-query.tool.js +124 -0
  38. package/dist/mcp-server/tools/definitions/dataframe-query.tool.js.map +1 -0
  39. package/dist/mcp-server/tools/definitions/describe-route.tool.d.ts +58 -0
  40. package/dist/mcp-server/tools/definitions/describe-route.tool.d.ts.map +1 -0
  41. package/dist/mcp-server/tools/definitions/describe-route.tool.js +164 -0
  42. package/dist/mcp-server/tools/definitions/describe-route.tool.js.map +1 -0
  43. package/dist/mcp-server/tools/definitions/query-route.tool.d.ts +66 -0
  44. package/dist/mcp-server/tools/definitions/query-route.tool.d.ts.map +1 -0
  45. package/dist/mcp-server/tools/definitions/query-route.tool.js +264 -0
  46. package/dist/mcp-server/tools/definitions/query-route.tool.js.map +1 -0
  47. package/dist/mcp-server/tools/definitions/search-routes.tool.d.ts +23 -0
  48. package/dist/mcp-server/tools/definitions/search-routes.tool.d.ts.map +1 -0
  49. package/dist/mcp-server/tools/definitions/search-routes.tool.js +94 -0
  50. package/dist/mcp-server/tools/definitions/search-routes.tool.js.map +1 -0
  51. package/dist/services/canvas-bridge/canvas-bridge.d.ts +68 -0
  52. package/dist/services/canvas-bridge/canvas-bridge.d.ts.map +1 -0
  53. package/dist/services/canvas-bridge/canvas-bridge.js +206 -0
  54. package/dist/services/canvas-bridge/canvas-bridge.js.map +1 -0
  55. package/dist/services/canvas-bridge/sql-gate-extras.d.ts +13 -0
  56. package/dist/services/canvas-bridge/sql-gate-extras.d.ts.map +1 -0
  57. package/dist/services/canvas-bridge/sql-gate-extras.js +37 -0
  58. package/dist/services/canvas-bridge/sql-gate-extras.js.map +1 -0
  59. package/dist/services/eia/eia-service.d.ts +72 -0
  60. package/dist/services/eia/eia-service.d.ts.map +1 -0
  61. package/dist/services/eia/eia-service.js +497 -0
  62. package/dist/services/eia/eia-service.js.map +1 -0
  63. package/dist/services/eia/route-cache.d.ts +65 -0
  64. package/dist/services/eia/route-cache.d.ts.map +1 -0
  65. package/dist/services/eia/route-cache.js +168 -0
  66. package/dist/services/eia/route-cache.js.map +1 -0
  67. package/dist/services/eia/types.d.ts +115 -0
  68. package/dist/services/eia/types.d.ts.map +1 -0
  69. package/dist/services/eia/types.js +7 -0
  70. package/dist/services/eia/types.js.map +1 -0
  71. package/package.json +104 -0
  72. package/server.json +163 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-routes.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-routes.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @fileoverview Adapter between EIA tools and the framework DataCanvas
3
+ * primitive. Mints df_<id> table handles, derives all-nullable column schemas
4
+ * (EIA data values are all strings), tracks per-table TTL and provenance in
5
+ * ctx.state, and lazy-sweeps expired tables on every public operation.
6
+ * Best-effort: failed canvas operations log a warning and return undefined so
7
+ * the caller's inline response remains useful.
8
+ * @module services/canvas-bridge/canvas-bridge
9
+ */
10
+ import type { Context } from '@cyanheads/mcp-ts-core';
11
+ import { type ColumnSchema, type DataCanvas, type QueryResult } from '@cyanheads/mcp-ts-core/canvas';
12
+ /** Per-table provenance + TTL metadata persisted in ctx.state. */
13
+ export interface DataframeMeta {
14
+ columnSchema: ColumnSchema[];
15
+ createdAt: string;
16
+ expiresAt: string;
17
+ maxRows: number | undefined;
18
+ queryParams: Record<string, unknown>;
19
+ rowCount: number;
20
+ sourceTool: string;
21
+ tableName: string;
22
+ truncated: boolean;
23
+ }
24
+ export interface RegisterDataframeResult {
25
+ columnSchema: ColumnSchema[];
26
+ expiresAt: string;
27
+ rowCount: number;
28
+ tableName: string;
29
+ }
30
+ export interface RegisterDataframeOptions {
31
+ maxRows?: number;
32
+ queryParams: Record<string, unknown>;
33
+ rows: Record<string, unknown>[];
34
+ sourceTool: string;
35
+ truncated?: boolean;
36
+ }
37
+ export interface BridgeQueryOptions {
38
+ preview?: number;
39
+ queryParams?: Record<string, unknown>;
40
+ registerAs?: string;
41
+ rowLimit?: number;
42
+ sourceTool?: string;
43
+ }
44
+ /**
45
+ * Derive an all-nullable column schema from a row sample. EIA data values are
46
+ * all strings (VARCHAR). Forcing nullable=true prevents DuckDB appender
47
+ * rollbacks when sparse columns carry nulls past the sniff window.
48
+ */
49
+ export declare function deriveAllNullableSchema(rows: Record<string, unknown>[]): ColumnSchema[];
50
+ export declare class CanvasBridge {
51
+ private readonly canvas;
52
+ constructor(canvas: DataCanvas);
53
+ registerDataframe(ctx: Context, options: RegisterDataframeOptions): Promise<RegisterDataframeResult | undefined>;
54
+ describe(ctx: Context, tableName?: string): Promise<DataframeMeta[]>;
55
+ query(ctx: Context, sql: string, options?: BridgeQueryOptions): Promise<{
56
+ result: QueryResult;
57
+ meta?: DataframeMeta;
58
+ }>;
59
+ drop(ctx: Context, tableName: string): Promise<boolean>;
60
+ private sweepExpired;
61
+ private iterateMeta;
62
+ private acquireSharedCanvas;
63
+ private mintTableName;
64
+ }
65
+ export declare function initCanvasBridge(canvas: DataCanvas | undefined): void;
66
+ export declare function getCanvasBridge(): CanvasBridge | undefined;
67
+ export declare function _resetCanvasBridge(): void;
68
+ //# sourceMappingURL=canvas-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-bridge.d.ts","sourceRoot":"","sources":["../../../src/services/canvas-bridge/canvas-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,UAAU,EAEf,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAKvC,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,YAAY,EAAE,CAEvF;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,UAAU;IAEzC,iBAAiB,CACrB,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAoDzC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAWpE,KAAK,CACT,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,IAAI,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;IAsCnD,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAmB/C,YAAY;YAqBX,WAAW;YAcZ,mBAAmB;IAcjC,OAAO,CAAC,aAAa;CAKtB;AAID,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,CAErE;AAED,wBAAgB,eAAe,IAAI,YAAY,GAAG,SAAS,CAE1D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * @fileoverview Adapter between EIA tools and the framework DataCanvas
3
+ * primitive. Mints df_<id> table handles, derives all-nullable column schemas
4
+ * (EIA data values are all strings), tracks per-table TTL and provenance in
5
+ * ctx.state, and lazy-sweeps expired tables on every public operation.
6
+ * Best-effort: failed canvas operations log a warning and return undefined so
7
+ * the caller's inline response remains useful.
8
+ * @module services/canvas-bridge/canvas-bridge
9
+ */
10
+ import { inferSchemaFromRows, } from '@cyanheads/mcp-ts-core/canvas';
11
+ import { idGenerator } from '@cyanheads/mcp-ts-core/utils';
12
+ import { getServerConfig } from '../../config/server-config.js';
13
+ import { assertNoSystemCatalogAccess } from './sql-gate-extras.js';
14
+ const META_PREFIX = 'eia-df-meta/';
15
+ const CANVAS_ID_KEY = 'eia-canvas-id';
16
+ const TABLE_NAME_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
17
+ /**
18
+ * Derive an all-nullable column schema from a row sample. EIA data values are
19
+ * all strings (VARCHAR). Forcing nullable=true prevents DuckDB appender
20
+ * rollbacks when sparse columns carry nulls past the sniff window.
21
+ */
22
+ export function deriveAllNullableSchema(rows) {
23
+ return inferSchemaFromRows(rows).map((col) => ({ ...col, nullable: true }));
24
+ }
25
+ export class CanvasBridge {
26
+ canvas;
27
+ constructor(canvas) {
28
+ this.canvas = canvas;
29
+ }
30
+ async registerDataframe(ctx, options) {
31
+ if (options.rows.length === 0) {
32
+ ctx.log.debug('Skipping dataframe registration — no rows', {
33
+ sourceTool: options.sourceTool,
34
+ });
35
+ return;
36
+ }
37
+ try {
38
+ await this.sweepExpired(ctx);
39
+ const instance = await this.acquireSharedCanvas(ctx);
40
+ const tableName = this.mintTableName();
41
+ const schema = deriveAllNullableSchema(options.rows);
42
+ const result = await instance.registerTable(tableName, options.rows, { schema });
43
+ const now = Date.now();
44
+ const ttlMs = getServerConfig().datasetTtlSeconds * 1000;
45
+ const meta = {
46
+ tableName: result.tableName,
47
+ sourceTool: options.sourceTool,
48
+ queryParams: options.queryParams,
49
+ createdAt: new Date(now).toISOString(),
50
+ expiresAt: new Date(now + ttlMs).toISOString(),
51
+ rowCount: result.rowCount,
52
+ truncated: options.truncated ?? false,
53
+ maxRows: options.maxRows,
54
+ columnSchema: schema,
55
+ };
56
+ await ctx.state.set(`${META_PREFIX}${result.tableName}`, meta);
57
+ ctx.log.info('EIA dataframe registered', {
58
+ tableName: result.tableName,
59
+ rowCount: result.rowCount,
60
+ sourceTool: options.sourceTool,
61
+ });
62
+ return {
63
+ tableName: result.tableName,
64
+ rowCount: result.rowCount,
65
+ expiresAt: meta.expiresAt,
66
+ columnSchema: schema,
67
+ };
68
+ }
69
+ catch (error) {
70
+ ctx.log.warning('EIA dataframe registration failed', {
71
+ error: error instanceof Error ? error.message : String(error),
72
+ sourceTool: options.sourceTool,
73
+ });
74
+ return;
75
+ }
76
+ }
77
+ async describe(ctx, tableName) {
78
+ await this.sweepExpired(ctx);
79
+ if (tableName) {
80
+ const meta = await ctx.state.get(`${META_PREFIX}${tableName}`);
81
+ return meta ? [meta] : [];
82
+ }
83
+ const entries = [];
84
+ for await (const { meta } of this.iterateMeta(ctx))
85
+ entries.push(meta);
86
+ return entries.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
87
+ }
88
+ async query(ctx, sql, options = {}) {
89
+ assertNoSystemCatalogAccess(sql);
90
+ await this.sweepExpired(ctx);
91
+ const instance = await this.acquireSharedCanvas(ctx);
92
+ const result = await instance.query(sql, {
93
+ ...(options.preview !== undefined && { preview: options.preview }),
94
+ ...(options.rowLimit !== undefined && { rowLimit: options.rowLimit }),
95
+ ...(options.registerAs !== undefined && { registerAs: options.registerAs }),
96
+ signal: ctx.signal,
97
+ });
98
+ const registerAs = options.registerAs;
99
+ let meta;
100
+ if (registerAs && result.tableName) {
101
+ const now = Date.now();
102
+ const ttlMs = getServerConfig().datasetTtlSeconds * 1000;
103
+ meta = {
104
+ tableName: result.tableName,
105
+ sourceTool: options.sourceTool ?? 'eia_dataframe_query',
106
+ queryParams: options.queryParams ?? { sql },
107
+ createdAt: new Date(now).toISOString(),
108
+ expiresAt: new Date(now + ttlMs).toISOString(),
109
+ rowCount: result.rowCount,
110
+ truncated: false,
111
+ maxRows: undefined,
112
+ columnSchema: result.columns.map((name) => ({
113
+ name,
114
+ type: 'VARCHAR',
115
+ nullable: true,
116
+ })),
117
+ };
118
+ await ctx.state.set(`${META_PREFIX}${result.tableName}`, meta);
119
+ }
120
+ return meta ? { result, meta } : { result };
121
+ }
122
+ async drop(ctx, tableName) {
123
+ await this.sweepExpired(ctx);
124
+ const metaKey = `${META_PREFIX}${tableName}`;
125
+ const hadMeta = (await ctx.state.get(metaKey)) !== null;
126
+ await ctx.state.delete(metaKey);
127
+ try {
128
+ const instance = await this.acquireSharedCanvas(ctx);
129
+ const dropped = await instance.drop(tableName);
130
+ return dropped || hadMeta;
131
+ }
132
+ catch (error) {
133
+ ctx.log.warning('Canvas drop failed', {
134
+ tableName,
135
+ error: error instanceof Error ? error.message : String(error),
136
+ });
137
+ return hadMeta;
138
+ }
139
+ }
140
+ async sweepExpired(ctx) {
141
+ const nowIso = new Date().toISOString();
142
+ let instance;
143
+ for await (const { key, meta } of this.iterateMeta(ctx)) {
144
+ if (meta.expiresAt > nowIso)
145
+ continue;
146
+ instance ??= await this.acquireSharedCanvas(ctx).catch(() => undefined);
147
+ if (instance) {
148
+ try {
149
+ await instance.drop(meta.tableName);
150
+ }
151
+ catch (error) {
152
+ ctx.log.warning('TTL sweep drop failed', {
153
+ tableName: meta.tableName,
154
+ error: error instanceof Error ? error.message : String(error),
155
+ });
156
+ }
157
+ }
158
+ await ctx.state.delete(key);
159
+ ctx.log.debug('Expired EIA dataframe swept', { tableName: meta.tableName });
160
+ }
161
+ }
162
+ async *iterateMeta(ctx) {
163
+ let cursor;
164
+ do {
165
+ const page = await ctx.state.list(META_PREFIX, {
166
+ ...(cursor !== undefined && { cursor }),
167
+ limit: 100,
168
+ });
169
+ for (const item of page.items) {
170
+ if (item.value)
171
+ yield { key: item.key, meta: item.value };
172
+ }
173
+ cursor = page.cursor;
174
+ } while (cursor);
175
+ }
176
+ async acquireSharedCanvas(ctx) {
177
+ const stored = await ctx.state.get(CANVAS_ID_KEY);
178
+ if (stored) {
179
+ try {
180
+ return await this.canvas.acquire(stored, ctx);
181
+ }
182
+ catch {
183
+ await ctx.state.delete(CANVAS_ID_KEY);
184
+ }
185
+ }
186
+ const instance = await this.canvas.acquire(undefined, ctx);
187
+ await ctx.state.set(CANVAS_ID_KEY, instance.canvasId);
188
+ return instance;
189
+ }
190
+ mintTableName() {
191
+ const left = idGenerator.generateRandomString(5, TABLE_NAME_CHARSET);
192
+ const right = idGenerator.generateRandomString(5, TABLE_NAME_CHARSET);
193
+ return `df_${left}_${right}`;
194
+ }
195
+ }
196
+ let _bridge;
197
+ export function initCanvasBridge(canvas) {
198
+ _bridge = canvas ? new CanvasBridge(canvas) : undefined;
199
+ }
200
+ export function getCanvasBridge() {
201
+ return _bridge;
202
+ }
203
+ export function _resetCanvasBridge() {
204
+ _bridge = undefined;
205
+ }
206
+ //# sourceMappingURL=canvas-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-bridge.js","sourceRoot":"","sources":["../../../src/services/canvas-bridge/canvas-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview Bridge-layer SQL gate additions on top of the framework's
3
+ * read-only gate. Additionally denies access to DuckDB system catalogs
4
+ * (information_schema, pg_catalog, sqlite_master, duckdb_*) so callers cannot
5
+ * enumerate df_<id> handles they don't already hold.
6
+ * @module services/canvas-bridge/sql-gate-extras
7
+ */
8
+ /**
9
+ * Reject SELECTs referencing DuckDB system catalogs. Throws ValidationError
10
+ * with data.reason = 'system_catalog_access'.
11
+ */
12
+ export declare function assertNoSystemCatalogAccess(sql: string): void;
13
+ //# sourceMappingURL=sql-gate-extras.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-gate-extras.d.ts","sourceRoot":"","sources":["../../../src/services/canvas-bridge/sql-gate-extras.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAc7D"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @fileoverview Bridge-layer SQL gate additions on top of the framework's
3
+ * read-only gate. Additionally denies access to DuckDB system catalogs
4
+ * (information_schema, pg_catalog, sqlite_master, duckdb_*) so callers cannot
5
+ * enumerate df_<id> handles they don't already hold.
6
+ * @module services/canvas-bridge/sql-gate-extras
7
+ */
8
+ import { validationError } from '@cyanheads/mcp-ts-core/errors';
9
+ const FORBIDDEN_CATALOG_PATTERNS = [
10
+ /\binformation_schema\b/i,
11
+ /\bpg_catalog\b/i,
12
+ /\bsqlite_master\b/i,
13
+ /\bduckdb_[a-z_]+\b/i,
14
+ ];
15
+ function stripStringLiterals(sql) {
16
+ return sql.replace(/'([^'\\]|\\.|'')*'/g, "''").replace(/"([^"\\]|\\.|"")*"/g, '""');
17
+ }
18
+ /**
19
+ * Reject SELECTs referencing DuckDB system catalogs. Throws ValidationError
20
+ * with data.reason = 'system_catalog_access'.
21
+ */
22
+ export function assertNoSystemCatalogAccess(sql) {
23
+ const stripped = stripStringLiterals(sql);
24
+ for (const pattern of FORBIDDEN_CATALOG_PATTERNS) {
25
+ const match = stripped.match(pattern);
26
+ if (match) {
27
+ throw validationError(`SQL references a denied system catalog: ${match[0]}.`, {
28
+ reason: 'system_catalog_access',
29
+ catalog: match[0],
30
+ recovery: {
31
+ hint: 'Query only df_<id> tables. Use eia_dataframe_describe to list available dataframes.',
32
+ },
33
+ });
34
+ }
35
+ }
36
+ }
37
+ //# sourceMappingURL=sql-gate-extras.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-gate-extras.js","sourceRoot":"","sources":["../../../src/services/canvas-bridge/sql-gate-extras.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,MAAM,0BAA0B,GAA0B;IACxD,yBAAyB;IACzB,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;CACtB,CAAC;AAEF,SAAS,mBAAmB,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;AACvF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAAW;IACrD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC1C,KAAK,MAAM,OAAO,IAAI,0BAA0B,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,eAAe,CAAC,2CAA2C,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE;gBAC5E,MAAM,EAAE,uBAAuB;gBAC/B,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;gBACjB,QAAQ,EAAE;oBACR,IAAI,EAAE,qFAAqF;iBAC5F;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @fileoverview EIA API v2 service. Wraps api.eia.gov/v2 with retry/timeout,
3
+ * route tree caching, per-route facet metadata caching, and Fuse.js fuzzy
4
+ * search. Exposes browse, describe, query, and search methods consumed by MCP
5
+ * tool handlers. Rate-limit detection: EIA returns `OVER_RATE_LIMIT` in the
6
+ * response body — classified as ServiceUnavailable (retryable).
7
+ * @module services/eia/eia-service
8
+ */
9
+ import type { Context } from '@cyanheads/mcp-ts-core';
10
+ import type { DataResponse, RouteEntry, RouteMetadata, SearchIndexEntry } from './types.js';
11
+ declare class EiaApiService {
12
+ private get baseUrl();
13
+ private get apiKey();
14
+ private buildUrl;
15
+ private fetchJson;
16
+ /**
17
+ * Warm the route tree cache. Called lazily on first browse/search.
18
+ * Fetches top-level routes, recursively discovers children via the cache
19
+ * structure from GET /v2/ and per-node GETs, then builds the Fuse.js index.
20
+ * STEO series are fetched in parallel and appended to the index.
21
+ */
22
+ ensureCacheWarmed(ctx: Context): Promise<void>;
23
+ private warmCache;
24
+ private buildRouteTree;
25
+ private fetchSteoSeries;
26
+ /**
27
+ * Browse child routes at a given path. Returns list of children with leaf
28
+ * classification.
29
+ */
30
+ browse(path: string | undefined, ctx: Context): Promise<{
31
+ path: string;
32
+ children: RouteEntry[];
33
+ isLeaf: boolean;
34
+ }>;
35
+ /**
36
+ * Describe a leaf route — returns full metadata including facets with values.
37
+ * Results are cached per-route to avoid repeat fan-out.
38
+ */
39
+ describe(route: string, ctx: Context): Promise<RouteMetadata>;
40
+ private fetchAndCacheMetadata;
41
+ /**
42
+ * Fetch data from a leaf route. Returns rows (all string values per EIA API),
43
+ * total count, and any warnings.
44
+ */
45
+ query(route: string, opts: {
46
+ filters?: Record<string, string | string[]>;
47
+ columns?: string[];
48
+ frequency?: string;
49
+ start?: string;
50
+ end?: string;
51
+ sort?: Array<{
52
+ column: string;
53
+ direction: 'asc' | 'desc';
54
+ }>;
55
+ offset?: number;
56
+ length?: number;
57
+ }, ctx: Context): Promise<DataResponse>;
58
+ /** Fuzzy search across the route index. */
59
+ search(query: string, limit: number, ctx: Context): Promise<{
60
+ results: Array<{
61
+ entry: SearchIndexEntry;
62
+ score: number;
63
+ }>;
64
+ totalIndexed: number;
65
+ }>;
66
+ }
67
+ export declare function initEiaApiService(): void;
68
+ export declare function getEiaApiService(): EiaApiService;
69
+ /** Reset for tests. */
70
+ export declare function _resetEiaApiService(): void;
71
+ export {};
72
+ //# sourceMappingURL=eia-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eia-service.d.ts","sourceRoot":"","sources":["../../../src/services/eia/eia-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAoBtD,OAAO,KAAK,EACV,YAAY,EAMZ,UAAU,EACV,aAAa,EACb,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAQpB,cAAM,aAAa;IACjB,OAAO,KAAK,OAAO,GAElB;IAED,OAAO,KAAK,MAAM,GAEjB;IAED,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,SAAS;IAiFjB;;;;;OAKG;IACG,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;YAgBtC,SAAS;YAiCT,cAAc;YAiDd,eAAe;IAwB7B;;;OAGG;IACG,MAAM,CACV,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC;QACT,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;IA+CF;;;OAGG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;YA4CrD,qBAAqB;IAmGnC;;;OAGG;IACG,KAAK,CACT,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;QACJ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;SAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,EACD,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,YAAY,CAAC;IAkHxB,2CAA2C;IACrC,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,OAAO,GACX,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,gBAAgB,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CAKjG;AAID,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAIhD;AAED,uBAAuB;AACvB,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C"}