@chkp/documentation-mcp 0.1.4 → 0.1.7
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/CHANGELOG.md +12 -0
- package/dist/index.js +1515 -74
- package/dist/package.json +42 -0
- package/package.json +8 -6
- package/dist/documentation-api-manager.d.ts +0 -30
- package/dist/documentation-api-manager.d.ts.map +0 -1
- package/dist/documentation-api-manager.js +0 -75
- package/dist/documentation-api-manager.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/settings.d.ts +0 -9
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js +0 -15
- package/dist/settings.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,1483 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// ../mcp-utils/dist/ui-dialog.js
|
|
13
|
+
var ui_dialog_exports = {};
|
|
14
|
+
__export(ui_dialog_exports, {
|
|
15
|
+
showDialog: () => showDialog,
|
|
16
|
+
showLoginDialog: () => showLoginDialog
|
|
17
|
+
});
|
|
18
|
+
import { createServer } from "http";
|
|
19
|
+
import { URL } from "url";
|
|
20
|
+
import { exec } from "child_process";
|
|
21
|
+
import { promisify } from "util";
|
|
22
|
+
import * as fs2 from "fs";
|
|
23
|
+
import * as path from "path";
|
|
24
|
+
async function showDialog(config) {
|
|
25
|
+
const dialog = new UIDialog();
|
|
26
|
+
return dialog.showDialog(config);
|
|
27
|
+
}
|
|
28
|
+
async function showLoginDialog(title, message) {
|
|
29
|
+
return showDialog({
|
|
30
|
+
title: title || "Login Information",
|
|
31
|
+
message,
|
|
32
|
+
fields: [
|
|
33
|
+
{
|
|
34
|
+
name: "address",
|
|
35
|
+
label: "Address",
|
|
36
|
+
type: "text",
|
|
37
|
+
placeholder: "Enter server address...",
|
|
38
|
+
required: true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "user",
|
|
42
|
+
label: "Username",
|
|
43
|
+
type: "text",
|
|
44
|
+
placeholder: "Enter username...",
|
|
45
|
+
required: true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "password",
|
|
49
|
+
label: "Password",
|
|
50
|
+
type: "password",
|
|
51
|
+
placeholder: "Enter password...",
|
|
52
|
+
required: true
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
var execAsync, UIDialog;
|
|
58
|
+
var init_ui_dialog = __esm({
|
|
59
|
+
"../mcp-utils/dist/ui-dialog.js"() {
|
|
60
|
+
"use strict";
|
|
61
|
+
execAsync = promisify(exec);
|
|
62
|
+
UIDialog = class {
|
|
63
|
+
constructor() {
|
|
64
|
+
this.server = null;
|
|
65
|
+
this.port = 0;
|
|
66
|
+
this.resolve = null;
|
|
67
|
+
}
|
|
68
|
+
async showDialog(config) {
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
this.resolve = resolve;
|
|
71
|
+
this.server = createServer((req, res) => {
|
|
72
|
+
this.handleRequest(req, res, config);
|
|
73
|
+
});
|
|
74
|
+
this.server.listen(0, "localhost", () => {
|
|
75
|
+
const address = this.server.address();
|
|
76
|
+
if (address && typeof address === "object") {
|
|
77
|
+
this.port = address.port;
|
|
78
|
+
this.openBrowser(`http://localhost:${this.port}`);
|
|
79
|
+
} else {
|
|
80
|
+
reject(new Error("Failed to start server"));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
this.cleanup();
|
|
85
|
+
resolve({ cancelled: true, data: {} });
|
|
86
|
+
}, 5 * 60 * 1e3);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
handleRequest(req, res, config) {
|
|
90
|
+
const url = new URL(req.url, `http://localhost:${this.port}`);
|
|
91
|
+
if (req.method === "GET" && url.pathname === "/") {
|
|
92
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
93
|
+
res.end(this.generateHTML(config));
|
|
94
|
+
} else if (req.method === "POST" && url.pathname === "/submit") {
|
|
95
|
+
let body = "";
|
|
96
|
+
req.on("data", (chunk) => {
|
|
97
|
+
body += chunk.toString();
|
|
98
|
+
});
|
|
99
|
+
req.on("end", () => {
|
|
100
|
+
const formData = new URLSearchParams(body);
|
|
101
|
+
const result = {};
|
|
102
|
+
for (const field of config.fields) {
|
|
103
|
+
result[field.name] = formData.get(field.name) || "";
|
|
104
|
+
}
|
|
105
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
106
|
+
res.end(`
|
|
107
|
+
<html>
|
|
108
|
+
<head><title>Success</title></head>
|
|
109
|
+
<body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
|
|
110
|
+
<h2>Form submitted successfully!</h2>
|
|
111
|
+
<p>You can close this window now.</p>
|
|
112
|
+
<script>setTimeout(() => window.close(), 2000);</script>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
|
+
`);
|
|
116
|
+
this.cleanup();
|
|
117
|
+
if (this.resolve) {
|
|
118
|
+
this.resolve({ cancelled: false, data: result });
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
} else if (req.method === "POST" && url.pathname === "/cancel") {
|
|
122
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
123
|
+
res.end(`
|
|
124
|
+
<html>
|
|
125
|
+
<head><title>Cancelled</title></head>
|
|
126
|
+
<body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
|
|
127
|
+
<h2>Form cancelled</h2>
|
|
128
|
+
<p>You can close this window now.</p>
|
|
129
|
+
<script>setTimeout(() => window.close(), 2000);</script>
|
|
130
|
+
</body>
|
|
131
|
+
</html>
|
|
132
|
+
`);
|
|
133
|
+
this.cleanup();
|
|
134
|
+
if (this.resolve) {
|
|
135
|
+
this.resolve({ cancelled: true, data: {} });
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
res.writeHead(404);
|
|
139
|
+
res.end("Not found");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
generateHTML(config) {
|
|
143
|
+
const fieldsHTML = config.fields.map((field) => {
|
|
144
|
+
let fieldHTML = "";
|
|
145
|
+
const fieldType = field.type || "text";
|
|
146
|
+
switch (fieldType) {
|
|
147
|
+
case "textarea":
|
|
148
|
+
fieldHTML = `
|
|
149
|
+
<textarea
|
|
150
|
+
name="${field.name}"
|
|
151
|
+
placeholder="${field.placeholder || ""}"
|
|
152
|
+
${field.required ? "required" : ""}
|
|
153
|
+
style="width: 100%; box-sizing: border-box; min-height: 80px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: Arial, sans-serif; font-size: 14px; resize: vertical;"
|
|
154
|
+
>${field.defaultValue || ""}</textarea>
|
|
155
|
+
`;
|
|
156
|
+
break;
|
|
157
|
+
case "select":
|
|
158
|
+
fieldHTML = `
|
|
159
|
+
<select
|
|
160
|
+
name="${field.name}"
|
|
161
|
+
${field.required ? "required" : ""}
|
|
162
|
+
style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: Arial, sans-serif; font-size: 14px;"
|
|
163
|
+
>
|
|
164
|
+
<option value="">Select an option...</option>
|
|
165
|
+
${(field.options || []).map((option) => `<option value="${option}" ${option === field.defaultValue ? "selected" : ""}>${option}</option>`).join("")}
|
|
166
|
+
</select>
|
|
167
|
+
`;
|
|
168
|
+
break;
|
|
169
|
+
default:
|
|
170
|
+
fieldHTML = `
|
|
171
|
+
<input
|
|
172
|
+
type="${fieldType}"
|
|
173
|
+
name="${field.name}"
|
|
174
|
+
placeholder="${field.placeholder || ""}"
|
|
175
|
+
value="${field.defaultValue || ""}"
|
|
176
|
+
${field.required ? "required" : ""}
|
|
177
|
+
style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: Arial, sans-serif; font-size: 14px;"
|
|
178
|
+
/>
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
return `
|
|
182
|
+
<div style="margin-bottom: 15px;">
|
|
183
|
+
<label style="display: block; margin-bottom: 5px; font-weight: bold; color: #333;">
|
|
184
|
+
${field.label}${field.required ? " *" : ""}
|
|
185
|
+
</label>
|
|
186
|
+
${fieldHTML}
|
|
187
|
+
</div>
|
|
188
|
+
`;
|
|
189
|
+
}).join("");
|
|
190
|
+
return `
|
|
191
|
+
<!DOCTYPE html>
|
|
192
|
+
<html>
|
|
193
|
+
<head>
|
|
194
|
+
<title>${config.title}</title>
|
|
195
|
+
<meta charset="UTF-8">
|
|
196
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
197
|
+
<style>
|
|
198
|
+
* {
|
|
199
|
+
box-sizing: border-box;
|
|
200
|
+
}
|
|
201
|
+
body {
|
|
202
|
+
font-family: Arial, sans-serif;
|
|
203
|
+
max-width: 500px;
|
|
204
|
+
margin: 50px auto;
|
|
205
|
+
padding: 20px;
|
|
206
|
+
background-color: #f5f5f5;
|
|
207
|
+
line-height: 1.4;
|
|
208
|
+
}
|
|
209
|
+
.dialog {
|
|
210
|
+
background: white;
|
|
211
|
+
padding: 30px;
|
|
212
|
+
border-radius: 8px;
|
|
213
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
214
|
+
}
|
|
215
|
+
.buttons {
|
|
216
|
+
margin-top: 20px;
|
|
217
|
+
text-align: right;
|
|
218
|
+
}
|
|
219
|
+
button {
|
|
220
|
+
padding: 10px 20px;
|
|
221
|
+
margin-left: 10px;
|
|
222
|
+
border: none;
|
|
223
|
+
border-radius: 4px;
|
|
224
|
+
cursor: pointer;
|
|
225
|
+
font-size: 14px;
|
|
226
|
+
font-family: Arial, sans-serif;
|
|
227
|
+
}
|
|
228
|
+
.submit-btn {
|
|
229
|
+
background-color: #007cba;
|
|
230
|
+
color: white;
|
|
231
|
+
}
|
|
232
|
+
.submit-btn:hover {
|
|
233
|
+
background-color: #005a87;
|
|
234
|
+
}
|
|
235
|
+
.cancel-btn {
|
|
236
|
+
background-color: #6c757d;
|
|
237
|
+
color: white;
|
|
238
|
+
}
|
|
239
|
+
.cancel-btn:hover {
|
|
240
|
+
background-color: #545b62;
|
|
241
|
+
}
|
|
242
|
+
input:focus, textarea:focus, select:focus {
|
|
243
|
+
outline: none;
|
|
244
|
+
border-color: #007cba;
|
|
245
|
+
box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2);
|
|
246
|
+
}
|
|
247
|
+
</style>
|
|
248
|
+
</head>
|
|
249
|
+
<body>
|
|
250
|
+
<div class="dialog">
|
|
251
|
+
<h2 style="margin-top: 0; color: #333;">${config.title}</h2>
|
|
252
|
+
${config.message ? `<p style="color: #666; margin-bottom: 20px;">${config.message}</p>` : ""}
|
|
253
|
+
|
|
254
|
+
<form id="dialogForm" method="POST" action="/submit">
|
|
255
|
+
${fieldsHTML}
|
|
256
|
+
|
|
257
|
+
<div class="buttons">
|
|
258
|
+
<button type="button" class="cancel-btn" onclick="cancel()">
|
|
259
|
+
${config.cancelButtonText || "Cancel"}
|
|
260
|
+
</button>
|
|
261
|
+
<button type="submit" class="submit-btn">
|
|
262
|
+
${config.submitButtonText || "Submit"}
|
|
263
|
+
</button>
|
|
264
|
+
</div>
|
|
265
|
+
</form>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<script>
|
|
269
|
+
function cancel() {
|
|
270
|
+
fetch('/cancel', { method: 'POST' })
|
|
271
|
+
.then(() => {
|
|
272
|
+
document.body.innerHTML = '<div style="text-align: center; padding: 50px; font-family: Arial, sans-serif;"><h2>Cancelled</h2><p>You can close this window now.</p></div>';
|
|
273
|
+
setTimeout(() => window.close(), 2000);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Auto-focus first input
|
|
278
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
279
|
+
const firstInput = document.querySelector('input, textarea, select');
|
|
280
|
+
if (firstInput) {
|
|
281
|
+
firstInput.focus();
|
|
282
|
+
// If it's a text input, place cursor at end
|
|
283
|
+
if (firstInput.type === 'text' || firstInput.tagName === 'TEXTAREA') {
|
|
284
|
+
const val = firstInput.value;
|
|
285
|
+
firstInput.value = '';
|
|
286
|
+
firstInput.value = val;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Prevent form submission if required fields are empty
|
|
292
|
+
document.getElementById('dialogForm').addEventListener('submit', function(e) {
|
|
293
|
+
const requiredFields = document.querySelectorAll('[required]');
|
|
294
|
+
for (let field of requiredFields) {
|
|
295
|
+
if (!field.value.trim()) {
|
|
296
|
+
e.preventDefault();
|
|
297
|
+
field.focus();
|
|
298
|
+
alert('Please fill in all required fields.');
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
</script>
|
|
304
|
+
</body>
|
|
305
|
+
</html>
|
|
306
|
+
`;
|
|
307
|
+
}
|
|
308
|
+
async openBrowser(urlString) {
|
|
309
|
+
try {
|
|
310
|
+
let url;
|
|
311
|
+
try {
|
|
312
|
+
url = new URL(urlString);
|
|
313
|
+
if (!["http:", "https:"].includes(url.protocol) || url.hostname !== "localhost" && url.hostname !== "127.0.0.1") {
|
|
314
|
+
throw new Error("Invalid URL protocol or non-localhost hostname");
|
|
315
|
+
}
|
|
316
|
+
} catch (e) {
|
|
317
|
+
console.error("Invalid URL:", e);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
switch (process.platform) {
|
|
321
|
+
case "darwin": {
|
|
322
|
+
const scriptPath = path.join(process.env.TMPDIR || "/tmp", `open-browser-${Date.now()}.applescript`);
|
|
323
|
+
const script = `open location "${urlString}"`;
|
|
324
|
+
fs2.writeFileSync(scriptPath, script);
|
|
325
|
+
await execAsync(`osascript "${scriptPath}"`);
|
|
326
|
+
try {
|
|
327
|
+
fs2.unlinkSync(scriptPath);
|
|
328
|
+
} catch (e) {
|
|
329
|
+
}
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
case "win32": {
|
|
333
|
+
await execAsync(`rundll32 url.dll,FileProtocolHandler "${urlString}"`);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
default: {
|
|
337
|
+
const browsers = ["xdg-open", "google-chrome", "firefox", "chromium-browser"];
|
|
338
|
+
for (const browser of browsers) {
|
|
339
|
+
try {
|
|
340
|
+
await execAsync(`which ${browser}`);
|
|
341
|
+
await execAsync(`${browser} "${urlString}"`);
|
|
342
|
+
break;
|
|
343
|
+
} catch {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error("Failed to open browser:", error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
cleanup() {
|
|
354
|
+
if (this.server) {
|
|
355
|
+
this.server.close();
|
|
356
|
+
this.server = null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// src/index.ts
|
|
364
|
+
import { z } from "zod";
|
|
365
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
366
|
+
|
|
367
|
+
// ../mcp-utils/dist/launcher.js
|
|
368
|
+
import { Command } from "commander";
|
|
369
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
370
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
371
|
+
import fs from "fs";
|
|
372
|
+
import http from "http";
|
|
373
|
+
import { randomUUID } from "crypto";
|
|
374
|
+
async function launchMCPServer(configPath, serverModule2) {
|
|
375
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
376
|
+
const program = new Command();
|
|
377
|
+
if (config.description) {
|
|
378
|
+
program.description(config.description);
|
|
379
|
+
}
|
|
380
|
+
config.options.forEach((option) => {
|
|
381
|
+
const envValue = option.env ? process.env[option.env] : void 0;
|
|
382
|
+
const defaultValue = option.default || envValue;
|
|
383
|
+
if (option.type === "boolean") {
|
|
384
|
+
const boolDefault = envValue === "true" || option.default === "true";
|
|
385
|
+
program.option(option.flag, option.description, boolDefault);
|
|
386
|
+
} else {
|
|
387
|
+
program.option(option.flag, option.description, defaultValue);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
const transportTypeDefault = process.env.MCP_TRANSPORT_TYPE || "stdio";
|
|
391
|
+
program.option("--transport <type>", "Transport type (stdio or http)", transportTypeDefault);
|
|
392
|
+
const transportPortDefault = process.env.MCP_TRANSPORT_PORT || "3000";
|
|
393
|
+
program.option("--transport-port <number>", "Port for network transports (e.g., HTTP)", transportPortDefault);
|
|
394
|
+
const debugDefault = process.env.DEBUG === "true" || false;
|
|
395
|
+
program.option("--debug", "Enable debug mode", debugDefault);
|
|
396
|
+
program.parse(process.argv);
|
|
397
|
+
const options = program.opts();
|
|
398
|
+
if (!serverModule2.settingsManager) {
|
|
399
|
+
throw new Error("ServerModule must have a settingsManager. Create it with createServerModule.");
|
|
400
|
+
}
|
|
401
|
+
const settings = serverModule2.settingsManager.createFromArgs(options);
|
|
402
|
+
const transportType = (options.transport || process.env.MCP_TRANSPORT_TYPE || "stdio").toLowerCase() === "http" ? "http" : "stdio";
|
|
403
|
+
const transportPort = options.transportPort ? parseInt(options.transportPort, 10) : process.env.MCP_TRANSPORT_PORT ? parseInt(process.env.MCP_TRANSPORT_PORT, 10) : 3e3;
|
|
404
|
+
if (transportType === "http") {
|
|
405
|
+
await launchHTTPServer(config, serverModule2, transportPort);
|
|
406
|
+
} else {
|
|
407
|
+
const transport = new StdioServerTransport();
|
|
408
|
+
const defaultSessionId = "default";
|
|
409
|
+
const sessionMetadata = {
|
|
410
|
+
type: "stdio",
|
|
411
|
+
startedAt: /* @__PURE__ */ new Date()
|
|
412
|
+
};
|
|
413
|
+
serverModule2.sessionManager.createSession(defaultSessionId, sessionMetadata);
|
|
414
|
+
transport.extraContext = () => {
|
|
415
|
+
return {
|
|
416
|
+
sessionId: defaultSessionId,
|
|
417
|
+
transport
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
await serverModule2.server.connect(transport);
|
|
421
|
+
console.error(`${config.name} running on stdio transport. Version: ${serverModule2.pkg.version}`);
|
|
422
|
+
console.error(`Transport type: stdio`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
async function launchHTTPServer(config, serverModule2, port) {
|
|
426
|
+
const transports = {};
|
|
427
|
+
const server2 = http.createServer(async (req, res) => {
|
|
428
|
+
if (req.url === "/" || req.url === "/mcp") {
|
|
429
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
430
|
+
if (req.method === "POST") {
|
|
431
|
+
const chunks = [];
|
|
432
|
+
try {
|
|
433
|
+
for await (const chunk of req) {
|
|
434
|
+
chunks.push(Buffer.from(chunk));
|
|
435
|
+
}
|
|
436
|
+
const bodyBuffer = Buffer.concat(chunks);
|
|
437
|
+
let body;
|
|
438
|
+
try {
|
|
439
|
+
const bodyText = bodyBuffer.toString("utf8");
|
|
440
|
+
if (bodyText) {
|
|
441
|
+
body = JSON.parse(bodyText);
|
|
442
|
+
}
|
|
443
|
+
} catch (err) {
|
|
444
|
+
console.error("Error parsing request body:", err);
|
|
445
|
+
}
|
|
446
|
+
let transport;
|
|
447
|
+
if (sessionId && transports[sessionId]) {
|
|
448
|
+
transport = transports[sessionId];
|
|
449
|
+
if (!serverModule2.settingsManager) {
|
|
450
|
+
throw new Error("ServerModule must have a settingsManager. Create it with createServerModule.");
|
|
451
|
+
}
|
|
452
|
+
serverModule2.settingsManager.createFromHeaders(headersToEnvVars(req.headers, config), sessionId);
|
|
453
|
+
} else if (!sessionId && body && body.method === "initialize") {
|
|
454
|
+
transport = new StreamableHTTPServerTransport({
|
|
455
|
+
sessionIdGenerator: () => randomUUID(),
|
|
456
|
+
onsessioninitialized: (sid) => {
|
|
457
|
+
transports[sid] = transport;
|
|
458
|
+
const headers = headersToEnvVars(req.headers, config);
|
|
459
|
+
const metadata = {
|
|
460
|
+
userAgent: req.headers["user-agent"],
|
|
461
|
+
origin: req.headers.origin || req.headers.referer,
|
|
462
|
+
remoteAddress: req.socket.remoteAddress,
|
|
463
|
+
initialPath: req.url
|
|
464
|
+
};
|
|
465
|
+
serverModule2.sessionManager.createSession(sid, metadata);
|
|
466
|
+
serverModule2.settingsManager.createFromHeaders(headers, sid);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
transport.onclose = () => {
|
|
470
|
+
const sid = transport.sessionId;
|
|
471
|
+
if (sid) {
|
|
472
|
+
delete transports[sid];
|
|
473
|
+
serverModule2.settingsManager.clearSession(sid);
|
|
474
|
+
serverModule2.apiManagerFactory.clearSession(sid);
|
|
475
|
+
serverModule2.sessionManager.removeSession(sid);
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
if (!serverModule2.settingsManager) {
|
|
479
|
+
throw new Error("ServerModule must have a settingsManager. Create it with createServerModule.");
|
|
480
|
+
}
|
|
481
|
+
transport.extraContext = (msg) => {
|
|
482
|
+
const sessionId2 = transport.sessionId;
|
|
483
|
+
return {
|
|
484
|
+
sessionId: sessionId2,
|
|
485
|
+
transport
|
|
486
|
+
};
|
|
487
|
+
};
|
|
488
|
+
await serverModule2.server.connect(transport).catch((error) => {
|
|
489
|
+
console.error("Error connecting to HTTP transport:", error);
|
|
490
|
+
});
|
|
491
|
+
} else {
|
|
492
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
493
|
+
res.end(JSON.stringify({
|
|
494
|
+
jsonrpc: "2.0",
|
|
495
|
+
error: {
|
|
496
|
+
code: -32e3,
|
|
497
|
+
message: "Bad Request: No valid session ID provided"
|
|
498
|
+
},
|
|
499
|
+
id: null
|
|
500
|
+
}));
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
await transport.handleRequest(req, res, body);
|
|
504
|
+
} catch (error) {
|
|
505
|
+
console.error("Error handling POST request:", error);
|
|
506
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
507
|
+
res.end(JSON.stringify({
|
|
508
|
+
jsonrpc: "2.0",
|
|
509
|
+
error: {
|
|
510
|
+
code: -32e3,
|
|
511
|
+
message: "Internal server error: " + error.message
|
|
512
|
+
},
|
|
513
|
+
id: null
|
|
514
|
+
}));
|
|
515
|
+
}
|
|
516
|
+
} else if (req.method === "GET") {
|
|
517
|
+
if (!sessionId || !transports[sessionId]) {
|
|
518
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
519
|
+
res.end("Invalid or missing session ID");
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const transport = transports[sessionId];
|
|
523
|
+
await transport.handleRequest(req, res);
|
|
524
|
+
} else if (req.method === "DELETE") {
|
|
525
|
+
if (!sessionId || !transports[sessionId]) {
|
|
526
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
527
|
+
res.end("Invalid or missing session ID");
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const transport = transports[sessionId];
|
|
531
|
+
await transport.handleRequest(req, res);
|
|
532
|
+
} else {
|
|
533
|
+
res.writeHead(405, { "Content-Type": "text/plain", "Allow": "GET, POST, DELETE" });
|
|
534
|
+
res.end("Method not allowed");
|
|
535
|
+
}
|
|
536
|
+
} else if (req.url === "/health" || req.url === "/status") {
|
|
537
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
538
|
+
const sessionCount = serverModule2.sessionManager.getSessionCount();
|
|
539
|
+
const sessions = serverModule2.sessionManager.getAllSessions().map((session) => ({
|
|
540
|
+
id: session.sessionId,
|
|
541
|
+
createdAt: session.createdAt,
|
|
542
|
+
lastActive: session.lastActive
|
|
543
|
+
// Don't include potentially sensitive metadata
|
|
544
|
+
}));
|
|
545
|
+
res.end(JSON.stringify({
|
|
546
|
+
status: "ok",
|
|
547
|
+
server: config.name,
|
|
548
|
+
version: serverModule2.pkg.version,
|
|
549
|
+
activeSessions: sessionCount,
|
|
550
|
+
sessions
|
|
551
|
+
}));
|
|
552
|
+
} else {
|
|
553
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
554
|
+
res.end("Not found");
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
server2.listen(port, () => {
|
|
558
|
+
console.error(`${config.name} running on HTTP transport at http://localhost:${port}. Version: ${serverModule2.pkg.version}`);
|
|
559
|
+
console.error(`Transport type: HTTP, Transport-port: ${port}`);
|
|
560
|
+
});
|
|
561
|
+
server2.on("error", (err) => {
|
|
562
|
+
console.error(`Server error: ${err.message}`);
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
function headersToEnvVars(headers, config) {
|
|
566
|
+
const result = {};
|
|
567
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
568
|
+
if (value !== void 0) {
|
|
569
|
+
const envName = name.toUpperCase().replace(/-/g, "_");
|
|
570
|
+
result[envName] = value;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (config && config.options) {
|
|
574
|
+
const headerToEnvMap = {};
|
|
575
|
+
for (const option of config.options) {
|
|
576
|
+
if (option.env) {
|
|
577
|
+
const flagName = option.flag.split(" ")[0].replace(/^--?/, "").replace(/-/g, "_");
|
|
578
|
+
headerToEnvMap[flagName.toLowerCase()] = option.env;
|
|
579
|
+
headerToEnvMap[option.env.toLowerCase()] = option.env;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
for (const [headerName, headerValue] of Object.entries(headers)) {
|
|
583
|
+
if (headerValue !== void 0) {
|
|
584
|
+
const normalizedHeaderName = headerName.toLowerCase().replace(/-/g, "_");
|
|
585
|
+
const envVarName = headerToEnvMap[normalizedHeaderName];
|
|
586
|
+
if (envVarName) {
|
|
587
|
+
result[envVarName] = headerValue;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// ../mcp-utils/dist/settings-manager.js
|
|
596
|
+
var SettingsManager = class _SettingsManager {
|
|
597
|
+
/**
|
|
598
|
+
* Creates a new SettingsManager
|
|
599
|
+
* @param settingsClass The Settings class to use for creating settings instances
|
|
600
|
+
*/
|
|
601
|
+
constructor(settingsClass) {
|
|
602
|
+
this.settingsMap = /* @__PURE__ */ new Map();
|
|
603
|
+
this.defaultSessionId = "default";
|
|
604
|
+
this.settingsClass = settingsClass;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Get settings for a specific session
|
|
608
|
+
* @param sessionId The session ID (defaults to 'default' for stdio mode)
|
|
609
|
+
* @returns Settings instance for the session
|
|
610
|
+
*/
|
|
611
|
+
getSettings(sessionId) {
|
|
612
|
+
const sid = sessionId || this.defaultSessionId;
|
|
613
|
+
if (!this.settingsMap.has(sid)) {
|
|
614
|
+
this.settingsMap.set(sid, new this.settingsClass());
|
|
615
|
+
}
|
|
616
|
+
return this.settingsMap.get(sid);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Set settings for a specific session
|
|
620
|
+
* @param settings The settings object to set
|
|
621
|
+
* @param sessionId The session ID (defaults to 'default' for stdio mode)
|
|
622
|
+
*/
|
|
623
|
+
setSettings(settings, sessionId) {
|
|
624
|
+
const sid = sessionId || this.defaultSessionId;
|
|
625
|
+
this.settingsMap.set(sid, settings);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Injects debug settings from source into settings object
|
|
629
|
+
* @param settings The settings object to update
|
|
630
|
+
* @param source Source object containing debug information
|
|
631
|
+
* @private
|
|
632
|
+
*/
|
|
633
|
+
injectDebug(settings, source) {
|
|
634
|
+
const debug = source?.debug ?? _SettingsManager.globalDebugState ?? process.env.DEBUG;
|
|
635
|
+
if (typeof debug !== "undefined") {
|
|
636
|
+
if (source?.debug !== void 0) {
|
|
637
|
+
_SettingsManager.globalDebugState = source.debug;
|
|
638
|
+
}
|
|
639
|
+
if (typeof settings.set === "function") {
|
|
640
|
+
settings.set("debug", debug);
|
|
641
|
+
} else {
|
|
642
|
+
settings.debug = debug;
|
|
643
|
+
}
|
|
644
|
+
if (debug) {
|
|
645
|
+
console.error("Debug enabled. Source object contents:");
|
|
646
|
+
console.error(JSON.stringify(source, null, 2));
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Print settings object for debugging by dynamically discovering its properties
|
|
652
|
+
* @param settings The settings object to print
|
|
653
|
+
* @private
|
|
654
|
+
*/
|
|
655
|
+
printSettingsDebug(settings) {
|
|
656
|
+
if (typeof settings.get === "function") {
|
|
657
|
+
console.error("Settings (using get method):");
|
|
658
|
+
if (settings.data && typeof settings.data === "object") {
|
|
659
|
+
Object.entries(settings.data).forEach(([key, value]) => {
|
|
660
|
+
console.error(` ${key}: ${JSON.stringify(value)}`);
|
|
661
|
+
});
|
|
662
|
+
} else {
|
|
663
|
+
console.error(" Unable to enumerate settings properties - no accessible data structure");
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
console.error("Settings (plain object):");
|
|
667
|
+
Object.entries(settings).forEach(([key, value]) => {
|
|
668
|
+
console.error(` ${key}: ${JSON.stringify(value)}`);
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Create settings from command-line arguments
|
|
674
|
+
* @param args Command line arguments
|
|
675
|
+
* @param sessionId Optional session ID
|
|
676
|
+
* @returns Settings instance
|
|
677
|
+
*/
|
|
678
|
+
createFromArgs(args, sessionId) {
|
|
679
|
+
const settings = this.settingsClass.fromArgs(args);
|
|
680
|
+
this.injectDebug(settings, args);
|
|
681
|
+
this.setSettings(settings, sessionId);
|
|
682
|
+
return settings;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Create settings from HTTP headers
|
|
686
|
+
* @param headers HTTP headers
|
|
687
|
+
* @param sessionId Optional session ID
|
|
688
|
+
* @returns Settings instance
|
|
689
|
+
*/
|
|
690
|
+
createFromHeaders(headers, sessionId) {
|
|
691
|
+
if (_SettingsManager.globalDebugState) {
|
|
692
|
+
console.error("=== createFromHeaders Debug Info ===");
|
|
693
|
+
console.error("Incoming headers:");
|
|
694
|
+
console.error(JSON.stringify(headers, null, 2));
|
|
695
|
+
}
|
|
696
|
+
const normalizedHeaders = {};
|
|
697
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
698
|
+
const normalizedKey = key.includes("_") ? key.replace(/_/g, "-") : key;
|
|
699
|
+
normalizedHeaders[normalizedKey] = value;
|
|
700
|
+
}
|
|
701
|
+
if (_SettingsManager.globalDebugState) {
|
|
702
|
+
console.error("Normalized headers:");
|
|
703
|
+
console.error(JSON.stringify(normalizedHeaders, null, 2));
|
|
704
|
+
}
|
|
705
|
+
const debugHeader = normalizedHeaders["debug"] || normalizedHeaders["Debug"] || normalizedHeaders["DEBUG"];
|
|
706
|
+
const debugSource = { debug: debugHeader !== void 0 ? debugHeader : _SettingsManager.globalDebugState };
|
|
707
|
+
const settings = this.settingsClass.fromHeaders(normalizedHeaders);
|
|
708
|
+
this.injectDebug(settings, debugSource);
|
|
709
|
+
if (_SettingsManager.globalDebugState) {
|
|
710
|
+
console.error("Final settings object:");
|
|
711
|
+
this.printSettingsDebug(settings);
|
|
712
|
+
console.error("=== End createFromHeaders Debug Info ===");
|
|
713
|
+
}
|
|
714
|
+
this.setSettings(settings, sessionId);
|
|
715
|
+
return settings;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Clear settings for a session
|
|
719
|
+
* @param sessionId The session ID to clear
|
|
720
|
+
*/
|
|
721
|
+
clearSession(sessionId) {
|
|
722
|
+
if (sessionId !== this.defaultSessionId) {
|
|
723
|
+
this.settingsMap.delete(sessionId);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
SettingsManager.globalDebugState = void 0;
|
|
728
|
+
|
|
729
|
+
// ../mcp-utils/dist/api-manager-factory.js
|
|
730
|
+
var APIManagerFactory = class {
|
|
731
|
+
/**
|
|
732
|
+
* Creates a new APIManagerFactory
|
|
733
|
+
* @param apiManagerClass The API Manager class to use for creating instances
|
|
734
|
+
* @param settingsManager The settings manager to get settings from
|
|
735
|
+
*/
|
|
736
|
+
constructor(apiManagerClass, settingsManager) {
|
|
737
|
+
this.apiManagerMap = /* @__PURE__ */ new Map();
|
|
738
|
+
this.defaultSessionId = "default";
|
|
739
|
+
this.apiManagerClass = apiManagerClass;
|
|
740
|
+
this.settingsManager = settingsManager;
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Get or create an API manager for a specific session
|
|
744
|
+
* @param sessionId The session ID (defaults to 'default' for stdio mode)
|
|
745
|
+
* @returns API Manager instance for the session
|
|
746
|
+
*/
|
|
747
|
+
getAPIManager(sessionId) {
|
|
748
|
+
const sid = sessionId || this.defaultSessionId;
|
|
749
|
+
if (!this.apiManagerMap.has(sid)) {
|
|
750
|
+
const settings2 = this.settingsManager.getSettings(sid);
|
|
751
|
+
const apiManager2 = this.createAPIManagerFromSettings(settings2);
|
|
752
|
+
this.apiManagerMap.set(sid, apiManager2);
|
|
753
|
+
}
|
|
754
|
+
const apiManager = this.apiManagerMap.get(sid);
|
|
755
|
+
const settings = this.settingsManager.getSettings(sid);
|
|
756
|
+
if (settings && typeof settings.debug !== "undefined") {
|
|
757
|
+
apiManager.debug = !!settings.debug;
|
|
758
|
+
}
|
|
759
|
+
return apiManager;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Create an API manager from settings
|
|
763
|
+
* @param settings Settings instance
|
|
764
|
+
* @returns API Manager instance
|
|
765
|
+
*/
|
|
766
|
+
createAPIManagerFromSettings(settings) {
|
|
767
|
+
return this.apiManagerClass.create(settings);
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Clear API manager for a session
|
|
771
|
+
* @param sessionId The session ID to clear
|
|
772
|
+
*/
|
|
773
|
+
clearSession(sessionId) {
|
|
774
|
+
if (sessionId !== this.defaultSessionId) {
|
|
775
|
+
this.apiManagerMap.delete(sessionId);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
// ../mcp-utils/dist/session-context.js
|
|
781
|
+
var _a;
|
|
782
|
+
var SessionContext = class {
|
|
783
|
+
/**
|
|
784
|
+
* Start automatic cleanup timer
|
|
785
|
+
*/
|
|
786
|
+
static startCleanupTimer() {
|
|
787
|
+
if (this.cleanupTimer)
|
|
788
|
+
return;
|
|
789
|
+
this.cleanupTimer = setInterval(() => {
|
|
790
|
+
this.cleanupExpiredSessions();
|
|
791
|
+
}, this.CLEANUP_INTERVAL_MS);
|
|
792
|
+
if (this.cleanupTimer.unref) {
|
|
793
|
+
this.cleanupTimer.unref();
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Clean up expired sessions
|
|
798
|
+
*/
|
|
799
|
+
static cleanupExpiredSessions() {
|
|
800
|
+
const now = Date.now();
|
|
801
|
+
const expiredSessions = [];
|
|
802
|
+
for (const [sessionId, sessionInfo] of this.sessionData.entries()) {
|
|
803
|
+
if (sessionId === this.DEFAULT_SESSION_ID)
|
|
804
|
+
continue;
|
|
805
|
+
if (now - sessionInfo.lastAccessed > this.SESSION_TIMEOUT_MS) {
|
|
806
|
+
expiredSessions.push(sessionId);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
expiredSessions.forEach((sessionId) => {
|
|
810
|
+
this.sessionData.delete(sessionId);
|
|
811
|
+
});
|
|
812
|
+
if (this.sessionData.size > this.MAX_SESSIONS) {
|
|
813
|
+
const sessions = Array.from(this.sessionData.entries()).filter(([sessionId]) => sessionId !== this.DEFAULT_SESSION_ID).sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed);
|
|
814
|
+
const toRemove = sessions.slice(0, sessions.length - this.MAX_SESSIONS + 1);
|
|
815
|
+
toRemove.forEach(([sessionId]) => {
|
|
816
|
+
this.sessionData.delete(sessionId);
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Get the current session ID from the MCP extra context
|
|
822
|
+
* @param extra Extra context passed to MCP tool callbacks
|
|
823
|
+
* @returns The session ID or DEFAULT_SESSION_ID if not available
|
|
824
|
+
*/
|
|
825
|
+
static getSessionId(extra) {
|
|
826
|
+
if (!extra)
|
|
827
|
+
return this.DEFAULT_SESSION_ID;
|
|
828
|
+
if (extra.transport && extra.transport.sessionId) {
|
|
829
|
+
return extra.transport.sessionId;
|
|
830
|
+
}
|
|
831
|
+
if (extra.sessionId) {
|
|
832
|
+
return extra.sessionId;
|
|
833
|
+
}
|
|
834
|
+
return this.DEFAULT_SESSION_ID;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Get settings for the current session
|
|
838
|
+
* @param serverModule ServerModule with settingsManager
|
|
839
|
+
* @param extra Extra context passed to MCP tool callbacks
|
|
840
|
+
* @returns Settings for the current session
|
|
841
|
+
*/
|
|
842
|
+
static getSettings(serverModule2, extra) {
|
|
843
|
+
const sessionId = this.getSessionId(extra);
|
|
844
|
+
if (!serverModule2.settingsManager) {
|
|
845
|
+
throw new Error("ServerModule does not have a settingsManager. Create it with createServerModule.");
|
|
846
|
+
}
|
|
847
|
+
return serverModule2.settingsManager.getSettings(sessionId);
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Get API manager for the current session
|
|
851
|
+
* @param serverModule ServerModule with apiManagerFactory
|
|
852
|
+
* @param extra Extra context passed to MCP tool callbacks
|
|
853
|
+
* @returns API manager for the current session
|
|
854
|
+
*/
|
|
855
|
+
static getAPIManager(serverModule2, extra) {
|
|
856
|
+
const sessionId = this.getSessionId(extra);
|
|
857
|
+
if (!serverModule2.apiManagerFactory) {
|
|
858
|
+
throw new Error("ServerModule does not have an apiManagerFactory. Create it with createServerModule.");
|
|
859
|
+
}
|
|
860
|
+
return serverModule2.apiManagerFactory.getAPIManager(sessionId);
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Set session data for the current session
|
|
864
|
+
* @param key The key to store the data under
|
|
865
|
+
* @param value The value to store
|
|
866
|
+
* @param extra Extra context with session information
|
|
867
|
+
*/
|
|
868
|
+
static setData(key, value, extra) {
|
|
869
|
+
const sessionId = this.getSessionId(extra);
|
|
870
|
+
const now = Date.now();
|
|
871
|
+
if (!this.sessionData.has(sessionId)) {
|
|
872
|
+
this.sessionData.set(sessionId, {
|
|
873
|
+
data: /* @__PURE__ */ new Map(),
|
|
874
|
+
lastAccessed: now,
|
|
875
|
+
createdAt: now
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
const sessionInfo = this.sessionData.get(sessionId);
|
|
879
|
+
sessionInfo.lastAccessed = now;
|
|
880
|
+
sessionInfo.data.set(key, value);
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Get session data for the current session
|
|
884
|
+
* @param key The key to retrieve data for
|
|
885
|
+
* @param extra Extra context with session information
|
|
886
|
+
* @returns The stored data or undefined if not found
|
|
887
|
+
*/
|
|
888
|
+
static getData(key, extra) {
|
|
889
|
+
const sessionId = this.getSessionId(extra);
|
|
890
|
+
if (!this.sessionData.has(sessionId)) {
|
|
891
|
+
return void 0;
|
|
892
|
+
}
|
|
893
|
+
const sessionInfo = this.sessionData.get(sessionId);
|
|
894
|
+
sessionInfo.lastAccessed = Date.now();
|
|
895
|
+
return sessionInfo.data.get(key);
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Clear session data for a specific session
|
|
899
|
+
* @param sessionId The session ID to clear
|
|
900
|
+
*/
|
|
901
|
+
static clearSessionData(sessionId) {
|
|
902
|
+
this.sessionData.delete(sessionId);
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Get session statistics (for monitoring)
|
|
906
|
+
*/
|
|
907
|
+
static getSessionStats() {
|
|
908
|
+
const now = Date.now();
|
|
909
|
+
let oldestAccess = null;
|
|
910
|
+
for (const [, sessionInfo] of this.sessionData.entries()) {
|
|
911
|
+
if (oldestAccess === null || sessionInfo.lastAccessed < oldestAccess) {
|
|
912
|
+
oldestAccess = sessionInfo.lastAccessed;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return {
|
|
916
|
+
totalSessions: this.sessionData.size,
|
|
917
|
+
memoryUsage: this.sessionData.size * 1e3,
|
|
918
|
+
// Rough estimate
|
|
919
|
+
oldestSession: oldestAccess ? now - oldestAccess : null
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Get or prompt for user interactive data with automatic caching and expiration
|
|
924
|
+
* @param options Configuration for the interactive data request
|
|
925
|
+
* @param extra Extra context with session information
|
|
926
|
+
* @returns Promise that resolves to the user data (either from cache or fresh prompt)
|
|
927
|
+
*/
|
|
928
|
+
static async getOrPromptUserData(options, extra) {
|
|
929
|
+
const sessionId = this.getSessionId(extra);
|
|
930
|
+
const now = Date.now();
|
|
931
|
+
const expirationMs = (options.expirationMinutes || 30) * 60 * 1e3;
|
|
932
|
+
const cachedData = this.getData(options.cacheKey, extra);
|
|
933
|
+
if (cachedData && cachedData.expiresAt && now < cachedData.expiresAt) {
|
|
934
|
+
const refreshedData = {
|
|
935
|
+
...cachedData,
|
|
936
|
+
expiresAt: now + expirationMs,
|
|
937
|
+
lastUsed: now
|
|
938
|
+
};
|
|
939
|
+
this.setData(options.cacheKey, refreshedData, extra);
|
|
940
|
+
let dataToReturn2 = cachedData.data;
|
|
941
|
+
if (options.fieldsToShow) {
|
|
942
|
+
dataToReturn2 = {};
|
|
943
|
+
for (const field of options.fieldsToShow) {
|
|
944
|
+
if (cachedData.data[field] !== void 0) {
|
|
945
|
+
dataToReturn2[field] = cachedData.data[field];
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
console.error(`Using cached user data for session: ${sessionId}, key: ${options.cacheKey}`);
|
|
950
|
+
return { cancelled: false, data: dataToReturn2 };
|
|
951
|
+
}
|
|
952
|
+
console.error(`Prompting user for data for session: ${sessionId}, key: ${options.cacheKey}`);
|
|
953
|
+
let result;
|
|
954
|
+
if (options.showLoginDialog) {
|
|
955
|
+
const { showLoginDialog: showLoginDialog2 } = await Promise.resolve().then(() => (init_ui_dialog(), ui_dialog_exports));
|
|
956
|
+
result = await showLoginDialog2(options.dialogTitle, options.dialogMessage);
|
|
957
|
+
} else {
|
|
958
|
+
const { showDialog: showDialog2 } = await Promise.resolve().then(() => (init_ui_dialog(), ui_dialog_exports));
|
|
959
|
+
const defaultFields = [
|
|
960
|
+
{
|
|
961
|
+
name: "message",
|
|
962
|
+
label: "Message",
|
|
963
|
+
type: "text",
|
|
964
|
+
placeholder: "Enter your message here...",
|
|
965
|
+
required: true
|
|
966
|
+
}
|
|
967
|
+
];
|
|
968
|
+
result = await showDialog2({
|
|
969
|
+
title: options.dialogTitle || "Input Required",
|
|
970
|
+
message: options.dialogMessage,
|
|
971
|
+
fields: options.customFields || defaultFields
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
if (result.cancelled) {
|
|
975
|
+
return result;
|
|
976
|
+
}
|
|
977
|
+
const cachedEntry = {
|
|
978
|
+
data: result.data,
|
|
979
|
+
expiresAt: now + expirationMs,
|
|
980
|
+
createdAt: now,
|
|
981
|
+
lastUsed: now
|
|
982
|
+
};
|
|
983
|
+
this.setData(options.cacheKey, cachedEntry, extra);
|
|
984
|
+
let dataToReturn = result.data;
|
|
985
|
+
if (options.fieldsToShow) {
|
|
986
|
+
dataToReturn = {};
|
|
987
|
+
for (const field of options.fieldsToShow) {
|
|
988
|
+
if (result.data[field] !== void 0) {
|
|
989
|
+
dataToReturn[field] = result.data[field];
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return { cancelled: false, data: dataToReturn };
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Clear cached user data for a specific key
|
|
997
|
+
* @param cacheKey The cache key to clear
|
|
998
|
+
* @param extra Extra context with session information
|
|
999
|
+
*/
|
|
1000
|
+
static clearUserData(cacheKey, extra) {
|
|
1001
|
+
const sessionId = this.getSessionId(extra);
|
|
1002
|
+
if (this.sessionData.has(sessionId)) {
|
|
1003
|
+
const sessionInfo = this.sessionData.get(sessionId);
|
|
1004
|
+
sessionInfo.data.delete(cacheKey);
|
|
1005
|
+
sessionInfo.lastAccessed = Date.now();
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Check if user data exists and is not expired
|
|
1010
|
+
* @param cacheKey The cache key to check
|
|
1011
|
+
* @param extra Extra context with session information
|
|
1012
|
+
* @returns true if valid cached data exists, false otherwise
|
|
1013
|
+
*/
|
|
1014
|
+
static hasValidUserData(cacheKey, extra) {
|
|
1015
|
+
const cachedData = this.getData(cacheKey, extra);
|
|
1016
|
+
if (!cachedData || !cachedData.expiresAt) {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
return Date.now() < cachedData.expiresAt;
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
_a = SessionContext;
|
|
1023
|
+
SessionContext.DEFAULT_SESSION_ID = "default";
|
|
1024
|
+
SessionContext.sessionData = /* @__PURE__ */ new Map();
|
|
1025
|
+
SessionContext.SESSION_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
|
|
1026
|
+
SessionContext.CLEANUP_INTERVAL_MS = 60 * 60 * 1e3;
|
|
1027
|
+
SessionContext.MAX_SESSIONS = 1e3;
|
|
1028
|
+
SessionContext.cleanupTimer = null;
|
|
1029
|
+
(() => {
|
|
1030
|
+
_a.startCleanupTimer();
|
|
1031
|
+
})();
|
|
1032
|
+
|
|
1033
|
+
// ../mcp-utils/dist/session-manager.js
|
|
1034
|
+
var DEFAULT_SESSION_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
1035
|
+
var SessionManager = class {
|
|
1036
|
+
constructor() {
|
|
1037
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Create a new session
|
|
1041
|
+
* @param sessionId The session ID
|
|
1042
|
+
* @param metadata Optional metadata to store with the session
|
|
1043
|
+
* @returns Session info
|
|
1044
|
+
*/
|
|
1045
|
+
createSession(sessionId, metadata = {}) {
|
|
1046
|
+
const now = /* @__PURE__ */ new Date();
|
|
1047
|
+
const sessionInfo = {
|
|
1048
|
+
sessionId,
|
|
1049
|
+
createdAt: now,
|
|
1050
|
+
lastActive: now,
|
|
1051
|
+
...metadata
|
|
1052
|
+
};
|
|
1053
|
+
this.sessions.set(sessionId, sessionInfo);
|
|
1054
|
+
return sessionInfo;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Get info for a session
|
|
1058
|
+
* @param sessionId The session ID
|
|
1059
|
+
* @returns Session info or undefined if not found
|
|
1060
|
+
*/
|
|
1061
|
+
getSession(sessionId) {
|
|
1062
|
+
if (this.sessions.has(sessionId)) {
|
|
1063
|
+
this.touchSession(sessionId);
|
|
1064
|
+
}
|
|
1065
|
+
return this.sessions.get(sessionId);
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Update session activity timestamp
|
|
1069
|
+
* @param sessionId The session ID
|
|
1070
|
+
*/
|
|
1071
|
+
touchSession(sessionId) {
|
|
1072
|
+
const session = this.sessions.get(sessionId);
|
|
1073
|
+
if (session) {
|
|
1074
|
+
session.lastActive = /* @__PURE__ */ new Date();
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Remove a session
|
|
1079
|
+
* @param sessionId The session ID
|
|
1080
|
+
*/
|
|
1081
|
+
removeSession(sessionId) {
|
|
1082
|
+
this.sessions.delete(sessionId);
|
|
1083
|
+
SessionContext.clearSessionData(sessionId);
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Get all active sessions
|
|
1087
|
+
* @returns Array of session info objects
|
|
1088
|
+
*/
|
|
1089
|
+
getAllSessions() {
|
|
1090
|
+
return Array.from(this.sessions.values());
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Get count of active sessions
|
|
1094
|
+
* @returns Number of active sessions
|
|
1095
|
+
*/
|
|
1096
|
+
getSessionCount() {
|
|
1097
|
+
return this.sessions.size;
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Clean up inactive sessions older than the specified timeout
|
|
1101
|
+
* @param timeoutMs Timeout in milliseconds (default: 30 minutes)
|
|
1102
|
+
* @returns Number of sessions removed
|
|
1103
|
+
*/
|
|
1104
|
+
cleanupInactiveSessions(timeoutMs = DEFAULT_SESSION_TIMEOUT_MS) {
|
|
1105
|
+
const now = /* @__PURE__ */ new Date();
|
|
1106
|
+
let removedCount = 0;
|
|
1107
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
1108
|
+
const inactiveTime = now.getTime() - session.lastActive.getTime();
|
|
1109
|
+
if (inactiveTime > timeoutMs) {
|
|
1110
|
+
this.removeSession(sessionId);
|
|
1111
|
+
removedCount++;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return removedCount;
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
// ../mcp-utils/dist/server-utils.js
|
|
1119
|
+
function getHeaderValue(headers, key) {
|
|
1120
|
+
const value = headers[key] || headers[key.toUpperCase()] || headers[key.toLowerCase()];
|
|
1121
|
+
return typeof value === "string" ? value : Array.isArray(value) ? value[0] : void 0;
|
|
1122
|
+
}
|
|
1123
|
+
function createApiRunner(serverModule2) {
|
|
1124
|
+
return async (method, uri, data, extra, domain) => {
|
|
1125
|
+
const apiManager = SessionContext.getAPIManager(serverModule2, extra);
|
|
1126
|
+
return await apiManager.callApi(method, uri, data, domain);
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function createServerModule(server2, Settings2, pkg2, apiManagerClass) {
|
|
1130
|
+
const settingsManager = new SettingsManager(Settings2);
|
|
1131
|
+
const apiManagerFactory = new APIManagerFactory(apiManagerClass, settingsManager);
|
|
1132
|
+
const sessionManager = new SessionManager();
|
|
1133
|
+
return {
|
|
1134
|
+
server: server2,
|
|
1135
|
+
Settings: Settings2,
|
|
1136
|
+
settingsManager,
|
|
1137
|
+
apiManagerFactory,
|
|
1138
|
+
sessionManager,
|
|
1139
|
+
pkg: pkg2
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// ../mcp-utils/dist/index.js
|
|
1144
|
+
init_ui_dialog();
|
|
1145
|
+
|
|
1146
|
+
// src/index.ts
|
|
1147
|
+
import { readFileSync } from "fs";
|
|
1148
|
+
import { join as join2, dirname } from "path";
|
|
1149
|
+
import { fileURLToPath } from "url";
|
|
1150
|
+
|
|
1151
|
+
// src/documentation-api-manager.ts
|
|
1152
|
+
import axios3 from "axios";
|
|
1153
|
+
|
|
1154
|
+
// ../infra/dist/api-client.js
|
|
1155
|
+
import axios from "axios";
|
|
1156
|
+
import https from "https";
|
|
1157
|
+
var TokenType;
|
|
1158
|
+
(function(TokenType2) {
|
|
1159
|
+
TokenType2["API_KEY"] = "API_KEY";
|
|
1160
|
+
TokenType2["CI_TOKEN"] = "CI_TOKEN";
|
|
1161
|
+
})(TokenType || (TokenType = {}));
|
|
1162
|
+
|
|
1163
|
+
// ../infra/dist/string-utils.js
|
|
1164
|
+
function nullOrEmpty(val) {
|
|
1165
|
+
return val === void 0 || val === null || typeof val === "string" && (val.trim() === "" || val === "undefined" || val === "null");
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// ../infra/dist/settings.js
|
|
1169
|
+
var Settings = class _Settings {
|
|
1170
|
+
constructor({ apiKey = process.env.API_KEY, username = process.env.USERNAME, password = process.env.PASSWORD, s1cUrl = process.env.S1C_URL, managementHost = process.env.MANAGEMENT_HOST, managementPort = process.env.MANAGEMENT_PORT || "443", cloudInfraToken = process.env.CLOUD_INFRA_TOKEN, clientId = process.env.CLIENT_ID, secretKey = process.env.SECRET_KEY, region = process.env.REGION || "EU", devPort = process.env.DEV_PORT || "8006" } = {}) {
|
|
1171
|
+
this.region = "EU";
|
|
1172
|
+
this.devPort = "8006";
|
|
1173
|
+
this.apiKey = apiKey;
|
|
1174
|
+
this.username = username;
|
|
1175
|
+
this.password = password;
|
|
1176
|
+
this.s1cUrl = s1cUrl;
|
|
1177
|
+
this.managementHost = managementHost;
|
|
1178
|
+
this.managementPort = managementPort;
|
|
1179
|
+
this.cloudInfraToken = cloudInfraToken;
|
|
1180
|
+
this.clientId = clientId;
|
|
1181
|
+
this.secretKey = secretKey;
|
|
1182
|
+
this.region = this.isValidRegion(region) ? region : "EU";
|
|
1183
|
+
this.devPort = devPort;
|
|
1184
|
+
this.validate();
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Check if the provided string is a valid region
|
|
1188
|
+
*/
|
|
1189
|
+
isValidRegion(region) {
|
|
1190
|
+
return ["EU", "US", "STG", "LOCAL"].includes(region.toUpperCase());
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Get Cloud Infra Gateway based on region
|
|
1194
|
+
*/
|
|
1195
|
+
getCloudInfraGateway() {
|
|
1196
|
+
switch (this.region) {
|
|
1197
|
+
case "EU":
|
|
1198
|
+
return "https://cloudinfra-gw.portal.checkpoint.com";
|
|
1199
|
+
case "US":
|
|
1200
|
+
return "https://cloudinfra-gw-us.portal.checkpoint.com";
|
|
1201
|
+
case "STG":
|
|
1202
|
+
case "LOCAL":
|
|
1203
|
+
return "https://dev-cloudinfra-gw.kube1.iaas.checkpoint.com";
|
|
1204
|
+
default:
|
|
1205
|
+
return "";
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Validate the settings
|
|
1210
|
+
*/
|
|
1211
|
+
validate() {
|
|
1212
|
+
if (!nullOrEmpty(this.s1cUrl) && nullOrEmpty(this.apiKey) && nullOrEmpty(this.cloudInfraToken)) {
|
|
1213
|
+
throw new Error("API key or CI Token is required for S1C (via --api-key or API_KEY env var)");
|
|
1214
|
+
}
|
|
1215
|
+
if (!nullOrEmpty(this.managementHost) && nullOrEmpty(this.apiKey) && (nullOrEmpty(this.username) || nullOrEmpty(this.password))) {
|
|
1216
|
+
throw new Error("Either API key or username/password are required for on-prem management (via CLI args or env vars)");
|
|
1217
|
+
}
|
|
1218
|
+
if (nullOrEmpty(this.s1cUrl) && nullOrEmpty(this.managementHost)) {
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Create settings from command-line arguments
|
|
1223
|
+
*/
|
|
1224
|
+
static fromArgs(args) {
|
|
1225
|
+
return new _Settings({
|
|
1226
|
+
apiKey: args.apiKey,
|
|
1227
|
+
username: args.username,
|
|
1228
|
+
password: args.password,
|
|
1229
|
+
s1cUrl: args.s1cUrl,
|
|
1230
|
+
managementHost: args.managementHost,
|
|
1231
|
+
managementPort: args.managementPort,
|
|
1232
|
+
cloudInfraToken: args.cloudInfraToken,
|
|
1233
|
+
clientId: args.clientId,
|
|
1234
|
+
secretKey: args.secretKey,
|
|
1235
|
+
region: typeof args.region === "string" ? args.region.toUpperCase() : void 0,
|
|
1236
|
+
devPort: args.devPort
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Create settings from HTTP headers
|
|
1241
|
+
* Maps headers to environment variable format based on server config
|
|
1242
|
+
*/
|
|
1243
|
+
static fromHeaders(headers) {
|
|
1244
|
+
return new _Settings({
|
|
1245
|
+
apiKey: getHeaderValue(headers, "API-KEY"),
|
|
1246
|
+
username: getHeaderValue(headers, "USERNAME"),
|
|
1247
|
+
password: getHeaderValue(headers, "PASSWORD"),
|
|
1248
|
+
s1cUrl: getHeaderValue(headers, "S1C-URL"),
|
|
1249
|
+
managementHost: getHeaderValue(headers, "MANAGEMENT-HOST"),
|
|
1250
|
+
managementPort: getHeaderValue(headers, "MANAGEMENT-PORT"),
|
|
1251
|
+
cloudInfraToken: getHeaderValue(headers, "CLOUD-INFRA-TOKEN"),
|
|
1252
|
+
clientId: getHeaderValue(headers, "CLIENT-ID"),
|
|
1253
|
+
secretKey: getHeaderValue(headers, "SECRET-KEY"),
|
|
1254
|
+
region: getHeaderValue(headers, "REGION")?.toUpperCase(),
|
|
1255
|
+
devPort: getHeaderValue(headers, "DEV-PORT")
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
// ../infra/dist/external-user-token-manager.js
|
|
1261
|
+
import axios2 from "axios";
|
|
1262
|
+
var ExternalTokenManager = class _ExternalTokenManager {
|
|
1263
|
+
/**
|
|
1264
|
+
* Create a new ExternalTokenManager
|
|
1265
|
+
*
|
|
1266
|
+
* @param settings Settings containing clientId and secretKey
|
|
1267
|
+
*/
|
|
1268
|
+
constructor(settings) {
|
|
1269
|
+
this.settings = settings;
|
|
1270
|
+
this.tokenCache = /* @__PURE__ */ new Map();
|
|
1271
|
+
}
|
|
1272
|
+
authUrl() {
|
|
1273
|
+
const gatewayUrl = this.settings.getCloudInfraGateway();
|
|
1274
|
+
return `${gatewayUrl}/auth/external`;
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Get a valid auth token, fetching a new one if necessary
|
|
1278
|
+
* @returns Promise resolving to a valid token
|
|
1279
|
+
*/
|
|
1280
|
+
async getToken() {
|
|
1281
|
+
if (!this.settings.clientId || !this.settings.secretKey) {
|
|
1282
|
+
throw new Error("Client ID and Secret Key are required for external user authentication");
|
|
1283
|
+
}
|
|
1284
|
+
const cacheKey = `${this.settings.clientId}:${this.settings.region || "EU"}`;
|
|
1285
|
+
const cached = this.tokenCache.get(cacheKey);
|
|
1286
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
1287
|
+
return cached.token;
|
|
1288
|
+
}
|
|
1289
|
+
const response = await axios2.post(this.authUrl(), {
|
|
1290
|
+
clientId: this.settings.clientId,
|
|
1291
|
+
accessKey: this.settings.secretKey
|
|
1292
|
+
}, {
|
|
1293
|
+
timeout: 3e4,
|
|
1294
|
+
validateStatus: () => true
|
|
1295
|
+
});
|
|
1296
|
+
if (response.status !== 200) {
|
|
1297
|
+
const errorData = response.data ? JSON.stringify(response.data) : "No response data";
|
|
1298
|
+
throw new Error(`Failed to get auth token: ${response.statusText} (${response.status}). Response data: ${errorData}`);
|
|
1299
|
+
}
|
|
1300
|
+
const responseData = response.data;
|
|
1301
|
+
const token = responseData.data.token;
|
|
1302
|
+
const expiresIn = responseData.data.expiresIn;
|
|
1303
|
+
if (!token || !expiresIn) {
|
|
1304
|
+
throw new Error("Invalid token response from auth server");
|
|
1305
|
+
}
|
|
1306
|
+
const tokenInfo = {
|
|
1307
|
+
token,
|
|
1308
|
+
expiresAt: Date.now() + expiresIn * 1e3 - 5e3
|
|
1309
|
+
};
|
|
1310
|
+
this.tokenCache.set(cacheKey, tokenInfo);
|
|
1311
|
+
return token;
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Create a token service instance
|
|
1315
|
+
* @param settings Settings containing clientId and secretKey
|
|
1316
|
+
* @returns A new ExternalUserTokenManager
|
|
1317
|
+
* instance
|
|
1318
|
+
*/
|
|
1319
|
+
static create(settings) {
|
|
1320
|
+
return new _ExternalTokenManager(settings);
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
var ExternalUserTokenManager = class _ExternalUserTokenManager extends ExternalTokenManager {
|
|
1324
|
+
static create(settings) {
|
|
1325
|
+
return new _ExternalUserTokenManager(settings);
|
|
1326
|
+
}
|
|
1327
|
+
authUrl() {
|
|
1328
|
+
const gatewayUrl = this.settings.getCloudInfraGateway();
|
|
1329
|
+
return `${gatewayUrl}/auth/external/user`;
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
// src/documentation-api-manager.ts
|
|
1334
|
+
var DocumentationToolAPIManager = class _DocumentationToolAPIManager {
|
|
1335
|
+
constructor(settings) {
|
|
1336
|
+
this.settings = settings;
|
|
1337
|
+
this.tokenService = new ExternalUserTokenManager(settings);
|
|
1338
|
+
this.axios = axios3.create({
|
|
1339
|
+
timeout: 3e4,
|
|
1340
|
+
validateStatus: () => true
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Create a new DocumentationToolAPIManager instance from settings for Documentation Tool
|
|
1345
|
+
* @param settings The complete settings object for this session
|
|
1346
|
+
* @returns A new DocumentationToolAPIManager instance
|
|
1347
|
+
*/
|
|
1348
|
+
static create(settings) {
|
|
1349
|
+
return new _DocumentationToolAPIManager(settings);
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Get a valid auth token using the shared token service
|
|
1353
|
+
*/
|
|
1354
|
+
async getToken() {
|
|
1355
|
+
return await this.tokenService.getToken();
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Get the base URL for API calls, handling local development if needed
|
|
1359
|
+
*/
|
|
1360
|
+
getBaseUrl() {
|
|
1361
|
+
if (this.settings.region === "LOCAL") {
|
|
1362
|
+
return `http://localhost:${this.settings.devPort || "8006"}`;
|
|
1363
|
+
}
|
|
1364
|
+
return `${this.settings.getCloudInfraGateway()}/app/console-one`;
|
|
1365
|
+
}
|
|
1366
|
+
async callApi(method, uri, data) {
|
|
1367
|
+
const token = await this.getToken();
|
|
1368
|
+
const chatUrl = `${this.getBaseUrl()}/${uri}`;
|
|
1369
|
+
const question = data.question;
|
|
1370
|
+
const product = data.product;
|
|
1371
|
+
const headers = {
|
|
1372
|
+
Authorization: `Bearer ${token}`,
|
|
1373
|
+
"Content-Type": "application/json"
|
|
1374
|
+
};
|
|
1375
|
+
const params = {
|
|
1376
|
+
question,
|
|
1377
|
+
product,
|
|
1378
|
+
skip_llm: true
|
|
1379
|
+
};
|
|
1380
|
+
try {
|
|
1381
|
+
const response = await this.axios.post(chatUrl, params, {
|
|
1382
|
+
headers
|
|
1383
|
+
});
|
|
1384
|
+
if (response.status !== 200) {
|
|
1385
|
+
console.error(
|
|
1386
|
+
"Error response body:",
|
|
1387
|
+
JSON.stringify(response.data, null, 2)
|
|
1388
|
+
);
|
|
1389
|
+
throw new Error(
|
|
1390
|
+
`Chat request failed: ${response.statusText} (${response.status})`
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
return {
|
|
1394
|
+
response: response.data.response
|
|
1395
|
+
};
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
const axiosError = error;
|
|
1398
|
+
console.error("Axios error:", axiosError.message);
|
|
1399
|
+
if (axiosError.response) {
|
|
1400
|
+
console.error("Error status:", axiosError.response.status);
|
|
1401
|
+
console.error(
|
|
1402
|
+
"Error headers:",
|
|
1403
|
+
JSON.stringify(axiosError.response.headers, null, 2)
|
|
1404
|
+
);
|
|
1405
|
+
console.error(
|
|
1406
|
+
"Error data:",
|
|
1407
|
+
JSON.stringify(axiosError.response.data, null, 2)
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1410
|
+
throw error;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
|
|
1415
|
+
// src/settings.ts
|
|
1416
|
+
var DocumentationToolSettings = class _DocumentationToolSettings extends Settings {
|
|
1417
|
+
constructor(args = {}) {
|
|
1418
|
+
super(args);
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Create DocumentationToolSettings from command-line arguments for Documentation Tool
|
|
1422
|
+
*/
|
|
1423
|
+
static fromArgs(args) {
|
|
1424
|
+
return new _DocumentationToolSettings({
|
|
1425
|
+
...args
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
// src/index.ts
|
|
1431
|
+
var pkg = JSON.parse(
|
|
1432
|
+
readFileSync(
|
|
1433
|
+
join2(dirname(fileURLToPath(import.meta.url)), "../package.json"),
|
|
1434
|
+
"utf-8"
|
|
1435
|
+
)
|
|
1436
|
+
);
|
|
11
1437
|
process.env.CP_MCP_MAIN_PKG = `${pkg.name} v${pkg.version}`;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
version: '1.0.0',
|
|
1438
|
+
var server = new McpServer({
|
|
1439
|
+
name: "checkpoint-documentation",
|
|
1440
|
+
description: "Comprehensive Check Point documentation assistant providing instant access to product information, technical specifications, configuration guidance, and feature documentation across the entire Check Point security portfolio.",
|
|
1441
|
+
version: "1.0.0"
|
|
17
1442
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1443
|
+
var serverModule = createServerModule(
|
|
1444
|
+
server,
|
|
1445
|
+
DocumentationToolSettings,
|
|
1446
|
+
pkg,
|
|
1447
|
+
DocumentationToolAPIManager
|
|
1448
|
+
);
|
|
1449
|
+
var runApi = createApiRunner(serverModule);
|
|
1450
|
+
server.tool(
|
|
1451
|
+
"ask-checkpoint-docs",
|
|
1452
|
+
"Ask Check Point documentation. Use this to get information about Check Point products and features.",
|
|
1453
|
+
{
|
|
1454
|
+
text: z.string().describe(
|
|
1455
|
+
`The question to ask Check Point, in clear text with clear concise context and instructions.
|
|
24
1456
|
The question should be about one of the Check Point products (e.g., Smart-1 Cloud, Harmony SASE, CloudGuard, Harmony Endpoint, etc.).
|
|
25
|
-
Include relevant technical details, product names, feature names, and specific use cases for the most accurate and helpful response.`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
])
|
|
48
|
-
|
|
49
|
-
.describe(`Select the specific Check Point product to target for focused documentation queries. This parameter helps narrow down search results to the most relevant product-specific information. Defaults to 's1c' (Smart-1 Cloud) if not specified. Choose from:
|
|
1457
|
+
Include relevant technical details, product names, feature names, and specific use cases for the most accurate and helpful response.`
|
|
1458
|
+
),
|
|
1459
|
+
product: z.enum([
|
|
1460
|
+
"s1c",
|
|
1461
|
+
"maas",
|
|
1462
|
+
"mobile",
|
|
1463
|
+
"endpoint",
|
|
1464
|
+
"edr",
|
|
1465
|
+
"xdr",
|
|
1466
|
+
"playblocks",
|
|
1467
|
+
"infinity-events",
|
|
1468
|
+
"cloudguard",
|
|
1469
|
+
"cloudguardnetwork",
|
|
1470
|
+
"cloudnetworksecurity",
|
|
1471
|
+
"rfp-copilot",
|
|
1472
|
+
"harmony-sase",
|
|
1473
|
+
"customer-support",
|
|
1474
|
+
"smp",
|
|
1475
|
+
"appsec",
|
|
1476
|
+
"sd-wan",
|
|
1477
|
+
"horizonmonitoring",
|
|
1478
|
+
"ndr"
|
|
1479
|
+
]).optional().describe(
|
|
1480
|
+
`Select the specific Check Point product to target for focused documentation queries. This parameter helps narrow down search results to the most relevant product-specific information. Defaults to 's1c' (Smart-1 Cloud) if not specified. Choose from:
|
|
50
1481
|
|
|
51
1482
|
Cloud Management & Security:
|
|
52
1483
|
- s1c: Smart-1 Cloud - Cloud-based security management platform for centralized policy management (DEFAULT)
|
|
@@ -80,37 +1511,47 @@ server.tool('ask-checkpoint-docs', 'Ask Check Point documentation. Use this to g
|
|
|
80
1511
|
Usage Guidelines:
|
|
81
1512
|
- Defaults to s1c (Smart-1 Cloud) for general management and security questions
|
|
82
1513
|
- Specify a different product when the user's question is clearly about that specific product
|
|
83
|
-
- For troubleshooting or configuration questions, specify the exact product involved.`
|
|
84
|
-
|
|
1514
|
+
- For troubleshooting or configuration questions, specify the exact product involved.`
|
|
1515
|
+
)
|
|
1516
|
+
},
|
|
1517
|
+
async ({ text, product }, extra) => {
|
|
85
1518
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
1519
|
+
const result = await runApi(
|
|
1520
|
+
"post",
|
|
1521
|
+
"api/v1/doc/ask_docs",
|
|
1522
|
+
{ question: text, product },
|
|
1523
|
+
extra
|
|
1524
|
+
);
|
|
1525
|
+
return {
|
|
1526
|
+
content: [
|
|
1527
|
+
{
|
|
1528
|
+
type: "text",
|
|
1529
|
+
text: result.response
|
|
1530
|
+
}
|
|
1531
|
+
]
|
|
1532
|
+
};
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
return {
|
|
1535
|
+
content: [
|
|
1536
|
+
{
|
|
1537
|
+
type: "text",
|
|
1538
|
+
text: `API Error: ${error.message}`
|
|
1539
|
+
}
|
|
1540
|
+
]
|
|
1541
|
+
};
|
|
105
1542
|
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
1543
|
+
}
|
|
1544
|
+
);
|
|
1545
|
+
var main = async () => {
|
|
1546
|
+
await launchMCPServer(
|
|
1547
|
+
join2(dirname(fileURLToPath(import.meta.url)), "server-config.json"),
|
|
1548
|
+
serverModule
|
|
1549
|
+
);
|
|
111
1550
|
};
|
|
112
1551
|
main().catch((error) => {
|
|
113
|
-
|
|
114
|
-
|
|
1552
|
+
console.error("Fatal error in main():", error);
|
|
1553
|
+
process.exit(1);
|
|
115
1554
|
});
|
|
116
|
-
|
|
1555
|
+
export {
|
|
1556
|
+
server
|
|
1557
|
+
};
|