@usex/mikrotik-mcp 4.0.0 → 4.2.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 +1 -1
- package/dist/cli.js +21 -1
- package/dist/index.js +21 -1
- package/dist/shared/{cli-cjrk8g04.js → cli-10a2apr1.js} +1 -1
- package/dist/shared/{cli-athwegwy.js → cli-365rqpy1.js} +21 -40
- package/dist/shared/{library-f5kwsqv2.js → library-ndn9hj45.js} +21 -40
- package/dist/shared/{library-4fpy49qb.js → library-wbht6e2m.js} +1 -1
- package/package.json +8 -8
- package/schemas/tool-catalog.json +2 -2
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ precedence over a password. Full configuration reference:
|
|
|
113
113
|
### From source
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
|
-
git clone https://github.com/
|
|
116
|
+
git clone https://github.com/mikrotik-mcp/mikrotik-mcp && cd mikrotik-mcp
|
|
117
117
|
bun install
|
|
118
118
|
bun run start # serve from source
|
|
119
119
|
bun run build # bundle to dist/
|
package/dist/cli.js
CHANGED
|
@@ -106,7 +106,7 @@ import {
|
|
|
106
106
|
updateAaaEntity,
|
|
107
107
|
updateSummaryLine,
|
|
108
108
|
writeBackup
|
|
109
|
-
} from "./shared/cli-
|
|
109
|
+
} from "./shared/cli-365rqpy1.js";
|
|
110
110
|
|
|
111
111
|
// src/cli.ts
|
|
112
112
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -2723,11 +2723,31 @@ accepts an optional "device" argument to choose which router it runs on; omit it
|
|
|
2723
2723
|
to use the default. Use list_mikrotik_devices to see them. For cross-device work
|
|
2724
2724
|
(e.g. a tunnel between two routers) configure each side by passing the matching
|
|
2725
2725
|
"device", then verify reachability with ping/traceroute from each end.`;
|
|
2726
|
+
var MEMORY_INSTRUCTIONS = `
|
|
2727
|
+
|
|
2728
|
+
Persistent memory \u2014 you have a knowledge graph that survives across sessions
|
|
2729
|
+
(entities, observations, relations in a local SQLite database). USE IT on every
|
|
2730
|
+
task so context is never lost between conversations:
|
|
2731
|
+
1. AT THE START of a task, recall what you already know before touching the
|
|
2732
|
+
device: call \`memory_search_nodes\` for the device/subject at hand (or
|
|
2733
|
+
\`memory_read_graph\` for the whole picture on a fresh device). Apply what you
|
|
2734
|
+
find \u2014 do not re-discover facts you already recorded.
|
|
2735
|
+
2. WHILE WORKING, when you learn a durable fact about the network, a device, a
|
|
2736
|
+
user, or a config pattern (RouterOS version, port layout, VLAN scheme, WAN
|
|
2737
|
+
uplink, owner, recurring fix), record it: \`memory_create_entities\` for new
|
|
2738
|
+
subjects, \`memory_add_observations\` for facts about existing ones, and
|
|
2739
|
+
\`memory_create_relations\` to link them (e.g. router --provides_dhcp_for-->
|
|
2740
|
+
subnet). Every device you touch is auto-added as an entity, so attach
|
|
2741
|
+
observations to it by name.
|
|
2742
|
+
3. Prefer specific, reusable facts over transient state. Skip one-off command
|
|
2743
|
+
output; record what will still be true next session.`;
|
|
2726
2744
|
function createServer(opts = {}) {
|
|
2727
2745
|
process.title = `Mikrotik MCP Server v${VERSION}`;
|
|
2728
2746
|
const { names, default: defaultDevice } = listDevices();
|
|
2729
2747
|
const readOnly = getConfig().readOnly;
|
|
2730
2748
|
let instructions = names.length > 1 ? INSTRUCTIONS + MULTI_DEVICE_INSTRUCTIONS.replace("{{names}}", names.join(", ")).replace("{{default}}", defaultDevice) : INSTRUCTIONS;
|
|
2749
|
+
if (getConfig().memory.enabled)
|
|
2750
|
+
instructions += MEMORY_INSTRUCTIONS;
|
|
2731
2751
|
if (!getConfig().disableUpdateCheck) {
|
|
2732
2752
|
const cached = loadFileCacheSync();
|
|
2733
2753
|
if (cached) {
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
selectToolModules,
|
|
30
30
|
setConfig,
|
|
31
31
|
updateSummaryLine
|
|
32
|
-
} from "./shared/library-
|
|
32
|
+
} from "./shared/library-ndn9hj45.js";
|
|
33
33
|
// src/server.ts
|
|
34
34
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
35
35
|
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -201,11 +201,31 @@ accepts an optional "device" argument to choose which router it runs on; omit it
|
|
|
201
201
|
to use the default. Use list_mikrotik_devices to see them. For cross-device work
|
|
202
202
|
(e.g. a tunnel between two routers) configure each side by passing the matching
|
|
203
203
|
"device", then verify reachability with ping/traceroute from each end.`;
|
|
204
|
+
var MEMORY_INSTRUCTIONS = `
|
|
205
|
+
|
|
206
|
+
Persistent memory \u2014 you have a knowledge graph that survives across sessions
|
|
207
|
+
(entities, observations, relations in a local SQLite database). USE IT on every
|
|
208
|
+
task so context is never lost between conversations:
|
|
209
|
+
1. AT THE START of a task, recall what you already know before touching the
|
|
210
|
+
device: call \`memory_search_nodes\` for the device/subject at hand (or
|
|
211
|
+
\`memory_read_graph\` for the whole picture on a fresh device). Apply what you
|
|
212
|
+
find \u2014 do not re-discover facts you already recorded.
|
|
213
|
+
2. WHILE WORKING, when you learn a durable fact about the network, a device, a
|
|
214
|
+
user, or a config pattern (RouterOS version, port layout, VLAN scheme, WAN
|
|
215
|
+
uplink, owner, recurring fix), record it: \`memory_create_entities\` for new
|
|
216
|
+
subjects, \`memory_add_observations\` for facts about existing ones, and
|
|
217
|
+
\`memory_create_relations\` to link them (e.g. router --provides_dhcp_for-->
|
|
218
|
+
subnet). Every device you touch is auto-added as an entity, so attach
|
|
219
|
+
observations to it by name.
|
|
220
|
+
3. Prefer specific, reusable facts over transient state. Skip one-off command
|
|
221
|
+
output; record what will still be true next session.`;
|
|
204
222
|
function createServer(opts = {}) {
|
|
205
223
|
process.title = `Mikrotik MCP Server v${VERSION}`;
|
|
206
224
|
const { names, default: defaultDevice } = listDevices();
|
|
207
225
|
const readOnly = getConfig().readOnly;
|
|
208
226
|
let instructions = names.length > 1 ? INSTRUCTIONS + MULTI_DEVICE_INSTRUCTIONS.replace("{{names}}", names.join(", ")).replace("{{default}}", defaultDevice) : INSTRUCTIONS;
|
|
227
|
+
if (getConfig().memory.enabled)
|
|
228
|
+
instructions += MEMORY_INSTRUCTIONS;
|
|
209
229
|
if (!getConfig().disableUpdateCheck) {
|
|
210
230
|
const cached = loadFileCacheSync();
|
|
211
231
|
if (cached) {
|
|
@@ -8364,7 +8364,7 @@ var cache = null;
|
|
|
8364
8364
|
async function gateway() {
|
|
8365
8365
|
if (cache)
|
|
8366
8366
|
return cache;
|
|
8367
|
-
const { moduleCatalog } = await import("./cli-
|
|
8367
|
+
const { moduleCatalog } = await import("./cli-10a2apr1.js");
|
|
8368
8368
|
const forIndex = [];
|
|
8369
8369
|
const byName = new Map;
|
|
8370
8370
|
for (const mod of moduleCatalog) {
|
|
@@ -10158,23 +10158,26 @@ Management access may be partially restricted \u2014 review immediately.`;
|
|
|
10158
10158
|
|
|
10159
10159
|
// src/tools/firewall-filter.ts
|
|
10160
10160
|
import { z as z28 } from "zod";
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
return count.trim() !== "0" ? ruleId : null;
|
|
10161
|
+
|
|
10162
|
+
// src/tools/_resolve-rule-id.ts
|
|
10163
|
+
function ruleResolver(scope) {
|
|
10164
|
+
return async function resolveRuleId(ruleId, ctx) {
|
|
10165
|
+
if (/^\d+$/.test(ruleId)) {
|
|
10166
|
+
const idsRaw = await executeMikrotikCommand(`:foreach i in=[${scope} find] do={:put $i}`, ctx);
|
|
10167
|
+
if (isEmpty(idsRaw))
|
|
10168
|
+
return null;
|
|
10169
|
+
const ids = idsRaw.trim().split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
10170
|
+
const pos = Number.parseInt(ruleId, 10);
|
|
10171
|
+
return pos >= 0 && pos < ids.length ? ids[pos] : null;
|
|
10172
|
+
}
|
|
10173
|
+
const count = await executeMikrotikCommand(`${scope} print count-only where .id=${ruleId}`, ctx);
|
|
10174
|
+
return count.trim() !== "0" ? ruleId : null;
|
|
10175
|
+
};
|
|
10177
10176
|
}
|
|
10177
|
+
|
|
10178
|
+
// src/tools/firewall-filter.ts
|
|
10179
|
+
var isDigits = (s) => /^\d+$/.test(s);
|
|
10180
|
+
var resolveFilterRuleId = ruleResolver("/ip firewall filter");
|
|
10178
10181
|
async function updateFilterRule(a, ctx) {
|
|
10179
10182
|
ctx.info(`Updating firewall filter rule: rule_id=${a.rule_id}`);
|
|
10180
10183
|
const updates = [];
|
|
@@ -10561,28 +10564,6 @@ ${results.join(`
|
|
|
10561
10564
|
|
|
10562
10565
|
// src/tools/firewall-nat.ts
|
|
10563
10566
|
import { z as z29 } from "zod";
|
|
10564
|
-
|
|
10565
|
-
// src/tools/_resolve-rule-id.ts
|
|
10566
|
-
function ruleResolver(scope) {
|
|
10567
|
-
return async function resolveRuleId(ruleId, ctx) {
|
|
10568
|
-
if (/^\d+$/.test(ruleId)) {
|
|
10569
|
-
const id = `*${ruleId}`;
|
|
10570
|
-
const byId = await executeMikrotikCommand(`${scope} print count-only where .id=${id}`, ctx);
|
|
10571
|
-
if (byId.trim() !== "0")
|
|
10572
|
-
return id;
|
|
10573
|
-
const idsRaw = await executeMikrotikCommand(`:foreach i in=[${scope} find] do={:put $i}`, ctx);
|
|
10574
|
-
if (isEmpty(idsRaw))
|
|
10575
|
-
return null;
|
|
10576
|
-
const ids = idsRaw.trim().split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
10577
|
-
const pos = Number.parseInt(ruleId, 10);
|
|
10578
|
-
return pos >= 0 && pos < ids.length ? ids[pos] : null;
|
|
10579
|
-
}
|
|
10580
|
-
const count = await executeMikrotikCommand(`${scope} print count-only where .id=${ruleId}`, ctx);
|
|
10581
|
-
return count.trim() !== "0" ? ruleId : null;
|
|
10582
|
-
};
|
|
10583
|
-
}
|
|
10584
|
-
|
|
10585
|
-
// src/tools/firewall-nat.ts
|
|
10586
10567
|
var isDigits2 = (s) => /^\d+$/.test(s);
|
|
10587
10568
|
var resolveNatRuleId = ruleResolver("/ip firewall nat");
|
|
10588
10569
|
async function updateNatRule(a, ctx) {
|
|
@@ -24967,7 +24948,7 @@ var SERVER_NAME = "mikrotik-mcp";
|
|
|
24967
24948
|
var PKG_META = pkg;
|
|
24968
24949
|
|
|
24969
24950
|
// src/core/update-check.ts
|
|
24970
|
-
var GITHUB_API = "https://api.github.com/repos/
|
|
24951
|
+
var GITHUB_API = "https://api.github.com/repos/mikrotik-mcp/mikrotik-mcp/releases/latest";
|
|
24971
24952
|
var MEMORY_CACHE_TTL = 15 * 60 * 1000;
|
|
24972
24953
|
var FILE_CACHE_TTL = 6 * 60 * 60 * 1000;
|
|
24973
24954
|
var CACHE_PATH = join6(homedir2(), ".mikrotik-mcp", "update-check.json");
|
|
@@ -8294,7 +8294,7 @@ var cache = null;
|
|
|
8294
8294
|
async function gateway() {
|
|
8295
8295
|
if (cache)
|
|
8296
8296
|
return cache;
|
|
8297
|
-
const { moduleCatalog } = await import("./library-
|
|
8297
|
+
const { moduleCatalog } = await import("./library-wbht6e2m.js");
|
|
8298
8298
|
const forIndex = [];
|
|
8299
8299
|
const byName = new Map;
|
|
8300
8300
|
for (const mod of moduleCatalog) {
|
|
@@ -10088,23 +10088,26 @@ Management access may be partially restricted \u2014 review immediately.`;
|
|
|
10088
10088
|
|
|
10089
10089
|
// src/tools/firewall-filter.ts
|
|
10090
10090
|
import { z as z28 } from "zod";
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
return count.trim() !== "0" ? ruleId : null;
|
|
10091
|
+
|
|
10092
|
+
// src/tools/_resolve-rule-id.ts
|
|
10093
|
+
function ruleResolver(scope) {
|
|
10094
|
+
return async function resolveRuleId(ruleId, ctx) {
|
|
10095
|
+
if (/^\d+$/.test(ruleId)) {
|
|
10096
|
+
const idsRaw = await executeMikrotikCommand(`:foreach i in=[${scope} find] do={:put $i}`, ctx);
|
|
10097
|
+
if (isEmpty(idsRaw))
|
|
10098
|
+
return null;
|
|
10099
|
+
const ids = idsRaw.trim().split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
10100
|
+
const pos = Number.parseInt(ruleId, 10);
|
|
10101
|
+
return pos >= 0 && pos < ids.length ? ids[pos] : null;
|
|
10102
|
+
}
|
|
10103
|
+
const count = await executeMikrotikCommand(`${scope} print count-only where .id=${ruleId}`, ctx);
|
|
10104
|
+
return count.trim() !== "0" ? ruleId : null;
|
|
10105
|
+
};
|
|
10107
10106
|
}
|
|
10107
|
+
|
|
10108
|
+
// src/tools/firewall-filter.ts
|
|
10109
|
+
var isDigits = (s) => /^\d+$/.test(s);
|
|
10110
|
+
var resolveFilterRuleId = ruleResolver("/ip firewall filter");
|
|
10108
10111
|
async function updateFilterRule(a, ctx) {
|
|
10109
10112
|
ctx.info(`Updating firewall filter rule: rule_id=${a.rule_id}`);
|
|
10110
10113
|
const updates = [];
|
|
@@ -10491,28 +10494,6 @@ ${results.join(`
|
|
|
10491
10494
|
|
|
10492
10495
|
// src/tools/firewall-nat.ts
|
|
10493
10496
|
import { z as z29 } from "zod";
|
|
10494
|
-
|
|
10495
|
-
// src/tools/_resolve-rule-id.ts
|
|
10496
|
-
function ruleResolver(scope) {
|
|
10497
|
-
return async function resolveRuleId(ruleId, ctx) {
|
|
10498
|
-
if (/^\d+$/.test(ruleId)) {
|
|
10499
|
-
const id = `*${ruleId}`;
|
|
10500
|
-
const byId = await executeMikrotikCommand(`${scope} print count-only where .id=${id}`, ctx);
|
|
10501
|
-
if (byId.trim() !== "0")
|
|
10502
|
-
return id;
|
|
10503
|
-
const idsRaw = await executeMikrotikCommand(`:foreach i in=[${scope} find] do={:put $i}`, ctx);
|
|
10504
|
-
if (isEmpty(idsRaw))
|
|
10505
|
-
return null;
|
|
10506
|
-
const ids = idsRaw.trim().split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
10507
|
-
const pos = Number.parseInt(ruleId, 10);
|
|
10508
|
-
return pos >= 0 && pos < ids.length ? ids[pos] : null;
|
|
10509
|
-
}
|
|
10510
|
-
const count = await executeMikrotikCommand(`${scope} print count-only where .id=${ruleId}`, ctx);
|
|
10511
|
-
return count.trim() !== "0" ? ruleId : null;
|
|
10512
|
-
};
|
|
10513
|
-
}
|
|
10514
|
-
|
|
10515
|
-
// src/tools/firewall-nat.ts
|
|
10516
10497
|
var isDigits2 = (s) => /^\d+$/.test(s);
|
|
10517
10498
|
var resolveNatRuleId = ruleResolver("/ip firewall nat");
|
|
10518
10499
|
async function updateNatRule(a, ctx) {
|
|
@@ -24896,7 +24877,7 @@ var SERVER_DESCRIPTION = pkg.description ?? "";
|
|
|
24896
24877
|
var SERVER_NAME = "mikrotik-mcp";
|
|
24897
24878
|
|
|
24898
24879
|
// src/core/update-check.ts
|
|
24899
|
-
var GITHUB_API = "https://api.github.com/repos/
|
|
24880
|
+
var GITHUB_API = "https://api.github.com/repos/mikrotik-mcp/mikrotik-mcp/releases/latest";
|
|
24900
24881
|
var MEMORY_CACHE_TTL = 15 * 60 * 1000;
|
|
24901
24882
|
var FILE_CACHE_TTL = 6 * 60 * 60 * 1000;
|
|
24902
24883
|
var CACHE_PATH = join6(homedir2(), ".mikrotik-mcp", "update-check.json");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usex/mikrotik-mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "MCP server for MikroTik RouterOS — 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"homepage": "https://mikrotik-mcp.usestrict.dev",
|
|
14
14
|
"bugs": {
|
|
15
|
-
"url": "https://github.com/
|
|
15
|
+
"url": "https://github.com/mikrotik-mcp/mikrotik-mcp/issues"
|
|
16
16
|
},
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"author": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "https://github.com/
|
|
25
|
+
"url": "https://github.com/mikrotik-mcp/mikrotik-mcp.git"
|
|
26
26
|
},
|
|
27
27
|
"bin": {
|
|
28
28
|
"mikrotik-mcp": "dist/cli.js"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"build": "bunup && chmod +x dist/cli.js && bun run build:ui",
|
|
51
51
|
"build:ui": "bun run --bun scripts/build-ui.ts",
|
|
52
52
|
"dev": "bunup --watch",
|
|
53
|
-
"prepack": "bunup && chmod +x dist/cli.js && bun run build:ui",
|
|
53
|
+
"prepack": "bunup && chmod +x dist/cli.js && bun run build:ui && bun run gen",
|
|
54
54
|
"auth-check": "bun run --bun src/cli.ts auth-check",
|
|
55
55
|
"gen:schemas": "bun run --bun scripts/gen-schemas.ts",
|
|
56
56
|
"gen:docs": "bun run --bun scripts/gen-tool-docs.ts",
|
|
@@ -117,8 +117,8 @@
|
|
|
117
117
|
},
|
|
118
118
|
"packageManager": "bun@1.3.14",
|
|
119
119
|
"catalog": {
|
|
120
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@0.2.
|
|
121
|
-
"vite-plus": "0.2.
|
|
120
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.2.3",
|
|
121
|
+
"vite-plus": "0.2.3"
|
|
122
122
|
},
|
|
123
123
|
"changelog": {
|
|
124
124
|
"labels": {
|
|
@@ -132,6 +132,6 @@
|
|
|
132
132
|
"breaking change": "Breaking changes"
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
|
-
"logo": "https://raw.githubusercontent.com/
|
|
136
|
-
"logoIcon": "https://raw.githubusercontent.com/
|
|
135
|
+
"logo": "https://raw.githubusercontent.com/mikrotik-mcp/mikrotik-mcp/master/assets/logo.svg",
|
|
136
|
+
"logoIcon": "https://raw.githubusercontent.com/mikrotik-mcp/mikrotik-mcp/master/assets/logo-icon.svg"
|
|
137
137
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"generated": "by scripts/gen-schemas.ts — do not edit by hand",
|
|
5
5
|
"toolCount": 794,
|
|
6
6
|
"tools": [
|
|
@@ -3477,7 +3477,7 @@
|
|
|
3477
3477
|
"idempotentHint": true,
|
|
3478
3478
|
"openWorldHint": false
|
|
3479
3479
|
},
|
|
3480
|
-
"description": "Resolves which nexthop RouterOS would use for a given IPv4 destination — answers \"which gateway will this packet take?\" without sending any traffic. Version-aware: uses `/ip route check` on v6, falls back to `/ip route print where dst-address
|
|
3480
|
+
"description": "Resolves which nexthop RouterOS would use for a given IPv4 destination — answers \"which gateway will this packet take?\" without sending any traffic. Version-aware: uses `/ip route check` on v6, falls back to `/ip route print where <dest> in dst-address active=yes` on v7+ where the check command was removed. Optionally scoped by `routing_table` (v7) / `routing_mark` (v6) for policy-routing table lookups. For listing all known routes use list_routes; for a named-table view use get_routing_table. Returns the resolved nexthop and interface detail.",
|
|
3481
3481
|
"inputSchema": {
|
|
3482
3482
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3483
3483
|
"type": "object",
|