@davidfuchs/mcp-uptime-kuma 0.7.0 → 0.10.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 +47 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +300 -6
- package/dist/server.js.map +1 -1
- package/dist/types/docker-host.d.ts +26 -0
- package/dist/types/docker-host.d.ts.map +1 -0
- package/dist/types/docker-host.js +13 -0
- package/dist/types/docker-host.js.map +1 -0
- package/dist/types/heartbeat.d.ts +32 -32
- package/dist/types/heartbeat.d.ts.map +1 -1
- package/dist/types/heartbeat.js +2 -2
- package/dist/types/heartbeat.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/maintenance.d.ts +27 -27
- package/dist/types/maintenance.js +3 -3
- package/dist/types/maintenance.js.map +1 -1
- package/dist/types/monitor-base.js +1 -1
- package/dist/types/monitor-base.js.map +1 -1
- package/dist/types/status-page.d.ts +12 -12
- package/dist/types/status-page.js +4 -4
- package/dist/types/status-page.js.map +1 -1
- package/dist/uptime-kuma-client.d.ts +108 -6
- package/dist/uptime-kuma-client.d.ts.map +1 -1
- package/dist/uptime-kuma-client.js +341 -20
- package/dist/uptime-kuma-client.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +75 -1
package/README.md
CHANGED
|
@@ -86,16 +86,56 @@ See [Authentication Methods](#authentication-methods) for JWT token and anonymou
|
|
|
86
86
|
|
|
87
87
|
## Available Tools
|
|
88
88
|
|
|
89
|
+
### Monitors
|
|
90
|
+
|
|
89
91
|
| Tool | Purpose |
|
|
90
92
|
|------|---------|
|
|
91
93
|
| `getMonitorSummary` | Get a quick overview of all monitors with their current status. Supports filtering. |
|
|
92
94
|
| `listMonitors` | Get the full list of all monitors with configurations. Supports filtering. |
|
|
93
95
|
| `listMonitorTypes` | Get all available monitor types supported by Uptime Kuma. |
|
|
94
96
|
| `getMonitor` | Get detailed configuration for a specific monitor by ID. |
|
|
97
|
+
| `createMonitor` | Create a new monitor (requires name and type at minimum). |
|
|
98
|
+
| `updateMonitor` | Update an existing monitor's configuration. |
|
|
99
|
+
| `deleteMonitor` | Permanently delete a monitor and all its heartbeat history. |
|
|
95
100
|
| `pauseMonitor` | Pause a monitor to stop performing checks. |
|
|
96
101
|
| `resumeMonitor` | Resume a paused monitor to restart checks. |
|
|
102
|
+
|
|
103
|
+
### Heartbeats
|
|
104
|
+
|
|
105
|
+
| Tool | Purpose |
|
|
106
|
+
|------|---------|
|
|
97
107
|
| `listHeartbeats` | Get status check history for all monitors. |
|
|
98
108
|
| `getHeartbeats` | Get status check history for a specific monitor. |
|
|
109
|
+
|
|
110
|
+
### Notifications
|
|
111
|
+
|
|
112
|
+
| Tool | Purpose |
|
|
113
|
+
|------|---------|
|
|
114
|
+
| `listNotifications` | List all configured notification channels (Slack, Discord, email, webhooks, etc.). |
|
|
115
|
+
| `addNotification` | Create a new notification channel. |
|
|
116
|
+
| `updateNotification` | Update an existing notification channel. |
|
|
117
|
+
| `deleteNotification` | Permanently delete a notification channel. |
|
|
118
|
+
|
|
119
|
+
### Tags
|
|
120
|
+
|
|
121
|
+
| Tool | Purpose |
|
|
122
|
+
|------|---------|
|
|
123
|
+
| `listTags` | List all tags defined in Uptime Kuma. |
|
|
124
|
+
| `addTag` | Create a new tag that can be assigned to monitors. |
|
|
125
|
+
| `deleteTag` | Permanently delete a tag (removes it from all monitors). |
|
|
126
|
+
|
|
127
|
+
### Maintenance
|
|
128
|
+
|
|
129
|
+
| Tool | Purpose |
|
|
130
|
+
|------|---------|
|
|
131
|
+
| `getMaintenanceWindows` | List all scheduled maintenance windows. |
|
|
132
|
+
| `createMaintenance` | Schedule a new maintenance window. |
|
|
133
|
+
|
|
134
|
+
### Status Pages & Settings
|
|
135
|
+
|
|
136
|
+
| Tool | Purpose |
|
|
137
|
+
|------|---------|
|
|
138
|
+
| `listStatusPages` | List all configured status pages. |
|
|
99
139
|
| `getSettings` | Get Uptime Kuma server settings. |
|
|
100
140
|
|
|
101
141
|
### Filtering
|
|
@@ -167,12 +207,19 @@ mcpServers:
|
|
|
167
207
|
```
|
|
168
208
|
|
|
169
209
|
**streamable HTTP transport:**
|
|
210
|
+
|
|
211
|
+
Update the allowed domains to whatever domain you're using in the URL (e.g., `localhost` or `host.docker.internal` for Docker setups):
|
|
212
|
+
|
|
170
213
|
```yaml
|
|
171
214
|
mcpServers:
|
|
172
215
|
uptime-kuma:
|
|
173
216
|
type: streamable-http
|
|
174
217
|
url: "http://mcp-uptime-kuma:3000/mcp"
|
|
175
218
|
serverInstructions: true
|
|
219
|
+
|
|
220
|
+
mcpSettings:
|
|
221
|
+
allowedDomains:
|
|
222
|
+
- 'mcp-uptime-kuma'
|
|
176
223
|
```
|
|
177
224
|
|
|
178
225
|
## Contributing
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAs2C9J"}
|
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import { McpError, ErrorCode, SetLevelRequestSchema, LoggingLevelSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { UptimeKumaClient } from './uptime-kuma-client.js';
|
|
5
|
-
import { HeartbeatSchema, MonitorBaseSchema, MonitorSummarySchema, SettingsSchema, NotificationSchema, MaintenanceSchema, StatusPageSchema } from './types/index.js';
|
|
5
|
+
import { HeartbeatSchema, MonitorBaseSchema, MonitorSummarySchema, SettingsSchema, NotificationSchema, MaintenanceSchema, StatusPageSchema, DockerHostSchema } from './types/index.js';
|
|
6
6
|
import { VERSION } from './version.js';
|
|
7
7
|
/**
|
|
8
8
|
* Creates and configures the MCP server with tools, resources, and prompts
|
|
@@ -25,13 +25,17 @@ export async function createServer(config) {
|
|
|
25
25
|
- Use 'listNotifications' to see notification channels.
|
|
26
26
|
- Use 'listTags' to see available tags.
|
|
27
27
|
- Use 'getMaintenanceWindows' to see scheduled maintenance.
|
|
28
|
-
- Use 'listStatusPages' to see status page configurations.
|
|
28
|
+
- Use 'listStatusPages' to see status page configurations, or 'getStatusPage' for one page's full details (groups + monitors).
|
|
29
|
+
- Use 'listDockerHosts' to see configured docker daemons (used by docker container monitors).
|
|
29
30
|
|
|
30
31
|
WRITE operations:
|
|
31
32
|
- Use 'createMonitor' / 'updateMonitor' / 'deleteMonitor' to manage monitors.
|
|
32
33
|
- Use 'addNotification' / 'updateNotification' / 'deleteNotification' to manage notification channels.
|
|
33
34
|
- Use 'addTag' / 'deleteTag' to manage tags.
|
|
34
35
|
- Use 'createMaintenance' to schedule a maintenance window.
|
|
36
|
+
- Use 'addDockerHost' / 'updateDockerHost' / 'deleteDockerHost' to manage docker daemon connections.
|
|
37
|
+
- Use 'testDockerHost' to verify a docker daemon is reachable before saving.
|
|
38
|
+
- Use 'createStatusPage' / 'updateStatusPage' / 'deleteStatusPage' to manage status pages. Creating returns an empty page — follow up with updateStatusPage to set groups and monitors.
|
|
35
39
|
- Use 'pauseMonitor' / 'resumeMonitor' to temporarily stop/start checks.
|
|
36
40
|
`,
|
|
37
41
|
capabilities: {
|
|
@@ -436,6 +440,10 @@ export async function createServer(config) {
|
|
|
436
440
|
maxredirects: z.coerce.number().optional().describe('Max HTTP redirects (default: 10)'),
|
|
437
441
|
upsideDown: z.boolean().optional().describe('Invert status — treat up as down'),
|
|
438
442
|
parent: z.coerce.number().nullable().optional().describe('Parent group monitor ID'),
|
|
443
|
+
docker_container: z.string().optional().describe('Docker container name (required for docker type)'),
|
|
444
|
+
docker_host: z.coerce.number().optional().describe('Docker host ID (required for docker type). Use listDockerHosts to find available IDs.'),
|
|
445
|
+
dns_resolve_server: z.string().optional().describe('DNS server to use for resolution (required for dns type, default: 1.1.1.1)'),
|
|
446
|
+
dns_resolve_type: z.enum(['A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT', 'CAA']).optional().describe('DNS record type to query (required for dns type, default: A)'),
|
|
439
447
|
},
|
|
440
448
|
outputSchema: {
|
|
441
449
|
ok: z.boolean(),
|
|
@@ -447,10 +455,18 @@ export async function createServer(config) {
|
|
|
447
455
|
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
448
456
|
}
|
|
449
457
|
try {
|
|
450
|
-
const
|
|
458
|
+
const defaults = {
|
|
451
459
|
notificationIDList: {},
|
|
452
460
|
accepted_statuscodes: ['200-299'],
|
|
453
461
|
conditions: [],
|
|
462
|
+
retryInterval: 60,
|
|
463
|
+
};
|
|
464
|
+
if (input.type === 'dns') {
|
|
465
|
+
defaults.dns_resolve_server = '1.1.1.1';
|
|
466
|
+
defaults.dns_resolve_type = 'A';
|
|
467
|
+
}
|
|
468
|
+
const monitorData = {
|
|
469
|
+
...defaults,
|
|
454
470
|
...input,
|
|
455
471
|
};
|
|
456
472
|
const response = await client.createMonitor(monitorData);
|
|
@@ -492,6 +508,10 @@ export async function createServer(config) {
|
|
|
492
508
|
maxredirects: z.coerce.number().optional().describe('Max HTTP redirects'),
|
|
493
509
|
upsideDown: z.boolean().optional().describe('Invert status'),
|
|
494
510
|
active: z.boolean().optional().describe('Whether the monitor is active'),
|
|
511
|
+
docker_container: z.string().optional().describe('Docker container name (required for docker type)'),
|
|
512
|
+
docker_host: z.coerce.number().optional().describe('Docker host ID (required for docker type). Use listDockerHosts to find available IDs.'),
|
|
513
|
+
dns_resolve_server: z.string().optional().describe('DNS server to use for resolution (for dns type)'),
|
|
514
|
+
dns_resolve_type: z.enum(['A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT', 'CAA']).optional().describe('DNS record type to query (for dns type)'),
|
|
495
515
|
},
|
|
496
516
|
outputSchema: {
|
|
497
517
|
ok: z.boolean(),
|
|
@@ -507,7 +527,14 @@ export async function createServer(config) {
|
|
|
507
527
|
if (!existing) {
|
|
508
528
|
throw new Error(`Monitor ${monitorID} not found`);
|
|
509
529
|
}
|
|
510
|
-
|
|
530
|
+
// Strip undefined values so existing config is preserved for omitted fields
|
|
531
|
+
const defined = Object.fromEntries(Object.entries(rest).filter(([, v]) => v !== undefined));
|
|
532
|
+
const merged = { ...existing, ...defined, id: monitorID };
|
|
533
|
+
// Ensure retryInterval is valid — Kuma rejects values < 1 on edit even if
|
|
534
|
+
// it stored 0 during creation (pre-existing monitors or older defaults)
|
|
535
|
+
if (!merged.retryInterval || merged.retryInterval < 1) {
|
|
536
|
+
merged.retryInterval = merged.interval || 60;
|
|
537
|
+
}
|
|
511
538
|
const response = await client.updateMonitor(merged);
|
|
512
539
|
return {
|
|
513
540
|
content: [{ type: 'text', text: response.msg || `Monitor ${monitorID} updated successfully` }],
|
|
@@ -669,6 +696,156 @@ export async function createServer(config) {
|
|
|
669
696
|
throw new McpError(ErrorCode.InternalError, `Failed to delete notification: ${errorMessage}`);
|
|
670
697
|
}
|
|
671
698
|
});
|
|
699
|
+
// ─── Docker host tools ───────────────────────────────────────────────────
|
|
700
|
+
server.registerTool('listDockerHosts', {
|
|
701
|
+
title: 'List Docker Hosts',
|
|
702
|
+
description: 'Returns all docker daemon connections configured in Uptime Kuma. These are referenced by docker container monitors via docker_host.',
|
|
703
|
+
inputSchema: {},
|
|
704
|
+
outputSchema: {
|
|
705
|
+
dockerHosts: z.array(DockerHostSchema).describe('Array of docker host configurations'),
|
|
706
|
+
count: z.number(),
|
|
707
|
+
},
|
|
708
|
+
}, async () => {
|
|
709
|
+
if (!isAuthenticated) {
|
|
710
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
const dockerHosts = client.getDockerHostList();
|
|
714
|
+
return {
|
|
715
|
+
content: [{ type: 'text', text: JSON.stringify(dockerHosts, null, 2) }],
|
|
716
|
+
structuredContent: { dockerHosts, count: dockerHosts.length },
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
catch (error) {
|
|
720
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
721
|
+
throw new McpError(ErrorCode.InternalError, `Failed to list docker hosts: ${errorMessage}`);
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
server.registerTool('addDockerHost', {
|
|
725
|
+
title: 'Add Docker Host',
|
|
726
|
+
description: 'Creates a new docker daemon connection. For a unix socket use dockerType="socket" and dockerDaemon="/var/run/docker.sock". For a TCP proxy (e.g. tecnativa/docker-socket-proxy) use dockerType="tcp" and dockerDaemon="http://host:2375". Consider calling testDockerHost first to verify reachability.',
|
|
727
|
+
inputSchema: {
|
|
728
|
+
name: z.string().describe('Human-readable name for this docker host'),
|
|
729
|
+
dockerType: z.enum(['socket', 'tcp']).describe('"socket" for a unix socket path, "tcp" for an HTTP/HTTPS URL'),
|
|
730
|
+
dockerDaemon: z.string().describe('Unix socket path (e.g. /var/run/docker.sock) when dockerType=socket, or TCP URL (e.g. http://docker-proxy:2375) when dockerType=tcp'),
|
|
731
|
+
},
|
|
732
|
+
outputSchema: {
|
|
733
|
+
ok: z.boolean(),
|
|
734
|
+
id: z.number().optional(),
|
|
735
|
+
msg: z.string().optional(),
|
|
736
|
+
},
|
|
737
|
+
}, async ({ name, dockerType, dockerDaemon }) => {
|
|
738
|
+
if (!isAuthenticated) {
|
|
739
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
740
|
+
}
|
|
741
|
+
try {
|
|
742
|
+
const response = await client.addDockerHost({ name, dockerType, dockerDaemon });
|
|
743
|
+
return {
|
|
744
|
+
content: [{ type: 'text', text: response.msg || `Docker host created with ID ${response.id}` }],
|
|
745
|
+
structuredContent: { ok: response.ok, id: response.id, msg: response.msg },
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
750
|
+
throw new McpError(ErrorCode.InternalError, `Failed to add docker host: ${errorMessage}`);
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
server.registerTool('updateDockerHost', {
|
|
754
|
+
title: 'Update Docker Host',
|
|
755
|
+
description: 'Updates an existing docker daemon connection. Use listDockerHosts to find the docker host ID. Only the fields you pass are changed — the others are preserved.',
|
|
756
|
+
inputSchema: {
|
|
757
|
+
dockerHostID: z.coerce.number().int().nonnegative().describe('The ID of the docker host to update'),
|
|
758
|
+
name: z.string().optional().describe('New human-readable name'),
|
|
759
|
+
dockerType: z.enum(['socket', 'tcp']).optional().describe('New connection type'),
|
|
760
|
+
dockerDaemon: z.string().optional().describe('New socket path or TCP URL'),
|
|
761
|
+
},
|
|
762
|
+
outputSchema: {
|
|
763
|
+
ok: z.boolean(),
|
|
764
|
+
id: z.number().optional(),
|
|
765
|
+
msg: z.string().optional(),
|
|
766
|
+
},
|
|
767
|
+
}, async ({ dockerHostID, name, dockerType, dockerDaemon }) => {
|
|
768
|
+
if (!isAuthenticated) {
|
|
769
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
770
|
+
}
|
|
771
|
+
try {
|
|
772
|
+
// Merge new values onto the current record so callers can omit unchanged fields.
|
|
773
|
+
// Uptime Kuma's addDockerHost handler overwrites every column it receives, so we
|
|
774
|
+
// need to send the full set to avoid clobbering existing values with undefined.
|
|
775
|
+
const existing = client.getDockerHostList().find(h => h.id === dockerHostID);
|
|
776
|
+
if (!existing) {
|
|
777
|
+
throw new Error(`Docker host ${dockerHostID} not found — call listDockerHosts to see available IDs`);
|
|
778
|
+
}
|
|
779
|
+
const merged = {
|
|
780
|
+
name: name ?? existing.name,
|
|
781
|
+
dockerType: dockerType ?? existing.dockerType,
|
|
782
|
+
dockerDaemon: dockerDaemon ?? existing.dockerDaemon,
|
|
783
|
+
};
|
|
784
|
+
const response = await client.addDockerHost(merged, dockerHostID);
|
|
785
|
+
return {
|
|
786
|
+
content: [{ type: 'text', text: response.msg || `Docker host ${dockerHostID} updated` }],
|
|
787
|
+
structuredContent: { ok: response.ok, id: response.id, msg: response.msg },
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
792
|
+
throw new McpError(ErrorCode.InternalError, `Failed to update docker host: ${errorMessage}`);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
server.registerTool('deleteDockerHost', {
|
|
796
|
+
title: 'Delete Docker Host',
|
|
797
|
+
description: 'Permanently deletes a docker daemon connection. Any monitors referencing it will have their docker_host cleared by Uptime Kuma (the monitors themselves are not deleted).',
|
|
798
|
+
inputSchema: {
|
|
799
|
+
dockerHostID: z.coerce.number().int().nonnegative().describe('The ID of the docker host to delete'),
|
|
800
|
+
},
|
|
801
|
+
outputSchema: {
|
|
802
|
+
ok: z.boolean(),
|
|
803
|
+
msg: z.string().optional(),
|
|
804
|
+
},
|
|
805
|
+
}, async ({ dockerHostID }) => {
|
|
806
|
+
if (!isAuthenticated) {
|
|
807
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
const response = await client.deleteDockerHost(dockerHostID);
|
|
811
|
+
return {
|
|
812
|
+
content: [{ type: 'text', text: response.msg || `Docker host ${dockerHostID} deleted` }],
|
|
813
|
+
structuredContent: { ok: response.ok, msg: response.msg },
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
catch (error) {
|
|
817
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
818
|
+
throw new McpError(ErrorCode.InternalError, `Failed to delete docker host: ${errorMessage}`);
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
server.registerTool('testDockerHost', {
|
|
822
|
+
title: 'Test Docker Host',
|
|
823
|
+
description: 'Tests connectivity to a docker daemon without persisting it. On success the message includes the number of containers. Use this before addDockerHost to avoid saving a broken configuration.',
|
|
824
|
+
inputSchema: {
|
|
825
|
+
name: z.string().describe('Display name (used only in the test request)'),
|
|
826
|
+
dockerType: z.enum(['socket', 'tcp']).describe('"socket" for a unix socket path, "tcp" for an HTTP/HTTPS URL'),
|
|
827
|
+
dockerDaemon: z.string().describe('Unix socket path or TCP URL to probe'),
|
|
828
|
+
},
|
|
829
|
+
outputSchema: {
|
|
830
|
+
ok: z.boolean(),
|
|
831
|
+
msg: z.string().optional(),
|
|
832
|
+
},
|
|
833
|
+
}, async ({ name, dockerType, dockerDaemon }) => {
|
|
834
|
+
if (!isAuthenticated) {
|
|
835
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
836
|
+
}
|
|
837
|
+
try {
|
|
838
|
+
const response = await client.testDockerHost({ name, dockerType, dockerDaemon });
|
|
839
|
+
return {
|
|
840
|
+
content: [{ type: 'text', text: response.msg || (response.ok ? 'Docker host reachable' : 'Docker host unreachable') }],
|
|
841
|
+
structuredContent: { ok: response.ok, msg: response.msg },
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
catch (error) {
|
|
845
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
846
|
+
throw new McpError(ErrorCode.InternalError, `Failed to test docker host: ${errorMessage}`);
|
|
847
|
+
}
|
|
848
|
+
});
|
|
672
849
|
// ─── Tag tools ───────────────────────────────────────────────────────────
|
|
673
850
|
server.registerTool('listTags', {
|
|
674
851
|
title: 'List Tags',
|
|
@@ -687,7 +864,7 @@ export async function createServer(config) {
|
|
|
687
864
|
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
688
865
|
}
|
|
689
866
|
try {
|
|
690
|
-
const tags = client.getTagList();
|
|
867
|
+
const tags = await client.getTagList();
|
|
691
868
|
return {
|
|
692
869
|
content: [{ type: 'text', text: JSON.stringify(tags, null, 2) }],
|
|
693
870
|
structuredContent: { tags, count: tags.length },
|
|
@@ -782,7 +959,7 @@ export async function createServer(config) {
|
|
|
782
959
|
description: 'Schedules a new maintenance window. During maintenance, affected monitors are suppressed and show MAINTENANCE status instead of DOWN.',
|
|
783
960
|
inputSchema: {
|
|
784
961
|
title: z.string().describe('Title of the maintenance window'),
|
|
785
|
-
description: z.string().
|
|
962
|
+
description: z.string().default('').describe('Description or reason for the maintenance'),
|
|
786
963
|
strategy: z.enum(['single', 'recurring-interval', 'recurring-weekday', 'recurring-day-of-month', 'manual'])
|
|
787
964
|
.describe('Scheduling strategy: single=one-time, recurring-interval=every N days, recurring-weekday=specific weekdays, recurring-day-of-month=specific dates, manual=manually activated'),
|
|
788
965
|
active: z.boolean().optional().describe('Whether the window is active (default: true)'),
|
|
@@ -843,6 +1020,123 @@ export async function createServer(config) {
|
|
|
843
1020
|
throw new McpError(ErrorCode.InternalError, `Failed to list status pages: ${errorMessage}`);
|
|
844
1021
|
}
|
|
845
1022
|
});
|
|
1023
|
+
server.registerTool('getStatusPage', {
|
|
1024
|
+
title: 'Get Status Page',
|
|
1025
|
+
description: 'Returns the full configuration of a status page by slug, including the ordered list of groups, the monitors inside each group, and active incidents. Only works for published status pages (fetches the public `/api/status-page/{slug}` endpoint).',
|
|
1026
|
+
inputSchema: {
|
|
1027
|
+
slug: z.string().describe('The status page slug (the URL-safe identifier)'),
|
|
1028
|
+
},
|
|
1029
|
+
outputSchema: {
|
|
1030
|
+
ok: z.boolean(),
|
|
1031
|
+
config: StatusPageSchema.optional(),
|
|
1032
|
+
publicGroupList: z.array(z.record(z.string(), z.unknown())).optional().describe('Ordered groups with their monitorList'),
|
|
1033
|
+
incidents: z.array(z.record(z.string(), z.unknown())).optional().describe('Active incidents on the status page'),
|
|
1034
|
+
msg: z.string().optional(),
|
|
1035
|
+
},
|
|
1036
|
+
}, async ({ slug }) => {
|
|
1037
|
+
if (!isAuthenticated) {
|
|
1038
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
1039
|
+
}
|
|
1040
|
+
try {
|
|
1041
|
+
const response = await client.getStatusPage(slug);
|
|
1042
|
+
return {
|
|
1043
|
+
content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
|
|
1044
|
+
structuredContent: {
|
|
1045
|
+
ok: response.ok,
|
|
1046
|
+
config: response.config,
|
|
1047
|
+
publicGroupList: response.publicGroupList,
|
|
1048
|
+
incidents: response.incidents,
|
|
1049
|
+
msg: response.msg,
|
|
1050
|
+
},
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
catch (error) {
|
|
1054
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1055
|
+
throw new McpError(ErrorCode.InternalError, `Failed to get status page: ${errorMessage}`);
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
server.registerTool('createStatusPage', {
|
|
1059
|
+
title: 'Create Status Page',
|
|
1060
|
+
description: 'Creates a new (empty) status page with the given title and slug. After creating, call updateStatusPage to set the description, theme, groups, and monitors. Slug must be lowercase letters, digits, and dashes only.',
|
|
1061
|
+
inputSchema: {
|
|
1062
|
+
title: z.string().describe('Display title of the status page'),
|
|
1063
|
+
slug: z.string().regex(/^[a-z0-9-]+$/).describe('URL slug (lowercase letters, digits, and dashes only)'),
|
|
1064
|
+
},
|
|
1065
|
+
outputSchema: {
|
|
1066
|
+
ok: z.boolean(),
|
|
1067
|
+
msg: z.string().optional(),
|
|
1068
|
+
},
|
|
1069
|
+
}, async ({ title, slug }) => {
|
|
1070
|
+
if (!isAuthenticated) {
|
|
1071
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
1072
|
+
}
|
|
1073
|
+
try {
|
|
1074
|
+
const response = await client.createStatusPage(title, slug);
|
|
1075
|
+
return {
|
|
1076
|
+
content: [{ type: 'text', text: response.msg || `Status page ${slug} created` }],
|
|
1077
|
+
structuredContent: { ok: response.ok, msg: response.msg },
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
catch (error) {
|
|
1081
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1082
|
+
throw new McpError(ErrorCode.InternalError, `Failed to create status page: ${errorMessage}`);
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
server.registerTool('updateStatusPage', {
|
|
1086
|
+
title: 'Update Status Page',
|
|
1087
|
+
description: 'Updates an existing status page. Pass the full config (title, description, theme, published, etc.) and the full publicGroupList — both are replaced wholesale. Each group has a name, weight, and monitorList of [{id}]. Use getStatusPage first to read current state before modifying.',
|
|
1088
|
+
inputSchema: {
|
|
1089
|
+
slug: z.string().describe('The status page slug (immutable identifier)'),
|
|
1090
|
+
config: z.record(z.string(), z.unknown()).describe('Full status page config (title, description, theme, published, showTags, showPoweredBy, domainNameList, customCSS, footerText, icon, etc.)'),
|
|
1091
|
+
publicGroupList: z.array(z.record(z.string(), z.unknown())).optional().describe('Ordered groups. Each: {name, weight, monitorList: [{id}]}. Defaults to empty list.'),
|
|
1092
|
+
imgDataUrl: z.string().optional().describe('Icon as data URL. Omit or pass empty string to keep existing.'),
|
|
1093
|
+
},
|
|
1094
|
+
outputSchema: {
|
|
1095
|
+
ok: z.boolean(),
|
|
1096
|
+
msg: z.string().optional(),
|
|
1097
|
+
},
|
|
1098
|
+
}, async ({ slug, config, publicGroupList, imgDataUrl }) => {
|
|
1099
|
+
if (!isAuthenticated) {
|
|
1100
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
1101
|
+
}
|
|
1102
|
+
try {
|
|
1103
|
+
const response = await client.updateStatusPage(slug, config, publicGroupList ?? [], imgDataUrl ?? '');
|
|
1104
|
+
return {
|
|
1105
|
+
content: [{ type: 'text', text: response.msg || `Status page ${slug} updated` }],
|
|
1106
|
+
structuredContent: { ok: response.ok, msg: response.msg },
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
catch (error) {
|
|
1110
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1111
|
+
throw new McpError(ErrorCode.InternalError, `Failed to update status page: ${errorMessage}`);
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
server.registerTool('deleteStatusPage', {
|
|
1115
|
+
title: 'Delete Status Page',
|
|
1116
|
+
description: 'Permanently deletes a status page by slug. The status page URL will no longer be accessible.',
|
|
1117
|
+
inputSchema: {
|
|
1118
|
+
slug: z.string().describe('The status page slug to delete'),
|
|
1119
|
+
},
|
|
1120
|
+
outputSchema: {
|
|
1121
|
+
ok: z.boolean(),
|
|
1122
|
+
msg: z.string().optional(),
|
|
1123
|
+
},
|
|
1124
|
+
}, async ({ slug }) => {
|
|
1125
|
+
if (!isAuthenticated) {
|
|
1126
|
+
throw new McpError(ErrorCode.InternalError, 'Not authenticated with Uptime Kuma');
|
|
1127
|
+
}
|
|
1128
|
+
try {
|
|
1129
|
+
const response = await client.deleteStatusPage(slug);
|
|
1130
|
+
return {
|
|
1131
|
+
content: [{ type: 'text', text: response.msg || `Status page ${slug} deleted` }],
|
|
1132
|
+
structuredContent: { ok: response.ok, msg: response.msg },
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
catch (error) {
|
|
1136
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1137
|
+
throw new McpError(ErrorCode.InternalError, `Failed to delete status page: ${errorMessage}`);
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
846
1140
|
// Clean up on server shutdown
|
|
847
1141
|
process.on('SIGINT', () => {
|
|
848
1142
|
client.disconnect();
|