@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 +106 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +494 -0
- package/package.json +36 -0
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
|
package/dist/index.d.ts
ADDED
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
|
+
}
|