@access-mcp/system-status 0.4.0 → 0.5.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 +70 -83
- package/dist/__tests__/server.integration.test.js +26 -26
- package/dist/__tests__/server.test.js +39 -48
- package/dist/index.js +3 -12
- package/dist/server.d.ts +16 -50
- package/dist/server.js +78 -111
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,79 +1,67 @@
|
|
|
1
1
|
# System Status MCP Server
|
|
2
2
|
|
|
3
|
-
MCP server providing real-time system status information for ACCESS-CI resources.
|
|
3
|
+
MCP server providing real-time system status information for ACCESS-CI resources. Provides critical operational information about ACCESS-CI systems, including current outages, scheduled maintenance, and system-wide announcements.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
This server provides critical operational information about ACCESS-CI systems, including current outages, scheduled maintenance, and system-wide announcements.
|
|
8
|
-
|
|
9
|
-
## Tools
|
|
10
|
-
|
|
11
|
-
### get_current_outages
|
|
12
|
-
|
|
13
|
-
Get current system outages and issues affecting ACCESS-CI resources.
|
|
14
|
-
|
|
15
|
-
**Parameters:**
|
|
16
|
-
|
|
17
|
-
- `resource_filter` (string, optional): Filter by specific resource name or ID
|
|
18
|
-
|
|
19
|
-
**Returns:**
|
|
20
|
-
- Total outages count and severity breakdown
|
|
21
|
-
- Affected resources list
|
|
22
|
-
- Enhanced outage details with severity levels
|
|
5
|
+
## Usage Examples
|
|
23
6
|
|
|
24
|
-
###
|
|
7
|
+
### **Current Status**
|
|
25
8
|
|
|
26
|
-
|
|
9
|
+
```
|
|
10
|
+
"Current ACCESS-CI outages"
|
|
11
|
+
"Delta operational status"
|
|
12
|
+
"Systems experiencing issues"
|
|
13
|
+
"GPU systems status check"
|
|
14
|
+
```
|
|
27
15
|
|
|
28
|
-
**
|
|
16
|
+
### **Maintenance & Incidents**
|
|
29
17
|
|
|
30
|
-
|
|
18
|
+
```
|
|
19
|
+
"Scheduled maintenance this week"
|
|
20
|
+
"Next Expanse maintenance window"
|
|
21
|
+
"Past outages for Bridges-2"
|
|
22
|
+
"All infrastructure news for Delta"
|
|
23
|
+
```
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
- Scheduled maintenance sorted by start time
|
|
34
|
-
- Time until maintenance starts
|
|
35
|
-
- Duration calculations for planned windows
|
|
25
|
+
## Tools
|
|
36
26
|
|
|
37
|
-
###
|
|
27
|
+
### get_infrastructure_news
|
|
38
28
|
|
|
39
|
-
Get
|
|
29
|
+
Comprehensive ACCESS-CI infrastructure status and outage information. Get current outages, scheduled maintenance, past incidents, and operational status for ACCESS-CI resources. Provides real-time monitoring of system health and availability.
|
|
40
30
|
|
|
41
31
|
**Parameters:**
|
|
42
32
|
|
|
43
|
-
- `
|
|
44
|
-
- `
|
|
33
|
+
- `resource` (string, optional): Filter by specific resource name or ID (e.g., 'delta', 'bridges2')
|
|
34
|
+
- `time` (string, optional): Time period for infrastructure news: 'current' (active outages), 'scheduled' (future maintenance), 'past' (historical incidents), 'all' (comprehensive overview). Default: 'current'
|
|
35
|
+
- `resource_ids` (array of strings, optional): Check operational status for specific resource IDs (returns 'operational' or 'affected' status). Use instead of 'resource' parameter for status checking
|
|
36
|
+
- `limit` (number, optional): Maximum number of items to return (default: 50 for 'all', 100 for 'past')
|
|
37
|
+
- `use_group_api` (boolean, optional): Use resource group API for status checking (only with resource_ids parameter, default: false)
|
|
45
38
|
|
|
46
|
-
**
|
|
47
|
-
- Historical outage data with duration analysis
|
|
48
|
-
- Recent outages (last 30 days) summary
|
|
49
|
-
- Outage type categorization
|
|
39
|
+
**Examples:**
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
**Parameters:**
|
|
56
|
-
|
|
57
|
-
- `limit` (number, optional): Maximum number of announcements to return (default: 50)
|
|
41
|
+
```typescript
|
|
42
|
+
// Get current outages across all resources
|
|
43
|
+
get_infrastructure_news({})
|
|
58
44
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- Categorized announcements for better organization
|
|
62
|
-
- Timeline-based sorting
|
|
45
|
+
// Get scheduled maintenance
|
|
46
|
+
get_infrastructure_news({ time: "scheduled" })
|
|
63
47
|
|
|
64
|
-
|
|
48
|
+
// Get comprehensive overview of all infrastructure news
|
|
49
|
+
get_infrastructure_news({ time: "all" })
|
|
65
50
|
|
|
66
|
-
Check
|
|
51
|
+
// Check current status for specific resource
|
|
52
|
+
get_infrastructure_news({ resource: "delta", time: "current" })
|
|
67
53
|
|
|
68
|
-
|
|
54
|
+
// Get all news for specific resource
|
|
55
|
+
get_infrastructure_news({ resource: "delta", time: "all" })
|
|
69
56
|
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
// Check operational status of specific resources
|
|
58
|
+
get_infrastructure_news({
|
|
59
|
+
resource_ids: ["delta.ncsa.access-ci.org", "bridges2.psc.access-ci.org"]
|
|
60
|
+
})
|
|
72
61
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- API method used (direct vs group-specific)
|
|
62
|
+
// Get past outages with limit
|
|
63
|
+
get_infrastructure_news({ time: "past", limit: 50 })
|
|
64
|
+
```
|
|
77
65
|
|
|
78
66
|
## Resources
|
|
79
67
|
|
|
@@ -133,74 +121,73 @@ Add to your Claude Desktop configuration:
|
|
|
133
121
|
**Natural Language**: "Are there any systems down right now?"
|
|
134
122
|
|
|
135
123
|
**Tool Call**:
|
|
136
|
-
|
|
137
124
|
```typescript
|
|
138
|
-
const outages = await
|
|
125
|
+
const outages = await get_infrastructure_news({});
|
|
126
|
+
// or explicitly: get_infrastructure_news({ time: "current" })
|
|
139
127
|
```
|
|
140
128
|
|
|
141
129
|
**Returns**: List of active outages with:
|
|
142
|
-
|
|
143
130
|
- Affected resources
|
|
131
|
+
- Severity categorization (high/medium/low)
|
|
144
132
|
- Start time and expected resolution
|
|
145
133
|
- Impact description
|
|
146
|
-
-
|
|
134
|
+
- Summary statistics (total outages, affected resources)
|
|
147
135
|
|
|
148
136
|
### Finding Scheduled Maintenance
|
|
149
137
|
|
|
150
138
|
**Natural Language**: "When is Delta scheduled for maintenance?"
|
|
151
139
|
|
|
152
140
|
**Tool Call**:
|
|
153
|
-
|
|
154
141
|
```typescript
|
|
155
|
-
const maintenance = await
|
|
156
|
-
|
|
142
|
+
const maintenance = await get_infrastructure_news({
|
|
143
|
+
resource: "delta",
|
|
144
|
+
time: "scheduled"
|
|
157
145
|
});
|
|
158
146
|
```
|
|
159
147
|
|
|
160
148
|
**Returns**: Upcoming maintenance windows including:
|
|
161
|
-
|
|
162
149
|
- Scheduled start and end times
|
|
163
150
|
- Systems affected
|
|
164
|
-
-
|
|
165
|
-
-
|
|
151
|
+
- Duration in hours
|
|
152
|
+
- Hours until maintenance starts
|
|
153
|
+
- Summary (upcoming in 24h, upcoming this week)
|
|
166
154
|
|
|
167
|
-
### Getting System
|
|
155
|
+
### Getting Comprehensive System Overview
|
|
168
156
|
|
|
169
157
|
**Natural Language**: "What are the latest announcements?"
|
|
170
158
|
|
|
171
159
|
**Tool Call**:
|
|
172
|
-
|
|
173
160
|
```typescript
|
|
174
|
-
const announcements = await
|
|
175
|
-
|
|
161
|
+
const announcements = await get_infrastructure_news({
|
|
162
|
+
time: "all",
|
|
163
|
+
limit: 10
|
|
176
164
|
});
|
|
177
165
|
```
|
|
178
166
|
|
|
179
|
-
**Returns**:
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
167
|
+
**Returns**: Comprehensive overview including:
|
|
168
|
+
- Current outages
|
|
169
|
+
- Scheduled maintenance
|
|
170
|
+
- Recent past outages (last 30 days)
|
|
171
|
+
- Category breakdown
|
|
172
|
+
- Sorted by relevance (current issues first)
|
|
185
173
|
|
|
186
174
|
### Checking Specific Resource Status
|
|
187
175
|
|
|
188
176
|
**Natural Language**: "Is Expanse available?"
|
|
189
177
|
|
|
190
178
|
**Tool Call**:
|
|
191
|
-
|
|
192
179
|
```typescript
|
|
193
|
-
const status = await
|
|
194
|
-
|
|
180
|
+
const status = await get_infrastructure_news({
|
|
181
|
+
resource_ids: ["expanse.sdsc.access-ci.org"]
|
|
195
182
|
});
|
|
196
183
|
```
|
|
197
184
|
|
|
198
185
|
**Returns**: Current operational status:
|
|
199
|
-
|
|
200
|
-
-
|
|
201
|
-
-
|
|
202
|
-
-
|
|
203
|
-
-
|
|
186
|
+
- Overall status ("operational" or "affected")
|
|
187
|
+
- Severity level if affected
|
|
188
|
+
- Number of active outages
|
|
189
|
+
- Outage details with subjects
|
|
190
|
+
- Timestamp of status check
|
|
204
191
|
|
|
205
192
|
## API Endpoints
|
|
206
193
|
|
|
@@ -9,8 +9,8 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
9
9
|
it("should fetch current outages from real API", async () => {
|
|
10
10
|
const result = await server["handleToolCall"]({
|
|
11
11
|
params: {
|
|
12
|
-
name: "
|
|
13
|
-
arguments: { limit: 5 }
|
|
12
|
+
name: "get_infrastructure_news",
|
|
13
|
+
arguments: { time: "current", limit: 5 }
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
16
|
const responseData = JSON.parse(result.content[0].text);
|
|
@@ -26,15 +26,14 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
26
26
|
if (responseData.outages.length > 0) {
|
|
27
27
|
const outage = responseData.outages[0];
|
|
28
28
|
expect(outage).toHaveProperty("severity");
|
|
29
|
-
|
|
30
|
-
expect(outage).toHaveProperty("
|
|
29
|
+
// The API response includes original fields like Subject, OutageStart, OutageEnd, etc.
|
|
30
|
+
expect(outage).toHaveProperty("Subject");
|
|
31
31
|
}
|
|
32
32
|
}, 10000);
|
|
33
33
|
it("should fetch scheduled maintenance from real API", async () => {
|
|
34
34
|
const result = await server["handleToolCall"]({
|
|
35
35
|
params: {
|
|
36
|
-
name: "
|
|
37
|
-
arguments: { limit: 5 }
|
|
36
|
+
name: "get_infrastructure_news", arguments: { time: "scheduled", limit: 5 }
|
|
38
37
|
}
|
|
39
38
|
});
|
|
40
39
|
const responseData = JSON.parse(result.content[0].text);
|
|
@@ -54,8 +53,7 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
54
53
|
it("should fetch past outages from real API", async () => {
|
|
55
54
|
const result = await server["handleToolCall"]({
|
|
56
55
|
params: {
|
|
57
|
-
name: "
|
|
58
|
-
arguments: { limit: 10 }
|
|
56
|
+
name: "get_infrastructure_news", arguments: { time: "past", limit: 5 }
|
|
59
57
|
}
|
|
60
58
|
});
|
|
61
59
|
const responseData = JSON.parse(result.content[0].text);
|
|
@@ -77,8 +75,8 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
77
75
|
it("should get comprehensive system announcements", async () => {
|
|
78
76
|
const result = await server["handleToolCall"]({
|
|
79
77
|
params: {
|
|
80
|
-
name: "
|
|
81
|
-
arguments: { limit: 20 }
|
|
78
|
+
name: "get_infrastructure_news",
|
|
79
|
+
arguments: { time: "all", limit: 20 }
|
|
82
80
|
}
|
|
83
81
|
});
|
|
84
82
|
const responseData = JSON.parse(result.content[0].text);
|
|
@@ -102,9 +100,9 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
102
100
|
// Test with common resource names that might exist
|
|
103
101
|
const result = await server["handleToolCall"]({
|
|
104
102
|
params: {
|
|
105
|
-
name: "
|
|
103
|
+
name: "get_infrastructure_news",
|
|
106
104
|
arguments: {
|
|
107
|
-
|
|
105
|
+
ids: ["anvil", "bridges", "jetstream"],
|
|
108
106
|
use_group_api: false
|
|
109
107
|
}
|
|
110
108
|
}
|
|
@@ -130,9 +128,9 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
130
128
|
// Test group API with a resource that might have a group ID
|
|
131
129
|
const result = await server["handleToolCall"]({
|
|
132
130
|
params: {
|
|
133
|
-
name: "
|
|
131
|
+
name: "get_infrastructure_news",
|
|
134
132
|
arguments: {
|
|
135
|
-
|
|
133
|
+
ids: ["anvil"],
|
|
136
134
|
use_group_api: true
|
|
137
135
|
}
|
|
138
136
|
}
|
|
@@ -159,19 +157,21 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
159
157
|
it("should filter outages by resource correctly", async () => {
|
|
160
158
|
const result = await server["handleToolCall"]({
|
|
161
159
|
params: {
|
|
162
|
-
name: "
|
|
163
|
-
arguments: {
|
|
160
|
+
name: "get_infrastructure_news",
|
|
161
|
+
arguments: { time: "current", query: "anvil" }
|
|
164
162
|
}
|
|
165
163
|
});
|
|
166
164
|
const responseData = JSON.parse(result.content[0].text);
|
|
167
165
|
expect(responseData).toHaveProperty("total_outages");
|
|
168
166
|
// If there are any results, they should match the filter
|
|
169
|
-
responseData.outages.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
resource.
|
|
173
|
-
|
|
174
|
-
|
|
167
|
+
if (responseData.outages.length > 0) {
|
|
168
|
+
responseData.outages.forEach((outage) => {
|
|
169
|
+
const matchesFilter = outage.Subject?.toLowerCase().includes("anvil") ||
|
|
170
|
+
outage.AffectedResources?.some((resource) => resource.ResourceName?.toLowerCase().includes("anvil") ||
|
|
171
|
+
resource.ResourceID?.toString().includes("anvil"));
|
|
172
|
+
expect(matchesFilter).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
175
|
}, 10000);
|
|
176
176
|
it("should handle resource reads for all endpoints", async () => {
|
|
177
177
|
const resources = [
|
|
@@ -200,8 +200,8 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
200
200
|
// This tests the robustness of our logic with potentially empty responses
|
|
201
201
|
const result = await server["handleToolCall"]({
|
|
202
202
|
params: {
|
|
203
|
-
name: "
|
|
204
|
-
arguments: {
|
|
203
|
+
name: "get_infrastructure_news",
|
|
204
|
+
arguments: { time: "current", query: "nonexistent-resource-xyz-12345" }
|
|
205
205
|
}
|
|
206
206
|
});
|
|
207
207
|
const responseData = JSON.parse(result.content[0].text);
|
|
@@ -212,8 +212,8 @@ describe("SystemStatusServer Integration Tests", () => {
|
|
|
212
212
|
it("should handle large limit values gracefully", async () => {
|
|
213
213
|
const result = await server["handleToolCall"]({
|
|
214
214
|
params: {
|
|
215
|
-
name: "
|
|
216
|
-
arguments: { limit: 1000 }
|
|
215
|
+
name: "get_infrastructure_news",
|
|
216
|
+
arguments: { time: "past", limit: 1000 }
|
|
217
217
|
}
|
|
218
218
|
});
|
|
219
219
|
const responseData = JSON.parse(result.content[0].text);
|
|
@@ -10,8 +10,8 @@ describe("SystemStatusServer", () => {
|
|
|
10
10
|
id: "1",
|
|
11
11
|
Subject: "Emergency maintenance on Anvil",
|
|
12
12
|
Content: "Critical issue requiring immediate attention",
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
OutageStart: "2024-08-27T10:00:00Z",
|
|
14
|
+
OutageEnd: "2024-08-27T11:00:00Z",
|
|
15
15
|
AffectedResources: [
|
|
16
16
|
{ ResourceName: "Anvil", ResourceID: "anvil-1" }
|
|
17
17
|
]
|
|
@@ -20,8 +20,8 @@ describe("SystemStatusServer", () => {
|
|
|
20
20
|
id: "2",
|
|
21
21
|
Subject: "Scheduled maintenance on Bridges-2",
|
|
22
22
|
Content: "Regular maintenance window",
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
OutageStart: "2024-08-27T08:00:00Z",
|
|
24
|
+
OutageEnd: "2024-08-27T08:30:00Z",
|
|
25
25
|
AffectedResources: [
|
|
26
26
|
{ ResourceName: "Bridges-2", ResourceID: "bridges2-1" }
|
|
27
27
|
]
|
|
@@ -32,10 +32,8 @@ describe("SystemStatusServer", () => {
|
|
|
32
32
|
id: "3",
|
|
33
33
|
Subject: "Scheduled Jetstream maintenance",
|
|
34
34
|
Content: "Planned maintenance",
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
OutageStartDateTime: "2024-08-30T10:00:00Z",
|
|
38
|
-
OutageEndDateTime: "2024-08-30T14:00:00Z",
|
|
35
|
+
OutageStart: "2024-08-30T10:00:00Z",
|
|
36
|
+
OutageEnd: "2024-08-30T14:00:00Z",
|
|
39
37
|
AffectedResources: [
|
|
40
38
|
{ ResourceName: "Jetstream", ResourceID: "jetstream-1" }
|
|
41
39
|
]
|
|
@@ -46,10 +44,8 @@ describe("SystemStatusServer", () => {
|
|
|
46
44
|
id: "4",
|
|
47
45
|
Subject: "Past maintenance on Stampede3",
|
|
48
46
|
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
|
|
47
|
+
OutageStart: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(), // 3 days ago
|
|
48
|
+
OutageEnd: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000 + 6 * 60 * 60 * 1000).toISOString(), // 3 days ago + 6 hours
|
|
53
49
|
OutageType: "Full",
|
|
54
50
|
AffectedResources: [
|
|
55
51
|
{ ResourceName: "Stampede3", ResourceID: "stampede3-1" }
|
|
@@ -80,14 +76,8 @@ describe("SystemStatusServer", () => {
|
|
|
80
76
|
});
|
|
81
77
|
it("should provide correct tools", () => {
|
|
82
78
|
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
|
-
]);
|
|
79
|
+
expect(tools).toHaveLength(1);
|
|
80
|
+
expect(tools[0].name).toBe("get_infrastructure_news");
|
|
91
81
|
});
|
|
92
82
|
it("should provide correct resources", () => {
|
|
93
83
|
const resources = server["getResources"]();
|
|
@@ -107,7 +97,7 @@ describe("SystemStatusServer", () => {
|
|
|
107
97
|
data: { results: mockCurrentOutagesData }
|
|
108
98
|
});
|
|
109
99
|
const result = await server["handleToolCall"]({
|
|
110
|
-
params: { name: "
|
|
100
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } }
|
|
111
101
|
});
|
|
112
102
|
expect(mockHttpClient.get).toHaveBeenCalledWith("/wh2/news/v1/affiliation/access-ci.org/current_outages/");
|
|
113
103
|
const response = JSON.parse(result.content[0].text);
|
|
@@ -116,7 +106,6 @@ describe("SystemStatusServer", () => {
|
|
|
116
106
|
expect(response.severity_counts).toHaveProperty("high", 1); // Emergency
|
|
117
107
|
expect(response.severity_counts).toHaveProperty("low", 1); // Scheduled maintenance
|
|
118
108
|
expect(response.outages[0]).toHaveProperty("severity");
|
|
119
|
-
expect(response.outages[0]).toHaveProperty("posted_time");
|
|
120
109
|
});
|
|
121
110
|
it("should filter outages by resource", async () => {
|
|
122
111
|
mockHttpClient.get.mockResolvedValue({
|
|
@@ -125,8 +114,8 @@ describe("SystemStatusServer", () => {
|
|
|
125
114
|
});
|
|
126
115
|
const result = await server["handleToolCall"]({
|
|
127
116
|
params: {
|
|
128
|
-
name: "
|
|
129
|
-
arguments: {
|
|
117
|
+
name: "get_infrastructure_news",
|
|
118
|
+
arguments: { query: "Anvil", time: "current" }
|
|
130
119
|
}
|
|
131
120
|
});
|
|
132
121
|
const response = JSON.parse(result.content[0].text);
|
|
@@ -139,7 +128,7 @@ describe("SystemStatusServer", () => {
|
|
|
139
128
|
data: { results: mockCurrentOutagesData }
|
|
140
129
|
});
|
|
141
130
|
const result = await server["handleToolCall"]({
|
|
142
|
-
params: { name: "
|
|
131
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } }
|
|
143
132
|
});
|
|
144
133
|
const response = JSON.parse(result.content[0].text);
|
|
145
134
|
const emergencyOutage = response.outages.find((o) => o.Subject.includes("Emergency"));
|
|
@@ -155,7 +144,7 @@ describe("SystemStatusServer", () => {
|
|
|
155
144
|
data: { results: mockFutureOutagesData }
|
|
156
145
|
});
|
|
157
146
|
const result = await server["handleToolCall"]({
|
|
158
|
-
params: { name: "
|
|
147
|
+
params: { name: "get_infrastructure_news", arguments: { time: "scheduled" } }
|
|
159
148
|
});
|
|
160
149
|
const response = JSON.parse(result.content[0].text);
|
|
161
150
|
expect(response.total_scheduled).toBe(1);
|
|
@@ -167,15 +156,15 @@ describe("SystemStatusServer", () => {
|
|
|
167
156
|
it("should handle missing scheduled times", async () => {
|
|
168
157
|
const dataWithoutSchedule = [{
|
|
169
158
|
...mockFutureOutagesData[0],
|
|
170
|
-
|
|
171
|
-
|
|
159
|
+
OutageStart: null,
|
|
160
|
+
OutageEnd: null
|
|
172
161
|
}];
|
|
173
162
|
mockHttpClient.get.mockResolvedValue({
|
|
174
163
|
status: 200,
|
|
175
164
|
data: { results: dataWithoutSchedule }
|
|
176
165
|
});
|
|
177
166
|
const result = await server["handleToolCall"]({
|
|
178
|
-
params: { name: "
|
|
167
|
+
params: { name: "get_infrastructure_news", arguments: { time: "scheduled" } }
|
|
179
168
|
});
|
|
180
169
|
const response = JSON.parse(result.content[0].text);
|
|
181
170
|
expect(response.maintenance[0].has_scheduled_time).toBe(false);
|
|
@@ -185,12 +174,12 @@ describe("SystemStatusServer", () => {
|
|
|
185
174
|
const multipleMaintenanceData = [
|
|
186
175
|
{
|
|
187
176
|
...mockFutureOutagesData[0],
|
|
188
|
-
|
|
177
|
+
OutageStart: "2024-08-31T10:00:00Z", // Later
|
|
189
178
|
Subject: "Later maintenance"
|
|
190
179
|
},
|
|
191
180
|
{
|
|
192
181
|
...mockFutureOutagesData[0],
|
|
193
|
-
|
|
182
|
+
OutageStart: "2024-08-30T10:00:00Z", // Earlier
|
|
194
183
|
Subject: "Earlier maintenance"
|
|
195
184
|
}
|
|
196
185
|
];
|
|
@@ -199,7 +188,7 @@ describe("SystemStatusServer", () => {
|
|
|
199
188
|
data: { results: multipleMaintenanceData }
|
|
200
189
|
});
|
|
201
190
|
const result = await server["handleToolCall"]({
|
|
202
|
-
params: { name: "
|
|
191
|
+
params: { name: "get_infrastructure_news", arguments: { time: "scheduled" } }
|
|
203
192
|
});
|
|
204
193
|
const response = JSON.parse(result.content[0].text);
|
|
205
194
|
expect(response.maintenance[0].Subject).toBe("Earlier maintenance");
|
|
@@ -213,7 +202,7 @@ describe("SystemStatusServer", () => {
|
|
|
213
202
|
data: { results: mockPastOutagesData }
|
|
214
203
|
});
|
|
215
204
|
const result = await server["handleToolCall"]({
|
|
216
|
-
params: { name: "
|
|
205
|
+
params: { name: "get_infrastructure_news", arguments: { time: "past" } }
|
|
217
206
|
});
|
|
218
207
|
const response = JSON.parse(result.content[0].text);
|
|
219
208
|
expect(response.total_past_outages).toBe(1);
|
|
@@ -234,8 +223,8 @@ describe("SystemStatusServer", () => {
|
|
|
234
223
|
});
|
|
235
224
|
const result = await server["handleToolCall"]({
|
|
236
225
|
params: {
|
|
237
|
-
name: "
|
|
238
|
-
arguments: { limit: 10 }
|
|
226
|
+
name: "get_infrastructure_news",
|
|
227
|
+
arguments: { time: "past", limit: 10 }
|
|
239
228
|
}
|
|
240
229
|
});
|
|
241
230
|
const response = JSON.parse(result.content[0].text);
|
|
@@ -249,7 +238,7 @@ describe("SystemStatusServer", () => {
|
|
|
249
238
|
.mockResolvedValueOnce({ status: 200, data: { results: mockFutureOutagesData } })
|
|
250
239
|
.mockResolvedValueOnce({ status: 200, data: { results: mockPastOutagesData } });
|
|
251
240
|
const result = await server["handleToolCall"]({
|
|
252
|
-
params: { name: "
|
|
241
|
+
params: { name: "get_infrastructure_news", arguments: { time: "all" } }
|
|
253
242
|
});
|
|
254
243
|
expect(mockHttpClient.get).toHaveBeenCalledTimes(3);
|
|
255
244
|
const response = JSON.parse(result.content[0].text);
|
|
@@ -266,7 +255,7 @@ describe("SystemStatusServer", () => {
|
|
|
266
255
|
.mockResolvedValueOnce({ status: 200, data: { results: mockFutureOutagesData } })
|
|
267
256
|
.mockResolvedValueOnce({ status: 200, data: { results: mockPastOutagesData } });
|
|
268
257
|
const result = await server["handleToolCall"]({
|
|
269
|
-
params: { name: "
|
|
258
|
+
params: { name: "get_infrastructure_news", arguments: { time: "all" } }
|
|
270
259
|
});
|
|
271
260
|
const response = JSON.parse(result.content[0].text);
|
|
272
261
|
const firstAnnouncement = response.announcements[0];
|
|
@@ -281,8 +270,8 @@ describe("SystemStatusServer", () => {
|
|
|
281
270
|
});
|
|
282
271
|
const result = await server["handleToolCall"]({
|
|
283
272
|
params: {
|
|
284
|
-
name: "
|
|
285
|
-
arguments: {
|
|
273
|
+
name: "get_infrastructure_news",
|
|
274
|
+
arguments: { ids: ["anvil-1", "unknown-resource"] }
|
|
286
275
|
}
|
|
287
276
|
});
|
|
288
277
|
const response = JSON.parse(result.content[0].text);
|
|
@@ -301,9 +290,9 @@ describe("SystemStatusServer", () => {
|
|
|
301
290
|
});
|
|
302
291
|
const result = await server["handleToolCall"]({
|
|
303
292
|
params: {
|
|
304
|
-
name: "
|
|
293
|
+
name: "get_infrastructure_news",
|
|
305
294
|
arguments: {
|
|
306
|
-
|
|
295
|
+
ids: ["anvil"],
|
|
307
296
|
use_group_api: true
|
|
308
297
|
}
|
|
309
298
|
}
|
|
@@ -318,15 +307,15 @@ describe("SystemStatusServer", () => {
|
|
|
318
307
|
mockHttpClient.get.mockRejectedValue(new Error("API Error"));
|
|
319
308
|
const result = await server["handleToolCall"]({
|
|
320
309
|
params: {
|
|
321
|
-
name: "
|
|
310
|
+
name: "get_infrastructure_news",
|
|
322
311
|
arguments: {
|
|
323
|
-
|
|
312
|
+
ids: ["invalid-resource"],
|
|
324
313
|
use_group_api: true
|
|
325
314
|
}
|
|
326
315
|
}
|
|
327
316
|
});
|
|
328
317
|
const response = JSON.parse(result.content[0].text);
|
|
329
|
-
expect(response
|
|
318
|
+
expect(response).toHaveProperty("unknown", 1);
|
|
330
319
|
expect(response.resource_status[0].status).toBe("unknown");
|
|
331
320
|
expect(response.resource_status[0].api_method).toBe("group_specific_failed");
|
|
332
321
|
expect(response.resource_status[0]).toHaveProperty("error");
|
|
@@ -339,16 +328,18 @@ describe("SystemStatusServer", () => {
|
|
|
339
328
|
statusText: "Internal Server Error"
|
|
340
329
|
});
|
|
341
330
|
const result = await server["handleToolCall"]({
|
|
342
|
-
params: { name: "
|
|
331
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } }
|
|
343
332
|
});
|
|
344
|
-
|
|
333
|
+
const response = JSON.parse(result.content[0].text);
|
|
334
|
+
expect(response.error).toBeDefined();
|
|
345
335
|
});
|
|
346
336
|
it("should handle network errors", async () => {
|
|
347
337
|
mockHttpClient.get.mockRejectedValue(new Error("Network error"));
|
|
348
338
|
const result = await server["handleToolCall"]({
|
|
349
|
-
params: { name: "
|
|
339
|
+
params: { name: "get_infrastructure_news", arguments: { time: "current" } }
|
|
350
340
|
});
|
|
351
|
-
|
|
341
|
+
const response = JSON.parse(result.content[0].text);
|
|
342
|
+
expect(response.error).toBe("Network error");
|
|
352
343
|
});
|
|
353
344
|
it("should handle unknown tools", async () => {
|
|
354
345
|
const result = await server["handleToolCall"]({
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { SystemStatusServer } from "./server.js";
|
|
3
|
-
import { startWebServer } from "./web-server.js";
|
|
4
3
|
async function main() {
|
|
5
|
-
|
|
6
|
-
const port = process.env.PORT;
|
|
7
|
-
|
|
8
|
-
// Running in web mode (deployment)
|
|
9
|
-
startWebServer(parseInt(port));
|
|
10
|
-
}
|
|
11
|
-
else {
|
|
12
|
-
// Running in MCP mode (stdio)
|
|
13
|
-
const server = new SystemStatusServer();
|
|
14
|
-
await server.start();
|
|
15
|
-
}
|
|
4
|
+
const server = new SystemStatusServer();
|
|
5
|
+
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
|
|
6
|
+
await server.start(port ? { httpPort: port } : undefined);
|
|
16
7
|
}
|
|
17
8
|
main().catch((error) => {
|
|
18
9
|
// Log errors to a file instead of stderr to avoid interfering with JSON-RPC
|
package/dist/server.d.ts
CHANGED
|
@@ -1,81 +1,42 @@
|
|
|
1
1
|
import { BaseAccessServer } from "@access-mcp/shared";
|
|
2
2
|
export declare class SystemStatusServer extends BaseAccessServer {
|
|
3
3
|
constructor();
|
|
4
|
-
protected getTools():
|
|
4
|
+
protected getTools(): {
|
|
5
5
|
name: string;
|
|
6
6
|
description: string;
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: string;
|
|
9
9
|
properties: {
|
|
10
|
-
|
|
10
|
+
query: {
|
|
11
11
|
type: string;
|
|
12
12
|
description: string;
|
|
13
13
|
};
|
|
14
|
-
|
|
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: {
|
|
14
|
+
time: {
|
|
27
15
|
type: string;
|
|
16
|
+
enum: string[];
|
|
28
17
|
description: string;
|
|
18
|
+
default: string;
|
|
29
19
|
};
|
|
30
|
-
|
|
20
|
+
ids: {
|
|
31
21
|
type: string;
|
|
22
|
+
items: {
|
|
23
|
+
type: string;
|
|
24
|
+
};
|
|
32
25
|
description: string;
|
|
33
26
|
};
|
|
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
27
|
limit: {
|
|
46
28
|
type: string;
|
|
47
29
|
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;
|
|
30
|
+
default: number;
|
|
67
31
|
};
|
|
68
32
|
use_group_api: {
|
|
69
33
|
type: string;
|
|
70
34
|
description: string;
|
|
71
35
|
default: boolean;
|
|
72
36
|
};
|
|
73
|
-
resource_filter?: undefined;
|
|
74
|
-
limit?: undefined;
|
|
75
37
|
};
|
|
76
|
-
required: string[];
|
|
77
38
|
};
|
|
78
|
-
}
|
|
39
|
+
}[];
|
|
79
40
|
protected getResources(): {
|
|
80
41
|
uri: string;
|
|
81
42
|
name: string;
|
|
@@ -88,6 +49,11 @@ export declare class SystemStatusServer extends BaseAccessServer {
|
|
|
88
49
|
text: string;
|
|
89
50
|
}[];
|
|
90
51
|
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Router for consolidated get_infrastructure_news tool
|
|
54
|
+
* Routes to appropriate handler based on parameters
|
|
55
|
+
*/
|
|
56
|
+
private getInfrastructureNewsRouter;
|
|
91
57
|
handleResourceRead(request: any): Promise<{
|
|
92
58
|
contents: {
|
|
93
59
|
uri: any;
|
package/dist/server.js
CHANGED
|
@@ -6,83 +6,37 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
6
6
|
getTools() {
|
|
7
7
|
return [
|
|
8
8
|
{
|
|
9
|
-
name: "
|
|
10
|
-
description: "Get
|
|
9
|
+
name: "get_infrastructure_news",
|
|
10
|
+
description: "Get ACCESS-CI infrastructure status (outages, maintenance, incidents). Returns {total, items}.",
|
|
11
11
|
inputSchema: {
|
|
12
12
|
type: "object",
|
|
13
13
|
properties: {
|
|
14
|
-
|
|
14
|
+
query: {
|
|
15
15
|
type: "string",
|
|
16
|
-
description: "
|
|
16
|
+
description: "Filter by resource name (e.g., 'delta', 'bridges2')"
|
|
17
17
|
},
|
|
18
|
-
|
|
19
|
-
required: [],
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "get_scheduled_maintenance",
|
|
24
|
-
description: "Get scheduled maintenance and future outages for ACCESS-CI resources",
|
|
25
|
-
inputSchema: {
|
|
26
|
-
type: "object",
|
|
27
|
-
properties: {
|
|
28
|
-
resource_filter: {
|
|
18
|
+
time: {
|
|
29
19
|
type: "string",
|
|
30
|
-
|
|
20
|
+
enum: ["current", "scheduled", "past", "all"],
|
|
21
|
+
description: "Period: current (active), scheduled (future), past, all",
|
|
22
|
+
default: "current"
|
|
31
23
|
},
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{
|
|
37
|
-
name: "get_past_outages",
|
|
38
|
-
description: "Get historical outages and past incidents affecting ACCESS-CI resources",
|
|
39
|
-
inputSchema: {
|
|
40
|
-
type: "object",
|
|
41
|
-
properties: {
|
|
42
|
-
resource_filter: {
|
|
43
|
-
type: "string",
|
|
44
|
-
description: "Optional: filter by specific resource name or ID",
|
|
24
|
+
ids: {
|
|
25
|
+
type: "array",
|
|
26
|
+
items: { type: "string" },
|
|
27
|
+
description: "Check status for specific resource IDs"
|
|
45
28
|
},
|
|
46
29
|
limit: {
|
|
47
30
|
type: "number",
|
|
48
|
-
description: "
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
required: [],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: "get_system_announcements",
|
|
56
|
-
description: "Get all system announcements (current and scheduled)",
|
|
57
|
-
inputSchema: {
|
|
58
|
-
type: "object",
|
|
59
|
-
properties: {
|
|
60
|
-
limit: {
|
|
61
|
-
type: "number",
|
|
62
|
-
description: "Maximum number of announcements to return (default: 50)",
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
required: [],
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "check_resource_status",
|
|
70
|
-
description: "Check the operational status of specific ACCESS-CI resources",
|
|
71
|
-
inputSchema: {
|
|
72
|
-
type: "object",
|
|
73
|
-
properties: {
|
|
74
|
-
resource_ids: {
|
|
75
|
-
type: "array",
|
|
76
|
-
items: { type: "string" },
|
|
77
|
-
description: "List of resource IDs or names to check status for",
|
|
31
|
+
description: "Max results (default: 50)",
|
|
32
|
+
default: 50
|
|
78
33
|
},
|
|
79
34
|
use_group_api: {
|
|
80
35
|
type: "boolean",
|
|
81
|
-
description: "Use
|
|
82
|
-
default: false
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
required: ["resource_ids"],
|
|
36
|
+
description: "Use group API for status (with ids only)",
|
|
37
|
+
default: false
|
|
38
|
+
}
|
|
39
|
+
}
|
|
86
40
|
},
|
|
87
41
|
},
|
|
88
42
|
];
|
|
@@ -119,29 +73,44 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
119
73
|
const { name, arguments: args = {} } = request.params;
|
|
120
74
|
try {
|
|
121
75
|
switch (name) {
|
|
122
|
-
case "
|
|
123
|
-
return await this.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
case "check_resource_status":
|
|
131
|
-
return await this.checkResourceStatus(args.resource_ids, args.use_group_api);
|
|
76
|
+
case "get_infrastructure_news":
|
|
77
|
+
return await this.getInfrastructureNewsRouter({
|
|
78
|
+
resource: args.query,
|
|
79
|
+
time: args.time,
|
|
80
|
+
resource_ids: args.ids,
|
|
81
|
+
limit: args.limit,
|
|
82
|
+
use_group_api: args.use_group_api
|
|
83
|
+
});
|
|
132
84
|
default:
|
|
133
|
-
|
|
85
|
+
return this.errorResponse(`Unknown tool: ${name}`);
|
|
134
86
|
}
|
|
135
87
|
}
|
|
136
88
|
catch (error) {
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
89
|
+
return this.errorResponse(handleApiError(error));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Router for consolidated get_infrastructure_news tool
|
|
94
|
+
* Routes to appropriate handler based on parameters
|
|
95
|
+
*/
|
|
96
|
+
async getInfrastructureNewsRouter(args) {
|
|
97
|
+
const { resource, time = "current", resource_ids, limit, use_group_api = false } = args;
|
|
98
|
+
// Check resource status (returns operational/affected)
|
|
99
|
+
if (resource_ids && Array.isArray(resource_ids)) {
|
|
100
|
+
return await this.checkResourceStatus(resource_ids, use_group_api);
|
|
101
|
+
}
|
|
102
|
+
// Time-based routing
|
|
103
|
+
switch (time) {
|
|
104
|
+
case "current":
|
|
105
|
+
return await this.getCurrentOutages(resource);
|
|
106
|
+
case "scheduled":
|
|
107
|
+
return await this.getScheduledMaintenance(resource);
|
|
108
|
+
case "past":
|
|
109
|
+
return await this.getPastOutages(resource, limit || 100);
|
|
110
|
+
case "all":
|
|
111
|
+
return await this.getSystemAnnouncements(limit || 50);
|
|
112
|
+
default:
|
|
113
|
+
throw new Error(`Invalid time parameter: ${time}. Must be one of: current, scheduled, past, all`);
|
|
145
114
|
}
|
|
146
115
|
}
|
|
147
116
|
async handleResourceRead(request) {
|
|
@@ -230,8 +199,6 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
230
199
|
return {
|
|
231
200
|
...outage,
|
|
232
201
|
severity,
|
|
233
|
-
posted_time: outage.CreationTime,
|
|
234
|
-
last_updated: outage.LastModificationTime,
|
|
235
202
|
};
|
|
236
203
|
});
|
|
237
204
|
const summary = {
|
|
@@ -261,8 +228,8 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
261
228
|
}
|
|
262
229
|
// Sort by scheduled start time
|
|
263
230
|
maintenance.sort((a, b) => {
|
|
264
|
-
const dateA = new Date(a.
|
|
265
|
-
const dateB = new Date(b.
|
|
231
|
+
const dateA = new Date(a.OutageStart);
|
|
232
|
+
const dateB = new Date(b.OutageStart);
|
|
266
233
|
return dateA.getTime() - dateB.getTime();
|
|
267
234
|
});
|
|
268
235
|
// Initialize tracking variables
|
|
@@ -274,9 +241,9 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
274
241
|
item.AffectedResources?.forEach((resource) => {
|
|
275
242
|
affectedResources.add(resource.ResourceName);
|
|
276
243
|
});
|
|
277
|
-
// Check timing -
|
|
278
|
-
const hasScheduledTime = !!item.
|
|
279
|
-
const startTime = new Date(item.
|
|
244
|
+
// Check timing - use OutageStart for scheduling
|
|
245
|
+
const hasScheduledTime = !!item.OutageStart;
|
|
246
|
+
const startTime = new Date(item.OutageStart);
|
|
280
247
|
const now = new Date();
|
|
281
248
|
const hoursUntil = (startTime.getTime() - now.getTime()) / (1000 * 60 * 60);
|
|
282
249
|
if (hoursUntil <= 24)
|
|
@@ -285,12 +252,12 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
285
252
|
upcomingWeek++; // 7 days * 24 hours
|
|
286
253
|
return {
|
|
287
254
|
...item,
|
|
288
|
-
scheduled_start: item.
|
|
289
|
-
scheduled_end: item.
|
|
255
|
+
scheduled_start: item.OutageStart,
|
|
256
|
+
scheduled_end: item.OutageEnd,
|
|
290
257
|
hours_until_start: Math.max(0, Math.round(hoursUntil)),
|
|
291
|
-
duration_hours: item.
|
|
292
|
-
? Math.round((new Date(item.
|
|
293
|
-
new Date(item.
|
|
258
|
+
duration_hours: item.OutageEnd && item.OutageStart
|
|
259
|
+
? Math.round((new Date(item.OutageEnd).getTime() -
|
|
260
|
+
new Date(item.OutageStart).getTime()) /
|
|
294
261
|
(1000 * 60 * 60))
|
|
295
262
|
: null,
|
|
296
263
|
has_scheduled_time: hasScheduledTime,
|
|
@@ -324,8 +291,8 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
324
291
|
}
|
|
325
292
|
// Sort by outage end time (most recent first)
|
|
326
293
|
pastOutages.sort((a, b) => {
|
|
327
|
-
const dateA = new Date(a.
|
|
328
|
-
const dateB = new Date(b.
|
|
294
|
+
const dateA = new Date(a.OutageEnd);
|
|
295
|
+
const dateB = new Date(b.OutageEnd);
|
|
329
296
|
return dateB.getTime() - dateA.getTime();
|
|
330
297
|
});
|
|
331
298
|
// Apply limit
|
|
@@ -336,7 +303,7 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
336
303
|
const affectedResources = new Set();
|
|
337
304
|
const outageTypes = new Set();
|
|
338
305
|
const recentOutages = pastOutages.filter((outage) => {
|
|
339
|
-
const endTime = new Date(outage.
|
|
306
|
+
const endTime = new Date(outage.OutageEnd);
|
|
340
307
|
const daysAgo = (Date.now() - endTime.getTime()) / (1000 * 60 * 60 * 24);
|
|
341
308
|
return daysAgo <= 30; // Last 30 days
|
|
342
309
|
});
|
|
@@ -351,20 +318,18 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
351
318
|
outageTypes.add(outage.OutageType);
|
|
352
319
|
}
|
|
353
320
|
// Calculate duration
|
|
354
|
-
const startTime = new Date(outage.
|
|
355
|
-
const endTime = new Date(outage.
|
|
321
|
+
const startTime = new Date(outage.OutageStart);
|
|
322
|
+
const endTime = new Date(outage.OutageEnd);
|
|
356
323
|
const durationHours = Math.round((endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60));
|
|
357
324
|
// Calculate how long ago it ended
|
|
358
325
|
const daysAgo = Math.round((Date.now() - endTime.getTime()) / (1000 * 60 * 60 * 24));
|
|
359
326
|
return {
|
|
360
327
|
...outage,
|
|
361
|
-
outage_start: outage.
|
|
362
|
-
outage_end: outage.
|
|
328
|
+
outage_start: outage.OutageStart,
|
|
329
|
+
outage_end: outage.OutageEnd,
|
|
363
330
|
duration_hours: durationHours,
|
|
364
331
|
days_ago: daysAgo,
|
|
365
332
|
outage_type: outage.OutageType,
|
|
366
|
-
posted_time: outage.CreationTime,
|
|
367
|
-
last_updated: outage.LastModificationTime,
|
|
368
333
|
};
|
|
369
334
|
});
|
|
370
335
|
const summary = {
|
|
@@ -401,7 +366,7 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
401
366
|
const pastOutages = pastResponse.data.results || [];
|
|
402
367
|
// Filter recent past outages (last 30 days) for announcements
|
|
403
368
|
const recentPastOutages = pastOutages.filter((outage) => {
|
|
404
|
-
const endTime = new Date(outage.
|
|
369
|
+
const endTime = new Date(outage.OutageEnd);
|
|
405
370
|
const daysAgo = (Date.now() - endTime.getTime()) / (1000 * 60 * 60 * 24);
|
|
406
371
|
return daysAgo <= 30;
|
|
407
372
|
});
|
|
@@ -417,8 +382,8 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
417
382
|
return -1;
|
|
418
383
|
if (b.category === 'current' && a.category !== 'current')
|
|
419
384
|
return 1;
|
|
420
|
-
const dateA = new Date(a.
|
|
421
|
-
const dateB = new Date(b.
|
|
385
|
+
const dateA = new Date(a.OutageStart);
|
|
386
|
+
const dateB = new Date(b.OutageStart);
|
|
422
387
|
return dateB.getTime() - dateA.getTime(); // Most recent first
|
|
423
388
|
})
|
|
424
389
|
.slice(0, limit);
|
|
@@ -444,6 +409,9 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
444
409
|
};
|
|
445
410
|
}
|
|
446
411
|
async checkResourceStatus(resourceIds, useGroupApi = false) {
|
|
412
|
+
if (!resourceIds || !Array.isArray(resourceIds) || resourceIds.length === 0) {
|
|
413
|
+
throw new Error("resource_ids parameter is required and must be a non-empty array of resource IDs");
|
|
414
|
+
}
|
|
447
415
|
if (useGroupApi) {
|
|
448
416
|
return await this.checkResourceStatusViaGroups(resourceIds);
|
|
449
417
|
}
|
|
@@ -485,8 +453,6 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
485
453
|
outage_details: affectedOutages.map((outage) => ({
|
|
486
454
|
subject: outage.Subject,
|
|
487
455
|
severity,
|
|
488
|
-
posted: outage.CreationTime,
|
|
489
|
-
last_updated: outage.LastModificationTime,
|
|
490
456
|
})),
|
|
491
457
|
};
|
|
492
458
|
});
|
|
@@ -508,6 +474,9 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
508
474
|
};
|
|
509
475
|
}
|
|
510
476
|
async checkResourceStatusViaGroups(resourceIds) {
|
|
477
|
+
if (!resourceIds || !Array.isArray(resourceIds) || resourceIds.length === 0) {
|
|
478
|
+
throw new Error("resource_ids parameter is required and must be a non-empty array of resource IDs");
|
|
479
|
+
}
|
|
511
480
|
// Try to use the more efficient group-based API
|
|
512
481
|
const statusPromises = resourceIds.map(async (resourceId) => {
|
|
513
482
|
try {
|
|
@@ -521,8 +490,6 @@ export class SystemStatusServer extends BaseAccessServer {
|
|
|
521
490
|
active_outages: groupData.length,
|
|
522
491
|
outage_details: groupData.map((outage) => ({
|
|
523
492
|
subject: outage.Subject,
|
|
524
|
-
posted: outage.CreationTime,
|
|
525
|
-
last_updated: outage.LastModificationTime,
|
|
526
493
|
})),
|
|
527
494
|
api_method: "group_specific",
|
|
528
495
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@access-mcp/system-status",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "MCP server for ACCESS-CI System Status and Outages API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"node": ">=18.0.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@access-mcp/shared": "^0.3.
|
|
47
|
+
"@access-mcp/shared": "^0.3.3",
|
|
48
48
|
"express": "^4.18.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|