winhelm-mcp 1.1.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/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +646 -0
- package/dist/config/config-manager.d.ts +50 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +300 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/default-config.d.ts +4 -0
- package/dist/config/default-config.d.ts.map +1 -0
- package/dist/config/default-config.js +48 -0
- package/dist/config/default-config.js.map +1 -0
- package/dist/config/profiles.d.ts +16 -0
- package/dist/config/profiles.d.ts.map +1 -0
- package/dist/config/profiles.js +208 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/engine/filesystem.d.ts +52 -0
- package/dist/engine/filesystem.d.ts.map +1 -0
- package/dist/engine/filesystem.js +499 -0
- package/dist/engine/filesystem.js.map +1 -0
- package/dist/engine/http-client.d.ts +10 -0
- package/dist/engine/http-client.d.ts.map +1 -0
- package/dist/engine/http-client.js +93 -0
- package/dist/engine/http-client.js.map +1 -0
- package/dist/engine/pdf-generator.d.ts +15 -0
- package/dist/engine/pdf-generator.d.ts.map +1 -0
- package/dist/engine/pdf-generator.js +325 -0
- package/dist/engine/pdf-generator.js.map +1 -0
- package/dist/engine/powershell-runner.d.ts +23 -0
- package/dist/engine/powershell-runner.d.ts.map +1 -0
- package/dist/engine/powershell-runner.js +148 -0
- package/dist/engine/powershell-runner.js.map +1 -0
- package/dist/engine/ripgrep.d.ts +20 -0
- package/dist/engine/ripgrep.d.ts.map +1 -0
- package/dist/engine/ripgrep.js +173 -0
- package/dist/engine/ripgrep.js.map +1 -0
- package/dist/engine/system.d.ts +67 -0
- package/dist/engine/system.d.ts.map +1 -0
- package/dist/engine/system.js +558 -0
- package/dist/engine/system.js.map +1 -0
- package/dist/engine/task-manager.d.ts +66 -0
- package/dist/engine/task-manager.d.ts.map +1 -0
- package/dist/engine/task-manager.js +203 -0
- package/dist/engine/task-manager.js.map +1 -0
- package/dist/gateway/dashboard-html.d.ts +7 -0
- package/dist/gateway/dashboard-html.d.ts.map +1 -0
- package/dist/gateway/dashboard-html.js +624 -0
- package/dist/gateway/dashboard-html.js.map +1 -0
- package/dist/gateway/preview-html.d.ts +5 -0
- package/dist/gateway/preview-html.d.ts.map +1 -0
- package/dist/gateway/preview-html.js +351 -0
- package/dist/gateway/preview-html.js.map +1 -0
- package/dist/gateway/rate-limiter.d.ts +8 -0
- package/dist/gateway/rate-limiter.d.ts.map +1 -0
- package/dist/gateway/rate-limiter.js +51 -0
- package/dist/gateway/rate-limiter.js.map +1 -0
- package/dist/gateway/server.d.ts +18 -0
- package/dist/gateway/server.d.ts.map +1 -0
- package/dist/gateway/server.js +337 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/gateway/sse-gateway.d.ts +43 -0
- package/dist/gateway/sse-gateway.d.ts.map +1 -0
- package/dist/gateway/sse-gateway.js +161 -0
- package/dist/gateway/sse-gateway.js.map +1 -0
- package/dist/gateway/streamable-gateway.d.ts +39 -0
- package/dist/gateway/streamable-gateway.d.ts.map +1 -0
- package/dist/gateway/streamable-gateway.js +176 -0
- package/dist/gateway/streamable-gateway.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +164 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/desktop-tools.d.ts +3 -0
- package/dist/tools/desktop-tools.d.ts.map +1 -0
- package/dist/tools/desktop-tools.js +280 -0
- package/dist/tools/desktop-tools.js.map +1 -0
- package/dist/tools/file-tools.d.ts +3 -0
- package/dist/tools/file-tools.d.ts.map +1 -0
- package/dist/tools/file-tools.js +433 -0
- package/dist/tools/file-tools.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/network-tools.d.ts +3 -0
- package/dist/tools/network-tools.d.ts.map +1 -0
- package/dist/tools/network-tools.js +108 -0
- package/dist/tools/network-tools.js.map +1 -0
- package/dist/tools/registry.d.ts +50 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +54 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/terminal-tools.d.ts +3 -0
- package/dist/tools/terminal-tools.d.ts.map +1 -0
- package/dist/tools/terminal-tools.js +152 -0
- package/dist/tools/terminal-tools.js.map +1 -0
- package/dist/tools/tool-wrapper.d.ts +7 -0
- package/dist/tools/tool-wrapper.d.ts.map +1 -0
- package/dist/tools/tool-wrapper.js +47 -0
- package/dist/tools/tool-wrapper.js.map +1 -0
- package/dist/types/index.d.ts +261 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/file-logger.d.ts +24 -0
- package/dist/utils/file-logger.d.ts.map +1 -0
- package/dist/utils/file-logger.js +117 -0
- package/dist/utils/file-logger.js.map +1 -0
- package/dist/utils/logger.d.ts +51 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +189 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/sanitizer.d.ts +13 -0
- package/dist/utils/sanitizer.d.ts.map +1 -0
- package/dist/utils/sanitizer.js +60 -0
- package/dist/utils/sanitizer.js.map +1 -0
- package/docs/EXAMPLES.md +171 -0
- package/docs/PROFILES.md +423 -0
- package/docs/SECURITY.md +190 -0
- package/docs/TOOLS.md +366 -0
- package/manifest.json +74 -0
- package/package.json +89 -0
- package/scripts/build-exe.ps1 +37 -0
- package/scripts/manage-service.ps1 +112 -0
- package/server.json +23 -0
- package/winhelm.config.example.json +30 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { ConfigManager } from "../config/config-manager.js";
|
|
5
|
+
import { SseGateway } from "./sse-gateway.js";
|
|
6
|
+
import { StreamableGateway } from "./streamable-gateway.js";
|
|
7
|
+
import { logger } from "../utils/logger.js";
|
|
8
|
+
import { fileLogger } from "../utils/file-logger.js";
|
|
9
|
+
import { taskManager } from "../engine/task-manager.js";
|
|
10
|
+
import { RateLimiter } from "./rate-limiter.js";
|
|
11
|
+
import { getDashboardHtml } from "./dashboard-html.js";
|
|
12
|
+
import { getFilePreviewHtml } from "./preview-html.js";
|
|
13
|
+
import { getGpuInfo, getSystemInfo } from "../engine/system.js";
|
|
14
|
+
export function safeCompare(a, b) {
|
|
15
|
+
const bufA = Buffer.from(a);
|
|
16
|
+
const bufB = Buffer.from(b);
|
|
17
|
+
if (bufA.length !== bufB.length)
|
|
18
|
+
return false;
|
|
19
|
+
return crypto.timingSafeEqual(bufA, bufB);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validate Host header against loopback, bound interface, and allowed hosts whitelist (DNS Rebinding protection)
|
|
23
|
+
*/
|
|
24
|
+
export function isHostAllowed(hostHeader, allowedHosts = ["localhost", "127.0.0.1", "[::1]", "*.ts.net"], bindHost = "127.0.0.1") {
|
|
25
|
+
if (!hostHeader)
|
|
26
|
+
return false;
|
|
27
|
+
let hostname;
|
|
28
|
+
const cleanHeader = hostHeader.trim().toLowerCase();
|
|
29
|
+
// Handle IPv6 literal with brackets [::1]:port or naked ::1
|
|
30
|
+
if (cleanHeader.startsWith("[")) {
|
|
31
|
+
const endBracket = cleanHeader.indexOf("]");
|
|
32
|
+
hostname = endBracket !== -1 ? cleanHeader.slice(1, endBracket) : cleanHeader;
|
|
33
|
+
}
|
|
34
|
+
else if (cleanHeader === "::1" || cleanHeader.startsWith("::1:")) {
|
|
35
|
+
hostname = "::1";
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
hostname = cleanHeader.split(":")[0];
|
|
39
|
+
}
|
|
40
|
+
// Always permit loopback interfaces
|
|
41
|
+
if (hostname === "localhost" ||
|
|
42
|
+
hostname === "127.0.0.1" ||
|
|
43
|
+
hostname === "::1" ||
|
|
44
|
+
hostname === "[::1]") {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
// Permit explicitly configured bind host interface
|
|
48
|
+
if (bindHost && hostname === bindHost.toLowerCase()) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
// Check allowed hosts whitelist (supports wildcard prefixes like *.ts.net, *.example.com)
|
|
52
|
+
return allowedHosts.some((pattern) => {
|
|
53
|
+
const p = pattern.trim().toLowerCase();
|
|
54
|
+
if (p === "*")
|
|
55
|
+
return true;
|
|
56
|
+
if (p.startsWith("*.")) {
|
|
57
|
+
const suffix = p.slice(1);
|
|
58
|
+
return hostname.endsWith(suffix) || hostname === p.slice(2);
|
|
59
|
+
}
|
|
60
|
+
return hostname === p;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function createServer(options) {
|
|
64
|
+
const app = express();
|
|
65
|
+
// Trust proxy for loopback reverse proxies (Tailscale Funnel, local tunnel clients)
|
|
66
|
+
app.set("trust proxy", "loopback");
|
|
67
|
+
const configManager = ConfigManager.getInstance();
|
|
68
|
+
const config = configManager.getConfig();
|
|
69
|
+
const authToken = options.authToken ?? config.authToken;
|
|
70
|
+
// 1. Global Security Headers (Prevent MIME sniffing and Clickjacking)
|
|
71
|
+
app.use((_req, res, next) => {
|
|
72
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
73
|
+
res.setHeader("X-Frame-Options", "DENY");
|
|
74
|
+
next();
|
|
75
|
+
});
|
|
76
|
+
// 2. Host Header Validation Guard (Mitigates DNS Rebinding attacks)
|
|
77
|
+
app.use((req, res, next) => {
|
|
78
|
+
const hostHeader = req.get("host") || "";
|
|
79
|
+
const allowed = config.allowedHosts || ["localhost", "127.0.0.1", "[::1]", "*.ts.net"];
|
|
80
|
+
if (!isHostAllowed(hostHeader, allowed, options.host)) {
|
|
81
|
+
logger.security("Blocked request with unauthorized Host header (DNS Rebinding protection)", `Host: ${hostHeader} | IP: ${req.ip}`);
|
|
82
|
+
res.status(403).json({
|
|
83
|
+
error: "Forbidden: Host header not permitted by WinHelm security policy",
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
next();
|
|
88
|
+
});
|
|
89
|
+
// 3. Remote Proxy / Tunnel Security Guard (Fail-closed when no auth token is configured)
|
|
90
|
+
if (!authToken) {
|
|
91
|
+
app.use((req, res, next) => {
|
|
92
|
+
if (req.headers["x-forwarded-for"] || req.headers["forwarded"]) {
|
|
93
|
+
logger.security("Blocked unauthenticated remote proxy/tunnel request", `Path: ${req.path} | IP: ${req.ip} | X-Forwarded-For: ${req.headers["x-forwarded-for"]}`);
|
|
94
|
+
res.status(403).json({
|
|
95
|
+
error: "Forbidden: Remote proxy/tunnel access is strictly blocked when no authentication token is configured. Set an authToken or --auth.",
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
next();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const sseGateway = new SseGateway(config.sessionIdleTimeoutMs, config.maxConcurrentSessions);
|
|
103
|
+
const streamableGateway = new StreamableGateway(config.sessionIdleTimeoutMs, config.maxConcurrentSessions);
|
|
104
|
+
const rateLimiter = new RateLimiter();
|
|
105
|
+
// Cache system info for 2.5s to avoid PowerShell overhead during continuous polling
|
|
106
|
+
let cachedSystemInfo = null;
|
|
107
|
+
let lastSystemInfoFetch = 0;
|
|
108
|
+
async function getCachedSystemInfo() {
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
if (!cachedSystemInfo || now - lastSystemInfoFetch > 2500) {
|
|
111
|
+
try {
|
|
112
|
+
cachedSystemInfo = await getSystemInfo();
|
|
113
|
+
lastSystemInfoFetch = now;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Fallback to previous cache if exists
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return cachedSystemInfo || {};
|
|
120
|
+
}
|
|
121
|
+
// Cache GPU info for 5s
|
|
122
|
+
let cachedGpuInfo = null;
|
|
123
|
+
let lastGpuInfoFetch = 0;
|
|
124
|
+
async function getCachedGpuInfo() {
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
if (!cachedGpuInfo || now - lastGpuInfoFetch > 5000) {
|
|
127
|
+
try {
|
|
128
|
+
cachedGpuInfo = await getGpuInfo();
|
|
129
|
+
lastGpuInfoFetch = now;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Fallback
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return cachedGpuInfo;
|
|
136
|
+
}
|
|
137
|
+
// 4. CORS configuration: restricts web browser cross-origin requests
|
|
138
|
+
const corsOption = config.corsOrigins;
|
|
139
|
+
if (corsOption === true || corsOption === "*") {
|
|
140
|
+
app.use(cors({ origin: "*" }));
|
|
141
|
+
}
|
|
142
|
+
else if (Array.isArray(corsOption) || typeof corsOption === "string") {
|
|
143
|
+
app.use(cors({ origin: corsOption }));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Default: permit local requests and loopback origins
|
|
147
|
+
app.use(cors({
|
|
148
|
+
origin: (origin, callback) => {
|
|
149
|
+
if (!origin)
|
|
150
|
+
return callback(null, true);
|
|
151
|
+
if (/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin)) {
|
|
152
|
+
return callback(null, true);
|
|
153
|
+
}
|
|
154
|
+
return callback(new Error("CORS origin not allowed by WinHelm security policy"));
|
|
155
|
+
},
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
// Detailed Request Logger Middleware
|
|
159
|
+
app.use((req, res, next) => {
|
|
160
|
+
const start = Date.now();
|
|
161
|
+
res.on("finish", () => {
|
|
162
|
+
const duration = Date.now() - start;
|
|
163
|
+
const isInternalPolling = req.path === "/health" ||
|
|
164
|
+
req.path.startsWith("/api/monitor/") ||
|
|
165
|
+
req.path === "/" ||
|
|
166
|
+
req.path === "/dashboard";
|
|
167
|
+
// Only print terminal logs for MCP protocol and external actions, keeping terminal clean & stable
|
|
168
|
+
if (!isInternalPolling) {
|
|
169
|
+
const sessionId = (req.headers["mcp-session-id"] || req.query.sessionId);
|
|
170
|
+
const clientIp = req.ip || req.socket.remoteAddress || "-";
|
|
171
|
+
logger.req(req.method, req.originalUrl, res.statusCode, duration, clientIp, sessionId);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
next();
|
|
175
|
+
});
|
|
176
|
+
// Rate Limiting Guard
|
|
177
|
+
app.use(rateLimiter.middleware());
|
|
178
|
+
// Bearer Authentication Guard
|
|
179
|
+
if (authToken) {
|
|
180
|
+
logger.security("Bearer authentication guard is ACTIVE");
|
|
181
|
+
app.use((req, res, next) => {
|
|
182
|
+
// Allow only lightweight public endpoints: health check and basic dashboard shell
|
|
183
|
+
if (req.path === "/health" || req.path === "/" || req.path === "/dashboard") {
|
|
184
|
+
return next();
|
|
185
|
+
}
|
|
186
|
+
// 1. Check standard Authorization header (timing-safe comparison)
|
|
187
|
+
const authHeader = req.headers.authorization;
|
|
188
|
+
if (authHeader && safeCompare(authHeader, `Bearer ${authToken}`)) {
|
|
189
|
+
return next();
|
|
190
|
+
}
|
|
191
|
+
// 2. Fallback for URL query parameter (Claude.ai custom connectors without header UI, browser dashboard, preview) or cookie
|
|
192
|
+
const queryToken = (req.query.token || req.query.auth);
|
|
193
|
+
const cookieHeader = req.headers.cookie;
|
|
194
|
+
let cookieToken;
|
|
195
|
+
if (cookieHeader) {
|
|
196
|
+
const match = cookieHeader.match(/(?:^|;\s*)(?:authToken|token|auth)=([^;]+)/);
|
|
197
|
+
if (match) {
|
|
198
|
+
cookieToken = decodeURIComponent(match[1]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const browserToken = queryToken || cookieToken;
|
|
202
|
+
if (browserToken && safeCompare(browserToken, authToken)) {
|
|
203
|
+
if (queryToken && !cookieToken) {
|
|
204
|
+
const isSecure = req.secure || req.headers["x-forwarded-proto"] === "https";
|
|
205
|
+
res.setHeader("Set-Cookie", `authToken=${encodeURIComponent(queryToken)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=2592000${isSecure ? "; Secure" : ""}`);
|
|
206
|
+
}
|
|
207
|
+
return next();
|
|
208
|
+
}
|
|
209
|
+
logger.security("Unauthorized request blocked", `Path: ${req.path} | IP: ${req.ip}`);
|
|
210
|
+
res.status(401).json({ error: "Unauthorized: Invalid or missing Bearer token" });
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
logger.info("Public network mode active (No auth token set)");
|
|
215
|
+
}
|
|
216
|
+
// Web Monitor Dashboard
|
|
217
|
+
app.get(["/", "/dashboard"], (_req, res) => {
|
|
218
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
219
|
+
const activeProfile = configManager.getConfig().profile || "full";
|
|
220
|
+
res.send(getDashboardHtml(activeProfile));
|
|
221
|
+
});
|
|
222
|
+
// Interactive File & Markdown Previewer
|
|
223
|
+
app.get("/preview", async (req, res) => {
|
|
224
|
+
const filePath = req.query.path;
|
|
225
|
+
if (!filePath) {
|
|
226
|
+
res.status(400).send("Missing ?path= parameter");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const html = await getFilePreviewHtml(filePath);
|
|
230
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
231
|
+
res.setHeader("Content-Security-Policy", "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; frame-src data:; connect-src 'none'; form-action 'none'; frame-ancestors 'none';");
|
|
232
|
+
res.send(html);
|
|
233
|
+
});
|
|
234
|
+
// Health check endpoint
|
|
235
|
+
app.get("/health", (_req, res) => {
|
|
236
|
+
res.json({
|
|
237
|
+
status: "ok",
|
|
238
|
+
server: "winhelm-mcp",
|
|
239
|
+
version: "1.1.0",
|
|
240
|
+
activeSessions: {
|
|
241
|
+
sse: sseGateway.getActiveSessionCount(),
|
|
242
|
+
streamableHttp: streamableGateway.getActiveSessionCount(),
|
|
243
|
+
},
|
|
244
|
+
uptimeSeconds: Math.floor(process.uptime()),
|
|
245
|
+
timestamp: new Date().toISOString(),
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
// Monitor Stats API
|
|
249
|
+
app.get("/api/monitor/stats", async (_req, res) => {
|
|
250
|
+
const sys = await getCachedSystemInfo();
|
|
251
|
+
const gpu = await getCachedGpuInfo();
|
|
252
|
+
res.json({
|
|
253
|
+
server: "winhelm-mcp",
|
|
254
|
+
version: "1.1.0",
|
|
255
|
+
port: options.port,
|
|
256
|
+
host: options.host,
|
|
257
|
+
readOnly: configManager.isReadOnly(),
|
|
258
|
+
uptimeSeconds: Math.floor(process.uptime()),
|
|
259
|
+
activeSessions: {
|
|
260
|
+
sse: sseGateway.getActiveSessionCount(),
|
|
261
|
+
streamableHttp: streamableGateway.getActiveSessionCount(),
|
|
262
|
+
},
|
|
263
|
+
backgroundTasks: taskManager.listTasks().length,
|
|
264
|
+
system: sys,
|
|
265
|
+
gpu: gpu,
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
// Monitor Logs API
|
|
269
|
+
app.get("/api/monitor/logs", (_req, res) => {
|
|
270
|
+
res.json({
|
|
271
|
+
logs: logger.getRecentLogs(100),
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
app.delete("/api/monitor/logs", (_req, res) => {
|
|
275
|
+
logger.clearLogs();
|
|
276
|
+
res.json({ success: true, message: "Logs cleared" });
|
|
277
|
+
});
|
|
278
|
+
// Audit Logs Export API (JSON or CSV)
|
|
279
|
+
app.get("/api/monitor/export", async (req, res) => {
|
|
280
|
+
const format = req.query.format === "csv" ? "csv" : "json";
|
|
281
|
+
const data = await fileLogger.exportLogs(format);
|
|
282
|
+
if (format === "csv") {
|
|
283
|
+
res.setHeader("Content-Type", "text/csv");
|
|
284
|
+
res.setHeader("Content-Disposition", 'attachment; filename="winhelm-audit.csv"');
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
res.setHeader("Content-Type", "application/json");
|
|
288
|
+
res.setHeader("Content-Disposition", 'attachment; filename="winhelm-audit.json"');
|
|
289
|
+
}
|
|
290
|
+
res.send(data);
|
|
291
|
+
});
|
|
292
|
+
// Streamable HTTP Endpoint (/mcp)
|
|
293
|
+
app.all("/mcp", (req, res) => streamableGateway.handleRequest(req, res));
|
|
294
|
+
// SSE Endpoints (/sse & /message)
|
|
295
|
+
app.get("/sse", (req, res) => sseGateway.handleSseConnect(req, res));
|
|
296
|
+
app.post("/message", (req, res) => sseGateway.handlePostMessage(req, res));
|
|
297
|
+
const start = () => {
|
|
298
|
+
return new Promise((resolve) => {
|
|
299
|
+
const server = app.listen(options.port, options.host, () => {
|
|
300
|
+
console.log(`\n======================================================`);
|
|
301
|
+
console.log(` 🚀 WinHelm MCP Server is running!`);
|
|
302
|
+
console.log(`======================================================`);
|
|
303
|
+
console.log(` - Web Monitor: http://localhost:${options.port}/`);
|
|
304
|
+
console.log(` - Streamable HTTP: http://${options.host}:${options.port}/mcp`);
|
|
305
|
+
console.log(` - SSE Endpoint: http://${options.host}:${options.port}/sse`);
|
|
306
|
+
console.log(` - SSE Message: http://${options.host}:${options.port}/message`);
|
|
307
|
+
console.log(` - Health Check: http://${options.host}:${options.port}/health`);
|
|
308
|
+
console.log(`======================================================\n`);
|
|
309
|
+
resolve(server);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
const stop = async (server) => {
|
|
314
|
+
// 1. Kill any active background tasks
|
|
315
|
+
taskManager.killAll();
|
|
316
|
+
// 2. Close MCP Sessions
|
|
317
|
+
await Promise.all([
|
|
318
|
+
sseGateway.closeAll().catch(() => { }),
|
|
319
|
+
streamableGateway.closeAll().catch(() => { }),
|
|
320
|
+
]);
|
|
321
|
+
// 3. Close network sockets
|
|
322
|
+
return new Promise((resolve) => {
|
|
323
|
+
if (typeof server.closeAllConnections === "function") {
|
|
324
|
+
server.closeAllConnections();
|
|
325
|
+
}
|
|
326
|
+
server.close(() => {
|
|
327
|
+
resolve();
|
|
328
|
+
});
|
|
329
|
+
const safetyTimer = setTimeout(() => {
|
|
330
|
+
resolve();
|
|
331
|
+
}, 800);
|
|
332
|
+
safetyTimer.unref();
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
return { app, start, stop };
|
|
336
|
+
}
|
|
337
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/gateway/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
4
|
+
interface SseSession {
|
|
5
|
+
transport: SSEServerTransport;
|
|
6
|
+
mcpServer: McpServer;
|
|
7
|
+
lastActivityAt: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class SseGateway {
|
|
10
|
+
private sessions;
|
|
11
|
+
private cleanupTimer;
|
|
12
|
+
private idleTimeoutMs;
|
|
13
|
+
private maxSessions;
|
|
14
|
+
constructor(idleTimeoutMs?: number, maxSessions?: number);
|
|
15
|
+
private createMcpServerInstance;
|
|
16
|
+
/**
|
|
17
|
+
* Evict least recently used session if capacity is reached
|
|
18
|
+
*/
|
|
19
|
+
private evictOldestIfNeeded;
|
|
20
|
+
/**
|
|
21
|
+
* Cleanup sessions that have been idle longer than idleTimeoutMs
|
|
22
|
+
*/
|
|
23
|
+
cleanupIdleSessions(): number;
|
|
24
|
+
/**
|
|
25
|
+
* Handle GET /sse - Client initializes SSE stream
|
|
26
|
+
*/
|
|
27
|
+
handleSseConnect(req: Request, res: Response): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Handle POST /message - Client sends JSON-RPC message for SSE session
|
|
30
|
+
*/
|
|
31
|
+
handlePostMessage(req: Request, res: Response): Promise<void>;
|
|
32
|
+
getActiveSessionCount(): number;
|
|
33
|
+
getSession(sessionId: string): SseSession | undefined;
|
|
34
|
+
setSessionLastActivity(sessionId: string, timestamp: number): boolean;
|
|
35
|
+
registerSessionForTest(sessionId: string, session?: {
|
|
36
|
+
transport?: any;
|
|
37
|
+
mcpServer?: any;
|
|
38
|
+
lastActivityAt?: number;
|
|
39
|
+
}): void;
|
|
40
|
+
closeAll(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=sse-gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-gateway.d.ts","sourceRoot":"","sources":["../../src/gateway/sse-gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAG7E,UAAU,UAAU;IAClB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAS;IAE5B,YAAY,aAAa,SAAiB,EAAE,WAAW,SAAM,EAW5D;IAED,OAAO,CAAC,uBAAuB;IAS/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwB3B;;OAEG;IACI,mBAAmB,IAAI,MAAM,CAWnC;IAED;;OAEG;IACU,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BxE;IAED;;OAEG;IACU,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBzE;IAEM,qBAAqB,IAAI,MAAM,CAErC;IAEM,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAE3D;IAEM,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAO3E;IAEM,sBAAsB,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,GAAG,CAAC;QAAC,SAAS,CAAC,EAAE,GAAG,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAO,GAC1E,IAAI,CAON;IAEY,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAarC;CACF"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
3
|
+
import { registerAllTools } from "../tools/index.js";
|
|
4
|
+
export class SseGateway {
|
|
5
|
+
sessions = new Map();
|
|
6
|
+
cleanupTimer = null;
|
|
7
|
+
idleTimeoutMs;
|
|
8
|
+
maxSessions;
|
|
9
|
+
constructor(idleTimeoutMs = 45 * 60 * 1000, maxSessions = 100) {
|
|
10
|
+
this.idleTimeoutMs = idleTimeoutMs;
|
|
11
|
+
this.maxSessions = maxSessions;
|
|
12
|
+
const intervalMs = Math.min(60000, Math.max(1000, Math.floor(this.idleTimeoutMs / 2)));
|
|
13
|
+
this.cleanupTimer = setInterval(() => {
|
|
14
|
+
this.cleanupIdleSessions();
|
|
15
|
+
}, intervalMs);
|
|
16
|
+
if (this.cleanupTimer && this.cleanupTimer.unref) {
|
|
17
|
+
this.cleanupTimer.unref();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
createMcpServerInstance() {
|
|
21
|
+
const server = new McpServer({
|
|
22
|
+
name: "winhelm-mcp",
|
|
23
|
+
version: "1.1.0",
|
|
24
|
+
});
|
|
25
|
+
registerAllTools(server);
|
|
26
|
+
return server;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Evict least recently used session if capacity is reached
|
|
30
|
+
*/
|
|
31
|
+
evictOldestIfNeeded() {
|
|
32
|
+
while (this.sessions.size >= this.maxSessions) {
|
|
33
|
+
let oldestKey = null;
|
|
34
|
+
let oldestTime = Infinity;
|
|
35
|
+
for (const [key, session] of this.sessions.entries()) {
|
|
36
|
+
if (session.lastActivityAt < oldestTime) {
|
|
37
|
+
oldestTime = session.lastActivityAt;
|
|
38
|
+
oldestKey = key;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (oldestKey) {
|
|
42
|
+
const session = this.sessions.get(oldestKey);
|
|
43
|
+
this.sessions.delete(oldestKey);
|
|
44
|
+
if (session) {
|
|
45
|
+
session.mcpServer.close().catch(() => { });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Cleanup sessions that have been idle longer than idleTimeoutMs
|
|
55
|
+
*/
|
|
56
|
+
cleanupIdleSessions() {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
let count = 0;
|
|
59
|
+
for (const [key, session] of this.sessions.entries()) {
|
|
60
|
+
if (now - session.lastActivityAt > this.idleTimeoutMs) {
|
|
61
|
+
this.sessions.delete(key);
|
|
62
|
+
session.mcpServer.close().catch(() => { });
|
|
63
|
+
count++;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return count;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Handle GET /sse - Client initializes SSE stream
|
|
70
|
+
*/
|
|
71
|
+
async handleSseConnect(req, res) {
|
|
72
|
+
console.log(`[SSE] New SSE client connecting from ${req.ip}`);
|
|
73
|
+
try {
|
|
74
|
+
this.evictOldestIfNeeded();
|
|
75
|
+
const mcpServer = this.createMcpServerInstance();
|
|
76
|
+
const transport = new SSEServerTransport("/message", res);
|
|
77
|
+
await mcpServer.connect(transport);
|
|
78
|
+
const sessionId = transport.sessionId;
|
|
79
|
+
this.sessions.set(sessionId, {
|
|
80
|
+
transport,
|
|
81
|
+
mcpServer,
|
|
82
|
+
lastActivityAt: Date.now(),
|
|
83
|
+
});
|
|
84
|
+
console.log(`[SSE] Session initialized: ${sessionId}`);
|
|
85
|
+
req.on("close", () => {
|
|
86
|
+
console.log(`[SSE] Client disconnected (session: ${sessionId})`);
|
|
87
|
+
this.sessions.delete(sessionId);
|
|
88
|
+
mcpServer.close().catch(() => { });
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
console.error("[SSE] Failed to establish SSE connection:", err);
|
|
93
|
+
if (!res.headersSent) {
|
|
94
|
+
res.status(500).send("Failed to initialize SSE connection");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Handle POST /message - Client sends JSON-RPC message for SSE session
|
|
100
|
+
*/
|
|
101
|
+
async handlePostMessage(req, res) {
|
|
102
|
+
const sessionId = req.query.sessionId;
|
|
103
|
+
if (!sessionId) {
|
|
104
|
+
res.status(400).send("Missing sessionId parameter");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const session = this.sessions.get(sessionId);
|
|
108
|
+
if (!session) {
|
|
109
|
+
res.status(404).send(`Active session not found for ID: ${sessionId}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
session.lastActivityAt = Date.now();
|
|
113
|
+
try {
|
|
114
|
+
await session.transport.handlePostMessage(req, res);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
console.error(`[SSE] Error processing POST message for session ${sessionId}:`, err);
|
|
118
|
+
if (!res.headersSent) {
|
|
119
|
+
res.status(500).send("Error handling message");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
getActiveSessionCount() {
|
|
124
|
+
return this.sessions.size;
|
|
125
|
+
}
|
|
126
|
+
getSession(sessionId) {
|
|
127
|
+
return this.sessions.get(sessionId);
|
|
128
|
+
}
|
|
129
|
+
setSessionLastActivity(sessionId, timestamp) {
|
|
130
|
+
const session = this.sessions.get(sessionId);
|
|
131
|
+
if (session) {
|
|
132
|
+
session.lastActivityAt = timestamp;
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
registerSessionForTest(sessionId, session = {}) {
|
|
138
|
+
this.evictOldestIfNeeded();
|
|
139
|
+
this.sessions.set(sessionId, {
|
|
140
|
+
transport: session.transport || {},
|
|
141
|
+
mcpServer: session.mcpServer || { close: async () => { } },
|
|
142
|
+
lastActivityAt: session.lastActivityAt ?? Date.now(),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async closeAll() {
|
|
146
|
+
if (this.cleanupTimer) {
|
|
147
|
+
clearInterval(this.cleanupTimer);
|
|
148
|
+
this.cleanupTimer = null;
|
|
149
|
+
}
|
|
150
|
+
for (const session of this.sessions.values()) {
|
|
151
|
+
try {
|
|
152
|
+
await session.mcpServer.close();
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// ignore
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
this.sessions.clear();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=sse-gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-gateway.js","sourceRoot":"","sources":["../../src/gateway/sse-gateway.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAQrD,MAAM,OAAO,UAAU;IACb,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;IACzC,YAAY,GAA0B,IAAI,CAAC;IAC3C,aAAa,CAAS;IACtB,WAAW,CAAS;IAE5B,YAAY,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,WAAW,GAAG,GAAG;QAC3D,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvF,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;YACnC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC7B,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YACjD,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAEO,uBAAuB;QAC7B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;YAC3B,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QACH,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,mBAAmB;QACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC9C,IAAI,SAAS,GAAkB,IAAI,CAAC;YACpC,IAAI,UAAU,GAAG,QAAQ,CAAC;YAE1B,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;gBACrD,IAAI,OAAO,CAAC,cAAc,GAAG,UAAU,EAAE,CAAC;oBACxC,UAAU,GAAG,OAAO,CAAC,cAAc,CAAC;oBACpC,SAAS,GAAG,GAAG,CAAC;gBAClB,CAAC;YACH,CAAC;YAED,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC7C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAChC,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC5C,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACI,mBAAmB;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YACrD,IAAI,GAAG,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBACtD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC1B,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC1C,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,gBAAgB,CAAC,GAAY,EAAE,GAAa;QACvD,OAAO,CAAC,GAAG,CAAC,wCAAwC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAE3B,MAAM,SAAS,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACjD,MAAM,SAAS,GAAG,IAAI,kBAAkB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAC1D,MAAM,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAEnC,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;YACtC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE;gBAC3B,SAAS;gBACT,SAAS;gBACT,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;aAC3B,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,8BAA8B,SAAS,EAAE,CAAC,CAAC;YAEvD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnB,OAAO,CAAC,GAAG,CAAC,uCAAuC,SAAS,GAAG,CAAC,CAAC;gBACjE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAChC,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACpC,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAAC;YAChE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,iBAAiB,CAAC,GAAY,EAAE,GAAa;QACxD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,SAAmB,CAAC;QAChD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;YACtE,OAAO;QACT,CAAC;QAED,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,mDAAmD,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;YACpF,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;IACH,CAAC;IAEM,qBAAqB;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAEM,UAAU,CAAC,SAAiB;QACjC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAEM,sBAAsB,CAAC,SAAiB,EAAE,SAAiB;QAChE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;YACnC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEM,sBAAsB,CAC3B,SAAiB,EACjB,OAAO,GAAkE,EAAE;QAE3E,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE;YAC3B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAK,EAAU;YAC3C,SAAS,EAAE,OAAO,CAAC,SAAS,IAAK,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAU;YAClE,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC,GAAG,EAAE;SACrD,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,QAAQ;QACnB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;YAClC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
interface StreamableSession {
|
|
5
|
+
transport: StreamableHTTPServerTransport;
|
|
6
|
+
mcpServer: McpServer;
|
|
7
|
+
lastActivityAt: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class StreamableGateway {
|
|
10
|
+
private sessions;
|
|
11
|
+
private cleanupTimer;
|
|
12
|
+
private idleTimeoutMs;
|
|
13
|
+
private maxSessions;
|
|
14
|
+
constructor(idleTimeoutMs?: number, maxSessions?: number);
|
|
15
|
+
private createMcpServerInstance;
|
|
16
|
+
/**
|
|
17
|
+
* Evict the least recently used session if capacity is reached
|
|
18
|
+
*/
|
|
19
|
+
private evictOldestIfNeeded;
|
|
20
|
+
/**
|
|
21
|
+
* Cleanup sessions that have been idle longer than idleTimeoutMs
|
|
22
|
+
*/
|
|
23
|
+
cleanupIdleSessions(): number;
|
|
24
|
+
/**
|
|
25
|
+
* Handle ALL /mcp - Streamable HTTP transport
|
|
26
|
+
*/
|
|
27
|
+
handleRequest(req: Request, res: Response): Promise<void>;
|
|
28
|
+
getActiveSessionCount(): number;
|
|
29
|
+
getSession(sessionId: string): StreamableSession | undefined;
|
|
30
|
+
setSessionLastActivity(sessionId: string, timestamp: number): boolean;
|
|
31
|
+
registerSessionForTest(sessionId: string, session?: {
|
|
32
|
+
transport?: any;
|
|
33
|
+
mcpServer?: any;
|
|
34
|
+
lastActivityAt?: number;
|
|
35
|
+
}): void;
|
|
36
|
+
closeAll(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=streamable-gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamable-gateway.d.ts","sourceRoot":"","sources":["../../src/gateway/streamable-gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAGnG,UAAU,iBAAiB;IACzB,SAAS,EAAE,6BAA6B,CAAC;IACzC,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAS;IAE5B,YAAY,aAAa,SAAiB,EAAE,WAAW,SAAM,EAY5D;IAED,OAAO,CAAC,uBAAuB;IAS/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwB3B;;OAEG;IACI,mBAAmB,IAAI,MAAM,CAWnC;IAED;;OAEG;IACU,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAsErE;IAEM,qBAAqB,IAAI,MAAM,CAErC;IAEM,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAElE;IAEM,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAO3E;IAEM,sBAAsB,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,GAAG,CAAC;QAAC,SAAS,CAAC,EAAE,GAAG,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAO,GAC1E,IAAI,CAON;IAEY,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAarC;CACF"}
|