@usex/mikrotik-mcp 3.37.0 → 3.38.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/cli.js +92 -83
- package/dist/index.js +2 -2
- package/dist/shared/{cli-9p1cxztb.js → cli-e484wzek.js} +2 -2
- package/dist/shared/{cli-fpfchpjm.js → cli-h0gydx8r.js} +1 -1
- package/dist/shared/{library-9pt0yeqy.js → library-nxh3rb8t.js} +2 -2
- package/dist/shared/{library-r6dx5hw2.js → library-q0q3kf6m.js} +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -84,7 +84,7 @@ import {
|
|
|
84
84
|
toggleAaaEntity,
|
|
85
85
|
updateAaaEntity,
|
|
86
86
|
writeBackup
|
|
87
|
-
} from "./shared/cli-
|
|
87
|
+
} from "./shared/cli-e484wzek.js";
|
|
88
88
|
|
|
89
89
|
// src/cli.ts
|
|
90
90
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -1948,6 +1948,90 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
1948
1948
|
const bearer = req.headers.get("authorization")?.replace(/^Bearer\s+/i, "");
|
|
1949
1949
|
return bearer === cfg.token || url.searchParams.get("token") === cfg.token;
|
|
1950
1950
|
};
|
|
1951
|
+
async function dashboardRoute(req, url, srv, ca, tl) {
|
|
1952
|
+
if (url.pathname === "/api/stream") {
|
|
1953
|
+
if (srv.upgrade(req, { data: {} }))
|
|
1954
|
+
return;
|
|
1955
|
+
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
1956
|
+
}
|
|
1957
|
+
if (url.pathname === "/api/sse") {
|
|
1958
|
+
return sseResponse(tl);
|
|
1959
|
+
}
|
|
1960
|
+
const configResp = await configRoutes(req, url, ca);
|
|
1961
|
+
if (configResp)
|
|
1962
|
+
return configResp;
|
|
1963
|
+
const modulesResp = await modulesRoutes(req, url);
|
|
1964
|
+
if (modulesResp)
|
|
1965
|
+
return modulesResp;
|
|
1966
|
+
const captureResp = await captureRoutes(req, url);
|
|
1967
|
+
if (captureResp)
|
|
1968
|
+
return captureResp;
|
|
1969
|
+
const clientsResp = await clientsRoutes(req, url);
|
|
1970
|
+
if (clientsResp)
|
|
1971
|
+
return clientsResp;
|
|
1972
|
+
const aaaResp = await aaaRoutes(req, url);
|
|
1973
|
+
if (aaaResp)
|
|
1974
|
+
return aaaResp;
|
|
1975
|
+
const usageResp = await usageRoutes(req, url);
|
|
1976
|
+
if (usageResp)
|
|
1977
|
+
return usageResp;
|
|
1978
|
+
const featureResp = await featureRoutes(req, url);
|
|
1979
|
+
if (featureResp)
|
|
1980
|
+
return featureResp;
|
|
1981
|
+
const db = getEventStore();
|
|
1982
|
+
if (!db)
|
|
1983
|
+
return json({ error: "recorder not active" }, 503);
|
|
1984
|
+
if (url.pathname === "/api/events" && req.method === "DELETE") {
|
|
1985
|
+
let body = {};
|
|
1986
|
+
try {
|
|
1987
|
+
body = await req.json();
|
|
1988
|
+
} catch {}
|
|
1989
|
+
const ids = Array.isArray(body.ids) ? body.ids.filter((x) => typeof x === "string") : [];
|
|
1990
|
+
const removed = body.all === true ? db.clear() : db.delete(ids);
|
|
1991
|
+
return json({ removed, total: db.total() });
|
|
1992
|
+
}
|
|
1993
|
+
if (url.pathname === "/api/devices") {
|
|
1994
|
+
return json(devicesPayload(db));
|
|
1995
|
+
}
|
|
1996
|
+
if (url.pathname === "/api/topology") {
|
|
1997
|
+
return json(topologyPayload());
|
|
1998
|
+
}
|
|
1999
|
+
if (url.pathname === "/api/config") {
|
|
2000
|
+
return json(configPayload());
|
|
2001
|
+
}
|
|
2002
|
+
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
2003
|
+
return new Response(dashboardHtml(), {
|
|
2004
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
if (url.pathname === "/api/meta") {
|
|
2008
|
+
const f = facets(db);
|
|
2009
|
+
return json({
|
|
2010
|
+
...f,
|
|
2011
|
+
risks: ["READ", "WRITE", "WRITE_IDEMPOTENT", "DESTRUCTIVE", "DANGEROUS"],
|
|
2012
|
+
total: db.total(),
|
|
2013
|
+
liveClients: subscriberCount(),
|
|
2014
|
+
transport: tl
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2017
|
+
if (url.pathname === "/api/events") {
|
|
2018
|
+
const filter = filterFromQuery(url);
|
|
2019
|
+
return json({ events: db.query(filter), total: db.total() });
|
|
2020
|
+
}
|
|
2021
|
+
const eventMatch = url.pathname.match(/^\/api\/event\/(.+)$/);
|
|
2022
|
+
if (eventMatch) {
|
|
2023
|
+
const e = db.get(decodeURIComponent(eventMatch[1]));
|
|
2024
|
+
return e ? json(e) : json({ error: "not found" }, 404);
|
|
2025
|
+
}
|
|
2026
|
+
if (url.pathname === "/api/stats") {
|
|
2027
|
+
const now = Date.now();
|
|
2028
|
+
const windowMs = Number(url.searchParams.get("window") ?? 3600000);
|
|
2029
|
+
const buckets = Number(url.searchParams.get("buckets") ?? 60);
|
|
2030
|
+
const events = db.query({ since: now - windowMs, limit: 5000 });
|
|
2031
|
+
return json(computeStats(events, { now, windowMs, buckets }));
|
|
2032
|
+
}
|
|
2033
|
+
return new Response("Not Found", { status: 404 });
|
|
2034
|
+
}
|
|
1951
2035
|
const server = serve({
|
|
1952
2036
|
hostname: cfg.host,
|
|
1953
2037
|
port: cfg.port,
|
|
@@ -1959,88 +2043,13 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
1959
2043
|
if (!tokenOk(req, url)) {
|
|
1960
2044
|
return new Response("Unauthorized", { status: 401 });
|
|
1961
2045
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
return sseResponse(transportLabel);
|
|
1969
|
-
}
|
|
1970
|
-
const configResp = await configRoutes(req, url, configAdmin);
|
|
1971
|
-
if (configResp)
|
|
1972
|
-
return configResp;
|
|
1973
|
-
const modulesResp = await modulesRoutes(req, url);
|
|
1974
|
-
if (modulesResp)
|
|
1975
|
-
return modulesResp;
|
|
1976
|
-
const captureResp = await captureRoutes(req, url);
|
|
1977
|
-
if (captureResp)
|
|
1978
|
-
return captureResp;
|
|
1979
|
-
const clientsResp = await clientsRoutes(req, url);
|
|
1980
|
-
if (clientsResp)
|
|
1981
|
-
return clientsResp;
|
|
1982
|
-
const aaaResp = await aaaRoutes(req, url);
|
|
1983
|
-
if (aaaResp)
|
|
1984
|
-
return aaaResp;
|
|
1985
|
-
const usageResp = await usageRoutes(req, url);
|
|
1986
|
-
if (usageResp)
|
|
1987
|
-
return usageResp;
|
|
1988
|
-
const featureResp = await featureRoutes(req, url);
|
|
1989
|
-
if (featureResp)
|
|
1990
|
-
return featureResp;
|
|
1991
|
-
const db = getEventStore();
|
|
1992
|
-
if (!db)
|
|
1993
|
-
return json({ error: "recorder not active" }, 503);
|
|
1994
|
-
if (url.pathname === "/api/events" && req.method === "DELETE") {
|
|
1995
|
-
let body = {};
|
|
1996
|
-
try {
|
|
1997
|
-
body = await req.json();
|
|
1998
|
-
} catch {}
|
|
1999
|
-
const ids = Array.isArray(body.ids) ? body.ids.filter((x) => typeof x === "string") : [];
|
|
2000
|
-
const removed = body.all === true ? db.clear() : db.delete(ids);
|
|
2001
|
-
return json({ removed, total: db.total() });
|
|
2002
|
-
}
|
|
2003
|
-
if (url.pathname === "/api/devices") {
|
|
2004
|
-
return json(devicesPayload(db));
|
|
2005
|
-
}
|
|
2006
|
-
if (url.pathname === "/api/topology") {
|
|
2007
|
-
return json(topologyPayload());
|
|
2008
|
-
}
|
|
2009
|
-
if (url.pathname === "/api/config") {
|
|
2010
|
-
return json(configPayload());
|
|
2011
|
-
}
|
|
2012
|
-
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
2013
|
-
return new Response(dashboardHtml(), {
|
|
2014
|
-
headers: { "content-type": "text/html; charset=utf-8" }
|
|
2015
|
-
});
|
|
2016
|
-
}
|
|
2017
|
-
if (url.pathname === "/api/meta") {
|
|
2018
|
-
const f = facets(db);
|
|
2019
|
-
return json({
|
|
2020
|
-
...f,
|
|
2021
|
-
risks: ["READ", "WRITE", "WRITE_IDEMPOTENT", "DESTRUCTIVE", "DANGEROUS"],
|
|
2022
|
-
total: db.total(),
|
|
2023
|
-
liveClients: subscriberCount(),
|
|
2024
|
-
transport: transportLabel
|
|
2025
|
-
});
|
|
2026
|
-
}
|
|
2027
|
-
if (url.pathname === "/api/events") {
|
|
2028
|
-
const filter = filterFromQuery(url);
|
|
2029
|
-
return json({ events: db.query(filter), total: db.total() });
|
|
2030
|
-
}
|
|
2031
|
-
const eventMatch = url.pathname.match(/^\/api\/event\/(.+)$/);
|
|
2032
|
-
if (eventMatch) {
|
|
2033
|
-
const e = db.get(decodeURIComponent(eventMatch[1]));
|
|
2034
|
-
return e ? json(e) : json({ error: "not found" }, 404);
|
|
2035
|
-
}
|
|
2036
|
-
if (url.pathname === "/api/stats") {
|
|
2037
|
-
const now = Date.now();
|
|
2038
|
-
const windowMs = Number(url.searchParams.get("window") ?? 3600000);
|
|
2039
|
-
const buckets = Number(url.searchParams.get("buckets") ?? 60);
|
|
2040
|
-
const events = db.query({ since: now - windowMs, limit: 5000 });
|
|
2041
|
-
return json(computeStats(events, { now, windowMs, buckets }));
|
|
2046
|
+
try {
|
|
2047
|
+
return await dashboardRoute(req, url, srv, configAdmin, transportLabel);
|
|
2048
|
+
} catch (e) {
|
|
2049
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2050
|
+
logger.error(`Dashboard request failed (${url.pathname}): ${msg}`);
|
|
2051
|
+
return json({ error: msg }, 502);
|
|
2042
2052
|
}
|
|
2043
|
-
return new Response("Not Found", { status: 404 });
|
|
2044
2053
|
},
|
|
2045
2054
|
websocket: {
|
|
2046
2055
|
open(ws) {
|
|
@@ -2233,7 +2242,7 @@ function registerPrompts(server) {
|
|
|
2233
2242
|
// package.json
|
|
2234
2243
|
var package_default = {
|
|
2235
2244
|
name: "@usex/mikrotik-mcp",
|
|
2236
|
-
version: "3.
|
|
2245
|
+
version: "3.38.0",
|
|
2237
2246
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
2238
2247
|
keywords: [
|
|
2239
2248
|
"ai",
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
resolveDeviceName,
|
|
22
22
|
selectToolModules,
|
|
23
23
|
setConfig
|
|
24
|
-
} from "./shared/library-
|
|
24
|
+
} from "./shared/library-nxh3rb8t.js";
|
|
25
25
|
// src/server.ts
|
|
26
26
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
27
27
|
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -131,7 +131,7 @@ function registerPrompts(server) {
|
|
|
131
131
|
// package.json
|
|
132
132
|
var package_default = {
|
|
133
133
|
name: "@usex/mikrotik-mcp",
|
|
134
|
-
version: "3.
|
|
134
|
+
version: "3.38.0",
|
|
135
135
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
136
136
|
keywords: [
|
|
137
137
|
"ai",
|
|
@@ -3075,7 +3075,7 @@ var backupTools = [
|
|
|
3075
3075
|
await executeMikrotikCommand(`/file remove ${deviceFile}`, ctx);
|
|
3076
3076
|
return `[LOW DISK \u2014 ${disk.usedPct?.toFixed(0)}% used] Backup '${deviceFile}' created, downloaded to ` + `local vault as '${vaultName}' (${data.length} bytes) at ${backupDir()}, and removed from ` + "the device to free disk space.";
|
|
3077
3077
|
} catch (e) {
|
|
3078
|
-
return `Backup '${name}.backup' created on device, but the automatic download-and-cleanup ` + `failed: ${e instanceof Error ? e.message : String(e)}. The file is still on the device
|
|
3078
|
+
return `Backup '${name}.backup' created on device, but the automatic download-and-cleanup ` + `failed: ${e instanceof Error ? e.message : String(e)}. The file is still on the device. ` + "Ensure the SSH user has the 'read' and 'sensitive' policies for SFTP downloads to work.";
|
|
3079
3079
|
}
|
|
3080
3080
|
}
|
|
3081
3081
|
const fileDetails = await executeMikrotikCommand(`/file print detail where name=${name}.backup`, ctx);
|
|
@@ -6141,7 +6141,7 @@ var cache = null;
|
|
|
6141
6141
|
async function gateway() {
|
|
6142
6142
|
if (cache)
|
|
6143
6143
|
return cache;
|
|
6144
|
-
const { moduleCatalog } = await import("./cli-
|
|
6144
|
+
const { moduleCatalog } = await import("./cli-h0gydx8r.js");
|
|
6145
6145
|
const forIndex = [];
|
|
6146
6146
|
const byName = new Map;
|
|
6147
6147
|
for (const mod of moduleCatalog) {
|
|
@@ -3051,7 +3051,7 @@ var backupTools = [
|
|
|
3051
3051
|
await executeMikrotikCommand(`/file remove ${deviceFile}`, ctx);
|
|
3052
3052
|
return `[LOW DISK \u2014 ${disk.usedPct?.toFixed(0)}% used] Backup '${deviceFile}' created, downloaded to ` + `local vault as '${vaultName}' (${data.length} bytes) at ${backupDir()}, and removed from ` + "the device to free disk space.";
|
|
3053
3053
|
} catch (e) {
|
|
3054
|
-
return `Backup '${name}.backup' created on device, but the automatic download-and-cleanup ` + `failed: ${e instanceof Error ? e.message : String(e)}. The file is still on the device
|
|
3054
|
+
return `Backup '${name}.backup' created on device, but the automatic download-and-cleanup ` + `failed: ${e instanceof Error ? e.message : String(e)}. The file is still on the device. ` + "Ensure the SSH user has the 'read' and 'sensitive' policies for SFTP downloads to work.";
|
|
3055
3055
|
}
|
|
3056
3056
|
}
|
|
3057
3057
|
const fileDetails = await executeMikrotikCommand(`/file print detail where name=${name}.backup`, ctx);
|
|
@@ -6117,7 +6117,7 @@ var cache = null;
|
|
|
6117
6117
|
async function gateway() {
|
|
6118
6118
|
if (cache)
|
|
6119
6119
|
return cache;
|
|
6120
|
-
const { moduleCatalog } = await import("./library-
|
|
6120
|
+
const { moduleCatalog } = await import("./library-q0q3kf6m.js");
|
|
6121
6121
|
const forIndex = [];
|
|
6122
6122
|
const byName = new Map;
|
|
6123
6123
|
for (const mod of moduleCatalog) {
|
package/package.json
CHANGED