@davidfuchs/mcp-uptime-kuma 0.5.3 → 0.6.2
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 +114 -3
- package/dist/get-jwt.d.ts +3 -0
- package/dist/get-jwt.d.ts.map +1 -0
- package/dist/get-jwt.js +82 -0
- package/dist/get-jwt.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +109 -18
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +93 -24
- package/dist/server.js.map +1 -1
- package/dist/types/config.d.ts +11 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/heartbeat.d.ts +65 -0
- package/dist/types/heartbeat.d.ts.map +1 -0
- package/dist/types/heartbeat.js +23 -0
- package/dist/types/heartbeat.js.map +1 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/monitor-auth.d.ts +43 -0
- package/dist/types/monitor-auth.d.ts.map +1 -0
- package/dist/types/monitor-auth.js +21 -0
- package/dist/types/monitor-auth.js.map +1 -0
- package/dist/types/monitor-base.d.ts +206 -0
- package/dist/types/monitor-base.d.ts.map +1 -0
- package/dist/types/monitor-base.js +81 -0
- package/dist/types/monitor-base.js.map +1 -0
- package/dist/types/monitor-conditions.d.ts +17 -0
- package/dist/types/monitor-conditions.d.ts.map +1 -0
- package/dist/types/monitor-conditions.js +13 -0
- package/dist/types/monitor-conditions.js.map +1 -0
- package/dist/types/monitor-types.d.ts +5528 -0
- package/dist/types/monitor-types.d.ts.map +1 -0
- package/dist/types/monitor-types.js +248 -0
- package/dist/types/monitor-types.js.map +1 -0
- package/dist/types/responses.d.ts +94 -0
- package/dist/types/responses.d.ts.map +1 -0
- package/dist/types/responses.js +19 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/types/settings.d.ts +53 -0
- package/dist/types/settings.d.ts.map +1 -0
- package/dist/types/settings.js +18 -0
- package/dist/types/settings.js.map +1 -0
- package/dist/types/tags.d.ts +28 -0
- package/dist/types/tags.d.ts.map +1 -0
- package/dist/types/tags.js +12 -0
- package/dist/types/tags.js.map +1 -0
- package/dist/uptime-kuma-client.d.ts +14 -37
- package/dist/uptime-kuma-client.d.ts.map +1 -1
- package/dist/uptime-kuma-client.js +44 -55
- package/dist/uptime-kuma-client.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -3
- package/dist/types.d.ts +0 -351
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -90
- package/dist/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for [U
|
|
|
33
33
|
| `getHeartbeats` | Get status check history for a specific monitor |
|
|
34
34
|
| `getSettings` | Get Uptime Kuma server settings |
|
|
35
35
|
|
|
36
|
-
###
|
|
36
|
+
### Filtering
|
|
37
37
|
|
|
38
38
|
Both `getMonitorSummary` and `listMonitors` support powerful filtering options:
|
|
39
39
|
|
|
@@ -61,6 +61,8 @@ Both `getMonitorSummary` and `listMonitors` support powerful filtering options:
|
|
|
61
61
|
|
|
62
62
|
## Quick Start
|
|
63
63
|
|
|
64
|
+
### Using npx (stdio transport)
|
|
65
|
+
|
|
64
66
|
Most folks will want to configure mcp-uptime-kuma using stdio as follows.
|
|
65
67
|
|
|
66
68
|
```json
|
|
@@ -81,6 +83,77 @@ Most folks will want to configure mcp-uptime-kuma using stdio as follows.
|
|
|
81
83
|
|
|
82
84
|
If authentication is disabled on your Uptime Kuma instance, you can remove the username/password environment variables. See the [Usage Instructions](#usage-instructions) section for more details on authentication methods.
|
|
83
85
|
|
|
86
|
+
### Using Docker (streamable HTTP transport)
|
|
87
|
+
|
|
88
|
+
For remote access or containerized deployments, you can run mcp-uptime-kuma as a Docker container using the streamable HTTP transport.
|
|
89
|
+
|
|
90
|
+
**Option 1: Using Docker Compose**
|
|
91
|
+
|
|
92
|
+
Create a `docker-compose.yml` file:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
services:
|
|
96
|
+
mcp-uptime-kuma:
|
|
97
|
+
image: davidfuchs/mcp-uptime-kuma:latest
|
|
98
|
+
container_name: mcp-uptime-kuma
|
|
99
|
+
environment:
|
|
100
|
+
- UPTIME_KUMA_URL=http://your-uptime-kuma-instance:3001
|
|
101
|
+
- UPTIME_KUMA_USERNAME=your_username # Optional
|
|
102
|
+
- UPTIME_KUMA_PASSWORD=your_password # Optional
|
|
103
|
+
# OR use JWT token authentication:
|
|
104
|
+
# - UPTIME_KUMA_JWT_TOKEN=your_jwt_token
|
|
105
|
+
- PORT=3000 # Optional, defaults to 3000
|
|
106
|
+
ports:
|
|
107
|
+
- "3000:3000"
|
|
108
|
+
command: ["-t", "streamable-http"]
|
|
109
|
+
restart: unless-stopped
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then run:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
docker compose up -d
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Option 2: Using Docker Run**
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
docker run -d \
|
|
122
|
+
--name mcp-uptime-kuma \
|
|
123
|
+
-p 3000:3000 \
|
|
124
|
+
-e UPTIME_KUMA_URL=http://your-uptime-kuma-instance:3001 \
|
|
125
|
+
-e UPTIME_KUMA_USERNAME=your_username \
|
|
126
|
+
-e UPTIME_KUMA_PASSWORD=your_password \
|
|
127
|
+
davidfuchs/mcp-uptime-kuma:latest \
|
|
128
|
+
-t streamable-http
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The MCP endpoint will be available at `http://mcp-uptime-kuma:3000/mcp` on your Docker host.
|
|
132
|
+
|
|
133
|
+
**Configuring Your MCP Client**
|
|
134
|
+
|
|
135
|
+
After starting the Docker container, configure your MCP client to connect to it:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"mcpServers": {
|
|
140
|
+
"uptime-kuma": {
|
|
141
|
+
"url": "http://mcp-uptime-kuma:3000/mcp"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Or for LibreChat (librechat.yaml):
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
mcpServers:
|
|
151
|
+
uptime-kuma:
|
|
152
|
+
type: streamable-http
|
|
153
|
+
url: "http://mcp-uptime-kuma:3000/mcp"
|
|
154
|
+
serverInstructions: true
|
|
155
|
+
```
|
|
156
|
+
|
|
84
157
|
## Usage Instructions
|
|
85
158
|
|
|
86
159
|
### Prerequisites
|
|
@@ -120,12 +193,50 @@ Token-based authentication using a JWT token from Uptime Kuma. This method uses
|
|
|
120
193
|
- `UPTIME_KUMA_URL`: The URL of your Uptime Kuma instance
|
|
121
194
|
- `UPTIME_KUMA_JWT_TOKEN`: Your JWT token (see instructions below for how to obtain it)
|
|
122
195
|
|
|
123
|
-
##### How to
|
|
196
|
+
##### How to Obtain Your JWT Token:
|
|
197
|
+
|
|
198
|
+
**Method 1: Using the JWT Helper Utility (Recommended)**
|
|
199
|
+
|
|
200
|
+
This package includes a handy command-line utility to retrieve a JWT token directly from your Uptime Kuma instance:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
npx @davidfuchs/mcp-uptime-kuma-get-jwt <url> <username> <password> [2fa-token]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Examples:**
|
|
207
|
+
```bash
|
|
208
|
+
# Without 2FA
|
|
209
|
+
npx @davidfuchs/mcp-uptime-kuma-get-jwt http://localhost:3001 admin mypassword
|
|
210
|
+
|
|
211
|
+
# With 2FA
|
|
212
|
+
npx @davidfuchs/mcp-uptime-kuma-get-jwt http://localhost:3001 admin mypassword 123456
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Method 2: Using Docker**
|
|
216
|
+
|
|
217
|
+
You can also use the Docker image to retrieve the JWT token:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
docker run --rm davidfuchs/mcp-uptime-kuma:latest get-jwt <url> <username> <password> [2fa-token]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Examples:**
|
|
224
|
+
```bash
|
|
225
|
+
# Without 2FA
|
|
226
|
+
docker run --rm davidfuchs/mcp-uptime-kuma:latest get-jwt http://host.docker.internal:3001 admin mypassword
|
|
227
|
+
|
|
228
|
+
# With 2FA
|
|
229
|
+
docker run --rm davidfuchs/mcp-uptime-kuma:latest get-jwt http://host.docker.internal:3001 admin mypassword 123456
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
> **Note:** If Docker and your Uptime Kuma instance are on the same machine, use `http://host.docker.internal:3001` instead of `http://localhost:3001` to access the host machine from within the container.
|
|
233
|
+
|
|
234
|
+
**Method 3: Extract from Browser**
|
|
124
235
|
|
|
125
236
|
1. Log into your Uptime Kuma instance in your web browser
|
|
126
237
|
2. Open your browser's Developer Tools (F12 or right-click → Inspect)
|
|
127
238
|
3. Navigate to the **Storage** tab (Firefox) or **Application** tab (Chrome/Edge)
|
|
128
|
-
4. Under **Local Storage**
|
|
239
|
+
4. Under **Local Storage** find your Uptime Kuma domain
|
|
129
240
|
5. Look for a key named `token` - the value is your JWT token (it should start with 'ey...')
|
|
130
241
|
6. Copy the token value and use it as `UPTIME_KUMA_JWT_TOKEN`
|
|
131
242
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-jwt.d.ts","sourceRoot":"","sources":["../src/get-jwt.ts"],"names":[],"mappings":""}
|
package/dist/get-jwt.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { io, Socket } from 'socket.io-client';
|
|
3
|
+
function showHelp() {
|
|
4
|
+
console.log(`Usage: mcp-uptime-kuma-get-jwt <url> <username> <password> [2fa-token]
|
|
5
|
+
|
|
6
|
+
Arguments:
|
|
7
|
+
url Uptime Kuma server URL (e.g., http://localhost:3001)
|
|
8
|
+
username Username for authentication
|
|
9
|
+
password Password for authentication
|
|
10
|
+
2fa-token Optional 2FA token if required
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
mcp-uptime-kuma-get-jwt http://localhost:3001 admin mypassword
|
|
14
|
+
mcp-uptime-kuma-get-jwt http://localhost:3001 admin mypassword 123456
|
|
15
|
+
|
|
16
|
+
The JWT token will be printed to stdout on success.
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
async function getJwtToken(url, username, password, token) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const socket = io(url, {
|
|
22
|
+
reconnection: false,
|
|
23
|
+
});
|
|
24
|
+
socket.on('connect', () => {
|
|
25
|
+
const loginData = {
|
|
26
|
+
username,
|
|
27
|
+
password,
|
|
28
|
+
};
|
|
29
|
+
if (token) {
|
|
30
|
+
loginData.token = token;
|
|
31
|
+
}
|
|
32
|
+
socket.emit('login', loginData, (response) => {
|
|
33
|
+
if (response.ok && response.token) {
|
|
34
|
+
socket.disconnect();
|
|
35
|
+
resolve(response.token);
|
|
36
|
+
}
|
|
37
|
+
else if (response.ok && response.tokenRequired) {
|
|
38
|
+
socket.disconnect();
|
|
39
|
+
reject(new Error('2FA token is required but was not provided'));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
socket.disconnect();
|
|
43
|
+
reject(new Error(response.msg || 'Login failed'));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
socket.on('connect_error', (error) => {
|
|
48
|
+
socket.disconnect();
|
|
49
|
+
reject(new Error(`Connection failed: ${error.message}`));
|
|
50
|
+
});
|
|
51
|
+
socket.on('error', (error) => {
|
|
52
|
+
socket.disconnect();
|
|
53
|
+
reject(new Error(`Socket error: ${error.message}`));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function main() {
|
|
58
|
+
const args = process.argv.slice(2);
|
|
59
|
+
// Check for help flag
|
|
60
|
+
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
|
61
|
+
showHelp();
|
|
62
|
+
process.exit(args.length === 0 ? 1 : 0);
|
|
63
|
+
}
|
|
64
|
+
// Validate arguments
|
|
65
|
+
if (args.length < 3) {
|
|
66
|
+
console.error('Error: Missing required arguments\n');
|
|
67
|
+
showHelp();
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
const [url, username, password, token] = args;
|
|
71
|
+
try {
|
|
72
|
+
const jwtToken = await getJwtToken(url, username, password, token);
|
|
73
|
+
console.log(jwtToken);
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
main();
|
|
82
|
+
//# sourceMappingURL=get-jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-jwt.js","sourceRoot":"","sources":["../src/get-jwt.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA0RA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
// Load .env file only if not in test mode (when MCP_TEST_MODE is set)
|
|
3
|
+
// This allows tests to pass environment variables directly without .env file interference
|
|
4
|
+
if (!process.env.MCP_TEST_MODE) {
|
|
5
|
+
await import('dotenv/config');
|
|
6
|
+
}
|
|
3
7
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
8
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
9
|
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
5
10
|
import express from 'express';
|
|
11
|
+
import cors from 'cors';
|
|
6
12
|
import rateLimit from 'express-rate-limit';
|
|
13
|
+
import { randomUUID } from 'node:crypto';
|
|
14
|
+
import NodeCache from 'node-cache';
|
|
7
15
|
import { createServer } from './server.js';
|
|
8
16
|
/**
|
|
9
17
|
* Main entry point for @davidfuchs/mcp-uptime-kuma
|
|
@@ -74,6 +82,13 @@ async function runStdio(config) {
|
|
|
74
82
|
async function runHttp(config) {
|
|
75
83
|
const app = express();
|
|
76
84
|
app.use(express.json());
|
|
85
|
+
// CORS configuration for MCP client compatibility
|
|
86
|
+
app.use(cors({
|
|
87
|
+
origin: process.env.ALLOWED_ORIGIN || '*', // Configure via environment variable
|
|
88
|
+
exposedHeaders: ['mcp-session-id'],
|
|
89
|
+
allowedHeaders: ['Content-Type', 'mcp-session-id', 'mcp-protocol-version'],
|
|
90
|
+
// MUST include 'mcp-protocol-version' otherwise preflight check will error out
|
|
91
|
+
}));
|
|
77
92
|
// Rate limiting configuration
|
|
78
93
|
const limiter = rateLimit({
|
|
79
94
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
@@ -86,26 +101,58 @@ async function runHttp(config) {
|
|
|
86
101
|
app.use(limiter);
|
|
87
102
|
// Create the MCP server once (reused across requests)
|
|
88
103
|
const { server, authenticateClient } = await createServer(config);
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
// Store transports by session ID
|
|
105
|
+
const transportCache = new NodeCache({
|
|
106
|
+
stdTTL: 3600, // 1-hour expiry
|
|
107
|
+
checkperiod: 0,
|
|
108
|
+
useClones: false, // MUST include this to store references instead of cloning objects
|
|
109
|
+
// otherwise the StreamableHTTPServerTransport object will be broken!
|
|
110
|
+
});
|
|
111
|
+
// Track authentication status at server level (not per-session)
|
|
112
|
+
let isServerAuthenticated = false;
|
|
113
|
+
// Handle POST requests for client-to-server communication
|
|
93
114
|
app.post('/mcp', async (req, res) => {
|
|
94
115
|
try {
|
|
95
|
-
//
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
// Check for existing session ID
|
|
117
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
118
|
+
let transport;
|
|
119
|
+
// Check if the session ID exists in the transport cache; if so reuse the transport
|
|
120
|
+
if (sessionId) {
|
|
121
|
+
transport = transportCache.get(sessionId);
|
|
122
|
+
}
|
|
123
|
+
if (!transport && isInitializeRequest(req.body)) {
|
|
124
|
+
// Create a new transport only for new initialization request
|
|
125
|
+
let capturedSessionId;
|
|
126
|
+
transport = new StreamableHTTPServerTransport({
|
|
127
|
+
sessionIdGenerator: () => randomUUID(),
|
|
128
|
+
onsessioninitialized: (sessionId) => {
|
|
129
|
+
capturedSessionId = sessionId;
|
|
130
|
+
// Store the transport by session ID
|
|
131
|
+
transportCache.set(sessionId, transport);
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
// Clean up transport when closed
|
|
135
|
+
transport.onclose = () => {
|
|
136
|
+
if (capturedSessionId) {
|
|
137
|
+
transportCache.del(capturedSessionId);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
// Connect the transport to the MCP server
|
|
141
|
+
await server.connect(transport);
|
|
108
142
|
}
|
|
143
|
+
else if (!transport) {
|
|
144
|
+
// Invalid request - no valid session ID and not an initialization request
|
|
145
|
+
res.status(400).json({
|
|
146
|
+
jsonrpc: '2.0',
|
|
147
|
+
error: {
|
|
148
|
+
code: -32000,
|
|
149
|
+
message: 'Bad Request: No valid session ID provided',
|
|
150
|
+
},
|
|
151
|
+
id: null,
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Handle the request (authentication happens later in GET handler when SSE stream is ready)
|
|
109
156
|
await transport.handleRequest(req, res, req.body);
|
|
110
157
|
}
|
|
111
158
|
catch (error) {
|
|
@@ -122,6 +169,50 @@ async function runHttp(config) {
|
|
|
122
169
|
}
|
|
123
170
|
}
|
|
124
171
|
});
|
|
172
|
+
// Reusable handler for GET and DELETE requests
|
|
173
|
+
const handleSessionRequest = async (req, res) => {
|
|
174
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
175
|
+
let transport;
|
|
176
|
+
// Check if the session ID exists in the transport cache; if so reuse the transport
|
|
177
|
+
if (sessionId) {
|
|
178
|
+
transport = transportCache.get(sessionId);
|
|
179
|
+
}
|
|
180
|
+
if (!sessionId || !transport) {
|
|
181
|
+
res.status(400).send('Invalid or missing session ID');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// Handle the request
|
|
185
|
+
await transport.handleRequest(req, res);
|
|
186
|
+
};
|
|
187
|
+
// Handle GET requests for server-to-client notifications via SSE
|
|
188
|
+
app.get('/mcp', async (req, res) => {
|
|
189
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
190
|
+
let transport;
|
|
191
|
+
// Check if the session ID exists in the transport cache; if so reuse the transport
|
|
192
|
+
if (sessionId) {
|
|
193
|
+
transport = transportCache.get(sessionId);
|
|
194
|
+
}
|
|
195
|
+
if (!sessionId || !transport) {
|
|
196
|
+
res.status(400).send('Invalid or missing session ID');
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// Authenticate once for the entire server after the first SSE stream is established
|
|
200
|
+
// This ensures the client can receive authentication log messages
|
|
201
|
+
if (!isServerAuthenticated) {
|
|
202
|
+
isServerAuthenticated = true; // Set immediately to prevent race conditions
|
|
203
|
+
try {
|
|
204
|
+
await authenticateClient();
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
console.error('Authentication error:', error);
|
|
208
|
+
// Continue anyway - the error will be logged via sendLoggingMessage
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Handle the request
|
|
212
|
+
await transport.handleRequest(req, res);
|
|
213
|
+
});
|
|
214
|
+
// Handle DELETE requests for session termination
|
|
215
|
+
app.delete('/mcp', handleSessionRequest);
|
|
125
216
|
// Health check endpoint
|
|
126
217
|
app.get('/health', (req, res) => {
|
|
127
218
|
res.json({ status: 'ok', server: 'mcp-uptime-kuma' });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { UptimeKumaClient } from './uptime-kuma-client.js';
|
|
3
|
-
import type { UptimeKumaConfig } from './types.js';
|
|
3
|
+
import type { UptimeKumaConfig } from './types/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Creates and configures the MCP server with tools, resources, and prompts
|
|
6
6
|
* Note: Authentication must be done separately after connecting the transport
|
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,
|
|
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,CAygB9J"}
|