@mgsoftwarebv/mg-dashboard-mcp 3.4.3 → 3.5.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/dist/index.d.ts +7 -0
- package/dist/index.js +124 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
|
-
import { existsSync, readFileSync } from 'fs';
|
|
4
|
-
import { join } from 'path';
|
|
3
|
+
import { existsSync, statSync, readFileSync } from 'fs';
|
|
4
|
+
import { join, isAbsolute } from 'path';
|
|
5
5
|
import { Client as Client$1 } from '@modelcontextprotocol/sdk/client/index.js';
|
|
6
6
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
7
7
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
8
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
|
-
import { ListToolsRequestSchema, CallToolRequestSchema, isInitializeRequest, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema,
|
|
9
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, isInitializeRequest, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListToolsResultSchema, CallToolResultSchema, ListPromptsResultSchema, GetPromptResultSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, EmptyResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
10
10
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
11
11
|
import { createServer } from 'http';
|
|
12
12
|
import { randomUUID, createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
@@ -187,10 +187,6 @@ async function runBridge(handshake, proxyUrl2, refresh) {
|
|
|
187
187
|
ListResourceTemplatesRequestSchema,
|
|
188
188
|
forwardRequest(ListResourceTemplatesResultSchema)
|
|
189
189
|
);
|
|
190
|
-
stdioServer.setRequestHandler(
|
|
191
|
-
CompleteRequestSchema,
|
|
192
|
-
forwardRequest(CompleteResultSchema)
|
|
193
|
-
);
|
|
194
190
|
stdioServer.setRequestHandler(
|
|
195
191
|
SubscribeRequestSchema,
|
|
196
192
|
forwardRequest(EmptyResultSchema)
|
|
@@ -2009,7 +2005,8 @@ function sshExecViaProxy(proxyOpts, targetOpts, command) {
|
|
|
2009
2005
|
});
|
|
2010
2006
|
});
|
|
2011
2007
|
}
|
|
2012
|
-
function connectSshClient(opts, proxy, readyTimeout = 6e4) {
|
|
2008
|
+
function connectSshClient(opts, proxy, readyTimeout = 6e4, extraConnect) {
|
|
2009
|
+
const compress = extraConnect?.compress;
|
|
2013
2010
|
if (!proxy) {
|
|
2014
2011
|
return new Promise((resolve, reject) => {
|
|
2015
2012
|
const ssh = new Client();
|
|
@@ -2022,7 +2019,8 @@ function connectSshClient(opts, proxy, readyTimeout = 6e4) {
|
|
|
2022
2019
|
password: opts.password,
|
|
2023
2020
|
privateKey: opts.privateKey,
|
|
2024
2021
|
passphrase: opts.passphrase,
|
|
2025
|
-
readyTimeout
|
|
2022
|
+
readyTimeout,
|
|
2023
|
+
...compress ? { compress } : {}
|
|
2026
2024
|
});
|
|
2027
2025
|
});
|
|
2028
2026
|
}
|
|
@@ -2056,7 +2054,8 @@ function connectSshClient(opts, proxy, readyTimeout = 6e4) {
|
|
|
2056
2054
|
password: opts.password,
|
|
2057
2055
|
privateKey: opts.privateKey,
|
|
2058
2056
|
passphrase: opts.passphrase,
|
|
2059
|
-
readyTimeout
|
|
2057
|
+
readyTimeout,
|
|
2058
|
+
...compress ? { compress } : {}
|
|
2060
2059
|
});
|
|
2061
2060
|
});
|
|
2062
2061
|
});
|
|
@@ -2196,46 +2195,129 @@ async function sftpRead(opts, filePath, proxy) {
|
|
|
2196
2195
|
return `Error: ${e.message}`;
|
|
2197
2196
|
}
|
|
2198
2197
|
}
|
|
2199
|
-
|
|
2198
|
+
var SFTP_FASTPUT_CONCURRENCY = 64;
|
|
2199
|
+
var SFTP_FASTPUT_CHUNK_SIZE = 65536;
|
|
2200
|
+
var SFTP_IDLE_TIMEOUT_MS = 12e4;
|
|
2201
|
+
var SFTP_INLINE_TIMEOUT_MS = 6e4;
|
|
2202
|
+
var SFTP_PROGRESS_LOG_BYTES = 50 * 1024 * 1024;
|
|
2203
|
+
function formatBytes(bytes) {
|
|
2204
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2205
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2206
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
2207
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
2208
|
+
}
|
|
2209
|
+
async function sftpWrite(opts, filePath, input, proxy) {
|
|
2200
2210
|
const safe = sanitizePath(filePath);
|
|
2201
2211
|
assertWritablePath(safe);
|
|
2212
|
+
let mode;
|
|
2213
|
+
let inlineBuffer;
|
|
2214
|
+
let localPath;
|
|
2215
|
+
let expectedBytes = 0;
|
|
2216
|
+
if ("content" in input && typeof input.content === "string") {
|
|
2217
|
+
mode = "content";
|
|
2218
|
+
inlineBuffer = Buffer.from(input.content, "utf-8");
|
|
2219
|
+
expectedBytes = inlineBuffer.length;
|
|
2220
|
+
} else if ("sourcePath" in input && typeof input.sourcePath === "string") {
|
|
2221
|
+
mode = "sourcePath";
|
|
2222
|
+
localPath = input.sourcePath;
|
|
2223
|
+
if (!isAbsolute(localPath)) return `Error: sourcePath must be an absolute path: ${localPath}`;
|
|
2224
|
+
if (!existsSync(localPath)) return `Error: sourcePath does not exist: ${localPath}`;
|
|
2225
|
+
const st = statSync(localPath);
|
|
2226
|
+
if (!st.isFile()) return `Error: sourcePath is not a regular file: ${localPath}`;
|
|
2227
|
+
expectedBytes = st.size;
|
|
2228
|
+
} else {
|
|
2229
|
+
return "Error: sftp-write requires exactly one of: content (string) or sourcePath (absolute local path)";
|
|
2230
|
+
}
|
|
2231
|
+
const compress = mode === "content";
|
|
2232
|
+
const startedAt = Date.now();
|
|
2202
2233
|
let cleanup;
|
|
2203
2234
|
try {
|
|
2204
|
-
const { client, cleanup: c } = await connectSshClient(opts, proxy, 6e4);
|
|
2235
|
+
const { client, cleanup: c } = await connectSshClient(opts, proxy, 6e4, { compress });
|
|
2205
2236
|
cleanup = c;
|
|
2206
2237
|
return await new Promise((resolve) => {
|
|
2207
|
-
|
|
2238
|
+
let resolved = false;
|
|
2239
|
+
let bytesWritten = 0;
|
|
2240
|
+
const finish = (msg) => {
|
|
2241
|
+
if (resolved) return;
|
|
2242
|
+
resolved = true;
|
|
2243
|
+
watchdog?.cancel();
|
|
2244
|
+
wallTimer && clearTimeout(wallTimer);
|
|
2208
2245
|
cleanup?.();
|
|
2209
|
-
resolve("Error: timeout");
|
|
2210
2246
|
cleanup = void 0;
|
|
2211
|
-
|
|
2247
|
+
resolve(msg);
|
|
2248
|
+
};
|
|
2249
|
+
let watchdog;
|
|
2250
|
+
let wallTimer;
|
|
2251
|
+
if (mode === "sourcePath") {
|
|
2252
|
+
const armWatchdog = () => {
|
|
2253
|
+
let timer = setTimeout(
|
|
2254
|
+
() => finish(`Error: idle timeout (no SFTP progress for ${SFTP_IDLE_TIMEOUT_MS / 1e3}s, wrote ${formatBytes(bytesWritten)} of ${formatBytes(expectedBytes)})`),
|
|
2255
|
+
SFTP_IDLE_TIMEOUT_MS
|
|
2256
|
+
);
|
|
2257
|
+
return {
|
|
2258
|
+
reset: () => {
|
|
2259
|
+
clearTimeout(timer);
|
|
2260
|
+
timer = setTimeout(
|
|
2261
|
+
() => finish(`Error: idle timeout (no SFTP progress for ${SFTP_IDLE_TIMEOUT_MS / 1e3}s, wrote ${formatBytes(bytesWritten)} of ${formatBytes(expectedBytes)})`),
|
|
2262
|
+
SFTP_IDLE_TIMEOUT_MS
|
|
2263
|
+
);
|
|
2264
|
+
},
|
|
2265
|
+
cancel: () => clearTimeout(timer)
|
|
2266
|
+
};
|
|
2267
|
+
};
|
|
2268
|
+
watchdog = armWatchdog();
|
|
2269
|
+
} else {
|
|
2270
|
+
wallTimer = setTimeout(
|
|
2271
|
+
() => finish(`Error: timeout after ${SFTP_INLINE_TIMEOUT_MS / 1e3}s`),
|
|
2272
|
+
SFTP_INLINE_TIMEOUT_MS
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
2212
2275
|
client.sftp((err, sftp) => {
|
|
2213
|
-
if (err) {
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2276
|
+
if (err) return finish(`Error: ${err.message}`);
|
|
2277
|
+
if (mode === "content") {
|
|
2278
|
+
const ws = sftp.createWriteStream(safe, { mode: 420 });
|
|
2279
|
+
ws.on("error", (e) => finish(`Error: ${e.message}`));
|
|
2280
|
+
ws.on("close", () => {
|
|
2281
|
+
const elapsed = Date.now() - startedAt;
|
|
2282
|
+
finish(`Written ${expectedBytes} bytes to ${safe} in ${elapsed}ms`);
|
|
2283
|
+
});
|
|
2284
|
+
ws.end(inlineBuffer);
|
|
2218
2285
|
return;
|
|
2219
2286
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2287
|
+
let nextProgressLog = SFTP_PROGRESS_LOG_BYTES;
|
|
2288
|
+
sftp.fastPut(
|
|
2289
|
+
localPath,
|
|
2290
|
+
safe,
|
|
2291
|
+
{
|
|
2292
|
+
concurrency: SFTP_FASTPUT_CONCURRENCY,
|
|
2293
|
+
chunkSize: SFTP_FASTPUT_CHUNK_SIZE,
|
|
2294
|
+
mode: 420,
|
|
2295
|
+
fileSize: expectedBytes,
|
|
2296
|
+
step: (transferred) => {
|
|
2297
|
+
bytesWritten = transferred;
|
|
2298
|
+
watchdog?.reset();
|
|
2299
|
+
if (transferred >= nextProgressLog) {
|
|
2300
|
+
console.error(
|
|
2301
|
+
`[sftp-write] ${formatBytes(transferred)} / ${formatBytes(expectedBytes)} \u2192 ${safe}`
|
|
2302
|
+
);
|
|
2303
|
+
nextProgressLog += SFTP_PROGRESS_LOG_BYTES;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
},
|
|
2307
|
+
(fpErr) => {
|
|
2308
|
+
if (fpErr) return finish(`Error: ${fpErr.message}`);
|
|
2309
|
+
const elapsed = Date.now() - startedAt;
|
|
2310
|
+
const mbps = expectedBytes / (1024 * 1024) / Math.max(1e-3, elapsed / 1e3);
|
|
2311
|
+
finish(
|
|
2312
|
+
`Uploaded ${formatBytes(expectedBytes)} from ${localPath} to ${safe} in ${(elapsed / 1e3).toFixed(1)}s (${mbps.toFixed(1)} MB/s)`
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
);
|
|
2234
2316
|
});
|
|
2235
2317
|
});
|
|
2236
2318
|
} catch (e) {
|
|
2237
2319
|
cleanup?.();
|
|
2238
|
-
return `Error: ${e.message}`;
|
|
2320
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
2239
2321
|
}
|
|
2240
2322
|
}
|
|
2241
2323
|
async function sftpDelete(opts, filePath, proxy) {
|
|
@@ -2507,15 +2589,16 @@ var TOOLS = [
|
|
|
2507
2589
|
},
|
|
2508
2590
|
{
|
|
2509
2591
|
name: "sftp-write",
|
|
2510
|
-
description: "Write
|
|
2592
|
+
description: "Write a file to a remote server via SFTP. Two modes (provide exactly one of `content` or `sourcePath`):\n- `content` (string): inline UTF-8 text. Best for configs, scripts, small JSON. Practical max ~1 MB.\n- `sourcePath` (absolute local path): streams a local file with ssh2 fastPut (64 parallel pipelined writes, 64 KiB chunks). Handles GB-scale files (zips, dumps, builds) without going through the LLM. Only usable when the MCP server runs locally on your machine (i.e. not via --proxy-url).\nProtected system paths on the remote are blocked.",
|
|
2511
2593
|
inputSchema: {
|
|
2512
2594
|
type: "object",
|
|
2513
2595
|
properties: {
|
|
2514
2596
|
serverId: { type: "string", description: "UUID of the SSH server" },
|
|
2515
|
-
path: { type: "string", description: "
|
|
2516
|
-
content: { type: "string", description: "
|
|
2597
|
+
path: { type: "string", description: "Remote file path to write" },
|
|
2598
|
+
content: { type: "string", description: "Inline UTF-8 file content (mutually exclusive with sourcePath)" },
|
|
2599
|
+
sourcePath: { type: "string", description: "Absolute local file path to upload via fastPut (mutually exclusive with content). Use for files >1 MB or any binary." }
|
|
2517
2600
|
},
|
|
2518
|
-
required: ["serverId", "path"
|
|
2601
|
+
required: ["serverId", "path"]
|
|
2519
2602
|
}
|
|
2520
2603
|
},
|
|
2521
2604
|
{
|
|
@@ -2828,7 +2911,8 @@ ${result.stderr}`);
|
|
|
2828
2911
|
}
|
|
2829
2912
|
case "sftp-write": {
|
|
2830
2913
|
const { conn, proxy } = await getServerConnection(String(a.serverId));
|
|
2831
|
-
const
|
|
2914
|
+
const input = typeof a.sourcePath === "string" && a.sourcePath.length > 0 ? { sourcePath: a.sourcePath } : { content: String(a.content ?? "") };
|
|
2915
|
+
const result = await sftpWrite(conn, String(a.path), input, proxy);
|
|
2832
2916
|
return { content: [{ type: "text", text: result }] };
|
|
2833
2917
|
}
|
|
2834
2918
|
case "sftp-delete": {
|