@usex/mikrotik-mcp 3.7.0 → 3.9.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/README.md +7 -7
- package/dist/cli.js +144 -65
- package/dist/index.js +144 -65
- package/dist/ui/observability.html +46 -46
- package/package.json +1 -1
- package/schemas/README.md +1 -1
- package/schemas/config.schema.json +44 -3
- package/schemas/tool-catalog.json +28 -6
- package/schemas/tools/get_wireguard_status.json +12 -0
package/dist/index.js
CHANGED
|
@@ -56,6 +56,12 @@ var DashboardConfigSchema = z.object({
|
|
|
56
56
|
maxBodyBytes: z.coerce.number().int().nonnegative().default(16384),
|
|
57
57
|
token: z.string().optional()
|
|
58
58
|
});
|
|
59
|
+
var ToolFilterSchema = z.object({
|
|
60
|
+
enabledModules: z.array(z.string()).default([]),
|
|
61
|
+
disabledModules: z.array(z.string()).default([]),
|
|
62
|
+
enabledGroups: z.array(z.string()).default([]),
|
|
63
|
+
disabledGroups: z.array(z.string()).default([])
|
|
64
|
+
});
|
|
59
65
|
var MikrotikConfigSchema = z.object({
|
|
60
66
|
devices: z.record(z.string(), DeviceConfigSchema).default(() => ({ default: DeviceConfigSchema.parse({}) })),
|
|
61
67
|
defaultDevice: z.string().default("default"),
|
|
@@ -63,6 +69,7 @@ var MikrotikConfigSchema = z.object({
|
|
|
63
69
|
s3: S3ConfigSchema.optional(),
|
|
64
70
|
dashboard: DashboardConfigSchema.default(() => DashboardConfigSchema.parse({})),
|
|
65
71
|
readOnly: z.boolean().default(false),
|
|
72
|
+
tools: ToolFilterSchema.default(() => ToolFilterSchema.parse({})),
|
|
66
73
|
backupDir: z.string().optional()
|
|
67
74
|
});
|
|
68
75
|
function env(...names) {
|
|
@@ -108,7 +115,8 @@ function parseDevicesSource(raw, fromFile) {
|
|
|
108
115
|
const defaultDevice = typeof obj.defaultDevice === "string" ? obj.defaultDevice : undefined;
|
|
109
116
|
const s3 = structured && obj.s3 && typeof obj.s3 === "object" ? obj.s3 : undefined;
|
|
110
117
|
const dashboard = structured && obj.dashboard && typeof obj.dashboard === "object" ? obj.dashboard : undefined;
|
|
111
|
-
|
|
118
|
+
const tools = structured && obj.tools && typeof obj.tools === "object" ? obj.tools : undefined;
|
|
119
|
+
return { devices, defaultDevice, s3, dashboard, tools };
|
|
112
120
|
}
|
|
113
121
|
var configSource = { path: DEFAULT_CONFIG_FILE, fromFile: false };
|
|
114
122
|
function loadConfig(argv = process.argv.slice(2)) {
|
|
@@ -140,6 +148,7 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
140
148
|
const devicesInline = flags.devices ?? env("MIKROTIK_DEVICES");
|
|
141
149
|
let fileS3 = {};
|
|
142
150
|
let fileDashboard = {};
|
|
151
|
+
let fileTools;
|
|
143
152
|
if (configFile || devicesInline) {
|
|
144
153
|
const src = configFile ? parseDevicesSource(configFile, true) : parseDevicesSource(devicesInline, false);
|
|
145
154
|
for (const [name, dc] of Object.entries(src.devices))
|
|
@@ -150,6 +159,7 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
150
159
|
defaultDevice = Object.keys(src.devices)[0];
|
|
151
160
|
fileS3 = src.s3;
|
|
152
161
|
fileDashboard = src.dashboard;
|
|
162
|
+
fileTools = src.tools;
|
|
153
163
|
}
|
|
154
164
|
const s3 = {
|
|
155
165
|
accessKeyId: pick("s3-access-key-id", "S3_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID"),
|
|
@@ -172,6 +182,14 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
172
182
|
};
|
|
173
183
|
const isTruthy = (v) => /^(1|true|yes|on)$/i.test(v ?? "");
|
|
174
184
|
const readOnly = isTruthy(pick("read-only", "MIKROTIK_READ_ONLY"));
|
|
185
|
+
const csv = (v) => v === undefined ? undefined : v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
186
|
+
const tools = {
|
|
187
|
+
enabledModules: csv(pick("tools-enabled-modules", "MIKROTIK_TOOLS__ENABLED_MODULES")),
|
|
188
|
+
disabledModules: csv(pick("tools-disabled-modules", "MIKROTIK_TOOLS__DISABLED_MODULES")),
|
|
189
|
+
enabledGroups: csv(pick("tools-enabled-groups", "MIKROTIK_TOOLS__ENABLED_GROUPS")),
|
|
190
|
+
disabledGroups: csv(pick("tools-disabled-groups", "MIKROTIK_TOOLS__DISABLED_GROUPS")),
|
|
191
|
+
...fileTools
|
|
192
|
+
};
|
|
175
193
|
const boolOpt = (v) => v === undefined ? undefined : isTruthy(v);
|
|
176
194
|
const dashboard = {
|
|
177
195
|
enabled: boolOpt(pick("dashboard", "MIKROTIK_DASHBOARD__ENABLED", "MIKROTIK_DASHBOARD")),
|
|
@@ -192,6 +210,7 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
192
210
|
mcp,
|
|
193
211
|
dashboard,
|
|
194
212
|
readOnly,
|
|
213
|
+
tools,
|
|
195
214
|
...hasS3 ? { s3 } : {}
|
|
196
215
|
};
|
|
197
216
|
const pruned = JSON.parse(JSON.stringify(raw));
|
|
@@ -1033,6 +1052,16 @@ function isEmpty(result) {
|
|
|
1033
1052
|
const t = result.trim();
|
|
1034
1053
|
return t === "" || t === "no such item" || t === "no such item (4)";
|
|
1035
1054
|
}
|
|
1055
|
+
function extractCreatedId(output) {
|
|
1056
|
+
const star = output.match(/\*[0-9A-Fa-f]+/);
|
|
1057
|
+
if (star)
|
|
1058
|
+
return star[0];
|
|
1059
|
+
const num = output.trim().match(/^\d+$/);
|
|
1060
|
+
return num ? num[0] : undefined;
|
|
1061
|
+
}
|
|
1062
|
+
function readBackUnavailable(details) {
|
|
1063
|
+
return isEmpty(details) || /no such item/i.test(details) || looksLikeError(details);
|
|
1064
|
+
}
|
|
1036
1065
|
function looksLikeError(result) {
|
|
1037
1066
|
const t = result.toLowerCase();
|
|
1038
1067
|
return t.includes("failure:") || t.includes("syntax error") || t.includes("bad command") || t.includes("bad parameter") || t.includes("expected end of command") || t.includes("invalid value") || t.includes("input does not match") || t.includes("ambiguous value") || t.startsWith("error");
|
|
@@ -6056,16 +6085,17 @@ var firewallFilterTools = [
|
|
|
6056
6085
|
ctx.info(`Creating firewall filter rule: chain=${a.chain}, action=${a.action}`);
|
|
6057
6086
|
const cmd = new Cmd("/ip firewall filter add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("connection-state", a.connection_state).opt("connection-nat-state", a.connection_nat_state).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("limit", a.limit).opt("tcp-flags", a.tcp_flags).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6058
6087
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6088
|
+
const trimmed = result.trim();
|
|
6089
|
+
if (looksLikeError(trimmed)) {
|
|
6090
|
+
const hint = placeBeforeError(trimmed, a.place_before);
|
|
6091
|
+
return `Failed to create firewall filter rule: ${hint ?? trimmed}`;
|
|
6092
|
+
}
|
|
6093
|
+
const createdId = extractCreatedId(trimmed);
|
|
6094
|
+
if (createdId) {
|
|
6095
|
+
const details = await executeMikrotikCommand(`/ip firewall filter print detail where .id=${createdId}`, ctx);
|
|
6096
|
+
return readBackUnavailable(details) ? `Firewall filter rule created (id ${createdId}).` : `Firewall filter rule created successfully:
|
|
6064
6097
|
|
|
6065
|
-
${details}
|
|
6066
|
-
}
|
|
6067
|
-
const hint = placeBeforeError(result, a.place_before);
|
|
6068
|
-
return `Failed to create firewall filter rule: ${hint ?? result}`;
|
|
6098
|
+
${details}`;
|
|
6069
6099
|
}
|
|
6070
6100
|
const count = await executeMikrotikCommand("/ip firewall filter print detail count-only", ctx);
|
|
6071
6101
|
const c = count.trim();
|
|
@@ -6353,16 +6383,17 @@ var firewallNatTools = [
|
|
|
6353
6383
|
}
|
|
6354
6384
|
const cmd = new Cmd("/ip firewall nat add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("to-addresses", a.to_addresses).opt("to-ports", a.to_ports).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6355
6385
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6386
|
+
const trimmed = result.trim();
|
|
6387
|
+
if (looksLikeError(trimmed)) {
|
|
6388
|
+
const hint = placeBeforeError(trimmed, a.place_before);
|
|
6389
|
+
return `Failed to create NAT rule: ${hint ?? trimmed}`;
|
|
6390
|
+
}
|
|
6391
|
+
const createdId = extractCreatedId(trimmed);
|
|
6392
|
+
if (createdId) {
|
|
6393
|
+
const details = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${createdId}`, ctx);
|
|
6394
|
+
return readBackUnavailable(details) ? `NAT rule created (id ${createdId}).` : `NAT rule created successfully:
|
|
6361
6395
|
|
|
6362
|
-
${details}
|
|
6363
|
-
}
|
|
6364
|
-
const hint = placeBeforeError(result, a.place_before);
|
|
6365
|
-
return `Failed to create NAT rule: ${hint ?? result}`;
|
|
6396
|
+
${details}`;
|
|
6366
6397
|
}
|
|
6367
6398
|
const count = await executeMikrotikCommand("/ip firewall nat print detail count-only", ctx);
|
|
6368
6399
|
const c = count.trim();
|
|
@@ -8025,15 +8056,16 @@ var ipv6FirewallFilterTools = [
|
|
|
8025
8056
|
ctx.info(`Creating IPv6 firewall filter rule: chain=${a.chain}, action=${a.action}`);
|
|
8026
8057
|
const cmd = new Cmd("/ipv6 firewall filter add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("in-interface-list", a.in_interface_list).opt("out-interface-list", a.out_interface_list).opt("connection-state", a.connection_state).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("hop-limit", a.hop_limit).opt("limit", a.limit).opt("tcp-flags", a.tcp_flags).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
8027
8058
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8059
|
+
const trimmed = result.trim();
|
|
8060
|
+
if (looksLikeError(trimmed)) {
|
|
8061
|
+
return `Failed to create IPv6 firewall filter rule: ${trimmed}`;
|
|
8062
|
+
}
|
|
8063
|
+
const createdId = extractCreatedId(trimmed);
|
|
8064
|
+
if (createdId) {
|
|
8065
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${createdId}`, ctx);
|
|
8066
|
+
return readBackUnavailable(details) ? `IPv6 firewall filter rule created (id ${createdId}).` : `IPv6 firewall filter rule created successfully:
|
|
8033
8067
|
|
|
8034
|
-
${details}
|
|
8035
|
-
}
|
|
8036
|
-
return `Failed to create IPv6 firewall filter rule: ${result}`;
|
|
8068
|
+
${details}`;
|
|
8037
8069
|
}
|
|
8038
8070
|
const count = await executeMikrotikCommand("/ipv6 firewall filter print detail count-only", ctx);
|
|
8039
8071
|
const c = count.trim();
|
|
@@ -8286,15 +8318,16 @@ var ipv6FirewallNatTools = [
|
|
|
8286
8318
|
ctx.info(`Creating IPv6 firewall NAT rule: chain=${a.chain}, action=${a.action}`);
|
|
8287
8319
|
const cmd = new Cmd("/ipv6 firewall nat add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("to-address", a.to_address).opt("to-ports", a.to_ports).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
8288
8320
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8321
|
+
const trimmed = result.trim();
|
|
8322
|
+
if (looksLikeError(trimmed)) {
|
|
8323
|
+
return `Failed to create IPv6 firewall NAT rule: ${trimmed}`;
|
|
8324
|
+
}
|
|
8325
|
+
const createdId = extractCreatedId(trimmed);
|
|
8326
|
+
if (createdId) {
|
|
8327
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${createdId}`, ctx);
|
|
8328
|
+
return readBackUnavailable(details) ? `IPv6 firewall NAT rule created (id ${createdId}).` : `IPv6 firewall NAT rule created successfully:
|
|
8294
8329
|
|
|
8295
|
-
${details}
|
|
8296
|
-
}
|
|
8297
|
-
return `Failed to create IPv6 firewall NAT rule: ${result}`;
|
|
8330
|
+
${details}`;
|
|
8298
8331
|
}
|
|
8299
8332
|
const count = await executeMikrotikCommand("/ipv6 firewall nat print detail count-only", ctx);
|
|
8300
8333
|
const c = count.trim();
|
|
@@ -8550,15 +8583,16 @@ var ipv6FirewallMangleTools = [
|
|
|
8550
8583
|
ctx.info(`Creating IPv6 firewall mangle rule: chain=${a.chain}, action=${a.action}`);
|
|
8551
8584
|
const cmd = new Cmd("/ipv6 firewall mangle add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("connection-mark", a.connection_mark).opt("packet-mark", a.packet_mark).opt("routing-mark", a.routing_mark).opt("new-connection-mark", a.new_connection_mark).opt("new-packet-mark", a.new_packet_mark).opt("new-routing-mark", a.new_routing_mark).opt("new-dscp", a.new_dscp).opt("new-hop-limit", a.new_hop_limit).bool("passthrough", a.passthrough).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
8552
8585
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8586
|
+
const trimmed = result.trim();
|
|
8587
|
+
if (looksLikeError(trimmed)) {
|
|
8588
|
+
return `Failed to create IPv6 firewall mangle rule: ${trimmed}`;
|
|
8589
|
+
}
|
|
8590
|
+
const createdId = extractCreatedId(trimmed);
|
|
8591
|
+
if (createdId) {
|
|
8592
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${createdId}`, ctx);
|
|
8593
|
+
return readBackUnavailable(details) ? `IPv6 firewall mangle rule created (id ${createdId}).` : `IPv6 firewall mangle rule created successfully:
|
|
8558
8594
|
|
|
8559
|
-
${details}
|
|
8560
|
-
}
|
|
8561
|
-
return `Failed to create IPv6 firewall mangle rule: ${result}`;
|
|
8595
|
+
${details}`;
|
|
8562
8596
|
}
|
|
8563
8597
|
const count = await executeMikrotikCommand("/ipv6 firewall mangle print detail count-only", ctx);
|
|
8564
8598
|
const c = count.trim();
|
|
@@ -8789,15 +8823,16 @@ var ipv6FirewallRawTools = [
|
|
|
8789
8823
|
ctx.info(`Creating IPv6 firewall raw rule: chain=${a.chain}, action=${a.action}`);
|
|
8790
8824
|
const cmd = new Cmd("/ipv6 firewall raw add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
8791
8825
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8826
|
+
const trimmed = result.trim();
|
|
8827
|
+
if (looksLikeError(trimmed)) {
|
|
8828
|
+
return `Failed to create IPv6 firewall raw rule: ${trimmed}`;
|
|
8829
|
+
}
|
|
8830
|
+
const createdId = extractCreatedId(trimmed);
|
|
8831
|
+
if (createdId) {
|
|
8832
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${createdId}`, ctx);
|
|
8833
|
+
return readBackUnavailable(details) ? `IPv6 firewall raw rule created (id ${createdId}).` : `IPv6 firewall raw rule created successfully:
|
|
8797
8834
|
|
|
8798
|
-
${details}
|
|
8799
|
-
}
|
|
8800
|
-
return `Failed to create IPv6 firewall raw rule: ${result}`;
|
|
8835
|
+
${details}`;
|
|
8801
8836
|
}
|
|
8802
8837
|
const count = await executeMikrotikCommand("/ipv6 firewall raw print detail count-only", ctx);
|
|
8803
8838
|
const c = count.trim();
|
|
@@ -17710,15 +17745,16 @@ var switchRuleTools = [
|
|
|
17710
17745
|
ctx.info(`Adding switch rule: switch=${a.switch}, ports=${a.ports}`);
|
|
17711
17746
|
const cmd = new Cmd("/interface ethernet switch rule add").set("switch", a.switch).set("ports", a.ports).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-mac-address", a.src_mac_address).opt("dst-mac-address", a.dst_mac_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("mac-protocol", a.mac_protocol).opt("vlan-id", a.vlan_id).opt("vlan-priority", a.vlan_priority).opt("dscp", a.dscp).opt("flow-label", a.flow_label).opt("new-dst-ports", a.new_dst_ports).opt("new-vlan-id", a.new_vlan_id).opt("new-vlan-priority", a.new_vlan_priority).bool("redirect-to-cpu", a.redirect_to_cpu).bool("copy-to-cpu", a.copy_to_cpu).bool("mirror", a.mirror).opt("rate", a.rate).opt("comment", a.comment).flag("disabled", a.disabled).build();
|
|
17712
17747
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
17713
|
-
|
|
17714
|
-
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17748
|
+
const trimmed = result.trim();
|
|
17749
|
+
if (looksLikeError(trimmed)) {
|
|
17750
|
+
return `Failed to add switch rule: ${trimmed}`;
|
|
17751
|
+
}
|
|
17752
|
+
const createdId = extractCreatedId(trimmed);
|
|
17753
|
+
if (createdId) {
|
|
17754
|
+
const details = await executeMikrotikCommand(`/interface ethernet switch rule print detail where .id=${createdId}`, ctx);
|
|
17755
|
+
return readBackUnavailable(details) ? `Switch rule added (id ${createdId}).` : `Switch rule added successfully:
|
|
17718
17756
|
|
|
17719
|
-
${details}
|
|
17720
|
-
}
|
|
17721
|
-
return `Failed to add switch rule: ${result}`;
|
|
17757
|
+
${details}`;
|
|
17722
17758
|
}
|
|
17723
17759
|
const count = await executeMikrotikCommand("/interface ethernet switch rule print detail count-only", ctx);
|
|
17724
17760
|
const c = count.trim();
|
|
@@ -20014,7 +20050,7 @@ ${details}` : "WireGuard interface created successfully.";
|
|
|
20014
20050
|
name: "list_wireguard_interfaces",
|
|
20015
20051
|
title: "List WireGuard Interfaces",
|
|
20016
20052
|
annotations: READ,
|
|
20017
|
-
description: "
|
|
20053
|
+
description: "`list_wireguard_interfaces` \u2014 READ / list / show / inspect all WireGuard tunnel interfaces" + " (`/interface wireguard print`). The go-to tool to read the current WireGuard state on a" + " device. Returns each interface's name, listen-port, public-key, MTU, and running/disabled" + " status. Filter by name substring (name_filter), disabled-only, or running-only." + " For one interface's full detail use get_wireguard_interface; for the peer table use" + " list_wireguard_peers; for interfaces AND peers in one call use get_wireguard_status.",
|
|
20018
20054
|
inputSchema: {
|
|
20019
20055
|
name_filter: z109.string().optional(),
|
|
20020
20056
|
disabled_only: z109.boolean().default(false),
|
|
@@ -20039,7 +20075,7 @@ ${result}`;
|
|
|
20039
20075
|
name: "get_wireguard_interface",
|
|
20040
20076
|
title: "Get WireGuard Interface Details",
|
|
20041
20077
|
annotations: READ,
|
|
20042
|
-
description: "
|
|
20078
|
+
description: "`get_wireguard_interface` \u2014 READ / get / show the full detail of ONE WireGuard tunnel interface" + " (`/interface wireguard print detail`) looked up by name. Returns listen-port, private-key," + " public-key, MTU, running state, and comment. To list all interfaces use" + " list_wireguard_interfaces; for that interface's peers use list_wireguard_peers; for" + " interfaces AND peers in one call use get_wireguard_status.",
|
|
20043
20079
|
inputSchema: { name: z109.string() },
|
|
20044
20080
|
async handler(a, ctx) {
|
|
20045
20081
|
ctx.info(`Getting WireGuard interface details: name=${a.name}`);
|
|
@@ -20049,6 +20085,31 @@ ${result}`;
|
|
|
20049
20085
|
${result}`;
|
|
20050
20086
|
}
|
|
20051
20087
|
}),
|
|
20088
|
+
defineTool({
|
|
20089
|
+
name: "get_wireguard_status",
|
|
20090
|
+
title: "Get WireGuard Status (Interfaces + Peers)",
|
|
20091
|
+
annotations: READ,
|
|
20092
|
+
description: "`get_wireguard_status` \u2014 READ the current WireGuard state on a device in ONE call: ALL tunnel" + " interfaces AND ALL peers together. Use this FIRST to inspect / show / check WireGuard before" + " changing anything (e.g. to read public keys, see which side initiates, or confirm the tunnel" + " is up). Interfaces report name, listen-port, public-key, MTU and running state; peers report" + " .id, interface, public-key, allowed-address, endpoint/current-endpoint, last-handshake time" + " and rx/tx bytes. Combines list_wireguard_interfaces + list_wireguard_peers; optionally filter" + " both to one interface with interface_filter.",
|
|
20093
|
+
inputSchema: {
|
|
20094
|
+
interface_filter: z109.string().optional().describe("Limit to one interface name, e.g. 'wg-mesh'")
|
|
20095
|
+
},
|
|
20096
|
+
async handler(a, ctx) {
|
|
20097
|
+
ctx.info("Reading WireGuard status (interfaces + peers)");
|
|
20098
|
+
const ifFilters = a.interface_filter ? [`name="${a.interface_filter}"`] : [];
|
|
20099
|
+
const peerFilters = a.interface_filter ? [`interface="${a.interface_filter}"`] : [];
|
|
20100
|
+
const interfaces = await executeMikrotikCommand(`/interface wireguard print${whereClause(ifFilters)}`, ctx);
|
|
20101
|
+
const peers = await executeMikrotikCommand(`/interface wireguard peers print${whereClause(peerFilters)}`, ctx);
|
|
20102
|
+
const ifBlock = isEmpty(interfaces) ? "(none)" : interfaces;
|
|
20103
|
+
const peerBlock = isEmpty(peers) ? "(none)" : peers;
|
|
20104
|
+
return `WIREGUARD INTERFACES:
|
|
20105
|
+
|
|
20106
|
+
${ifBlock}
|
|
20107
|
+
|
|
20108
|
+
WIREGUARD PEERS:
|
|
20109
|
+
|
|
20110
|
+
${peerBlock}`;
|
|
20111
|
+
}
|
|
20112
|
+
}),
|
|
20052
20113
|
defineTool({
|
|
20053
20114
|
name: "update_wireguard_interface",
|
|
20054
20115
|
title: "Update WireGuard Interface",
|
|
@@ -20161,7 +20222,7 @@ ${details}` : "WireGuard peer added successfully.";
|
|
|
20161
20222
|
name: "list_wireguard_peers",
|
|
20162
20223
|
title: "List WireGuard Peers",
|
|
20163
20224
|
annotations: READ,
|
|
20164
|
-
description: "
|
|
20225
|
+
description: "`list_wireguard_peers` \u2014 READ / list / show / inspect all WireGuard peers" + " (`/interface wireguard peers print`). Use this to read which peers are configured and" + " whether the tunnel is up (handshake). Returns each peer's .id, interface, public-key," + " allowed-address, endpoint/current-endpoint, last-handshake time, and rx/tx byte counters." + " Filter by interface name (interface_filter) or disabled-only. For the interface table use" + " list_wireguard_interfaces; for one peer's full detail use get_wireguard_peer; for interfaces" + " AND peers in one call use get_wireguard_status. Use the .id from this output with" + " update_wireguard_peer, remove_wireguard_peer, enable_wireguard_peer, or disable_wireguard_peer.",
|
|
20165
20226
|
inputSchema: {
|
|
20166
20227
|
interface_filter: z109.string().optional(),
|
|
20167
20228
|
disabled_only: z109.boolean().default(false)
|
|
@@ -20183,7 +20244,7 @@ ${result}`;
|
|
|
20183
20244
|
name: "get_wireguard_peer",
|
|
20184
20245
|
title: "Get WireGuard Peer Details",
|
|
20185
20246
|
annotations: READ,
|
|
20186
|
-
description: "
|
|
20247
|
+
description: "`get_wireguard_peer` \u2014 READ / get / show the full detail of ONE WireGuard peer" + " (`/interface wireguard peers print detail`) by its .id. Returns public-key, allowed-address," + " endpoint/current-endpoint, preshared-key presence, last-handshake time, and rx/tx bytes." + " To list all peers use list_wireguard_peers; for interfaces AND peers in one call use" + ` get_wireguard_status.
|
|
20187
20248
|
|
|
20188
20249
|
` + `Notes:
|
|
20189
20250
|
` + ' peer_id: the .id from list_wireguard_peers, format "*N" or "N" e.g. "*2"',
|
|
@@ -21623,10 +21684,27 @@ var moduleCatalog = [
|
|
|
21623
21684
|
}
|
|
21624
21685
|
];
|
|
21625
21686
|
var allToolModules = moduleCatalog.map((m) => m.tools);
|
|
21687
|
+
function selectToolModules(filter = {}, catalog = moduleCatalog) {
|
|
21688
|
+
const lc = (xs) => new Set((xs ?? []).map((s) => s.toLowerCase()));
|
|
21689
|
+
const enabledModules = lc(filter.enabledModules);
|
|
21690
|
+
const disabledModules = lc(filter.disabledModules);
|
|
21691
|
+
const enabledGroups = lc(filter.enabledGroups);
|
|
21692
|
+
const disabledGroups = lc(filter.disabledGroups);
|
|
21693
|
+
const hasAllow = enabledModules.size > 0 || enabledGroups.size > 0;
|
|
21694
|
+
return catalog.filter((m) => {
|
|
21695
|
+
const slug = m.slug.toLowerCase();
|
|
21696
|
+
const group = m.group.toLowerCase();
|
|
21697
|
+
if (disabledModules.has(slug) || disabledGroups.has(group))
|
|
21698
|
+
return false;
|
|
21699
|
+
if (hasAllow && !(enabledModules.has(slug) || enabledGroups.has(group)))
|
|
21700
|
+
return false;
|
|
21701
|
+
return true;
|
|
21702
|
+
}).map((m) => m.tools);
|
|
21703
|
+
}
|
|
21626
21704
|
// package.json
|
|
21627
21705
|
var package_default = {
|
|
21628
21706
|
name: "@usex/mikrotik-mcp",
|
|
21629
|
-
version: "3.
|
|
21707
|
+
version: "3.9.0",
|
|
21630
21708
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
21631
21709
|
keywords: [
|
|
21632
21710
|
"ai",
|
|
@@ -21818,7 +21896,8 @@ function createServer(opts = {}) {
|
|
|
21818
21896
|
server.server.sendLoggingMessage({ level, data: message }).catch(() => {});
|
|
21819
21897
|
} catch {}
|
|
21820
21898
|
});
|
|
21821
|
-
const
|
|
21899
|
+
const toolModules = selectToolModules(getConfig().tools);
|
|
21900
|
+
const toolCount = registerTools(server, toolModules, {
|
|
21822
21901
|
sendLog,
|
|
21823
21902
|
deviceNames: names,
|
|
21824
21903
|
readOnly
|