mikromcp 1.14.0 → 1.14.1
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 +1 -1
- package/dist/main.js +101 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
[](https://github.com/AliKarami/MikroMCP/actions/workflows/ci.yml)
|
|
14
14
|
[](https://github.com/AliKarami/MikroMCP/actions/workflows/release.yml)
|
|
15
|
-
[](package.json)
|
|
16
16
|
[](LICENSE)
|
|
17
17
|
[](package.json)
|
|
18
18
|
[](https://help.mikrotik.com/docs/display/ROS/REST+API)
|
package/dist/main.js
CHANGED
|
@@ -3270,6 +3270,7 @@ var wifiTools = [listWifiTool, listWifiClientsTool, manageWifiTool];
|
|
|
3270
3270
|
|
|
3271
3271
|
// src/domain/tools/wireguard-tools.ts
|
|
3272
3272
|
import { z as z13 } from "zod";
|
|
3273
|
+
import { isIP } from "net";
|
|
3273
3274
|
var log11 = createLogger("wireguard-tools");
|
|
3274
3275
|
var listWgInputSchema = z13.object({
|
|
3275
3276
|
routerId,
|
|
@@ -3363,6 +3364,7 @@ var listPeersTool = {
|
|
|
3363
3364
|
"interface",
|
|
3364
3365
|
"public-key",
|
|
3365
3366
|
"endpoint-address",
|
|
3367
|
+
"endpoint-port",
|
|
3366
3368
|
"allowed-address",
|
|
3367
3369
|
"disabled"
|
|
3368
3370
|
])
|
|
@@ -3381,20 +3383,71 @@ var listPeersTool = {
|
|
|
3381
3383
|
}
|
|
3382
3384
|
}
|
|
3383
3385
|
};
|
|
3386
|
+
function parsePeerEndpoint(endpoint) {
|
|
3387
|
+
if (/\s/.test(endpoint) || endpoint.includes("%")) return void 0;
|
|
3388
|
+
if (isIP(endpoint) === 6) return { "endpoint-address": endpoint };
|
|
3389
|
+
const ipv6 = /^\[([^\]]+)\](?::(\d+))?$/.exec(endpoint);
|
|
3390
|
+
const host = /^([^:]+)(?::(\d+))?$/.exec(endpoint);
|
|
3391
|
+
const match = ipv6 ?? host;
|
|
3392
|
+
if (!match) return void 0;
|
|
3393
|
+
const [, address, port] = match;
|
|
3394
|
+
if (ipv6) {
|
|
3395
|
+
if (isIP(address) !== 6) return void 0;
|
|
3396
|
+
} else if (isIP(address) !== 4) {
|
|
3397
|
+
const hostname = address.endsWith(".") ? address.slice(0, -1) : address;
|
|
3398
|
+
if (hostname.length > 253 || /^[\d.]+$/.test(hostname) || !hostname.split(".").every((label) => /^[a-z\d_](?:[a-z\d_-]{0,61}[a-z\d_])?$/i.test(label))) {
|
|
3399
|
+
return void 0;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
if (port !== void 0 && (Number(port) < 1 || Number(port) > 65535)) return void 0;
|
|
3403
|
+
return {
|
|
3404
|
+
"endpoint-address": address,
|
|
3405
|
+
...port === void 0 ? {} : { "endpoint-port": String(Number(port)) }
|
|
3406
|
+
};
|
|
3407
|
+
}
|
|
3408
|
+
var peerEndpointSchema = z13.string().transform((endpoint, ctx) => {
|
|
3409
|
+
const fields = parsePeerEndpoint(endpoint);
|
|
3410
|
+
if (fields) return fields;
|
|
3411
|
+
ctx.addIssue({
|
|
3412
|
+
code: z13.ZodIssueCode.custom,
|
|
3413
|
+
message: "Use IPv4:port, hostname:port or [IPv6]:port with port 1\u201365535, or an address without a port"
|
|
3414
|
+
});
|
|
3415
|
+
return z13.NEVER;
|
|
3416
|
+
});
|
|
3417
|
+
function canonicalPeerAddress(value) {
|
|
3418
|
+
const address = String(value ?? "");
|
|
3419
|
+
if (isIP(address) === 6) return new URL(`http://[${address}]/`).hostname;
|
|
3420
|
+
return address.toLowerCase().replace(/\.$/, "");
|
|
3421
|
+
}
|
|
3422
|
+
function canonicalAllowedAddresses(value) {
|
|
3423
|
+
const addresses = String(value ?? "").split(",").map((item) => {
|
|
3424
|
+
const cidr2 = item.trim();
|
|
3425
|
+
const [address, prefix] = cidr2.split("/");
|
|
3426
|
+
const family = isIP(address);
|
|
3427
|
+
if (!family || prefix !== void 0 && !/^\d+$/.test(prefix)) return cidr2;
|
|
3428
|
+
const bits = family === 4 ? 32 : 128;
|
|
3429
|
+
const length = Number(prefix ?? bits);
|
|
3430
|
+
if (length > bits || cidr2.split("/").length > 2) return cidr2;
|
|
3431
|
+
return `${canonicalPeerAddress(address)}/${length}`;
|
|
3432
|
+
});
|
|
3433
|
+
return [...new Set(addresses)].sort().join(",");
|
|
3434
|
+
}
|
|
3384
3435
|
var managePeerInputSchema = z13.object({
|
|
3385
3436
|
routerId,
|
|
3386
3437
|
action: z13.enum(["add", "remove"]).describe("Action to perform"),
|
|
3387
3438
|
interface: z13.string().describe("WireGuard interface name (e.g. wg0)"),
|
|
3388
3439
|
publicKey: z13.string().min(1).describe("Peer public key in base64 format (44 characters)"),
|
|
3389
3440
|
allowedAddress: z13.string().optional().describe("Allowed IP address/CIDR for this peer (e.g. 10.0.0.2/32)"),
|
|
3390
|
-
endpoint: z13.string().optional().describe(
|
|
3441
|
+
endpoint: z13.string().optional().describe(
|
|
3442
|
+
"Add only: IPv4:port, hostname:port or [IPv6]:port (port 1\u201365535); hostname labels may contain underscores. Address-only IPv4/hostname/IPv6 (bare or bracketed) leaves the RouterOS port default unchanged, which may not allow initiation. Ignored for remove; omit for a roaming peer."
|
|
3443
|
+
),
|
|
3391
3444
|
comment: z13.string().max(255).optional().describe("Optional comment"),
|
|
3392
3445
|
dryRun
|
|
3393
3446
|
}).strict();
|
|
3394
3447
|
var managePeerTool = {
|
|
3395
3448
|
name: "manage_wireguard_peer",
|
|
3396
3449
|
title: "Manage WireGuard Peer",
|
|
3397
|
-
description: "Add or remove a WireGuard peer.
|
|
3450
|
+
description: "Add or remove a WireGuard peer. Add returns already_exists when the public key and explicitly requested endpoint/allowed addresses match; different settings produce CONFLICT without changing the peer. Omitted settings are left unchanged.",
|
|
3398
3451
|
inputSchema: managePeerInputSchema,
|
|
3399
3452
|
annotations: {
|
|
3400
3453
|
readOnlyHint: false,
|
|
@@ -3410,11 +3463,44 @@ var managePeerTool = {
|
|
|
3410
3463
|
"Managing WireGuard peer"
|
|
3411
3464
|
);
|
|
3412
3465
|
try {
|
|
3466
|
+
const endpointFields = parsed.action === "add" && parsed.endpoint !== void 0 ? peerEndpointSchema.parse(parsed.endpoint) : {};
|
|
3413
3467
|
const existing = await context.routerClient.get("interface/wireguard/peers", {
|
|
3414
3468
|
filter: { interface: parsed.interface, "public-key": parsed.publicKey }
|
|
3415
3469
|
});
|
|
3416
3470
|
if (parsed.action === "add") {
|
|
3417
3471
|
if (existing.length > 0) {
|
|
3472
|
+
const rec2 = existing[0];
|
|
3473
|
+
const requestedFields = {
|
|
3474
|
+
...endpointFields,
|
|
3475
|
+
...parsed.allowedAddress === void 0 ? {} : { "allowed-address": parsed.allowedAddress }
|
|
3476
|
+
};
|
|
3477
|
+
const mismatched = Object.entries(requestedFields).filter(([property, desired]) => {
|
|
3478
|
+
if (property === "endpoint-address") {
|
|
3479
|
+
return canonicalPeerAddress(rec2[property]) !== canonicalPeerAddress(desired);
|
|
3480
|
+
}
|
|
3481
|
+
if (property === "allowed-address") {
|
|
3482
|
+
return canonicalAllowedAddresses(rec2[property]) !== canonicalAllowedAddresses(desired);
|
|
3483
|
+
}
|
|
3484
|
+
return !sameValue(rec2[property], desired);
|
|
3485
|
+
});
|
|
3486
|
+
if (mismatched.length > 0) {
|
|
3487
|
+
throw new MikroMCPError({
|
|
3488
|
+
category: "CONFLICT" /* CONFLICT */,
|
|
3489
|
+
code: "WIREGUARD_PEER_CONFLICT",
|
|
3490
|
+
message: `WireGuard peer already exists on "${parsed.interface}" with different requested settings.`,
|
|
3491
|
+
details: {
|
|
3492
|
+
existing: Object.fromEntries(
|
|
3493
|
+
mismatched.map(([property]) => [property, rec2[property]])
|
|
3494
|
+
),
|
|
3495
|
+
requested: Object.fromEntries(mismatched)
|
|
3496
|
+
},
|
|
3497
|
+
recoverability: {
|
|
3498
|
+
retryable: false,
|
|
3499
|
+
suggestedAction: "Inspect the peer with list_wireguard_peers, then remove it before adding different settings.",
|
|
3500
|
+
alternativeTools: ["list_wireguard_peers", "manage_wireguard_peer"]
|
|
3501
|
+
}
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3418
3504
|
return {
|
|
3419
3505
|
content: `WireGuard peer with this public key already exists on "${parsed.interface}". No changes made.`,
|
|
3420
3506
|
structuredContent: { action: "already_exists", peer: existing[0] }
|
|
@@ -3424,8 +3510,12 @@ var managePeerTool = {
|
|
|
3424
3510
|
const diff = [
|
|
3425
3511
|
{ property: "interface", before: null, after: parsed.interface },
|
|
3426
3512
|
{ property: "public-key", before: null, after: parsed.publicKey },
|
|
3427
|
-
...parsed.allowedAddress ? [{ property: "allowed-address", before: null, after: parsed.allowedAddress }] : [],
|
|
3428
|
-
...
|
|
3513
|
+
...parsed.allowedAddress !== void 0 ? [{ property: "allowed-address", before: null, after: parsed.allowedAddress }] : [],
|
|
3514
|
+
...Object.entries(endpointFields).map(([property, after]) => ({
|
|
3515
|
+
property,
|
|
3516
|
+
before: null,
|
|
3517
|
+
after
|
|
3518
|
+
}))
|
|
3429
3519
|
];
|
|
3430
3520
|
return {
|
|
3431
3521
|
content: `Dry run: Would add WireGuard peer on "${parsed.interface}".`,
|
|
@@ -3434,10 +3524,10 @@ var managePeerTool = {
|
|
|
3434
3524
|
}
|
|
3435
3525
|
const body = {
|
|
3436
3526
|
interface: parsed.interface,
|
|
3437
|
-
"public-key": parsed.publicKey
|
|
3527
|
+
"public-key": parsed.publicKey,
|
|
3528
|
+
...endpointFields
|
|
3438
3529
|
};
|
|
3439
|
-
if (parsed.allowedAddress) body["allowed-address"] = parsed.allowedAddress;
|
|
3440
|
-
if (parsed.endpoint) body["endpoint-address"] = parsed.endpoint;
|
|
3530
|
+
if (parsed.allowedAddress !== void 0) body["allowed-address"] = parsed.allowedAddress;
|
|
3441
3531
|
if (parsed.comment) body.comment = parsed.comment.replace(/[\x00-\x1f\x7f]/g, "");
|
|
3442
3532
|
const created = await context.routerClient.create("interface/wireguard/peers", body);
|
|
3443
3533
|
log11.info({ interface: parsed.interface, id: created[".id"] }, "WireGuard peer added");
|
|
@@ -12236,7 +12326,7 @@ var manageIpServiceInputSchema = z37.object({
|
|
|
12236
12326
|
var manageIpServiceTool = {
|
|
12237
12327
|
name: "manage_ip_service",
|
|
12238
12328
|
title: "Manage IP Service",
|
|
12239
|
-
description: "Enable or disable a RouterOS IP service (api, api-ssl, ssh, telnet, www, www-ssl, winbox, ftp). Port number changes are intentionally not supported to prevent accidental lockout.",
|
|
12329
|
+
description: "Enable or disable a RouterOS IP service (api, api-ssl, ssh, telnet, www, www-ssl, winbox, ftp). Port number changes are intentionally not supported to prevent accidental lockout. MikroMCP reaches RouterOS through the REST API on www-ssl (or www for plaintext): disabling the service its connection uses cuts MikroMCP off from the router, including any later call to re-enable it.",
|
|
12240
12330
|
inputSchema: manageIpServiceInputSchema,
|
|
12241
12331
|
annotations: {
|
|
12242
12332
|
readOnlyHint: false,
|
|
@@ -18308,7 +18398,7 @@ var ConnectionPool = class {
|
|
|
18308
18398
|
};
|
|
18309
18399
|
|
|
18310
18400
|
// src/version.ts
|
|
18311
|
-
var VERSION2 = "1.14.
|
|
18401
|
+
var VERSION2 = "1.14.1";
|
|
18312
18402
|
|
|
18313
18403
|
// src/mcp/server.ts
|
|
18314
18404
|
var SERVER_INSTRUCTIONS = [
|
|
@@ -18786,7 +18876,7 @@ import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
|
|
|
18786
18876
|
import { homedir as homedir2 } from "os";
|
|
18787
18877
|
import * as net from "net";
|
|
18788
18878
|
import chalk from "chalk";
|
|
18789
|
-
var localVersion = "1.14.
|
|
18879
|
+
var localVersion = "1.14.1";
|
|
18790
18880
|
var results = [];
|
|
18791
18881
|
function ok(msg) {
|
|
18792
18882
|
console.log(chalk.green("\u2705 ") + msg);
|
|
@@ -19602,7 +19692,7 @@ async function runInit() {
|
|
|
19602
19692
|
}
|
|
19603
19693
|
|
|
19604
19694
|
// src/cli/index.ts
|
|
19605
|
-
var program = new Command().name("mikromcp").description("MikroTik RouterOS MCP server").version("1.14.
|
|
19695
|
+
var program = new Command().name("mikromcp").description("MikroTik RouterOS MCP server").version("1.14.1");
|
|
19606
19696
|
program.command("serve", { isDefault: true }).description("Start the MCP server (default)").action(runServe);
|
|
19607
19697
|
program.command("doctor").description("Check config, connectivity, and permissions").action(runDoctor);
|
|
19608
19698
|
program.command("init").description("Interactive setup wizard").action(runInit);
|