@access-mcp/system-status 0.4.1 → 0.5.1
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 +44 -172
- package/dist/__tests__/server.integration.test.js +71 -50
- package/dist/__tests__/server.test.js +136 -111
- package/dist/index.js +3 -11
- package/dist/server.d.ts +11 -95
- package/dist/server.js +129 -144
- package/dist/web-server.js +1 -1
- package/package.json +2 -2
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
|
|
2
2
|
import { SystemStatusServer } from "../server.js";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
const { version } = require("../../package.json");
|
|
3
6
|
// Mock axios
|
|
4
7
|
vi.mock("axios");
|
|
5
8
|
describe("SystemStatusServer", () => {
|
|
@@ -10,51 +13,39 @@ describe("SystemStatusServer", () => {
|
|
|
10
13
|
id: "1",
|
|
11
14
|
Subject: "Emergency maintenance on Anvil",
|
|
12
15
|
Content: "Critical issue requiring immediate attention",
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
AffectedResources: [
|
|
16
|
-
{ ResourceName: "Anvil", ResourceID: "anvil-1" }
|
|
17
|
-
]
|
|
16
|
+
OutageStart: "2024-08-27T10:00:00Z",
|
|
17
|
+
OutageEnd: "2024-08-27T11:00:00Z",
|
|
18
|
+
AffectedResources: [{ ResourceName: "Anvil", ResourceID: "anvil-1" }],
|
|
18
19
|
},
|
|
19
20
|
{
|
|
20
21
|
id: "2",
|
|
21
22
|
Subject: "Scheduled maintenance on Bridges-2",
|
|
22
23
|
Content: "Regular maintenance window",
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
AffectedResources: [
|
|
26
|
-
|
|
27
|
-
]
|
|
28
|
-
}
|
|
24
|
+
OutageStart: "2024-08-27T08:00:00Z",
|
|
25
|
+
OutageEnd: "2024-08-27T08:30:00Z",
|
|
26
|
+
AffectedResources: [{ ResourceName: "Bridges-2", ResourceID: "bridges2-1" }],
|
|
27
|
+
},
|
|
29
28
|
];
|
|
30
29
|
const mockFutureOutagesData = [
|
|
31
30
|
{
|
|
32
31
|
id: "3",
|
|
33
32
|
Subject: "Scheduled Jetstream maintenance",
|
|
34
33
|
Content: "Planned maintenance",
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
AffectedResources: [
|
|
40
|
-
{ ResourceName: "Jetstream", ResourceID: "jetstream-1" }
|
|
41
|
-
]
|
|
42
|
-
}
|
|
34
|
+
OutageStart: "2024-08-30T10:00:00Z",
|
|
35
|
+
OutageEnd: "2024-08-30T14:00:00Z",
|
|
36
|
+
AffectedResources: [{ ResourceName: "Jetstream", ResourceID: "jetstream-1" }],
|
|
37
|
+
},
|
|
43
38
|
];
|
|
44
39
|
const mockPastOutagesData = [
|
|
45
40
|
{
|
|
46
41
|
id: "4",
|
|
47
42
|
Subject: "Past maintenance on Stampede3",
|
|
48
43
|
Content: "Completed maintenance",
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
OutageStartDateTime: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(), // 3 days ago
|
|
52
|
-
OutageEndDateTime: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000 + 6 * 60 * 60 * 1000).toISOString(), // 3 days ago + 6 hours
|
|
44
|
+
OutageStart: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(), // 3 days ago
|
|
45
|
+
OutageEnd: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000 + 6 * 60 * 60 * 1000).toISOString(), // 3 days ago + 6 hours
|
|
53
46
|
OutageType: "Full",
|
|
54
|
-
AffectedResources: [
|
|
55
|
-
|
|
56
|
-
]
|
|
57
|
-
}
|
|
47
|
+
AffectedResources: [{ ResourceName: "Stampede3", ResourceID: "stampede3-1" }],
|
|
48
|
+
},
|
|
58
49
|
];
|
|
59
50
|
beforeEach(() => {
|
|
60
51
|
server = new SystemStatusServer();
|
|
@@ -75,28 +66,22 @@ describe("SystemStatusServer", () => {
|
|
|
75
66
|
it("should initialize with correct server info", () => {
|
|
76
67
|
expect(server).toBeDefined();
|
|
77
68
|
expect(server["serverName"]).toBe("access-mcp-system-status");
|
|
78
|
-
expect(server["version"]).toBe(
|
|
69
|
+
expect(server["version"]).toBe(version);
|
|
79
70
|
expect(server["baseURL"]).toBe("https://operations-api.access-ci.org");
|
|
80
71
|
});
|
|
81
72
|
it("should provide correct tools", () => {
|
|
82
73
|
const tools = server["getTools"]();
|
|
83
|
-
expect(tools).toHaveLength(
|
|
84
|
-
expect(tools.
|
|
85
|
-
"get_current_outages",
|
|
86
|
-
"get_scheduled_maintenance",
|
|
87
|
-
"get_past_outages",
|
|
88
|
-
"get_system_announcements",
|
|
89
|
-
"check_resource_status"
|
|
90
|
-
]);
|
|
74
|
+
expect(tools).toHaveLength(1);
|
|
75
|
+
expect(tools[0].name).toBe("get_infrastructure_news");
|
|
91
76
|
});
|
|
92
77
|
it("should provide correct resources", () => {
|
|
93
78
|
const resources = server["getResources"]();
|
|
94
79
|
expect(resources).toHaveLength(4);
|
|
95
|
-
expect(resources.map(r => r.uri)).toEqual([
|
|
80
|
+
expect(resources.map((r) => r.uri)).toEqual([
|
|
96
81
|
"accessci://system-status",
|
|
97
82
|
"accessci://outages/current",
|
|
98
83
|
"accessci://outages/scheduled",
|
|
99
|
-
"accessci://outages/past"
|
|
84
|
+
"accessci://outages/past",
|
|
100
85
|
]);
|
|
101
86
|
});
|
|
102
87
|
});
|
|
@@ -104,44 +89,49 @@ describe("SystemStatusServer", () => {
|
|
|
104
89
|
it("should fetch and enhance current outages", async () => {
|
|
105
90
|
mockHttpClient.get.mockResolvedValue({
|
|
106
91
|
status: 200,
|
|
107
|
-
data: { results: mockCurrentOutagesData }
|
|
92
|
+
data: { results: mockCurrentOutagesData },
|
|
108
93
|
});
|
|
109
94
|
const result = await server["handleToolCall"]({
|
|
110
|
-
|
|
95
|
+
method: "tools/call",
|
|
96
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } },
|
|
111
97
|
});
|
|
112
98
|
expect(mockHttpClient.get).toHaveBeenCalledWith("/wh2/news/v1/affiliation/access-ci.org/current_outages/");
|
|
113
|
-
const
|
|
99
|
+
const content = result.content[0];
|
|
100
|
+
const response = JSON.parse(content.text);
|
|
114
101
|
expect(response.total_outages).toBe(2);
|
|
115
102
|
expect(response.affected_resources).toEqual(["Anvil", "Bridges-2"]);
|
|
116
103
|
expect(response.severity_counts).toHaveProperty("high", 1); // Emergency
|
|
117
104
|
expect(response.severity_counts).toHaveProperty("low", 1); // Scheduled maintenance
|
|
118
105
|
expect(response.outages[0]).toHaveProperty("severity");
|
|
119
|
-
expect(response.outages[0]).toHaveProperty("posted_time");
|
|
120
106
|
});
|
|
121
107
|
it("should filter outages by resource", async () => {
|
|
122
108
|
mockHttpClient.get.mockResolvedValue({
|
|
123
109
|
status: 200,
|
|
124
|
-
data: { results: mockCurrentOutagesData }
|
|
110
|
+
data: { results: mockCurrentOutagesData },
|
|
125
111
|
});
|
|
126
112
|
const result = await server["handleToolCall"]({
|
|
113
|
+
method: "tools/call",
|
|
127
114
|
params: {
|
|
128
|
-
name: "
|
|
129
|
-
arguments: {
|
|
130
|
-
}
|
|
115
|
+
name: "get_infrastructure_news",
|
|
116
|
+
arguments: { query: "Anvil", time: "current" },
|
|
117
|
+
},
|
|
131
118
|
});
|
|
132
|
-
const
|
|
119
|
+
const content = result.content[0];
|
|
120
|
+
const response = JSON.parse(content.text);
|
|
133
121
|
expect(response.total_outages).toBe(1);
|
|
134
122
|
expect(response.outages[0].Subject).toContain("Anvil");
|
|
135
123
|
});
|
|
136
124
|
it("should categorize severity correctly", async () => {
|
|
137
125
|
mockHttpClient.get.mockResolvedValue({
|
|
138
126
|
status: 200,
|
|
139
|
-
data: { results: mockCurrentOutagesData }
|
|
127
|
+
data: { results: mockCurrentOutagesData },
|
|
140
128
|
});
|
|
141
129
|
const result = await server["handleToolCall"]({
|
|
142
|
-
|
|
130
|
+
method: "tools/call",
|
|
131
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } },
|
|
143
132
|
});
|
|
144
|
-
const
|
|
133
|
+
const content = result.content[0];
|
|
134
|
+
const response = JSON.parse(content.text);
|
|
145
135
|
const emergencyOutage = response.outages.find((o) => o.Subject.includes("Emergency"));
|
|
146
136
|
const maintenanceOutage = response.outages.find((o) => o.Subject.includes("Scheduled"));
|
|
147
137
|
expect(emergencyOutage.severity).toBe("high");
|
|
@@ -152,12 +142,14 @@ describe("SystemStatusServer", () => {
|
|
|
152
142
|
it("should fetch and enhance scheduled maintenance", async () => {
|
|
153
143
|
mockHttpClient.get.mockResolvedValue({
|
|
154
144
|
status: 200,
|
|
155
|
-
data: { results: mockFutureOutagesData }
|
|
145
|
+
data: { results: mockFutureOutagesData },
|
|
156
146
|
});
|
|
157
147
|
const result = await server["handleToolCall"]({
|
|
158
|
-
|
|
148
|
+
method: "tools/call",
|
|
149
|
+
params: { name: "get_infrastructure_news", arguments: { time: "scheduled" } },
|
|
159
150
|
});
|
|
160
|
-
const
|
|
151
|
+
const content = result.content[0];
|
|
152
|
+
const response = JSON.parse(content.text);
|
|
161
153
|
expect(response.total_scheduled).toBe(1);
|
|
162
154
|
expect(response.affected_resources).toEqual(["Jetstream"]);
|
|
163
155
|
expect(response.maintenance[0]).toHaveProperty("hours_until_start");
|
|
@@ -165,19 +157,23 @@ describe("SystemStatusServer", () => {
|
|
|
165
157
|
expect(response.maintenance[0]).toHaveProperty("has_scheduled_time", true);
|
|
166
158
|
});
|
|
167
159
|
it("should handle missing scheduled times", async () => {
|
|
168
|
-
const dataWithoutSchedule = [
|
|
160
|
+
const dataWithoutSchedule = [
|
|
161
|
+
{
|
|
169
162
|
...mockFutureOutagesData[0],
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
163
|
+
OutageStart: null,
|
|
164
|
+
OutageEnd: null,
|
|
165
|
+
},
|
|
166
|
+
];
|
|
173
167
|
mockHttpClient.get.mockResolvedValue({
|
|
174
168
|
status: 200,
|
|
175
|
-
data: { results: dataWithoutSchedule }
|
|
169
|
+
data: { results: dataWithoutSchedule },
|
|
176
170
|
});
|
|
177
171
|
const result = await server["handleToolCall"]({
|
|
178
|
-
|
|
172
|
+
method: "tools/call",
|
|
173
|
+
params: { name: "get_infrastructure_news", arguments: { time: "scheduled" } },
|
|
179
174
|
});
|
|
180
|
-
const
|
|
175
|
+
const content = result.content[0];
|
|
176
|
+
const response = JSON.parse(content.text);
|
|
181
177
|
expect(response.maintenance[0].has_scheduled_time).toBe(false);
|
|
182
178
|
expect(response.maintenance[0].duration_hours).toBe(null);
|
|
183
179
|
});
|
|
@@ -185,23 +181,25 @@ describe("SystemStatusServer", () => {
|
|
|
185
181
|
const multipleMaintenanceData = [
|
|
186
182
|
{
|
|
187
183
|
...mockFutureOutagesData[0],
|
|
188
|
-
|
|
189
|
-
Subject: "Later maintenance"
|
|
184
|
+
OutageStart: "2024-08-31T10:00:00Z", // Later
|
|
185
|
+
Subject: "Later maintenance",
|
|
190
186
|
},
|
|
191
187
|
{
|
|
192
188
|
...mockFutureOutagesData[0],
|
|
193
|
-
|
|
194
|
-
Subject: "Earlier maintenance"
|
|
195
|
-
}
|
|
189
|
+
OutageStart: "2024-08-30T10:00:00Z", // Earlier
|
|
190
|
+
Subject: "Earlier maintenance",
|
|
191
|
+
},
|
|
196
192
|
];
|
|
197
193
|
mockHttpClient.get.mockResolvedValue({
|
|
198
194
|
status: 200,
|
|
199
|
-
data: { results: multipleMaintenanceData }
|
|
195
|
+
data: { results: multipleMaintenanceData },
|
|
200
196
|
});
|
|
201
197
|
const result = await server["handleToolCall"]({
|
|
202
|
-
|
|
198
|
+
method: "tools/call",
|
|
199
|
+
params: { name: "get_infrastructure_news", arguments: { time: "scheduled" } },
|
|
203
200
|
});
|
|
204
|
-
const
|
|
201
|
+
const content = result.content[0];
|
|
202
|
+
const response = JSON.parse(content.text);
|
|
205
203
|
expect(response.maintenance[0].Subject).toBe("Earlier maintenance");
|
|
206
204
|
expect(response.maintenance[1].Subject).toBe("Later maintenance");
|
|
207
205
|
});
|
|
@@ -210,12 +208,14 @@ describe("SystemStatusServer", () => {
|
|
|
210
208
|
it("should fetch and enhance past outages", async () => {
|
|
211
209
|
mockHttpClient.get.mockResolvedValue({
|
|
212
210
|
status: 200,
|
|
213
|
-
data: { results: mockPastOutagesData }
|
|
211
|
+
data: { results: mockPastOutagesData },
|
|
214
212
|
});
|
|
215
213
|
const result = await server["handleToolCall"]({
|
|
216
|
-
|
|
214
|
+
method: "tools/call",
|
|
215
|
+
params: { name: "get_infrastructure_news", arguments: { time: "past" } },
|
|
217
216
|
});
|
|
218
|
-
const
|
|
217
|
+
const content = result.content[0];
|
|
218
|
+
const response = JSON.parse(content.text);
|
|
219
219
|
expect(response.total_past_outages).toBe(1);
|
|
220
220
|
expect(response.outage_types).toEqual(["Full"]);
|
|
221
221
|
expect(response.average_duration_hours).toBe(6); // 6 hour duration
|
|
@@ -223,22 +223,26 @@ describe("SystemStatusServer", () => {
|
|
|
223
223
|
expect(response.outages[0]).toHaveProperty("days_ago");
|
|
224
224
|
});
|
|
225
225
|
it("should apply limit correctly", async () => {
|
|
226
|
-
const manyOutages = Array(50)
|
|
226
|
+
const manyOutages = Array(50)
|
|
227
|
+
.fill(0)
|
|
228
|
+
.map((_, i) => ({
|
|
227
229
|
...mockPastOutagesData[0],
|
|
228
230
|
id: `past-${i}`,
|
|
229
|
-
Subject: `Past outage ${i}
|
|
231
|
+
Subject: `Past outage ${i}`,
|
|
230
232
|
}));
|
|
231
233
|
mockHttpClient.get.mockResolvedValue({
|
|
232
234
|
status: 200,
|
|
233
|
-
data: { results: manyOutages }
|
|
235
|
+
data: { results: manyOutages },
|
|
234
236
|
});
|
|
235
237
|
const result = await server["handleToolCall"]({
|
|
238
|
+
method: "tools/call",
|
|
236
239
|
params: {
|
|
237
|
-
name: "
|
|
238
|
-
arguments: { limit: 10 }
|
|
239
|
-
}
|
|
240
|
+
name: "get_infrastructure_news",
|
|
241
|
+
arguments: { time: "past", limit: 10 },
|
|
242
|
+
},
|
|
240
243
|
});
|
|
241
|
-
const
|
|
244
|
+
const content = result.content[0];
|
|
245
|
+
const response = JSON.parse(content.text);
|
|
242
246
|
expect(response.outages).toHaveLength(10);
|
|
243
247
|
});
|
|
244
248
|
});
|
|
@@ -249,10 +253,12 @@ describe("SystemStatusServer", () => {
|
|
|
249
253
|
.mockResolvedValueOnce({ status: 200, data: { results: mockFutureOutagesData } })
|
|
250
254
|
.mockResolvedValueOnce({ status: 200, data: { results: mockPastOutagesData } });
|
|
251
255
|
const result = await server["handleToolCall"]({
|
|
252
|
-
|
|
256
|
+
method: "tools/call",
|
|
257
|
+
params: { name: "get_infrastructure_news", arguments: { time: "all" } },
|
|
253
258
|
});
|
|
254
259
|
expect(mockHttpClient.get).toHaveBeenCalledTimes(3);
|
|
255
|
-
const
|
|
260
|
+
const content = result.content[0];
|
|
261
|
+
const response = JSON.parse(content.text);
|
|
256
262
|
expect(response.current_outages).toBe(2);
|
|
257
263
|
expect(response.scheduled_maintenance).toBe(1);
|
|
258
264
|
expect(response.recent_past_outages).toBe(1); // Within 30 days
|
|
@@ -266,9 +272,11 @@ describe("SystemStatusServer", () => {
|
|
|
266
272
|
.mockResolvedValueOnce({ status: 200, data: { results: mockFutureOutagesData } })
|
|
267
273
|
.mockResolvedValueOnce({ status: 200, data: { results: mockPastOutagesData } });
|
|
268
274
|
const result = await server["handleToolCall"]({
|
|
269
|
-
|
|
275
|
+
method: "tools/call",
|
|
276
|
+
params: { name: "get_infrastructure_news", arguments: { time: "all" } },
|
|
270
277
|
});
|
|
271
|
-
const
|
|
278
|
+
const content = result.content[0];
|
|
279
|
+
const response = JSON.parse(content.text);
|
|
272
280
|
const firstAnnouncement = response.announcements[0];
|
|
273
281
|
expect(firstAnnouncement.category).toBe("current");
|
|
274
282
|
});
|
|
@@ -277,15 +285,17 @@ describe("SystemStatusServer", () => {
|
|
|
277
285
|
it("should check resource status efficiently (direct method)", async () => {
|
|
278
286
|
mockHttpClient.get.mockResolvedValue({
|
|
279
287
|
status: 200,
|
|
280
|
-
data: { results: mockCurrentOutagesData }
|
|
288
|
+
data: { results: mockCurrentOutagesData },
|
|
281
289
|
});
|
|
282
290
|
const result = await server["handleToolCall"]({
|
|
291
|
+
method: "tools/call",
|
|
283
292
|
params: {
|
|
284
|
-
name: "
|
|
285
|
-
arguments: {
|
|
286
|
-
}
|
|
293
|
+
name: "get_infrastructure_news",
|
|
294
|
+
arguments: { ids: ["anvil-1", "unknown-resource"] },
|
|
295
|
+
},
|
|
287
296
|
});
|
|
288
|
-
const
|
|
297
|
+
const content = result.content[0];
|
|
298
|
+
const response = JSON.parse(content.text);
|
|
289
299
|
expect(response.api_method).toBe("direct_outages_check");
|
|
290
300
|
expect(response.resources_checked).toBe(2);
|
|
291
301
|
expect(response.operational).toBe(1); // unknown-resource
|
|
@@ -297,19 +307,21 @@ describe("SystemStatusServer", () => {
|
|
|
297
307
|
it("should use group API when requested", async () => {
|
|
298
308
|
mockHttpClient.get.mockResolvedValue({
|
|
299
309
|
status: 200,
|
|
300
|
-
data: { results: [] } // No outages for this group
|
|
310
|
+
data: { results: [] }, // No outages for this group
|
|
301
311
|
});
|
|
302
312
|
const result = await server["handleToolCall"]({
|
|
313
|
+
method: "tools/call",
|
|
303
314
|
params: {
|
|
304
|
-
name: "
|
|
315
|
+
name: "get_infrastructure_news",
|
|
305
316
|
arguments: {
|
|
306
|
-
|
|
307
|
-
use_group_api: true
|
|
308
|
-
}
|
|
309
|
-
}
|
|
317
|
+
ids: ["anvil"],
|
|
318
|
+
use_group_api: true,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
310
321
|
});
|
|
311
322
|
expect(mockHttpClient.get).toHaveBeenCalledWith("/wh2/news/v1/info_groupid/anvil/");
|
|
312
|
-
const
|
|
323
|
+
const content = result.content[0];
|
|
324
|
+
const response = JSON.parse(content.text);
|
|
313
325
|
expect(response.api_method).toBe("resource_group_api");
|
|
314
326
|
expect(response.resource_status[0].status).toBe("operational");
|
|
315
327
|
expect(response.resource_status[0].api_method).toBe("group_specific");
|
|
@@ -317,16 +329,18 @@ describe("SystemStatusServer", () => {
|
|
|
317
329
|
it("should handle group API failures gracefully", async () => {
|
|
318
330
|
mockHttpClient.get.mockRejectedValue(new Error("API Error"));
|
|
319
331
|
const result = await server["handleToolCall"]({
|
|
332
|
+
method: "tools/call",
|
|
320
333
|
params: {
|
|
321
|
-
name: "
|
|
334
|
+
name: "get_infrastructure_news",
|
|
322
335
|
arguments: {
|
|
323
|
-
|
|
324
|
-
use_group_api: true
|
|
325
|
-
}
|
|
326
|
-
}
|
|
336
|
+
ids: ["invalid-resource"],
|
|
337
|
+
use_group_api: true,
|
|
338
|
+
},
|
|
339
|
+
},
|
|
327
340
|
});
|
|
328
|
-
const
|
|
329
|
-
|
|
341
|
+
const content = result.content[0];
|
|
342
|
+
const response = JSON.parse(content.text);
|
|
343
|
+
expect(response).toHaveProperty("unknown", 1);
|
|
330
344
|
expect(response.resource_status[0].status).toBe("unknown");
|
|
331
345
|
expect(response.resource_status[0].api_method).toBe("group_specific_failed");
|
|
332
346
|
expect(response.resource_status[0]).toHaveProperty("error");
|
|
@@ -336,35 +350,45 @@ describe("SystemStatusServer", () => {
|
|
|
336
350
|
it("should handle API errors gracefully", async () => {
|
|
337
351
|
mockHttpClient.get.mockResolvedValue({
|
|
338
352
|
status: 500,
|
|
339
|
-
statusText: "Internal Server Error"
|
|
353
|
+
statusText: "Internal Server Error",
|
|
354
|
+
data: null,
|
|
340
355
|
});
|
|
341
356
|
const result = await server["handleToolCall"]({
|
|
342
|
-
|
|
357
|
+
method: "tools/call",
|
|
358
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } },
|
|
343
359
|
});
|
|
344
|
-
|
|
360
|
+
const content = result.content[0];
|
|
361
|
+
const response = JSON.parse(content.text);
|
|
362
|
+
expect(response.error).toBeDefined();
|
|
345
363
|
});
|
|
346
364
|
it("should handle network errors", async () => {
|
|
347
365
|
mockHttpClient.get.mockRejectedValue(new Error("Network error"));
|
|
348
366
|
const result = await server["handleToolCall"]({
|
|
349
|
-
|
|
367
|
+
method: "tools/call",
|
|
368
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } },
|
|
350
369
|
});
|
|
351
|
-
|
|
370
|
+
const content = result.content[0];
|
|
371
|
+
const response = JSON.parse(content.text);
|
|
372
|
+
expect(response.error).toBe("Network error");
|
|
352
373
|
});
|
|
353
374
|
it("should handle unknown tools", async () => {
|
|
354
375
|
const result = await server["handleToolCall"]({
|
|
355
|
-
|
|
376
|
+
method: "tools/call",
|
|
377
|
+
params: { name: "unknown_tool", arguments: {} },
|
|
356
378
|
});
|
|
357
|
-
|
|
379
|
+
const content = result.content[0];
|
|
380
|
+
expect(content.text).toContain("Unknown tool");
|
|
358
381
|
});
|
|
359
382
|
});
|
|
360
383
|
describe("Resource Handling", () => {
|
|
361
384
|
it("should handle resource reads correctly", async () => {
|
|
362
385
|
mockHttpClient.get.mockResolvedValue({
|
|
363
386
|
status: 200,
|
|
364
|
-
data: { results: mockCurrentOutagesData }
|
|
387
|
+
data: { results: mockCurrentOutagesData },
|
|
365
388
|
});
|
|
366
389
|
const result = await server["handleResourceRead"]({
|
|
367
|
-
|
|
390
|
+
method: "resources/read",
|
|
391
|
+
params: { uri: "accessci://outages/current" },
|
|
368
392
|
});
|
|
369
393
|
expect(result.contents[0].mimeType).toBe("application/json");
|
|
370
394
|
expect(result.contents[0].text).toBeDefined();
|
|
@@ -372,7 +396,8 @@ describe("SystemStatusServer", () => {
|
|
|
372
396
|
it("should handle unknown resources", async () => {
|
|
373
397
|
await expect(async () => {
|
|
374
398
|
await server["handleResourceRead"]({
|
|
375
|
-
|
|
399
|
+
method: "resources/read",
|
|
400
|
+
params: { uri: "accessci://unknown" },
|
|
376
401
|
});
|
|
377
402
|
}).rejects.toThrow("Unknown resource");
|
|
378
403
|
});
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { SystemStatusServer } from "./server.js";
|
|
3
3
|
async function main() {
|
|
4
|
-
// Check if we should run as HTTP server (for deployment)
|
|
5
|
-
const port = process.env.PORT;
|
|
6
4
|
const server = new SystemStatusServer();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
await server.start({ httpPort: parseInt(port) });
|
|
10
|
-
}
|
|
11
|
-
else {
|
|
12
|
-
// Running in MCP mode (stdio)
|
|
13
|
-
await server.start();
|
|
14
|
-
}
|
|
5
|
+
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
|
|
6
|
+
await server.start(port ? { httpPort: port } : undefined);
|
|
15
7
|
}
|
|
16
|
-
main().catch((
|
|
8
|
+
main().catch(() => {
|
|
17
9
|
// Log errors to a file instead of stderr to avoid interfering with JSON-RPC
|
|
18
10
|
process.exit(1);
|
|
19
11
|
});
|
package/dist/server.d.ts
CHANGED
|
@@ -1,100 +1,16 @@
|
|
|
1
|
-
import { BaseAccessServer } from "@access-mcp/shared";
|
|
1
|
+
import { BaseAccessServer, Tool, Resource, CallToolResult } from "@access-mcp/shared";
|
|
2
|
+
import { CallToolRequest, ReadResourceRequest, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
3
|
export declare class SystemStatusServer extends BaseAccessServer {
|
|
3
4
|
constructor();
|
|
4
|
-
protected getTools():
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
limit?: undefined;
|
|
15
|
-
resource_ids?: undefined;
|
|
16
|
-
use_group_api?: undefined;
|
|
17
|
-
};
|
|
18
|
-
required: never[];
|
|
19
|
-
};
|
|
20
|
-
} | {
|
|
21
|
-
name: string;
|
|
22
|
-
description: string;
|
|
23
|
-
inputSchema: {
|
|
24
|
-
type: string;
|
|
25
|
-
properties: {
|
|
26
|
-
resource_filter: {
|
|
27
|
-
type: string;
|
|
28
|
-
description: string;
|
|
29
|
-
};
|
|
30
|
-
limit: {
|
|
31
|
-
type: string;
|
|
32
|
-
description: string;
|
|
33
|
-
};
|
|
34
|
-
resource_ids?: undefined;
|
|
35
|
-
use_group_api?: undefined;
|
|
36
|
-
};
|
|
37
|
-
required: never[];
|
|
38
|
-
};
|
|
39
|
-
} | {
|
|
40
|
-
name: string;
|
|
41
|
-
description: string;
|
|
42
|
-
inputSchema: {
|
|
43
|
-
type: string;
|
|
44
|
-
properties: {
|
|
45
|
-
limit: {
|
|
46
|
-
type: string;
|
|
47
|
-
description: string;
|
|
48
|
-
};
|
|
49
|
-
resource_filter?: undefined;
|
|
50
|
-
resource_ids?: undefined;
|
|
51
|
-
use_group_api?: undefined;
|
|
52
|
-
};
|
|
53
|
-
required: never[];
|
|
54
|
-
};
|
|
55
|
-
} | {
|
|
56
|
-
name: string;
|
|
57
|
-
description: string;
|
|
58
|
-
inputSchema: {
|
|
59
|
-
type: string;
|
|
60
|
-
properties: {
|
|
61
|
-
resource_ids: {
|
|
62
|
-
type: string;
|
|
63
|
-
items: {
|
|
64
|
-
type: string;
|
|
65
|
-
};
|
|
66
|
-
description: string;
|
|
67
|
-
};
|
|
68
|
-
use_group_api: {
|
|
69
|
-
type: string;
|
|
70
|
-
description: string;
|
|
71
|
-
default: boolean;
|
|
72
|
-
};
|
|
73
|
-
resource_filter?: undefined;
|
|
74
|
-
limit?: undefined;
|
|
75
|
-
};
|
|
76
|
-
required: string[];
|
|
77
|
-
};
|
|
78
|
-
})[];
|
|
79
|
-
protected getResources(): {
|
|
80
|
-
uri: string;
|
|
81
|
-
name: string;
|
|
82
|
-
description: string;
|
|
83
|
-
mimeType: string;
|
|
84
|
-
}[];
|
|
85
|
-
handleToolCall(request: any): Promise<{
|
|
86
|
-
content: {
|
|
87
|
-
type: string;
|
|
88
|
-
text: string;
|
|
89
|
-
}[];
|
|
90
|
-
}>;
|
|
91
|
-
handleResourceRead(request: any): Promise<{
|
|
92
|
-
contents: {
|
|
93
|
-
uri: any;
|
|
94
|
-
mimeType: string;
|
|
95
|
-
text: string;
|
|
96
|
-
}[];
|
|
97
|
-
}>;
|
|
5
|
+
protected getTools(): Tool[];
|
|
6
|
+
protected getResources(): Resource[];
|
|
7
|
+
protected handleToolCall(request: CallToolRequest): Promise<CallToolResult>;
|
|
8
|
+
/**
|
|
9
|
+
* Router for consolidated get_infrastructure_news tool
|
|
10
|
+
* Routes to appropriate handler based on parameters
|
|
11
|
+
*/
|
|
12
|
+
private getInfrastructureNewsRouter;
|
|
13
|
+
protected handleResourceRead(request: ReadResourceRequest): Promise<ReadResourceResult>;
|
|
98
14
|
private getCurrentOutages;
|
|
99
15
|
private getScheduledMaintenance;
|
|
100
16
|
private getPastOutages;
|