@usex/mikrotik-mcp 3.29.0 → 3.31.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 CHANGED
@@ -23,7 +23,8 @@ var McpServerSettingsSchema = z.object({
23
23
  allowedHosts: z.string().default(""),
24
24
  allowedOrigins: z.string().default(""),
25
25
  corsOrigins: z.string().default(""),
26
- toolPageSize: z.coerce.number().int().min(0).default(0)
26
+ toolPageSize: z.coerce.number().int().min(0).default(0),
27
+ appViews: z.boolean().default(true)
27
28
  });
28
29
  var DeviceConfigSchema = z.object({
29
30
  host: z.string().default("127.0.0.1"),
@@ -121,7 +122,8 @@ function parseDevicesSource(raw, fromFile) {
121
122
  const s3 = structured && obj.s3 && typeof obj.s3 === "object" ? obj.s3 : undefined;
122
123
  const dashboard = structured && obj.dashboard && typeof obj.dashboard === "object" ? obj.dashboard : undefined;
123
124
  const tools = structured && obj.tools && typeof obj.tools === "object" ? obj.tools : undefined;
124
- return { devices, defaultDevice, s3, dashboard, tools };
125
+ const mcp = structured && obj.mcp && typeof obj.mcp === "object" ? obj.mcp : undefined;
126
+ return { devices, defaultDevice, s3, dashboard, tools, mcp };
125
127
  }
126
128
  var configSource = { path: DEFAULT_CONFIG_FILE, fromFile: false };
127
129
  function getConfigSource() {
@@ -157,6 +159,7 @@ function loadConfig(argv = process.argv.slice(2)) {
157
159
  let fileS3 = {};
158
160
  let fileDashboard = {};
159
161
  let fileTools;
162
+ let fileMcp;
160
163
  if (configFile || devicesInline) {
161
164
  const src = configFile ? parseDevicesSource(configFile, true) : parseDevicesSource(devicesInline, false);
162
165
  for (const [name, dc] of Object.entries(src.devices))
@@ -168,6 +171,7 @@ function loadConfig(argv = process.argv.slice(2)) {
168
171
  fileS3 = src.s3;
169
172
  fileDashboard = src.dashboard;
170
173
  fileTools = src.tools;
174
+ fileMcp = src.mcp;
171
175
  }
172
176
  const s3 = {
173
177
  accessKeyId: pick("s3-access-key-id", "S3_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID"),
@@ -180,6 +184,8 @@ function loadConfig(argv = process.argv.slice(2)) {
180
184
  presignExpiresIn: pick("s3-presign-expires-in", "MIKROTIK_S3_PRESIGN_EXPIRES_IN"),
181
185
  ...fileS3
182
186
  };
187
+ const appViewsRaw = pick("app-views", "MIKROTIK_MCP__APP_VIEWS");
188
+ const appViewsEnv = appViewsRaw === undefined ? undefined : !/^(0|false|no|off)$/i.test(appViewsRaw);
183
189
  const mcp = {
184
190
  transport: pick("transport", "MIKROTIK_MCP__TRANSPORT", "MCP_TRANSPORT"),
185
191
  host: pick("mcp-host", "MIKROTIK_MCP__HOST"),
@@ -187,7 +193,9 @@ function loadConfig(argv = process.argv.slice(2)) {
187
193
  allowedHosts: pick("mcp-allowed-hosts", "MIKROTIK_MCP__ALLOWED_HOSTS"),
188
194
  allowedOrigins: pick("mcp-allowed-origins", "MIKROTIK_MCP__ALLOWED_ORIGINS"),
189
195
  corsOrigins: pick("mcp-cors-origins", "MIKROTIK_MCP__CORS_ORIGINS"),
190
- toolPageSize: pick("tool-page-size", "MIKROTIK_MCP__TOOL_PAGE_SIZE")
196
+ toolPageSize: pick("tool-page-size", "MIKROTIK_MCP__TOOL_PAGE_SIZE"),
197
+ appViews: appViewsEnv,
198
+ ...fileMcp
191
199
  };
192
200
  const isTruthy = (v) => /^(1|true|yes|on)$/i.test(v ?? "");
193
201
  const readOnly = isTruthy(pick("read-only", "MIKROTIK_READ_ONLY"));
@@ -618,10 +626,10 @@ class MikroTikMacTelnetClient {
618
626
  const explicitSourceMac = this.opts.sourceMac ? parseMac(this.opts.sourceMac) : undefined;
619
627
  const host = this.opts.host ?? DEFAULT_MAC_TELNET_BROADCAST;
620
628
  const askedDiscovery = host === DEFAULT_MAC_TELNET_BROADCAST && !explicitSourceMac;
621
- if (askedDiscovery) {
622
- const candidates = listBroadcastInterfaces();
623
- logger.info(`[mac-telnet] discovery candidates: ${candidates.length ? candidates.map((i) => `${i.name}(${formatMac(i.mac)}\u2192${i.broadcast})`).join(", ") : "NONE \u2014 no usable non-internal IPv4 interface found"}`);
624
- }
629
+ const candidates = askedDiscovery ? listBroadcastInterfaces() : [];
630
+ const candidatesText = candidates.length ? candidates.map((i) => `${i.name}(${formatMac(i.mac)}\u2192${i.broadcast})`).join(", ") : "NONE \u2014 no usable non-internal IPv4 interface found (is this host on the device's LAN?)";
631
+ if (askedDiscovery)
632
+ logger.info(`[mac-telnet] discovery candidates: ${candidatesText}`);
625
633
  logger.info(`[mac-telnet] resolving route to ${this.opts.mac}\u2026`);
626
634
  const route = await resolveMacTelnetRoute({
627
635
  destinationMac,
@@ -631,7 +639,7 @@ class MikroTikMacTelnetClient {
631
639
  explicitSourceMac
632
640
  });
633
641
  const discoveryFailed = askedDiscovery && route.host === DEFAULT_MAC_TELNET_BROADCAST;
634
- this.routeHint = discoveryFailed ? "no local interface got a reply from the device during discovery \u2014 it is likely on a " + "different Layer-2 segment, mac-server is disabled on the facing interface, or a host " + "firewall is dropping the UDP 20561 reply. Pin the segment by setting `macHost` to the " + "device subnet's broadcast (e.g. 192.168.88.255) and, if needed, `sourceMac`." : undefined;
642
+ this.routeHint = discoveryFailed ? "no local interface got a reply from the device during discovery \u2014 MAC-Telnet is Layer-2 " + "only, so the machine running this MCP server MUST be on the SAME Ethernet/Wi-Fi segment as " + "the device (it cannot cross any router or the internet). Likely: this host is not on that " + "segment, mac-server is disabled on the device's facing interface (`/tool mac-server`), or a " + "host firewall is dropping the UDP 20561 reply. " + `Interfaces tried: ${candidatesText}. ` + "To pin it, set the device's `macHost` to the LAN's directed broadcast (e.g. 192.168.88.255) " + "and `sourceMac` to the real MAC of the interface on that LAN." : undefined;
635
643
  logger.info(`[mac-telnet] route: source ${formatMac(route.sourceMac)} \u2192 ${route.host} ` + `(${explicitSourceMac ? "explicit" : discoveryFailed ? "DISCOVERY FAILED \u2014 limited broadcast" : "discovered"})`);
636
644
  const transport = createUdpMacTelnetTransport({
637
645
  host: route.host,
@@ -684,6 +692,7 @@ class MikroTikMacTelnetClient {
684
692
  // src/ssh/client.ts
685
693
  import { readFileSync as readFileSync2 } from "fs";
686
694
  import { Client } from "ssh2";
695
+ var RUN_IDLE_TIMEOUT_MS = 60000;
687
696
  function decodeOutput(data) {
688
697
  if (!data || data.length === 0)
689
698
  return "";
@@ -754,16 +763,43 @@ class MikroTikSSHClient {
754
763
  const stderrBuf = [];
755
764
  let settled = false;
756
765
  let timer;
766
+ let idleTimer;
767
+ const clearTimers = () => {
768
+ if (timer)
769
+ clearTimeout(timer);
770
+ if (idleTimer)
771
+ clearTimeout(idleTimer);
772
+ };
757
773
  const finish = () => {
758
774
  if (settled)
759
775
  return;
760
776
  settled = true;
761
- if (timer)
762
- clearTimeout(timer);
777
+ clearTimers();
763
778
  const out = decodeOutput(Buffer.concat(stdout));
764
779
  const error = decodeOutput(Buffer.concat(stderrBuf));
765
780
  resolve2(error && !out ? error : out);
766
781
  };
782
+ const fail = (e) => {
783
+ if (settled)
784
+ return;
785
+ settled = true;
786
+ clearTimers();
787
+ try {
788
+ stream.close();
789
+ } catch {}
790
+ reject(e);
791
+ };
792
+ const armIdle = () => {
793
+ if (idleTimer)
794
+ clearTimeout(idleTimer);
795
+ idleTimer = setTimeout(() => {
796
+ try {
797
+ stream.signal("INT");
798
+ } catch {}
799
+ fail(new Error(`MikroTik command produced no output for ${RUN_IDLE_TIMEOUT_MS / 1000}s and the SSH ` + "channel never closed \u2014 it appears wedged (often a malformed or unbalanced command). " + `Aborted to avoid hanging the connection. Command: ${command.slice(0, 120)}`));
800
+ }, RUN_IDLE_TIMEOUT_MS);
801
+ };
802
+ armIdle();
767
803
  if (opts.maxMs && opts.maxMs > 0) {
768
804
  timer = setTimeout(() => {
769
805
  try {
@@ -775,7 +811,13 @@ class MikroTikSSHClient {
775
811
  finish();
776
812
  }, opts.maxMs);
777
813
  }
778
- stream.on("close", finish).on("data", (d) => stdout.push(d)).stderr.on("data", (d) => stderrBuf.push(d));
814
+ stream.on("close", finish).on("data", (d) => {
815
+ stdout.push(d);
816
+ armIdle();
817
+ }).stderr.on("data", (d) => {
818
+ stderrBuf.push(d);
819
+ armIdle();
820
+ });
779
821
  });
780
822
  });
781
823
  }
@@ -1794,9 +1836,9 @@ function defineTool(def) {
1794
1836
  inputSchema: def.inputSchema,
1795
1837
  ui: def.ui,
1796
1838
  register(server, opts = {}) {
1797
- const { sendLog, deviceNames, deviceAliases, deviceDirectory: deviceDirectory2 } = opts;
1839
+ const { sendLog, deviceNames, deviceAliases, deviceDirectory: deviceDirectory2, appViews } = opts;
1798
1840
  const multiDevice = !!deviceNames && deviceNames.length > 1;
1799
- const { ui, auto } = effectiveUi(def);
1841
+ const { ui, auto } = appViews === false ? { ui: undefined, auto: false } : effectiveUi(def);
1800
1842
  const selectorNames = multiDevice && deviceNames ? [...new Set([...deviceNames, ...deviceAliases ?? []])] : [];
1801
1843
  const inputSchema = multiDevice ? {
1802
1844
  ...def.inputSchema,
@@ -27067,7 +27109,7 @@ function registerPrompts(server) {
27067
27109
  // package.json
27068
27110
  var package_default = {
27069
27111
  name: "@usex/mikrotik-mcp",
27070
- version: "3.29.0",
27112
+ version: "3.31.0",
27071
27113
  description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
27072
27114
  keywords: [
27073
27115
  "ai",
@@ -27160,11 +27202,11 @@ var package_default = {
27160
27202
  "class-variance-authority": "^0.7.1",
27161
27203
  clsx: "^2.1.1",
27162
27204
  gsap: "^3.15.0",
27163
- prettier: "^3.8.4",
27205
+ prettier: "^3.9.1",
27164
27206
  react: "^19.2.7",
27165
27207
  "react-dom": "^19.2.7",
27166
27208
  recharts: "^3.9.0",
27167
- "release-it": "^20.2.0",
27209
+ "release-it": "^20.2.1",
27168
27210
  "tailwind-merge": "^3.6.0",
27169
27211
  tailwindcss: "^4.3.1",
27170
27212
  vite: "npm:@voidzero-dev/vite-plus-core@latest",
@@ -27273,6 +27315,7 @@ function createServer(opts = {}) {
27273
27315
  deviceNames: names,
27274
27316
  deviceAliases: deviceLabels(),
27275
27317
  deviceDirectory: deviceDirectory(),
27318
+ appViews: getConfig().mcp.appViews,
27276
27319
  readOnly
27277
27320
  });
27278
27321
  const promptCount = registerPrompts(server);
package/dist/index.d.ts CHANGED
@@ -94,6 +94,14 @@ interface RegisterOptions {
94
94
  */
95
95
  deviceDirectory?: DeviceDirectoryEntry[];
96
96
  /**
97
+ * Emit MCP App view metadata (`_meta.ui`) on tools. Default true. Set false for
98
+ * hosts whose tool discovery hides/deprioritises tools that carry App/
99
+ * `openai/outputTemplate` metadata — without it, every `list_*`/`get_*` read
100
+ * tool gains the auto-records view and some clients then surface only the
101
+ * (metadata-free) write tools. Disabling makes reads plain, surfacing tools.
102
+ */
103
+ appViews?: boolean;
104
+ /**
97
105
  * Read-only mode: register only tools annotated `readOnlyHint`. Used to
98
106
  * withhold every write/destructive tool from a publicly-exposed surface (e.g.
99
107
  * a ChatGPT Apps connector) until authentication is in place.
package/dist/index.js CHANGED
@@ -19,7 +19,8 @@ var McpServerSettingsSchema = z.object({
19
19
  allowedHosts: z.string().default(""),
20
20
  allowedOrigins: z.string().default(""),
21
21
  corsOrigins: z.string().default(""),
22
- toolPageSize: z.coerce.number().int().min(0).default(0)
22
+ toolPageSize: z.coerce.number().int().min(0).default(0),
23
+ appViews: z.boolean().default(true)
23
24
  });
24
25
  var DeviceConfigSchema = z.object({
25
26
  host: z.string().default("127.0.0.1"),
@@ -117,7 +118,8 @@ function parseDevicesSource(raw, fromFile) {
117
118
  const s3 = structured && obj.s3 && typeof obj.s3 === "object" ? obj.s3 : undefined;
118
119
  const dashboard = structured && obj.dashboard && typeof obj.dashboard === "object" ? obj.dashboard : undefined;
119
120
  const tools = structured && obj.tools && typeof obj.tools === "object" ? obj.tools : undefined;
120
- return { devices, defaultDevice, s3, dashboard, tools };
121
+ const mcp = structured && obj.mcp && typeof obj.mcp === "object" ? obj.mcp : undefined;
122
+ return { devices, defaultDevice, s3, dashboard, tools, mcp };
121
123
  }
122
124
  var configSource = { path: DEFAULT_CONFIG_FILE, fromFile: false };
123
125
  function loadConfig(argv = process.argv.slice(2)) {
@@ -150,6 +152,7 @@ function loadConfig(argv = process.argv.slice(2)) {
150
152
  let fileS3 = {};
151
153
  let fileDashboard = {};
152
154
  let fileTools;
155
+ let fileMcp;
153
156
  if (configFile || devicesInline) {
154
157
  const src = configFile ? parseDevicesSource(configFile, true) : parseDevicesSource(devicesInline, false);
155
158
  for (const [name, dc] of Object.entries(src.devices))
@@ -161,6 +164,7 @@ function loadConfig(argv = process.argv.slice(2)) {
161
164
  fileS3 = src.s3;
162
165
  fileDashboard = src.dashboard;
163
166
  fileTools = src.tools;
167
+ fileMcp = src.mcp;
164
168
  }
165
169
  const s3 = {
166
170
  accessKeyId: pick("s3-access-key-id", "S3_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID"),
@@ -173,6 +177,8 @@ function loadConfig(argv = process.argv.slice(2)) {
173
177
  presignExpiresIn: pick("s3-presign-expires-in", "MIKROTIK_S3_PRESIGN_EXPIRES_IN"),
174
178
  ...fileS3
175
179
  };
180
+ const appViewsRaw = pick("app-views", "MIKROTIK_MCP__APP_VIEWS");
181
+ const appViewsEnv = appViewsRaw === undefined ? undefined : !/^(0|false|no|off)$/i.test(appViewsRaw);
176
182
  const mcp = {
177
183
  transport: pick("transport", "MIKROTIK_MCP__TRANSPORT", "MCP_TRANSPORT"),
178
184
  host: pick("mcp-host", "MIKROTIK_MCP__HOST"),
@@ -180,7 +186,9 @@ function loadConfig(argv = process.argv.slice(2)) {
180
186
  allowedHosts: pick("mcp-allowed-hosts", "MIKROTIK_MCP__ALLOWED_HOSTS"),
181
187
  allowedOrigins: pick("mcp-allowed-origins", "MIKROTIK_MCP__ALLOWED_ORIGINS"),
182
188
  corsOrigins: pick("mcp-cors-origins", "MIKROTIK_MCP__CORS_ORIGINS"),
183
- toolPageSize: pick("tool-page-size", "MIKROTIK_MCP__TOOL_PAGE_SIZE")
189
+ toolPageSize: pick("tool-page-size", "MIKROTIK_MCP__TOOL_PAGE_SIZE"),
190
+ appViews: appViewsEnv,
191
+ ...fileMcp
184
192
  };
185
193
  const isTruthy = (v) => /^(1|true|yes|on)$/i.test(v ?? "");
186
194
  const readOnly = isTruthy(pick("read-only", "MIKROTIK_READ_ONLY"));
@@ -610,10 +618,10 @@ class MikroTikMacTelnetClient {
610
618
  const explicitSourceMac = this.opts.sourceMac ? parseMac(this.opts.sourceMac) : undefined;
611
619
  const host = this.opts.host ?? DEFAULT_MAC_TELNET_BROADCAST;
612
620
  const askedDiscovery = host === DEFAULT_MAC_TELNET_BROADCAST && !explicitSourceMac;
613
- if (askedDiscovery) {
614
- const candidates = listBroadcastInterfaces();
615
- logger.info(`[mac-telnet] discovery candidates: ${candidates.length ? candidates.map((i) => `${i.name}(${formatMac(i.mac)}\u2192${i.broadcast})`).join(", ") : "NONE \u2014 no usable non-internal IPv4 interface found"}`);
616
- }
621
+ const candidates = askedDiscovery ? listBroadcastInterfaces() : [];
622
+ const candidatesText = candidates.length ? candidates.map((i) => `${i.name}(${formatMac(i.mac)}\u2192${i.broadcast})`).join(", ") : "NONE \u2014 no usable non-internal IPv4 interface found (is this host on the device's LAN?)";
623
+ if (askedDiscovery)
624
+ logger.info(`[mac-telnet] discovery candidates: ${candidatesText}`);
617
625
  logger.info(`[mac-telnet] resolving route to ${this.opts.mac}\u2026`);
618
626
  const route = await resolveMacTelnetRoute({
619
627
  destinationMac,
@@ -623,7 +631,7 @@ class MikroTikMacTelnetClient {
623
631
  explicitSourceMac
624
632
  });
625
633
  const discoveryFailed = askedDiscovery && route.host === DEFAULT_MAC_TELNET_BROADCAST;
626
- this.routeHint = discoveryFailed ? "no local interface got a reply from the device during discovery \u2014 it is likely on a " + "different Layer-2 segment, mac-server is disabled on the facing interface, or a host " + "firewall is dropping the UDP 20561 reply. Pin the segment by setting `macHost` to the " + "device subnet's broadcast (e.g. 192.168.88.255) and, if needed, `sourceMac`." : undefined;
634
+ this.routeHint = discoveryFailed ? "no local interface got a reply from the device during discovery \u2014 MAC-Telnet is Layer-2 " + "only, so the machine running this MCP server MUST be on the SAME Ethernet/Wi-Fi segment as " + "the device (it cannot cross any router or the internet). Likely: this host is not on that " + "segment, mac-server is disabled on the device's facing interface (`/tool mac-server`), or a " + "host firewall is dropping the UDP 20561 reply. " + `Interfaces tried: ${candidatesText}. ` + "To pin it, set the device's `macHost` to the LAN's directed broadcast (e.g. 192.168.88.255) " + "and `sourceMac` to the real MAC of the interface on that LAN." : undefined;
627
635
  logger.info(`[mac-telnet] route: source ${formatMac(route.sourceMac)} \u2192 ${route.host} ` + `(${explicitSourceMac ? "explicit" : discoveryFailed ? "DISCOVERY FAILED \u2014 limited broadcast" : "discovered"})`);
628
636
  const transport = createUdpMacTelnetTransport({
629
637
  host: route.host,
@@ -676,6 +684,7 @@ class MikroTikMacTelnetClient {
676
684
  // src/ssh/client.ts
677
685
  import { readFileSync as readFileSync2 } from "fs";
678
686
  import { Client } from "ssh2";
687
+ var RUN_IDLE_TIMEOUT_MS = 60000;
679
688
  function decodeOutput(data) {
680
689
  if (!data || data.length === 0)
681
690
  return "";
@@ -746,16 +755,43 @@ class MikroTikSSHClient {
746
755
  const stderrBuf = [];
747
756
  let settled = false;
748
757
  let timer;
758
+ let idleTimer;
759
+ const clearTimers = () => {
760
+ if (timer)
761
+ clearTimeout(timer);
762
+ if (idleTimer)
763
+ clearTimeout(idleTimer);
764
+ };
749
765
  const finish = () => {
750
766
  if (settled)
751
767
  return;
752
768
  settled = true;
753
- if (timer)
754
- clearTimeout(timer);
769
+ clearTimers();
755
770
  const out = decodeOutput(Buffer.concat(stdout));
756
771
  const error = decodeOutput(Buffer.concat(stderrBuf));
757
772
  resolve2(error && !out ? error : out);
758
773
  };
774
+ const fail = (e) => {
775
+ if (settled)
776
+ return;
777
+ settled = true;
778
+ clearTimers();
779
+ try {
780
+ stream.close();
781
+ } catch {}
782
+ reject(e);
783
+ };
784
+ const armIdle = () => {
785
+ if (idleTimer)
786
+ clearTimeout(idleTimer);
787
+ idleTimer = setTimeout(() => {
788
+ try {
789
+ stream.signal("INT");
790
+ } catch {}
791
+ fail(new Error(`MikroTik command produced no output for ${RUN_IDLE_TIMEOUT_MS / 1000}s and the SSH ` + "channel never closed \u2014 it appears wedged (often a malformed or unbalanced command). " + `Aborted to avoid hanging the connection. Command: ${command.slice(0, 120)}`));
792
+ }, RUN_IDLE_TIMEOUT_MS);
793
+ };
794
+ armIdle();
759
795
  if (opts.maxMs && opts.maxMs > 0) {
760
796
  timer = setTimeout(() => {
761
797
  try {
@@ -767,7 +803,13 @@ class MikroTikSSHClient {
767
803
  finish();
768
804
  }, opts.maxMs);
769
805
  }
770
- stream.on("close", finish).on("data", (d) => stdout.push(d)).stderr.on("data", (d) => stderrBuf.push(d));
806
+ stream.on("close", finish).on("data", (d) => {
807
+ stdout.push(d);
808
+ armIdle();
809
+ }).stderr.on("data", (d) => {
810
+ stderrBuf.push(d);
811
+ armIdle();
812
+ });
771
813
  });
772
814
  });
773
815
  }
@@ -1700,9 +1742,9 @@ function defineTool(def) {
1700
1742
  inputSchema: def.inputSchema,
1701
1743
  ui: def.ui,
1702
1744
  register(server, opts = {}) {
1703
- const { sendLog, deviceNames, deviceAliases, deviceDirectory: deviceDirectory2 } = opts;
1745
+ const { sendLog, deviceNames, deviceAliases, deviceDirectory: deviceDirectory2, appViews } = opts;
1704
1746
  const multiDevice = !!deviceNames && deviceNames.length > 1;
1705
- const { ui, auto } = effectiveUi(def);
1747
+ const { ui, auto } = appViews === false ? { ui: undefined, auto: false } : effectiveUi(def);
1706
1748
  const selectorNames = multiDevice && deviceNames ? [...new Set([...deviceNames, ...deviceAliases ?? []])] : [];
1707
1749
  const inputSchema = multiDevice ? {
1708
1750
  ...def.inputSchema,
@@ -25045,7 +25087,7 @@ function selectToolModules(filter = {}, catalog = moduleCatalog) {
25045
25087
  // package.json
25046
25088
  var package_default = {
25047
25089
  name: "@usex/mikrotik-mcp",
25048
- version: "3.29.0",
25090
+ version: "3.31.0",
25049
25091
  description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
25050
25092
  keywords: [
25051
25093
  "ai",
@@ -25138,11 +25180,11 @@ var package_default = {
25138
25180
  "class-variance-authority": "^0.7.1",
25139
25181
  clsx: "^2.1.1",
25140
25182
  gsap: "^3.15.0",
25141
- prettier: "^3.8.4",
25183
+ prettier: "^3.9.1",
25142
25184
  react: "^19.2.7",
25143
25185
  "react-dom": "^19.2.7",
25144
25186
  recharts: "^3.9.0",
25145
- "release-it": "^20.2.0",
25187
+ "release-it": "^20.2.1",
25146
25188
  "tailwind-merge": "^3.6.0",
25147
25189
  tailwindcss: "^4.3.1",
25148
25190
  vite: "npm:@voidzero-dev/vite-plus-core@latest",
@@ -25251,6 +25293,7 @@ function createServer(opts = {}) {
25251
25293
  deviceNames: names,
25252
25294
  deviceAliases: deviceLabels(),
25253
25295
  deviceDirectory: deviceDirectory(),
25296
+ appViews: getConfig().mcp.appViews,
25254
25297
  readOnly
25255
25298
  });
25256
25299
  const promptCount = registerPrompts(server);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usex/mikrotik-mcp",
3
- "version": "3.29.0",
3
+ "version": "3.31.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",
@@ -93,11 +93,11 @@
93
93
  "class-variance-authority": "^0.7.1",
94
94
  "clsx": "^2.1.1",
95
95
  "gsap": "^3.15.0",
96
- "prettier": "^3.8.4",
96
+ "prettier": "^3.9.1",
97
97
  "react": "^19.2.7",
98
98
  "react-dom": "^19.2.7",
99
99
  "recharts": "^3.9.0",
100
- "release-it": "^20.2.0",
100
+ "release-it": "^20.2.1",
101
101
  "tailwind-merge": "^3.6.0",
102
102
  "tailwindcss": "^4.3.1",
103
103
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
@@ -87,7 +87,8 @@
87
87
  "allowedHosts": "",
88
88
  "allowedOrigins": "",
89
89
  "corsOrigins": "",
90
- "toolPageSize": 0
90
+ "toolPageSize": 0,
91
+ "appViews": true
91
92
  },
92
93
  "type": "object",
93
94
  "properties": {
@@ -123,6 +124,10 @@
123
124
  "type": "integer",
124
125
  "minimum": 0,
125
126
  "maximum": 9007199254740991
127
+ },
128
+ "appViews": {
129
+ "default": true,
130
+ "type": "boolean"
126
131
  }
127
132
  },
128
133
  "required": [
@@ -132,7 +137,8 @@
132
137
  "allowedHosts",
133
138
  "allowedOrigins",
134
139
  "corsOrigins",
135
- "toolPageSize"
140
+ "toolPageSize",
141
+ "appViews"
136
142
  ],
137
143
  "additionalProperties": false
138
144
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "version": "3.28.0",
3
+ "version": "3.29.0",
4
4
  "generated": "by scripts/gen-schemas.ts — do not edit by hand",
5
5
  "toolCount": 758,
6
6
  "tools": [