@energyatit/mcp-server 0.1.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.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # @energyatit/mcp-server
2
+
3
+ MCP server for the EnergyAtIt energy infrastructure platform. Connect Claude, GPT, or any MCP-compatible AI client to real energy grid data — dispatch batteries, verify carbon chains, run demand response, and more.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx @energyatit/mcp-server
9
+ ```
10
+
11
+ ## Configure with Claude Desktop
12
+
13
+ Add to your `claude_desktop_config.json`:
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "energyatit": {
19
+ "command": "npx",
20
+ "args": ["-y", "@energyatit/mcp-server"],
21
+ "env": {
22
+ "ENERGYATIT_API_KEY": "eat_live_your_key_here"
23
+ }
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ ## Configure with Claude Code
30
+
31
+ ```bash
32
+ claude mcp add energyatit -- npx -y @energyatit/mcp-server
33
+ ```
34
+
35
+ Set your API key:
36
+ ```bash
37
+ export ENERGYATIT_API_KEY=eat_live_your_key_here
38
+ ```
39
+
40
+ ## Environment Variables
41
+
42
+ | Variable | Description |
43
+ |----------|-------------|
44
+ | `ENERGYATIT_API_KEY` | Your API key (`eat_live_xxx` or `eat_test_xxx`) |
45
+ | `ENERGYATIT_TOKEN` | JWT token (alternative to API key) |
46
+ | `ENERGYATIT_BASE_URL` | API base URL (default: `https://energyatit.com`) |
47
+
48
+ ## Available Tools
49
+
50
+ ### Sites & Assets
51
+ - `list_sites` — List all energy sites
52
+ - `get_site` — Get site details
53
+ - `list_assets` — List assets (BESS, HVAC, Solar, EV chargers)
54
+ - `list_grid_connections` — List grid connections
55
+
56
+ ### Dispatch
57
+ - `dispatch_command` — Send commands to assets (charge, discharge, curtail, shed_load)
58
+ - `dispatch_history` — View dispatch history
59
+
60
+ ### Carbon Attestation
61
+ - `create_carbon_record` — Append to the SHA-256 hash chain
62
+ - `verify_carbon_chain` — Verify hash chain integrity
63
+ - `get_carbon_certificate` — Generate carbon certificates
64
+ - `get_carbon_attestation` — Get attestation for a site
65
+
66
+ ### Demand Response
67
+ - `create_dr_event` — Create DR events (shed, shift, shimmy)
68
+ - `list_dr_events` — List DR events
69
+ - `dispatch_dr_event` — Execute DR dispatch
70
+ - `settle_dr_event` — Settle with carbon attestation
71
+
72
+ ### Settlements
73
+ - `generate_settlement` — Generate hash-chained settlement
74
+ - `verify_settlement` — Verify settlement integrity
75
+ - `list_settlements` — List settlements
76
+
77
+ ### Compliance
78
+ - `generate_compliance_package` — IEC 61850, ISO 50001, GHG Scope 2
79
+ - `list_compliance_packages` — List packages
80
+ - `generate_scope2_report` — GHG Scope 2 report
81
+
82
+ ### Intelligence
83
+ - `get_asset_reliability` — Asset reliability score
84
+ - `get_site_reliability` — Site reliability score
85
+ - `get_grid_capacity` — Grid capacity by region
86
+ - `get_grid_trends` — Historical grid trends
87
+
88
+ ### Procurement
89
+ - `create_procurement` — PPA, REC, carbon offset requests
90
+ - `analyze_procurement` — Run procurement analysis
91
+ - `get_procurement_options` — Get available options
92
+
93
+ ### Sandbox
94
+ - `provision_sandbox` — Get a sandbox environment with test data
95
+ - `sandbox_status` — Check sandbox usage
96
+
97
+ ## Get an API Key
98
+
99
+ 1. Sign up at [energyatit.com](https://energyatit.com)
100
+ 2. Go to Settings > API Keys
101
+ 3. Create a new key
102
+ 4. Or provision a sandbox: use the `provision_sandbox` tool
103
+
104
+ ## License
105
+
106
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,494 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ // ─── Config ────────────────────────────────────────────────────────────────
6
+ const BASE_URL = (process.env.ENERGYATIT_BASE_URL ??
7
+ process.env.ENERGYATIT_URL ??
8
+ "https://energyatit.com").replace(/\/$/, "");
9
+ const API_KEY = process.env.ENERGYATIT_API_KEY ?? "";
10
+ const TOKEN = process.env.ENERGYATIT_TOKEN ?? "";
11
+ function authHeaders() {
12
+ const h = { "Content-Type": "application/json" };
13
+ if (TOKEN)
14
+ h["Authorization"] = `Bearer ${TOKEN}`;
15
+ else if (API_KEY)
16
+ h["X-API-Key"] = API_KEY;
17
+ return h;
18
+ }
19
+ // ─── HTTP helpers ──────────────────────────────────────────────────────────
20
+ async function apiGet(path) {
21
+ const res = await fetch(`${BASE_URL}${path}`, { headers: authHeaders() });
22
+ const json = await res.json();
23
+ if (json.success === false)
24
+ throw new Error(String(json.error ?? `API error ${res.status}`));
25
+ return json.data ?? json;
26
+ }
27
+ async function apiPost(path, body) {
28
+ const res = await fetch(`${BASE_URL}${path}`, {
29
+ method: "POST",
30
+ headers: authHeaders(),
31
+ body: body ? JSON.stringify(body) : undefined,
32
+ });
33
+ const json = await res.json();
34
+ if (json.success === false)
35
+ throw new Error(String(json.error ?? `API error ${res.status}`));
36
+ return json.data ?? json;
37
+ }
38
+ async function apiPatch(path, body) {
39
+ const res = await fetch(`${BASE_URL}${path}`, {
40
+ method: "PATCH",
41
+ headers: authHeaders(),
42
+ body: body ? JSON.stringify(body) : undefined,
43
+ });
44
+ const json = await res.json();
45
+ if (json.success === false)
46
+ throw new Error(String(json.error ?? `API error ${res.status}`));
47
+ return json.data ?? json;
48
+ }
49
+ function text(data) {
50
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
51
+ }
52
+ function errorResult(err) {
53
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
54
+ }
55
+ // ─── MCP Server ────────────────────────────────────────────────────────────
56
+ const server = new McpServer({
57
+ name: "energyatit",
58
+ version: "0.1.0",
59
+ });
60
+ // ── Sites ────────────────────────────────────────────────────────────────
61
+ server.tool("list_sites", "List all energy sites in your tenant", {}, async () => {
62
+ try {
63
+ return text(await apiGet("/api/sites"));
64
+ }
65
+ catch (e) {
66
+ return errorResult(e);
67
+ }
68
+ });
69
+ server.tool("get_site", "Get details of a specific site", {
70
+ site_id: z.number().describe("Site ID"),
71
+ }, async ({ site_id }) => {
72
+ try {
73
+ return text(await apiGet(`/api/sites/${site_id}`));
74
+ }
75
+ catch (e) {
76
+ return errorResult(e);
77
+ }
78
+ });
79
+ // ── Assets ───────────────────────────────────────────────────────────────
80
+ server.tool("list_assets", "List assets, optionally filtered by site", {
81
+ site_id: z.number().optional().describe("Optional site ID filter"),
82
+ }, async ({ site_id }) => {
83
+ try {
84
+ const qs = site_id ? `?siteId=${site_id}` : "";
85
+ return text(await apiGet(`/api/assets${qs}`));
86
+ }
87
+ catch (e) {
88
+ return errorResult(e);
89
+ }
90
+ });
91
+ // ── Grid Connections ─────────────────────────────────────────────────────
92
+ server.tool("list_grid_connections", "List grid connections for a site", {
93
+ site_id: z.number().describe("Site ID"),
94
+ }, async ({ site_id }) => {
95
+ try {
96
+ return text(await apiGet(`/api/grid-connections?siteId=${site_id}`));
97
+ }
98
+ catch (e) {
99
+ return errorResult(e);
100
+ }
101
+ });
102
+ // ── Meter Readings ───────────────────────────────────────────────────────
103
+ server.tool("get_meter_readings", "Get meter readings for a grid connection", {
104
+ grid_connection_id: z.number().describe("Grid connection ID"),
105
+ }, async ({ grid_connection_id }) => {
106
+ try {
107
+ return text(await apiGet(`/api/meter-readings?gridConnectionId=${grid_connection_id}`));
108
+ }
109
+ catch (e) {
110
+ return errorResult(e);
111
+ }
112
+ });
113
+ // ── Dispatch ─────────────────────────────────────────────────────────────
114
+ server.tool("dispatch_command", "Send a dispatch command to an asset (battery, HVAC, EV charger, etc.)", {
115
+ asset_id: z.number().describe("Asset ID"),
116
+ command: z.string().describe("Command: charge, discharge, reduce, curtail, shed_load, restore"),
117
+ target_kw: z.number().optional().describe("Target power in kW"),
118
+ duration_minutes: z.number().optional().describe("Duration in minutes"),
119
+ }, async ({ asset_id, command, target_kw, duration_minutes }) => {
120
+ try {
121
+ return text(await apiPost(`/api/v1/dispatch/${asset_id}/command`, {
122
+ command,
123
+ targetKw: target_kw,
124
+ durationMinutes: duration_minutes,
125
+ }));
126
+ }
127
+ catch (e) {
128
+ return errorResult(e);
129
+ }
130
+ });
131
+ server.tool("dispatch_history", "Get dispatch history for an asset", {
132
+ asset_id: z.number().describe("Asset ID"),
133
+ }, async ({ asset_id }) => {
134
+ try {
135
+ return text(await apiGet(`/api/v1/dispatch/${asset_id}/history`));
136
+ }
137
+ catch (e) {
138
+ return errorResult(e);
139
+ }
140
+ });
141
+ // ── Settlements ──────────────────────────────────────────────────────────
142
+ server.tool("list_settlements", "List settlements for a site", {
143
+ site_id: z.number().optional().describe("Optional site ID filter"),
144
+ }, async ({ site_id }) => {
145
+ try {
146
+ const qs = site_id ? `?siteId=${site_id}` : "";
147
+ return text(await apiGet(`/api/settlements${qs}`));
148
+ }
149
+ catch (e) {
150
+ return errorResult(e);
151
+ }
152
+ });
153
+ server.tool("generate_settlement", "Generate a hash-chained settlement for a site", {
154
+ site_id: z.number().describe("Site ID"),
155
+ period_start: z.string().describe("Period start (ISO date)"),
156
+ period_end: z.string().describe("Period end (ISO date)"),
157
+ }, async ({ site_id, period_start, period_end }) => {
158
+ try {
159
+ return text(await apiGet(`/api/v1/settlements/${site_id}/generate?periodStart=${period_start}&periodEnd=${period_end}`));
160
+ }
161
+ catch (e) {
162
+ return errorResult(e);
163
+ }
164
+ });
165
+ server.tool("verify_settlement", "Verify a settlement's hash chain integrity", {
166
+ settlement_id: z.number().describe("Settlement ID"),
167
+ }, async ({ settlement_id }) => {
168
+ try {
169
+ return text(await apiGet(`/api/v1/settlements/${settlement_id}/verify`));
170
+ }
171
+ catch (e) {
172
+ return errorResult(e);
173
+ }
174
+ });
175
+ // ── Carbon Attestation ───────────────────────────────────────────────────
176
+ server.tool("get_carbon_attestation", "Get carbon attestation for a site", {
177
+ site_id: z.number().describe("Site ID"),
178
+ }, async ({ site_id }) => {
179
+ try {
180
+ return text(await apiGet(`/api/v1/settlements/${site_id}/carbon-attestation`));
181
+ }
182
+ catch (e) {
183
+ return errorResult(e);
184
+ }
185
+ });
186
+ server.tool("create_carbon_record", "Create a carbon attestation record in the hash chain", {
187
+ meter_id: z.string().describe("Meter ID"),
188
+ facility_id: z.string().describe("Facility UUID"),
189
+ timestamp: z.string().describe("ISO timestamp"),
190
+ kwh: z.number().describe("Energy in kWh"),
191
+ grid_zone: z.string().optional().describe("Grid zone"),
192
+ }, async (params) => {
193
+ try {
194
+ return text(await apiPost("/api/v1/carbon/record", {
195
+ meterId: params.meter_id,
196
+ facilityId: params.facility_id,
197
+ timestamp: params.timestamp,
198
+ kwh: params.kwh,
199
+ gridZone: params.grid_zone,
200
+ }));
201
+ }
202
+ catch (e) {
203
+ return errorResult(e);
204
+ }
205
+ });
206
+ server.tool("verify_carbon_chain", "Verify the SHA-256 hash chain for a meter", {
207
+ meter_id: z.string().describe("Meter ID"),
208
+ }, async ({ meter_id }) => {
209
+ try {
210
+ return text(await apiGet(`/api/v1/carbon/verify/${meter_id}`));
211
+ }
212
+ catch (e) {
213
+ return errorResult(e);
214
+ }
215
+ });
216
+ server.tool("get_carbon_certificate", "Generate a carbon certificate for a facility", {
217
+ facility_id: z.string().describe("Facility UUID"),
218
+ start: z.string().describe("Period start (ISO date)"),
219
+ end: z.string().describe("Period end (ISO date)"),
220
+ }, async ({ facility_id, start, end }) => {
221
+ try {
222
+ return text(await apiGet(`/api/v1/carbon/certificate/${facility_id}?start=${start}&end=${end}`));
223
+ }
224
+ catch (e) {
225
+ return errorResult(e);
226
+ }
227
+ });
228
+ // ── Demand Response ──────────────────────────────────────────────────────
229
+ server.tool("create_dr_event", "Create a demand response event", {
230
+ signal_type: z.string().describe("Signal type: shed, shift, shimmy"),
231
+ facility_id: z.string().describe("Facility UUID"),
232
+ scheduled_start: z.string().describe("Start time (ISO)"),
233
+ target_reduction_kw: z.number().optional().describe("Target reduction in kW"),
234
+ duration_minutes: z.number().optional().describe("Duration in minutes"),
235
+ }, async (params) => {
236
+ try {
237
+ return text(await apiPost("/api/v1/dr/events", {
238
+ signalType: params.signal_type,
239
+ facilityId: params.facility_id,
240
+ scheduledStart: params.scheduled_start,
241
+ targetReductionKw: params.target_reduction_kw,
242
+ durationMinutes: params.duration_minutes,
243
+ }));
244
+ }
245
+ catch (e) {
246
+ return errorResult(e);
247
+ }
248
+ });
249
+ server.tool("list_dr_events", "List demand response events", {
250
+ status: z.string().optional().describe("Filter by status"),
251
+ facility_id: z.string().optional().describe("Filter by facility"),
252
+ }, async ({ status, facility_id }) => {
253
+ try {
254
+ const qs = new URLSearchParams();
255
+ if (status)
256
+ qs.set("status", status);
257
+ if (facility_id)
258
+ qs.set("facility_id", facility_id);
259
+ const q = qs.toString() ? `?${qs.toString()}` : "";
260
+ return text(await apiGet(`/api/v1/dr/events${q}`));
261
+ }
262
+ catch (e) {
263
+ return errorResult(e);
264
+ }
265
+ });
266
+ server.tool("get_dr_event", "Get details of a DR event", {
267
+ event_id: z.string().describe("Event UUID"),
268
+ }, async ({ event_id }) => {
269
+ try {
270
+ return text(await apiGet(`/api/v1/dr/events/${event_id}`));
271
+ }
272
+ catch (e) {
273
+ return errorResult(e);
274
+ }
275
+ });
276
+ server.tool("dispatch_dr_event", "Execute dispatch for a DR event", {
277
+ event_id: z.string().describe("Event UUID"),
278
+ }, async ({ event_id }) => {
279
+ try {
280
+ return text(await apiPost(`/api/v1/dr/events/${event_id}/dispatch`));
281
+ }
282
+ catch (e) {
283
+ return errorResult(e);
284
+ }
285
+ });
286
+ server.tool("settle_dr_event", "Settle a DR event with carbon attestation", {
287
+ event_id: z.string().describe("Event UUID"),
288
+ }, async ({ event_id }) => {
289
+ try {
290
+ return text(await apiPost(`/api/v1/dr/events/${event_id}/settle`));
291
+ }
292
+ catch (e) {
293
+ return errorResult(e);
294
+ }
295
+ });
296
+ // ── Compliance ───────────────────────────────────────────────────────────
297
+ server.tool("generate_compliance_package", "Generate a compliance package for a site", {
298
+ site_id: z.number().describe("Site ID"),
299
+ standard: z.string().optional().describe("Standard: IEC61850, ISO50001, GHG_Scope2"),
300
+ }, async ({ site_id, standard }) => {
301
+ try {
302
+ return text(await apiPost(`/api/v1/comply/${site_id}/generate`, { standard }));
303
+ }
304
+ catch (e) {
305
+ return errorResult(e);
306
+ }
307
+ });
308
+ server.tool("list_compliance_packages", "List compliance packages for a site", {
309
+ site_id: z.number().describe("Site ID"),
310
+ }, async ({ site_id }) => {
311
+ try {
312
+ return text(await apiGet(`/api/v1/comply/${site_id}/packages`));
313
+ }
314
+ catch (e) {
315
+ return errorResult(e);
316
+ }
317
+ });
318
+ server.tool("generate_scope2_report", "Generate GHG Scope 2 compliance report", {
319
+ facility_id: z.string().describe("Facility UUID"),
320
+ period_start: z.string().describe("Period start"),
321
+ period_end: z.string().describe("Period end"),
322
+ methodology: z.enum(["location-based", "market-based", "dual"]).optional(),
323
+ }, async (params) => {
324
+ try {
325
+ return text(await apiPost("/api/v1/comply/report", {
326
+ facility_id: params.facility_id,
327
+ period_start: params.period_start,
328
+ period_end: params.period_end,
329
+ methodology: params.methodology,
330
+ }));
331
+ }
332
+ catch (e) {
333
+ return errorResult(e);
334
+ }
335
+ });
336
+ // ── Intel ────────────────────────────────────────────────────────────────
337
+ server.tool("get_asset_reliability", "Get reliability score for an asset", {
338
+ asset_id: z.number().describe("Asset ID"),
339
+ }, async ({ asset_id }) => {
340
+ try {
341
+ return text(await apiGet(`/api/v1/intel/assets/${asset_id}/score`));
342
+ }
343
+ catch (e) {
344
+ return errorResult(e);
345
+ }
346
+ });
347
+ server.tool("get_site_reliability", "Get reliability score for a site", {
348
+ site_id: z.number().describe("Site ID"),
349
+ }, async ({ site_id }) => {
350
+ try {
351
+ return text(await apiGet(`/api/v1/intel/sites/${site_id}/score`));
352
+ }
353
+ catch (e) {
354
+ return errorResult(e);
355
+ }
356
+ });
357
+ server.tool("get_grid_capacity", "Get grid capacity for a region", {
358
+ region: z.string().describe("Region code (e.g. AE-DXB, PH-LUZ)"),
359
+ }, async ({ region }) => {
360
+ try {
361
+ return text(await apiGet(`/api/v1/intel/grid/${region}/capacity`));
362
+ }
363
+ catch (e) {
364
+ return errorResult(e);
365
+ }
366
+ });
367
+ server.tool("get_grid_trends", "Get grid capacity trends for a region", {
368
+ region: z.string().describe("Region code"),
369
+ }, async ({ region }) => {
370
+ try {
371
+ return text(await apiGet(`/api/v1/intel/grid/${region}/trends`));
372
+ }
373
+ catch (e) {
374
+ return errorResult(e);
375
+ }
376
+ });
377
+ // ── Procurement ──────────────────────────────────────────────────────────
378
+ server.tool("create_procurement", "Create an energy procurement request", {
379
+ type: z.string().describe("Type: ppa, rec, carbon_offset"),
380
+ volume_kwh: z.number().describe("Volume in kWh"),
381
+ region: z.string().optional().describe("Region"),
382
+ }, async (params) => {
383
+ try {
384
+ return text(await apiPost("/api/v1/procurement", params));
385
+ }
386
+ catch (e) {
387
+ return errorResult(e);
388
+ }
389
+ });
390
+ server.tool("analyze_procurement", "Run analysis on a procurement request", {
391
+ id: z.number().describe("Procurement request ID"),
392
+ }, async ({ id }) => {
393
+ try {
394
+ return text(await apiPost(`/api/v1/procurement/${id}/analyze`));
395
+ }
396
+ catch (e) {
397
+ return errorResult(e);
398
+ }
399
+ });
400
+ server.tool("get_procurement_options", "Get procurement options", {
401
+ id: z.number().describe("Procurement request ID"),
402
+ }, async ({ id }) => {
403
+ try {
404
+ return text(await apiGet(`/api/v1/procurement/${id}/options`));
405
+ }
406
+ catch (e) {
407
+ return errorResult(e);
408
+ }
409
+ });
410
+ // ── Integration Status ───────────────────────────────────────────────────
411
+ server.tool("get_integration_status", "Get status of all integrations (Modbus, OpenADR, BESS, grid prices)", {}, async () => {
412
+ try {
413
+ return text(await apiGet("/api/v1/integrations/status"));
414
+ }
415
+ catch (e) {
416
+ return errorResult(e);
417
+ }
418
+ });
419
+ server.tool("get_grid_prices", "Get current grid electricity prices", {
420
+ region: z.string().optional().describe("Region code"),
421
+ }, async ({ region }) => {
422
+ try {
423
+ const path = region ? `/api/v1/integrations/grid-prices/${region}` : "/api/v1/integrations/grid-prices";
424
+ return text(await apiGet(path));
425
+ }
426
+ catch (e) {
427
+ return errorResult(e);
428
+ }
429
+ });
430
+ // ── Sandbox ──────────────────────────────────────────────────────────────
431
+ server.tool("provision_sandbox", "Provision a developer sandbox environment with simulated data", {}, async () => {
432
+ try {
433
+ return text(await apiPost("/api/v1/sandbox/provision"));
434
+ }
435
+ catch (e) {
436
+ return errorResult(e);
437
+ }
438
+ });
439
+ server.tool("sandbox_status", "Check sandbox environment status and usage", {}, async () => {
440
+ try {
441
+ return text(await apiGet("/api/v1/sandbox/status"));
442
+ }
443
+ catch (e) {
444
+ return errorResult(e);
445
+ }
446
+ });
447
+ // ── Health ───────────────────────────────────────────────────────────────
448
+ server.tool("health_check", "Check platform health and connectivity", {}, async () => {
449
+ try {
450
+ return text(await apiGet("/api/health"));
451
+ }
452
+ catch (e) {
453
+ return errorResult(e);
454
+ }
455
+ });
456
+ // ─── Resources ─────────────────────────────────────────────────────────────
457
+ server.resource("platform-overview", "energyatit://overview", async () => ({
458
+ contents: [{
459
+ uri: "energyatit://overview",
460
+ mimeType: "text/plain",
461
+ text: [
462
+ "EnergyAtIt — Energy Infrastructure Platform",
463
+ "",
464
+ "Capabilities:",
465
+ " - Sites & Assets: Manage energy sites, assets (BESS, HVAC, Solar, EV chargers)",
466
+ " - Dispatch: Send commands to batteries, HVAC, EV chargers",
467
+ " - Carbon Attestation: SHA-256 hash-chained carbon records with certificates",
468
+ " - Demand Response: Create, dispatch, measure, and settle DR events",
469
+ " - Settlements: Generate and verify hash-chained energy settlements",
470
+ " - Compliance: Generate IEC 61850, ISO 50001, GHG Scope 2 packages",
471
+ " - Intel: Reliability scores, grid capacity, load forecasting",
472
+ " - Procurement: PPA, REC, and carbon offset procurement",
473
+ " - Integrations: Modbus, OpenADR 2.0b, OCPP 2.0, IEC 61850",
474
+ "",
475
+ `Connected to: ${BASE_URL}`,
476
+ `Auth: ${TOKEN ? "JWT token" : API_KEY ? "API key" : "none (set ENERGYATIT_API_KEY)"}`,
477
+ ].join("\n"),
478
+ }],
479
+ }));
480
+ // ─── Start ─────────────────────────────────────────────────────────────────
481
+ async function main() {
482
+ if (!API_KEY && !TOKEN) {
483
+ console.error("Warning: No ENERGYATIT_API_KEY or ENERGYATIT_TOKEN set. Requests will be unauthenticated.");
484
+ console.error("Set ENERGYATIT_API_KEY=eat_live_xxx or ENERGYATIT_TOKEN=jwt_xxx in your environment.");
485
+ }
486
+ console.error(`EnergyAtIt MCP server v0.1.0 — connecting to ${BASE_URL}`);
487
+ const transport = new StdioServerTransport();
488
+ await server.connect(transport);
489
+ console.error("EnergyAtIt MCP server running on stdio");
490
+ }
491
+ main().catch((err) => {
492
+ console.error("Failed to start MCP server:", err);
493
+ process.exit(1);
494
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@energyatit/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for EnergyAtIt — connect Claude, GPT, or any MCP client to energy grid data",
5
+ "type": "module",
6
+ "bin": {
7
+ "energyatit-mcp": "./dist/index.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "files": ["dist", "README.md"],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "mcp", "energy", "grid", "ai", "agent", "claude", "openai",
24
+ "carbon", "dispatch", "settlement", "compliance", "demand-response",
25
+ "openadr", "modbus", "iec61850"
26
+ ],
27
+ "author": "EnergyAtIt <dsk@energyatit.com>",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.12.1",
31
+ "zod": "^3.24.2"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.7.0"
35
+ }
36
+ }