@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
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview eia-energy-mcp-server MCP server entry point.
4
+ * @module index
5
+ */
6
+ import { createApp, disabledTool } from '@cyanheads/mcp-ts-core';
7
+ import { getServerConfig } from './config/server-config.js';
8
+ import { browseRoutesTool } from './mcp-server/tools/definitions/browse-routes.tool.js';
9
+ import { dataframeDescribeTool } from './mcp-server/tools/definitions/dataframe-describe.tool.js';
10
+ import { dataframeDropTool } from './mcp-server/tools/definitions/dataframe-drop.tool.js';
11
+ import { dataframeQueryTool } from './mcp-server/tools/definitions/dataframe-query.tool.js';
12
+ import { describeRouteTool } from './mcp-server/tools/definitions/describe-route.tool.js';
13
+ import { queryRouteTool } from './mcp-server/tools/definitions/query-route.tool.js';
14
+ import { searchRoutesTool } from './mcp-server/tools/definitions/search-routes.tool.js';
15
+ import { initCanvasBridge } from './services/canvas-bridge/canvas-bridge.js';
16
+ import { initEiaApiService } from './services/eia/eia-service.js';
17
+ const serverConfig = getServerConfig();
18
+ const dropTool = serverConfig.dataframeDropEnabled
19
+ ? dataframeDropTool
20
+ : disabledTool(dataframeDropTool, {
21
+ reason: 'Dataframe drop is disabled in this deployment.',
22
+ hint: 'EIA_DATAFRAME_DROP_ENABLED=true',
23
+ });
24
+ await createApp({
25
+ tools: [
26
+ browseRoutesTool,
27
+ describeRouteTool,
28
+ searchRoutesTool,
29
+ queryRouteTool,
30
+ dataframeDescribeTool,
31
+ dataframeQueryTool,
32
+ dropTool,
33
+ ],
34
+ setup(core) {
35
+ initEiaApiService();
36
+ initCanvasBridge(core.canvas);
37
+ },
38
+ });
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sDAAsD,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2DAA2D,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sDAAsD,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;AAEvC,MAAM,QAAQ,GAAG,YAAY,CAAC,oBAAoB;IAChD,CAAC,CAAC,iBAAiB;IACnB,CAAC,CAAC,YAAY,CAAC,iBAAiB,EAAE;QAC9B,MAAM,EAAE,gDAAgD;QACxD,IAAI,EAAE,iCAAiC;KACxC,CAAC,CAAC;AAEP,MAAM,SAAS,CAAC;IACd,KAAK,EAAE;QACL,gBAAgB;QAChB,iBAAiB;QACjB,gBAAgB;QAChB,cAAc;QACd,qBAAqB;QACrB,kBAAkB;QAClB,QAAQ;KACT;IACD,KAAK,CAAC,IAAI;QACR,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_browse_routes. Lists child routes under
3
+ * a given path in the EIA dataset taxonomy. Start at root (omit path) to see
4
+ * the 14 top-level categories, then drill into subcategories until reaching
5
+ * leaf routes that can be queried with eia_query_route.
6
+ * @module mcp-server/tools/definitions/browse-routes.tool
7
+ */
8
+ import { z } from '@cyanheads/mcp-ts-core';
9
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
+ export declare const browseRoutesTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
11
+ path: z.ZodOptional<z.ZodString>;
12
+ }, z.core.$strip>, z.ZodObject<{
13
+ path: z.ZodString;
14
+ children: z.ZodArray<z.ZodObject<{
15
+ id: z.ZodString;
16
+ name: z.ZodString;
17
+ description: z.ZodString;
18
+ route: z.ZodString;
19
+ isLeaf: z.ZodBoolean;
20
+ }, z.core.$strip>>;
21
+ isLeaf: z.ZodBoolean;
22
+ }, z.core.$strip>, readonly [{
23
+ readonly reason: "route_not_found";
24
+ readonly code: JsonRpcErrorCode.NotFound;
25
+ readonly when: "Path does not exist in the EIA taxonomy.";
26
+ readonly recovery: "Call eia_browse_routes without a path to see valid top-level categories.";
27
+ }]>;
28
+ //# sourceMappingURL=browse-routes.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browse-routes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-routes.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;GA2E3B,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_browse_routes. Lists child routes under
3
+ * a given path in the EIA dataset taxonomy. Start at root (omit path) to see
4
+ * the 14 top-level categories, then drill into subcategories until reaching
5
+ * leaf routes that can be queried with eia_query_route.
6
+ * @module mcp-server/tools/definitions/browse-routes.tool
7
+ */
8
+ import { tool, z } from '@cyanheads/mcp-ts-core';
9
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
+ import { getEiaApiService } from '../../../services/eia/eia-service.js';
11
+ export const browseRoutesTool = tool('eia_browse_routes', {
12
+ title: 'Browse EIA Routes',
13
+ description: '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.',
14
+ annotations: { readOnlyHint: true, openWorldHint: false },
15
+ input: z.object({
16
+ path: z
17
+ .string()
18
+ .optional()
19
+ .describe('Route path to browse (e.g. "electricity", "petroleum/pri"). Omit for root.'),
20
+ }),
21
+ output: z.object({
22
+ path: z.string().describe('The path that was browsed (empty string for root).'),
23
+ children: z
24
+ .array(z
25
+ .object({
26
+ id: z.string().describe('Route segment ID.'),
27
+ name: z.string().describe('Human-readable name.'),
28
+ description: z.string().describe('Route description.'),
29
+ route: z
30
+ .string()
31
+ .describe('Full route path usable in eia_describe_route or eia_query_route.'),
32
+ isLeaf: z
33
+ .boolean()
34
+ .describe('True when this child is a queryable leaf route with data.'),
35
+ })
36
+ .describe('A child route entry.'))
37
+ .describe('Child entries under the browsed path.'),
38
+ isLeaf: z
39
+ .boolean()
40
+ .describe('True when the browsed path itself is a leaf route — no children to drill into; use eia_describe_route instead.'),
41
+ }),
42
+ errors: [
43
+ {
44
+ reason: 'route_not_found',
45
+ code: JsonRpcErrorCode.NotFound,
46
+ when: 'Path does not exist in the EIA taxonomy.',
47
+ recovery: 'Call eia_browse_routes without a path to see valid top-level categories.',
48
+ },
49
+ ],
50
+ handler(input, ctx) {
51
+ ctx.log.info('Executing eia_browse_routes', { path: input.path });
52
+ return getEiaApiService().browse(input.path, ctx);
53
+ },
54
+ format: (result) => {
55
+ const lines = [];
56
+ if (result.isLeaf && result.children.length === 0) {
57
+ lines.push(`**${result.path}** is a leaf route — use eia_describe_route to inspect its facets and columns.`);
58
+ }
59
+ const label = result.path ? `**${result.path}**` : 'root';
60
+ if (result.children.length > 0) {
61
+ lines.push(`Children of ${label} (${result.children.length}):\n`);
62
+ }
63
+ for (const c of result.children) {
64
+ const tag = c.isLeaf ? '[leaf]' : '[cat]';
65
+ lines.push(`${tag} **${c.route}** (${c.id}) — ${c.name}`);
66
+ if (c.description)
67
+ lines.push(` ${c.description}`);
68
+ }
69
+ return [{ type: 'text', text: lines.join('\n') }];
70
+ },
71
+ });
72
+ //# sourceMappingURL=browse-routes.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browse-routes.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-routes.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_dataframe_describe. Lists canvas
3
+ * dataframes materialized by eia_query_route with provenance, TTL, row count,
4
+ * and column schema. Lazy-sweeps expired tables before responding so the list
5
+ * is always current.
6
+ * @module mcp-server/tools/definitions/dataframe-describe.tool
7
+ */
8
+ import { z } from '@cyanheads/mcp-ts-core';
9
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
+ export declare const dataframeDescribeTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
11
+ name: z.ZodOptional<z.ZodString>;
12
+ }, z.core.$strip>, z.ZodObject<{
13
+ dataframes: z.ZodArray<z.ZodObject<{
14
+ name: z.ZodString;
15
+ source_tool: z.ZodString;
16
+ query_params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
17
+ created_at: z.ZodString;
18
+ expires_at: z.ZodString;
19
+ row_count: z.ZodNumber;
20
+ truncated: z.ZodBoolean;
21
+ max_rows: z.ZodOptional<z.ZodNumber>;
22
+ column_schema: z.ZodArray<z.ZodObject<{
23
+ name: z.ZodString;
24
+ type: z.ZodString;
25
+ nullable: z.ZodBoolean;
26
+ }, z.core.$strip>>;
27
+ }, z.core.$strip>>;
28
+ }, z.core.$strip>, readonly [{
29
+ readonly reason: "canvas_unavailable";
30
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
31
+ readonly when: "DataCanvas service is not configured for this deployment.";
32
+ readonly recovery: "Set CANVAS_PROVIDER_TYPE=duckdb in the server environment to enable dataframes.";
33
+ }]>;
34
+ //# sourceMappingURL=dataframe-describe.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataframe-describe.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/dataframe-describe.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;GAqHhC,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_dataframe_describe. Lists canvas
3
+ * dataframes materialized by eia_query_route with provenance, TTL, row count,
4
+ * and column schema. Lazy-sweeps expired tables before responding so the list
5
+ * is always current.
6
+ * @module mcp-server/tools/definitions/dataframe-describe.tool
7
+ */
8
+ import { tool, z } from '@cyanheads/mcp-ts-core';
9
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
+ import { getCanvasBridge } from '../../../services/canvas-bridge/canvas-bridge.js';
11
+ export const dataframeDescribeTool = tool('eia_dataframe_describe', {
12
+ title: 'Describe EIA Dataframes',
13
+ description: 'List canvas dataframes (df_<id>) materialized by eia_query_route, with provenance, TTL, row count, and column schema. Lazy-sweeps expired tables 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.',
14
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
15
+ errors: [
16
+ {
17
+ reason: 'canvas_unavailable',
18
+ code: JsonRpcErrorCode.ServiceUnavailable,
19
+ when: 'DataCanvas service is not configured for this deployment.',
20
+ recovery: 'Set CANVAS_PROVIDER_TYPE=duckdb in the server environment to enable dataframes.',
21
+ },
22
+ ],
23
+ input: z.object({
24
+ name: z
25
+ .string()
26
+ .optional()
27
+ .describe('df_<id> handle to describe a single dataframe. Omit to list all active dataframes.'),
28
+ }),
29
+ output: z.object({
30
+ dataframes: z
31
+ .array(z
32
+ .object({
33
+ name: z.string().describe('Canvas table name (df_<id>).'),
34
+ source_tool: z.string().describe('Tool that produced this dataframe.'),
35
+ query_params: z
36
+ .record(z.string(), z.unknown())
37
+ .describe('Input parameters the source tool was called with.'),
38
+ created_at: z.string().describe('ISO 8601 creation timestamp.'),
39
+ expires_at: z.string().describe('ISO 8601 expiry timestamp (sliding TTL).'),
40
+ row_count: z.number().describe('Rows materialized in the dataframe.'),
41
+ truncated: z
42
+ .boolean()
43
+ .describe('True when the EIA upstream had more rows than were registered.'),
44
+ max_rows: z
45
+ .number()
46
+ .optional()
47
+ .describe('Materialization cap that produced truncated, when applicable.'),
48
+ column_schema: z
49
+ .array(z
50
+ .object({
51
+ name: z.string().describe('Column name.'),
52
+ type: z.string().describe('DuckDB column type (VARCHAR for EIA data values).'),
53
+ nullable: z.boolean().describe('Whether the column permits NULL.'),
54
+ })
55
+ .describe('A column in the dataframe schema.'))
56
+ .describe('Column schema (all EIA data columns are VARCHAR and nullable).'),
57
+ })
58
+ .describe('A canvas dataframe entry.'))
59
+ .describe('Active dataframes for this tenant, newest first. Empty when none are registered.'),
60
+ }),
61
+ async handler(input, ctx) {
62
+ const bridge = getCanvasBridge();
63
+ if (!bridge) {
64
+ throw ctx.fail('canvas_unavailable', 'DataCanvas is not configured on this server.', {
65
+ ...ctx.recoveryFor('canvas_unavailable'),
66
+ });
67
+ }
68
+ const entries = await bridge.describe(ctx, input.name);
69
+ return {
70
+ dataframes: entries.map((meta) => ({
71
+ name: meta.tableName,
72
+ source_tool: meta.sourceTool,
73
+ query_params: meta.queryParams,
74
+ created_at: meta.createdAt,
75
+ expires_at: meta.expiresAt,
76
+ row_count: meta.rowCount,
77
+ truncated: meta.truncated,
78
+ max_rows: meta.maxRows,
79
+ column_schema: meta.columnSchema.map((c) => ({
80
+ name: c.name,
81
+ type: c.type,
82
+ nullable: c.nullable ?? true,
83
+ })),
84
+ })),
85
+ };
86
+ },
87
+ format: (result) => {
88
+ if (result.dataframes.length === 0) {
89
+ return [{ type: 'text', text: 'No active dataframes.' }];
90
+ }
91
+ const lines = [`**${result.dataframes.length} active dataframe(s):**\n`];
92
+ for (const df of result.dataframes) {
93
+ const truncated = df.truncated
94
+ ? ` (truncated${df.max_rows != null ? ` at ${df.max_rows}` : ''})`
95
+ : '';
96
+ lines.push(`### ${df.name}`);
97
+ lines.push(`- Source: ${df.source_tool}`);
98
+ lines.push(`- Rows: ${df.row_count}${truncated}`);
99
+ lines.push(`- Created: ${df.created_at} — Expires: ${df.expires_at}`);
100
+ const paramEntries = Object.entries(df.query_params);
101
+ if (paramEntries.length > 0) {
102
+ const params = paramEntries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(', ');
103
+ lines.push(`- Params: ${params}`);
104
+ }
105
+ const cols = df.column_schema
106
+ .map((c) => `${c.name}:${c.type}(nullable=${c.nullable})`)
107
+ .join(', ');
108
+ lines.push(`- Columns: ${cols}`);
109
+ lines.push('');
110
+ }
111
+ return [{ type: 'text', text: lines.join('\n').trimEnd() }];
112
+ },
113
+ });
114
+ //# sourceMappingURL=dataframe-describe.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataframe-describe.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/dataframe-describe.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_dataframe_drop. Drops a canvas
3
+ * dataframe by name. Opt-in — only registered in createApp() when
4
+ * EIA_DATAFRAME_DROP_ENABLED=true. Idempotent: returns dropped=false when
5
+ * nothing matched. TTL (default 24 h) handles cleanup in normal operation;
6
+ * this tool is for manual cleanup in long-running sessions.
7
+ * @module mcp-server/tools/definitions/dataframe-drop.tool
8
+ */
9
+ import { z } from '@cyanheads/mcp-ts-core';
10
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
11
+ export declare const dataframeDropTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
12
+ name: z.ZodString;
13
+ }, z.core.$strip>, z.ZodObject<{
14
+ name: z.ZodString;
15
+ dropped: z.ZodBoolean;
16
+ }, z.core.$strip>, readonly [{
17
+ readonly reason: "canvas_unavailable";
18
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
19
+ readonly when: "DataCanvas service is not configured for this deployment.";
20
+ readonly recovery: "Set CANVAS_PROVIDER_TYPE=duckdb in the server environment to enable dataframes.";
21
+ }]>;
22
+ //# sourceMappingURL=dataframe-drop.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataframe-drop.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/dataframe-drop.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,iBAAiB;;;;;;;;;;GAkD5B,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_dataframe_drop. Drops a canvas
3
+ * dataframe by name. Opt-in — only registered in createApp() when
4
+ * EIA_DATAFRAME_DROP_ENABLED=true. Idempotent: returns dropped=false when
5
+ * nothing matched. TTL (default 24 h) handles cleanup in normal operation;
6
+ * this tool is for manual cleanup in long-running sessions.
7
+ * @module mcp-server/tools/definitions/dataframe-drop.tool
8
+ */
9
+ import { tool, z } from '@cyanheads/mcp-ts-core';
10
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
11
+ import { getCanvasBridge } from '../../../services/canvas-bridge/canvas-bridge.js';
12
+ export const dataframeDropTool = tool('eia_dataframe_drop', {
13
+ title: 'Drop EIA Dataframe',
14
+ description: 'Drop a canvas dataframe by name. Idempotent — returns dropped=false when nothing matched. Use to free canvas resources ahead of the per-table TTL when an analysis is complete. In normal operation, TTL cleanup (default 24 h, sliding) is sufficient and this tool is unnecessary. Only available when EIA_DATAFRAME_DROP_ENABLED=true.',
15
+ annotations: {
16
+ readOnlyHint: false,
17
+ idempotentHint: true,
18
+ destructiveHint: true,
19
+ openWorldHint: false,
20
+ },
21
+ errors: [
22
+ {
23
+ reason: 'canvas_unavailable',
24
+ code: JsonRpcErrorCode.ServiceUnavailable,
25
+ when: 'DataCanvas service is not configured for this deployment.',
26
+ recovery: 'Set CANVAS_PROVIDER_TYPE=duckdb in the server environment to enable dataframes.',
27
+ },
28
+ ],
29
+ input: z.object({
30
+ name: z.string().min(1).describe('df_<id> handle to drop.'),
31
+ }),
32
+ output: z.object({
33
+ name: z.string().describe('The name that was requested.'),
34
+ dropped: z
35
+ .boolean()
36
+ .describe('True when the dataframe existed and was removed; false when nothing matched.'),
37
+ }),
38
+ async handler(input, ctx) {
39
+ const bridge = getCanvasBridge();
40
+ if (!bridge) {
41
+ throw ctx.fail('canvas_unavailable', 'DataCanvas is not configured on this server.', {
42
+ ...ctx.recoveryFor('canvas_unavailable'),
43
+ });
44
+ }
45
+ const dropped = await bridge.drop(ctx, input.name);
46
+ ctx.log.info('EIA dataframe drop requested', { name: input.name, dropped });
47
+ return { name: input.name, dropped };
48
+ },
49
+ format: (result) => [
50
+ {
51
+ type: 'text',
52
+ text: result.dropped ? `Dropped ${result.name}.` : `${result.name} not found.`,
53
+ },
54
+ ],
55
+ });
56
+ //# sourceMappingURL=dataframe-drop.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataframe-drop.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/dataframe-drop.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAE5E,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,oBAAoB,EAAE;IAC1D,KAAK,EAAE,oBAAoB;IAC3B,WAAW,EACT,2UAA2U;IAC7U,WAAW,EAAE;QACX,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,IAAI;QACpB,eAAe,EAAE,IAAI;QACrB,aAAa,EAAE,KAAK;KACrB;IAED,MAAM,EAAE;QACN;YACE,MAAM,EAAE,oBAAoB;YAC5B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;YACzC,IAAI,EAAE,2DAA2D;YACjE,QAAQ,EAAE,iFAAiF;SAC5F;KACF;IAED,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC;KAC5D,CAAC;IAEF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QACzD,OAAO,EAAE,CAAC;aACP,OAAO,EAAE;aACT,QAAQ,CAAC,8EAA8E,CAAC;KAC5F,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG;QACtB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,8CAA8C,EAAE;gBACnF,GAAG,GAAG,CAAC,WAAW,CAAC,oBAAoB,CAAC;aACzC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5E,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC;QAClB;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,aAAa;SAC/E;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_dataframe_query. Runs a single-statement
3
+ * SELECT against canvas dataframes registered by eia_query_route. Four-layer
4
+ * read-only enforcement: text deny-list, statement count, statement type, and
5
+ * EXPLAIN-plan walk in the framework; plus bridge-layer system-catalog deny.
6
+ * EIA data values are VARCHAR — cast to DOUBLE for arithmetic.
7
+ * @module mcp-server/tools/definitions/dataframe-query.tool
8
+ */
9
+ import { z } from '@cyanheads/mcp-ts-core';
10
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
11
+ export declare const dataframeQueryTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
12
+ sql: z.ZodString;
13
+ register_as: z.ZodOptional<z.ZodString>;
14
+ preview: z.ZodOptional<z.ZodNumber>;
15
+ row_limit: z.ZodDefault<z.ZodNumber>;
16
+ }, z.core.$strip>, z.ZodObject<{
17
+ columns: z.ZodArray<z.ZodString>;
18
+ row_count: z.ZodNumber;
19
+ rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
20
+ registered_as: z.ZodOptional<z.ZodString>;
21
+ expires_at: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strip>, readonly [{
23
+ readonly reason: "canvas_unavailable";
24
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
25
+ readonly when: "DataCanvas service is not configured for this deployment.";
26
+ readonly recovery: "Set CANVAS_PROVIDER_TYPE=duckdb in the server environment to enable dataframes.";
27
+ }]>;
28
+ //# sourceMappingURL=dataframe-query.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataframe-query.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/dataframe-query.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;GAoI7B,CAAC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_dataframe_query. Runs a single-statement
3
+ * SELECT against canvas dataframes registered by eia_query_route. Four-layer
4
+ * read-only enforcement: text deny-list, statement count, statement type, and
5
+ * EXPLAIN-plan walk in the framework; plus bridge-layer system-catalog deny.
6
+ * EIA data values are VARCHAR — cast to DOUBLE for arithmetic.
7
+ * @module mcp-server/tools/definitions/dataframe-query.tool
8
+ */
9
+ import { tool, z } from '@cyanheads/mcp-ts-core';
10
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
11
+ import { getCanvasBridge } from '../../../services/canvas-bridge/canvas-bridge.js';
12
+ export const dataframeQueryTool = tool('eia_dataframe_query', {
13
+ title: 'Query EIA Dataframes',
14
+ description: 'Run a single-statement SELECT against canvas dataframes registered by eia_query_route. 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 at the bridge layer. 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 TTL.',
15
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
16
+ errors: [
17
+ {
18
+ reason: 'canvas_unavailable',
19
+ code: JsonRpcErrorCode.ServiceUnavailable,
20
+ when: 'DataCanvas service is not configured for this deployment.',
21
+ recovery: 'Set CANVAS_PROVIDER_TYPE=duckdb in the server environment to enable dataframes.',
22
+ },
23
+ ],
24
+ input: z.object({
25
+ sql: z
26
+ .string()
27
+ .min(1)
28
+ .describe('Single-statement SELECT against df_<id> tables. EIA data columns are VARCHAR — use CAST(col AS DOUBLE) for arithmetic. Example: SELECT period, CAST(value AS DOUBLE) AS val FROM df_XXXXX ORDER BY period'),
29
+ register_as: z
30
+ .string()
31
+ .optional()
32
+ .describe('When set, persist the result as a new dataframe with a fresh TTL. Use to chain analyses without re-running upstream queries. Conflicts with an existing name throw Conflict.'),
33
+ preview: z
34
+ .number()
35
+ .int()
36
+ .min(0)
37
+ .max(10000)
38
+ .optional()
39
+ .describe('Rows to include in the immediate response. Defaults to row_limit. Set lower when chaining via register_as and only a sample is needed inline.'),
40
+ row_limit: z
41
+ .number()
42
+ .int()
43
+ .min(1)
44
+ .max(10000)
45
+ .default(1000)
46
+ .describe('Hard cap on rows materialized in the response (default 1000, max 10000).'),
47
+ }),
48
+ output: z.object({
49
+ columns: z.array(z.string()).describe('Column names in projection order.'),
50
+ row_count: z
51
+ .number()
52
+ .describe('Total rows the query produced (may exceed rows.length when capped).'),
53
+ rows: z
54
+ .array(z.record(z.string(), z.unknown()))
55
+ .describe('Materialized rows, bounded by preview / row_limit.'),
56
+ registered_as: z
57
+ .string()
58
+ .optional()
59
+ .describe('Set when register_as was supplied and the new dataframe was materialized.'),
60
+ expires_at: z
61
+ .string()
62
+ .optional()
63
+ .describe('ISO 8601 expiry for the newly registered dataframe, when applicable.'),
64
+ }),
65
+ async handler(input, ctx) {
66
+ const bridge = getCanvasBridge();
67
+ if (!bridge) {
68
+ throw ctx.fail('canvas_unavailable', 'DataCanvas is not configured on this server.', {
69
+ ...ctx.recoveryFor('canvas_unavailable'),
70
+ });
71
+ }
72
+ const { result, meta } = await bridge.query(ctx, input.sql, {
73
+ ...(input.register_as !== undefined && { registerAs: input.register_as }),
74
+ ...(input.preview !== undefined && { preview: input.preview }),
75
+ rowLimit: input.row_limit,
76
+ sourceTool: 'eia_dataframe_query',
77
+ queryParams: { sql: input.sql },
78
+ });
79
+ ctx.log.info('EIA dataframe query executed', {
80
+ rowCount: result.rowCount,
81
+ returned: result.rows.length,
82
+ registeredAs: meta?.tableName,
83
+ });
84
+ return {
85
+ columns: result.columns,
86
+ row_count: result.rowCount,
87
+ rows: result.rows,
88
+ registered_as: meta?.tableName,
89
+ expires_at: meta?.expiresAt,
90
+ };
91
+ },
92
+ format: (result) => {
93
+ const lines = [];
94
+ if (result.registered_as) {
95
+ lines.push(`Registered as ${result.registered_as} (expires ${result.expires_at ?? 'unknown'}).`);
96
+ }
97
+ const cappedNote = result.row_count > result.rows.length
98
+ ? ` (showing ${result.rows.length} of ${result.row_count})`
99
+ : '';
100
+ lines.push(`**${result.row_count} rows**${cappedNote}\n`);
101
+ if (result.rows.length === 0) {
102
+ lines.push('_No rows._');
103
+ return [{ type: 'text', text: lines.join('\n') }];
104
+ }
105
+ const header = `| ${result.columns.join(' | ')} |`;
106
+ const sep = `| ${result.columns.map(() => '---').join(' | ')} |`;
107
+ lines.push(header, sep);
108
+ for (const row of result.rows) {
109
+ const cells = result.columns.map((c) => {
110
+ const v = row[c];
111
+ if (v === null || v === undefined)
112
+ return '';
113
+ if (typeof v === 'string')
114
+ return v.replace(/\|/g, '\\|');
115
+ if (typeof v === 'object')
116
+ return JSON.stringify(v).replace(/\|/g, '\\|');
117
+ return String(v);
118
+ });
119
+ lines.push(`| ${cells.join(' | ')} |`);
120
+ }
121
+ return [{ type: 'text', text: lines.join('\n') }];
122
+ },
123
+ });
124
+ //# sourceMappingURL=dataframe-query.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataframe-query.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/dataframe-query.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview Tool definition for eia_describe_route. Returns full metadata
3
+ * for a leaf route: facets with valid values, data columns, frequencies, units,
4
+ * and date range. Required reading before constructing filters for eia_query_route.
5
+ * Facet values are fetched from separate /facet/{id} endpoints and merged.
6
+ * @module mcp-server/tools/definitions/describe-route.tool
7
+ */
8
+ import { z } from '@cyanheads/mcp-ts-core';
9
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
+ export declare const describeRouteTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
11
+ route: z.ZodString;
12
+ }, z.core.$strip>, z.ZodObject<{
13
+ route: z.ZodString;
14
+ description: z.ZodString;
15
+ facets: z.ZodArray<z.ZodObject<{
16
+ id: z.ZodString;
17
+ description: z.ZodString;
18
+ values: z.ZodArray<z.ZodObject<{
19
+ id: z.ZodString;
20
+ name: z.ZodString;
21
+ alias: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strip>>;
23
+ }, z.core.$strip>>;
24
+ data_columns: z.ZodArray<z.ZodObject<{
25
+ id: z.ZodString;
26
+ alias: z.ZodString;
27
+ units: z.ZodString;
28
+ }, z.core.$strip>>;
29
+ frequencies: z.ZodArray<z.ZodObject<{
30
+ id: z.ZodString;
31
+ description: z.ZodString;
32
+ query: z.ZodString;
33
+ format: z.ZodString;
34
+ }, z.core.$strip>>;
35
+ date_range: z.ZodObject<{
36
+ start: z.ZodString;
37
+ end: z.ZodString;
38
+ }, z.core.$strip>;
39
+ default_frequency: z.ZodString;
40
+ default_date_format: z.ZodString;
41
+ }, z.core.$strip>, readonly [{
42
+ readonly reason: "route_not_found";
43
+ readonly code: JsonRpcErrorCode.NotFound;
44
+ readonly when: "Route does not exist in the EIA taxonomy.";
45
+ readonly recovery: "Use eia_browse_routes or eia_search_routes to discover valid leaf route paths.";
46
+ }, {
47
+ readonly reason: "route_not_queryable";
48
+ readonly code: JsonRpcErrorCode.ValidationError;
49
+ readonly when: "Route is a category node with sub-routes, not a queryable leaf.";
50
+ readonly recovery: "Use eia_browse_routes to drill into sub-routes, or eia_search_routes to find leaf routes.";
51
+ }, {
52
+ readonly reason: "rate_limited";
53
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
54
+ readonly retryable: true;
55
+ readonly when: "EIA rate limit hit during facet fan-out.";
56
+ readonly recovery: "Back off and retry; use a production EIA API key for higher rate limits.";
57
+ }]>;
58
+ //# sourceMappingURL=describe-route.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe-route.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/describe-route.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoL5B,CAAC"}