@access-mcp/system-status 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # ACCESS-CI System Status MCP Server
2
+
3
+ MCP server providing real-time system status information for ACCESS-CI resources.
4
+
5
+ ## Overview
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
+ Get current system outages and issues affecting ACCESS-CI resources.
13
+
14
+ **Parameters:**
15
+ - `resource_filter` (string, optional): Filter by specific resource name or ID
16
+
17
+ ### get_scheduled_maintenance
18
+ Get scheduled maintenance and future outages for ACCESS-CI resources.
19
+
20
+ **Parameters:**
21
+ - `resource_filter` (string, optional): Filter by specific resource name or ID
22
+
23
+ ### get_system_announcements
24
+ Get all system announcements (current and scheduled).
25
+
26
+ **Parameters:**
27
+ - `limit` (number, optional): Maximum number of announcements to return (default: 50)
28
+
29
+ ### get_resource_status
30
+ Get the current operational status of a specific resource.
31
+
32
+ **Parameters:**
33
+ - `resource_id` (string): The resource ID to check status for
34
+
35
+ ## Resources
36
+
37
+ - `accessci://system-status`: Current operational status of all ACCESS-CI resources
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install -g @access-mcp/system-status
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ Add to your Claude Desktop configuration:
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "access-system-status": {
53
+ "command": "access-mcp-system-status"
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## API Endpoints
60
+
61
+ This server connects to the ACCESS-CI Operations API at `https://operations-api.access-ci.org`
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { SystemStatusServer } from './server.js';
3
+ import { startWebServer } from './web-server.js';
4
+ async function main() {
5
+ // Check if we should run as web server (for deployment)
6
+ const port = process.env.PORT;
7
+ if (port) {
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
+ }
16
+ }
17
+ main().catch((error) => {
18
+ // Log errors to a file instead of stderr to avoid interfering with JSON-RPC
19
+ process.exit(1);
20
+ });
@@ -0,0 +1,76 @@
1
+ import { BaseAccessServer } from '../../shared/dist/index.js';
2
+ export declare class SystemStatusServer extends BaseAccessServer {
3
+ constructor();
4
+ protected getTools(): ({
5
+ name: string;
6
+ description: string;
7
+ inputSchema: {
8
+ type: string;
9
+ properties: {
10
+ resource_filter: {
11
+ type: string;
12
+ description: string;
13
+ };
14
+ limit?: undefined;
15
+ resource_ids?: undefined;
16
+ };
17
+ required: never[];
18
+ };
19
+ } | {
20
+ name: string;
21
+ description: string;
22
+ inputSchema: {
23
+ type: string;
24
+ properties: {
25
+ limit: {
26
+ type: string;
27
+ description: string;
28
+ };
29
+ resource_filter?: undefined;
30
+ resource_ids?: undefined;
31
+ };
32
+ required: never[];
33
+ };
34
+ } | {
35
+ name: string;
36
+ description: string;
37
+ inputSchema: {
38
+ type: string;
39
+ properties: {
40
+ resource_ids: {
41
+ type: string;
42
+ items: {
43
+ type: string;
44
+ };
45
+ description: string;
46
+ };
47
+ resource_filter?: undefined;
48
+ limit?: undefined;
49
+ };
50
+ required: string[];
51
+ };
52
+ })[];
53
+ protected getResources(): {
54
+ uri: string;
55
+ name: string;
56
+ description: string;
57
+ mimeType: string;
58
+ }[];
59
+ handleToolCall(request: any): Promise<{
60
+ content: {
61
+ type: string;
62
+ text: string;
63
+ }[];
64
+ }>;
65
+ handleResourceRead(request: any): Promise<{
66
+ contents: {
67
+ uri: any;
68
+ mimeType: string;
69
+ text: string;
70
+ }[];
71
+ }>;
72
+ private getCurrentOutages;
73
+ private getScheduledMaintenance;
74
+ private getSystemAnnouncements;
75
+ private checkResourceStatus;
76
+ }
package/dist/server.js ADDED
@@ -0,0 +1,347 @@
1
+ import { BaseAccessServer, handleApiError } from '../../shared/dist/index.js';
2
+ export class SystemStatusServer extends BaseAccessServer {
3
+ constructor() {
4
+ super('access-mcp-system-status', '0.1.0', 'https://operations-api.access-ci.org');
5
+ }
6
+ getTools() {
7
+ return [
8
+ {
9
+ name: 'get_current_outages',
10
+ description: 'Get current system outages and issues affecting ACCESS-CI resources',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ resource_filter: {
15
+ type: 'string',
16
+ description: 'Optional: filter by specific resource name or ID',
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: {
29
+ type: 'string',
30
+ description: 'Optional: filter by specific resource name or ID',
31
+ },
32
+ },
33
+ required: [],
34
+ },
35
+ },
36
+ {
37
+ name: 'get_system_announcements',
38
+ description: 'Get all system announcements (current and scheduled)',
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: {
42
+ limit: {
43
+ type: 'number',
44
+ description: 'Maximum number of announcements to return (default: 50)',
45
+ },
46
+ },
47
+ required: [],
48
+ },
49
+ },
50
+ {
51
+ name: 'check_resource_status',
52
+ description: 'Check the operational status of specific ACCESS-CI resources',
53
+ inputSchema: {
54
+ type: 'object',
55
+ properties: {
56
+ resource_ids: {
57
+ type: 'array',
58
+ items: { type: 'string' },
59
+ description: 'List of resource IDs to check status for',
60
+ },
61
+ },
62
+ required: ['resource_ids'],
63
+ },
64
+ },
65
+ ];
66
+ }
67
+ getResources() {
68
+ return [
69
+ {
70
+ uri: 'accessci://system-status',
71
+ name: 'ACCESS-CI System Status',
72
+ description: 'Real-time status of ACCESS-CI infrastructure, outages, and maintenance',
73
+ mimeType: 'application/json',
74
+ },
75
+ {
76
+ uri: 'accessci://outages/current',
77
+ name: 'Current Outages',
78
+ description: 'Currently active outages and system issues',
79
+ mimeType: 'application/json',
80
+ },
81
+ {
82
+ uri: 'accessci://outages/scheduled',
83
+ name: 'Scheduled Maintenance',
84
+ description: 'Upcoming scheduled maintenance and planned outages',
85
+ mimeType: 'application/json',
86
+ },
87
+ ];
88
+ }
89
+ async handleToolCall(request) {
90
+ const { name, arguments: args = {} } = request.params;
91
+ try {
92
+ switch (name) {
93
+ case 'get_current_outages':
94
+ return await this.getCurrentOutages(args.resource_filter);
95
+ case 'get_scheduled_maintenance':
96
+ return await this.getScheduledMaintenance(args.resource_filter);
97
+ case 'get_system_announcements':
98
+ return await this.getSystemAnnouncements(args.limit);
99
+ case 'check_resource_status':
100
+ return await this.checkResourceStatus(args.resource_ids);
101
+ default:
102
+ throw new Error(`Unknown tool: ${name}`);
103
+ }
104
+ }
105
+ catch (error) {
106
+ return {
107
+ content: [
108
+ {
109
+ type: 'text',
110
+ text: `Error: ${handleApiError(error)}`,
111
+ },
112
+ ],
113
+ };
114
+ }
115
+ }
116
+ async handleResourceRead(request) {
117
+ const { uri } = request.params;
118
+ switch (uri) {
119
+ case 'accessci://system-status':
120
+ return {
121
+ contents: [
122
+ {
123
+ uri,
124
+ mimeType: 'text/plain',
125
+ text: 'ACCESS-CI System Status API - Monitor real-time status, outages, and maintenance for ACCESS-CI resources.',
126
+ },
127
+ ],
128
+ };
129
+ case 'accessci://outages/current':
130
+ const currentOutages = await this.getCurrentOutages();
131
+ return {
132
+ contents: [
133
+ {
134
+ uri,
135
+ mimeType: 'application/json',
136
+ text: currentOutages.content[0].text,
137
+ },
138
+ ],
139
+ };
140
+ case 'accessci://outages/scheduled':
141
+ const scheduledMaintenance = await this.getScheduledMaintenance();
142
+ return {
143
+ contents: [
144
+ {
145
+ uri,
146
+ mimeType: 'application/json',
147
+ text: scheduledMaintenance.content[0].text,
148
+ },
149
+ ],
150
+ };
151
+ default:
152
+ throw new Error(`Unknown resource: ${uri}`);
153
+ }
154
+ }
155
+ async getCurrentOutages(resourceFilter) {
156
+ const response = await this.httpClient.get('/wh2/news/v1/affiliation/access-ci.org/current_outages/');
157
+ let outages = response.data.results || [];
158
+ // Filter by resource if specified
159
+ if (resourceFilter) {
160
+ const filter = resourceFilter.toLowerCase();
161
+ outages = outages.filter((outage) => outage.Subject?.toLowerCase().includes(filter) ||
162
+ outage.AffectedResources?.some((resource) => resource.ResourceName?.toLowerCase().includes(filter) ||
163
+ resource.ResourceID?.toString().includes(filter)));
164
+ }
165
+ // Initialize tracking variables
166
+ const affectedResources = new Set();
167
+ const severityCounts = { high: 0, medium: 0, low: 0, unknown: 0 };
168
+ // Enhance outages with status summary
169
+ const enhancedOutages = outages.map((outage) => {
170
+ // Track affected resources
171
+ outage.AffectedResources?.forEach((resource) => {
172
+ affectedResources.add(resource.ResourceName);
173
+ });
174
+ // Categorize severity (basic heuristic)
175
+ const subject = outage.Subject?.toLowerCase() || '';
176
+ let severity = 'unknown';
177
+ if (subject.includes('emergency') || subject.includes('critical')) {
178
+ severity = 'high';
179
+ }
180
+ else if (subject.includes('maintenance') || subject.includes('scheduled')) {
181
+ severity = 'low';
182
+ }
183
+ else {
184
+ severity = 'medium';
185
+ }
186
+ severityCounts[severity]++;
187
+ return {
188
+ ...outage,
189
+ severity,
190
+ posted_time: outage.CreationTime,
191
+ last_updated: outage.LastModificationTime,
192
+ };
193
+ });
194
+ const summary = {
195
+ total_outages: outages.length,
196
+ affected_resources: Array.from(affectedResources),
197
+ severity_counts: severityCounts,
198
+ outages: enhancedOutages
199
+ };
200
+ return {
201
+ content: [
202
+ {
203
+ type: 'text',
204
+ text: JSON.stringify({
205
+ ...summary,
206
+ affected_resources: summary.affected_resources
207
+ }, null, 2),
208
+ },
209
+ ],
210
+ };
211
+ }
212
+ async getScheduledMaintenance(resourceFilter) {
213
+ const response = await this.httpClient.get('/wh2/news/v1/affiliation/access-ci.org/future_outages/');
214
+ let maintenance = response.data.results || [];
215
+ // Filter by resource if specified
216
+ if (resourceFilter) {
217
+ const filter = resourceFilter.toLowerCase();
218
+ maintenance = maintenance.filter((item) => item.Subject?.toLowerCase().includes(filter) ||
219
+ item.AffectedResources?.some((resource) => resource.ResourceName?.toLowerCase().includes(filter) ||
220
+ resource.ResourceID?.toString().includes(filter)));
221
+ }
222
+ // Sort by scheduled start time
223
+ maintenance.sort((a, b) => {
224
+ const dateA = new Date(a.OutageStartDateTime || a.CreationTime);
225
+ const dateB = new Date(b.OutageStartDateTime || b.CreationTime);
226
+ return dateA.getTime() - dateB.getTime();
227
+ });
228
+ const summary = {
229
+ total_scheduled: maintenance.length,
230
+ upcoming_24h: 0,
231
+ upcoming_week: 0,
232
+ affected_resources: new Set(),
233
+ maintenance: maintenance.map((item) => {
234
+ // Track affected resources
235
+ item.AffectedResources?.forEach((resource) => {
236
+ summary.affected_resources.add(resource.ResourceName);
237
+ });
238
+ // Check timing
239
+ const startTime = new Date(item.OutageStartDateTime || item.CreationTime);
240
+ const now = new Date();
241
+ const hoursUntil = (startTime.getTime() - now.getTime()) / (1000 * 60 * 60);
242
+ if (hoursUntil <= 24)
243
+ summary.upcoming_24h++;
244
+ if (hoursUntil <= 168)
245
+ summary.upcoming_week++; // 7 days * 24 hours
246
+ return {
247
+ ...item,
248
+ scheduled_start: item.OutageStartDateTime,
249
+ scheduled_end: item.OutageEndDateTime,
250
+ hours_until_start: Math.max(0, Math.round(hoursUntil)),
251
+ duration_hours: item.OutageEndDateTime && item.OutageStartDateTime
252
+ ? Math.round((new Date(item.OutageEndDateTime).getTime() -
253
+ new Date(item.OutageStartDateTime).getTime()) / (1000 * 60 * 60))
254
+ : null,
255
+ };
256
+ })
257
+ };
258
+ return {
259
+ content: [
260
+ {
261
+ type: 'text',
262
+ text: JSON.stringify({
263
+ ...summary,
264
+ affected_resources: summary.affected_resources
265
+ }, null, 2),
266
+ },
267
+ ],
268
+ };
269
+ }
270
+ async getSystemAnnouncements(limit = 50) {
271
+ // Get both current and future announcements
272
+ const [currentResponse, futureResponse] = await Promise.all([
273
+ this.httpClient.get('/wh2/news/v1/affiliation/access-ci.org/current_outages/'),
274
+ this.httpClient.get('/wh2/news/v1/affiliation/access-ci.org/future_outages/')
275
+ ]);
276
+ const currentOutages = currentResponse.data.results || [];
277
+ const futureOutages = futureResponse.data.results || [];
278
+ // Combine and sort by creation time
279
+ const allAnnouncements = [...currentOutages, ...futureOutages]
280
+ .sort((a, b) => {
281
+ const dateA = new Date(a.CreationTime);
282
+ const dateB = new Date(b.CreationTime);
283
+ return dateB.getTime() - dateA.getTime(); // Most recent first
284
+ })
285
+ .slice(0, limit);
286
+ return {
287
+ content: [
288
+ {
289
+ type: 'text',
290
+ text: JSON.stringify({
291
+ total_announcements: allAnnouncements.length,
292
+ current_outages: currentOutages.length,
293
+ scheduled_maintenance: futureOutages.length,
294
+ announcements: allAnnouncements
295
+ }, null, 2),
296
+ },
297
+ ],
298
+ };
299
+ }
300
+ async checkResourceStatus(resourceIds) {
301
+ // Get current outages to check against resource IDs
302
+ const currentOutages = await this.getCurrentOutages();
303
+ const outageData = JSON.parse(currentOutages.content[0].text);
304
+ const resourceStatus = resourceIds.map(resourceId => {
305
+ const affectedOutages = outageData.outages.filter((outage) => outage.AffectedResources?.some((resource) => resource.ResourceID?.toString() === resourceId ||
306
+ resource.ResourceName?.toLowerCase().includes(resourceId.toLowerCase())));
307
+ let status = 'operational';
308
+ let severity = null;
309
+ if (affectedOutages.length > 0) {
310
+ status = 'affected';
311
+ // Get highest severity
312
+ const severities = affectedOutages.map((o) => o.severity);
313
+ if (severities.includes('high'))
314
+ severity = 'high';
315
+ else if (severities.includes('medium'))
316
+ severity = 'medium';
317
+ else
318
+ severity = 'low';
319
+ }
320
+ return {
321
+ resource_id: resourceId,
322
+ status,
323
+ severity,
324
+ active_outages: affectedOutages.length,
325
+ outage_details: affectedOutages.map((outage) => ({
326
+ subject: outage.Subject,
327
+ severity: outage.severity,
328
+ posted: outage.posted_time
329
+ }))
330
+ };
331
+ });
332
+ return {
333
+ content: [
334
+ {
335
+ type: 'text',
336
+ text: JSON.stringify({
337
+ checked_at: new Date().toISOString(),
338
+ resources_checked: resourceIds.length,
339
+ operational: resourceStatus.filter(r => r.status === 'operational').length,
340
+ affected: resourceStatus.filter(r => r.status === 'affected').length,
341
+ resource_status: resourceStatus
342
+ }, null, 2),
343
+ },
344
+ ],
345
+ };
346
+ }
347
+ }
@@ -0,0 +1 @@
1
+ export declare function startWebServer(port?: number): void;
@@ -0,0 +1,50 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import { SystemStatusServer } from './server.js';
4
+ export function startWebServer(port = 3000) {
5
+ const app = express();
6
+ const server = new SystemStatusServer();
7
+ // Serve static files from public directory
8
+ const publicDir = path.join(__dirname, '../../../public');
9
+ app.use(express.static(publicDir));
10
+ // Health check endpoint
11
+ app.get('/health', (req, res) => {
12
+ res.json({ status: 'ok', service: 'access-mcp-system-status' });
13
+ });
14
+ // API endpoint to list tools (for documentation)
15
+ app.get('/api/tools', (req, res) => {
16
+ res.json({
17
+ server: 'ACCESS-CI System Status MCP Server',
18
+ version: '0.1.0',
19
+ tools: [
20
+ {
21
+ name: 'get_current_outages',
22
+ description: 'Get current system outages and issues affecting ACCESS-CI resources'
23
+ },
24
+ {
25
+ name: 'get_scheduled_maintenance',
26
+ description: 'Get scheduled maintenance and future outages for ACCESS-CI resources'
27
+ },
28
+ {
29
+ name: 'get_system_announcements',
30
+ description: 'Get all system announcements (current and scheduled)'
31
+ },
32
+ {
33
+ name: 'check_resource_status',
34
+ description: 'Check the operational status of specific ACCESS-CI resources'
35
+ }
36
+ ],
37
+ resources: [
38
+ {
39
+ uri: 'accessci://system-status',
40
+ name: 'ACCESS-CI System Status',
41
+ description: 'Real-time status of ACCESS-CI infrastructure, outages, and maintenance'
42
+ }
43
+ ]
44
+ });
45
+ });
46
+ // Start server
47
+ app.listen(port, () => {
48
+ console.log(`ACCESS-CI System Status web server running on port ${port}`);
49
+ });
50
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@access-mcp/system-status",
3
+ "version": "0.2.0",
4
+ "description": "MCP server for ACCESS-CI System Status and Outages API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "access-mcp-system-status": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "mcp",
20
+ "model-context-protocol",
21
+ "access-ci",
22
+ "hpc",
23
+ "system-status",
24
+ "outages",
25
+ "monitoring"
26
+ ],
27
+ "author": "ACCESS-CI",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/access-ci/access-mcp.git",
32
+ "directory": "packages/system-status"
33
+ },
34
+ "homepage": "https://github.com/access-ci/access-mcp#readme",
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "dependencies": {
39
+ "@access-mcp/shared": "^0.1.0",
40
+ "express": "^4.18.0"
41
+ }
42
+ }