@usex/mikrotik-mcp 3.56.0 → 3.58.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 +78 -66
- package/dist/index.d.ts +1 -1
- package/dist/index.js +45 -19
- package/dist/shared/{cli-thhk9f7n.js → cli-r5p22dag.js} +825 -595
- package/dist/shared/{cli-0ctwspf1.js → cli-zq0rg72c.js} +1 -1
- package/dist/shared/{library-jve65pzw.js → library-41tn0b6b.js} +1 -1
- package/dist/shared/{library-ymtf9zyk.js → library-84jwmz7a.js} +824 -595
- package/dist/ui/aaa.html +112 -4
- package/dist/ui/connected-devices.html +112 -4
- package/dist/ui/dashboard.html +112 -3
- package/dist/ui/firewall-audit.html +112 -4
- package/dist/ui/firewall.html +113 -4
- package/dist/ui/interfaces.html +112 -4
- package/dist/ui/observability.html +1 -1
- package/dist/ui/records.html +113 -4
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -8,12 +8,18 @@ import {
|
|
|
8
8
|
DEFAULT_SNAPSHOT_DB,
|
|
9
9
|
DEFAULT_TZSP_PORT,
|
|
10
10
|
DeviceConfigSchema,
|
|
11
|
+
LOGO_URL,
|
|
11
12
|
MikrotikConfigSchema,
|
|
12
|
-
|
|
13
|
+
PKG_META,
|
|
13
14
|
PROMPTS_DIR,
|
|
14
15
|
REDACTED,
|
|
16
|
+
SERVER_DESCRIPTION,
|
|
17
|
+
SERVER_NAME,
|
|
18
|
+
SERVER_TITLE,
|
|
15
19
|
ToolFilterSchema,
|
|
16
20
|
UI_DIST_DIR,
|
|
21
|
+
VERSION,
|
|
22
|
+
WEBSITE_URL,
|
|
17
23
|
__require,
|
|
18
24
|
addAaaEntity,
|
|
19
25
|
allToolModules,
|
|
@@ -24,6 +30,7 @@ import {
|
|
|
24
30
|
blockDevice,
|
|
25
31
|
buildChangePlan,
|
|
26
32
|
capture,
|
|
33
|
+
checkForUpdate,
|
|
27
34
|
closeAll,
|
|
28
35
|
closeMemoryStore,
|
|
29
36
|
commandUnsupported,
|
|
@@ -39,6 +46,7 @@ import {
|
|
|
39
46
|
diffLines,
|
|
40
47
|
executeMikrotikCommand,
|
|
41
48
|
fetchDevices,
|
|
49
|
+
fetchLatestRelease,
|
|
42
50
|
getConfig,
|
|
43
51
|
getConfigSource,
|
|
44
52
|
getEventStore,
|
|
@@ -54,6 +62,7 @@ import {
|
|
|
54
62
|
listBackups,
|
|
55
63
|
listDevices,
|
|
56
64
|
loadConfig,
|
|
65
|
+
loadFileCacheSync,
|
|
57
66
|
logger,
|
|
58
67
|
looksLikeError,
|
|
59
68
|
makeDeviceStatic,
|
|
@@ -95,16 +104,17 @@ import {
|
|
|
95
104
|
subscriberCount,
|
|
96
105
|
toggleAaaEntity,
|
|
97
106
|
updateAaaEntity,
|
|
107
|
+
updateSummaryLine,
|
|
98
108
|
writeBackup
|
|
99
|
-
} from "./shared/cli-
|
|
109
|
+
} from "./shared/cli-r5p22dag.js";
|
|
100
110
|
|
|
101
111
|
// src/cli.ts
|
|
102
112
|
import { existsSync as existsSync2 } from "fs";
|
|
103
113
|
|
|
104
114
|
// src/observability/dashboard.ts
|
|
105
|
-
import { readFileSync as
|
|
115
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
106
116
|
import { homedir, networkInterfaces } from "os";
|
|
107
|
-
import { dirname as dirname4, join as
|
|
117
|
+
import { dirname as dirname4, join as join2 } from "path";
|
|
108
118
|
var {serve } = globalThis.Bun;
|
|
109
119
|
import { z } from "zod";
|
|
110
120
|
|
|
@@ -1148,18 +1158,6 @@ function computeStats(events, opts) {
|
|
|
1148
1158
|
};
|
|
1149
1159
|
}
|
|
1150
1160
|
|
|
1151
|
-
// src/version.ts
|
|
1152
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
1153
|
-
import { join as join2 } from "path";
|
|
1154
|
-
var pkg = JSON.parse(readFileSync2(join2(PROJECT_ROOT, "package.json"), "utf-8"));
|
|
1155
|
-
var VERSION = pkg.version ?? "0.0.0";
|
|
1156
|
-
var WEBSITE_URL = pkg.homepage ?? "";
|
|
1157
|
-
var LOGO_URL = pkg.logoIcon ?? "";
|
|
1158
|
-
var SERVER_TITLE = pkg.name ?? "mcp-mikrotik";
|
|
1159
|
-
var SERVER_DESCRIPTION = pkg.description ?? "";
|
|
1160
|
-
var SERVER_NAME = "mcp-mikrotik";
|
|
1161
|
-
var PKG_META = pkg;
|
|
1162
|
-
|
|
1163
1161
|
// src/observability/drift-routes.ts
|
|
1164
1162
|
var storePromise = null;
|
|
1165
1163
|
function getStore() {
|
|
@@ -1425,51 +1423,13 @@ async function memoryRoutes(req, url) {
|
|
|
1425
1423
|
|
|
1426
1424
|
// src/observability/dashboard.ts
|
|
1427
1425
|
var SERVER_TAG2 = "mikrotik-mcp";
|
|
1428
|
-
var releaseCache = null;
|
|
1429
|
-
var RELEASE_CACHE_TTL = 15 * 60 * 1000;
|
|
1430
|
-
function compareVersions(a, b) {
|
|
1431
|
-
const pa = a.replace(/^v/, "").split(".").map(Number);
|
|
1432
|
-
const pb = b.replace(/^v/, "").split(".").map(Number);
|
|
1433
|
-
for (let i = 0;i < Math.max(pa.length, pb.length); i++) {
|
|
1434
|
-
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1435
|
-
if (diff !== 0)
|
|
1436
|
-
return diff;
|
|
1437
|
-
}
|
|
1438
|
-
return 0;
|
|
1439
|
-
}
|
|
1440
|
-
async function fetchLatestRelease() {
|
|
1441
|
-
if (releaseCache && Date.now() - releaseCache.fetchedAt < RELEASE_CACHE_TTL) {
|
|
1442
|
-
return releaseCache.data;
|
|
1443
|
-
}
|
|
1444
|
-
const res = await fetch("https://api.github.com/repos/ali-master/mikrotik-mcp/releases/latest", {
|
|
1445
|
-
headers: {
|
|
1446
|
-
accept: "application/vnd.github+json",
|
|
1447
|
-
"user-agent": "mikrotik-mcp-dashboard"
|
|
1448
|
-
}
|
|
1449
|
-
});
|
|
1450
|
-
if (!res.ok)
|
|
1451
|
-
throw new Error(`GitHub API ${res.status}`);
|
|
1452
|
-
const gh = await res.json();
|
|
1453
|
-
const latestVersion = gh.tag_name.replace(/^v/, "");
|
|
1454
|
-
const data = {
|
|
1455
|
-
version: latestVersion,
|
|
1456
|
-
name: gh.name || `v${latestVersion}`,
|
|
1457
|
-
body: gh.body || "",
|
|
1458
|
-
publishedAt: gh.published_at,
|
|
1459
|
-
url: gh.html_url,
|
|
1460
|
-
isNewer: compareVersions(latestVersion, VERSION) > 0,
|
|
1461
|
-
currentVersion: VERSION
|
|
1462
|
-
};
|
|
1463
|
-
releaseCache = { data, fetchedAt: Date.now() };
|
|
1464
|
-
return data;
|
|
1465
|
-
}
|
|
1466
1426
|
var JSON_HEADERS3 = { "content-type": "application/json; charset=utf-8" };
|
|
1467
1427
|
function json3(body, status = 200) {
|
|
1468
1428
|
return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS3 });
|
|
1469
1429
|
}
|
|
1470
1430
|
function dashboardHtml() {
|
|
1471
1431
|
try {
|
|
1472
|
-
return
|
|
1432
|
+
return readFileSync2(join2(UI_DIST_DIR, "observability.html"), "utf8");
|
|
1473
1433
|
} catch {
|
|
1474
1434
|
return `<!doctype html><meta charset=utf-8><body style="font:14px system-ui;padding:24px;background:#0b0d10;color:#e8eaed">
|
|
1475
1435
|
<h2>MikroTik MCP \u2014 Observability Dashboard</h2>
|
|
@@ -2156,7 +2116,7 @@ async function featureRoutes(req, url) {
|
|
|
2156
2116
|
const raw = b?.dir?.trim();
|
|
2157
2117
|
if (!raw)
|
|
2158
2118
|
return json3({ error: "dir required" }, 400);
|
|
2159
|
-
const dir = raw === "~" || raw.startsWith("~/") ?
|
|
2119
|
+
const dir = raw === "~" || raw.startsWith("~/") ? join2(homedir(), raw.slice(1)) : raw;
|
|
2160
2120
|
const next = { ...getConfig(), backupDir: dir };
|
|
2161
2121
|
setConfig(next);
|
|
2162
2122
|
try {
|
|
@@ -2264,7 +2224,7 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
2264
2224
|
});
|
|
2265
2225
|
startHealthChecks(30000);
|
|
2266
2226
|
try {
|
|
2267
|
-
usageStore = await openUsageStore(
|
|
2227
|
+
usageStore = await openUsageStore(join2(dirname4(cfg.dbPath), "usage.db"));
|
|
2268
2228
|
startUsageSampler(usageStore);
|
|
2269
2229
|
} catch (e) {
|
|
2270
2230
|
logger.warn(`[${SERVER_TAG2}] usage history disabled: ${String(e)}`);
|
|
@@ -2281,7 +2241,7 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
2281
2241
|
source: getConfigSource,
|
|
2282
2242
|
readFile: (pth) => {
|
|
2283
2243
|
try {
|
|
2284
|
-
return
|
|
2244
|
+
return readFileSync2(pth, "utf8");
|
|
2285
2245
|
} catch {
|
|
2286
2246
|
return null;
|
|
2287
2247
|
}
|
|
@@ -2564,8 +2524,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2564
2524
|
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
2565
2525
|
|
|
2566
2526
|
// src/prompts/index.ts
|
|
2567
|
-
import { readFileSync as
|
|
2568
|
-
import { join as
|
|
2527
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
2528
|
+
import { join as join3 } from "path";
|
|
2569
2529
|
import { z as z2 } from "zod";
|
|
2570
2530
|
function parseFrontmatter(raw) {
|
|
2571
2531
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
@@ -2625,18 +2585,30 @@ function substitute(body, vars) {
|
|
|
2625
2585
|
return typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
2626
2586
|
});
|
|
2627
2587
|
}
|
|
2628
|
-
function
|
|
2588
|
+
function deviceArgDescription(directory) {
|
|
2589
|
+
if (directory && directory.length > 0) {
|
|
2590
|
+
const rows = directory.map((d) => `\u2022 ${d.key}${d.label && d.label !== d.key ? ` ("${d.label}")` : ""} \u2192 ${d.target}${d.isDefault ? " [default]" : ""}`).join(`
|
|
2591
|
+
`);
|
|
2592
|
+
return "Which configured MikroTik device to run this workflow on. " + `Pass the EXACT config key or its label. Configured devices:
|
|
2593
|
+
` + `${rows}
|
|
2594
|
+
` + "Omit to use the default device.";
|
|
2595
|
+
}
|
|
2596
|
+
return "Which configured MikroTik device to run this workflow on. Omit to use the default device.";
|
|
2597
|
+
}
|
|
2598
|
+
function registerPrompts(server, opts = {}) {
|
|
2629
2599
|
let files;
|
|
2630
2600
|
try {
|
|
2631
2601
|
files = readdirSync2(PROMPTS_DIR).filter((f) => f.endsWith(".md"));
|
|
2632
2602
|
} catch {
|
|
2633
2603
|
return 0;
|
|
2634
2604
|
}
|
|
2605
|
+
const multiDevice = opts.deviceNames && opts.deviceNames.length > 1;
|
|
2606
|
+
const selectorNames = multiDevice ? [...new Set([...opts.deviceNames, ...opts.deviceAliases ?? []])] : [];
|
|
2635
2607
|
let count = 0;
|
|
2636
2608
|
for (const file of files) {
|
|
2637
2609
|
let parsed;
|
|
2638
2610
|
try {
|
|
2639
|
-
parsed = parseFrontmatter(
|
|
2611
|
+
parsed = parseFrontmatter(readFileSync3(join3(PROMPTS_DIR, file), "utf8"));
|
|
2640
2612
|
} catch (e) {
|
|
2641
2613
|
logger.warn(`Skipping prompt ${file}: ${String(e)}`);
|
|
2642
2614
|
continue;
|
|
@@ -2650,6 +2622,10 @@ function registerPrompts(server) {
|
|
|
2650
2622
|
const s = z2.string().describe(arg.description ?? "");
|
|
2651
2623
|
argsSchema[arg.name] = arg.required ? s : s.optional();
|
|
2652
2624
|
}
|
|
2625
|
+
const hasOwnDevice = parsed.arguments.some((a) => a.name === "device" || a.name === "device_a");
|
|
2626
|
+
if (multiDevice && !hasOwnDevice) {
|
|
2627
|
+
argsSchema.device = z2.enum(selectorNames).optional().describe(deviceArgDescription(opts.deviceDirectory));
|
|
2628
|
+
}
|
|
2653
2629
|
server.registerPrompt(parsed.name, { title: parsed.title, description: parsed.description, argsSchema }, (args) => ({
|
|
2654
2630
|
messages: [
|
|
2655
2631
|
{
|
|
@@ -2718,7 +2694,17 @@ function createServer(opts = {}) {
|
|
|
2718
2694
|
process.title = `Mikrotik MCP Server v${VERSION}`;
|
|
2719
2695
|
const { names, default: defaultDevice } = listDevices();
|
|
2720
2696
|
const readOnly = getConfig().readOnly;
|
|
2721
|
-
|
|
2697
|
+
let instructions = names.length > 1 ? INSTRUCTIONS + MULTI_DEVICE_INSTRUCTIONS.replace("{{names}}", names.join(", ")).replace("{{default}}", defaultDevice) : INSTRUCTIONS;
|
|
2698
|
+
if (!getConfig().disableUpdateCheck) {
|
|
2699
|
+
const cached = loadFileCacheSync();
|
|
2700
|
+
if (cached) {
|
|
2701
|
+
const note = updateSummaryLine(cached);
|
|
2702
|
+
if (note)
|
|
2703
|
+
instructions += `
|
|
2704
|
+
|
|
2705
|
+
${note}`;
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2722
2708
|
const server = new McpServer({
|
|
2723
2709
|
name: SERVER_NAME,
|
|
2724
2710
|
version: VERSION,
|
|
@@ -2728,8 +2714,8 @@ function createServer(opts = {}) {
|
|
|
2728
2714
|
icons: [
|
|
2729
2715
|
{
|
|
2730
2716
|
src: LOGO_URL,
|
|
2731
|
-
mimeType: "image/
|
|
2732
|
-
sizes: ["
|
|
2717
|
+
mimeType: "image/svg+xml",
|
|
2718
|
+
sizes: ["any"],
|
|
2733
2719
|
theme: "light"
|
|
2734
2720
|
}
|
|
2735
2721
|
]
|
|
@@ -2751,7 +2737,11 @@ function createServer(opts = {}) {
|
|
|
2751
2737
|
appViews: getConfig().mcp.appViews,
|
|
2752
2738
|
readOnly
|
|
2753
2739
|
});
|
|
2754
|
-
const promptCount = registerPrompts(server
|
|
2740
|
+
const promptCount = registerPrompts(server, {
|
|
2741
|
+
deviceNames: names,
|
|
2742
|
+
deviceAliases: deviceLabels(),
|
|
2743
|
+
deviceDirectory: deviceDirectory()
|
|
2744
|
+
});
|
|
2755
2745
|
const uiViewCount = registerUiResources(server);
|
|
2756
2746
|
installToolPagination(server, getConfig().mcp.toolPageSize);
|
|
2757
2747
|
return { server, toolCount, promptCount, uiViewCount, readOnly };
|
|
@@ -2866,6 +2856,13 @@ async function runHttp(mcp) {
|
|
|
2866
2856
|
}
|
|
2867
2857
|
});
|
|
2868
2858
|
logger.info(`MCP server ready on http://${mcp.host}:${mcp.port}${mcpPath} \u2014 ${toolCount} tools, ${promptCount} prompts, ${uiViewCount} app views ` + `(${mcp.transport})${readOnly ? " [READ-ONLY]" : ""}`);
|
|
2859
|
+
if (!getConfig().disableUpdateCheck) {
|
|
2860
|
+
checkForUpdate().then((r) => {
|
|
2861
|
+
if (r.release?.isNewer) {
|
|
2862
|
+
logger.info(`[update] MikroTik MCP v${r.release.version} available (running v${VERSION}). ` + `Upgrade: bun i -g @usex/mikrotik-mcp@latest`);
|
|
2863
|
+
}
|
|
2864
|
+
}).catch(() => {});
|
|
2865
|
+
}
|
|
2869
2866
|
}
|
|
2870
2867
|
|
|
2871
2868
|
// src/transport/stdio.ts
|
|
@@ -2875,6 +2872,21 @@ async function runStdio() {
|
|
|
2875
2872
|
const transport = new StdioServerTransport;
|
|
2876
2873
|
await server.connect(transport);
|
|
2877
2874
|
logger.info(`MCP server ready on stdio \u2014 ${toolCount} tools, ${promptCount} prompts, ${uiViewCount} app views${readOnly ? " [READ-ONLY]" : ""}`);
|
|
2875
|
+
if (!getConfig().disableUpdateCheck) {
|
|
2876
|
+
whisperUpdate(server).catch(() => {});
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
async function whisperUpdate(server) {
|
|
2880
|
+
const result = await checkForUpdate();
|
|
2881
|
+
if (!result.release?.isNewer)
|
|
2882
|
+
return;
|
|
2883
|
+
const r = result.release;
|
|
2884
|
+
try {
|
|
2885
|
+
server.server.sendLoggingMessage({
|
|
2886
|
+
level: "info",
|
|
2887
|
+
data: `[MikroTik MCP] Update available: v${r.version} (running v${VERSION}). ` + `${r.name ? `"${r.name}" \u2014 ` : ""}` + `Call check_server_pulse for release notes, or upgrade: bun i -g @usex/mikrotik-mcp@latest`
|
|
2888
|
+
}).catch(() => {});
|
|
2889
|
+
} catch {}
|
|
2878
2890
|
}
|
|
2879
2891
|
|
|
2880
2892
|
// src/cli-logo.ts
|
package/dist/index.d.ts
CHANGED
|
@@ -324,5 +324,5 @@ declare class SafeModeManager {
|
|
|
324
324
|
declare function getSafeModeManager(deviceName: string): SafeModeManager;
|
|
325
325
|
declare const allToolModules: ToolModule[];
|
|
326
326
|
declare const VERSION: string;
|
|
327
|
-
declare const SERVER_NAME = "mcp
|
|
327
|
+
declare const SERVER_NAME = "mikrotik-mcp";
|
|
328
328
|
export { setConfig, resolveDeviceName, registerTools, loadConfig, listDevices, getSafeModeManager, getDevice, getConfig, executeMikrotikCommand, defineTool, createServer, allToolModules, VERSION, ToolModule, ToolContext, SafeModeManager, SERVER_NAME, RegisterableTool, MikrotikConfigSchema, MikrotikConfig, MikroTikSSHClient, DeviceConfigSchema, DeviceConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
DeviceConfigSchema,
|
|
4
|
+
LOGO_URL,
|
|
4
5
|
MikroTikSSHClient,
|
|
5
6
|
MikrotikConfigSchema,
|
|
6
|
-
PROJECT_ROOT,
|
|
7
7
|
PROMPTS_DIR,
|
|
8
|
+
SERVER_DESCRIPTION,
|
|
9
|
+
SERVER_NAME,
|
|
10
|
+
SERVER_TITLE,
|
|
8
11
|
SafeModeManager,
|
|
12
|
+
VERSION,
|
|
13
|
+
WEBSITE_URL,
|
|
9
14
|
allToolModules,
|
|
10
15
|
defineTool,
|
|
11
16
|
deviceDirectory,
|
|
@@ -16,13 +21,15 @@ import {
|
|
|
16
21
|
getSafeModeManager,
|
|
17
22
|
listDevices,
|
|
18
23
|
loadConfig,
|
|
24
|
+
loadFileCacheSync,
|
|
19
25
|
logger,
|
|
20
26
|
registerTools,
|
|
21
27
|
registerUiResources,
|
|
22
28
|
resolveDeviceName,
|
|
23
29
|
selectToolModules,
|
|
24
|
-
setConfig
|
|
25
|
-
|
|
30
|
+
setConfig,
|
|
31
|
+
updateSummaryLine
|
|
32
|
+
} from "./shared/library-84jwmz7a.js";
|
|
26
33
|
// src/server.ts
|
|
27
34
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28
35
|
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -89,13 +96,25 @@ function substitute(body, vars) {
|
|
|
89
96
|
return typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
90
97
|
});
|
|
91
98
|
}
|
|
92
|
-
function
|
|
99
|
+
function deviceArgDescription(directory) {
|
|
100
|
+
if (directory && directory.length > 0) {
|
|
101
|
+
const rows = directory.map((d) => `\u2022 ${d.key}${d.label && d.label !== d.key ? ` ("${d.label}")` : ""} \u2192 ${d.target}${d.isDefault ? " [default]" : ""}`).join(`
|
|
102
|
+
`);
|
|
103
|
+
return "Which configured MikroTik device to run this workflow on. " + `Pass the EXACT config key or its label. Configured devices:
|
|
104
|
+
` + `${rows}
|
|
105
|
+
` + "Omit to use the default device.";
|
|
106
|
+
}
|
|
107
|
+
return "Which configured MikroTik device to run this workflow on. Omit to use the default device.";
|
|
108
|
+
}
|
|
109
|
+
function registerPrompts(server, opts = {}) {
|
|
93
110
|
let files;
|
|
94
111
|
try {
|
|
95
112
|
files = readdirSync(PROMPTS_DIR).filter((f) => f.endsWith(".md"));
|
|
96
113
|
} catch {
|
|
97
114
|
return 0;
|
|
98
115
|
}
|
|
116
|
+
const multiDevice = opts.deviceNames && opts.deviceNames.length > 1;
|
|
117
|
+
const selectorNames = multiDevice ? [...new Set([...opts.deviceNames, ...opts.deviceAliases ?? []])] : [];
|
|
99
118
|
let count = 0;
|
|
100
119
|
for (const file of files) {
|
|
101
120
|
let parsed;
|
|
@@ -114,6 +133,10 @@ function registerPrompts(server) {
|
|
|
114
133
|
const s = z.string().describe(arg.description ?? "");
|
|
115
134
|
argsSchema[arg.name] = arg.required ? s : s.optional();
|
|
116
135
|
}
|
|
136
|
+
const hasOwnDevice = parsed.arguments.some((a) => a.name === "device" || a.name === "device_a");
|
|
137
|
+
if (multiDevice && !hasOwnDevice) {
|
|
138
|
+
argsSchema.device = z.enum(selectorNames).optional().describe(deviceArgDescription(opts.deviceDirectory));
|
|
139
|
+
}
|
|
117
140
|
server.registerPrompt(parsed.name, { title: parsed.title, description: parsed.description, argsSchema }, (args) => ({
|
|
118
141
|
messages: [
|
|
119
142
|
{
|
|
@@ -130,17 +153,6 @@ function registerPrompts(server) {
|
|
|
130
153
|
return count;
|
|
131
154
|
}
|
|
132
155
|
|
|
133
|
-
// src/version.ts
|
|
134
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
135
|
-
import { join as join2 } from "path";
|
|
136
|
-
var pkg = JSON.parse(readFileSync2(join2(PROJECT_ROOT, "package.json"), "utf-8"));
|
|
137
|
-
var VERSION = pkg.version ?? "0.0.0";
|
|
138
|
-
var WEBSITE_URL = pkg.homepage ?? "";
|
|
139
|
-
var LOGO_URL = pkg.logoIcon ?? "";
|
|
140
|
-
var SERVER_TITLE = pkg.name ?? "mcp-mikrotik";
|
|
141
|
-
var SERVER_DESCRIPTION = pkg.description ?? "";
|
|
142
|
-
var SERVER_NAME = "mcp-mikrotik";
|
|
143
|
-
|
|
144
156
|
// src/server.ts
|
|
145
157
|
var INSTRUCTIONS = `MikroTik RouterOS management over SSH.
|
|
146
158
|
|
|
@@ -193,7 +205,17 @@ function createServer(opts = {}) {
|
|
|
193
205
|
process.title = `Mikrotik MCP Server v${VERSION}`;
|
|
194
206
|
const { names, default: defaultDevice } = listDevices();
|
|
195
207
|
const readOnly = getConfig().readOnly;
|
|
196
|
-
|
|
208
|
+
let instructions = names.length > 1 ? INSTRUCTIONS + MULTI_DEVICE_INSTRUCTIONS.replace("{{names}}", names.join(", ")).replace("{{default}}", defaultDevice) : INSTRUCTIONS;
|
|
209
|
+
if (!getConfig().disableUpdateCheck) {
|
|
210
|
+
const cached = loadFileCacheSync();
|
|
211
|
+
if (cached) {
|
|
212
|
+
const note = updateSummaryLine(cached);
|
|
213
|
+
if (note)
|
|
214
|
+
instructions += `
|
|
215
|
+
|
|
216
|
+
${note}`;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
197
219
|
const server = new McpServer({
|
|
198
220
|
name: SERVER_NAME,
|
|
199
221
|
version: VERSION,
|
|
@@ -203,8 +225,8 @@ function createServer(opts = {}) {
|
|
|
203
225
|
icons: [
|
|
204
226
|
{
|
|
205
227
|
src: LOGO_URL,
|
|
206
|
-
mimeType: "image/
|
|
207
|
-
sizes: ["
|
|
228
|
+
mimeType: "image/svg+xml",
|
|
229
|
+
sizes: ["any"],
|
|
208
230
|
theme: "light"
|
|
209
231
|
}
|
|
210
232
|
]
|
|
@@ -226,7 +248,11 @@ function createServer(opts = {}) {
|
|
|
226
248
|
appViews: getConfig().mcp.appViews,
|
|
227
249
|
readOnly
|
|
228
250
|
});
|
|
229
|
-
const promptCount = registerPrompts(server
|
|
251
|
+
const promptCount = registerPrompts(server, {
|
|
252
|
+
deviceNames: names,
|
|
253
|
+
deviceAliases: deviceLabels(),
|
|
254
|
+
deviceDirectory: deviceDirectory()
|
|
255
|
+
});
|
|
230
256
|
const uiViewCount = registerUiResources(server);
|
|
231
257
|
installToolPagination(server, getConfig().mcp.toolPageSize);
|
|
232
258
|
return { server, toolCount, promptCount, uiViewCount, readOnly };
|