@usex/mikrotik-mcp 3.24.0 → 3.26.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/index.js CHANGED
@@ -1766,6 +1766,11 @@ var UI_VIEWS = [
1766
1766
  id: "connected-devices",
1767
1767
  name: "MikroTik Connected Devices",
1768
1768
  description: "Devices on the network with per-device Download/Upload charts, uptime/status, and one-click " + "block/allow and pin-IP."
1769
+ },
1770
+ {
1771
+ id: "aaa",
1772
+ name: "MikroTik RADIUS & User Manager",
1773
+ description: "Full RADIUS client + built-in User Manager RADIUS server management: servers, users, " + "profiles, limitations, NAS clients, assignments, sessions and settings, with add/edit/remove."
1769
1774
  }
1770
1775
  ];
1771
1776
  function placeholderHtml(view) {
@@ -2070,18 +2075,54 @@ async function fetchDevices(ctx) {
2070
2075
  async function sampleDeviceTraffic(ctx, ip) {
2071
2076
  const q = await executeMikrotikCommand(`/queue simple print stats detail where target~"${ip}"`, ctx);
2072
2077
  if (isEmpty(q) || looksLikeError(q)) {
2073
- return { ip, source: "none", rxBitsPerSec: 0, txBitsPerSec: 0, rxBytes: 0, txBytes: 0 };
2078
+ return {
2079
+ ip,
2080
+ source: "none",
2081
+ rxBitsPerSec: 0,
2082
+ txBitsPerSec: 0,
2083
+ rxBytes: 0,
2084
+ txBytes: 0,
2085
+ downloadLimit: "",
2086
+ uploadLimit: ""
2087
+ };
2074
2088
  }
2075
2089
  const row = parseRecords(q).rows[0] ?? {};
2076
2090
  const [up, down] = (row.rate ?? "0/0").split("/");
2077
2091
  const [upB, downB] = (row.bytes ?? "0/0").split("/");
2092
+ const [upLim, downLim] = (row["max-limit"] ?? "0/0").split("/");
2093
+ const limit = (v) => v && v !== "0" ? v : "";
2078
2094
  return {
2079
2095
  ip,
2080
2096
  source: "queue",
2081
2097
  txBitsPerSec: parseLeadingNumber(up) ?? 0,
2082
2098
  rxBitsPerSec: parseLeadingNumber(down) ?? 0,
2083
2099
  txBytes: parseLeadingNumber(upB) ?? 0,
2084
- rxBytes: parseLeadingNumber(downB) ?? 0
2100
+ rxBytes: parseLeadingNumber(downB) ?? 0,
2101
+ downloadLimit: limit(downLim),
2102
+ uploadLimit: limit(upLim)
2103
+ };
2104
+ }
2105
+ async function setDeviceLimits(ctx, ip, opts) {
2106
+ const rate = (v) => {
2107
+ const t = (v ?? "").trim();
2108
+ return t === "" ? "0" : t;
2109
+ };
2110
+ const down = rate(opts.download);
2111
+ const up = rate(opts.upload);
2112
+ const maxLimit = `${up}/${down}`;
2113
+ const existing = await executeMikrotikCommand(`/queue simple print count-only where target~"${ip}"`, ctx);
2114
+ const have = (parseLeadingNumber(existing.trim()) ?? 0) > 0;
2115
+ if (!have && down === "0" && up === "0") {
2116
+ return { ok: true, message: `No rate limit set for ${ip} (left unlimited).` };
2117
+ }
2118
+ const cmd = have ? new Cmd(`/queue simple set [find target~"${ip}"]`).set("max-limit", maxLimit).build() : new Cmd("/queue simple add").set("name", opts.name?.trim() || `client-${ip}`).set("target", ip).set("max-limit", maxLimit).build();
2119
+ const out = await executeMikrotikCommand(cmd, ctx);
2120
+ if (looksLikeError(out))
2121
+ return { ok: false, message: `Failed to set limits: ${out}` };
2122
+ const human = (r) => r === "0" ? "unlimited" : r;
2123
+ return {
2124
+ ok: true,
2125
+ message: `Limits for ${ip} set \u2014 \u2193 ${human(down)} / \u2191 ${human(up)}.`
2085
2126
  };
2086
2127
  }
2087
2128
  async function blockRuleCount(mac, ctx) {
@@ -2304,6 +2345,25 @@ ${isEmpty(arp) ? "(no arp entry)" : arp}`;
2304
2345
  return (await setDeviceLabel(ctx, a.mac, a.label)).message;
2305
2346
  }
2306
2347
  }),
2348
+ defineTool({
2349
+ name: "set_device_limits",
2350
+ title: "Set a Device's Download/Upload Rate Limits",
2351
+ annotations: WRITE_IDEMPOTENT,
2352
+ description: "Throttle (or unthrottle) a device's bandwidth by managing a `/queue simple` targeting its IP " + "\u2014 sets the max download and upload rate. RouterOS rate strings: e.g. `10M`, `512k`; pass `0` " + "or omit a side to leave it unlimited. Updates the device's existing simple queue if there is " + "one, otherwise creates it (which also enables the per-device traffic counter the Connected " + "Devices charts read). Identify the device by IP (the queue target).",
2353
+ inputSchema: {
2354
+ ip: z5.string().describe("Device IP address (the queue target)"),
2355
+ download_limit: z5.string().optional().describe('Max download rate, e.g. "10M". "0"/omit = unlimited'),
2356
+ upload_limit: z5.string().optional().describe('Max upload rate, e.g. "2M". "0"/omit = unlimited'),
2357
+ name: z5.string().optional().describe("Optional name for a newly created queue")
2358
+ },
2359
+ async handler(a, ctx) {
2360
+ return (await setDeviceLimits(ctx, a.ip, {
2361
+ download: a.download_limit,
2362
+ upload: a.upload_limit,
2363
+ name: a.name
2364
+ })).message;
2365
+ }
2366
+ }),
2307
2367
  defineTool({
2308
2368
  name: "block_device",
2309
2369
  title: "Block a Device (Deny Network Access)",
@@ -15855,8 +15915,407 @@ ${details}`;
15855
15915
  })
15856
15916
  ];
15857
15917
 
15858
- // src/tools/radius.ts
15918
+ // src/tools/aaa-view.ts
15859
15919
  import { z as z77 } from "zod";
15920
+
15921
+ // src/tools/aaa-data.ts
15922
+ var UM_NOT_AVAILABLE = "User Manager is not available on this device (the user-manager package is not installed).";
15923
+ var SECRET_KEYS = new Set(["secret", "password", "shared-secret", "otp-secret"]);
15924
+ var REDACTED2 = "\u2022\u2022\u2022\u2022\u2022\u2022";
15925
+ var RADIUS_FIELDS = [
15926
+ "address",
15927
+ "secret",
15928
+ "service",
15929
+ "authentication-port",
15930
+ "accounting-port",
15931
+ "timeout",
15932
+ "src-address",
15933
+ "realm",
15934
+ "called-id",
15935
+ "domain",
15936
+ "protocol",
15937
+ "certificate",
15938
+ "accounting-backup",
15939
+ "comment",
15940
+ "disabled"
15941
+ ];
15942
+ var UM_USER_FIELDS = [
15943
+ "name",
15944
+ "password",
15945
+ "group",
15946
+ "shared-users",
15947
+ "attributes",
15948
+ "caller-id",
15949
+ "otp-secret",
15950
+ "comment",
15951
+ "disabled"
15952
+ ];
15953
+ var UM_PROFILE_FIELDS = [
15954
+ "name",
15955
+ "name-for-users",
15956
+ "validity",
15957
+ "price",
15958
+ "starts-when",
15959
+ "override-shared-users",
15960
+ "comment"
15961
+ ];
15962
+ var UM_ROUTER_FIELDS = [
15963
+ "name",
15964
+ "address",
15965
+ "shared-secret",
15966
+ "coa-port",
15967
+ "protocol",
15968
+ "comment",
15969
+ "disabled"
15970
+ ];
15971
+ var UM_LIMITATION_FIELDS = [
15972
+ "name",
15973
+ "rate-limit-rx",
15974
+ "rate-limit-tx",
15975
+ "rate-limit-min-rx",
15976
+ "rate-limit-min-tx",
15977
+ "rate-limit-burst-rx",
15978
+ "rate-limit-burst-tx",
15979
+ "rate-limit-burst-threshold-rx",
15980
+ "rate-limit-burst-threshold-tx",
15981
+ "rate-limit-burst-time-rx",
15982
+ "rate-limit-burst-time-tx",
15983
+ "rate-limit-priority",
15984
+ "download-limit",
15985
+ "upload-limit",
15986
+ "transfer-limit",
15987
+ "uptime-limit",
15988
+ "reset-counters-interval",
15989
+ "reset-counters-start-time",
15990
+ "comment"
15991
+ ];
15992
+ var AAA_ENTITIES = {
15993
+ radius: { menu: "radius", key: ".id", fields: RADIUS_FIELDS, toggle: true },
15994
+ "um-users": {
15995
+ menu: "user-manager user",
15996
+ key: "name",
15997
+ fields: UM_USER_FIELDS,
15998
+ toggle: true,
15999
+ um: true
16000
+ },
16001
+ "um-profiles": { menu: "user-manager profile", key: "name", fields: UM_PROFILE_FIELDS, um: true },
16002
+ "um-user-profiles": {
16003
+ menu: "user-manager user-profile",
16004
+ key: ".id",
16005
+ fields: ["user", "profile"],
16006
+ um: true
16007
+ },
16008
+ "um-routers": {
16009
+ menu: "user-manager router",
16010
+ key: "name",
16011
+ fields: UM_ROUTER_FIELDS,
16012
+ toggle: true,
16013
+ um: true
16014
+ },
16015
+ "um-limitations": {
16016
+ menu: "user-manager limitation",
16017
+ key: "name",
16018
+ fields: UM_LIMITATION_FIELDS,
16019
+ um: true
16020
+ },
16021
+ "um-sessions": { menu: "user-manager session", key: ".id", fields: [], readonly: true, um: true }
16022
+ };
16023
+ function redactRow(r) {
16024
+ const out = {};
16025
+ for (const [k, v] of Object.entries(r))
16026
+ out[k] = SECRET_KEYS.has(k) && v ? REDACTED2 : v;
16027
+ return out;
16028
+ }
16029
+ async function idsFor(menu, ctx) {
16030
+ const out = await executeMikrotikCommand(`:put [/${menu} find]`, ctx);
16031
+ if (looksLikeError(out) || commandUnsupported(out))
16032
+ return [];
16033
+ return out.trim().split(/[;\s]+/).filter(Boolean);
16034
+ }
16035
+ function findClause(entity, id) {
16036
+ return `[find ${entity.key}=${quoteValue(id)}]`;
16037
+ }
16038
+ function entityFor(slug) {
16039
+ const e = AAA_ENTITIES[slug];
16040
+ if (!e)
16041
+ throw new Error(`Unknown AAA entity '${slug}'.`);
16042
+ return e;
16043
+ }
16044
+ async function listAaaEntity(ctx, slug) {
16045
+ const entity = entityFor(slug);
16046
+ const out = await executeMikrotikCommand(`/${entity.menu} print detail`, ctx);
16047
+ if (commandUnsupported(out))
16048
+ return { available: false, rows: [] };
16049
+ if (isEmpty(out) || looksLikeError(out))
16050
+ return { available: true, rows: [] };
16051
+ const rows = parseRecords(out).rows;
16052
+ const ids = await idsFor(entity.menu, ctx);
16053
+ return {
16054
+ available: true,
16055
+ rows: rows.map((r, i) => redactRow({ ...r, ".id": r[".id"] ?? ids[i] ?? "" }))
16056
+ };
16057
+ }
16058
+ function pickFields(entity, fields) {
16059
+ return entity.fields.filter((k) => fields[k] !== undefined && fields[k] !== "").map((k) => [k, fields[k]]);
16060
+ }
16061
+ function applyFields(cmd, pairs) {
16062
+ for (const [k, v] of pairs)
16063
+ cmd.set(k, v);
16064
+ return cmd;
16065
+ }
16066
+ async function addAaaEntity(ctx, slug, fields) {
16067
+ const entity = entityFor(slug);
16068
+ if (entity.readonly)
16069
+ return { ok: false, message: `${slug} is read-only.` };
16070
+ const pairs = pickFields(entity, fields);
16071
+ if (pairs.length === 0)
16072
+ return { ok: false, message: "No fields supplied." };
16073
+ const cmd = applyFields(new Cmd(`/${entity.menu} add`), pairs).build();
16074
+ const out = await executeMikrotikCommand(cmd, ctx);
16075
+ if (commandUnsupported(out))
16076
+ return { ok: false, message: UM_NOT_AVAILABLE };
16077
+ if (looksLikeError(out))
16078
+ return { ok: false, message: out.trim() };
16079
+ return { ok: true, message: "Created." };
16080
+ }
16081
+ async function updateAaaEntity(ctx, slug, id, fields) {
16082
+ const entity = entityFor(slug);
16083
+ if (entity.readonly)
16084
+ return { ok: false, message: `${slug} is read-only.` };
16085
+ const pairs = pickFields(entity, fields);
16086
+ if (pairs.length === 0)
16087
+ return { ok: false, message: "No updates supplied." };
16088
+ const cmd = applyFields(new Cmd(`/${entity.menu} set ${findClause(entity, id)}`), pairs).build();
16089
+ const out = await executeMikrotikCommand(cmd, ctx);
16090
+ if (commandUnsupported(out))
16091
+ return { ok: false, message: UM_NOT_AVAILABLE };
16092
+ if (looksLikeError(out))
16093
+ return { ok: false, message: out.trim() };
16094
+ return { ok: true, message: "Updated." };
16095
+ }
16096
+ async function removeAaaEntity(ctx, slug, id) {
16097
+ const entity = entityFor(slug);
16098
+ if (entity.readonly)
16099
+ return { ok: false, message: `${slug} is read-only.` };
16100
+ const count = await executeMikrotikCommand(`/${entity.menu} print count-only where ${entity.key}=${quoteValue(id)}`, ctx);
16101
+ if (commandUnsupported(count))
16102
+ return { ok: false, message: UM_NOT_AVAILABLE };
16103
+ if (count.trim() === "0")
16104
+ return { ok: false, message: `${slug} '${id}' not found.` };
16105
+ const out = await executeMikrotikCommand(`/${entity.menu} remove ${findClause(entity, id)}`, ctx);
16106
+ if (looksLikeError(out))
16107
+ return { ok: false, message: out.trim() };
16108
+ return { ok: true, message: "Removed." };
16109
+ }
16110
+ async function toggleAaaEntity(ctx, slug, id, enable) {
16111
+ const entity = entityFor(slug);
16112
+ if (!entity.toggle)
16113
+ return { ok: false, message: `${slug} cannot be enabled/disabled.` };
16114
+ const cmd = new Cmd(`/${entity.menu} set ${findClause(entity, id)}`).set("disabled", enable ? "no" : "yes").build();
16115
+ const out = await executeMikrotikCommand(cmd, ctx);
16116
+ if (commandUnsupported(out))
16117
+ return { ok: false, message: UM_NOT_AVAILABLE };
16118
+ if (looksLikeError(out))
16119
+ return { ok: false, message: out.trim() };
16120
+ return { ok: true, message: enable ? "Enabled." : "Disabled." };
16121
+ }
16122
+ function parseSingleton(out) {
16123
+ const rows = parseRecords(out).rows;
16124
+ return redactRow(rows[0] ?? {});
16125
+ }
16126
+ async function getRadiusIncoming(ctx) {
16127
+ const out = await executeMikrotikCommand("/radius incoming print", ctx);
16128
+ return looksLikeError(out) ? {} : parseSingleton(out);
16129
+ }
16130
+ async function setRadiusIncoming(ctx, fields) {
16131
+ const cmd = new Cmd("/radius incoming set");
16132
+ if (fields.accept !== undefined && fields.accept !== "")
16133
+ cmd.set("accept", fields.accept);
16134
+ if (fields.port !== undefined && fields.port !== "")
16135
+ cmd.set("port", fields.port);
16136
+ const built = cmd.build();
16137
+ if (!built.includes("="))
16138
+ return { ok: false, message: "No updates supplied." };
16139
+ const out = await executeMikrotikCommand(built, ctx);
16140
+ if (looksLikeError(out))
16141
+ return { ok: false, message: out.trim() };
16142
+ return { ok: true, message: "RADIUS incoming (CoA) updated." };
16143
+ }
16144
+ async function resetRadiusCounters(ctx) {
16145
+ const out = await executeMikrotikCommand("/radius reset-counters", ctx);
16146
+ if (looksLikeError(out))
16147
+ return { ok: false, message: out.trim() };
16148
+ return { ok: true, message: "RADIUS counters reset." };
16149
+ }
16150
+ async function getUmSettings(ctx) {
16151
+ const out = await executeMikrotikCommand("/user-manager print", ctx);
16152
+ if (commandUnsupported(out))
16153
+ return { available: false, settings: {} };
16154
+ return { available: true, settings: looksLikeError(out) ? {} : parseSingleton(out) };
16155
+ }
16156
+ var UM_SETTINGS_FIELDS = [
16157
+ "enabled",
16158
+ "certificate",
16159
+ "radsec-certificate",
16160
+ "accounting-port",
16161
+ "authentication-port",
16162
+ "use-profiles"
16163
+ ];
16164
+ async function setUmSettings(ctx, fields) {
16165
+ const cmd = new Cmd("/user-manager set");
16166
+ let any = false;
16167
+ for (const k of UM_SETTINGS_FIELDS) {
16168
+ if (fields[k] !== undefined && fields[k] !== "") {
16169
+ cmd.set(k, fields[k]);
16170
+ any = true;
16171
+ }
16172
+ }
16173
+ if (!any)
16174
+ return { ok: false, message: "No updates supplied." };
16175
+ const out = await executeMikrotikCommand(cmd.build(), ctx);
16176
+ if (commandUnsupported(out))
16177
+ return { ok: false, message: UM_NOT_AVAILABLE };
16178
+ if (looksLikeError(out))
16179
+ return { ok: false, message: out.trim() };
16180
+ return { ok: true, message: "User Manager settings updated." };
16181
+ }
16182
+
16183
+ // src/tools/aaa-view.ts
16184
+ var SLUGS = Object.keys(AAA_ENTITIES);
16185
+ var AAA_UI = {
16186
+ resourceUri: uiViewUri("aaa"),
16187
+ visibility: ["model", "app"]
16188
+ };
16189
+ var APP_ONLY = { resourceUri: uiViewUri("aaa"), visibility: ["app"] };
16190
+ function sectionView(slug, list) {
16191
+ return {
16192
+ __mikrotikView: "aaa-section",
16193
+ slug,
16194
+ available: list.available,
16195
+ rows: list.rows,
16196
+ generatedAt: new Date().toISOString()
16197
+ };
16198
+ }
16199
+ function summarize(slug, list) {
16200
+ if (!list.available)
16201
+ return `User Manager is not installed on this device (section '${slug}').`;
16202
+ return `${slug}: ${list.rows.length} row(s).`;
16203
+ }
16204
+ var aaaViewTools = [
16205
+ defineTool({
16206
+ name: "manage_radius_user_manager",
16207
+ title: "Manage RADIUS & User Manager (Dashboard)",
16208
+ annotations: READ,
16209
+ ui: { ...AAA_UI },
16210
+ description: "Opens the interactive RADIUS & User Manager management dashboard \u2014 a single view to manage" + " the router's RADIUS client (`/radius`) and the built-in User Manager RADIUS server" + " (`/user-manager`). Tabs cover RADIUS servers, User Manager users, service profiles," + " rate/quota limitations, NAS clients (routers), profile assignments, accounting sessions," + " and global/CoA settings, each with full add/edit/enable-disable/remove. `section` picks the" + " tab to open first. For one-off scripted changes the granular tools (add_radius_server," + " add_user_manager_user, \u2026) still apply.",
16211
+ inputSchema: {
16212
+ section: z77.enum(SLUGS).optional().describe("Which section/tab to open first (default 'radius')")
16213
+ },
16214
+ async handler(a, ctx) {
16215
+ const slug = a.section ?? "radius";
16216
+ ctx.info(`Opening AAA dashboard: ${slug}`);
16217
+ const list = await listAaaEntity(ctx, slug);
16218
+ return { text: summarize(slug, list), structuredContent: sectionView(slug, list) };
16219
+ }
16220
+ }),
16221
+ defineTool({
16222
+ name: "get_aaa_section",
16223
+ title: "Get AAA Section (Dashboard)",
16224
+ annotations: READ,
16225
+ ui: { ...APP_ONLY },
16226
+ description: "App-only helper for the RADIUS & User Manager dashboard: returns the rows of one section" + " (radius, um-users, um-profiles, um-limitations, um-routers, um-user-profiles, um-sessions)" + " as structured records with a stable id and secrets redacted, so the view can render and" + " refresh its table.",
16227
+ inputSchema: { slug: z77.enum(SLUGS) },
16228
+ async handler(a, ctx) {
16229
+ const list = await listAaaEntity(ctx, a.slug);
16230
+ return { text: summarize(a.slug, list), structuredContent: sectionView(a.slug, list) };
16231
+ }
16232
+ }),
16233
+ defineTool({
16234
+ name: "aaa_mutate",
16235
+ title: "Mutate AAA Row (Dashboard)",
16236
+ annotations: WRITE,
16237
+ ui: { ...APP_ONLY },
16238
+ description: "App-only helper for the RADIUS & User Manager dashboard: performs one create/update/remove/" + "toggle on a section row through the shared whitelist-guarded data layer, then returns the" + " refreshed section so the view can adopt it. `op` is add|update|remove|toggle; `slug` is the" + " section; `id` is the row's stable identifier (RouterOS .id or name); `fields` carries the" + " RouterOS attribute values for add/update; `enable` is the target state for toggle.",
16239
+ inputSchema: {
16240
+ op: z77.enum(["add", "update", "remove", "toggle"]),
16241
+ slug: z77.enum(SLUGS),
16242
+ id: z77.string().optional().describe("Row stable id (.id or name) for update/remove/toggle"),
16243
+ enable: z77.boolean().optional().describe("Target enabled state for op=toggle"),
16244
+ fields: z77.record(z77.string(), z77.string()).optional().describe("RouterOS attribute=value pairs for add/update")
16245
+ },
16246
+ async handler(a, ctx) {
16247
+ const fields = a.fields ?? {};
16248
+ let result;
16249
+ if (a.op === "add")
16250
+ result = await addAaaEntity(ctx, a.slug, fields);
16251
+ else if (a.op === "update")
16252
+ result = a.id ? await updateAaaEntity(ctx, a.slug, a.id, fields) : { ok: false, message: "id required" };
16253
+ else if (a.op === "remove")
16254
+ result = a.id ? await removeAaaEntity(ctx, a.slug, a.id) : { ok: false, message: "id required" };
16255
+ else
16256
+ result = a.id ? await toggleAaaEntity(ctx, a.slug, a.id, a.enable === true) : { ok: false, message: "id required" };
16257
+ const list = await listAaaEntity(ctx, a.slug);
16258
+ return {
16259
+ text: `${result.ok ? "OK" : "Failed"}: ${result.message}`,
16260
+ structuredContent: { ...sectionView(a.slug, list), lastOp: result }
16261
+ };
16262
+ }
16263
+ }),
16264
+ defineTool({
16265
+ name: "get_aaa_settings",
16266
+ title: "Get AAA Settings (Dashboard)",
16267
+ annotations: READ,
16268
+ ui: { ...APP_ONLY },
16269
+ description: "App-only helper for the RADIUS & User Manager dashboard: returns the singleton settings \u2014" + " RADIUS incoming/CoA listener (`/radius incoming`) and User Manager global settings" + " (`/user-manager`) \u2014 for the Settings tab.",
16270
+ async handler(_a, ctx) {
16271
+ const [radiusIncoming, um] = await Promise.all([getRadiusIncoming(ctx), getUmSettings(ctx)]);
16272
+ return {
16273
+ text: "AAA settings loaded.",
16274
+ structuredContent: {
16275
+ __mikrotikView: "aaa-settings",
16276
+ radiusIncoming,
16277
+ umAvailable: um.available,
16278
+ umSettings: um.settings
16279
+ }
16280
+ };
16281
+ }
16282
+ }),
16283
+ defineTool({
16284
+ name: "set_aaa_settings",
16285
+ title: "Set AAA Settings (Dashboard)",
16286
+ annotations: WRITE,
16287
+ ui: { ...APP_ONLY },
16288
+ description: "App-only helper for the RADIUS & User Manager dashboard: writes a singleton setting and" + " returns the refreshed settings. `target` is radius-incoming (CoA accept/port), um-settings" + " (enabled, use-profiles, certificate, ports) or radius-reset-counters; `fields` carries the" + " RouterOS attribute values.",
16289
+ inputSchema: {
16290
+ target: z77.enum(["radius-incoming", "um-settings", "radius-reset-counters"]),
16291
+ fields: z77.record(z77.string(), z77.string()).optional()
16292
+ },
16293
+ async handler(a, ctx) {
16294
+ const fields = a.fields ?? {};
16295
+ let result;
16296
+ if (a.target === "radius-incoming")
16297
+ result = await setRadiusIncoming(ctx, fields);
16298
+ else if (a.target === "um-settings")
16299
+ result = await setUmSettings(ctx, fields);
16300
+ else
16301
+ result = await resetRadiusCounters(ctx);
16302
+ const [radiusIncoming, um] = await Promise.all([getRadiusIncoming(ctx), getUmSettings(ctx)]);
16303
+ return {
16304
+ text: `${result.ok ? "OK" : "Failed"}: ${result.message}`,
16305
+ structuredContent: {
16306
+ __mikrotikView: "aaa-settings",
16307
+ radiusIncoming,
16308
+ umAvailable: um.available,
16309
+ umSettings: um.settings,
16310
+ lastOp: result
16311
+ }
16312
+ };
16313
+ }
16314
+ })
16315
+ ];
16316
+
16317
+ // src/tools/radius.ts
16318
+ import { z as z78 } from "zod";
15860
16319
  var radiusTools = [
15861
16320
  defineTool({
15862
16321
  name: "add_radius_server",
@@ -15864,21 +16323,21 @@ var radiusTools = [
15864
16323
  annotations: WRITE,
15865
16324
  description: "Adds a RADIUS server entry (`/radius add`) \u2014 configures the router as a RADIUS client" + " that authenticates PPP users, hotspot clients, wireless stations, DHCP leases, login" + " sessions, IPsec peers, or dot1x supplicants against an external RADIUS server." + " For listing existing entries use `list_radius_servers`." + " Returns the created entry's detail with the shared secret redacted." + ' `service` is a comma-separated list, e.g. `"login,ppp,hotspot,wireless,dhcp,ipsec,dot1x"`;' + ' `timeout` is a RouterOS duration string, e.g. `"300ms"`;' + " default authentication port is 1812 and accounting port is 1813.",
15866
16325
  inputSchema: {
15867
- address: z77.string().describe("RADIUS server IP address or hostname"),
15868
- secret: z77.string().describe("Shared secret used with the RADIUS server"),
15869
- service: z77.string().describe('Comma-separated services, e.g. "login,ppp,hotspot,wireless,dhcp,ipsec,dot1x"'),
15870
- authentication_port: z77.number().int().default(1812),
15871
- accounting_port: z77.number().int().default(1813),
15872
- timeout: z77.string().optional().describe('Request timeout, e.g. "300ms"'),
15873
- src_address: z77.string().optional(),
15874
- realm: z77.string().optional(),
15875
- called_id: z77.string().optional(),
15876
- domain: z77.string().optional(),
15877
- protocol: z77.enum(["udp", "radsec"]).optional().describe("Transport protocol used to reach the server (default udp)"),
15878
- certificate: z77.string().optional().describe("Certificate name to present when protocol is radsec"),
15879
- accounting_backup: z77.boolean().default(false).describe("Mark this entry as a backup accounting server"),
15880
- comment: z77.string().optional(),
15881
- disabled: z77.boolean().default(false)
16326
+ address: z78.string().describe("RADIUS server IP address or hostname"),
16327
+ secret: z78.string().describe("Shared secret used with the RADIUS server"),
16328
+ service: z78.string().describe('Comma-separated services, e.g. "login,ppp,hotspot,wireless,dhcp,ipsec,dot1x"'),
16329
+ authentication_port: z78.number().int().default(1812),
16330
+ accounting_port: z78.number().int().default(1813),
16331
+ timeout: z78.string().optional().describe('Request timeout, e.g. "300ms"'),
16332
+ src_address: z78.string().optional(),
16333
+ realm: z78.string().optional(),
16334
+ called_id: z78.string().optional(),
16335
+ domain: z78.string().optional(),
16336
+ protocol: z78.enum(["udp", "radsec"]).optional().describe("Transport protocol used to reach the server (default udp)"),
16337
+ certificate: z78.string().optional().describe("Certificate name to present when protocol is radsec"),
16338
+ accounting_backup: z78.boolean().default(false).describe("Mark this entry as a backup accounting server"),
16339
+ comment: z78.string().optional(),
16340
+ disabled: z78.boolean().default(false)
15882
16341
  },
15883
16342
  async handler(a, ctx) {
15884
16343
  ctx.info(`Adding RADIUS server: address=${a.address}, service=${a.service}`);
@@ -15898,8 +16357,8 @@ ${redactSecrets(details)}` : "RADIUS server creation completed but unable to ver
15898
16357
  annotations: READ,
15899
16358
  description: "Lists all configured RADIUS server entries (`/radius print`) \u2014 the router's RADIUS" + " client table. Optionally filter by partial service name or partial address string." + " Returns all matching entries with shared secrets redacted; use the `.id` values from" + " this output with `get_radius_server`, `update_radius_server`, `remove_radius_server`," + " `enable_radius_server`, or `disable_radius_server`.",
15900
16359
  inputSchema: {
15901
- service_filter: z77.string().optional().describe("Partial service match"),
15902
- address_filter: z77.string().optional().describe("Partial address match")
16360
+ service_filter: z78.string().optional().describe("Partial service match"),
16361
+ address_filter: z78.string().optional().describe("Partial address match")
15903
16362
  },
15904
16363
  async handler(a, ctx) {
15905
16364
  ctx.info("Listing RADIUS servers");
@@ -15920,7 +16379,7 @@ ${redactSecrets(result)}`;
15920
16379
  annotations: READ,
15921
16380
  description: "Retrieves full detail of a single RADIUS server entry (`/radius print detail where .id=`)" + " \u2014 use when you need the exact current configuration of one entry." + " The `radius_id` is the `.id` returned by `list_radius_servers` (e.g. `'*1'`)." + " Returns the entry's full field set with the shared secret redacted." + " For a summary list of all entries use `list_radius_servers`.",
15922
16381
  inputSchema: {
15923
- radius_id: z77.string().describe("RADIUS entry internal .id, e.g. '*1'")
16382
+ radius_id: z78.string().describe("RADIUS entry internal .id, e.g. '*1'")
15924
16383
  },
15925
16384
  async handler(a, ctx) {
15926
16385
  ctx.info(`Getting RADIUS server details: radius_id=${a.radius_id}`);
@@ -15936,22 +16395,22 @@ ${redactSecrets(result)}`;
15936
16395
  annotations: WRITE_IDEMPOTENT,
15937
16396
  description: "Updates one or more fields on an existing RADIUS server entry (`/radius set <id>`) \u2014" + " use to change the address, shared secret, services, ports, timeout, realm, or enabled" + " state without recreating the entry. The `radius_id` is the `.id` returned by" + " `list_radius_servers`. Returns the updated entry's full detail with the shared secret" + " redacted. To create a new entry use `add_radius_server`; to toggle enabled state only" + " use `enable_radius_server` or `disable_radius_server`.",
15938
16397
  inputSchema: {
15939
- radius_id: z77.string().describe("RADIUS entry internal .id, e.g. '*1'"),
15940
- address: z77.string().optional(),
15941
- secret: z77.string().optional(),
15942
- service: z77.string().optional(),
15943
- authentication_port: z77.number().int().optional(),
15944
- accounting_port: z77.number().int().optional(),
15945
- timeout: z77.string().optional(),
15946
- src_address: z77.string().optional(),
15947
- realm: z77.string().optional(),
15948
- called_id: z77.string().optional(),
15949
- domain: z77.string().optional(),
15950
- protocol: z77.enum(["udp", "radsec"]).optional().describe("Transport protocol used to reach the server (default udp)"),
15951
- certificate: z77.string().optional().describe("Certificate name to present when protocol is radsec"),
15952
- accounting_backup: z77.boolean().optional().describe("Mark this entry as a backup accounting server"),
15953
- comment: z77.string().optional(),
15954
- disabled: z77.boolean().optional()
16398
+ radius_id: z78.string().describe("RADIUS entry internal .id, e.g. '*1'"),
16399
+ address: z78.string().optional(),
16400
+ secret: z78.string().optional(),
16401
+ service: z78.string().optional(),
16402
+ authentication_port: z78.number().int().optional(),
16403
+ accounting_port: z78.number().int().optional(),
16404
+ timeout: z78.string().optional(),
16405
+ src_address: z78.string().optional(),
16406
+ realm: z78.string().optional(),
16407
+ called_id: z78.string().optional(),
16408
+ domain: z78.string().optional(),
16409
+ protocol: z78.enum(["udp", "radsec"]).optional().describe("Transport protocol used to reach the server (default udp)"),
16410
+ certificate: z78.string().optional().describe("Certificate name to present when protocol is radsec"),
16411
+ accounting_backup: z78.boolean().optional().describe("Mark this entry as a backup accounting server"),
16412
+ comment: z78.string().optional(),
16413
+ disabled: z78.boolean().optional()
15955
16414
  },
15956
16415
  async handler(a, ctx) {
15957
16416
  ctx.info(`Updating RADIUS server: radius_id=${a.radius_id}`);
@@ -15973,7 +16432,7 @@ ${redactSecrets(details)}`;
15973
16432
  annotations: DESTRUCTIVE,
15974
16433
  description: "Permanently deletes a RADIUS server entry (`/radius remove [find .id=...]`) \u2014 use when" + " an external RADIUS server is decommissioned or should no longer be used for any service." + " The `radius_id` is the `.id` returned by `list_radius_servers`." + " Performs an existence check before removal and reports if the entry is not found." + " To temporarily stop using a server without deleting it use `disable_radius_server`.",
15975
16434
  inputSchema: {
15976
- radius_id: z77.string().describe("RADIUS entry internal .id, e.g. '*1'")
16435
+ radius_id: z78.string().describe("RADIUS entry internal .id, e.g. '*1'")
15977
16436
  },
15978
16437
  async handler(a, ctx) {
15979
16438
  ctx.info(`Removing RADIUS server: radius_id=${a.radius_id}`);
@@ -15992,7 +16451,7 @@ ${redactSecrets(details)}`;
15992
16451
  annotations: WRITE_IDEMPOTENT,
15993
16452
  description: "Re-enables a previously disabled RADIUS server entry (`/radius enable [find .id=...]`) \u2014" + " the router resumes sending authentication/accounting requests to this server for the" + " services it covers. The `radius_id` is the `.id` returned by `list_radius_servers`." + " To disable without deleting use `disable_radius_server`;" + " to remove permanently use `remove_radius_server`.",
15994
16453
  inputSchema: {
15995
- radius_id: z77.string().describe("RADIUS entry internal .id, e.g. '*1'")
16454
+ radius_id: z78.string().describe("RADIUS entry internal .id, e.g. '*1'")
15996
16455
  },
15997
16456
  async handler(a, ctx) {
15998
16457
  ctx.info(`Enabling RADIUS server: radius_id=${a.radius_id}`);
@@ -16008,7 +16467,7 @@ ${redactSecrets(details)}`;
16008
16467
  annotations: WRITE_IDEMPOTENT,
16009
16468
  description: "Disables a RADIUS server entry without removing it (`/radius disable [find .id=...]`) \u2014" + " the router stops sending authentication/accounting requests to this server but preserves" + " its configuration for later re-activation. The `radius_id` is the `.id` returned by" + " `list_radius_servers`. To re-enable use `enable_radius_server`;" + " to delete permanently use `remove_radius_server`.",
16010
16469
  inputSchema: {
16011
- radius_id: z77.string().describe("RADIUS entry internal .id, e.g. '*1'")
16470
+ radius_id: z78.string().describe("RADIUS entry internal .id, e.g. '*1'")
16012
16471
  },
16013
16472
  async handler(a, ctx) {
16014
16473
  ctx.info(`Disabling RADIUS server: radius_id=${a.radius_id}`);
@@ -16037,8 +16496,8 @@ ${result}`;
16037
16496
  annotations: WRITE_IDEMPOTENT,
16038
16497
  description: "Configures the global RADIUS Change of Authorization (CoA) listener" + " (`/radius incoming set`) \u2014 controls whether the router accepts unsolicited CoA or" + " Disconnect-Request packets pushed by the RADIUS server to forcibly terminate or update" + " active sessions. `accept` enables/disables the listener; `port` sets the UDP listen" + " port (default 3799). This is a router-wide singleton; no `radius_id` is needed." + " To read current CoA settings use `get_radius_incoming`.",
16039
16498
  inputSchema: {
16040
- accept: z77.boolean().optional().describe("Whether to accept incoming CoA requests"),
16041
- port: z77.number().int().optional().describe("UDP port to listen on for CoA requests")
16499
+ accept: z78.boolean().optional().describe("Whether to accept incoming CoA requests"),
16500
+ port: z78.number().int().optional().describe("UDP port to listen on for CoA requests")
16042
16501
  },
16043
16502
  async handler(a, ctx) {
16044
16503
  ctx.info("Setting RADIUS incoming (CoA) settings");
@@ -16070,7 +16529,7 @@ ${details}`;
16070
16529
  ];
16071
16530
 
16072
16531
  // src/tools/multiwan.ts
16073
- import { z as z78 } from "zod";
16532
+ import { z as z79 } from "zod";
16074
16533
  async function runOrPreview(commands, apply, ctx, what) {
16075
16534
  if (!apply) {
16076
16535
  const plan = commands.map((c, i) => `${i + 1}. ${c}`).join(`
@@ -16102,11 +16561,11 @@ var multiwanTools = [
16102
16561
  annotations: DANGEROUS,
16103
16562
  description: "Builds ACTIVE-PASSIVE multi-WAN failover by adding health-checked default routes with ascending " + "distances (primary=1, each backup +1). RouterOS uses the lowest-distance reachable route; " + "`check-gateway` withdraws a route when its gateway stops answering, so traffic fails over to " + "the next WAN \u2014 and fails back automatically when the primary recovers. DEFAULTS TO A DRY RUN " + "(`apply=false`) so you can review before changing default routing (which can cut your own " + "access); set `apply=true` to execute. Does NOT remove existing 0.0.0.0/0 routes \u2014 clear " + "conflicting ones first with the route tools. For load balancing instead of failover use " + "setup_wan_loadbalance. Returns the plan or the resulting default routes.",
16104
16563
  inputSchema: {
16105
- primary_gateway: z78.string().describe("Primary WAN gateway IP (or interface name)"),
16106
- backup_gateways: z78.array(z78.string()).min(1).describe("One or more backup WAN gateways, in failover order"),
16107
- check: z78.enum(["ping", "arp"]).default("ping").describe("Gateway health-check method"),
16108
- comment_prefix: z78.string().default("wan-failover"),
16109
- apply: z78.boolean().default(false).describe("false = preview only (default); true = execute")
16564
+ primary_gateway: z79.string().describe("Primary WAN gateway IP (or interface name)"),
16565
+ backup_gateways: z79.array(z79.string()).min(1).describe("One or more backup WAN gateways, in failover order"),
16566
+ check: z79.enum(["ping", "arp"]).default("ping").describe("Gateway health-check method"),
16567
+ comment_prefix: z79.string().default("wan-failover"),
16568
+ apply: z79.boolean().default(false).describe("false = preview only (default); true = execute")
16110
16569
  },
16111
16570
  async handler(a, ctx) {
16112
16571
  const backups = a.backup_gateways;
@@ -16125,10 +16584,10 @@ var multiwanTools = [
16125
16584
  annotations: DANGEROUS,
16126
16585
  description: "Builds ECMP (equal-cost multi-path) load balancing across multiple WANs by adding ONE default " + "route whose `gateway` lists every WAN \u2014 RouterOS then spreads connections across the links " + "(per-connection, so a single flow stays on one WAN). `check-gateway` drops a dead link from " + "the set. DEFAULTS TO A DRY RUN (`apply=false`); set `apply=true` to execute. Does NOT remove " + "existing 0.0.0.0/0 routes \u2014 clear conflicting ones first. ECMP is simple but balances by " + "connection-hash, not by bandwidth; for sticky per-source balancing (PCC) use the firewall " + "mangle + routing-table tools. For failover instead of balancing use setup_wan_failover. " + "Returns the plan or the resulting default route.",
16127
16586
  inputSchema: {
16128
- gateways: z78.array(z78.string()).min(2).describe("Two or more WAN gateways to balance across"),
16129
- check: z78.enum(["ping", "arp"]).default("ping").describe("Gateway health-check method"),
16130
- comment: z78.string().default("wan-ecmp"),
16131
- apply: z78.boolean().default(false).describe("false = preview only (default); true = execute")
16587
+ gateways: z79.array(z79.string()).min(2).describe("Two or more WAN gateways to balance across"),
16588
+ check: z79.enum(["ping", "arp"]).default("ping").describe("Gateway health-check method"),
16589
+ comment: z79.string().default("wan-ecmp"),
16590
+ apply: z79.boolean().default(false).describe("false = preview only (default); true = execute")
16132
16591
  },
16133
16592
  async handler(a, ctx) {
16134
16593
  const gateways = a.gateways;
@@ -16140,7 +16599,7 @@ var multiwanTools = [
16140
16599
  ];
16141
16600
 
16142
16601
  // src/tools/routes.ts
16143
- import { z as z79 } from "zod";
16602
+ import { z as z80 } from "zod";
16144
16603
  async function addRoute(a, ctx) {
16145
16604
  ctx.info(`Adding route: dst=${a.dst_address}, gateway=${a.gateway}`);
16146
16605
  const cmd = new Cmd("/ip route add").set("dst-address", a.dst_address).set("gateway", a.gateway).opt("distance", a.distance).opt("scope", a.scope).opt("target-scope", a.target_scope).opt("routing-table", a.routing_table ?? a.routing_mark).opt("comment", a.comment).flag("disabled", a.disabled).opt("vrf-interface", a.vrf_interface).opt("pref-src", a.pref_src).opt("check-gateway", a.check_gateway).bool("suppress-hw-offload", a.suppress_hw_offload).opt("type", a.type).build();
@@ -16177,20 +16636,20 @@ var routeTools = [
16177
16636
  annotations: WRITE,
16178
16637
  description: "Adds an IPv4 static route (`/ip route add`) to the routing table \u2014 use this to define any " + "non-default unicast next-hop (host, subnet, or summarized prefix). " + "For the 0.0.0.0/0 default gateway use add_default_route; for null/drop routes use " + "add_blackhole_route; for IPv6 use add_ipv6_route. " + "`distance` sets priority (1\u2013255, lower wins); `routing_table` assigns the route to a " + 'policy-routing table (must already exist \u2014 see add_routing_table); `check_gateway` ("ping" ' + 'or "arp") enables active gateway monitoring. ' + "Returns the created route's detail including its `.id`.",
16179
16638
  inputSchema: {
16180
- dst_address: z79.string().describe('CIDR e.g. "0.0.0.0/0", "192.168.1.0/24"'),
16181
- gateway: z79.string(),
16182
- distance: z79.number().int().optional().describe("1-255 (lower = higher priority)"),
16183
- scope: z79.number().int().optional(),
16184
- target_scope: z79.number().int().optional(),
16185
- routing_table: z79.string().optional().describe('Policy-routing table name, e.g. "main" or a custom table (RouterOS v7)'),
16186
- routing_mark: z79.string().optional().describe("Deprecated alias for routing_table (RouterOS v6 name)"),
16187
- comment: z79.string().optional(),
16188
- disabled: z79.boolean().default(false),
16189
- vrf_interface: z79.string().optional(),
16190
- pref_src: z79.string().optional(),
16191
- check_gateway: z79.string().optional().describe('"ping" or "arp"'),
16192
- suppress_hw_offload: z79.boolean().optional().describe("Exclude route from hardware (HW) offloading"),
16193
- type: z79.string().optional().describe('"unicast" (default), "blackhole", "unreachable", or "prohibit"')
16639
+ dst_address: z80.string().describe('CIDR e.g. "0.0.0.0/0", "192.168.1.0/24"'),
16640
+ gateway: z80.string(),
16641
+ distance: z80.number().int().optional().describe("1-255 (lower = higher priority)"),
16642
+ scope: z80.number().int().optional(),
16643
+ target_scope: z80.number().int().optional(),
16644
+ routing_table: z80.string().optional().describe('Policy-routing table name, e.g. "main" or a custom table (RouterOS v7)'),
16645
+ routing_mark: z80.string().optional().describe("Deprecated alias for routing_table (RouterOS v6 name)"),
16646
+ comment: z80.string().optional(),
16647
+ disabled: z80.boolean().default(false),
16648
+ vrf_interface: z80.string().optional(),
16649
+ pref_src: z80.string().optional(),
16650
+ check_gateway: z80.string().optional().describe('"ping" or "arp"'),
16651
+ suppress_hw_offload: z80.boolean().optional().describe("Exclude route from hardware (HW) offloading"),
16652
+ type: z80.string().optional().describe('"unicast" (default), "blackhole", "unreachable", or "prohibit"')
16194
16653
  },
16195
16654
  async handler(a, ctx) {
16196
16655
  return addRoute(a, ctx);
@@ -16202,15 +16661,15 @@ var routeTools = [
16202
16661
  annotations: READ,
16203
16662
  description: "Lists IPv4 routes from `/ip route` with optional filters \u2014 the primary tool for inspecting " + "what routes the router knows. `dst_filter` and `gateway_filter` do substring matching; " + "`routing_table_filter` and `distance_filter` do exact matching; " + "`active_only`/`disabled_only`/`dynamic_only`/`static_only` are boolean flags. " + "For a table-scoped view of active routes use get_routing_table; for counts and summary stats " + "use get_route_statistics; for IPv6 use list_ipv6_routes. " + "Returns all matching route entries.",
16204
16663
  inputSchema: {
16205
- dst_filter: z79.string().optional(),
16206
- gateway_filter: z79.string().optional(),
16207
- routing_table_filter: z79.string().optional().describe("Exact policy-routing table name (RouterOS v7)"),
16208
- routing_mark_filter: z79.string().optional().describe("Deprecated alias for routing_table_filter"),
16209
- distance_filter: z79.number().int().optional(),
16210
- active_only: z79.boolean().default(false),
16211
- disabled_only: z79.boolean().default(false),
16212
- dynamic_only: z79.boolean().default(false),
16213
- static_only: z79.boolean().default(false)
16664
+ dst_filter: z80.string().optional(),
16665
+ gateway_filter: z80.string().optional(),
16666
+ routing_table_filter: z80.string().optional().describe("Exact policy-routing table name (RouterOS v7)"),
16667
+ routing_mark_filter: z80.string().optional().describe("Deprecated alias for routing_table_filter"),
16668
+ distance_filter: z80.number().int().optional(),
16669
+ active_only: z80.boolean().default(false),
16670
+ disabled_only: z80.boolean().default(false),
16671
+ dynamic_only: z80.boolean().default(false),
16672
+ static_only: z80.boolean().default(false)
16214
16673
  },
16215
16674
  async handler(a, ctx) {
16216
16675
  ctx.info(`Listing routes with filters: dst=${a.dst_filter}, gateway=${a.gateway_filter}`);
@@ -16244,7 +16703,7 @@ ${result}`;
16244
16703
  annotations: READ,
16245
16704
  description: "Fetches full detail for a single IPv4 route (`/ip route print detail where .id=\u2026`) \u2014 " + "use this after list_routes to inspect one entry's flags, nexthop, distance, scope, and " + "all attributes. `route_id` is the `*N` or `N` `.id` value from list_routes. " + "For listing multiple routes use list_routes; for a table-scoped view use get_routing_table. " + "Returns the complete detail block for that route, or a not-found message.",
16246
16705
  inputSchema: {
16247
- route_id: z79.string().describe('"*N" or "N" from list output e.g. "*3"')
16706
+ route_id: z80.string().describe('"*N" or "N" from list output e.g. "*3"')
16248
16707
  },
16249
16708
  async handler(a, ctx) {
16250
16709
  ctx.info(`Getting route details: route_id=${a.route_id}`);
@@ -16260,21 +16719,21 @@ ${result}`;
16260
16719
  annotations: WRITE_IDEMPOTENT,
16261
16720
  description: "Modifies an existing IPv4 static route (`/ip route set`) \u2014 change its gateway, dst-address, " + "distance, scope, routing-table, VRF interface, preferred-source, or gateway check method. " + "`route_id` is the `*N` `.id` from list_routes. " + 'Pass an empty string ("") for `routing_table`, `vrf_interface`, or `pref_src` to clear those fields. ' + "For toggling active state without editing attributes use enable_route / disable_route. " + "Returns the updated route detail.",
16262
16721
  inputSchema: {
16263
- route_id: z79.string().describe('"*N" or "N" from list output e.g. "*3"'),
16264
- dst_address: z79.string().optional().describe('CIDR e.g. "192.168.1.0/24"'),
16265
- gateway: z79.string().optional(),
16266
- distance: z79.number().int().optional().describe("1-255"),
16267
- scope: z79.number().int().optional(),
16268
- target_scope: z79.number().int().optional(),
16269
- routing_table: z79.string().optional().describe('Policy-routing table name (RouterOS v7); "" clears it'),
16270
- routing_mark: z79.string().optional().describe("Deprecated alias for routing_table (RouterOS v6 name)"),
16271
- comment: z79.string().optional(),
16272
- disabled: z79.boolean().optional(),
16273
- vrf_interface: z79.string().optional(),
16274
- pref_src: z79.string().optional(),
16275
- check_gateway: z79.string().optional().describe('"ping" or "arp"'),
16276
- suppress_hw_offload: z79.boolean().optional().describe("Exclude route from hardware (HW) offloading"),
16277
- type: z79.string().optional().describe('"unicast", "blackhole", "unreachable", or "prohibit"')
16722
+ route_id: z80.string().describe('"*N" or "N" from list output e.g. "*3"'),
16723
+ dst_address: z80.string().optional().describe('CIDR e.g. "192.168.1.0/24"'),
16724
+ gateway: z80.string().optional(),
16725
+ distance: z80.number().int().optional().describe("1-255"),
16726
+ scope: z80.number().int().optional(),
16727
+ target_scope: z80.number().int().optional(),
16728
+ routing_table: z80.string().optional().describe('Policy-routing table name (RouterOS v7); "" clears it'),
16729
+ routing_mark: z80.string().optional().describe("Deprecated alias for routing_table (RouterOS v6 name)"),
16730
+ comment: z80.string().optional(),
16731
+ disabled: z80.boolean().optional(),
16732
+ vrf_interface: z80.string().optional(),
16733
+ pref_src: z80.string().optional(),
16734
+ check_gateway: z80.string().optional().describe('"ping" or "arp"'),
16735
+ suppress_hw_offload: z80.boolean().optional().describe("Exclude route from hardware (HW) offloading"),
16736
+ type: z80.string().optional().describe('"unicast", "blackhole", "unreachable", or "prohibit"')
16278
16737
  },
16279
16738
  async handler(a, ctx) {
16280
16739
  ctx.info(`Updating route: route_id=${a.route_id}`);
@@ -16328,7 +16787,7 @@ ${details}`;
16328
16787
  annotations: DESTRUCTIVE,
16329
16788
  description: "Permanently deletes an IPv4 route (`/ip route remove`) \u2014 verifies the entry exists first " + "(count-only check) then removes it. `route_id` is the `*N` `.id` from list_routes. " + "To temporarily take a route out of service without deleting it use disable_route instead. " + "Confirms deletion or reports not-found.",
16330
16789
  inputSchema: {
16331
- route_id: z79.string().describe('"*N" or "N" from list output e.g. "*3"')
16790
+ route_id: z80.string().describe('"*N" or "N" from list output e.g. "*3"')
16332
16791
  },
16333
16792
  async handler(a, ctx) {
16334
16793
  ctx.info(`Removing route: route_id=${a.route_id}`);
@@ -16347,7 +16806,7 @@ ${details}`;
16347
16806
  annotations: WRITE_IDEMPOTENT,
16348
16807
  description: "Activates a disabled IPv4 route (`/ip route set disabled=no`) \u2014 makes it eligible for " + "route selection without altering any other attributes. " + "`route_id` is the `*N` `.id` from list_routes. " + "To deactivate a route use disable_route; to remove it permanently use remove_route. " + "Returns the updated route detail.",
16349
16808
  inputSchema: {
16350
- route_id: z79.string().describe('"*N" or "N" from list output e.g. "*3"')
16809
+ route_id: z80.string().describe('"*N" or "N" from list output e.g. "*3"')
16351
16810
  },
16352
16811
  async handler(a, ctx) {
16353
16812
  return setRouteDisabled(a.route_id, false, ctx);
@@ -16359,7 +16818,7 @@ ${details}`;
16359
16818
  annotations: WRITE_IDEMPOTENT,
16360
16819
  description: "Deactivates an IPv4 route (`/ip route set disabled=yes`) \u2014 removes it from route selection " + "without deleting it so it can be re-enabled later. " + "`route_id` is the `*N` `.id` from list_routes. " + "To re-activate use enable_route; to permanently delete use remove_route. " + "Returns the updated route detail.",
16361
16820
  inputSchema: {
16362
- route_id: z79.string().describe('"*N" or "N" from list output e.g. "*3"')
16821
+ route_id: z80.string().describe('"*N" or "N" from list output e.g. "*3"')
16363
16822
  },
16364
16823
  async handler(a, ctx) {
16365
16824
  return setRouteDisabled(a.route_id, true, ctx);
@@ -16371,9 +16830,9 @@ ${details}`;
16371
16830
  annotations: READ,
16372
16831
  description: "Reads IPv4 route entries scoped by routing table (`/ip route print`) \u2014 for any `table_name` " + 'other than "main" adds `where routing-table=<name>`; for the implicit default "main" table ' + "the routing-table filter is omitted (routes without an explicit table belong to main). " + 'Designed for policy-routing setups with multiple tables (e.g. "main", "ISP1", "ISP2"). ' + 'Filters by `table_name` (default "main"), optional `protocol_filter`, and `active_only` ' + "(default true, showing only routes currently used for forwarding). " + "For an unfiltered list across all tables use list_routes; for total/active/static counts " + "use get_route_statistics. Returns matching route entries from the specified table.",
16373
16832
  inputSchema: {
16374
- table_name: z79.string().default("main"),
16375
- protocol_filter: z79.string().optional(),
16376
- active_only: z79.boolean().default(true)
16833
+ table_name: z80.string().default("main"),
16834
+ protocol_filter: z80.string().optional(),
16835
+ active_only: z80.boolean().default(true)
16377
16836
  },
16378
16837
  async handler(a, ctx) {
16379
16838
  ctx.info(`Getting routing table: table=${a.table_name}`);
@@ -16396,9 +16855,9 @@ ${result}`;
16396
16855
  annotations: READ,
16397
16856
  description: "Resolves which nexthop RouterOS would use for a given IPv4 destination (`/ip route check`) " + '\u2014 answers "which gateway will this packet take?" without sending any traffic. ' + "Optionally scoped by `source` address and `routing_mark` 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.",
16398
16857
  inputSchema: {
16399
- destination: z79.string(),
16400
- source: z79.string().optional(),
16401
- routing_mark: z79.string().optional()
16858
+ destination: z80.string(),
16859
+ source: z80.string().optional(),
16860
+ routing_mark: z80.string().optional()
16402
16861
  },
16403
16862
  async handler(a, ctx) {
16404
16863
  ctx.info(`Checking route path to: ${a.destination}`);
@@ -16456,10 +16915,10 @@ ${fib}`;
16456
16915
  annotations: WRITE,
16457
16916
  description: "Adds an IPv4 default route (`/ip route add dst-address=0.0.0.0/0`) with gateway health " + "monitoring \u2014 a convenience wrapper around add_route fixed to the 0.0.0.0/0 prefix. " + '`check_gateway` defaults to "ping" (active gateway probing); set `distance` to control ' + "failover priority when multiple default routes exist. " + "For any other prefix use add_route; for null/drop routes use add_blackhole_route; " + "for IPv6 use add_ipv6_route. Returns the created route detail including its `.id`.",
16458
16917
  inputSchema: {
16459
- gateway: z79.string(),
16460
- distance: z79.number().int().default(1),
16461
- comment: z79.string().optional(),
16462
- check_gateway: z79.string().default("ping")
16918
+ gateway: z80.string(),
16919
+ distance: z80.number().int().default(1),
16920
+ comment: z80.string().optional(),
16921
+ check_gateway: z80.string().default("ping")
16463
16922
  },
16464
16923
  async handler(a, ctx) {
16465
16924
  return addRoute({
@@ -16477,9 +16936,9 @@ ${fib}`;
16477
16936
  annotations: WRITE,
16478
16937
  description: "Adds an IPv4 null/blackhole route (`/ip route add type=blackhole`) \u2014 traffic to `dst_address` " + "is silently dropped at the routing layer without generating an ICMP unreachable. " + "Used for traffic engineering, bogon suppression, or null-routing abusive prefixes. " + "For a normal next-hop route use add_route; for a default gateway use add_default_route. " + "Returns the created route's `.id` on success.",
16479
16938
  inputSchema: {
16480
- dst_address: z79.string().describe('CIDR e.g. "10.0.0.0/8"'),
16481
- distance: z79.number().int().default(1).describe("1-255"),
16482
- comment: z79.string().optional()
16939
+ dst_address: z80.string().describe('CIDR e.g. "10.0.0.0/8"'),
16940
+ distance: z80.number().int().default(1).describe("1-255"),
16941
+ comment: z80.string().optional()
16483
16942
  },
16484
16943
  async handler(a, ctx) {
16485
16944
  ctx.info(`Adding blackhole route: dst=${a.dst_address}`);
@@ -16522,7 +16981,7 @@ ${stats.join(`
16522
16981
  ];
16523
16982
 
16524
16983
  // src/tools/routing-bfd.ts
16525
- import { z as z80 } from "zod";
16984
+ import { z as z81 } from "zod";
16526
16985
  var UNSUPPORTED = "BFD is not available on this device (requires RouterOS v7 with the routing package).";
16527
16986
  var routingBfdTools = [
16528
16987
  defineTool({
@@ -16546,14 +17005,14 @@ ${result}`;
16546
17005
  annotations: WRITE,
16547
17006
  description: "Creates a BFD timer-profile entry (`/routing bfd configuration add`) that binds sub-second " + "failure-detection parameters to an interface or interface-list in a VRF. `interfaces` names the " + "interface or interface-list where BFD runs; `min_rx`/`min_tx` set the desired minimum receive/transmit " + 'intervals (e.g. "200ms"); `multiplier` sets how many missed packets declare a session down ' + "(detection time \u2248 interval \xD7 multiplier). To view existing configurations use `list_bfd_configurations`; " + "to see live session state use `list_bfd_sessions`. Returns the new entry's `.id`.",
16548
17007
  inputSchema: {
16549
- interfaces: z80.string().describe("Interface or interface-list name BFD runs on"),
16550
- vrf: z80.string().optional().describe("VRF (default 'main')"),
16551
- min_rx: z80.string().optional().describe('Desired min RX interval, e.g. "200ms"'),
16552
- min_tx: z80.string().optional().describe('Desired min TX interval, e.g. "200ms"'),
16553
- multiplier: z80.number().int().optional().describe("Detection multiplier, e.g. 5"),
16554
- forbid_bfd: z80.boolean().optional().describe("Forbid BFD on the matched interfaces"),
16555
- comment: z80.string().optional(),
16556
- disabled: z80.boolean().default(false)
17008
+ interfaces: z81.string().describe("Interface or interface-list name BFD runs on"),
17009
+ vrf: z81.string().optional().describe("VRF (default 'main')"),
17010
+ min_rx: z81.string().optional().describe('Desired min RX interval, e.g. "200ms"'),
17011
+ min_tx: z81.string().optional().describe('Desired min TX interval, e.g. "200ms"'),
17012
+ multiplier: z81.number().int().optional().describe("Detection multiplier, e.g. 5"),
17013
+ forbid_bfd: z81.boolean().optional().describe("Forbid BFD on the matched interfaces"),
17014
+ comment: z81.string().optional(),
17015
+ disabled: z81.boolean().default(false)
16557
17016
  },
16558
17017
  async handler(a, ctx) {
16559
17018
  ctx.info(`Adding BFD configuration for ${a.interfaces}`);
@@ -16573,15 +17032,15 @@ ${result}`;
16573
17032
  annotations: WRITE_IDEMPOTENT,
16574
17033
  description: "Updates an existing BFD timer-profile entry (`/routing bfd configuration set`) by its `.id` " + "(obtain from `list_bfd_configurations`). Adjusts `interfaces`, `vrf`, `min_rx`/`min_tx` intervals " + '(e.g. "200ms"), `multiplier`, `comment`, or `disabled` state. To toggle enabled state only use ' + "`set_bfd_configuration_enabled`. Returns the updated entry's full detail.",
16575
17034
  inputSchema: {
16576
- config_id: z80.string().describe('Configuration id, e.g. "*1"'),
16577
- interfaces: z80.string().optional(),
16578
- vrf: z80.string().optional(),
16579
- min_rx: z80.string().optional(),
16580
- min_tx: z80.string().optional(),
16581
- multiplier: z80.number().int().optional(),
16582
- forbid_bfd: z80.boolean().optional(),
16583
- comment: z80.string().optional(),
16584
- disabled: z80.boolean().optional()
17035
+ config_id: z81.string().describe('Configuration id, e.g. "*1"'),
17036
+ interfaces: z81.string().optional(),
17037
+ vrf: z81.string().optional(),
17038
+ min_rx: z81.string().optional(),
17039
+ min_tx: z81.string().optional(),
17040
+ multiplier: z81.number().int().optional(),
17041
+ forbid_bfd: z81.boolean().optional(),
17042
+ comment: z81.string().optional(),
17043
+ disabled: z81.boolean().optional()
16585
17044
  },
16586
17045
  async handler(a, ctx) {
16587
17046
  ctx.info(`Updating BFD configuration ${a.config_id}`);
@@ -16611,7 +17070,7 @@ ${details}`;
16611
17070
  annotations: DESTRUCTIVE,
16612
17071
  description: "Permanently removes a BFD timer-profile entry (`/routing bfd configuration remove`) by its `.id` " + "(obtain from `list_bfd_configurations`). Any BFD sessions driven by this configuration will stop. " + "To suspend BFD without deleting the configuration use `set_bfd_configuration_enabled`.",
16613
17072
  inputSchema: {
16614
- config_id: z80.string().describe('Configuration id, e.g. "*1"')
17073
+ config_id: z81.string().describe('Configuration id, e.g. "*1"')
16615
17074
  },
16616
17075
  async handler(a, ctx) {
16617
17076
  ctx.info(`Removing BFD configuration ${a.config_id}`);
@@ -16629,8 +17088,8 @@ ${details}`;
16629
17088
  annotations: WRITE_IDEMPOTENT,
16630
17089
  description: "Enables or disables a BFD configuration entry (`/routing bfd configuration set ... disabled=yes/no`) " + "by its `.id` (obtain from `list_bfd_configurations`). Use this to suspend BFD on specific interfaces " + "without removing the configuration. To change timer parameters use `update_bfd_configuration`; " + "to delete the entry permanently use `remove_bfd_configuration`.",
16631
17090
  inputSchema: {
16632
- config_id: z80.string().describe('Configuration id, e.g. "*1"'),
16633
- enabled: z80.boolean()
17091
+ config_id: z81.string().describe('Configuration id, e.g. "*1"'),
17092
+ enabled: z81.boolean()
16634
17093
  },
16635
17094
  async handler(a, ctx) {
16636
17095
  ctx.info(`Setting BFD configuration ${a.config_id} enabled=${a.enabled}`);
@@ -16648,7 +17107,7 @@ ${details}`;
16648
17107
  annotations: READ,
16649
17108
  description: "Lists live BFD neighbor sessions (`/routing bfd session print detail`) \u2014 shows each peer's state " + "(up/down), local/remote discriminators, and negotiated rx/tx intervals. Read-only runtime state; " + "use it to verify BFD is actually up before relying on fast failover for OSPF/BGP. Filter to only " + "up sessions with `up_only=true`. To manage the timer-profile entries that drive these sessions use " + "`list_bfd_configurations`.",
16650
17109
  inputSchema: {
16651
- up_only: z80.boolean().default(false).describe("Show only sessions currently up")
17110
+ up_only: z81.boolean().default(false).describe("Show only sessions currently up")
16652
17111
  },
16653
17112
  async handler(a, ctx) {
16654
17113
  ctx.info("Listing BFD sessions");
@@ -16666,7 +17125,7 @@ ${result}`;
16666
17125
  ];
16667
17126
 
16668
17127
  // src/tools/routing-bgp.ts
16669
- import { z as z81 } from "zod";
17128
+ import { z as z82 } from "zod";
16670
17129
  var UNSUPPORTED2 = "BGP is not available on this device (requires RouterOS v7 with the routing package).";
16671
17130
  var routingBgpTools = [
16672
17131
  defineTool({
@@ -16675,7 +17134,7 @@ var routingBgpTools = [
16675
17134
  annotations: READ,
16676
17135
  description: "Lists configured BGP peer connections (`/routing bgp connection`) \u2014 the static configuration of each peer " + "or listener, including local/remote AS, addresses, and role. For runtime peering state (established/idle, " + "uptime, prefix counts) use `list_bgp_sessions`. For shared configuration objects inherited by connections " + "use `list_bgp_templates`. Optionally filters by connection name substring. Returns all matching connection " + "entries. Requires RouterOS v7 with the routing package.",
16677
17136
  inputSchema: {
16678
- name_filter: z81.string().optional().describe("Substring match on connection name")
17137
+ name_filter: z82.string().optional().describe("Substring match on connection name")
16679
17138
  },
16680
17139
  async handler(a, ctx) {
16681
17140
  ctx.info("Listing BGP connections");
@@ -16695,7 +17154,7 @@ ${result}`;
16695
17154
  title: "Get BGP Connection Details",
16696
17155
  annotations: READ,
16697
17156
  description: "Fetches full configuration detail for a single BGP connection by name " + '(`/routing bgp connection print detail where name="<name>"`). Use this to inspect one peer\'s full ' + "settings; to browse all connections use `list_bgp_connections`. For runtime session status (established, " + "uptime, prefix counts) use `list_bgp_sessions`. Returns the complete connection record or a not-found message.",
16698
- inputSchema: { name: z81.string().describe("BGP connection name") },
17157
+ inputSchema: { name: z82.string().describe("BGP connection name") },
16699
17158
  async handler(a, ctx) {
16700
17159
  ctx.info(`Getting BGP connection: ${a.name}`);
16701
17160
  const result = await executeMikrotikCommand(`/routing bgp connection print detail where name="${a.name}"`, ctx);
@@ -16712,41 +17171,41 @@ ${result}`;
16712
17171
  annotations: WRITE,
16713
17172
  description: "Creates a BGP peer connection (`/routing bgp connection add`) \u2014 the per-peer configuration that " + "initiates or accepts a BGP session. At minimum supply `name`, `remote_address`, and `as`/`local_role` for " + "the local side. `local_role` values: `ebgp`, `ibgp`, `ebgp-customer`, `ebgp-provider`, `ibgp-rr`, " + '`ibgp-rr-client`. For `address_families` pass a comma-separated list, e.g. `"ip"` or `"ip,ipv6,l2vpn"`. ' + 'For `hold_time`/`keepalive_time` use RouterOS duration strings, e.g. `"3m"` or `"180s"`. ' + "`input_filter`/`output_filter` reference `/routing filter` chain names. Common settings (AS, filters, timers) " + "can be factored into a template referenced via `templates`; to create templates use `add_bgp_template`. " + "To modify an existing connection use `update_bgp_connection`. Returns the created connection's full detail. " + "Requires RouterOS v7 with the routing package.",
16714
17173
  inputSchema: {
16715
- name: z81.string().describe("Unique connection name"),
16716
- remote_address: z81.string().describe("Peer IP address (remote.address)"),
16717
- remote_as: z81.number().int().optional().describe("Peer AS number (remote.as)"),
16718
- remote_port: z81.number().int().optional().describe("Peer TCP port (remote.port)"),
16719
- as: z81.number().int().optional().describe("Local AS number"),
16720
- local_role: z81.string().optional().describe("local.role: ebgp, ibgp, ebgp-customer, ebgp-provider, ibgp-rr, ibgp-rr-client, \u2026"),
16721
- local_address: z81.string().optional().describe("Local source address (local.address)"),
16722
- local_port: z81.number().int().optional().describe("Local TCP port (local.port)"),
16723
- router_id: z81.string().optional().describe("Override router-id for this connection"),
16724
- cluster_id: z81.string().optional().describe("Route-reflector cluster ID (cluster-id)"),
16725
- templates: z81.string().optional().describe("Template name(s) to inherit settings from"),
16726
- address_families: z81.string().optional().describe('Comma list, e.g. "ip" or "ip,ipv6,l2vpn"'),
16727
- hold_time: z81.string().optional().describe('e.g. "3m" or "180s"'),
16728
- keepalive_time: z81.string().optional(),
16729
- multihop: z81.boolean().optional().describe("Allow non-directly-connected peers"),
16730
- nexthop_choice: z81.string().optional().describe("default, force-self, or propagate"),
16731
- use_bfd: z81.boolean().optional().describe("Use BFD for fast failure detection (use-bfd)"),
16732
- tcp_md5_key: z81.string().optional().describe("TCP MD5 authentication key (tcp-md5-key)"),
16733
- connect: z81.boolean().optional().describe("Actively initiate the TCP session (connect)"),
16734
- listen: z81.boolean().optional().describe("Accept incoming TCP session (listen)"),
16735
- input_filter: z81.string().optional().describe("Routing filter chain for inbound routes (input.filter)"),
16736
- input_accept_nlri: z81.string().optional().describe("Address list of accepted NLRI prefixes (input.accept-nlri)"),
16737
- input_allow_as: z81.number().int().optional().describe("Number of times own AS may appear in AS-path (input.allow-as)"),
16738
- input_ignore_as_path_len: z81.boolean().optional().describe("Ignore AS-path length in best-path selection (input.ignore-as-path-len)"),
16739
- output_filter: z81.string().optional().describe("Routing filter chain for outbound routes (output.filter)"),
16740
- output_network: z81.string().optional().describe("Address list of networks to originate/advertise (output.network)"),
16741
- output_default_originate: z81.string().optional().describe("Advertise a default route: never, if-installed, or always (output.default-originate)"),
16742
- output_redistribute: z81.string().optional().describe("Comma list of route sources to redistribute, e.g. connected,static (output.redistribute)"),
16743
- output_as_override: z81.boolean().optional().describe("Replace peer AS in AS-path with local AS (output.as-override)"),
16744
- output_keep_sent_attributes: z81.boolean().optional().describe("Retain sent path attributes for inspection (output.keep-sent-attributes)"),
16745
- output_no_client_to_client_reflection: z81.boolean().optional().describe("Disable route-reflector client-to-client reflection (output.no-client-to-client-reflection)"),
16746
- routing_table: z81.string().optional().describe("RIB to install learned routes into"),
16747
- vrf: z81.string().optional(),
16748
- comment: z81.string().optional(),
16749
- disabled: z81.boolean().default(false)
17174
+ name: z82.string().describe("Unique connection name"),
17175
+ remote_address: z82.string().describe("Peer IP address (remote.address)"),
17176
+ remote_as: z82.number().int().optional().describe("Peer AS number (remote.as)"),
17177
+ remote_port: z82.number().int().optional().describe("Peer TCP port (remote.port)"),
17178
+ as: z82.number().int().optional().describe("Local AS number"),
17179
+ local_role: z82.string().optional().describe("local.role: ebgp, ibgp, ebgp-customer, ebgp-provider, ibgp-rr, ibgp-rr-client, \u2026"),
17180
+ local_address: z82.string().optional().describe("Local source address (local.address)"),
17181
+ local_port: z82.number().int().optional().describe("Local TCP port (local.port)"),
17182
+ router_id: z82.string().optional().describe("Override router-id for this connection"),
17183
+ cluster_id: z82.string().optional().describe("Route-reflector cluster ID (cluster-id)"),
17184
+ templates: z82.string().optional().describe("Template name(s) to inherit settings from"),
17185
+ address_families: z82.string().optional().describe('Comma list, e.g. "ip" or "ip,ipv6,l2vpn"'),
17186
+ hold_time: z82.string().optional().describe('e.g. "3m" or "180s"'),
17187
+ keepalive_time: z82.string().optional(),
17188
+ multihop: z82.boolean().optional().describe("Allow non-directly-connected peers"),
17189
+ nexthop_choice: z82.string().optional().describe("default, force-self, or propagate"),
17190
+ use_bfd: z82.boolean().optional().describe("Use BFD for fast failure detection (use-bfd)"),
17191
+ tcp_md5_key: z82.string().optional().describe("TCP MD5 authentication key (tcp-md5-key)"),
17192
+ connect: z82.boolean().optional().describe("Actively initiate the TCP session (connect)"),
17193
+ listen: z82.boolean().optional().describe("Accept incoming TCP session (listen)"),
17194
+ input_filter: z82.string().optional().describe("Routing filter chain for inbound routes (input.filter)"),
17195
+ input_accept_nlri: z82.string().optional().describe("Address list of accepted NLRI prefixes (input.accept-nlri)"),
17196
+ input_allow_as: z82.number().int().optional().describe("Number of times own AS may appear in AS-path (input.allow-as)"),
17197
+ input_ignore_as_path_len: z82.boolean().optional().describe("Ignore AS-path length in best-path selection (input.ignore-as-path-len)"),
17198
+ output_filter: z82.string().optional().describe("Routing filter chain for outbound routes (output.filter)"),
17199
+ output_network: z82.string().optional().describe("Address list of networks to originate/advertise (output.network)"),
17200
+ output_default_originate: z82.string().optional().describe("Advertise a default route: never, if-installed, or always (output.default-originate)"),
17201
+ output_redistribute: z82.string().optional().describe("Comma list of route sources to redistribute, e.g. connected,static (output.redistribute)"),
17202
+ output_as_override: z82.boolean().optional().describe("Replace peer AS in AS-path with local AS (output.as-override)"),
17203
+ output_keep_sent_attributes: z82.boolean().optional().describe("Retain sent path attributes for inspection (output.keep-sent-attributes)"),
17204
+ output_no_client_to_client_reflection: z82.boolean().optional().describe("Disable route-reflector client-to-client reflection (output.no-client-to-client-reflection)"),
17205
+ routing_table: z82.string().optional().describe("RIB to install learned routes into"),
17206
+ vrf: z82.string().optional(),
17207
+ comment: z82.string().optional(),
17208
+ disabled: z82.boolean().default(false)
16750
17209
  },
16751
17210
  async handler(a, ctx) {
16752
17211
  ctx.info(`Adding BGP connection: ${a.name}`);
@@ -16768,39 +17227,39 @@ ${details}`;
16768
17227
  annotations: WRITE_IDEMPOTENT,
16769
17228
  description: 'Modifies settings of an existing BGP connection by name (`/routing bgp connection set [find name="<name>"]`). ' + "Only supplied fields are changed; omitted fields are left unchanged. To enable or disable a connection " + "without touching other settings use `set_bgp_connection_enabled`. To delete a connection use " + "`remove_bgp_connection`. Returns the full updated connection detail on success.",
16770
17229
  inputSchema: {
16771
- name: z81.string().describe("Existing BGP connection name"),
16772
- remote_address: z81.string().optional(),
16773
- remote_as: z81.number().int().optional(),
16774
- remote_port: z81.number().int().optional(),
16775
- as: z81.number().int().optional(),
16776
- local_role: z81.string().optional(),
16777
- local_address: z81.string().optional(),
16778
- local_port: z81.number().int().optional(),
16779
- router_id: z81.string().optional(),
16780
- cluster_id: z81.string().optional(),
16781
- address_families: z81.string().optional(),
16782
- hold_time: z81.string().optional(),
16783
- keepalive_time: z81.string().optional(),
16784
- multihop: z81.boolean().optional(),
16785
- nexthop_choice: z81.string().optional(),
16786
- use_bfd: z81.boolean().optional(),
16787
- tcp_md5_key: z81.string().optional(),
16788
- connect: z81.boolean().optional(),
16789
- listen: z81.boolean().optional(),
16790
- input_filter: z81.string().optional(),
16791
- input_accept_nlri: z81.string().optional(),
16792
- input_allow_as: z81.number().int().optional(),
16793
- input_ignore_as_path_len: z81.boolean().optional(),
16794
- output_filter: z81.string().optional(),
16795
- output_network: z81.string().optional(),
16796
- output_default_originate: z81.string().optional(),
16797
- output_redistribute: z81.string().optional(),
16798
- output_as_override: z81.boolean().optional(),
16799
- output_keep_sent_attributes: z81.boolean().optional(),
16800
- output_no_client_to_client_reflection: z81.boolean().optional(),
16801
- routing_table: z81.string().optional(),
16802
- comment: z81.string().optional(),
16803
- disabled: z81.boolean().optional()
17230
+ name: z82.string().describe("Existing BGP connection name"),
17231
+ remote_address: z82.string().optional(),
17232
+ remote_as: z82.number().int().optional(),
17233
+ remote_port: z82.number().int().optional(),
17234
+ as: z82.number().int().optional(),
17235
+ local_role: z82.string().optional(),
17236
+ local_address: z82.string().optional(),
17237
+ local_port: z82.number().int().optional(),
17238
+ router_id: z82.string().optional(),
17239
+ cluster_id: z82.string().optional(),
17240
+ address_families: z82.string().optional(),
17241
+ hold_time: z82.string().optional(),
17242
+ keepalive_time: z82.string().optional(),
17243
+ multihop: z82.boolean().optional(),
17244
+ nexthop_choice: z82.string().optional(),
17245
+ use_bfd: z82.boolean().optional(),
17246
+ tcp_md5_key: z82.string().optional(),
17247
+ connect: z82.boolean().optional(),
17248
+ listen: z82.boolean().optional(),
17249
+ input_filter: z82.string().optional(),
17250
+ input_accept_nlri: z82.string().optional(),
17251
+ input_allow_as: z82.number().int().optional(),
17252
+ input_ignore_as_path_len: z82.boolean().optional(),
17253
+ output_filter: z82.string().optional(),
17254
+ output_network: z82.string().optional(),
17255
+ output_default_originate: z82.string().optional(),
17256
+ output_redistribute: z82.string().optional(),
17257
+ output_as_override: z82.boolean().optional(),
17258
+ output_keep_sent_attributes: z82.boolean().optional(),
17259
+ output_no_client_to_client_reflection: z82.boolean().optional(),
17260
+ routing_table: z82.string().optional(),
17261
+ comment: z82.string().optional(),
17262
+ disabled: z82.boolean().optional()
16804
17263
  },
16805
17264
  async handler(a, ctx) {
16806
17265
  ctx.info(`Updating BGP connection: ${a.name}`);
@@ -16845,7 +17304,7 @@ ${details}`;
16845
17304
  title: "Remove BGP Connection",
16846
17305
  annotations: DESTRUCTIVE,
16847
17306
  description: 'Permanently deletes a BGP peer connection by name (`/routing bgp connection remove [find name="<name>"]`), ' + "tearing down the peering and removing all its configuration. To only suspend a connection without deleting it " + "use `set_bgp_connection_enabled`. To remove a shared settings object use `remove_bgp_template`. " + "The `name` is the connection name visible in `list_bgp_connections`.",
16848
- inputSchema: { name: z81.string().describe("BGP connection name to remove") },
17307
+ inputSchema: { name: z82.string().describe("BGP connection name to remove") },
16849
17308
  async handler(a, ctx) {
16850
17309
  ctx.info(`Removing BGP connection: ${a.name}`);
16851
17310
  const result = await executeMikrotikCommand(`/routing bgp connection remove [find name="${a.name}"]`, ctx);
@@ -16862,8 +17321,8 @@ ${details}`;
16862
17321
  annotations: WRITE_IDEMPOTENT,
16863
17322
  description: 'Enables or disables a BGP connection by name (`/routing bgp connection set [find name="<name>"] ' + "disabled=yes/no`). Pass `enabled=true` to bring a peer up, `enabled=false` to suspend it without deleting " + "its configuration. To change other connection settings use `update_bgp_connection`. To permanently delete " + "the connection use `remove_bgp_connection`.",
16864
17323
  inputSchema: {
16865
- name: z81.string().describe("BGP connection name"),
16866
- enabled: z81.boolean()
17324
+ name: z82.string().describe("BGP connection name"),
17325
+ enabled: z82.boolean()
16867
17326
  },
16868
17327
  async handler(a, ctx) {
16869
17328
  ctx.info(`Setting BGP connection ${a.name} enabled=${a.enabled}`);
@@ -16896,24 +17355,24 @@ ${result}`;
16896
17355
  annotations: WRITE,
16897
17356
  description: "Creates a BGP template (`/routing bgp template add`) to hold shared peer settings (AS, address-families, " + "route filters, routing table) that multiple connections can inherit via their `templates` field, avoiding " + 'repetition across many peers. For `address_families` pass a comma-separated list, e.g. `"ip,ipv6"`. ' + "`input_filter`/`output_filter` reference `/routing filter` chain names. To create a peer that references " + "this template use `add_bgp_connection`. To remove a template use `remove_bgp_template`. Returns success " + "confirmation; verify with `list_bgp_templates`. Requires RouterOS v7 with the routing package.",
16898
17357
  inputSchema: {
16899
- name: z81.string().describe("Template name"),
16900
- as: z81.number().int().optional().describe("Local AS number"),
16901
- router_id: z81.string().optional(),
16902
- cluster_id: z81.string().optional().describe("Route-reflector cluster ID (cluster-id)"),
16903
- address_families: z81.string().optional().describe('e.g. "ip,ipv6"'),
16904
- hold_time: z81.string().optional().describe('e.g. "3m" or "180s"'),
16905
- keepalive_time: z81.string().optional(),
16906
- multihop: z81.boolean().optional().describe("Allow non-directly-connected peers"),
16907
- nexthop_choice: z81.string().optional().describe("default, force-self, or propagate"),
16908
- use_bfd: z81.boolean().optional().describe("Use BFD for fast failure detection (use-bfd)"),
16909
- input_filter: z81.string().optional(),
16910
- output_filter: z81.string().optional(),
16911
- output_network: z81.string().optional().describe("Address list of networks to originate/advertise (output.network)"),
16912
- output_default_originate: z81.string().optional().describe("Advertise a default route: never, if-installed, or always (output.default-originate)"),
16913
- output_redistribute: z81.string().optional().describe("Comma list of route sources to redistribute, e.g. connected,static (output.redistribute)"),
16914
- routing_table: z81.string().optional(),
16915
- comment: z81.string().optional(),
16916
- disabled: z81.boolean().default(false)
17358
+ name: z82.string().describe("Template name"),
17359
+ as: z82.number().int().optional().describe("Local AS number"),
17360
+ router_id: z82.string().optional(),
17361
+ cluster_id: z82.string().optional().describe("Route-reflector cluster ID (cluster-id)"),
17362
+ address_families: z82.string().optional().describe('e.g. "ip,ipv6"'),
17363
+ hold_time: z82.string().optional().describe('e.g. "3m" or "180s"'),
17364
+ keepalive_time: z82.string().optional(),
17365
+ multihop: z82.boolean().optional().describe("Allow non-directly-connected peers"),
17366
+ nexthop_choice: z82.string().optional().describe("default, force-self, or propagate"),
17367
+ use_bfd: z82.boolean().optional().describe("Use BFD for fast failure detection (use-bfd)"),
17368
+ input_filter: z82.string().optional(),
17369
+ output_filter: z82.string().optional(),
17370
+ output_network: z82.string().optional().describe("Address list of networks to originate/advertise (output.network)"),
17371
+ output_default_originate: z82.string().optional().describe("Advertise a default route: never, if-installed, or always (output.default-originate)"),
17372
+ output_redistribute: z82.string().optional().describe("Comma list of route sources to redistribute, e.g. connected,static (output.redistribute)"),
17373
+ routing_table: z82.string().optional(),
17374
+ comment: z82.string().optional(),
17375
+ disabled: z82.boolean().default(false)
16917
17376
  },
16918
17377
  async handler(a, ctx) {
16919
17378
  ctx.info(`Adding BGP template: ${a.name}`);
@@ -16931,7 +17390,7 @@ ${result}`;
16931
17390
  title: "Remove BGP Template",
16932
17391
  annotations: DESTRUCTIVE,
16933
17392
  description: 'Permanently deletes a BGP template by name (`/routing bgp template remove [find name="<name>"]`). ' + "Connections that referenced this template lose the inherited settings \u2014 review with `list_bgp_connections` " + "before removing. To remove a peer connection instead use `remove_bgp_connection`. The `name` is the template " + "name visible in `list_bgp_templates`.",
16934
- inputSchema: { name: z81.string().describe("BGP template name to remove") },
17393
+ inputSchema: { name: z82.string().describe("BGP template name to remove") },
16935
17394
  async handler(a, ctx) {
16936
17395
  ctx.info(`Removing BGP template: ${a.name}`);
16937
17396
  const result = await executeMikrotikCommand(`/routing bgp template remove [find name="${a.name}"]`, ctx);
@@ -16948,7 +17407,7 @@ ${result}`;
16948
17407
  annotations: READ,
16949
17408
  description: "Lists runtime BGP session state (`/routing bgp session print detail`) \u2014 negotiated peering status " + "(established/idle/active/\u2026), remote AS, uptime, and received/advertised prefix counts. This is the live " + "operational view, not configuration; to view or change peer configuration use `list_bgp_connections`. " + "Pass `established_only=true` to filter to established (fully up) sessions only. For prefixes being sent to a peer use " + "`list_bgp_advertisements`. Returns all matching session entries. Requires RouterOS v7 with the routing package.",
16950
17409
  inputSchema: {
16951
- established_only: z81.boolean().default(false).describe("Show only established sessions")
17410
+ established_only: z82.boolean().default(false).describe("Show only established sessions")
16952
17411
  },
16953
17412
  async handler(a, ctx) {
16954
17413
  ctx.info("Listing BGP sessions");
@@ -16969,7 +17428,7 @@ ${result}`;
16969
17428
  annotations: READ,
16970
17429
  description: "Lists prefixes currently being advertised to BGP peers (`/routing bgp advertisements print`) after output " + "filters are applied \u2014 use this to verify exactly what routes this router is sending to a given peer. " + "Read-only operational data; to see session status (prefix counts, uptime) use `list_bgp_sessions`. " + "Filter by peer name substring with `peer_filter`. Returns advertisement entries per peer, or a not-found message. " + "Requires RouterOS v7 with the routing package.",
16971
17430
  inputSchema: {
16972
- peer_filter: z81.string().optional().describe("Substring match on the peer name")
17431
+ peer_filter: z82.string().optional().describe("Substring match on the peer name")
16973
17432
  },
16974
17433
  async handler(a, ctx) {
16975
17434
  ctx.info("Listing BGP advertisements");
@@ -16987,7 +17446,7 @@ ${result}`;
16987
17446
  ];
16988
17447
 
16989
17448
  // src/tools/routing-filter.ts
16990
- import { z as z82 } from "zod";
17449
+ import { z as z83 } from "zod";
16991
17450
  var UNSUPPORTED3 = "Routing filters are not available on this device (requires RouterOS v7 with the routing package).";
16992
17451
  var routingFilterTools = [
16993
17452
  defineTool({
@@ -16996,7 +17455,7 @@ var routingFilterTools = [
16996
17455
  annotations: READ,
16997
17456
  description: "Lists routing filter rules (`/routing filter rule`) \u2014 the script-expression match/action chains used as " + "BGP/OSPF input and output filters on RouterOS v7. Each rule belongs to a named chain (e.g. `bgp-in`) " + "and is written as a script-like expression such as `if (dst in 10.0.0.0/8) { set distance 30; accept }`. " + "Returns all rules with their chain, expression, comment, and enabled state; narrow by chain with `chain_filter`. " + "For structured chain-dispatch (select-rule) entries use list_routing_filter_select_rules; " + "for IPv4 packet firewall rules use list_filter_rules; for routing policy rules use list_routing_rules.",
16998
17457
  inputSchema: {
16999
- chain_filter: z82.string().optional().describe("Show only rules in this chain")
17458
+ chain_filter: z83.string().optional().describe("Show only rules in this chain")
17000
17459
  },
17001
17460
  async handler(a, ctx) {
17002
17461
  ctx.info("Listing routing filter rules");
@@ -17017,11 +17476,11 @@ ${result}`;
17017
17476
  annotations: WRITE,
17018
17477
  description: "Adds a routing filter rule to a named chain (`/routing filter rule add`) \u2014 defines the match/action " + "script expressions that BGP/OSPF consult as input/output filters on RouterOS v7. " + '`rule` is the full script expression, e.g. `"if (dst-len <= 24) { accept } else { reject }"`. ' + "Specifying a new `chain` name implicitly creates that chain; reference it from a BGP/OSPF filter setting. " + "`place_before` takes a `.id` from list_routing_filter_rules to control insertion order. " + "Returns the new rule's `.id` on success. For structured chain-dispatch rules use list_routing_filter_select_rules.",
17019
17478
  inputSchema: {
17020
- chain: z82.string().describe("Filter chain name, e.g. 'bgp-in'"),
17021
- rule: z82.string().describe('Match/action expression, e.g. "if (dst-len <= 24) { accept } else { reject }"'),
17022
- comment: z82.string().optional(),
17023
- disabled: z82.boolean().default(false),
17024
- place_before: z82.string().optional().describe("Insert before this rule id to control ordering")
17479
+ chain: z83.string().describe("Filter chain name, e.g. 'bgp-in'"),
17480
+ rule: z83.string().describe('Match/action expression, e.g. "if (dst-len <= 24) { accept } else { reject }"'),
17481
+ comment: z83.string().optional(),
17482
+ disabled: z83.boolean().default(false),
17483
+ place_before: z83.string().optional().describe("Insert before this rule id to control ordering")
17025
17484
  },
17026
17485
  async handler(a, ctx) {
17027
17486
  ctx.info(`Adding routing filter rule to chain ${a.chain}`);
@@ -17041,11 +17500,11 @@ ${result}`;
17041
17500
  annotations: WRITE_IDEMPOTENT,
17042
17501
  description: "Updates an existing routing filter rule's chain, script expression, comment, or enabled state " + "(`/routing filter rule set`) on RouterOS v7. `rule_id` takes the `.id` from list_routing_filter_rules " + '(e.g. `"*3"`). Returns the updated rule\'s full detail. ' + "To toggle only enabled/disabled state use set_routing_filter_rule_enabled.",
17043
17502
  inputSchema: {
17044
- rule_id: z82.string().describe('Rule id, e.g. "*3"'),
17045
- chain: z82.string().optional(),
17046
- rule: z82.string().optional(),
17047
- comment: z82.string().optional(),
17048
- disabled: z82.boolean().optional()
17503
+ rule_id: z83.string().describe('Rule id, e.g. "*3"'),
17504
+ chain: z83.string().optional(),
17505
+ rule: z83.string().optional(),
17506
+ comment: z83.string().optional(),
17507
+ disabled: z83.boolean().optional()
17049
17508
  },
17050
17509
  async handler(a, ctx) {
17051
17510
  ctx.info(`Updating routing filter rule ${a.rule_id}`);
@@ -17078,7 +17537,7 @@ ${details}`;
17078
17537
  title: "Remove Routing Filter Rule",
17079
17538
  annotations: DESTRUCTIVE,
17080
17539
  description: "Permanently removes a routing filter rule (`/routing filter rule remove`) on RouterOS v7. " + '`rule_id` takes the `.id` from list_routing_filter_rules (e.g. `"*3"`). ' + "This is destructive \u2014 the rule cannot be recovered. " + "To deactivate without deleting use set_routing_filter_rule_enabled.",
17081
- inputSchema: { rule_id: z82.string().describe('Rule id, e.g. "*3"') },
17540
+ inputSchema: { rule_id: z83.string().describe('Rule id, e.g. "*3"') },
17082
17541
  async handler(a, ctx) {
17083
17542
  ctx.info(`Removing routing filter rule ${a.rule_id}`);
17084
17543
  const result = await executeMikrotikCommand(`/routing filter rule remove ${a.rule_id}`, ctx);
@@ -17095,8 +17554,8 @@ ${details}`;
17095
17554
  annotations: WRITE_IDEMPOTENT,
17096
17555
  description: "Enables or disables a routing filter rule (`/routing filter rule set disabled=`) on RouterOS v7 " + 'without removing it. `rule_id` takes the `.id` from list_routing_filter_rules (e.g. `"*3"`). ' + "Set `enabled=true` to activate or `enabled=false` to deactivate. " + "To permanently delete a rule use remove_routing_filter_rule; " + "to modify the rule's chain or expression use update_routing_filter_rule.",
17097
17556
  inputSchema: {
17098
- rule_id: z82.string().describe('Rule id, e.g. "*3"'),
17099
- enabled: z82.boolean()
17557
+ rule_id: z83.string().describe('Rule id, e.g. "*3"'),
17558
+ enabled: z83.boolean()
17100
17559
  },
17101
17560
  async handler(a, ctx) {
17102
17561
  ctx.info(`Setting routing filter rule ${a.rule_id} enabled=${a.enabled}`);
@@ -17129,7 +17588,7 @@ ${result}`;
17129
17588
  annotations: READ,
17130
17589
  description: "Lists routing filter num-lists (`/routing filter num-list`) \u2014 named sets of numeric ranges " + "(AS numbers, BGP communities, prefix lengths) that routing filter rules can match against by name on RouterOS v7. " + "Optionally filter to a specific named list with `list_filter`. " + "Returns each entry's list name, range, and comment. " + "To add entries use add_routing_filter_num_list; to view the rules that reference these lists use list_routing_filter_rules.",
17131
17590
  inputSchema: {
17132
- list_filter: z82.string().optional().describe("Show only entries of this named list")
17591
+ list_filter: z83.string().optional().describe("Show only entries of this named list")
17133
17592
  },
17134
17593
  async handler(a, ctx) {
17135
17594
  ctx.info("Listing routing filter num-lists");
@@ -17150,9 +17609,9 @@ ${result}`;
17150
17609
  annotations: WRITE,
17151
17610
  description: "Adds a numeric range entry to a named num-list (`/routing filter num-list add`) on RouterOS v7. " + "Num-lists hold AS numbers, BGP communities, or prefix lengths that routing filter rules match by list name. " + "`list` is the target num-list name (created implicitly if new); " + '`range` is a single value or hyphen-separated range (e.g. `"65000-65010"` or `"100"`). ' + "Returns the new entry's `.id`. " + "To view existing entries use list_routing_filter_num_lists; to remove an entry use remove_routing_filter_num_list.",
17152
17611
  inputSchema: {
17153
- list: z82.string().describe("Num-list name to add to"),
17154
- range: z82.string().describe('Numeric range or single value, e.g. "65000-65010" or "100"'),
17155
- comment: z82.string().optional()
17612
+ list: z83.string().describe("Num-list name to add to"),
17613
+ range: z83.string().describe('Numeric range or single value, e.g. "65000-65010" or "100"'),
17614
+ comment: z83.string().optional()
17156
17615
  },
17157
17616
  async handler(a, ctx) {
17158
17617
  ctx.info(`Adding num-list entry to ${a.list}`);
@@ -17171,7 +17630,7 @@ ${result}`;
17171
17630
  title: "Remove Routing Filter Num-List Entry",
17172
17631
  annotations: DESTRUCTIVE,
17173
17632
  description: "Permanently removes a numeric range entry from a num-list (`/routing filter num-list remove`) on RouterOS v7. " + '`entry_id` takes the `.id` from list_routing_filter_num_lists (e.g. `"*3"`). ' + "Removing an entry affects any routing filter rule that matches against the parent list by name. " + "To add entries use add_routing_filter_num_list.",
17174
- inputSchema: { entry_id: z82.string().describe('Entry id, e.g. "*3"') },
17633
+ inputSchema: { entry_id: z83.string().describe('Entry id, e.g. "*3"') },
17175
17634
  async handler(a, ctx) {
17176
17635
  ctx.info(`Removing num-list entry ${a.entry_id}`);
17177
17636
  const result = await executeMikrotikCommand(`/routing filter num-list remove ${a.entry_id}`, ctx);
@@ -17185,7 +17644,7 @@ ${result}`;
17185
17644
  ];
17186
17645
 
17187
17646
  // src/tools/routing-gmp.ts
17188
- import { z as z83 } from "zod";
17647
+ import { z as z84 } from "zod";
17189
17648
  var UNSUPPORTED4 = "GMP is not available on this device (requires RouterOS v7 with the routing/multicast package).";
17190
17649
  var routingGmpTools = [
17191
17650
  defineTool({
@@ -17209,8 +17668,8 @@ ${result}`;
17209
17668
  annotations: READ,
17210
17669
  description: "Lists GMP group membership records (`/routing gmp group`) \u2014 returns the multicast groups currently joined " + "per interface as learned from downstream IGMP (IPv4) or MLD (IPv6) reports; the source of truth for which " + "segments want which groups, used by PIM-SM and IGMP-proxy for forwarding decisions. " + "For per-interface querier role and timer state use list_gmp_interfaces. " + "Accepts optional filters: interface_filter (exact interface name) and group_filter (regex match on multicast group address). " + "Read-only; requires RouterOS v7 with the routing/multicast package.",
17211
17670
  inputSchema: {
17212
- interface_filter: z83.string().optional().describe("Show only memberships on this interface"),
17213
- group_filter: z83.string().optional().describe("Regex match on the multicast group address")
17671
+ interface_filter: z84.string().optional().describe("Show only memberships on this interface"),
17672
+ group_filter: z84.string().optional().describe("Regex match on the multicast group address")
17214
17673
  },
17215
17674
  async handler(a, ctx) {
17216
17675
  ctx.info("Listing GMP group memberships");
@@ -17230,7 +17689,7 @@ ${result}`;
17230
17689
  ];
17231
17690
 
17232
17691
  // src/tools/routing-id.ts
17233
- import { z as z84 } from "zod";
17692
+ import { z as z85 } from "zod";
17234
17693
  var UNSUPPORTED5 = "Routing Router-ID is not available on this device (requires RouterOS v7 with the routing package).";
17235
17694
  var routingIdTools = [
17236
17695
  defineTool({
@@ -17239,7 +17698,7 @@ var routingIdTools = [
17239
17698
  annotations: READ,
17240
17699
  description: "Lists all Router-ID instances (`/routing id print detail`). Router-IDs are named 32-bit identifiers " + "assigned to routing processes (OSPF, BGP) to uniquely identify the router within the routing domain; " + "each instance can carry a fixed IPv4 address or auto-derive one from an interface/loopback. " + "Use `get_routing_id` to inspect a single instance by name. " + "For IPv4 route table entries use `list_routes`; for policy routing rules use `list_routing_rules`; " + "for BGP connections that reference a router-id use `list_bgp_connections`. " + "Returns all instance names, configured IDs, dynamic-selection settings, and enabled/disabled state. " + "Requires RouterOS v7 with the routing package.",
17241
17700
  inputSchema: {
17242
- name_filter: z84.string().optional().describe("Substring match on the instance name")
17701
+ name_filter: z85.string().optional().describe("Substring match on the instance name")
17243
17702
  },
17244
17703
  async handler(a, ctx) {
17245
17704
  ctx.info("Listing routing IDs");
@@ -17258,7 +17717,7 @@ ${result}`;
17258
17717
  annotations: READ,
17259
17718
  description: "Returns full detail for a single named Router-ID instance (`/routing id print detail where name=...`). " + "Use this to inspect the configured or dynamically-selected 32-bit router identifier for a specific OSPF/BGP process. " + "Use `list_routing_ids` to enumerate all instances and discover names. " + "For IPv4 route table entries use `get_route`; for policy routing rules use `list_routing_rules`. " + "Returns all properties (id, select-dynamic-id, disabled, comment) of the named instance, " + "or a not-found message if the name does not exist.",
17260
17719
  inputSchema: {
17261
- name: z84.string().describe("Router-ID instance name")
17720
+ name: z85.string().describe("Router-ID instance name")
17262
17721
  },
17263
17722
  async handler(a, ctx) {
17264
17723
  ctx.info(`Getting routing ID: ${a.name}`);
@@ -17276,11 +17735,11 @@ ${result}`;
17276
17735
  annotations: WRITE,
17277
17736
  description: "Creates a new Router-ID instance (`/routing id add`) that assigns a stable 32-bit router identifier " + "to a routing process (OSPF, BGP). " + "Either pin a fixed `id` (an IPv4 address, e.g. '10.0.0.1') or let RouterOS auto-derive it from " + "an interface/loopback via `select_dynamic_id` \u2014 at least one of the two should be set. " + "For BGP connections that consume a router-id use `add_bgp_connection`; " + "to modify an existing instance use `update_routing_id`; " + "for IPv4 static routes use `add_route`; for IPv6 static routes use `add_ipv6_route`. " + "Requires RouterOS v7 with the routing package. " + "Returns the created instance's full detail.",
17278
17737
  inputSchema: {
17279
- name: z84.string().describe("Unique instance name, referenced by OSPF/BGP"),
17280
- id: z84.string().optional().describe("Fixed router-id as an IPv4 address, e.g. '10.0.0.1'"),
17281
- select_dynamic_id: z84.string().optional().describe("Interface/loopback name to auto-derive the id from when `id` is omitted"),
17282
- comment: z84.string().optional(),
17283
- disabled: z84.boolean().default(false)
17738
+ name: z85.string().describe("Unique instance name, referenced by OSPF/BGP"),
17739
+ id: z85.string().optional().describe("Fixed router-id as an IPv4 address, e.g. '10.0.0.1'"),
17740
+ select_dynamic_id: z85.string().optional().describe("Interface/loopback name to auto-derive the id from when `id` is omitted"),
17741
+ comment: z85.string().optional(),
17742
+ disabled: z85.boolean().default(false)
17284
17743
  },
17285
17744
  async handler(a, ctx) {
17286
17745
  ctx.info(`Adding routing ID: ${a.name}`);
@@ -17302,11 +17761,11 @@ ${details}`;
17302
17761
  annotations: WRITE_IDEMPOTENT,
17303
17762
  description: "Modifies an existing Router-ID instance (`/routing id set [find name=...]`). " + "Supply any combination of fields to change; omitted fields are left untouched. " + 'Pass `""` for `id` to clear the fixed router-id (RouterOS unsets it with `!id`); ' + 'pass `""` for `select_dynamic_id` to clear dynamic interface selection (`!select-dynamic-id`). ' + "Use `add_routing_id` to create a new instance; " + "use `set_routing_id_enabled` to toggle enabled state without changing other properties. " + "For updating IPv4 routes use `update_route`; for policy routing rules use `update_routing_rule`. " + "Returns the updated instance's full detail.",
17304
17763
  inputSchema: {
17305
- name: z84.string().describe("Existing Router-ID instance name"),
17306
- id: z84.string().optional().describe('IPv4 router-id, or "" to clear'),
17307
- select_dynamic_id: z84.string().optional().describe('Interface/loopback name, or "" to clear'),
17308
- comment: z84.string().optional(),
17309
- disabled: z84.boolean().optional()
17764
+ name: z85.string().describe("Existing Router-ID instance name"),
17765
+ id: z85.string().optional().describe('IPv4 router-id, or "" to clear'),
17766
+ select_dynamic_id: z85.string().optional().describe('Interface/loopback name, or "" to clear'),
17767
+ comment: z85.string().optional(),
17768
+ disabled: z85.boolean().optional()
17310
17769
  },
17311
17770
  async handler(a, ctx) {
17312
17771
  ctx.info(`Updating routing ID: ${a.name}`);
@@ -17341,7 +17800,7 @@ ${details}`;
17341
17800
  annotations: DESTRUCTIVE,
17342
17801
  description: "Permanently deletes a named Router-ID instance (`/routing id remove [find name=...]`). " + "Removing a router-id that is actively referenced by an OSPF or BGP process will break that " + "process's identifier \u2014 verify dependencies before removing. " + "Use `list_routing_ids` to discover instance names. " + "To disable without deleting use `set_routing_id_enabled`. " + "For removing IPv4 routes use `remove_route`; for IPv6 routes use `remove_ipv6_route`. " + "Returns a success confirmation or an error message.",
17343
17802
  inputSchema: {
17344
- name: z84.string().describe("Router-ID instance name to remove")
17803
+ name: z85.string().describe("Router-ID instance name to remove")
17345
17804
  },
17346
17805
  async handler(a, ctx) {
17347
17806
  ctx.info(`Removing routing ID: ${a.name}`);
@@ -17359,8 +17818,8 @@ ${details}`;
17359
17818
  annotations: WRITE_IDEMPOTENT,
17360
17819
  description: "Enables or disables a named Router-ID instance (`/routing id set [find name=...] disabled=yes/no`). " + "Disabling prevents the instance from being advertised or used by OSPF/BGP without removing it. " + "Use `add_routing_id` to create a new instance; " + "use `update_routing_id` to change the ID value or other properties; " + "use `remove_routing_id` for permanent deletion. " + "Set `enabled=true` to enable, `enabled=false` to disable. " + "Returns a confirmation string.",
17361
17820
  inputSchema: {
17362
- name: z84.string().describe("Router-ID instance name"),
17363
- enabled: z84.boolean().describe("true to enable, false to disable")
17821
+ name: z85.string().describe("Router-ID instance name"),
17822
+ enabled: z85.boolean().describe("true to enable, false to disable")
17364
17823
  },
17365
17824
  async handler(a, ctx) {
17366
17825
  ctx.info(`Setting routing ID ${a.name} enabled=${a.enabled}`);
@@ -17375,7 +17834,7 @@ ${details}`;
17375
17834
  ];
17376
17835
 
17377
17836
  // src/tools/routing-igmp-proxy.ts
17378
- import { z as z85 } from "zod";
17837
+ import { z as z86 } from "zod";
17379
17838
  var UNSUPPORTED6 = "IGMP proxy is not available on this device (requires RouterOS v7 with the routing/multicast package).";
17380
17839
  var routingIgmpProxyTools = [
17381
17840
  defineTool({
@@ -17399,9 +17858,9 @@ ${result}`;
17399
17858
  annotations: WRITE_IDEMPOTENT,
17400
17859
  description: "Update global IGMP proxy settings (`/routing igmp-proxy set`). Tunes IPv4 multicast membership refresh " + "timing and fast-leave behaviour for the proxy daemon. For interface-level changes use " + "`update_igmp_proxy_interface`. `quick_leave` prunes a multicast group immediately on member leave \u2014 " + 'recommended for IPTV channel zapping; `query_interval` e.g. "125s"; `query_response_interval` e.g. ' + '"10s". Returns updated settings on success.',
17401
17860
  inputSchema: {
17402
- quick_leave: z85.boolean().optional(),
17403
- query_interval: z85.string().optional().describe('e.g. "125s"'),
17404
- query_response_interval: z85.string().optional().describe('e.g. "10s"')
17861
+ quick_leave: z86.boolean().optional(),
17862
+ query_interval: z86.string().optional().describe('e.g. "125s"'),
17863
+ query_response_interval: z86.string().optional().describe('e.g. "10s"')
17405
17864
  },
17406
17865
  async handler(a, ctx) {
17407
17866
  ctx.info("Updating IGMP proxy settings");
@@ -17443,12 +17902,12 @@ ${result}`;
17443
17902
  annotations: WRITE,
17444
17903
  description: "Add an interface to the IGMP proxy configuration (`/routing igmp-proxy interface add`). Registers a " + "network interface as either upstream (`upstream=true`, toward the IPv4 multicast source) or downstream " + "(toward receivers). Only one upstream interface is permitted per proxy instance. " + "`alternative_subnets` whitelists extra source subnets reachable through this interface (e.g. " + '"10.0.0.0/8"); `threshold` sets the minimum TTL required to forward. For updating an existing entry ' + "use `update_igmp_proxy_interface`; to view current entries use `list_igmp_proxy_interfaces`.",
17445
17904
  inputSchema: {
17446
- interface: z85.string().describe("Interface name"),
17447
- upstream: z85.boolean().default(false).describe("true = upstream (toward source), false = downstream"),
17448
- alternative_subnets: z85.string().optional().describe('Comma list of source subnets, e.g. "10.0.0.0/8"'),
17449
- threshold: z85.number().int().optional().describe("Minimum TTL to forward"),
17450
- comment: z85.string().optional(),
17451
- disabled: z85.boolean().default(false)
17905
+ interface: z86.string().describe("Interface name"),
17906
+ upstream: z86.boolean().default(false).describe("true = upstream (toward source), false = downstream"),
17907
+ alternative_subnets: z86.string().optional().describe('Comma list of source subnets, e.g. "10.0.0.0/8"'),
17908
+ threshold: z86.number().int().optional().describe("Minimum TTL to forward"),
17909
+ comment: z86.string().optional(),
17910
+ disabled: z86.boolean().default(false)
17452
17911
  },
17453
17912
  async handler(a, ctx) {
17454
17913
  ctx.info(`Adding IGMP proxy interface ${a.interface}`);
@@ -17467,12 +17926,12 @@ ${result}`;
17467
17926
  annotations: WRITE_IDEMPOTENT,
17468
17927
  description: "Update an existing IGMP proxy interface entry by name (`/routing igmp-proxy interface set " + "[find interface=...]`). Modifies the upstream/downstream role, alternative subnets, TTL threshold, " + "comment, or disabled state of the named interface. For adding a new interface use " + "`add_igmp_proxy_interface`; to toggle only the enabled state use `set_igmp_proxy_interface_enabled`. " + "Returns updated interface detail on success.",
17469
17928
  inputSchema: {
17470
- interface: z85.string().describe("Existing IGMP-proxy interface name"),
17471
- upstream: z85.boolean().optional(),
17472
- alternative_subnets: z85.string().optional(),
17473
- threshold: z85.number().int().optional(),
17474
- comment: z85.string().optional(),
17475
- disabled: z85.boolean().optional()
17929
+ interface: z86.string().describe("Existing IGMP-proxy interface name"),
17930
+ upstream: z86.boolean().optional(),
17931
+ alternative_subnets: z86.string().optional(),
17932
+ threshold: z86.number().int().optional(),
17933
+ comment: z86.string().optional(),
17934
+ disabled: z86.boolean().optional()
17476
17935
  },
17477
17936
  async handler(a, ctx) {
17478
17937
  ctx.info(`Updating IGMP proxy interface ${a.interface}`);
@@ -17504,7 +17963,7 @@ ${details}`;
17504
17963
  annotations: DESTRUCTIVE,
17505
17964
  description: "Remove an IGMP proxy interface entry by name (`/routing igmp-proxy interface remove " + "[find interface=...]`). Permanently deletes the interface from the proxy configuration; multicast " + "groups that relied on it will stop being forwarded. For disabling without removal use " + "`set_igmp_proxy_interface_enabled`. Provide the interface name as it appears in " + "`list_igmp_proxy_interfaces`.",
17506
17965
  inputSchema: {
17507
- interface: z85.string().describe("IGMP-proxy interface name to remove")
17966
+ interface: z86.string().describe("IGMP-proxy interface name to remove")
17508
17967
  },
17509
17968
  async handler(a, ctx) {
17510
17969
  ctx.info(`Removing IGMP proxy interface ${a.interface}`);
@@ -17522,8 +17981,8 @@ ${details}`;
17522
17981
  annotations: WRITE_IDEMPOTENT,
17523
17982
  description: "Enable or disable a single IGMP proxy interface by name (`/routing igmp-proxy interface set " + "[find interface=...] disabled=yes/no`). Pauses multicast forwarding through the interface without " + "removing its configuration. For full property changes use `update_igmp_proxy_interface`; to " + "permanently remove the entry use `remove_igmp_proxy_interface`. Provide the interface name as it " + "appears in `list_igmp_proxy_interfaces`.",
17524
17983
  inputSchema: {
17525
- interface: z85.string().describe("IGMP-proxy interface name"),
17526
- enabled: z85.boolean()
17984
+ interface: z86.string().describe("IGMP-proxy interface name"),
17985
+ enabled: z86.boolean()
17527
17986
  },
17528
17987
  async handler(a, ctx) {
17529
17988
  ctx.info(`Setting IGMP proxy interface ${a.interface} enabled=${a.enabled}`);
@@ -17553,7 +18012,7 @@ ${result}`;
17553
18012
  ];
17554
18013
 
17555
18014
  // src/tools/routing-nexthop.ts
17556
- import { z as z86 } from "zod";
18015
+ import { z as z87 } from "zod";
17557
18016
  var UNSUPPORTED7 = "Routing next-hops are not available on this device (requires RouterOS v7 with the routing package).";
17558
18017
  var routingNexthopTools = [
17559
18018
  defineTool({
@@ -17562,8 +18021,8 @@ var routingNexthopTools = [
17562
18021
  annotations: READ,
17563
18022
  description: "List resolved routing next-hops (`/routing nexthop`) \u2014 the recursive next-hop resolution table that shows " + "how each gateway maps to a concrete egress interface and immediate next-hop address, " + "and whether it is currently active. Use this to debug recursive or BGP next-hop resolution failures where " + "a route exists but traffic is not forwarded as expected. " + "For the IPv4 route table use list_routes; for IPv6 routes use list_ipv6_routes; " + "for routing policy rules use list_routing_rules; for next-hop counts only use get_routing_nexthop_stats. " + "Requires RouterOS v7 with the routing package. " + "Returns full detail for each next-hop entry. " + "Filter with gateway_filter (substring match on gateway address) and active_only=true to restrict to active entries.",
17564
18023
  inputSchema: {
17565
- gateway_filter: z86.string().optional().describe("Substring match on the resolved gateway address"),
17566
- active_only: z86.boolean().default(false)
18024
+ gateway_filter: z87.string().optional().describe("Substring match on the resolved gateway address"),
18025
+ active_only: z87.boolean().default(false)
17567
18026
  },
17568
18027
  async handler(a, ctx) {
17569
18028
  ctx.info("Listing routing next-hops");
@@ -17600,7 +18059,7 @@ Active next-hops: ${active2.trim()}`;
17600
18059
  ];
17601
18060
 
17602
18061
  // src/tools/routing-ospf.ts
17603
- import { z as z87 } from "zod";
18062
+ import { z as z88 } from "zod";
17604
18063
  var UNSUPPORTED8 = "OSPF is not available on this device (requires RouterOS v7 with the routing package).";
17605
18064
  var routingOspfTools = [
17606
18065
  defineTool({
@@ -17624,21 +18083,21 @@ ${result}`;
17624
18083
  annotations: WRITE,
17625
18084
  description: "Create an OSPF instance (`/routing ospf instance add`) \u2014 the top-level OSPF process. " + "`version` 2 = OSPFv2 (IPv4 routing), 3 = OSPFv3 (IPv6 routing); each address family needs its own instance. " + "`router_id` may be an IPv4 address, 'main', or the name of a `/routing id`. " + "`redistribute` is a comma-separated list (connected,static,rip,bgp,\u2026). " + "Requires RouterOS v7 with the routing package. " + "After creating an instance, add areas with add_ospf_area and bind interfaces with add_ospf_interface_template. " + "Returns the created instance's full detail.",
17626
18085
  inputSchema: {
17627
- name: z87.string().describe("Unique instance name"),
17628
- version: z87.number().int().min(2).max(3).default(2).describe("2 = OSPFv2, 3 = OSPFv3"),
17629
- router_id: z87.string().optional().describe("IPv4 address, 'main', or a /routing id name"),
17630
- vrf: z87.string().optional(),
17631
- redistribute: z87.string().optional().describe('Comma list, e.g. "connected,static"'),
17632
- in_filter_chain: z87.string().optional().describe("Routing filter chain for received routes"),
17633
- out_filter_chain: z87.string().optional().describe("Routing filter chain for redistributed routes"),
17634
- originate_default: z87.string().optional().describe("never, if-installed, or always"),
17635
- domain_id: z87.string().optional().describe("OSPF domain ID (MPLS VPN PE-CE OSPF)"),
17636
- domain_tag: z87.number().int().optional().describe("OSPF domain tag (MPLS VPN PE-CE OSPF)"),
17637
- use_dn: z87.boolean().optional().describe("Use/ignore DN bit on OSPF routes (MPLS VPN)"),
17638
- mpls_te_area: z87.string().optional().describe("Area used for MPLS traffic engineering"),
17639
- mpls_te_address: z87.string().optional().describe("Router address advertised for MPLS TE"),
17640
- comment: z87.string().optional(),
17641
- disabled: z87.boolean().default(false)
18086
+ name: z88.string().describe("Unique instance name"),
18087
+ version: z88.number().int().min(2).max(3).default(2).describe("2 = OSPFv2, 3 = OSPFv3"),
18088
+ router_id: z88.string().optional().describe("IPv4 address, 'main', or a /routing id name"),
18089
+ vrf: z88.string().optional(),
18090
+ redistribute: z88.string().optional().describe('Comma list, e.g. "connected,static"'),
18091
+ in_filter_chain: z88.string().optional().describe("Routing filter chain for received routes"),
18092
+ out_filter_chain: z88.string().optional().describe("Routing filter chain for redistributed routes"),
18093
+ originate_default: z88.string().optional().describe("never, if-installed, or always"),
18094
+ domain_id: z88.string().optional().describe("OSPF domain ID (MPLS VPN PE-CE OSPF)"),
18095
+ domain_tag: z88.number().int().optional().describe("OSPF domain tag (MPLS VPN PE-CE OSPF)"),
18096
+ use_dn: z88.boolean().optional().describe("Use/ignore DN bit on OSPF routes (MPLS VPN)"),
18097
+ mpls_te_area: z88.string().optional().describe("Area used for MPLS traffic engineering"),
18098
+ mpls_te_address: z88.string().optional().describe("Router address advertised for MPLS TE"),
18099
+ comment: z88.string().optional(),
18100
+ disabled: z88.boolean().default(false)
17642
18101
  },
17643
18102
  async handler(a, ctx) {
17644
18103
  ctx.info(`Adding OSPF instance: ${a.name}`);
@@ -17663,19 +18122,19 @@ ${details}`;
17663
18122
  annotations: WRITE_IDEMPOTENT,
17664
18123
  description: "Update an existing OSPF instance (`/routing ospf instance set`) by name \u2014 change router-id, " + "redistribution list, import/export filter chains, originate-default policy, comment, or disabled state. " + "Only supplied fields are modified; omitting a field leaves it unchanged. " + "`name` identifies the target instance (use list_ospf_instances to find names). " + "Returns the updated instance detail. To remove an instance entirely use remove_ospf_instance.",
17665
18124
  inputSchema: {
17666
- name: z87.string().describe("Existing OSPF instance name"),
17667
- router_id: z87.string().optional(),
17668
- redistribute: z87.string().optional(),
17669
- in_filter_chain: z87.string().optional(),
17670
- out_filter_chain: z87.string().optional(),
17671
- originate_default: z87.string().optional(),
17672
- domain_id: z87.string().optional().describe("OSPF domain ID (MPLS VPN PE-CE OSPF)"),
17673
- domain_tag: z87.number().int().optional().describe("OSPF domain tag (MPLS VPN PE-CE OSPF)"),
17674
- use_dn: z87.boolean().optional().describe("Use/ignore DN bit on OSPF routes (MPLS VPN)"),
17675
- mpls_te_area: z87.string().optional().describe("Area used for MPLS traffic engineering"),
17676
- mpls_te_address: z87.string().optional().describe("Router address advertised for MPLS TE"),
17677
- comment: z87.string().optional(),
17678
- disabled: z87.boolean().optional()
18125
+ name: z88.string().describe("Existing OSPF instance name"),
18126
+ router_id: z88.string().optional(),
18127
+ redistribute: z88.string().optional(),
18128
+ in_filter_chain: z88.string().optional(),
18129
+ out_filter_chain: z88.string().optional(),
18130
+ originate_default: z88.string().optional(),
18131
+ domain_id: z88.string().optional().describe("OSPF domain ID (MPLS VPN PE-CE OSPF)"),
18132
+ domain_tag: z88.number().int().optional().describe("OSPF domain tag (MPLS VPN PE-CE OSPF)"),
18133
+ use_dn: z88.boolean().optional().describe("Use/ignore DN bit on OSPF routes (MPLS VPN)"),
18134
+ mpls_te_area: z88.string().optional().describe("Area used for MPLS traffic engineering"),
18135
+ mpls_te_address: z88.string().optional().describe("Router address advertised for MPLS TE"),
18136
+ comment: z88.string().optional(),
18137
+ disabled: z88.boolean().optional()
17679
18138
  },
17680
18139
  async handler(a, ctx) {
17681
18140
  ctx.info(`Updating OSPF instance: ${a.name}`);
@@ -17706,7 +18165,7 @@ ${details}`;
17706
18165
  title: "Remove OSPF Instance",
17707
18166
  annotations: DESTRUCTIVE,
17708
18167
  description: "Remove an OSPF instance by name (`/routing ospf instance remove`) \u2014 permanently stops the named OSPF process. " + "Use list_ospf_instances to find the instance name. " + "Remove dependent areas (remove_ospf_area), area ranges (remove_ospf_area_range), and interface templates " + "(remove_ospf_interface_template) first to avoid orphaned config or removal errors. " + "Irreversible \u2014 all OSPF adjacencies for that process will drop immediately. " + "For a non-destructive pause use update_ospf_instance with disabled=true.",
17709
- inputSchema: { name: z87.string().describe("OSPF instance name to remove") },
18168
+ inputSchema: { name: z88.string().describe("OSPF instance name to remove") },
17710
18169
  async handler(a, ctx) {
17711
18170
  ctx.info(`Removing OSPF instance: ${a.name}`);
17712
18171
  const result = await executeMikrotikCommand(`/routing ospf instance remove [find name="${a.name}"]`, ctx);
@@ -17738,14 +18197,14 @@ ${result}`;
17738
18197
  annotations: WRITE,
17739
18198
  description: "Create an OSPF area (`/routing ospf area add`) within a named instance \u2014 area-id must be in IPv4 " + "dotted notation (backbone = '0.0.0.0'). `type` controls LSA flooding: default (normal), stub (no external " + "LSAs), or nssa (allow external redistribution via NSSA LSAs). `no_summaries=true` makes a totally-stubby " + "or totally-NSSA area by blocking summary LSAs. " + "Requires the instance to already exist (add_ospf_instance). " + "After adding an area, bind interfaces to it with add_ospf_interface_template. " + "Returns a success confirmation.",
17740
18199
  inputSchema: {
17741
- name: z87.string().describe("Unique area name"),
17742
- area_id: z87.string().describe('Area id in IPv4 notation, e.g. "0.0.0.0" for backbone'),
17743
- instance: z87.string().describe("OSPF instance name this area belongs to"),
17744
- type: z87.enum(["default", "stub", "nssa"]).optional(),
17745
- no_summaries: z87.boolean().optional().describe("Make a totally-stubby/NSSA area (block summary LSAs)"),
17746
- default_cost: z87.number().int().optional().describe("Cost of the default route an ABR injects into a stub/NSSA area"),
17747
- comment: z87.string().optional(),
17748
- disabled: z87.boolean().default(false)
18200
+ name: z88.string().describe("Unique area name"),
18201
+ area_id: z88.string().describe('Area id in IPv4 notation, e.g. "0.0.0.0" for backbone'),
18202
+ instance: z88.string().describe("OSPF instance name this area belongs to"),
18203
+ type: z88.enum(["default", "stub", "nssa"]).optional(),
18204
+ no_summaries: z88.boolean().optional().describe("Make a totally-stubby/NSSA area (block summary LSAs)"),
18205
+ default_cost: z88.number().int().optional().describe("Cost of the default route an ABR injects into a stub/NSSA area"),
18206
+ comment: z88.string().optional(),
18207
+ disabled: z88.boolean().default(false)
17749
18208
  },
17750
18209
  async handler(a, ctx) {
17751
18210
  ctx.info(`Adding OSPF area: ${a.name}`);
@@ -17766,7 +18225,7 @@ ${result}`;
17766
18225
  title: "Remove OSPF Area",
17767
18226
  annotations: DESTRUCTIVE,
17768
18227
  description: "Remove an OSPF area by name (`/routing ospf area remove`) \u2014 permanently deletes the area and its " + "association with the instance. Use list_ospf_areas to find the area name. " + "Remove any area ranges (remove_ospf_area_range) and interface templates (remove_ospf_interface_template) " + "that reference this area first to avoid orphaned config or removal errors.",
17769
- inputSchema: { name: z87.string().describe("OSPF area name to remove") },
18228
+ inputSchema: { name: z88.string().describe("OSPF area name to remove") },
17770
18229
  async handler(a, ctx) {
17771
18230
  ctx.info(`Removing OSPF area: ${a.name}`);
17772
18231
  const result = await executeMikrotikCommand(`/routing ospf area remove [find name="${a.name}"]`, ctx);
@@ -17798,12 +18257,12 @@ ${result}`;
17798
18257
  annotations: WRITE,
17799
18258
  description: "Add an OSPF area summarisation range (`/routing ospf area range add`) to a named area \u2014 the ABR will " + "aggregate matching intra-area prefixes into a single Type-3 LSA advertised to other areas. " + "`advertise=false` suppresses the summary entirely (null-routes the aggregate). " + "`cost` overrides the auto-computed metric. Area must already exist (add_ospf_area). " + "`prefix` is the aggregate prefix, e.g. '10.10.0.0/16'. " + "Use list_ospf_area_ranges to verify after creation. Returns a success confirmation.",
17800
18259
  inputSchema: {
17801
- area: z87.string().describe("OSPF area name"),
17802
- prefix: z87.string().describe('Aggregate prefix, e.g. "10.10.0.0/16"'),
17803
- advertise: z87.boolean().default(true).describe("Advertise the summary (false suppresses it)"),
17804
- cost: z87.number().int().optional(),
17805
- comment: z87.string().optional(),
17806
- disabled: z87.boolean().optional()
18260
+ area: z88.string().describe("OSPF area name"),
18261
+ prefix: z88.string().describe('Aggregate prefix, e.g. "10.10.0.0/16"'),
18262
+ advertise: z88.boolean().default(true).describe("Advertise the summary (false suppresses it)"),
18263
+ cost: z88.number().int().optional(),
18264
+ comment: z88.string().optional(),
18265
+ disabled: z88.boolean().optional()
17807
18266
  },
17808
18267
  async handler(a, ctx) {
17809
18268
  ctx.info(`Adding OSPF area range ${a.prefix} to ${a.area}`);
@@ -17823,7 +18282,7 @@ ${result}`;
17823
18282
  title: "Remove OSPF Area Range",
17824
18283
  annotations: DESTRUCTIVE,
17825
18284
  description: "Remove an OSPF area summarisation range (`/routing ospf area range remove`) by its `.id` \u2014 " + "use list_ospf_area_ranges to obtain the id (e.g. '*1'). " + "The ABR will revert to advertising individual intra-area prefixes instead of the aggregate.",
17826
- inputSchema: { range_id: z87.string().describe('Range id, e.g. "*1"') },
18285
+ inputSchema: { range_id: z88.string().describe('Range id, e.g. "*1"') },
17827
18286
  async handler(a, ctx) {
17828
18287
  ctx.info(`Removing OSPF area range ${a.range_id}`);
17829
18288
  const result = await executeMikrotikCommand(`/routing ospf area range remove ${a.range_id}`, ctx);
@@ -17855,26 +18314,26 @@ ${result}`;
17855
18314
  annotations: WRITE,
17856
18315
  description: "Bind interfaces or network prefixes to an OSPF area (`/routing ospf interface-template add`). " + "Match links via `interfaces` (interface or interface-list name) and/or `networks` (prefix, " + "e.g. '10.0.0.0/24'). `type` sets the link model: broadcast (LAN), ptp (point-to-point), " + "nbma, ptmp, or virtual-link. `passive=true` advertises the subnet without forming OSPF adjacencies " + "(for stub networks). `auth`/`auth_id`/`auth_key` enable per-interface authentication; `hello_interval` " + "e.g. '10s', `dead_interval` e.g. '40s'. " + "Requires the area to already exist (add_ospf_area). " + "Returns the new template id (e.g. '*2') if RouterOS echoes one; use list_ospf_interface_templates to verify.",
17857
18316
  inputSchema: {
17858
- area: z87.string().describe("OSPF area name to attach matched interfaces to"),
17859
- interfaces: z87.string().optional().describe("Interface or interface-list name"),
17860
- networks: z87.string().optional().describe('Network prefix(es) to enable OSPF on, e.g. "10.0.0.0/24"'),
17861
- cost: z87.number().int().optional().describe("Output cost / metric"),
17862
- priority: z87.number().int().optional().describe("DR election priority (0 = never DR)"),
17863
- type: z87.enum(["broadcast", "ptp", "nbma", "ptmp", "virtual-link"]).optional(),
17864
- passive: z87.boolean().optional(),
17865
- hello_interval: z87.string().optional().describe('e.g. "10s"'),
17866
- dead_interval: z87.string().optional().describe('e.g. "40s"'),
17867
- retransmit_interval: z87.string().optional().describe('Interval between LSA retransmissions, e.g. "5s"'),
17868
- transmit_delay: z87.string().optional().describe('Estimated LSA transmit delay, e.g. "1s"'),
17869
- instance_id: z87.number().int().optional().describe("OSPF instance-id carried in hello packets"),
17870
- prefix_list: z87.string().optional().describe("Prefix-list name filtering which connected networks are advertised"),
17871
- vlink_neighbor_id: z87.string().optional().describe("Virtual-link remote router-id (type=virtual-link)"),
17872
- vlink_transit_area: z87.string().optional().describe("Transit area the virtual-link traverses (type=virtual-link)"),
17873
- auth: z87.enum(["simple", "md5", "sha1", "sha256", "sha384", "sha512"]).optional(),
17874
- auth_id: z87.number().int().optional().describe("Key id for keyed authentication"),
17875
- auth_key: z87.string().optional().describe("Authentication key/password"),
17876
- comment: z87.string().optional(),
17877
- disabled: z87.boolean().default(false)
18317
+ area: z88.string().describe("OSPF area name to attach matched interfaces to"),
18318
+ interfaces: z88.string().optional().describe("Interface or interface-list name"),
18319
+ networks: z88.string().optional().describe('Network prefix(es) to enable OSPF on, e.g. "10.0.0.0/24"'),
18320
+ cost: z88.number().int().optional().describe("Output cost / metric"),
18321
+ priority: z88.number().int().optional().describe("DR election priority (0 = never DR)"),
18322
+ type: z88.enum(["broadcast", "ptp", "nbma", "ptmp", "virtual-link"]).optional(),
18323
+ passive: z88.boolean().optional(),
18324
+ hello_interval: z88.string().optional().describe('e.g. "10s"'),
18325
+ dead_interval: z88.string().optional().describe('e.g. "40s"'),
18326
+ retransmit_interval: z88.string().optional().describe('Interval between LSA retransmissions, e.g. "5s"'),
18327
+ transmit_delay: z88.string().optional().describe('Estimated LSA transmit delay, e.g. "1s"'),
18328
+ instance_id: z88.number().int().optional().describe("OSPF instance-id carried in hello packets"),
18329
+ prefix_list: z88.string().optional().describe("Prefix-list name filtering which connected networks are advertised"),
18330
+ vlink_neighbor_id: z88.string().optional().describe("Virtual-link remote router-id (type=virtual-link)"),
18331
+ vlink_transit_area: z88.string().optional().describe("Transit area the virtual-link traverses (type=virtual-link)"),
18332
+ auth: z88.enum(["simple", "md5", "sha1", "sha256", "sha384", "sha512"]).optional(),
18333
+ auth_id: z88.number().int().optional().describe("Key id for keyed authentication"),
18334
+ auth_key: z88.string().optional().describe("Authentication key/password"),
18335
+ comment: z88.string().optional(),
18336
+ disabled: z88.boolean().default(false)
17878
18337
  },
17879
18338
  async handler(a, ctx) {
17880
18339
  ctx.info(`Adding OSPF interface template for area ${a.area}`);
@@ -17896,7 +18355,7 @@ ${result}`;
17896
18355
  title: "Remove OSPF Interface Template",
17897
18356
  annotations: DESTRUCTIVE,
17898
18357
  description: "Remove an OSPF interface template (`/routing ospf interface-template remove`) by its `.id` \u2014 " + "use list_ospf_interface_templates to obtain the id (e.g. '*2'). " + "Removing a template stops OSPF on all matched interfaces; existing adjacencies will drop immediately.",
17899
- inputSchema: { template_id: z87.string().describe('Template id, e.g. "*2"') },
18358
+ inputSchema: { template_id: z88.string().describe('Template id, e.g. "*2"') },
17900
18359
  async handler(a, ctx) {
17901
18360
  ctx.info(`Removing OSPF interface template ${a.template_id}`);
17902
18361
  const result = await executeMikrotikCommand(`/routing ospf interface-template remove ${a.template_id}`, ctx);
@@ -17928,7 +18387,7 @@ ${result}`;
17928
18387
  annotations: READ,
17929
18388
  description: "List the OSPF link-state database (`/routing ospf lsa`) \u2014 every LSA the router holds, by type " + "(Router/Network/Summary/ASBR-Summary/External/NSSA/\u2026), area, and originating router-id. " + "Read-only \u2014 used to inspect topology, verify summarisation (Type-3 LSAs), confirm external " + "redistribution (Type-5/Type-7), and diagnose flooding or database synchronisation problems. " + "Filter to a single area with `area_filter`. " + "For adjacency state use list_ospf_neighbors; for instance/area config use list_ospf_instances.",
17930
18389
  inputSchema: {
17931
- area_filter: z87.string().optional().describe("Show only LSAs for this area")
18390
+ area_filter: z88.string().optional().describe("Show only LSAs for this area")
17932
18391
  },
17933
18392
  async handler(a, ctx) {
17934
18393
  ctx.info("Listing OSPF LSAs");
@@ -17946,7 +18405,7 @@ ${result}`;
17946
18405
  ];
17947
18406
 
17948
18407
  // src/tools/routing-pimsm.ts
17949
- import { z as z88 } from "zod";
18408
+ import { z as z89 } from "zod";
17950
18409
  var UNSUPPORTED9 = "PIM-SM is not available on this device (requires RouterOS v7 with the routing/multicast package).";
17951
18410
  var routingPimsmTools = [
17952
18411
  defineTool({
@@ -17970,20 +18429,20 @@ ${result}`;
17970
18429
  annotations: WRITE,
17971
18430
  description: "Create a PIM-SM protocol instance (`/routing pimsm instance`). An instance is required before any " + "interface templates or static RPs can be added \u2014 it anchors the address family (ipv4/ipv6) and VRF " + "that multicast routing runs in. " + "Once created, bind interfaces with add_pimsm_interface_template and add RPs with add_pimsm_rp. " + "Returns the detail of the newly created instance including its name and settings.",
17972
18431
  inputSchema: {
17973
- name: z88.string().describe("Unique instance name"),
17974
- afi: z88.enum(["ipv4", "ipv6"]).default("ipv4").describe("Address family"),
17975
- vrf: z88.string().optional(),
17976
- rp_set: z88.string().optional().describe("Static RP-set name, if used"),
17977
- ssm_range: z88.string().optional().describe("Source-Specific Multicast group range(s) for this instance"),
17978
- rp_hash_mask_length: z88.number().int().optional().describe("RP hash mask length for group-to-RP mapping"),
17979
- rp_static_override: z88.boolean().optional().describe("Static RP entries override dynamically learned (BSR) RPs"),
17980
- switch_to_spt: z88.boolean().optional().describe("Switch from the shared (RP) tree to the shortest-path tree"),
17981
- switch_to_spt_bytes: z88.number().int().optional().describe("Traffic threshold in bytes before switching to the SPT"),
17982
- switch_to_spt_interval: z88.string().optional().describe('SPT switchover measurement interval, e.g. "1m"'),
17983
- bsm_forward_back: z88.boolean().optional().describe("Forward bootstrap (BSM) messages back out the receiving interface"),
17984
- crp_advertise_contained: z88.boolean().optional().describe("Candidate-RP advertises only contained group ranges"),
17985
- comment: z88.string().optional(),
17986
- disabled: z88.boolean().default(false)
18432
+ name: z89.string().describe("Unique instance name"),
18433
+ afi: z89.enum(["ipv4", "ipv6"]).default("ipv4").describe("Address family"),
18434
+ vrf: z89.string().optional(),
18435
+ rp_set: z89.string().optional().describe("Static RP-set name, if used"),
18436
+ ssm_range: z89.string().optional().describe("Source-Specific Multicast group range(s) for this instance"),
18437
+ rp_hash_mask_length: z89.number().int().optional().describe("RP hash mask length for group-to-RP mapping"),
18438
+ rp_static_override: z89.boolean().optional().describe("Static RP entries override dynamically learned (BSR) RPs"),
18439
+ switch_to_spt: z89.boolean().optional().describe("Switch from the shared (RP) tree to the shortest-path tree"),
18440
+ switch_to_spt_bytes: z89.number().int().optional().describe("Traffic threshold in bytes before switching to the SPT"),
18441
+ switch_to_spt_interval: z89.string().optional().describe('SPT switchover measurement interval, e.g. "1m"'),
18442
+ bsm_forward_back: z89.boolean().optional().describe("Forward bootstrap (BSM) messages back out the receiving interface"),
18443
+ crp_advertise_contained: z89.boolean().optional().describe("Candidate-RP advertises only contained group ranges"),
18444
+ comment: z89.string().optional(),
18445
+ disabled: z89.boolean().default(false)
17987
18446
  },
17988
18447
  async handler(a, ctx) {
17989
18448
  ctx.info(`Adding PIM-SM instance: ${a.name}`);
@@ -18005,7 +18464,7 @@ ${details}`;
18005
18464
  annotations: DESTRUCTIVE,
18006
18465
  description: "Remove a PIM-SM protocol instance (`/routing pimsm instance`) by name, tearing down all multicast " + "routing for that address family and VRF. " + "Use list_pimsm_instances to obtain the exact instance name. " + "To remove only an interface binding without deleting the instance use remove_pimsm_interface_template; " + "to remove only an RP entry use remove_pimsm_rp. " + "Confirms success or returns the RouterOS error on failure.",
18007
18466
  inputSchema: {
18008
- name: z88.string().describe("PIM-SM instance name to remove")
18467
+ name: z89.string().describe("PIM-SM instance name to remove")
18009
18468
  },
18010
18469
  async handler(a, ctx) {
18011
18470
  ctx.info(`Removing PIM-SM instance: ${a.name}`);
@@ -18038,18 +18497,18 @@ ${result}`;
18038
18497
  annotations: WRITE,
18039
18498
  description: "Activate PIM-SM on one or more interfaces by creating an interface template (`/routing pimsm interface-template`). " + "Binds a physical or logical interface (or interface-list) to an existing PIM-SM instance and sets " + 'DR election priority and hello interval (e.g. hello_period="30s"). ' + "Requires the parent instance to exist first \u2014 use add_pimsm_instance. " + "To configure static RPs for that instance use add_pimsm_rp instead. " + "Returns the new template's `.id` (e.g. '*1') if RouterOS echoes it back from the add command, " + "or a plain success message; use list_pimsm_interface_templates to look it up afterwards.",
18040
18499
  inputSchema: {
18041
- instance: z88.string().describe("PIM-SM instance name"),
18042
- interfaces: z88.string().describe("Interface or interface-list name"),
18043
- priority: z88.number().int().optional().describe("DR election priority"),
18044
- hello_period: z88.string().optional().describe('Hello interval, e.g. "30s"'),
18045
- hello_delay: z88.string().optional().describe('Max random delay before the first Hello, e.g. "5s"'),
18046
- join_prune_period: z88.string().optional().describe('Interval between periodic Join/Prune messages, e.g. "1m"'),
18047
- join_tracking_support: z88.boolean().optional().describe("Enable explicit Join/Prune tracking support"),
18048
- override_interval: z88.string().optional().describe('Randomized Join override interval, e.g. "2s500ms"'),
18049
- propagation_delay: z88.string().optional().describe('Expected link propagation delay, e.g. "500ms"'),
18050
- source_addresses: z88.string().optional().describe("Source IP address(es) used for PIM messages on these interfaces"),
18051
- comment: z88.string().optional(),
18052
- disabled: z88.boolean().default(false)
18500
+ instance: z89.string().describe("PIM-SM instance name"),
18501
+ interfaces: z89.string().describe("Interface or interface-list name"),
18502
+ priority: z89.number().int().optional().describe("DR election priority"),
18503
+ hello_period: z89.string().optional().describe('Hello interval, e.g. "30s"'),
18504
+ hello_delay: z89.string().optional().describe('Max random delay before the first Hello, e.g. "5s"'),
18505
+ join_prune_period: z89.string().optional().describe('Interval between periodic Join/Prune messages, e.g. "1m"'),
18506
+ join_tracking_support: z89.boolean().optional().describe("Enable explicit Join/Prune tracking support"),
18507
+ override_interval: z89.string().optional().describe('Randomized Join override interval, e.g. "2s500ms"'),
18508
+ propagation_delay: z89.string().optional().describe('Expected link propagation delay, e.g. "500ms"'),
18509
+ source_addresses: z89.string().optional().describe("Source IP address(es) used for PIM messages on these interfaces"),
18510
+ comment: z89.string().optional(),
18511
+ disabled: z89.boolean().default(false)
18053
18512
  },
18054
18513
  async handler(a, ctx) {
18055
18514
  ctx.info(`Adding PIM-SM interface template for instance ${a.instance}`);
@@ -18068,7 +18527,7 @@ ${result}`;
18068
18527
  title: "Remove PIM-SM Interface Template",
18069
18528
  annotations: DESTRUCTIVE,
18070
18529
  description: "Remove a PIM-SM interface template (`/routing pimsm interface-template`) by its `.id`, deactivating PIM " + "on the interfaces it covered without removing the parent instance. " + "The template_id (e.g. '*1') comes from list_pimsm_interface_templates. " + "To tear down the entire PIM instance use remove_pimsm_instance; " + "to remove a static RP entry use remove_pimsm_rp. " + "Confirms success or returns the RouterOS error on failure.",
18071
- inputSchema: { template_id: z88.string().describe('Template id, e.g. "*1"') },
18530
+ inputSchema: { template_id: z89.string().describe('Template id, e.g. "*1"') },
18072
18531
  async handler(a, ctx) {
18073
18532
  ctx.info(`Removing PIM-SM interface template ${a.template_id}`);
18074
18533
  const result = await executeMikrotikCommand(`/routing pimsm interface-template remove ${a.template_id}`, ctx);
@@ -18100,11 +18559,11 @@ ${result}`;
18100
18559
  annotations: WRITE,
18101
18560
  description: "Register a static Rendezvous Point (RP) for a multicast group range (`/routing pimsm rp`). Tells " + "PIM-SM routers which IP address is the root of the shared distribution tree for a given multicast " + "prefix. The group field accepts a multicast prefix e.g. '239.0.0.0/8'; omit to cover the default " + "multicast range. Requires the target instance to exist \u2014 use add_pimsm_instance first. " + "To enable PIM on interfaces (not configure an RP) use add_pimsm_interface_template. " + "Returns a success message; use list_pimsm_rps to retrieve the new entry's `.id`.",
18102
18561
  inputSchema: {
18103
- instance: z88.string().describe("PIM-SM instance name"),
18104
- address: z88.string().describe("RP IP address"),
18105
- group: z88.string().optional().describe('Multicast group range, e.g. "239.0.0.0/8"'),
18106
- comment: z88.string().optional(),
18107
- disabled: z88.boolean().default(false)
18562
+ instance: z89.string().describe("PIM-SM instance name"),
18563
+ address: z89.string().describe("RP IP address"),
18564
+ group: z89.string().optional().describe('Multicast group range, e.g. "239.0.0.0/8"'),
18565
+ comment: z89.string().optional(),
18566
+ disabled: z89.boolean().default(false)
18108
18567
  },
18109
18568
  async handler(a, ctx) {
18110
18569
  ctx.info(`Adding PIM-SM RP ${a.address}`);
@@ -18122,7 +18581,7 @@ ${result}`;
18122
18581
  title: "Remove PIM-SM Rendezvous Point",
18123
18582
  annotations: DESTRUCTIVE,
18124
18583
  description: "Remove a static PIM-SM Rendezvous Point entry (`/routing pimsm rp`) by its `.id`, stopping the router " + "from treating that address as the shared-tree root for the associated multicast group range. " + "The rp_id (e.g. '*1') comes from list_pimsm_rps. " + "To remove an interface template instead use remove_pimsm_interface_template; " + "to tear down the whole PIM instance use remove_pimsm_instance. " + "Confirms success or returns the RouterOS error on failure.",
18125
- inputSchema: { rp_id: z88.string().describe('RP id, e.g. "*1"') },
18584
+ inputSchema: { rp_id: z89.string().describe('RP id, e.g. "*1"') },
18126
18585
  async handler(a, ctx) {
18127
18586
  ctx.info(`Removing PIM-SM RP ${a.rp_id}`);
18128
18587
  const result = await executeMikrotikCommand(`/routing pimsm rp remove ${a.rp_id}`, ctx);
@@ -18151,7 +18610,7 @@ ${result}`;
18151
18610
  ];
18152
18611
 
18153
18612
  // src/tools/routing-rip.ts
18154
- import { z as z89 } from "zod";
18613
+ import { z as z90 } from "zod";
18155
18614
  var UNSUPPORTED10 = "RIP is not available on this device (requires RouterOS v7 with the routing package).";
18156
18615
  var routingRipTools = [
18157
18616
  defineTool({
@@ -18175,16 +18634,16 @@ ${result}`;
18175
18634
  annotations: WRITE,
18176
18635
  description: "Create a RIP process instance (`/routing rip instance add`) \u2014 the top-level RIPv2 process object that " + "governs route redistribution and filter chains. Required before attaching interfaces with " + 'add_rip_interface_template. `redistribute` is a comma-separated list (e.g. "connected,static,ospf,bgp"); ' + "`originate_default` accepts never, if-installed, or always; `router_id` accepts an IPv4 address, 'main', " + "or a /routing id name; filter chains reference /routing filter. For OSPF use the OSPF tools; for BGP use " + "add_bgp_connection. Returns the created instance's detail including its name. Requires RouterOS v7 with " + "the routing package.",
18177
18636
  inputSchema: {
18178
- name: z89.string().describe("Unique instance name"),
18179
- router_id: z89.string().optional().describe("IPv4 address, 'main', or a /routing id name"),
18180
- vrf: z89.string().optional(),
18181
- routing_table: z89.string().optional().describe("Routing table to install routes into"),
18182
- redistribute: z89.string().optional().describe('Comma list, e.g. "connected,static"'),
18183
- in_filter_chain: z89.string().optional(),
18184
- out_filter_chain: z89.string().optional(),
18185
- originate_default: z89.string().optional().describe("never, if-installed, or always"),
18186
- comment: z89.string().optional(),
18187
- disabled: z89.boolean().default(false)
18637
+ name: z90.string().describe("Unique instance name"),
18638
+ router_id: z90.string().optional().describe("IPv4 address, 'main', or a /routing id name"),
18639
+ vrf: z90.string().optional(),
18640
+ routing_table: z90.string().optional().describe("Routing table to install routes into"),
18641
+ redistribute: z90.string().optional().describe('Comma list, e.g. "connected,static"'),
18642
+ in_filter_chain: z90.string().optional(),
18643
+ out_filter_chain: z90.string().optional(),
18644
+ originate_default: z90.string().optional().describe("never, if-installed, or always"),
18645
+ comment: z90.string().optional(),
18646
+ disabled: z90.boolean().default(false)
18188
18647
  },
18189
18648
  async handler(a, ctx) {
18190
18649
  ctx.info(`Adding RIP instance: ${a.name}`);
@@ -18206,15 +18665,15 @@ ${details}`;
18206
18665
  annotations: WRITE_IDEMPOTENT,
18207
18666
  description: "Modify an existing RIP process instance (`/routing rip instance set`) identified by its name. Use to " + "change redistribution policy, filter chains, router-id, or enabled/disabled state without recreating the " + "instance. Obtain the name from list_rip_instances. For creating a new instance use add_rip_instance; for " + "removing one use remove_rip_instance. Returns the updated instance's detail. Requires RouterOS v7 with " + "the routing package.",
18208
18667
  inputSchema: {
18209
- name: z89.string().describe("Existing RIP instance name"),
18210
- router_id: z89.string().optional(),
18211
- routing_table: z89.string().optional().describe("Routing table to install routes into"),
18212
- redistribute: z89.string().optional(),
18213
- in_filter_chain: z89.string().optional(),
18214
- out_filter_chain: z89.string().optional(),
18215
- originate_default: z89.string().optional(),
18216
- comment: z89.string().optional(),
18217
- disabled: z89.boolean().optional()
18668
+ name: z90.string().describe("Existing RIP instance name"),
18669
+ router_id: z90.string().optional(),
18670
+ routing_table: z90.string().optional().describe("Routing table to install routes into"),
18671
+ redistribute: z90.string().optional(),
18672
+ in_filter_chain: z90.string().optional(),
18673
+ out_filter_chain: z90.string().optional(),
18674
+ originate_default: z90.string().optional(),
18675
+ comment: z90.string().optional(),
18676
+ disabled: z90.boolean().optional()
18218
18677
  },
18219
18678
  async handler(a, ctx) {
18220
18679
  ctx.info(`Updating RIP instance: ${a.name}`);
@@ -18243,7 +18702,7 @@ ${details}`;
18243
18702
  title: "Remove RIP Instance",
18244
18703
  annotations: DESTRUCTIVE,
18245
18704
  description: "Delete a RIP process instance (`/routing rip instance remove`) by its name, removing the process and all " + "associated routing state. Obtain the name from list_rip_instances. Remove associated interface templates " + "first with remove_rip_interface_template to avoid orphaned entries. To disable without deleting use " + "update_rip_instance with disabled=true. Requires RouterOS v7 with the routing package.",
18246
- inputSchema: { name: z89.string().describe("RIP instance name to remove") },
18705
+ inputSchema: { name: z90.string().describe("RIP instance name to remove") },
18247
18706
  async handler(a, ctx) {
18248
18707
  ctx.info(`Removing RIP instance: ${a.name}`);
18249
18708
  const result = await executeMikrotikCommand(`/routing rip instance remove [find name="${a.name}"]`, ctx);
@@ -18275,14 +18734,14 @@ ${result}`;
18275
18734
  annotations: WRITE,
18276
18735
  description: "Bind interfaces to a RIP instance (`/routing rip interface-template add`) so they participate in RIP " + "route advertisement. The `instance` must already exist \u2014 create it first with add_rip_instance. " + "`passive=true` suppresses outbound RIP updates while still receiving; `key_chain` names a /routing " + "key-chain for MD5 authentication. For static unicast neighbors on non-broadcast links use " + "add_rip_static_neighbor instead. Returns the new template's .id. Requires RouterOS v7 with the " + "routing package.",
18277
18736
  inputSchema: {
18278
- instance: z89.string().describe("RIP instance name"),
18279
- interfaces: z89.string().describe("Interface or interface-list name"),
18280
- passive: z89.boolean().optional(),
18281
- split_horizon: z89.boolean().optional().describe("Enable split-horizon loop prevention"),
18282
- poison_reverse: z89.boolean().optional().describe("Advertise back with infinite metric"),
18283
- key_chain: z89.string().optional().describe("Key-chain name for authentication"),
18284
- comment: z89.string().optional(),
18285
- disabled: z89.boolean().default(false)
18737
+ instance: z90.string().describe("RIP instance name"),
18738
+ interfaces: z90.string().describe("Interface or interface-list name"),
18739
+ passive: z90.boolean().optional(),
18740
+ split_horizon: z90.boolean().optional().describe("Enable split-horizon loop prevention"),
18741
+ poison_reverse: z90.boolean().optional().describe("Advertise back with infinite metric"),
18742
+ key_chain: z90.string().optional().describe("Key-chain name for authentication"),
18743
+ comment: z90.string().optional(),
18744
+ disabled: z90.boolean().default(false)
18286
18745
  },
18287
18746
  async handler(a, ctx) {
18288
18747
  ctx.info(`Adding RIP interface template for instance ${a.instance}`);
@@ -18308,7 +18767,7 @@ ${result}`;
18308
18767
  title: "Remove RIP Interface Template",
18309
18768
  annotations: DESTRUCTIVE,
18310
18769
  description: "Remove a RIP interface template (`/routing rip interface-template remove`) by its `.id`, detaching the " + "bound interfaces from the RIP instance. Obtain the `.id` from list_rip_interface_templates. To re-configure " + "instead of removing, delete and recreate with add_rip_interface_template. Requires RouterOS v7 with the " + "routing package.",
18311
- inputSchema: { template_id: z89.string().describe('Template id, e.g. "*1"') },
18770
+ inputSchema: { template_id: z90.string().describe('Template id, e.g. "*1"') },
18312
18771
  async handler(a, ctx) {
18313
18772
  ctx.info(`Removing RIP interface template ${a.template_id}`);
18314
18773
  const result = await executeMikrotikCommand(`/routing rip interface-template remove ${a.template_id}`, ctx);
@@ -18340,10 +18799,10 @@ ${result}`;
18340
18799
  annotations: WRITE,
18341
18800
  description: "Add a static RIP neighbor entry (`/routing rip static-neighbor add`) to unicast RIP updates to a specific " + "IPv4 address on non-broadcast or point-to-point links. Optionally scope to a specific RIP instance by name " + "(obtain from list_rip_instances). For multicast-reachable peers, binding an interface with " + "add_rip_interface_template is sufficient. Returns a confirmation message on success. Requires RouterOS v7 " + "with the routing package.",
18342
18801
  inputSchema: {
18343
- address: z89.string().describe("Neighbor IP address"),
18344
- instance: z89.string().optional().describe("RIP instance name"),
18345
- comment: z89.string().optional(),
18346
- disabled: z89.boolean().default(false)
18802
+ address: z90.string().describe("Neighbor IP address"),
18803
+ instance: z90.string().optional().describe("RIP instance name"),
18804
+ comment: z90.string().optional(),
18805
+ disabled: z90.boolean().default(false)
18347
18806
  },
18348
18807
  async handler(a, ctx) {
18349
18808
  ctx.info(`Adding RIP static neighbor ${a.address}`);
@@ -18362,7 +18821,7 @@ ${result}`;
18362
18821
  annotations: DESTRUCTIVE,
18363
18822
  description: "Delete a static RIP neighbor entry (`/routing rip static-neighbor remove`) by its `.id`, stopping unicast " + "RIP updates to that IPv4 peer. Obtain the `.id` from list_rip_static_neighbors. The link itself is " + "unaffected. Requires RouterOS v7 with the routing package.",
18364
18823
  inputSchema: {
18365
- neighbor_id: z89.string().describe('Static-neighbor id, e.g. "*1"')
18824
+ neighbor_id: z90.string().describe('Static-neighbor id, e.g. "*1"')
18366
18825
  },
18367
18826
  async handler(a, ctx) {
18368
18827
  ctx.info(`Removing RIP static neighbor ${a.neighbor_id}`);
@@ -18392,7 +18851,7 @@ ${result}`;
18392
18851
  ];
18393
18852
 
18394
18853
  // src/tools/routing-rpki.ts
18395
- import { z as z90 } from "zod";
18854
+ import { z as z91 } from "zod";
18396
18855
  var UNSUPPORTED11 = "RPKI is not available on this device (requires RouterOS v7 with the routing package).";
18397
18856
  var routingRpkiTools = [
18398
18857
  defineTool({
@@ -18401,7 +18860,7 @@ var routingRpkiTools = [
18401
18860
  annotations: READ,
18402
18861
  description: "Lists all RPKI RTR sessions (`/routing rpki print detail`) \u2014 each session is a connection to a validator " + "cache that streams Validated ROA Payloads (VRPs) for BGP Route Origin Validation. BGP route-policy filters " + "reference the session `group` name to mark prefixes valid/invalid/unknown. Use `add_rpki_session` to create " + "a session; for BGP peer configuration use `add_bgp_connection`. Returns connection status, group, address, " + "port, VRP counts, and refresh/expire state for each session; filtered to `group_filter` when supplied.",
18403
18862
  inputSchema: {
18404
- group_filter: z90.string().optional().describe("Show only sessions in this group")
18863
+ group_filter: z91.string().optional().describe("Show only sessions in this group")
18405
18864
  },
18406
18865
  async handler(a, ctx) {
18407
18866
  ctx.info("Listing RPKI sessions");
@@ -18422,15 +18881,15 @@ ${result}`;
18422
18881
  annotations: WRITE,
18423
18882
  description: "Adds an RPKI RTR session (`/routing rpki add`) connecting to a validator cache for BGP Route Origin " + "Validation. The `group` name is what BGP route-policy filters match against to mark prefixes " + "valid/invalid/unknown; `address`/`port` identify the RTR cache (port 8282 is the common default, 323 for " + 'RTR-over-TLS). Tune `refresh_interval`, `retry_interval`, and `expire_interval` (e.g. "10m", "30s", ' + '"2h") to control how frequently VRPs are pulled and when stale data is dropped. For BGP peer configuration ' + "use `add_bgp_connection`; to inspect existing sessions use `list_rpki_sessions`. Returns the new session `.id`.",
18424
18883
  inputSchema: {
18425
- group: z90.string().describe("RPKI group name referenced by BGP route filters"),
18426
- address: z90.string().describe("Validator (RTR cache) IP address or hostname"),
18427
- port: z90.number().int().default(8282).describe("RTR port, e.g. 8282 or 323"),
18428
- refresh_interval: z90.string().optional().describe('e.g. "10m"'),
18429
- expire_interval: z90.string().optional().describe('e.g. "2h"'),
18430
- retry_interval: z90.string().optional().describe('e.g. "30s"'),
18431
- vrf: z90.string().optional(),
18432
- comment: z90.string().optional(),
18433
- disabled: z90.boolean().default(false)
18884
+ group: z91.string().describe("RPKI group name referenced by BGP route filters"),
18885
+ address: z91.string().describe("Validator (RTR cache) IP address or hostname"),
18886
+ port: z91.number().int().default(8282).describe("RTR port, e.g. 8282 or 323"),
18887
+ refresh_interval: z91.string().optional().describe('e.g. "10m"'),
18888
+ expire_interval: z91.string().optional().describe('e.g. "2h"'),
18889
+ retry_interval: z91.string().optional().describe('e.g. "30s"'),
18890
+ vrf: z91.string().optional(),
18891
+ comment: z91.string().optional(),
18892
+ disabled: z91.boolean().default(false)
18434
18893
  },
18435
18894
  async handler(a, ctx) {
18436
18895
  ctx.info(`Adding RPKI session group=${a.group} -> ${a.address}:${a.port}`);
@@ -18450,14 +18909,14 @@ ${result}`;
18450
18909
  annotations: WRITE_IDEMPOTENT,
18451
18910
  description: "Modifies an existing RPKI RTR session (`/routing rpki set`) by its `.id`. Use `list_rpki_sessions` to " + 'obtain the `session_id` (e.g. "*1"). Updatable fields include `address`, `port`, interval timings ' + '(`refresh_interval`, `expire_interval`, `retry_interval`, e.g. "10m", "30s", "2h"), `comment`, and ' + "`disabled`. To toggle enabled/disabled only, prefer `set_rpki_session_enabled`. Returns the updated session " + "detail.",
18452
18911
  inputSchema: {
18453
- session_id: z90.string().describe('Session id, e.g. "*1"'),
18454
- address: z90.string().optional(),
18455
- port: z90.number().int().optional(),
18456
- refresh_interval: z90.string().optional(),
18457
- expire_interval: z90.string().optional(),
18458
- retry_interval: z90.string().optional(),
18459
- comment: z90.string().optional(),
18460
- disabled: z90.boolean().optional()
18912
+ session_id: z91.string().describe('Session id, e.g. "*1"'),
18913
+ address: z91.string().optional(),
18914
+ port: z91.number().int().optional(),
18915
+ refresh_interval: z91.string().optional(),
18916
+ expire_interval: z91.string().optional(),
18917
+ retry_interval: z91.string().optional(),
18918
+ comment: z91.string().optional(),
18919
+ disabled: z91.boolean().optional()
18461
18920
  },
18462
18921
  async handler(a, ctx) {
18463
18922
  ctx.info(`Updating RPKI session ${a.session_id}`);
@@ -18486,7 +18945,7 @@ ${details}`;
18486
18945
  title: "Remove RPKI Session",
18487
18946
  annotations: DESTRUCTIVE,
18488
18947
  description: "Permanently removes an RPKI RTR session (`/routing rpki remove`) by its `.id`. Use `list_rpki_sessions` " + 'to obtain the `session_id` (e.g. "*1"). After removal, BGP routes that matched this session\'s `group` ' + "fall back to validation state 'unknown'; ensure BGP filters are updated before removing. To deactivate " + "without deleting, use `set_rpki_session_enabled` instead.",
18489
- inputSchema: { session_id: z90.string().describe('Session id, e.g. "*1"') },
18948
+ inputSchema: { session_id: z91.string().describe('Session id, e.g. "*1"') },
18490
18949
  async handler(a, ctx) {
18491
18950
  ctx.info(`Removing RPKI session ${a.session_id}`);
18492
18951
  const result = await executeMikrotikCommand(`/routing rpki remove ${a.session_id}`, ctx);
@@ -18503,8 +18962,8 @@ ${details}`;
18503
18962
  annotations: WRITE_IDEMPOTENT,
18504
18963
  description: "Toggles an RPKI RTR session active or inactive (`/routing rpki set disabled=yes/no`) by its `.id`. Use " + '`list_rpki_sessions` to obtain the `session_id` (e.g. "*1"). Disabling suspends the RTR connection and ' + "stops VRP updates without deleting the session; BGP routes referencing its group fall back to 'unknown' " + "while disabled. To change address, port, or intervals use `update_rpki_session`; to delete the session " + "permanently use `remove_rpki_session`.",
18505
18964
  inputSchema: {
18506
- session_id: z90.string().describe('Session id, e.g. "*1"'),
18507
- enabled: z90.boolean()
18965
+ session_id: z91.string().describe('Session id, e.g. "*1"'),
18966
+ enabled: z91.boolean()
18508
18967
  },
18509
18968
  async handler(a, ctx) {
18510
18969
  ctx.info(`Setting RPKI session ${a.session_id} enabled=${a.enabled}`);
@@ -18519,7 +18978,7 @@ ${details}`;
18519
18978
  ];
18520
18979
 
18521
18980
  // src/tools/routing-settings.ts
18522
- import { z as z91 } from "zod";
18981
+ import { z as z92 } from "zod";
18523
18982
  var UNSUPPORTED12 = "Routing settings are not available on this device (requires RouterOS v7 with the routing package).";
18524
18983
  var HASH_POLICY = ["l3", "l3-inner", "l4"];
18525
18984
  var routingSettingsTools = [
@@ -18544,9 +19003,9 @@ ${result}`;
18544
19003
  annotations: WRITE_IDEMPOTENT,
18545
19004
  description: "Sets global routing daemon settings (`/routing settings set`) on RouterOS v7 \u2014 " + "controls ECMP/multipath hash policy and VRF-as-interface behavior. " + "`ipv4_multipath_hash_policy` and `ipv6_multipath_hash_policy` choose how equal-cost next-hops are selected: " + "`l3` = src/dst IP only, `l3-inner` = inner packet header for tunnelled traffic, `l4` = include L4 src/dst ports. " + "`ipv4_vrf_as_interface` exposes VRFs as interfaces to the rest of the config. " + "For modifying individual IPv4 route entries use update_route; " + "for policy-based routing rules use update_routing_rule. " + "Returns the full /routing settings after applying changes.",
18546
19005
  inputSchema: {
18547
- ipv4_multipath_hash_policy: z91.enum(HASH_POLICY).optional(),
18548
- ipv6_multipath_hash_policy: z91.enum(HASH_POLICY).optional(),
18549
- ipv4_vrf_as_interface: z91.boolean().optional()
19006
+ ipv4_multipath_hash_policy: z92.enum(HASH_POLICY).optional(),
19007
+ ipv6_multipath_hash_policy: z92.enum(HASH_POLICY).optional(),
19008
+ ipv4_vrf_as_interface: z92.boolean().optional()
18550
19009
  },
18551
19010
  async handler(a, ctx) {
18552
19011
  ctx.info("Updating routing settings");
@@ -18568,7 +19027,7 @@ ${details}`;
18568
19027
  ];
18569
19028
 
18570
19029
  // src/tools/routing-rule.ts
18571
- import { z as z92 } from "zod";
19030
+ import { z as z93 } from "zod";
18572
19031
  var UNSUPPORTED13 = "Routing rules are not available on this device (requires RouterOS v7 with the routing package).";
18573
19032
  var ACTIONS = ["lookup", "lookup-only-in-table", "drop", "unreachable"];
18574
19033
  var routingRuleTools = [
@@ -18578,8 +19037,8 @@ var routingRuleTools = [
18578
19037
  annotations: READ,
18579
19038
  description: "List policy routing rules (`/routing rule`) \u2014 the ordered table that steers packets into specific " + "routing tables based on src/dst prefix, incoming interface, or routing-mark. Rules are evaluated " + "top-down; requires RouterOS v7 with the routing package. For static routes in the main table use " + "`list_routes`; for IPv6 static routes use `list_ipv6_routes`. Returns all matching rules with full " + "detail, including `.id` values needed by `get_routing_rule`, `update_routing_rule`, " + "`remove_routing_rule`, and `set_routing_rule_enabled`.",
18580
19039
  inputSchema: {
18581
- table_filter: z92.string().optional().describe("Match rules targeting this table"),
18582
- disabled_only: z92.boolean().default(false)
19040
+ table_filter: z93.string().optional().describe("Match rules targeting this table"),
19041
+ disabled_only: z93.boolean().default(false)
18583
19042
  },
18584
19043
  async handler(a, ctx) {
18585
19044
  ctx.info("Listing routing rules");
@@ -18602,7 +19061,7 @@ ${result}`;
18602
19061
  annotations: READ,
18603
19062
  description: "Fetch full detail for a single policy routing rule (`/routing rule`) by its `.id` " + "(obtain the `.id` from `list_routing_rules`). Requires RouterOS v7 with the routing package. " + "For static route details use `get_route`. Returns the rule's complete property set, or a not-found " + "message if the id does not exist.",
18604
19063
  inputSchema: {
18605
- rule_id: z92.string().describe('Rule id from list output, e.g. "*3"')
19064
+ rule_id: z93.string().describe('Rule id from list output, e.g. "*3"')
18606
19065
  },
18607
19066
  async handler(a, ctx) {
18608
19067
  ctx.info(`Getting routing rule: ${a.rule_id}`);
@@ -18620,16 +19079,16 @@ ${result}`;
18620
19079
  annotations: WRITE,
18621
19080
  description: "Add a policy routing rule (`/routing rule add`) to steer matched packets into a specific routing table. " + "Rules are evaluated top-down: `action=lookup` consults `table` then falls through to lower-priority " + "tables on no match; `lookup-only-in-table` stops at that table; `drop`/`unreachable` discard the packet. " + 'Match on `src_address` (e.g. `"192.168.10.0/24"`), `dst_address`, `routing_mark`, or incoming ' + "`interface`. Use `place_before` with a rule `.id` to control insertion order. Requires RouterOS v7 with " + "the routing package. For static routes in the main or a named table use `add_route`; for IPv6 static " + "routes use `add_ipv6_route`. Returns the new rule's `.id`.",
18622
19081
  inputSchema: {
18623
- action: z92.enum(ACTIONS).default("lookup"),
18624
- table: z92.string().optional().describe("Target routing table for lookup actions"),
18625
- src_address: z92.string().optional().describe('Source prefix, e.g. "192.168.10.0/24"'),
18626
- dst_address: z92.string().optional().describe("Destination prefix"),
18627
- routing_mark: z92.string().optional().describe("Match packets carrying this routing-mark"),
18628
- interface: z92.string().optional().describe("Match this incoming interface"),
18629
- min_prefix: z92.number().int().optional(),
18630
- comment: z92.string().optional(),
18631
- disabled: z92.boolean().default(false),
18632
- place_before: z92.string().optional().describe("Insert before this rule id to control ordering")
19082
+ action: z93.enum(ACTIONS).default("lookup"),
19083
+ table: z93.string().optional().describe("Target routing table for lookup actions"),
19084
+ src_address: z93.string().optional().describe('Source prefix, e.g. "192.168.10.0/24"'),
19085
+ dst_address: z93.string().optional().describe("Destination prefix"),
19086
+ routing_mark: z93.string().optional().describe("Match packets carrying this routing-mark"),
19087
+ interface: z93.string().optional().describe("Match this incoming interface"),
19088
+ min_prefix: z93.number().int().optional(),
19089
+ comment: z93.string().optional(),
19090
+ disabled: z93.boolean().default(false),
19091
+ place_before: z93.string().optional().describe("Insert before this rule id to control ordering")
18633
19092
  },
18634
19093
  async handler(a, ctx) {
18635
19094
  ctx.info(`Adding routing rule: action=${a.action}, table=${a.table}`);
@@ -18649,16 +19108,16 @@ ${result}`;
18649
19108
  annotations: WRITE_IDEMPOTENT,
18650
19109
  description: "Modify an existing policy routing rule (`/routing rule set`) by its `.id` (obtain from `list_routing_rules`). " + 'Pass `""` for `src_address`, `dst_address`, `routing_mark`, `interface`, or `table` to clear that field. ' + "Requires RouterOS v7 with the routing package. For updating static routes use `update_route`. " + "Returns the rule's updated detail after applying changes.",
18651
19110
  inputSchema: {
18652
- rule_id: z92.string().describe('Rule id, e.g. "*3"'),
18653
- action: z92.enum(ACTIONS).optional(),
18654
- table: z92.string().optional(),
18655
- src_address: z92.string().optional(),
18656
- dst_address: z92.string().optional(),
18657
- routing_mark: z92.string().optional(),
18658
- interface: z92.string().optional(),
18659
- min_prefix: z92.number().int().optional(),
18660
- comment: z92.string().optional(),
18661
- disabled: z92.boolean().optional()
19111
+ rule_id: z93.string().describe('Rule id, e.g. "*3"'),
19112
+ action: z93.enum(ACTIONS).optional(),
19113
+ table: z93.string().optional(),
19114
+ src_address: z93.string().optional(),
19115
+ dst_address: z93.string().optional(),
19116
+ routing_mark: z93.string().optional(),
19117
+ interface: z93.string().optional(),
19118
+ min_prefix: z93.number().int().optional(),
19119
+ comment: z93.string().optional(),
19120
+ disabled: z93.boolean().optional()
18662
19121
  },
18663
19122
  async handler(a, ctx) {
18664
19123
  ctx.info(`Updating routing rule: ${a.rule_id}`);
@@ -18701,7 +19160,7 @@ ${details}`;
18701
19160
  title: "Remove Policy Routing Rule",
18702
19161
  annotations: DESTRUCTIVE,
18703
19162
  description: "Delete a policy routing rule (`/routing rule remove`) by its `.id` (obtain from `list_routing_rules`). " + "This is irreversible \u2014 confirm the correct rule with `get_routing_rule` before removing. Requires RouterOS v7 " + "with the routing package. For removing static routes use `remove_route`. Returns a confirmation message on success.",
18704
- inputSchema: { rule_id: z92.string().describe('Rule id, e.g. "*3"') },
19163
+ inputSchema: { rule_id: z93.string().describe('Rule id, e.g. "*3"') },
18705
19164
  async handler(a, ctx) {
18706
19165
  ctx.info(`Removing routing rule: ${a.rule_id}`);
18707
19166
  const result = await executeMikrotikCommand(`/routing rule remove ${a.rule_id}`, ctx);
@@ -18718,8 +19177,8 @@ ${details}`;
18718
19177
  annotations: WRITE_IDEMPOTENT,
18719
19178
  description: "Enable or disable a policy routing rule (`/routing rule set disabled=yes/no`) by its `.id` " + "(obtain from `list_routing_rules`). Set `enabled=true` to activate or `enabled=false` to temporarily " + "suspend the rule without deleting it. Requires RouterOS v7 with the routing package. To permanently " + "delete a rule use `remove_routing_rule`. Returns a confirmation of the new enabled/disabled state.",
18720
19179
  inputSchema: {
18721
- rule_id: z92.string().describe('Rule id, e.g. "*3"'),
18722
- enabled: z92.boolean().describe("true to enable, false to disable")
19180
+ rule_id: z93.string().describe('Rule id, e.g. "*3"'),
19181
+ enabled: z93.boolean().describe("true to enable, false to disable")
18723
19182
  },
18724
19183
  async handler(a, ctx) {
18725
19184
  ctx.info(`Setting routing rule ${a.rule_id} enabled=${a.enabled}`);
@@ -18734,7 +19193,7 @@ ${details}`;
18734
19193
  ];
18735
19194
 
18736
19195
  // src/tools/routing-table.ts
18737
- import { z as z93 } from "zod";
19196
+ import { z as z94 } from "zod";
18738
19197
  var UNSUPPORTED14 = "Routing tables are not available on this device (requires RouterOS v7 with the routing package).";
18739
19198
  function fibToken(fib, onSet = false) {
18740
19199
  if (fib === undefined)
@@ -18753,7 +19212,7 @@ var routingTableTools = [
18753
19212
  annotations: READ,
18754
19213
  description: "Lists named routing table definitions (`/routing table print detail`) \u2014 the RIB (Routing Information Base) " + "containers used by RouterOS v7 policy-based routing. Each table isolates a routing domain; `fib=true` means its " + "routes are installed into the forwarding plane. The built-in `main` table is always present. " + "For actual IPv4 routes stored inside a table use `list_routes`; for IPv6 routes use `list_ipv6_routes`; " + "for the policy rules that steer packets into a table use `list_routing_rules`. " + "Returns all table definitions with name, fib flag, disabled state, and comment; " + "supports optional `name_filter` substring match.",
18755
19214
  inputSchema: {
18756
- name_filter: z93.string().optional().describe("Substring match on table name")
19215
+ name_filter: z94.string().optional().describe("Substring match on table name")
18757
19216
  },
18758
19217
  async handler(a, ctx) {
18759
19218
  ctx.info("Listing routing tables");
@@ -18771,7 +19230,7 @@ ${result}`;
18771
19230
  title: "Get Routing Table Definition",
18772
19231
  annotations: READ,
18773
19232
  description: "Gets the definition of a single named routing table (`/routing table print detail where name=\u2026`) \u2014 " + "inspects whether `fib` is active and whether the table is disabled. " + "For listing all tables use `list_routing_tables`. " + "For the IPv4 routes stored inside a table use `list_routes`; for IPv6 routes use `list_ipv6_routes`. " + "Returns the full detail record for the named table, or a not-found message if the name does not exist.",
18774
- inputSchema: { name: z93.string().describe("Routing table name") },
19233
+ inputSchema: { name: z94.string().describe("Routing table name") },
18775
19234
  async handler(a, ctx) {
18776
19235
  ctx.info(`Getting routing table: ${a.name}`);
18777
19236
  const result = await executeMikrotikCommand(`/routing table print detail where name="${a.name}"`, ctx);
@@ -18788,10 +19247,10 @@ ${result}`;
18788
19247
  annotations: WRITE,
18789
19248
  description: "Creates a named routing table (`/routing table add`) on RouterOS v7 \u2014 establishes a new RIB container " + "for policy-based routing. `fib` defaults to `true`, which installs the table's routes into the forwarding plane; " + "set `fib=false` to keep it RIB-only, used purely for lookups by routing rules/marks. " + "After creation, assign IPv4 routes to this table via `add_route` (set its `routing-table` argument); " + "for IPv6 routes use `add_ipv6_route`. " + "For the policy rules that steer packets into a table use `list_routing_rules` / `update_routing_rule`. " + "To modify an existing table use `update_routing_table`. " + "Returns the new table's full detail record on success.",
18790
19249
  inputSchema: {
18791
- name: z93.string().describe("Unique table name, referenced by routes and routing rules"),
18792
- fib: z93.boolean().default(true).describe("Install routes into the FIB (forwarding plane)"),
18793
- comment: z93.string().optional(),
18794
- disabled: z93.boolean().default(false)
19250
+ name: z94.string().describe("Unique table name, referenced by routes and routing rules"),
19251
+ fib: z94.boolean().default(true).describe("Install routes into the FIB (forwarding plane)"),
19252
+ comment: z94.string().optional(),
19253
+ disabled: z94.boolean().default(false)
18795
19254
  },
18796
19255
  async handler(a, ctx) {
18797
19256
  ctx.info(`Adding routing table: ${a.name}`);
@@ -18813,10 +19272,10 @@ ${details}`;
18813
19272
  annotations: WRITE_IDEMPOTENT,
18814
19273
  description: "Modifies an existing routing table (`/routing table set [find name=\u2026]`) \u2014 changes its `fib` flag, " + "`comment`, or `disabled` state. " + "To create a new table use `add_routing_table`; to toggle only the enabled/disabled state use `set_routing_table_enabled`. " + "No-ops safely if no optional arguments are provided. " + "Returns the updated table's full detail record on success.",
18815
19274
  inputSchema: {
18816
- name: z93.string().describe("Existing routing table name"),
18817
- fib: z93.boolean().optional().describe("Install routes into the FIB"),
18818
- comment: z93.string().optional(),
18819
- disabled: z93.boolean().optional()
19275
+ name: z94.string().describe("Existing routing table name"),
19276
+ fib: z94.boolean().optional().describe("Install routes into the FIB"),
19277
+ comment: z94.string().optional(),
19278
+ disabled: z94.boolean().optional()
18820
19279
  },
18821
19280
  async handler(a, ctx) {
18822
19281
  ctx.info(`Updating routing table: ${a.name}`);
@@ -18846,7 +19305,7 @@ ${details}`;
18846
19305
  title: "Remove Routing Table Definition",
18847
19306
  annotations: DESTRUCTIVE,
18848
19307
  description: "Permanently deletes a named routing table (`/routing table remove [find name=\u2026]`). " + "The built-in `main` table cannot be removed. " + "Routes referencing this table should be removed first via `remove_route` to avoid orphaned entries. " + "To disable without deleting use `set_routing_table_enabled`; to modify properties use `update_routing_table`. " + "Confirms deletion by name on success.",
18849
- inputSchema: { name: z93.string().describe("Routing table name to remove") },
19308
+ inputSchema: { name: z94.string().describe("Routing table name to remove") },
18850
19309
  async handler(a, ctx) {
18851
19310
  ctx.info(`Removing routing table: ${a.name}`);
18852
19311
  const result = await executeMikrotikCommand(`/routing table remove [find name="${a.name}"]`, ctx);
@@ -18863,8 +19322,8 @@ ${details}`;
18863
19322
  annotations: WRITE_IDEMPOTENT,
18864
19323
  description: "Enables or disables a named routing table (`/routing table set [find name=\u2026] disabled=yes/no`) without " + "removing it \u2014 a disabled table is inactive in the routing engine. " + "To permanently delete a table use `remove_routing_table`; to change other properties (fib, comment) use `update_routing_table`. " + "Pass `enabled=true` to enable, `enabled=false` to disable. " + "Confirms the new state by name on success.",
18865
19324
  inputSchema: {
18866
- name: z93.string().describe("Routing table name"),
18867
- enabled: z93.boolean().describe("true to enable, false to disable")
19325
+ name: z94.string().describe("Routing table name"),
19326
+ enabled: z94.boolean().describe("true to enable, false to disable")
18868
19327
  },
18869
19328
  async handler(a, ctx) {
18870
19329
  ctx.info(`Setting routing table ${a.name} enabled=${a.enabled}`);
@@ -18879,7 +19338,7 @@ ${details}`;
18879
19338
  ];
18880
19339
 
18881
19340
  // src/tools/dr-drill.ts
18882
- import { z as z94 } from "zod";
19341
+ import { z as z95 } from "zod";
18883
19342
  function parsePingSummary(output) {
18884
19343
  const sent = output.match(/sent=(\d+)/)?.[1];
18885
19344
  const received = output.match(/received=(\d+)/)?.[1];
@@ -18899,11 +19358,11 @@ var drDrillTools = [
18899
19358
  annotations: DANGEROUS,
18900
19359
  description: "Rehearses a disaster safely: INSIDE SAFE MODE it disables a target (a WAN/tunnel interface, or " + "a route), pings `verify_host` to check the backup path actually carries traffic, then ROLLS " + "BACK \u2014 Safe Mode auto-reverts, so the change is never committed. Proves your failover works " + "without a real outage. Requires `confirm=true` to run (it briefly disrupts traffic on the " + "target); with confirm=false it just describes the drill. SSH-only (Safe Mode is unavailable on " + "MAC-Telnet). Returns whether connectivity held during the simulated failure.",
18901
19360
  inputSchema: {
18902
- target_type: z94.enum(["interface", "route"]).default("interface"),
18903
- target: z94.string().describe("Interface name (e.g. 'ether1-wan') or, for route, a find expression / .id"),
18904
- verify_host: z94.string().describe("Host to ping during the outage to confirm the backup path, e.g. '8.8.8.8'"),
18905
- ping_count: z94.number().int().min(1).max(50).default(5),
18906
- confirm: z94.boolean().default(false).describe("Must be true to actually run the drill")
19361
+ target_type: z95.enum(["interface", "route"]).default("interface"),
19362
+ target: z95.string().describe("Interface name (e.g. 'ether1-wan') or, for route, a find expression / .id"),
19363
+ verify_host: z95.string().describe("Host to ping during the outage to confirm the backup path, e.g. '8.8.8.8'"),
19364
+ ping_count: z95.number().int().min(1).max(50).default(5),
19365
+ confirm: z95.boolean().default(false).describe("Must be true to actually run the drill")
18907
19366
  },
18908
19367
  async handler(a, ctx) {
18909
19368
  const disableCmd = a.target_type === "interface" ? `/interface disable "${a.target}"` : `/ip route disable [find ${a.target.startsWith("*") ? `.id=${a.target}` : a.target}]`;
@@ -18987,7 +19446,7 @@ var safeModeTools = [
18987
19446
  ];
18988
19447
 
18989
19448
  // src/tools/scheduler.ts
18990
- import { z as z95 } from "zod";
19449
+ import { z as z96 } from "zod";
18991
19450
  var schedulerTools = [
18992
19451
  defineTool({
18993
19452
  name: "create_scheduler",
@@ -18995,14 +19454,14 @@ var schedulerTools = [
18995
19454
  annotations: WRITE,
18996
19455
  description: "Creates a scheduler entry (`/system scheduler add`) \u2014 time-driven automation that runs an inline script or calls a named script at a fixed interval or date/time. " + "Use this to trigger recurring or one-shot tasks without an interactive session. " + "For storing reusable scripts by name use add_script; to reference a stored script set on_event to its name. " + "interval accepts RouterOS duration strings like '00:05:00' (every 5 minutes); '0' means run once. " + "start_time accepts 'startup' or a wall-clock time like '12:00:00'; start_date accepts 'jan/01/2026'. " + "Returns the created entry's detail. To suspend without deleting use disable_scheduler; to re-activate use enable_scheduler.",
18997
19456
  inputSchema: {
18998
- name: z95.string().describe("Name for the scheduler entry"),
18999
- on_event: z95.string().describe("Script source to run on event (may contain spaces/semicolons)"),
19000
- interval: z95.string().optional().describe("Run interval, e.g. '00:05:00' (0 = run once)"),
19001
- start_time: z95.string().optional().describe("Start time, e.g. '12:00:00' or 'startup'"),
19002
- start_date: z95.string().optional().describe("Start date, e.g. 'jan/01/2026'"),
19003
- policy: z95.string().optional().describe("Permission policy list, e.g. 'read,write,test,policy'"),
19004
- comment: z95.string().optional(),
19005
- disabled: z95.boolean().default(false)
19457
+ name: z96.string().describe("Name for the scheduler entry"),
19458
+ on_event: z96.string().describe("Script source to run on event (may contain spaces/semicolons)"),
19459
+ interval: z96.string().optional().describe("Run interval, e.g. '00:05:00' (0 = run once)"),
19460
+ start_time: z96.string().optional().describe("Start time, e.g. '12:00:00' or 'startup'"),
19461
+ start_date: z96.string().optional().describe("Start date, e.g. 'jan/01/2026'"),
19462
+ policy: z96.string().optional().describe("Permission policy list, e.g. 'read,write,test,policy'"),
19463
+ comment: z96.string().optional(),
19464
+ disabled: z96.boolean().default(false)
19006
19465
  },
19007
19466
  async handler(a, ctx) {
19008
19467
  ctx.info(`Creating scheduler: name=${a.name}`);
@@ -19022,7 +19481,7 @@ ${details}` : "Scheduler creation completed but unable to verify.";
19022
19481
  annotations: READ,
19023
19482
  description: "Lists all scheduler entries (`/system scheduler print`) \u2014 every time-driven task configured on the device. " + "Optionally filters by partial name match via name_filter. " + "To retrieve full detail for a single entry use get_scheduler. " + "To list stored scripts (not schedulers) use list_scripts.",
19024
19483
  inputSchema: {
19025
- name_filter: z95.string().optional().describe("Partial name match")
19484
+ name_filter: z96.string().optional().describe("Partial name match")
19026
19485
  },
19027
19486
  async handler(a, ctx) {
19028
19487
  ctx.info("Listing schedulers");
@@ -19040,7 +19499,7 @@ ${result}`;
19040
19499
  title: "Get Scheduler Entry Details",
19041
19500
  annotations: READ,
19042
19501
  description: "Retrieves full detail for a single scheduler entry (`/system scheduler print detail where name=...`). " + "Use this to inspect the on-event script, interval, next-run time, and run-count of one specific entry. " + "For all entries use list_schedulers. To look up stored system scripts use list_scripts.",
19043
- inputSchema: { name: z95.string() },
19502
+ inputSchema: { name: z96.string() },
19044
19503
  async handler(a, ctx) {
19045
19504
  ctx.info(`Getting scheduler details: name=${a.name}`);
19046
19505
  const result = await executeMikrotikCommand(`/system scheduler print detail where name="${a.name}"`, ctx);
@@ -19054,7 +19513,7 @@ ${result}`;
19054
19513
  title: "Remove Scheduler Entry",
19055
19514
  annotations: DESTRUCTIVE,
19056
19515
  description: "Permanently removes a scheduler entry (`/system scheduler remove`) by name after verifying it exists. " + "Use this to delete a time-driven task from the device. " + "To suspend without deleting use disable_scheduler. " + "To remove a stored script (not a scheduler) use remove_script.",
19057
- inputSchema: { name: z95.string() },
19516
+ inputSchema: { name: z96.string() },
19058
19517
  async handler(a, ctx) {
19059
19518
  ctx.info(`Removing scheduler: name=${a.name}`);
19060
19519
  const count = await executeMikrotikCommand(`/system scheduler print count-only where name="${a.name}"`, ctx);
@@ -19071,7 +19530,7 @@ ${result}`;
19071
19530
  title: "Enable Scheduler Entry",
19072
19531
  annotations: WRITE_IDEMPOTENT,
19073
19532
  description: "Enables a disabled scheduler entry (`/system scheduler enable`) by name, allowing it to fire again on its configured interval or schedule. " + "Idempotent \u2014 safe to call when already enabled. " + "To pause without deleting use disable_scheduler. To permanently delete use remove_scheduler.",
19074
- inputSchema: { name: z95.string() },
19533
+ inputSchema: { name: z96.string() },
19075
19534
  async handler(a, ctx) {
19076
19535
  ctx.info(`Enabling scheduler: name=${a.name}`);
19077
19536
  const result = await executeMikrotikCommand(`/system scheduler enable [find name="${a.name}"]`, ctx);
@@ -19085,7 +19544,7 @@ ${result}`;
19085
19544
  title: "Disable Scheduler Entry",
19086
19545
  annotations: WRITE_IDEMPOTENT,
19087
19546
  description: "Disables an active scheduler entry (`/system scheduler disable`) by name, preventing it from firing without removing it. " + "Idempotent \u2014 safe to call when already disabled. " + "To resume use enable_scheduler. To permanently delete use remove_scheduler.",
19088
- inputSchema: { name: z95.string() },
19547
+ inputSchema: { name: z96.string() },
19089
19548
  async handler(a, ctx) {
19090
19549
  ctx.info(`Disabling scheduler: name=${a.name}`);
19091
19550
  const result = await executeMikrotikCommand(`/system scheduler disable [find name="${a.name}"]`, ctx);
@@ -19100,11 +19559,11 @@ ${result}`;
19100
19559
  annotations: WRITE,
19101
19560
  description: "Stores a named script in the system script repository (`/system script add`) \u2014 reusable RouterOS code callable by name. " + "Use this to keep logic out of on-event fields and share it across multiple schedulers or trigger on demand. " + "To execute a stored script immediately use run_script. " + "To schedule a script to run periodically use create_scheduler (set on_event to the script name). " + "dont_require_permissions skips policy-permission checks of the caller. " + "Returns the added script's detail including its name and source.",
19102
19561
  inputSchema: {
19103
- name: z95.string().describe("Name for the script"),
19104
- source: z95.string().describe("Script source code (may contain spaces/semicolons)"),
19105
- policy: z95.string().optional().describe("Permission policy list, e.g. 'read,write,test,policy'"),
19106
- comment: z95.string().optional(),
19107
- dont_require_permissions: z95.boolean().default(false).describe("Run without checking the policy permissions of the caller")
19562
+ name: z96.string().describe("Name for the script"),
19563
+ source: z96.string().describe("Script source code (may contain spaces/semicolons)"),
19564
+ policy: z96.string().optional().describe("Permission policy list, e.g. 'read,write,test,policy'"),
19565
+ comment: z96.string().optional(),
19566
+ dont_require_permissions: z96.boolean().default(false).describe("Run without checking the policy permissions of the caller")
19108
19567
  },
19109
19568
  async handler(a, ctx) {
19110
19569
  ctx.info(`Adding script: name=${a.name}`);
@@ -19124,7 +19583,7 @@ ${details}` : "Script creation completed but unable to verify.";
19124
19583
  annotations: READ,
19125
19584
  description: "Lists all stored scripts in the system script repository (`/system script print`). " + "Optionally filters by partial name match via name_filter. " + "Use this to see what named scripts are available for run_script or for referencing in a scheduler's on_event. " + "To list scheduled tasks (not stored scripts) use list_schedulers.",
19126
19585
  inputSchema: {
19127
- name_filter: z95.string().optional().describe("Partial name match")
19586
+ name_filter: z96.string().optional().describe("Partial name match")
19128
19587
  },
19129
19588
  async handler(a, ctx) {
19130
19589
  ctx.info("Listing scripts");
@@ -19142,7 +19601,7 @@ ${result}`;
19142
19601
  title: "Remove Script from Repository",
19143
19602
  annotations: DESTRUCTIVE,
19144
19603
  description: "Permanently removes a named script from the system script repository (`/system script remove`) after verifying it exists. " + "Use this to clean up scripts no longer needed. " + "Verify nothing references it via list_schedulers before removing \u2014 schedulers whose on-event names a deleted script will fail silently. " + "To remove a scheduler entry (not a script) use remove_scheduler.",
19145
- inputSchema: { name: z95.string() },
19604
+ inputSchema: { name: z96.string() },
19146
19605
  async handler(a, ctx) {
19147
19606
  ctx.info(`Removing script: name=${a.name}`);
19148
19607
  const count = await executeMikrotikCommand(`/system script print count-only where name="${a.name}"`, ctx);
@@ -19159,7 +19618,7 @@ ${result}`;
19159
19618
  title: "Run Script from Repository",
19160
19619
  annotations: WRITE,
19161
19620
  description: "Executes a named script from the system script repository immediately (`/system script run`). " + "Use this to trigger a stored script on demand without waiting for a scheduler. " + "The script must already exist in the repository \u2014 to add one use add_script; to see available scripts use list_scripts. " + "For recurring or time-based execution use create_scheduler. " + "Returns the script's console output.",
19162
- inputSchema: { name: z95.string() },
19621
+ inputSchema: { name: z96.string() },
19163
19622
  async handler(a, ctx) {
19164
19623
  ctx.info(`Running script: name=${a.name}`);
19165
19624
  const result = await executeMikrotikCommand(`/system script run [find name="${a.name}"]`, ctx);
@@ -19173,7 +19632,7 @@ ${result}`;
19173
19632
  ];
19174
19633
 
19175
19634
  // src/tools/threat-feed.ts
19176
- import { z as z96 } from "zod";
19635
+ import { z as z97 } from "zod";
19177
19636
  var FEED_TAG = "threat-feed";
19178
19637
  var threatFeedTools = [
19179
19638
  defineTool({
@@ -19182,12 +19641,12 @@ var threatFeedTools = [
19182
19641
  annotations: WRITE,
19183
19642
  description: "Subscribes the router to an external threat-intelligence IP feed: installs a `/system script` " + "that fetches `url` and imports it into the `address_list`, a `/system scheduler` that re-runs it " + "every `interval`, and (when `drop=true`) a raw pre-conntrack drop for that list \u2014 a self-updating " + "blocklist (e.g. Spamhaus DROP, tor exit nodes, your own deny list). `url` MUST point to a " + "RouterOS `.rsc` file that populates the address-list. DEFAULTS TO A DRY RUN (`apply=false`); set " + "`apply=true` to install. Script/scheduler/rule are named/tagged `threat-feed-<name>` so " + "remove_threat_feed can undo them. Returns the plan or a build report.",
19184
19643
  inputSchema: {
19185
- name: z96.string().describe("Short feed id, e.g. 'spamhaus-drop'"),
19186
- url: z96.string().describe("HTTPS URL of a RouterOS .rsc address-list file"),
19187
- address_list: z96.string().default("threat-blocklist").describe("Address-list to populate"),
19188
- interval: z96.string().default("1h").describe("How often to refresh the feed"),
19189
- drop: z96.boolean().default(true).describe("Also add a raw drop rule for the address-list"),
19190
- apply: z96.boolean().default(false).describe("false = preview (default); true = install")
19644
+ name: z97.string().describe("Short feed id, e.g. 'spamhaus-drop'"),
19645
+ url: z97.string().describe("HTTPS URL of a RouterOS .rsc address-list file"),
19646
+ address_list: z97.string().default("threat-blocklist").describe("Address-list to populate"),
19647
+ interval: z97.string().default("1h").describe("How often to refresh the feed"),
19648
+ drop: z97.boolean().default(true).describe("Also add a raw drop rule for the address-list"),
19649
+ apply: z97.boolean().default(false).describe("false = preview (default); true = install")
19191
19650
  },
19192
19651
  async handler(a, ctx) {
19193
19652
  const id = `${FEED_TAG}-${a.name}`;
@@ -19224,7 +19683,7 @@ ${plan}`;
19224
19683
  title: "Remove Threat-Intel Feed",
19225
19684
  annotations: DESTRUCTIVE,
19226
19685
  description: "Removes a threat feed installed by subscribe_threat_feed: deletes its `/system scheduler` and " + "`/system script` (named `threat-feed-<name>`) and any raw drop rule tagged for it. Does NOT " + "flush the address-list entries themselves. Returns what was removed.",
19227
- inputSchema: { name: z96.string().describe("The feed id used when subscribing") },
19686
+ inputSchema: { name: z97.string().describe("The feed id used when subscribing") },
19228
19687
  async handler(a, ctx) {
19229
19688
  const id = `${FEED_TAG}-${a.name}`;
19230
19689
  ctx.info(`Removing threat feed ${id}`);
@@ -19246,7 +19705,7 @@ ${plan}`;
19246
19705
  ];
19247
19706
 
19248
19707
  // src/tools/sstp.ts
19249
- import { z as z97 } from "zod";
19708
+ import { z as z98 } from "zod";
19250
19709
  var sstpTools = [
19251
19710
  defineTool({
19252
19711
  name: "get_sstp_server",
@@ -19267,19 +19726,19 @@ ${result}`;
19267
19726
  annotations: WRITE_IDEMPOTENT,
19268
19727
  description: "Configure the global SSTP server listener (`/interface sstp-server server set`) \u2014 accepts/rejects incoming TLS VPN connections on this router. " + "Use to enable the server, bind a TLS certificate, change the TCP port, restrict authentication methods (comma-separated, e.g. `'mschap2,mschap1'`), set the default PPP profile, pin TLS version (`any` or `only-1.2`), or enforce client certificate verification. " + "This is a singleton write that modifies the server block, not a client interface. " + "To read the current server config use `get_sstp_server`. " + "To create outbound SSTP tunnels (this router dials out) use `create_sstp_client`; for L2TP outbound use `create_l2tp_client`, for OpenVPN use `create_ovpn_client`, for PPTP use `create_pptp_client`. " + "Returns the updated server block on success.",
19269
19728
  inputSchema: {
19270
- enabled: z97.boolean().optional(),
19271
- default_profile: z97.string().optional(),
19272
- authentication: z97.string().optional().describe("Comma-separated, e.g. 'mschap2,mschap1'"),
19273
- certificate: z97.string().optional().describe("TLS certificate name"),
19274
- port: z97.number().int().optional(),
19275
- tls_version: z97.enum(["any", "only-1.2"]).optional(),
19276
- verify_client_certificate: z97.boolean().optional(),
19277
- pfs: z97.boolean().optional().describe("Enable Perfect Forward Secrecy"),
19278
- force_aes: z97.boolean().optional().describe("Require clients to use AES ciphers"),
19279
- max_mtu: z97.number().int().optional().describe("Maximum transmission unit"),
19280
- max_mru: z97.number().int().optional().describe("Maximum receive unit"),
19281
- mrru: z97.number().int().optional().describe("Max receive reconstructed unit for MP"),
19282
- keepalive_timeout: z97.number().int().optional().describe("Seconds before an idle connection is considered down")
19729
+ enabled: z98.boolean().optional(),
19730
+ default_profile: z98.string().optional(),
19731
+ authentication: z98.string().optional().describe("Comma-separated, e.g. 'mschap2,mschap1'"),
19732
+ certificate: z98.string().optional().describe("TLS certificate name"),
19733
+ port: z98.number().int().optional(),
19734
+ tls_version: z98.enum(["any", "only-1.2"]).optional(),
19735
+ verify_client_certificate: z98.boolean().optional(),
19736
+ pfs: z98.boolean().optional().describe("Enable Perfect Forward Secrecy"),
19737
+ force_aes: z98.boolean().optional().describe("Require clients to use AES ciphers"),
19738
+ max_mtu: z98.number().int().optional().describe("Maximum transmission unit"),
19739
+ max_mru: z98.number().int().optional().describe("Maximum receive unit"),
19740
+ mrru: z98.number().int().optional().describe("Max receive reconstructed unit for MP"),
19741
+ keepalive_timeout: z98.number().int().optional().describe("Seconds before an idle connection is considered down")
19283
19742
  },
19284
19743
  async handler(a, ctx) {
19285
19744
  ctx.info("Configuring SSTP server");
@@ -19301,27 +19760,27 @@ ${details}`;
19301
19760
  annotations: WRITE,
19302
19761
  description: "Create an outbound SSTP client tunnel interface (`/interface sstp-client add`) so this router dials out to a remote SSTP server over TLS. " + "Use when this device must act as a VPN client, not the VPN server \u2014 for server-side settings use `set_sstp_server`. " + "`connect_to` is the remote server address (IP or DNS name); the TCP port is separate. " + "You may pass the port in `port` or inline as `host:port` in `connect_to` (it is split out " + "automatically \u2014 RouterOS rejects a port embedded in connect-to). SSTP defaults to 443. " + "For L2TP outbound tunnels use `create_l2tp_client`, for OpenVPN use `create_ovpn_client`, for PPTP use `create_pptp_client`. " + "Credentials are accepted but redacted from return values. " + "Returns the created interface detail (name, status, remote address); use the interface `name` with `get_sstp_client` or `remove_sstp_client`.",
19303
19762
  inputSchema: {
19304
- name: z97.string().describe("Name for the new SSTP client interface"),
19305
- connect_to: z97.string().describe("Remote SSTP server address (IP or DNS name; host:port also accepted)"),
19306
- port: z97.number().int().optional().describe("TCP port (default 443 if omitted)"),
19307
- user: z97.string(),
19308
- password: z97.string(),
19309
- profile: z97.string().optional(),
19310
- certificate: z97.string().optional().describe("Client TLS certificate name"),
19311
- verify_server_certificate: z97.boolean().optional(),
19312
- authentication: z97.string().optional().describe("Comma-separated, e.g. 'mschap2,mschap1'"),
19313
- tls_version: z97.enum(["any", "only-1.2"]).optional(),
19314
- pfs: z97.boolean().optional().describe("Enable Perfect Forward Secrecy"),
19315
- add_default_route: z97.boolean().optional(),
19316
- default_route_distance: z97.number().int().optional().describe("Distance of the auto-added default route"),
19317
- dial_on_demand: z97.boolean().optional().describe("Connect only when traffic is sent over the tunnel"),
19318
- max_mtu: z97.number().int().optional().describe("Maximum transmission unit"),
19319
- max_mru: z97.number().int().optional().describe("Maximum receive unit"),
19320
- mrru: z97.number().int().optional().describe("Max receive reconstructed unit for MP"),
19321
- keepalive_timeout: z97.number().int().optional().describe("Seconds before an idle connection is considered down"),
19322
- http_proxy: z97.string().optional(),
19323
- comment: z97.string().optional(),
19324
- disabled: z97.boolean().default(false)
19763
+ name: z98.string().describe("Name for the new SSTP client interface"),
19764
+ connect_to: z98.string().describe("Remote SSTP server address (IP or DNS name; host:port also accepted)"),
19765
+ port: z98.number().int().optional().describe("TCP port (default 443 if omitted)"),
19766
+ user: z98.string(),
19767
+ password: z98.string(),
19768
+ profile: z98.string().optional(),
19769
+ certificate: z98.string().optional().describe("Client TLS certificate name"),
19770
+ verify_server_certificate: z98.boolean().optional(),
19771
+ authentication: z98.string().optional().describe("Comma-separated, e.g. 'mschap2,mschap1'"),
19772
+ tls_version: z98.enum(["any", "only-1.2"]).optional(),
19773
+ pfs: z98.boolean().optional().describe("Enable Perfect Forward Secrecy"),
19774
+ add_default_route: z98.boolean().optional(),
19775
+ default_route_distance: z98.number().int().optional().describe("Distance of the auto-added default route"),
19776
+ dial_on_demand: z98.boolean().optional().describe("Connect only when traffic is sent over the tunnel"),
19777
+ max_mtu: z98.number().int().optional().describe("Maximum transmission unit"),
19778
+ max_mru: z98.number().int().optional().describe("Maximum receive unit"),
19779
+ mrru: z98.number().int().optional().describe("Max receive reconstructed unit for MP"),
19780
+ keepalive_timeout: z98.number().int().optional().describe("Seconds before an idle connection is considered down"),
19781
+ http_proxy: z98.string().optional(),
19782
+ comment: z98.string().optional(),
19783
+ disabled: z98.boolean().default(false)
19325
19784
  },
19326
19785
  async handler(a, ctx) {
19327
19786
  ctx.info(`Creating SSTP client: name=${a.name}, connect_to=${a.connect_to}`);
@@ -19342,7 +19801,7 @@ ${redactSecrets(details)}` : "SSTP client creation completed but unable to verif
19342
19801
  annotations: READ,
19343
19802
  description: "List all outbound SSTP client tunnel interfaces (`/interface sstp-client print`), optionally narrowed by partial name match via `name_filter`. " + "Use to discover existing SSTP tunnels and their connection status before creating or removing one. " + "Passwords are redacted in the output. " + "For full detail on a single client use `get_sstp_client` with the interface name. " + "To inspect the inbound SSTP server config use `get_sstp_server`. " + "Returns a summary list of all matching SSTP client entries.",
19344
19803
  inputSchema: {
19345
- name_filter: z97.string().optional().describe("Partial name match")
19804
+ name_filter: z98.string().optional().describe("Partial name match")
19346
19805
  },
19347
19806
  async handler(a, ctx) {
19348
19807
  ctx.info("Listing SSTP clients");
@@ -19360,7 +19819,7 @@ ${redactSecrets(result)}`;
19360
19819
  title: "Get SSTP Client Interface Detail",
19361
19820
  annotations: READ,
19362
19821
  description: "Return full detail for one SSTP client interface (`/interface sstp-client print detail where name=...`) by interface name \u2014 includes status, remote server address, TLS certificate, PPP profile, and connection options; passwords are redacted. " + "Use when you need the complete property set for a single tunnel rather than the summary list. " + "Use `list_sstp_clients` first to discover valid interface names. " + "For the inbound server configuration use `get_sstp_server`. " + "Returns the full detail block for the named interface, or a not-found message.",
19363
- inputSchema: { name: z97.string() },
19822
+ inputSchema: { name: z98.string() },
19364
19823
  async handler(a, ctx) {
19365
19824
  ctx.info(`Getting SSTP client details: name=${a.name}`);
19366
19825
  const result = await executeMikrotikCommand(`/interface sstp-client print detail where name="${a.name}"`, ctx);
@@ -19374,7 +19833,7 @@ ${redactSecrets(result)}`;
19374
19833
  title: "Remove SSTP Client Interface",
19375
19834
  annotations: DESTRUCTIVE,
19376
19835
  description: "Permanently delete an SSTP client tunnel interface (`/interface sstp-client remove [find name=...]`) by interface name. " + "First verifies the interface exists (count-only check), then removes it; the tunnel is torn down immediately and the action is irreversible. " + "Use `list_sstp_clients` to confirm the interface name before calling this tool. " + "For L2TP, OpenVPN, or PPTP client interfaces see their respective tool scopes (`create_l2tp_client`, `create_ovpn_client`, `create_pptp_client`). " + "To disable the interface without deleting it, no dedicated enable/disable tool exists in this scope \u2014 set `disabled=yes` via RouterOS directly. " + "Returns a confirmation message on success or a not-found message if the name does not exist.",
19377
- inputSchema: { name: z97.string() },
19836
+ inputSchema: { name: z98.string() },
19378
19837
  async handler(a, ctx) {
19379
19838
  ctx.info(`Removing SSTP client: name=${a.name}`);
19380
19839
  const count = await executeMikrotikCommand(`/interface sstp-client print count-only where name="${a.name}"`, ctx);
@@ -19389,7 +19848,7 @@ ${redactSecrets(result)}`;
19389
19848
  ];
19390
19849
 
19391
19850
  // src/tools/switch-settings.ts
19392
- import { z as z98 } from "zod";
19851
+ import { z as z99 } from "zod";
19393
19852
  var switchSettingsTools = [
19394
19853
  defineTool({
19395
19854
  name: "list_switches",
@@ -19397,8 +19856,8 @@ var switchSettingsTools = [
19397
19856
  annotations: READ,
19398
19857
  description: "Lists hardware switch chips (`/interface ethernet switch`). " + "Use to discover switch chip names and types present on the device before targeting a chip with get_switch or update_switch. " + "Accepts optional `name_filter` and `type_filter` for partial-match filtering. " + "Returns chip name, type, and mirror/flow-control configuration for each matching entry; returns an empty message if none match.",
19399
19858
  inputSchema: {
19400
- name_filter: z98.string().optional().describe("Partial switch-name match"),
19401
- type_filter: z98.string().optional().describe("Partial switch-type match")
19859
+ name_filter: z99.string().optional().describe("Partial switch-name match"),
19860
+ type_filter: z99.string().optional().describe("Partial switch-type match")
19402
19861
  },
19403
19862
  async handler(a, ctx) {
19404
19863
  ctx.info("Listing switches");
@@ -19419,7 +19878,7 @@ ${result}`;
19419
19878
  annotations: READ,
19420
19879
  description: "Fetches full detail for a single hardware switch chip (`/interface ethernet switch print detail`). " + "Use to inspect the current cpu-flow-control flag and mirror-source, mirror-target, and mirror-egress settings before modifying them with update_switch. " + "`switch_id` accepts the chip name (e.g. 'switch1') or the RouterOS `.id` returned by list_switches \u2014 tries `.id` lookup first, then falls back to name lookup. " + "Returns detailed chip settings or a not-found message.",
19421
19880
  inputSchema: {
19422
- switch_id: z98.string().describe("Switch name (e.g. 'switch1') or RouterOS '.id'")
19881
+ switch_id: z99.string().describe("Switch name (e.g. 'switch1') or RouterOS '.id'")
19423
19882
  },
19424
19883
  async handler(a, ctx) {
19425
19884
  ctx.info(`Getting switch details: switch_id=${a.switch_id}`);
@@ -19443,14 +19902,14 @@ ${result}`;
19443
19902
 
19444
19903
  ` + "Returns updated switch details on success.",
19445
19904
  inputSchema: {
19446
- switch_id: z98.string().describe("Switch name (e.g. 'switch1') or RouterOS '.id'"),
19447
- name: z98.string().optional().describe("Rename the switch"),
19448
- cpu_flow_control: z98.boolean().optional(),
19449
- mirror_source: z98.string().optional().describe("Source port to mirror, or 'none'"),
19450
- mirror_target: z98.string().optional().describe("Monitor port, 'cpu', or 'none'"),
19451
- mirror_egress: z98.string().optional().describe("Egress mirror source port (newer chips), or 'none'"),
19452
- mirror_egress_target: z98.string().optional().describe("Egress mirror target port (88E6393X/88E6191X/88E6190 chips), or 'none'"),
19453
- switch_all_ports: z98.boolean().optional().describe("Switch all ports together (RB450G/RB435G/RB850Gx2 only)")
19905
+ switch_id: z99.string().describe("Switch name (e.g. 'switch1') or RouterOS '.id'"),
19906
+ name: z99.string().optional().describe("Rename the switch"),
19907
+ cpu_flow_control: z99.boolean().optional(),
19908
+ mirror_source: z99.string().optional().describe("Source port to mirror, or 'none'"),
19909
+ mirror_target: z99.string().optional().describe("Monitor port, 'cpu', or 'none'"),
19910
+ mirror_egress: z99.string().optional().describe("Egress mirror source port (newer chips), or 'none'"),
19911
+ mirror_egress_target: z99.string().optional().describe("Egress mirror target port (88E6393X/88E6191X/88E6190 chips), or 'none'"),
19912
+ switch_all_ports: z99.boolean().optional().describe("Switch all ports together (RB450G/RB435G/RB850Gx2 only)")
19454
19913
  },
19455
19914
  async handler(a, ctx) {
19456
19915
  ctx.info(`Updating switch: switch_id=${a.switch_id}`);
@@ -19473,9 +19932,9 @@ ${details}`;
19473
19932
  ];
19474
19933
 
19475
19934
  // src/tools/switch-port.ts
19476
- import { z as z99 } from "zod";
19477
- var VlanMode = z99.enum(["disabled", "optional", "enabled", "secure"]);
19478
- var VlanHeader = z99.enum(["leave-as-is", "always-strip", "add-if-missing"]);
19935
+ import { z as z100 } from "zod";
19936
+ var VlanMode = z100.enum(["disabled", "optional", "enabled", "secure"]);
19937
+ var VlanHeader = z100.enum(["leave-as-is", "always-strip", "add-if-missing"]);
19479
19938
  var switchPortTools = [
19480
19939
  defineTool({
19481
19940
  name: "list_switch_ports",
@@ -19483,8 +19942,8 @@ var switchPortTools = [
19483
19942
  annotations: READ,
19484
19943
  description: "List all switch chip ports (`/interface ethernet switch port print`) \u2014 the hardware-level " + "per-port VLAN configuration entries on RouterOS switch chips. " + "Use this to discover port names and their current vlan-mode, vlan-header, and " + "default-vlan-id (PVID) settings before updating them. " + "For a single port's full detail use get_switch_port; to change settings use update_switch_port. " + "Optional filters narrow results by partial port name or owning switch (e.g. 'switch1'). " + "Returns all matching port rows including VLAN mode, tag-header treatment, and PVID.",
19485
19944
  inputSchema: {
19486
- name_filter: z99.string().optional().describe("Partial port-name match"),
19487
- switch_filter: z99.string().optional().describe("Filter by owning switch, e.g. 'switch1'")
19945
+ name_filter: z100.string().optional().describe("Partial port-name match"),
19946
+ switch_filter: z100.string().optional().describe("Filter by owning switch, e.g. 'switch1'")
19488
19947
  },
19489
19948
  async handler(a, ctx) {
19490
19949
  ctx.info("Listing switch ports");
@@ -19505,7 +19964,7 @@ ${result}`;
19505
19964
  annotations: READ,
19506
19965
  description: "Retrieve detailed settings for a single switch chip port (`/interface ethernet switch port print detail`) " + "by port name (e.g. 'ether1') or RouterOS '.id' from list_switch_ports. " + "Use this when you need the full attribute set of one port before updating it; " + "for all ports use list_switch_ports; to change settings use update_switch_port. " + "Returns a detailed view of vlan-mode, vlan-header, default-vlan-id, and force-vlan-id for the matched port.",
19507
19966
  inputSchema: {
19508
- port_id: z99.string().describe("Port name (e.g. 'ether1') or RouterOS '.id'")
19967
+ port_id: z100.string().describe("Port name (e.g. 'ether1') or RouterOS '.id'")
19509
19968
  },
19510
19969
  async handler(a, ctx) {
19511
19970
  ctx.info(`Getting switch port details: port_id=${a.port_id}`);
@@ -19531,11 +19990,11 @@ ${result}`;
19531
19990
  ` + ` 'always-strip', or 'add-if-missing'.
19532
19991
  ` + " default_vlan_id: PVID for untagged ingress ('auto', 'none', or a number).",
19533
19992
  inputSchema: {
19534
- port_id: z99.string().describe("Port name (e.g. 'ether1') or RouterOS '.id'"),
19535
- default_vlan_id: z99.string().optional().describe("PVID: 'auto', 'none', or a VLAN id number"),
19993
+ port_id: z100.string().describe("Port name (e.g. 'ether1') or RouterOS '.id'"),
19994
+ default_vlan_id: z100.string().optional().describe("PVID: 'auto', 'none', or a VLAN id number"),
19536
19995
  vlan_mode: VlanMode.optional(),
19537
19996
  vlan_header: VlanHeader.optional(),
19538
- force_vlan_id: z99.boolean().optional()
19997
+ force_vlan_id: z100.boolean().optional()
19539
19998
  },
19540
19999
  async handler(a, ctx) {
19541
20000
  ctx.info(`Updating switch port: port_id=${a.port_id}`);
@@ -19557,7 +20016,7 @@ ${details}`;
19557
20016
  ];
19558
20017
 
19559
20018
  // src/tools/switch-port-isolation.ts
19560
- import { z as z100 } from "zod";
20019
+ import { z as z101 } from "zod";
19561
20020
  function selectorFor(id) {
19562
20021
  return id.startsWith("*") ? `.id="${id}"` : `port="${id}"`;
19563
20022
  }
@@ -19574,9 +20033,9 @@ var switchPortIsolationTools = [
19574
20033
  ` + ` port: source port to isolate, e.g. 'ether1'.
19575
20034
  ` + " forwarding_override_ports: comma-separated list of the ONLY ports this port " + " may forward to \u2014 all others are blocked in hardware.",
19576
20035
  inputSchema: {
19577
- port: z100.string().describe("Source port to isolate, e.g. 'ether1'"),
19578
- forwarding_override_ports: z100.string().describe("Comma-separated allowed destination ports"),
19579
- comment: z100.string().optional()
20036
+ port: z101.string().describe("Source port to isolate, e.g. 'ether1'"),
20037
+ forwarding_override_ports: z101.string().describe("Comma-separated allowed destination ports"),
20038
+ comment: z101.string().optional()
19580
20039
  },
19581
20040
  async handler(a, ctx) {
19582
20041
  ctx.info(`Adding switch port-isolation: port=${a.port}`);
@@ -19598,7 +20057,7 @@ ${details}` : "Switch port-isolation addition completed but unable to verify.";
19598
20057
 
19599
20058
  ` + "Returns a table of all matching entries; optionally filter by partial port name via port_filter.",
19600
20059
  inputSchema: {
19601
- port_filter: z100.string().optional().describe("Partial source-port match")
20060
+ port_filter: z101.string().optional().describe("Partial source-port match")
19602
20061
  },
19603
20062
  async handler(a, ctx) {
19604
20063
  ctx.info("Listing switch port-isolation entries");
@@ -19619,7 +20078,7 @@ ${result}`;
19619
20078
 
19620
20079
  ` + "Returns the full detail block for the matched entry, or a not-found message.",
19621
20080
  inputSchema: {
19622
- isolation_id: z100.string().describe("Source port name (e.g. 'ether1') or RouterOS '.id'")
20081
+ isolation_id: z101.string().describe("Source port name (e.g. 'ether1') or RouterOS '.id'")
19623
20082
  },
19624
20083
  async handler(a, ctx) {
19625
20084
  ctx.info(`Getting switch port-isolation: isolation_id=${a.isolation_id}`);
@@ -19639,9 +20098,9 @@ ${result}`;
19639
20098
 
19640
20099
  ` + "Returns the entry's updated detail block.",
19641
20100
  inputSchema: {
19642
- isolation_id: z100.string().describe("Source port name or RouterOS '.id'"),
19643
- forwarding_override_ports: z100.string().optional().describe("Comma-separated allowed destination ports"),
19644
- comment: z100.string().optional()
20101
+ isolation_id: z101.string().describe("Source port name or RouterOS '.id'"),
20102
+ forwarding_override_ports: z101.string().optional().describe("Comma-separated allowed destination ports"),
20103
+ comment: z101.string().optional()
19645
20104
  },
19646
20105
  async handler(a, ctx) {
19647
20106
  ctx.info(`Updating switch port-isolation: isolation_id=${a.isolation_id}`);
@@ -19670,7 +20129,7 @@ ${details}`;
19670
20129
 
19671
20130
  ` + "isolation_id accepts either the source port name (e.g. 'ether1') or the RouterOS `.id` " + "(e.g. '*1') from list_switch_port_isolation. Performs a count-only existence check before " + "removal and returns a not-found message if the entry does not exist. To add a new isolation rule " + "afterwards use add_switch_port_isolation; to only modify allowed ports use update_switch_port_isolation.",
19672
20131
  inputSchema: {
19673
- isolation_id: z100.string().describe("Source port name or RouterOS '.id'")
20132
+ isolation_id: z101.string().describe("Source port name or RouterOS '.id'")
19674
20133
  },
19675
20134
  async handler(a, ctx) {
19676
20135
  ctx.info(`Removing switch port-isolation: isolation_id=${a.isolation_id}`);
@@ -19687,7 +20146,7 @@ ${details}`;
19687
20146
  ];
19688
20147
 
19689
20148
  // src/tools/switch-rule.ts
19690
- import { z as z101 } from "zod";
20149
+ import { z as z102 } from "zod";
19691
20150
  var isDigits8 = (s) => /^\d+$/.test(s);
19692
20151
  async function updateSwitchRule(a, ctx) {
19693
20152
  ctx.info(`Updating switch rule: rule_id=${a.rule_id}`);
@@ -19754,32 +20213,32 @@ var switchRuleTools = [
19754
20213
  ` + ` rate: rate limit in bits/second.
19755
20214
  ` + " mac_protocol: e.g. 'ip', 'arp', 'vlan', or an EtherType number.",
19756
20215
  inputSchema: {
19757
- switch: z101.string().describe("Owning switch chip, e.g. 'switch1'"),
19758
- ports: z101.string().describe("Comma-separated source ports the rule matches"),
19759
- src_address: z101.string().optional().describe("Source IP/mask"),
19760
- dst_address: z101.string().optional().describe("Destination IP/mask"),
19761
- src_address6: z101.string().optional().describe("Source IPv6 address/mask"),
19762
- dst_address6: z101.string().optional().describe("Destination IPv6 address/mask"),
19763
- src_mac_address: z101.string().optional().describe("Source MAC/mask"),
19764
- dst_mac_address: z101.string().optional().describe("Destination MAC/mask"),
19765
- src_port: z101.string().optional().describe("Layer-4 source port(s)"),
19766
- dst_port: z101.string().optional().describe("Layer-4 destination port(s)"),
19767
- protocol: z101.string().optional().describe("IP protocol, e.g. 'tcp'"),
19768
- mac_protocol: z101.string().optional().describe("MAC protocol, e.g. 'ip', 'arp', 'vlan' or a number"),
19769
- vlan_header: z101.enum(["any", "not-present", "present"]).optional().describe("Match on VLAN tag presence"),
19770
- vlan_id: z101.string().optional(),
19771
- vlan_priority: z101.string().optional(),
19772
- dscp: z101.string().optional(),
19773
- flow_label: z101.string().optional().describe("IPv6 flow label"),
19774
- new_dst_ports: z101.string().optional().describe("Redirect target ports; empty string drops the traffic"),
19775
- new_vlan_id: z101.string().optional(),
19776
- new_vlan_priority: z101.string().optional(),
19777
- redirect_to_cpu: z101.boolean().optional(),
19778
- copy_to_cpu: z101.boolean().optional(),
19779
- mirror: z101.boolean().optional(),
19780
- rate: z101.string().optional().describe("Rate limit in bits/second"),
19781
- comment: z101.string().optional(),
19782
- disabled: z101.boolean().default(false)
20216
+ switch: z102.string().describe("Owning switch chip, e.g. 'switch1'"),
20217
+ ports: z102.string().describe("Comma-separated source ports the rule matches"),
20218
+ src_address: z102.string().optional().describe("Source IP/mask"),
20219
+ dst_address: z102.string().optional().describe("Destination IP/mask"),
20220
+ src_address6: z102.string().optional().describe("Source IPv6 address/mask"),
20221
+ dst_address6: z102.string().optional().describe("Destination IPv6 address/mask"),
20222
+ src_mac_address: z102.string().optional().describe("Source MAC/mask"),
20223
+ dst_mac_address: z102.string().optional().describe("Destination MAC/mask"),
20224
+ src_port: z102.string().optional().describe("Layer-4 source port(s)"),
20225
+ dst_port: z102.string().optional().describe("Layer-4 destination port(s)"),
20226
+ protocol: z102.string().optional().describe("IP protocol, e.g. 'tcp'"),
20227
+ mac_protocol: z102.string().optional().describe("MAC protocol, e.g. 'ip', 'arp', 'vlan' or a number"),
20228
+ vlan_header: z102.enum(["any", "not-present", "present"]).optional().describe("Match on VLAN tag presence"),
20229
+ vlan_id: z102.string().optional(),
20230
+ vlan_priority: z102.string().optional(),
20231
+ dscp: z102.string().optional(),
20232
+ flow_label: z102.string().optional().describe("IPv6 flow label"),
20233
+ new_dst_ports: z102.string().optional().describe("Redirect target ports; empty string drops the traffic"),
20234
+ new_vlan_id: z102.string().optional(),
20235
+ new_vlan_priority: z102.string().optional(),
20236
+ redirect_to_cpu: z102.boolean().optional(),
20237
+ copy_to_cpu: z102.boolean().optional(),
20238
+ mirror: z102.boolean().optional(),
20239
+ rate: z102.string().optional().describe("Rate limit in bits/second"),
20240
+ comment: z102.string().optional(),
20241
+ disabled: z102.boolean().default(false)
19783
20242
  },
19784
20243
  async handler(a, ctx) {
19785
20244
  ctx.info(`Adding switch rule: switch=${a.switch}, ports=${a.ports}`);
@@ -19813,9 +20272,9 @@ ${details}`;
19813
20272
  annotations: READ,
19814
20273
  description: "Lists all hardware switch ACL/redirect rules (`/interface ethernet switch rule print`) " + "on the switch chip. Use this to audit or discover existing rules before adding or updating. " + "Optional filters narrow by switch chip name, port name substring, or disabled-only status. " + "For software firewall rules use list_filter_rules; for NAT rules use list_nat_rules. " + "Returns a formatted table of all matching rules including their `.id` values needed by " + "get_switch_rule, update_switch_rule, remove_switch_rule, enable_switch_rule, and disable_switch_rule.",
19815
20274
  inputSchema: {
19816
- switch_filter: z101.string().optional(),
19817
- ports_filter: z101.string().optional(),
19818
- disabled_only: z101.boolean().default(false)
20275
+ switch_filter: z102.string().optional(),
20276
+ ports_filter: z102.string().optional(),
20277
+ disabled_only: z102.boolean().default(false)
19819
20278
  },
19820
20279
  async handler(a, ctx) {
19821
20280
  ctx.info("Listing switch rules");
@@ -19838,7 +20297,7 @@ ${result}`;
19838
20297
  annotations: READ,
19839
20298
  description: "Fetches full detail of a single hardware switch ACL/redirect rule " + "(`/interface ethernet switch rule print detail where .id=\u2026`). " + "Use this to inspect all match criteria and action fields of one rule. " + "`rule_id` takes the `.id` from list_switch_rules (e.g. '*1'). " + "For a tabular overview of all rules use list_switch_rules; " + "for software firewall rule detail use get_filter_rule.",
19840
20299
  inputSchema: {
19841
- rule_id: z101.string().describe("RouterOS '.id', e.g. '*1' or '0'")
20300
+ rule_id: z102.string().describe("RouterOS '.id', e.g. '*1' or '0'")
19842
20301
  },
19843
20302
  async handler(a, ctx) {
19844
20303
  ctx.info(`Getting switch rule: rule_id=${a.rule_id}`);
@@ -19854,33 +20313,33 @@ ${result}`;
19854
20313
  annotations: WRITE_IDEMPOTENT,
19855
20314
  description: "Modifies an existing hardware switch ACL/redirect rule " + "(`/interface ethernet switch rule set`) by `.id`. " + "Use this to change match criteria (ports, addresses, VLAN, protocol) or actions " + "(redirect target ports, mirror, rate limit, disabled state). " + 'Pass an empty string ("") for any optional field to clear it. ' + "`rule_id` takes the `.id` from list_switch_rules. Returns the updated rule's full detail. " + "For software firewall rule edits use update_filter_rule; " + "to only toggle the enabled state use enable_switch_rule or disable_switch_rule.",
19856
20315
  inputSchema: {
19857
- rule_id: z101.string(),
19858
- switch: z101.string().optional(),
19859
- ports: z101.string().optional(),
19860
- src_address: z101.string().optional(),
19861
- dst_address: z101.string().optional(),
19862
- src_address6: z101.string().optional(),
19863
- dst_address6: z101.string().optional(),
19864
- src_mac_address: z101.string().optional(),
19865
- dst_mac_address: z101.string().optional(),
19866
- src_port: z101.string().optional(),
19867
- dst_port: z101.string().optional(),
19868
- protocol: z101.string().optional(),
19869
- mac_protocol: z101.string().optional(),
19870
- vlan_header: z101.enum(["any", "not-present", "present"]).optional(),
19871
- vlan_id: z101.string().optional(),
19872
- vlan_priority: z101.string().optional(),
19873
- dscp: z101.string().optional(),
19874
- flow_label: z101.string().optional(),
19875
- new_dst_ports: z101.string().optional(),
19876
- new_vlan_id: z101.string().optional(),
19877
- new_vlan_priority: z101.string().optional(),
19878
- redirect_to_cpu: z101.boolean().optional(),
19879
- copy_to_cpu: z101.boolean().optional(),
19880
- mirror: z101.boolean().optional(),
19881
- rate: z101.string().optional(),
19882
- comment: z101.string().optional(),
19883
- disabled: z101.boolean().optional()
20316
+ rule_id: z102.string(),
20317
+ switch: z102.string().optional(),
20318
+ ports: z102.string().optional(),
20319
+ src_address: z102.string().optional(),
20320
+ dst_address: z102.string().optional(),
20321
+ src_address6: z102.string().optional(),
20322
+ dst_address6: z102.string().optional(),
20323
+ src_mac_address: z102.string().optional(),
20324
+ dst_mac_address: z102.string().optional(),
20325
+ src_port: z102.string().optional(),
20326
+ dst_port: z102.string().optional(),
20327
+ protocol: z102.string().optional(),
20328
+ mac_protocol: z102.string().optional(),
20329
+ vlan_header: z102.enum(["any", "not-present", "present"]).optional(),
20330
+ vlan_id: z102.string().optional(),
20331
+ vlan_priority: z102.string().optional(),
20332
+ dscp: z102.string().optional(),
20333
+ flow_label: z102.string().optional(),
20334
+ new_dst_ports: z102.string().optional(),
20335
+ new_vlan_id: z102.string().optional(),
20336
+ new_vlan_priority: z102.string().optional(),
20337
+ redirect_to_cpu: z102.boolean().optional(),
20338
+ copy_to_cpu: z102.boolean().optional(),
20339
+ mirror: z102.boolean().optional(),
20340
+ rate: z102.string().optional(),
20341
+ comment: z102.string().optional(),
20342
+ disabled: z102.boolean().optional()
19884
20343
  },
19885
20344
  async handler(a, ctx) {
19886
20345
  return updateSwitchRule(a, ctx);
@@ -19891,7 +20350,7 @@ ${result}`;
19891
20350
  title: "Remove Switch Chip ACL Rule",
19892
20351
  annotations: DESTRUCTIVE,
19893
20352
  description: "Permanently deletes a hardware switch ACL/redirect rule " + "(`/interface ethernet switch rule remove`) by `.id`. " + "Performs an existence check first and returns an error if the rule is not found. " + "`rule_id` takes the `.id` from list_switch_rules (e.g. '*1'). " + "To keep the rule but stop it from matching use disable_switch_rule instead. " + "For removing software firewall rules use remove_filter_rule.",
19894
- inputSchema: { rule_id: z101.string() },
20353
+ inputSchema: { rule_id: z102.string() },
19895
20354
  async handler(a, ctx) {
19896
20355
  ctx.info(`Removing switch rule: rule_id=${a.rule_id}`);
19897
20356
  const count = await executeMikrotikCommand(`/interface ethernet switch rule print count-only where .id=${a.rule_id}`, ctx);
@@ -19908,7 +20367,7 @@ ${result}`;
19908
20367
  title: "Enable Switch Chip ACL Rule",
19909
20368
  annotations: WRITE_IDEMPOTENT,
19910
20369
  description: "Re-enables a previously disabled hardware switch ACL/redirect rule " + "(`/interface ethernet switch rule set \u2026 disabled=no`). " + "Use this to activate a rule without recreating it. " + "`rule_id` takes the `.id` from list_switch_rules (e.g. '*1'). " + "To deactivate a rule without deleting it use disable_switch_rule; " + "to permanently delete use remove_switch_rule. Returns the updated rule's full detail.",
19911
- inputSchema: { rule_id: z101.string() },
20370
+ inputSchema: { rule_id: z102.string() },
19912
20371
  async handler(a, ctx) {
19913
20372
  return updateSwitchRule({ rule_id: a.rule_id, disabled: false }, ctx);
19914
20373
  }
@@ -19918,7 +20377,7 @@ ${result}`;
19918
20377
  title: "Disable Switch Chip ACL Rule",
19919
20378
  annotations: WRITE_IDEMPOTENT,
19920
20379
  description: "Deactivates a hardware switch ACL/redirect rule without deleting it " + "(`/interface ethernet switch rule set \u2026 disabled=yes`). " + "Use this to temporarily suspend a rule's match/action while preserving its configuration. " + "`rule_id` takes the `.id` from list_switch_rules (e.g. '*1'). " + "To reactivate the rule use enable_switch_rule; to permanently delete use remove_switch_rule. " + "Returns the updated rule's full detail.",
19921
- inputSchema: { rule_id: z101.string() },
20380
+ inputSchema: { rule_id: z102.string() },
19922
20381
  async handler(a, ctx) {
19923
20382
  return updateSwitchRule({ rule_id: a.rule_id, disabled: true }, ctx);
19924
20383
  }
@@ -19926,7 +20385,7 @@ ${result}`;
19926
20385
  ];
19927
20386
 
19928
20387
  // src/tools/system-config.ts
19929
- import { z as z102 } from "zod";
20388
+ import { z as z103 } from "zod";
19930
20389
  var systemConfigTools = [
19931
20390
  defineTool({
19932
20391
  name: "list_system_console",
@@ -19973,7 +20432,7 @@ ${result}`;
19973
20432
  annotations: WRITE_IDEMPOTENT,
19974
20433
  description: "Writes the global LED behaviour settings (`/system leds settings set`). " + "Use to enable dark mode by scheduling all LEDs off. " + "For per-LED trigger assignments use `list_leds`; to read current settings use `get_leds_settings`. " + "Accepts `all_leds_off`: `never` | `immediate` | `after-1h` | `after-1min`. " + "Returns the updated settings on success.",
19975
20434
  inputSchema: {
19976
- all_leds_off: z102.enum(["never", "immediate", "after-1h", "after-1min"]).optional().describe("When to turn all LEDs off (dark mode)")
20435
+ all_leds_off: z103.enum(["never", "immediate", "after-1h", "after-1min"]).optional().describe("When to turn all LEDs off (dark mode)")
19977
20436
  },
19978
20437
  async handler(a, ctx) {
19979
20438
  ctx.info("Setting LED settings");
@@ -20023,8 +20482,8 @@ ${result}`;
20023
20482
  annotations: WRITE_IDEMPOTENT,
20024
20483
  description: "Writes the system-wide login banner text (`/system note set`). " + "Use to set or clear the message displayed to users at login. " + "To read the current note use `get_note`. " + "Accepts optional `note` (text string) and `show_at_login` (boolean). " + "Returns the updated note on success.",
20025
20484
  inputSchema: {
20026
- note: z102.string().optional().describe("The note text to display"),
20027
- show_at_login: z102.boolean().optional().describe("Show the note on login")
20485
+ note: z103.string().optional().describe("The note text to display"),
20486
+ show_at_login: z103.boolean().optional().describe("Show the note on login")
20028
20487
  },
20029
20488
  async handler(a, ctx) {
20030
20489
  ctx.info("Setting system note");
@@ -20061,13 +20520,13 @@ ${result}`;
20061
20520
  annotations: WRITE_IDEMPOTENT,
20062
20521
  description: "Writes the device's built-in NTP *server* settings (`/system ntp server set`, RouterOS 7+). " + "Use to enable the router as a time server for LAN clients via broadcast, multicast, or manycast. " + "This tool configures the local NTP *server* role; to set upstream NTP *client* sources " + "use the NTP client tools in the system module. " + "Accepts `enabled`, `broadcast`, `multicast`, `manycast` (booleans) and `broadcast_address` (string). " + "Returns the updated server configuration on success.",
20063
20522
  inputSchema: {
20064
- enabled: z102.boolean().optional().describe("Enable or disable the NTP server"),
20065
- broadcast: z102.boolean().optional(),
20066
- multicast: z102.boolean().optional(),
20067
- manycast: z102.boolean().optional(),
20068
- broadcast_address: z102.string().optional().describe("Broadcast address for NTP broadcasts"),
20069
- use_local_clock: z102.boolean().optional().describe("Serve time from the device's local clock as reference"),
20070
- local_clock_stratum: z102.number().int().optional().describe("Stratum advertised when using the local clock (1-15)")
20523
+ enabled: z103.boolean().optional().describe("Enable or disable the NTP server"),
20524
+ broadcast: z103.boolean().optional(),
20525
+ multicast: z103.boolean().optional(),
20526
+ manycast: z103.boolean().optional(),
20527
+ broadcast_address: z103.string().optional().describe("Broadcast address for NTP broadcasts"),
20528
+ use_local_clock: z103.boolean().optional().describe("Serve time from the device's local clock as reference"),
20529
+ local_clock_stratum: z103.number().int().optional().describe("Stratum advertised when using the local clock (1-15)")
20071
20530
  },
20072
20531
  async handler(a, ctx) {
20073
20532
  ctx.info("Setting NTP server configuration");
@@ -20090,8 +20549,8 @@ ${details}`;
20090
20549
  annotations: WRITE,
20091
20550
  description: "Changes the password of the currently authenticated user (`/password`). " + "Use to rotate the login credential without touching other user accounts; " + "passwords are never echoed in the response. " + "To manage other users' accounts or create new users use `add_user`. " + "Requires `old_password` and `new_password`; returns success or a rejection message (no credentials logged).",
20092
20551
  inputSchema: {
20093
- old_password: z102.string().describe("The current password"),
20094
- new_password: z102.string().describe("The new password to set")
20552
+ old_password: z103.string().describe("The current password"),
20553
+ new_password: z103.string().describe("The new password to set")
20095
20554
  },
20096
20555
  async handler(a, ctx) {
20097
20556
  ctx.info("Changing device password");
@@ -20108,7 +20567,7 @@ ${details}`;
20108
20567
  annotations: READ,
20109
20568
  description: "Lists the physical serial ports registered on the device (`/port print`). " + "Use to discover available serial ports and their current line settings before reconfiguring. " + "For auto-login rules tied to a serial port use `list_special_login`; " + "for console session entries use `list_system_console`. " + "Accepts optional `name_filter` for partial name match. " + "Returns port entries or an empty message when none match.",
20110
20569
  inputSchema: {
20111
- name_filter: z102.string().optional().describe("Partial port name match")
20570
+ name_filter: z103.string().optional().describe("Partial port name match")
20112
20571
  },
20113
20572
  async handler(a, ctx) {
20114
20573
  ctx.info("Listing serial ports");
@@ -20127,7 +20586,7 @@ ${result}`;
20127
20586
  annotations: READ,
20128
20587
  description: "Reads detailed settings for a named serial port (`/port print detail where name=...`). " + "Use to inspect baud rate, parity, data/stop bits, and flow control for a specific port. " + "For an overview of all ports use `list_ports`; to change settings use `set_port`. " + "Requires `name` (e.g. `serial0`). Returns full detail for the named port.",
20129
20588
  inputSchema: {
20130
- name: z102.string().describe("Serial port name, e.g. 'serial0'")
20589
+ name: z103.string().describe("Serial port name, e.g. 'serial0'")
20131
20590
  },
20132
20591
  async handler(a, ctx) {
20133
20592
  ctx.info(`Getting serial port details: name=${a.name}`);
@@ -20143,12 +20602,12 @@ ${result}`;
20143
20602
  annotations: WRITE_IDEMPOTENT,
20144
20603
  description: "Writes line settings for a named serial port (`/port set [find name=...]`). " + "Use to change baud rate, data bits, parity, stop bits, or flow control on a serial port. " + "To read current settings first use `get_port`; for a list of all ports use `list_ports`. " + "Requires `name` (e.g. `serial0`), plus optional `baud_rate` (e.g. `115200` or `auto`), " + "`data_bits`, `parity` (`none`|`odd`|`even`), `stop_bits`, " + "`flow_control` (`none`|`hardware`|`xon-xoff`). " + "Returns the updated port detail on success.",
20145
20604
  inputSchema: {
20146
- name: z102.string().describe("Serial port name to update"),
20147
- baud_rate: z102.string().optional().describe("e.g. '115200' or 'auto'"),
20148
- data_bits: z102.number().int().optional(),
20149
- parity: z102.enum(["none", "odd", "even"]).optional(),
20150
- stop_bits: z102.number().int().optional(),
20151
- flow_control: z102.enum(["none", "hardware", "xon-xoff"]).optional()
20605
+ name: z103.string().describe("Serial port name to update"),
20606
+ baud_rate: z103.string().optional().describe("e.g. '115200' or 'auto'"),
20607
+ data_bits: z103.number().int().optional(),
20608
+ parity: z103.enum(["none", "odd", "even"]).optional(),
20609
+ stop_bits: z103.number().int().optional(),
20610
+ flow_control: z103.enum(["none", "hardware", "xon-xoff"]).optional()
20152
20611
  },
20153
20612
  async handler(a, ctx) {
20154
20613
  ctx.info(`Setting serial port: name=${a.name}`);
@@ -20187,12 +20646,12 @@ ${result}`;
20187
20646
  annotations: DANGEROUS,
20188
20647
  description: "Sends a factory-reset command to the device (`/system reset-configuration`). " + "Use ONLY to wipe the entire configuration and reboot into defaults \u2014 " + "the SSH connection will drop immediately after and all config will be lost. " + "Requires `confirm=true`; without it the command is blocked. " + "Optional: `keep_users` (preserve user accounts), `no_defaults` (skip loading default config), " + "`skip_backup` (skip automatic pre-reset backup), `caps_mode` (reset into CAPsMAN-managed CAP mode), " + "`run_after_reset` (script to execute post-reboot). " + "This operation is irreversible \u2014 there is no undo.",
20189
20648
  inputSchema: {
20190
- confirm: z102.boolean().describe("Must be true to actually ERASE the configuration"),
20191
- keep_users: z102.boolean().optional().describe("Keep existing user accounts after reset"),
20192
- no_defaults: z102.boolean().optional().describe("Do not load the default configuration"),
20193
- skip_backup: z102.boolean().optional().describe("Skip the automatic backup before reset"),
20194
- caps_mode: z102.boolean().optional().describe("Reset into CAPsMAN-managed CAP mode instead of standalone"),
20195
- run_after_reset: z102.string().optional().describe("Script file to run after reset")
20649
+ confirm: z103.boolean().describe("Must be true to actually ERASE the configuration"),
20650
+ keep_users: z103.boolean().optional().describe("Keep existing user accounts after reset"),
20651
+ no_defaults: z103.boolean().optional().describe("Do not load the default configuration"),
20652
+ skip_backup: z103.boolean().optional().describe("Skip the automatic backup before reset"),
20653
+ caps_mode: z103.boolean().optional().describe("Reset into CAPsMAN-managed CAP mode instead of standalone"),
20654
+ run_after_reset: z103.string().optional().describe("Script file to run after reset")
20196
20655
  },
20197
20656
  async handler(a, ctx) {
20198
20657
  if (!a.confirm)
@@ -20237,12 +20696,12 @@ ${result}`;
20237
20696
  annotations: WRITE_IDEMPOTENT,
20238
20697
  description: "Writes the hardware/software watchdog timer settings (`/system watchdog set`). " + "Use to enable automatic reboots when the device becomes unresponsive " + "(triggered when pings to `watch_address` fail). " + "To read current settings use `get_watchdog`. " + "Accepts `watchdog_timer` (enable HW watchdog), `watch_address` (IP to ping), " + "`ping_timeout` (e.g. `1m`), `no_ping_delay` (e.g. `5m`), " + "`automatic_supout` (generate diagnostic file on crash), " + "`auto_send_supout` (email the diagnostic). " + "Returns the updated watchdog configuration on success.",
20239
20698
  inputSchema: {
20240
- watchdog_timer: z102.boolean().optional().describe("Enable the hardware watchdog timer"),
20241
- watch_address: z102.string().optional().describe("Address to ping; reboot if unreachable"),
20242
- ping_timeout: z102.string().optional().describe("e.g. '1m'"),
20243
- no_ping_delay: z102.string().optional().describe("e.g. '5m'"),
20244
- automatic_supout: z102.boolean().optional().describe("Generate a supout.rif on software failure"),
20245
- auto_send_supout: z102.boolean().optional().describe("Email the generated supout.rif")
20699
+ watchdog_timer: z103.boolean().optional().describe("Enable the hardware watchdog timer"),
20700
+ watch_address: z103.string().optional().describe("Address to ping; reboot if unreachable"),
20701
+ ping_timeout: z103.string().optional().describe("e.g. '1m'"),
20702
+ no_ping_delay: z103.string().optional().describe("e.g. '5m'"),
20703
+ automatic_supout: z103.boolean().optional().describe("Generate a supout.rif on software failure"),
20704
+ auto_send_supout: z103.boolean().optional().describe("Email the generated supout.rif")
20246
20705
  },
20247
20706
  async handler(a, ctx) {
20248
20707
  ctx.info("Setting watchdog configuration");
@@ -20262,7 +20721,7 @@ ${details}`;
20262
20721
  ];
20263
20722
 
20264
20723
  // src/tools/system.ts
20265
- import { z as z103 } from "zod";
20724
+ import { z as z104 } from "zod";
20266
20725
  var systemTools = [
20267
20726
  defineTool({
20268
20727
  name: "get_system_identity",
@@ -20283,7 +20742,7 @@ ${result}`;
20283
20742
  annotations: WRITE,
20284
20743
  description: "Set the system hostname (`/system identity set`) \u2014 the name shown in neighbor discovery, " + "Winbox title bar, and SSH prompts. " + "To read the current value use `get_system_identity`. " + "Returns the updated identity after the change.",
20285
20744
  inputSchema: {
20286
- name: z103.string().describe("New system identity / hostname")
20745
+ name: z104.string().describe("New system identity / hostname")
20287
20746
  },
20288
20747
  async handler(a, ctx) {
20289
20748
  ctx.info(`Setting system identity: name=${a.name}`);
@@ -20355,10 +20814,10 @@ ${result}`;
20355
20814
  annotations: WRITE,
20356
20815
  description: "Set the system date, time, and/or time-zone (`/system clock set`). " + "Provide at least one of `time_zone_name` (e.g. `'Europe/Amsterdam'` or `'manual'`), " + "`date` (e.g. `'jun/19/2026'`), or `time` (e.g. `'13:45:00'`). " + "For automatic time synchronization configure NTP with `set_ntp_client`. " + "Returns the updated clock after the change.",
20357
20816
  inputSchema: {
20358
- time_zone_name: z103.string().optional().describe("e.g. 'Europe/Amsterdam' or 'manual'"),
20359
- date: z103.string().optional().describe("e.g. 'jun/19/2026'"),
20360
- time: z103.string().optional().describe("e.g. '13:45:00'"),
20361
- time_zone_autodetect: z103.boolean().optional().describe("Auto-detect the time zone from the public IP")
20817
+ time_zone_name: z104.string().optional().describe("e.g. 'Europe/Amsterdam' or 'manual'"),
20818
+ date: z104.string().optional().describe("e.g. 'jun/19/2026'"),
20819
+ time: z104.string().optional().describe("e.g. '13:45:00'"),
20820
+ time_zone_autodetect: z104.boolean().optional().describe("Auto-detect the time zone from the public IP")
20362
20821
  },
20363
20822
  async handler(a, ctx) {
20364
20823
  ctx.info("Setting system clock");
@@ -20393,10 +20852,10 @@ ${result}`;
20393
20852
  annotations: WRITE,
20394
20853
  description: "Configure the NTP client (`/system ntp client set`) \u2014 enable or disable it " + "and set the server list as a comma-separated string (e.g. `'0.pool.ntp.org,1.pool.ntp.org'`). " + "To read current NTP status use `get_ntp_client`; " + "to set the clock manually instead use `set_system_clock`. " + "Returns the updated NTP client configuration after the change.",
20395
20854
  inputSchema: {
20396
- enabled: z103.boolean().optional().describe("Enable or disable the NTP client"),
20397
- servers: z103.string().optional().describe("Comma-separated NTP server list"),
20398
- mode: z103.enum(["unicast", "broadcast", "multicast", "manycast"]).optional().describe("NTP client operating mode"),
20399
- vrf: z103.string().optional().describe("VRF the NTP client operates in (e.g. 'main')")
20855
+ enabled: z104.boolean().optional().describe("Enable or disable the NTP client"),
20856
+ servers: z104.string().optional().describe("Comma-separated NTP server list"),
20857
+ mode: z104.enum(["unicast", "broadcast", "multicast", "manycast"]).optional().describe("NTP client operating mode"),
20858
+ vrf: z104.string().optional().describe("VRF the NTP client operates in (e.g. 'main')")
20400
20859
  },
20401
20860
  async handler(a, ctx) {
20402
20861
  ctx.info("Setting NTP client configuration");
@@ -20455,7 +20914,7 @@ ${result}`;
20455
20914
  annotations: DANGEROUS,
20456
20915
  description: "Send a reboot command to the device (`/system reboot`). " + "This is a high-blast-radius action: all connections drop and the device is offline for " + "1-2 minutes while it restarts. " + "Must pass `confirm=true` or the command is rejected without touching the device. " + "For a permanent power-off use `shutdown_system`.",
20457
20916
  inputSchema: {
20458
- confirm: z103.boolean().describe("Must be true to actually reboot the device")
20917
+ confirm: z104.boolean().describe("Must be true to actually reboot the device")
20459
20918
  },
20460
20919
  async handler(a, ctx) {
20461
20920
  if (!a.confirm)
@@ -20471,7 +20930,7 @@ ${result}`;
20471
20930
  annotations: DANGEROUS,
20472
20931
  description: "Power off the device (`/system shutdown`). " + "This is a high-blast-radius action: all connections drop and the device remains offline " + "until physically powered on again. " + "Must pass `confirm=true` or the command is rejected without touching the device. " + "For a temporary restart use `reboot_system`.",
20473
20932
  inputSchema: {
20474
- confirm: z103.boolean().describe("Must be true to actually shut down the device")
20933
+ confirm: z104.boolean().describe("Must be true to actually shut down the device")
20475
20934
  },
20476
20935
  async handler(a, ctx) {
20477
20936
  if (!a.confirm)
@@ -20484,10 +20943,10 @@ ${result}`;
20484
20943
  ];
20485
20944
 
20486
20945
  // src/tools/tunnels.ts
20487
- import { z as z104 } from "zod";
20488
- var DontFragment = z104.enum(["inherit", "no"]);
20489
- var Arp = z104.enum(["disabled", "enabled", "local-proxy-arp", "proxy-arp", "reply-only"]);
20490
- var VtepsIpVersion = z104.enum(["ipv4", "ipv6"]);
20946
+ import { z as z105 } from "zod";
20947
+ var DontFragment = z105.enum(["inherit", "no"]);
20948
+ var Arp = z105.enum(["disabled", "enabled", "local-proxy-arp", "proxy-arp", "reply-only"]);
20949
+ var VtepsIpVersion = z105.enum(["ipv4", "ipv6"]);
20491
20950
  var tunnelTools = [
20492
20951
  defineTool({
20493
20952
  name: "create_gre_tunnel",
@@ -20495,18 +20954,18 @@ var tunnelTools = [
20495
20954
  annotations: WRITE,
20496
20955
  description: "Creates an IPv4 GRE (Generic Routing Encapsulation) L3 tunnel interface (`/interface gre`)." + " Use to encapsulate routed traffic between two endpoints for site-to-site connectivity when IPsec encryption is not needed." + " GRE is L3-only and not bridgeable; for bridgeable L2 Ethernet-over-IP tunnels use create_eoip_tunnel;" + " for raw IP-in-IP with less overhead use create_ipip_tunnel; for L2 VXLAN overlays use create_vxlan_tunnel;" + " for VPN client interfaces use create_l2tp_client, create_pptp_client, create_sstp_client, or create_ovpn_client." + " Keepalive format: '<interval>,<retries>', e.g. '10s,3'." + " Returns the created interface detail including name, remote-address, and run-time status.",
20497
20956
  inputSchema: {
20498
- name: z104.string().describe("Name for the new GRE tunnel interface, e.g. 'gre-to-hq'"),
20499
- remote_address: z104.string().describe("Remote endpoint IP address"),
20500
- local_address: z104.string().optional().describe("Local endpoint IP address"),
20501
- keepalive: z104.string().optional().describe("Keepalive interval/retries, e.g. '10s,3'"),
20957
+ name: z105.string().describe("Name for the new GRE tunnel interface, e.g. 'gre-to-hq'"),
20958
+ remote_address: z105.string().describe("Remote endpoint IP address"),
20959
+ local_address: z105.string().optional().describe("Local endpoint IP address"),
20960
+ keepalive: z105.string().optional().describe("Keepalive interval/retries, e.g. '10s,3'"),
20502
20961
  dont_fragment: DontFragment.optional().describe("Don't-fragment behavior"),
20503
- clamp_tcp_mss: z104.boolean().optional().describe("Clamp TCP MSS to the tunnel MTU"),
20504
- allow_fast_path: z104.boolean().optional().describe("Allow FastPath processing for this tunnel"),
20505
- ipsec_secret: z104.string().optional().describe("Pre-shared key to auto-create an IPsec policy securing the tunnel"),
20506
- dscp: z104.string().optional().describe("DSCP for encapsulated packets: 'inherit' or 0-63"),
20507
- mtu: z104.number().int().optional(),
20508
- comment: z104.string().optional(),
20509
- disabled: z104.boolean().default(false)
20962
+ clamp_tcp_mss: z105.boolean().optional().describe("Clamp TCP MSS to the tunnel MTU"),
20963
+ allow_fast_path: z105.boolean().optional().describe("Allow FastPath processing for this tunnel"),
20964
+ ipsec_secret: z105.string().optional().describe("Pre-shared key to auto-create an IPsec policy securing the tunnel"),
20965
+ dscp: z105.string().optional().describe("DSCP for encapsulated packets: 'inherit' or 0-63"),
20966
+ mtu: z105.number().int().optional(),
20967
+ comment: z105.string().optional(),
20968
+ disabled: z105.boolean().default(false)
20510
20969
  },
20511
20970
  async handler(a, ctx) {
20512
20971
  ctx.info(`Creating GRE tunnel: name=${a.name}, remote_address=${a.remote_address}`);
@@ -20526,7 +20985,7 @@ ${details}` : "GRE tunnel creation completed but unable to verify.";
20526
20985
  annotations: READ,
20527
20986
  description: "Lists all GRE tunnel interfaces (`/interface gre print`)." + " Use to inventory or audit existing GRE tunnels and their remote endpoints." + " Supports optional partial name filter via name_filter." + " For full detail on one tunnel use get_gre_tunnel; for IPIP tunnels use list_ipip_tunnels;" + " for EoIP tunnels use list_eoip_tunnels; for VXLAN use list_vxlan_tunnels." + " Returns name, remote-address, local-address, MTU, and run-time status for each interface.",
20528
20987
  inputSchema: {
20529
- name_filter: z104.string().optional().describe("Partial name match")
20988
+ name_filter: z105.string().optional().describe("Partial name match")
20530
20989
  },
20531
20990
  async handler(a, ctx) {
20532
20991
  ctx.info("Listing GRE tunnels");
@@ -20544,7 +21003,7 @@ ${result}`;
20544
21003
  title: "Get GRE Tunnel Interface Detail",
20545
21004
  annotations: READ,
20546
21005
  description: "Fetches full detail for a single GRE tunnel interface by name (`/interface gre print detail where name=...`)." + " Use to inspect all parameters of one tunnel \u2014 remote-address, local-address, keepalive, MTU, dont-fragment, and status." + " For a summary list of all GRE tunnels use list_gre_tunnels." + " For IPIP detail use get_ipip_tunnel; for EoIP detail use get_eoip_tunnel; for VXLAN detail use get_vxlan_tunnel." + " Returns the complete property set for the named interface, or a not-found message.",
20547
- inputSchema: { name: z104.string() },
21006
+ inputSchema: { name: z105.string() },
20548
21007
  async handler(a, ctx) {
20549
21008
  ctx.info(`Getting GRE tunnel details: name=${a.name}`);
20550
21009
  const result = await executeMikrotikCommand(`/interface gre print detail where name="${a.name}"`, ctx);
@@ -20558,7 +21017,7 @@ ${result}`;
20558
21017
  title: "Remove GRE Tunnel Interface",
20559
21018
  annotations: DESTRUCTIVE,
20560
21019
  description: "Permanently deletes a GRE tunnel interface by name (`/interface gre remove [find name=...]`)." + " Verifies existence via count-only before removal and returns a not-found message if the interface does not exist." + " This is destructive and immediately disconnects any traffic using the tunnel." + " For IPIP removal use remove_ipip_tunnel; for EoIP use remove_eoip_tunnel; for VXLAN use remove_vxlan_tunnel.",
20561
- inputSchema: { name: z104.string() },
21020
+ inputSchema: { name: z105.string() },
20562
21021
  async handler(a, ctx) {
20563
21022
  ctx.info(`Removing GRE tunnel: name=${a.name}`);
20564
21023
  const count = await executeMikrotikCommand(`/interface gre print count-only where name="${a.name}"`, ctx);
@@ -20576,18 +21035,18 @@ ${result}`;
20576
21035
  annotations: WRITE,
20577
21036
  description: "Creates an IPv4 IPIP (IP-in-IP) L3 tunnel interface (`/interface ipip`)." + " Use for lightweight point-to-point IP encapsulation with minimal overhead when GRE's extra header byte is undesirable." + " IPIP is L3-only and not bridgeable; for GRE encapsulation (with dont-fragment support) use create_gre_tunnel;" + " for L2 Ethernet-over-IP tunnels use create_eoip_tunnel; for VXLAN L2 overlays use create_vxlan_tunnel." + " Keepalive format: '<interval>,<retries>', e.g. '10s,3'." + " Returns the created interface detail including name, remote-address, and run-time status.",
20578
21037
  inputSchema: {
20579
- name: z104.string().describe("Name for the new IPIP tunnel interface, e.g. 'ipip-to-hq'"),
20580
- remote_address: z104.string().describe("Remote endpoint IP address"),
20581
- local_address: z104.string().optional().describe("Local endpoint IP address"),
20582
- keepalive: z104.string().optional().describe("Keepalive interval/retries, e.g. '10s,3'"),
21038
+ name: z105.string().describe("Name for the new IPIP tunnel interface, e.g. 'ipip-to-hq'"),
21039
+ remote_address: z105.string().describe("Remote endpoint IP address"),
21040
+ local_address: z105.string().optional().describe("Local endpoint IP address"),
21041
+ keepalive: z105.string().optional().describe("Keepalive interval/retries, e.g. '10s,3'"),
20583
21042
  dont_fragment: DontFragment.optional().describe("Don't-fragment behavior"),
20584
- clamp_tcp_mss: z104.boolean().optional().describe("Clamp TCP MSS to the tunnel MTU"),
20585
- allow_fast_path: z104.boolean().optional().describe("Allow FastPath processing for this tunnel"),
20586
- ipsec_secret: z104.string().optional().describe("Pre-shared key to auto-create an IPsec policy securing the tunnel"),
20587
- dscp: z104.string().optional().describe("DSCP for encapsulated packets: 'inherit' or 0-63"),
20588
- mtu: z104.number().int().optional(),
20589
- comment: z104.string().optional(),
20590
- disabled: z104.boolean().default(false)
21043
+ clamp_tcp_mss: z105.boolean().optional().describe("Clamp TCP MSS to the tunnel MTU"),
21044
+ allow_fast_path: z105.boolean().optional().describe("Allow FastPath processing for this tunnel"),
21045
+ ipsec_secret: z105.string().optional().describe("Pre-shared key to auto-create an IPsec policy securing the tunnel"),
21046
+ dscp: z105.string().optional().describe("DSCP for encapsulated packets: 'inherit' or 0-63"),
21047
+ mtu: z105.number().int().optional(),
21048
+ comment: z105.string().optional(),
21049
+ disabled: z105.boolean().default(false)
20591
21050
  },
20592
21051
  async handler(a, ctx) {
20593
21052
  ctx.info(`Creating IPIP tunnel: name=${a.name}, remote_address=${a.remote_address}`);
@@ -20607,7 +21066,7 @@ ${details}` : "IPIP tunnel creation completed but unable to verify.";
20607
21066
  annotations: READ,
20608
21067
  description: "Lists all IPIP tunnel interfaces (`/interface ipip print`)." + " Use to inventory existing IPIP tunnels and their remote endpoints." + " Supports optional partial name filter via name_filter." + " For full detail on one tunnel use get_ipip_tunnel; for GRE tunnels use list_gre_tunnels;" + " for EoIP tunnels use list_eoip_tunnels; for VXLAN use list_vxlan_tunnels." + " Returns name, remote-address, local-address, MTU, and run-time status for each interface.",
20609
21068
  inputSchema: {
20610
- name_filter: z104.string().optional().describe("Partial name match")
21069
+ name_filter: z105.string().optional().describe("Partial name match")
20611
21070
  },
20612
21071
  async handler(a, ctx) {
20613
21072
  ctx.info("Listing IPIP tunnels");
@@ -20625,7 +21084,7 @@ ${result}`;
20625
21084
  title: "Get IPIP Tunnel Interface Detail",
20626
21085
  annotations: READ,
20627
21086
  description: "Fetches full detail for a single IPIP tunnel interface by name (`/interface ipip print detail where name=...`)." + " Use to inspect all parameters of one tunnel \u2014 remote-address, local-address, keepalive, MTU, and status." + " For a summary list of all IPIP tunnels use list_ipip_tunnels." + " For GRE detail use get_gre_tunnel; for EoIP detail use get_eoip_tunnel; for VXLAN detail use get_vxlan_tunnel." + " Returns the complete property set for the named interface, or a not-found message.",
20628
- inputSchema: { name: z104.string() },
21087
+ inputSchema: { name: z105.string() },
20629
21088
  async handler(a, ctx) {
20630
21089
  ctx.info(`Getting IPIP tunnel details: name=${a.name}`);
20631
21090
  const result = await executeMikrotikCommand(`/interface ipip print detail where name="${a.name}"`, ctx);
@@ -20639,7 +21098,7 @@ ${result}`;
20639
21098
  title: "Remove IPIP Tunnel Interface",
20640
21099
  annotations: DESTRUCTIVE,
20641
21100
  description: "Permanently deletes an IPIP tunnel interface by name (`/interface ipip remove [find name=...]`)." + " Verifies existence via count-only before removal and returns a not-found message if the interface does not exist." + " This is destructive and immediately disconnects any traffic using the tunnel." + " For GRE removal use remove_gre_tunnel; for EoIP use remove_eoip_tunnel; for VXLAN use remove_vxlan_tunnel.",
20642
- inputSchema: { name: z104.string() },
21101
+ inputSchema: { name: z105.string() },
20643
21102
  async handler(a, ctx) {
20644
21103
  ctx.info(`Removing IPIP tunnel: name=${a.name}`);
20645
21104
  const count = await executeMikrotikCommand(`/interface ipip print count-only where name="${a.name}"`, ctx);
@@ -20657,22 +21116,22 @@ ${result}`;
20657
21116
  annotations: WRITE,
20658
21117
  description: "Creates an EoIP (Ethernet over IP) L2 tunnel interface (`/interface eoip`)." + " Use when you need a bridgeable L2 link between two MikroTik devices \u2014 traffic appears as raw Ethernet frames over IP." + " Each tunnel requires a unique tunnel_id (0-65535) that must match identically on both peers; mismatched IDs are the most common misconfiguration." + " EoIP is MikroTik-proprietary \u2014 both endpoints must run RouterOS." + " For L3-only encapsulation use create_gre_tunnel or create_ipip_tunnel; for open-standard L2 overlays use create_vxlan_tunnel." + " Keepalive format: '<interval>,<retries>', e.g. '10s,3'." + " Returns the created interface detail including name, remote-address, tunnel-id, and run-time status.",
20659
21118
  inputSchema: {
20660
- name: z104.string().describe("Name for the new EoIP tunnel interface, e.g. 'eoip-to-hq'"),
20661
- remote_address: z104.string().describe("Remote endpoint IP address"),
20662
- tunnel_id: z104.number().int().describe("Unique tunnel ID, must match on both peers"),
20663
- local_address: z104.string().optional().describe("Local endpoint IP address"),
20664
- keepalive: z104.string().optional().describe("Keepalive interval/retries, e.g. '10s,3'"),
21119
+ name: z105.string().describe("Name for the new EoIP tunnel interface, e.g. 'eoip-to-hq'"),
21120
+ remote_address: z105.string().describe("Remote endpoint IP address"),
21121
+ tunnel_id: z105.number().int().describe("Unique tunnel ID, must match on both peers"),
21122
+ local_address: z105.string().optional().describe("Local endpoint IP address"),
21123
+ keepalive: z105.string().optional().describe("Keepalive interval/retries, e.g. '10s,3'"),
20665
21124
  dont_fragment: DontFragment.optional().describe("Don't-fragment behavior"),
20666
- clamp_tcp_mss: z104.boolean().optional().describe("Clamp TCP MSS to the tunnel MTU"),
20667
- allow_fast_path: z104.boolean().optional().describe("Allow FastPath processing for this tunnel"),
20668
- ipsec_secret: z104.string().optional().describe("Pre-shared key to auto-create an IPsec policy securing the tunnel"),
20669
- dscp: z104.string().optional().describe("DSCP for encapsulated packets: 'inherit' or 0-63"),
20670
- mac_address: z104.string().optional().describe("MAC address of the EoIP interface"),
21125
+ clamp_tcp_mss: z105.boolean().optional().describe("Clamp TCP MSS to the tunnel MTU"),
21126
+ allow_fast_path: z105.boolean().optional().describe("Allow FastPath processing for this tunnel"),
21127
+ ipsec_secret: z105.string().optional().describe("Pre-shared key to auto-create an IPsec policy securing the tunnel"),
21128
+ dscp: z105.string().optional().describe("DSCP for encapsulated packets: 'inherit' or 0-63"),
21129
+ mac_address: z105.string().optional().describe("MAC address of the EoIP interface"),
20671
21130
  arp: Arp.optional().describe("Address Resolution Protocol mode for the interface"),
20672
- arp_timeout: z104.string().optional().describe("ARP entry timeout, e.g. '30s' or 'auto'"),
20673
- mtu: z104.number().int().optional(),
20674
- comment: z104.string().optional(),
20675
- disabled: z104.boolean().default(false)
21131
+ arp_timeout: z105.string().optional().describe("ARP entry timeout, e.g. '30s' or 'auto'"),
21132
+ mtu: z105.number().int().optional(),
21133
+ comment: z105.string().optional(),
21134
+ disabled: z105.boolean().default(false)
20676
21135
  },
20677
21136
  async handler(a, ctx) {
20678
21137
  ctx.info(`Creating EoIP tunnel: name=${a.name}, remote_address=${a.remote_address}, tunnel_id=${a.tunnel_id}`);
@@ -20692,7 +21151,7 @@ ${details}` : "EoIP tunnel creation completed but unable to verify.";
20692
21151
  annotations: READ,
20693
21152
  description: "Lists all EoIP tunnel interfaces (`/interface eoip print`)." + " Use to inventory existing EoIP tunnels and check their tunnel-id assignments to detect duplicates or mismatches." + " Supports optional partial name filter via name_filter." + " For full detail on one tunnel use get_eoip_tunnel; for GRE tunnels use list_gre_tunnels;" + " for IPIP tunnels use list_ipip_tunnels; for VXLAN use list_vxlan_tunnels." + " Returns name, remote-address, tunnel-id, MTU, and run-time status for each interface.",
20694
21153
  inputSchema: {
20695
- name_filter: z104.string().optional().describe("Partial name match")
21154
+ name_filter: z105.string().optional().describe("Partial name match")
20696
21155
  },
20697
21156
  async handler(a, ctx) {
20698
21157
  ctx.info("Listing EoIP tunnels");
@@ -20710,7 +21169,7 @@ ${result}`;
20710
21169
  title: "Get EoIP Tunnel Interface Detail",
20711
21170
  annotations: READ,
20712
21171
  description: "Fetches full detail for a single EoIP tunnel interface by name (`/interface eoip print detail where name=...`)." + " Use to inspect the tunnel-id, remote-address, local-address, keepalive, MTU, and status of one tunnel." + " For a summary list of all EoIP tunnels use list_eoip_tunnels." + " For GRE detail use get_gre_tunnel; for IPIP detail use get_ipip_tunnel; for VXLAN detail use get_vxlan_tunnel." + " Returns the complete property set for the named interface, or a not-found message.",
20713
- inputSchema: { name: z104.string() },
21172
+ inputSchema: { name: z105.string() },
20714
21173
  async handler(a, ctx) {
20715
21174
  ctx.info(`Getting EoIP tunnel details: name=${a.name}`);
20716
21175
  const result = await executeMikrotikCommand(`/interface eoip print detail where name="${a.name}"`, ctx);
@@ -20724,7 +21183,7 @@ ${result}`;
20724
21183
  title: "Remove EoIP Tunnel Interface",
20725
21184
  annotations: DESTRUCTIVE,
20726
21185
  description: "Permanently deletes an EoIP tunnel interface by name (`/interface eoip remove [find name=...]`)." + " Verifies existence via count-only before removal and returns a not-found message if the interface does not exist." + " This is destructive and immediately severs the L2 bridge link using this tunnel." + " For GRE removal use remove_gre_tunnel; for IPIP use remove_ipip_tunnel; for VXLAN use remove_vxlan_tunnel.",
20727
- inputSchema: { name: z104.string() },
21186
+ inputSchema: { name: z105.string() },
20728
21187
  async handler(a, ctx) {
20729
21188
  ctx.info(`Removing EoIP tunnel: name=${a.name}`);
20730
21189
  const count = await executeMikrotikCommand(`/interface eoip print count-only where name="${a.name}"`, ctx);
@@ -20742,21 +21201,21 @@ ${result}`;
20742
21201
  annotations: WRITE,
20743
21202
  description: "Creates a VXLAN (Virtual Extensible LAN) L2 overlay interface (`/interface vxlan`)." + " Use to build scalable L2 overlays across L3 networks \u2014 the VNI (VXLAN Network Identifier) scopes the broadcast domain, suited for multi-tenant or data-centre scenarios." + " VXLAN is an open standard and works with non-MikroTik peers; for MikroTik-proprietary L2 tunnels use create_eoip_tunnel;" + " for L3-only encapsulation use create_gre_tunnel or create_ipip_tunnel." + " UDP port defaults to 8472; VNI must match on all participating VTEPs." + " Returns the created interface detail including name, VNI, port, and run-time status.",
20744
21203
  inputSchema: {
20745
- name: z104.string().describe("Name for the new VXLAN interface, e.g. 'vxlan1'"),
20746
- vni: z104.number().int().describe("VXLAN Network Identifier (VNI)"),
20747
- port: z104.number().int().default(8472).describe("UDP port (default 8472)"),
20748
- local_address: z104.string().optional().describe("Local source IP address"),
20749
- interface: z104.string().optional().describe("Source interface"),
20750
- group: z104.string().optional().describe("Multicast group address for broadcast/unknown-unicast flooding"),
21204
+ name: z105.string().describe("Name for the new VXLAN interface, e.g. 'vxlan1'"),
21205
+ vni: z105.number().int().describe("VXLAN Network Identifier (VNI)"),
21206
+ port: z105.number().int().default(8472).describe("UDP port (default 8472)"),
21207
+ local_address: z105.string().optional().describe("Local source IP address"),
21208
+ interface: z105.string().optional().describe("Source interface"),
21209
+ group: z105.string().optional().describe("Multicast group address for broadcast/unknown-unicast flooding"),
20751
21210
  vteps_ip_version: VtepsIpVersion.optional().describe("IP version used for VTEP addressing"),
20752
- mac_address: z104.string().optional().describe("MAC address of the VXLAN interface"),
21211
+ mac_address: z105.string().optional().describe("MAC address of the VXLAN interface"),
20753
21212
  arp: Arp.optional().describe("Address Resolution Protocol mode for the interface"),
20754
- arp_timeout: z104.string().optional().describe("ARP entry timeout, e.g. '30s' or 'auto'"),
20755
- max_fdb_size: z104.number().int().optional().describe("Maximum forwarding database (FDB) size"),
20756
- allow_fast_path: z104.boolean().optional().describe("Allow FastPath processing for this interface"),
20757
- mtu: z104.number().int().optional(),
20758
- comment: z104.string().optional(),
20759
- disabled: z104.boolean().default(false)
21213
+ arp_timeout: z105.string().optional().describe("ARP entry timeout, e.g. '30s' or 'auto'"),
21214
+ max_fdb_size: z105.number().int().optional().describe("Maximum forwarding database (FDB) size"),
21215
+ allow_fast_path: z105.boolean().optional().describe("Allow FastPath processing for this interface"),
21216
+ mtu: z105.number().int().optional(),
21217
+ comment: z105.string().optional(),
21218
+ disabled: z105.boolean().default(false)
20760
21219
  },
20761
21220
  async handler(a, ctx) {
20762
21221
  ctx.info(`Creating VXLAN tunnel: name=${a.name}, vni=${a.vni}`);
@@ -20776,7 +21235,7 @@ ${details}` : "VXLAN tunnel creation completed but unable to verify.";
20776
21235
  annotations: READ,
20777
21236
  description: "Lists all VXLAN interfaces (`/interface vxlan print`)." + " Use to inventory existing VXLAN overlays and check their VNI and UDP port assignments." + " Supports optional partial name filter via name_filter." + " For full detail on one interface use get_vxlan_tunnel; for EoIP tunnels use list_eoip_tunnels;" + " for GRE tunnels use list_gre_tunnels; for IPIP tunnels use list_ipip_tunnels." + " Returns name, VNI, port, local-address, MTU, and run-time status for each interface.",
20778
21237
  inputSchema: {
20779
- name_filter: z104.string().optional().describe("Partial name match")
21238
+ name_filter: z105.string().optional().describe("Partial name match")
20780
21239
  },
20781
21240
  async handler(a, ctx) {
20782
21241
  ctx.info("Listing VXLAN tunnels");
@@ -20794,7 +21253,7 @@ ${result}`;
20794
21253
  title: "Get VXLAN Tunnel Interface Detail",
20795
21254
  annotations: READ,
20796
21255
  description: "Fetches full detail for a single VXLAN interface by name (`/interface vxlan print detail where name=...`)." + " Use to inspect the VNI, UDP port, local-address, source interface, MTU, and status of one VXLAN interface." + " For a summary list of all VXLAN interfaces use list_vxlan_tunnels." + " For EoIP detail use get_eoip_tunnel; for GRE detail use get_gre_tunnel; for IPIP detail use get_ipip_tunnel." + " Returns the complete property set for the named interface, or a not-found message.",
20797
- inputSchema: { name: z104.string() },
21256
+ inputSchema: { name: z105.string() },
20798
21257
  async handler(a, ctx) {
20799
21258
  ctx.info(`Getting VXLAN tunnel details: name=${a.name}`);
20800
21259
  const result = await executeMikrotikCommand(`/interface vxlan print detail where name="${a.name}"`, ctx);
@@ -20808,7 +21267,7 @@ ${result}`;
20808
21267
  title: "Remove VXLAN Tunnel Interface",
20809
21268
  annotations: DESTRUCTIVE,
20810
21269
  description: "Permanently deletes a VXLAN interface by name (`/interface vxlan remove [find name=...]`)." + " Verifies existence via count-only before removal and returns a not-found message if the interface does not exist." + " This is destructive and immediately severs all L2 overlay traffic using this VNI endpoint." + " For EoIP removal use remove_eoip_tunnel; for GRE use remove_gre_tunnel; for IPIP use remove_ipip_tunnel.",
20811
- inputSchema: { name: z104.string() },
21270
+ inputSchema: { name: z105.string() },
20812
21271
  async handler(a, ctx) {
20813
21272
  ctx.info(`Removing VXLAN tunnel: name=${a.name}`);
20814
21273
  const count = await executeMikrotikCommand(`/interface vxlan print count-only where name="${a.name}"`, ctx);
@@ -20823,7 +21282,7 @@ ${result}`;
20823
21282
  ];
20824
21283
 
20825
21284
  // src/tools/user-manager.ts
20826
- import { z as z105 } from "zod";
21285
+ import { z as z106 } from "zod";
20827
21286
  var NOT_AVAILABLE = "User Manager is not available on this device (package not installed).";
20828
21287
  var userManagerTools = [
20829
21288
  defineTool({
@@ -20847,12 +21306,12 @@ ${result}`;
20847
21306
  annotations: WRITE_IDEMPOTENT,
20848
21307
  description: "Updates the global User Manager daemon configuration (`/user-manager set`) \u2014 toggle the" + " built-in RADIUS server on/off (`enabled`), set the TLS certificate, or enable the" + " profile/payment subsystem (`use_profiles`). Applies changes idempotently to the single" + " global settings entry; returns the full updated settings after applying. For per-user" + " changes use update_user_manager_user; for profile creation use add_user_manager_profile.",
20849
21308
  inputSchema: {
20850
- enabled: z105.boolean().optional().describe("Enable or disable the User Manager server"),
20851
- certificate: z105.string().optional().describe("TLS certificate name for RADIUS over TLS"),
20852
- radsec_certificate: z105.string().optional().describe("Certificate name for RadSec (RADIUS over TLS)"),
20853
- accounting_port: z105.number().int().optional().describe("UDP port for RADIUS accounting"),
20854
- authentication_port: z105.number().int().optional().describe("UDP port for RADIUS authentication"),
20855
- use_profiles: z105.boolean().optional().describe("Enable the profile/payment subsystem")
21309
+ enabled: z106.boolean().optional().describe("Enable or disable the User Manager server"),
21310
+ certificate: z106.string().optional().describe("TLS certificate name for RADIUS over TLS"),
21311
+ radsec_certificate: z106.string().optional().describe("Certificate name for RadSec (RADIUS over TLS)"),
21312
+ accounting_port: z106.number().int().optional().describe("UDP port for RADIUS accounting"),
21313
+ authentication_port: z106.number().int().optional().describe("UDP port for RADIUS authentication"),
21314
+ use_profiles: z106.boolean().optional().describe("Enable the profile/payment subsystem")
20856
21315
  },
20857
21316
  async handler(a, ctx) {
20858
21317
  ctx.info("Updating User Manager settings");
@@ -20876,15 +21335,15 @@ ${details}`;
20876
21335
  annotations: WRITE,
20877
21336
  description: "Creates a new user in the User Manager RADIUS database (`/user-manager user add`) \u2014 for" + " hotspot, PPP, or 802.1X authentication managed by the built-in RADIUS server. Not for" + " local router login accounts; for those use add_user. Supply `name` and `password`;" + " optionally set `group`, `shared_users` (max simultaneous sessions), and custom RADIUS" + " `attributes`. Returns the created user's detail with secrets redacted.",
20878
21337
  inputSchema: {
20879
- name: z105.string().describe("Login name for the user"),
20880
- password: z105.string().describe("Login password for the user"),
20881
- group: z105.string().optional(),
20882
- shared_users: z105.number().int().optional().describe("Max simultaneous sessions"),
20883
- attributes: z105.string().optional().describe("Custom RADIUS attributes"),
20884
- caller_id: z105.string().optional().describe("Caller ID the user is restricted to (e.g. MAC)"),
20885
- otp_secret: z105.string().optional().describe("Base32 OTP shared secret for two-factor auth"),
20886
- comment: z105.string().optional(),
20887
- disabled: z105.boolean().default(false)
21338
+ name: z106.string().describe("Login name for the user"),
21339
+ password: z106.string().describe("Login password for the user"),
21340
+ group: z106.string().optional(),
21341
+ shared_users: z106.number().int().optional().describe("Max simultaneous sessions"),
21342
+ attributes: z106.string().optional().describe("Custom RADIUS attributes"),
21343
+ caller_id: z106.string().optional().describe("Caller ID the user is restricted to (e.g. MAC)"),
21344
+ otp_secret: z106.string().optional().describe("Base32 OTP shared secret for two-factor auth"),
21345
+ comment: z106.string().optional(),
21346
+ disabled: z106.boolean().default(false)
20888
21347
  },
20889
21348
  async handler(a, ctx) {
20890
21349
  ctx.info(`Adding User Manager user: name=${a.name}`);
@@ -20906,7 +21365,7 @@ ${redactSecrets(details)}` : "User Manager user creation completed but unable to
20906
21365
  annotations: READ,
20907
21366
  description: "Returns all users in the User Manager RADIUS database (`/user-manager user print`) \u2014 the" + " accounts that authenticate against the built-in RADIUS server for hotspot, PPP, or 802.1X." + " Optionally filter by partial `name_filter`. Not for local router login accounts; for those" + " use list_users. Returns user list with secrets redacted; for full" + " single-user detail use get_user_manager_user.",
20908
21367
  inputSchema: {
20909
- name_filter: z105.string().optional().describe("Partial name match")
21368
+ name_filter: z106.string().optional().describe("Partial name match")
20910
21369
  },
20911
21370
  async handler(a, ctx) {
20912
21371
  ctx.info("Listing User Manager users");
@@ -20926,7 +21385,7 @@ ${redactSecrets(result)}`;
20926
21385
  title: "Get User Manager RADIUS User Detail",
20927
21386
  annotations: READ,
20928
21387
  description: "Returns full detail for a single User Manager RADIUS user" + " (`/user-manager user print detail where name=`) \u2014 all fields including group," + " shared-users limit, RADIUS attributes, and status, with secrets redacted. Use when you" + " need the complete record for one user by exact name. To browse all users use" + " list_user_manager_users; to modify the record use update_user_manager_user.",
20929
- inputSchema: { name: z105.string() },
21388
+ inputSchema: { name: z106.string() },
20930
21389
  async handler(a, ctx) {
20931
21390
  ctx.info(`Getting User Manager user details: name=${a.name}`);
20932
21391
  const result = await executeMikrotikCommand(`/user-manager user print detail where name="${a.name}"`, ctx);
@@ -20943,16 +21402,16 @@ ${redactSecrets(result)}`;
20943
21402
  annotations: WRITE_IDEMPOTENT,
20944
21403
  description: "Modifies an existing User Manager RADIUS user (`/user-manager user set [find name=...]`)" + " \u2014 change name, password, group, shared-users limit, RADIUS attributes, comment, or" + " enabled/disabled state. Locate the user by its current `name` (from" + " list_user_manager_users or get_user_manager_user). Returns the updated record with" + " secrets redacted. To permanently delete the user use remove_user_manager_user.",
20945
21404
  inputSchema: {
20946
- name: z105.string().describe("Current name of the user to update"),
20947
- new_name: z105.string().optional(),
20948
- password: z105.string().optional(),
20949
- group: z105.string().optional(),
20950
- shared_users: z105.number().int().optional(),
20951
- attributes: z105.string().optional(),
20952
- caller_id: z105.string().optional().describe("Caller ID the user is restricted to (e.g. MAC)"),
20953
- otp_secret: z105.string().optional().describe("Base32 OTP shared secret for two-factor auth"),
20954
- comment: z105.string().optional(),
20955
- disabled: z105.boolean().optional()
21405
+ name: z106.string().describe("Current name of the user to update"),
21406
+ new_name: z106.string().optional(),
21407
+ password: z106.string().optional(),
21408
+ group: z106.string().optional(),
21409
+ shared_users: z106.number().int().optional(),
21410
+ attributes: z106.string().optional(),
21411
+ caller_id: z106.string().optional().describe("Caller ID the user is restricted to (e.g. MAC)"),
21412
+ otp_secret: z106.string().optional().describe("Base32 OTP shared secret for two-factor auth"),
21413
+ comment: z106.string().optional(),
21414
+ disabled: z106.boolean().optional()
20956
21415
  },
20957
21416
  async handler(a, ctx) {
20958
21417
  ctx.info(`Updating User Manager user: name=${a.name}`);
@@ -20976,7 +21435,7 @@ ${redactSecrets(details)}`;
20976
21435
  title: "Remove User Manager RADIUS User",
20977
21436
  annotations: DESTRUCTIVE,
20978
21437
  description: "Permanently deletes a User Manager RADIUS user (`/user-manager user remove [find name=...]`)" + " \u2014 verifies the user exists via count-only check first, then removes them. Does NOT" + " automatically remove the user's profile assignments; check list_user_manager_user_profiles" + " first. To disable without deleting use update_user_manager_user with `disabled=true`.",
20979
- inputSchema: { name: z105.string() },
21438
+ inputSchema: { name: z106.string() },
20980
21439
  async handler(a, ctx) {
20981
21440
  ctx.info(`Removing User Manager user: name=${a.name}`);
20982
21441
  const count = await executeMikrotikCommand(`/user-manager user print count-only where name="${a.name}"`, ctx);
@@ -20996,13 +21455,13 @@ ${redactSecrets(details)}`;
20996
21455
  annotations: WRITE,
20997
21456
  description: "Creates a new User Manager service/billing profile (`/user-manager profile add`) \u2014 a named" + " plan template that defines validity period (e.g. '30d'), price, and session override" + " limits (`starts_when`, `override_shared_users`) that can be assigned to users. Profiles are" + " templates only; to link a profile to a specific user use assign_user_manager_profile. For" + " rate/quota constraints attach a limitation (add_user_manager_limitation). Returns the" + " created profile's detail.",
20998
21457
  inputSchema: {
20999
- name: z105.string().describe("Profile name"),
21000
- name_for_users: z105.string().optional().describe("Display name shown to users"),
21001
- validity: z105.string().optional().describe("Validity period, e.g. '30d'"),
21002
- price: z105.number().optional(),
21003
- starts_when: z105.enum(["assigned", "first-auth"]).optional(),
21004
- override_shared_users: z105.string().optional(),
21005
- comment: z105.string().optional()
21458
+ name: z106.string().describe("Profile name"),
21459
+ name_for_users: z106.string().optional().describe("Display name shown to users"),
21460
+ validity: z106.string().optional().describe("Validity period, e.g. '30d'"),
21461
+ price: z106.number().optional(),
21462
+ starts_when: z106.enum(["assigned", "first-auth"]).optional(),
21463
+ override_shared_users: z106.string().optional(),
21464
+ comment: z106.string().optional()
21006
21465
  },
21007
21466
  async handler(a, ctx) {
21008
21467
  ctx.info(`Adding User Manager profile: name=${a.name}`);
@@ -21024,7 +21483,7 @@ ${details}` : "User Manager profile creation completed but unable to verify.";
21024
21483
  annotations: READ,
21025
21484
  description: "Returns all User Manager service/billing profile templates (`/user-manager profile print`)" + " \u2014 the named plans defining validity, price, and session limits. Optionally filter by" + " partial `name_filter`. Not the same as user-profile assignments; to see which profiles" + " are linked to which users use list_user_manager_user_profiles. Returns profile list.",
21026
21485
  inputSchema: {
21027
- name_filter: z105.string().optional().describe("Partial name match")
21486
+ name_filter: z106.string().optional().describe("Partial name match")
21028
21487
  },
21029
21488
  async handler(a, ctx) {
21030
21489
  ctx.info("Listing User Manager profiles");
@@ -21044,7 +21503,7 @@ ${result}`;
21044
21503
  title: "Remove User Manager Service Profile",
21045
21504
  annotations: DESTRUCTIVE,
21046
21505
  description: "Permanently deletes a User Manager service/billing profile" + " (`/user-manager profile remove [find name=...]`) \u2014 verifies existence via count-only check" + " first, then removes the plan template. Does NOT automatically remove existing user-profile" + " assignments that reference this profile; check list_user_manager_user_profiles before" + " removing to avoid orphaned assignments. For creating a profile use add_user_manager_profile.",
21047
- inputSchema: { name: z105.string() },
21506
+ inputSchema: { name: z106.string() },
21048
21507
  async handler(a, ctx) {
21049
21508
  ctx.info(`Removing User Manager profile: name=${a.name}`);
21050
21509
  const count = await executeMikrotikCommand(`/user-manager profile print count-only where name="${a.name}"`, ctx);
@@ -21064,8 +21523,8 @@ ${result}`;
21064
21523
  annotations: WRITE,
21065
21524
  description: "Creates a user-profile assignment in User Manager (`/user-manager user-profile add`) \u2014" + " links an existing service profile plan to a specific user so the user inherits the plan's" + " limits (rate, transfer, validity). Both `user` and `profile` must already exist; to create" + " a user use add_user_manager_user; to create a profile use add_user_manager_profile. To view" + " existing assignments use list_user_manager_user_profiles.",
21066
21525
  inputSchema: {
21067
- user: z105.string().describe("User to assign the profile to"),
21068
- profile: z105.string().describe("Profile to assign")
21526
+ user: z106.string().describe("User to assign the profile to"),
21527
+ profile: z106.string().describe("Profile to assign")
21069
21528
  },
21070
21529
  async handler(a, ctx) {
21071
21530
  ctx.info(`Assigning profile '${a.profile}' to user '${a.user}'`);
@@ -21086,7 +21545,7 @@ ${result}`;
21086
21545
  annotations: READ,
21087
21546
  description: "Returns all User Manager user-to-profile assignment records" + " (`/user-manager user-profile print`) \u2014 shows which service profile plan is linked to each" + " user. Optionally filter by partial `user_filter`. Not the same as listing profile" + " definitions; for the plan templates themselves use list_user_manager_profiles. To create an" + " assignment use assign_user_manager_profile.",
21088
21547
  inputSchema: {
21089
- user_filter: z105.string().optional().describe("Partial user match")
21548
+ user_filter: z106.string().optional().describe("Partial user match")
21090
21549
  },
21091
21550
  async handler(a, ctx) {
21092
21551
  ctx.info("Listing User Manager user-profiles");
@@ -21107,13 +21566,13 @@ ${result}`;
21107
21566
  annotations: WRITE,
21108
21567
  description: "Registers a new RADIUS client (router or NAS device) in User Manager" + " (`/user-manager router add`) \u2014 the network device that forwards authentication requests to" + " this built-in RADIUS server. Requires a friendly `name`, the client's IP `address`, and a" + " `shared_secret`; optionally set the CoA port. Not related to IP routing; for routing table" + " entries use add_route. Returns the created entry with secrets redacted.",
21109
21568
  inputSchema: {
21110
- name: z105.string().describe("Friendly name for the RADIUS client"),
21111
- address: z105.string().describe("IP address of the RADIUS client"),
21112
- shared_secret: z105.string().describe("Shared secret for the RADIUS client"),
21113
- coa_port: z105.number().int().optional().describe("Change-of-Authorization port"),
21114
- protocol: z105.string().optional().describe("RADIUS transport protocol for this client (e.g. radius, radsec)"),
21115
- comment: z105.string().optional(),
21116
- disabled: z105.boolean().default(false)
21569
+ name: z106.string().describe("Friendly name for the RADIUS client"),
21570
+ address: z106.string().describe("IP address of the RADIUS client"),
21571
+ shared_secret: z106.string().describe("Shared secret for the RADIUS client"),
21572
+ coa_port: z106.number().int().optional().describe("Change-of-Authorization port"),
21573
+ protocol: z106.string().optional().describe("RADIUS transport protocol for this client (e.g. radius, radsec)"),
21574
+ comment: z106.string().optional(),
21575
+ disabled: z106.boolean().default(false)
21117
21576
  },
21118
21577
  async handler(a, ctx) {
21119
21578
  ctx.info(`Adding User Manager router: name=${a.name}, address=${a.address}`);
@@ -21135,7 +21594,7 @@ ${redactSecrets(details)}` : "User Manager router creation completed but unable
21135
21594
  annotations: READ,
21136
21595
  description: "Returns all RADIUS clients (routers/NAS devices) registered in User Manager" + " (`/user-manager router print`) \u2014 the devices authorized to forward authentication requests" + " to this built-in RADIUS server. Optionally filter by partial `name_filter`. Not related to" + " IP routing; for routing table entries use list_routes. Returns entries with secrets redacted.",
21137
21596
  inputSchema: {
21138
- name_filter: z105.string().optional().describe("Partial name match")
21597
+ name_filter: z106.string().optional().describe("Partial name match")
21139
21598
  },
21140
21599
  async handler(a, ctx) {
21141
21600
  ctx.info("Listing User Manager routers");
@@ -21155,7 +21614,7 @@ ${redactSecrets(result)}`;
21155
21614
  title: "Remove User Manager RADIUS Client (Router/NAS)",
21156
21615
  annotations: DESTRUCTIVE,
21157
21616
  description: "Permanently removes a RADIUS client (router/NAS) from User Manager" + " (`/user-manager router remove [find name=...]`) \u2014 verifies existence via count-only check" + " first, then deletes the entry. The device will no longer be able to forward authentication" + " requests to this RADIUS server after removal. Not related to IP routing; for routing table" + " management use remove_route.",
21158
- inputSchema: { name: z105.string() },
21617
+ inputSchema: { name: z106.string() },
21159
21618
  async handler(a, ctx) {
21160
21619
  ctx.info(`Removing User Manager router: name=${a.name}`);
21161
21620
  const count = await executeMikrotikCommand(`/user-manager router print count-only where name="${a.name}"`, ctx);
@@ -21175,25 +21634,25 @@ ${redactSecrets(result)}`;
21175
21634
  annotations: WRITE,
21176
21635
  description: "Creates a new User Manager limitation template (`/user-manager limitation add`) \u2014 a reusable" + " named set of rate and quota constraints: download rate (`rate_limit_rx`, e.g. '10M')," + " upload rate (`rate_limit_tx`), total transfer cap (`transfer_limit`, e.g. '10G'), and" + " uptime cap (`uptime_limit`, e.g. '1d'). Limitations are templates attached to profiles, not" + " users directly; for service plan templates use add_user_manager_profile. Returns the created" + " limitation's detail.",
21177
21636
  inputSchema: {
21178
- name: z105.string().describe("Limitation name"),
21179
- rate_limit_rx: z105.string().optional().describe("Download rate limit, e.g. '10M'"),
21180
- rate_limit_tx: z105.string().optional().describe("Upload rate limit, e.g. '10M'"),
21181
- rate_limit_min_rx: z105.string().optional().describe("Guaranteed (CIR) download rate, e.g. '2M'"),
21182
- rate_limit_min_tx: z105.string().optional().describe("Guaranteed (CIR) upload rate, e.g. '2M'"),
21183
- rate_limit_burst_rx: z105.string().optional().describe("Download burst rate, e.g. '20M'"),
21184
- rate_limit_burst_tx: z105.string().optional().describe("Upload burst rate, e.g. '20M'"),
21185
- rate_limit_burst_threshold_rx: z105.string().optional().describe("Download burst threshold rate"),
21186
- rate_limit_burst_threshold_tx: z105.string().optional().describe("Upload burst threshold rate"),
21187
- rate_limit_burst_time_rx: z105.string().optional().describe("Download burst time, e.g. '10s'"),
21188
- rate_limit_burst_time_tx: z105.string().optional().describe("Upload burst time, e.g. '10s'"),
21189
- rate_limit_priority: z105.number().int().optional().describe("Queue priority (1-8)"),
21190
- download_limit: z105.string().optional().describe("Download transfer cap in bytes, e.g. '5G'"),
21191
- upload_limit: z105.string().optional().describe("Upload transfer cap in bytes, e.g. '5G'"),
21192
- transfer_limit: z105.string().optional().describe("Total transfer cap, e.g. '10G'"),
21193
- uptime_limit: z105.string().optional().describe("Uptime cap, e.g. '1d'"),
21194
- reset_counters_interval: z105.string().optional().describe("Interval to auto-reset usage counters"),
21195
- reset_counters_start_time: z105.string().optional().describe("Start time for counter reset interval"),
21196
- comment: z105.string().optional()
21637
+ name: z106.string().describe("Limitation name"),
21638
+ rate_limit_rx: z106.string().optional().describe("Download rate limit, e.g. '10M'"),
21639
+ rate_limit_tx: z106.string().optional().describe("Upload rate limit, e.g. '10M'"),
21640
+ rate_limit_min_rx: z106.string().optional().describe("Guaranteed (CIR) download rate, e.g. '2M'"),
21641
+ rate_limit_min_tx: z106.string().optional().describe("Guaranteed (CIR) upload rate, e.g. '2M'"),
21642
+ rate_limit_burst_rx: z106.string().optional().describe("Download burst rate, e.g. '20M'"),
21643
+ rate_limit_burst_tx: z106.string().optional().describe("Upload burst rate, e.g. '20M'"),
21644
+ rate_limit_burst_threshold_rx: z106.string().optional().describe("Download burst threshold rate"),
21645
+ rate_limit_burst_threshold_tx: z106.string().optional().describe("Upload burst threshold rate"),
21646
+ rate_limit_burst_time_rx: z106.string().optional().describe("Download burst time, e.g. '10s'"),
21647
+ rate_limit_burst_time_tx: z106.string().optional().describe("Upload burst time, e.g. '10s'"),
21648
+ rate_limit_priority: z106.number().int().optional().describe("Queue priority (1-8)"),
21649
+ download_limit: z106.string().optional().describe("Download transfer cap in bytes, e.g. '5G'"),
21650
+ upload_limit: z106.string().optional().describe("Upload transfer cap in bytes, e.g. '5G'"),
21651
+ transfer_limit: z106.string().optional().describe("Total transfer cap, e.g. '10G'"),
21652
+ uptime_limit: z106.string().optional().describe("Uptime cap, e.g. '1d'"),
21653
+ reset_counters_interval: z106.string().optional().describe("Interval to auto-reset usage counters"),
21654
+ reset_counters_start_time: z106.string().optional().describe("Start time for counter reset interval"),
21655
+ comment: z106.string().optional()
21197
21656
  },
21198
21657
  async handler(a, ctx) {
21199
21658
  ctx.info(`Adding User Manager limitation: name=${a.name}`);
@@ -21215,7 +21674,7 @@ ${details}` : "User Manager limitation creation completed but unable to verify."
21215
21674
  annotations: READ,
21216
21675
  description: "Returns all User Manager limitation templates (`/user-manager limitation print`) \u2014 the named" + " rate/quota constraint definitions (rate-limit-rx/tx, transfer-limit, uptime-limit) that can" + " be attached to service profiles. Optionally filter by partial `name_filter`. Limitations are" + " distinct from profile plan templates; for those use list_user_manager_profiles.",
21217
21676
  inputSchema: {
21218
- name_filter: z105.string().optional().describe("Partial name match")
21677
+ name_filter: z106.string().optional().describe("Partial name match")
21219
21678
  },
21220
21679
  async handler(a, ctx) {
21221
21680
  ctx.info("Listing User Manager limitations");
@@ -21235,7 +21694,7 @@ ${result}`;
21235
21694
  title: "Remove User Manager Limitation Template",
21236
21695
  annotations: DESTRUCTIVE,
21237
21696
  description: "Permanently deletes a User Manager limitation template" + " (`/user-manager limitation remove [find name=...]`) \u2014 verifies existence via count-only" + " check first, then removes the constraint definition. Does NOT check whether the limitation" + " is still referenced by any profiles; verify with list_user_manager_profiles before removing" + " to avoid orphaned references. For listing limitations use list_user_manager_limitations.",
21238
- inputSchema: { name: z105.string() },
21697
+ inputSchema: { name: z106.string() },
21239
21698
  async handler(a, ctx) {
21240
21699
  ctx.info(`Removing User Manager limitation: name=${a.name}`);
21241
21700
  const count = await executeMikrotikCommand(`/user-manager limitation print count-only where name="${a.name}"`, ctx);
@@ -21255,8 +21714,8 @@ ${result}`;
21255
21714
  annotations: READ,
21256
21715
  description: "Returns User Manager RADIUS accounting session records (`/user-manager session print`) \u2014 the" + " log of authentication and accounting events for users connecting through registered RADIUS" + " clients. Optionally filter by partial `user_filter` or restrict to currently active sessions" + " with `active_only=true`. Returns session data including bytes transferred, uptime, and" + " status. For the user accounts themselves use list_user_manager_users; for registered RADIUS" + " clients use list_user_manager_routers.",
21257
21716
  inputSchema: {
21258
- user_filter: z105.string().optional().describe("Partial user match"),
21259
- active_only: z105.boolean().default(false).describe("Only show currently active sessions")
21717
+ user_filter: z106.string().optional().describe("Partial user match"),
21718
+ active_only: z106.boolean().default(false).describe("Only show currently active sessions")
21260
21719
  },
21261
21720
  async handler(a, ctx) {
21262
21721
  ctx.info("Listing User Manager sessions");
@@ -21276,7 +21735,7 @@ ${result}`;
21276
21735
  ];
21277
21736
 
21278
21737
  // src/tools/users.ts
21279
- import { z as z106 } from "zod";
21738
+ import { z as z107 } from "zod";
21280
21739
  var VALID_POLICIES = [
21281
21740
  "local",
21282
21741
  "telnet",
@@ -21333,12 +21792,12 @@ var userTools = [
21333
21792
  annotations: WRITE,
21334
21793
  description: "Create a local user account (`/user add`) \u2014 grants interactive access to the router via SSH, Winbox, web, telnet, or API. " + "The `group` parameter (default: `read`) sets permissions; built-in groups are `read`, `write`, `full`; use `add_user_group` to create custom groups. " + "Use `address` to restrict login to a specific IP/subnet. For PPP/dial-in credentials use `create_ppp_secret` instead. " + "Returns the created account's full detail including its `.id`.",
21335
21794
  inputSchema: {
21336
- name: z106.string(),
21337
- password: z106.string(),
21338
- group: z106.string().default("read"),
21339
- address: z106.string().optional(),
21340
- comment: z106.string().optional(),
21341
- disabled: z106.boolean().default(false)
21795
+ name: z107.string(),
21796
+ password: z107.string(),
21797
+ group: z107.string().default("read"),
21798
+ address: z107.string().optional(),
21799
+ comment: z107.string().optional(),
21800
+ disabled: z107.boolean().default(false)
21342
21801
  },
21343
21802
  async handler(a, ctx) {
21344
21803
  ctx.info(`Adding user: name=${a.name}, group=${a.group}`);
@@ -21370,10 +21829,10 @@ ${redactSecrets(details)}`;
21370
21829
  annotations: READ,
21371
21830
  description: "List local router user accounts (`/user print`) \u2014 shows each account's group, address restriction, and enabled/disabled state. " + "Supports optional filters: `name_filter` (name substring match), `group_filter` (exact group name), `disabled_only`. " + "Note: `active_only` is accepted by the schema but is not applied as a filter \u2014 it has no effect on the output. " + "Passwords are redacted from output. For currently logged-in sessions use `get_active_users`. For PPP dial-in secrets use `create_ppp_secret`.",
21372
21831
  inputSchema: {
21373
- name_filter: z106.string().optional(),
21374
- group_filter: z106.string().optional(),
21375
- disabled_only: z106.boolean().default(false),
21376
- active_only: z106.boolean().default(false)
21832
+ name_filter: z107.string().optional(),
21833
+ group_filter: z107.string().optional(),
21834
+ disabled_only: z107.boolean().default(false),
21835
+ active_only: z107.boolean().default(false)
21377
21836
  },
21378
21837
  async handler(a, ctx) {
21379
21838
  ctx.info(`Listing users with filters: name=${a.name_filter}, group=${a.group_filter}`);
@@ -21397,7 +21856,7 @@ ${redactSecrets(result)}`;
21397
21856
  title: "Get Local User Account Details",
21398
21857
  annotations: READ,
21399
21858
  description: "Fetch full detail for a single local user account (`/user print detail where name=...`). " + "Identified by login `name`. Passwords are redacted from output. " + "For all accounts use `list_users`; for currently active sessions use `get_active_users`.",
21400
- inputSchema: { name: z106.string() },
21859
+ inputSchema: { name: z107.string() },
21401
21860
  async handler(a, ctx) {
21402
21861
  ctx.info(`Getting user details: name=${a.name}`);
21403
21862
  const result = await executeMikrotikCommand(`/user print detail where name="${a.name}"`, ctx);
@@ -21414,13 +21873,13 @@ ${redactSecrets(result)}`;
21414
21873
  annotations: WRITE_IDEMPOTENT,
21415
21874
  description: "Modify a local user account (`/user set [find name=...]`). " + "Can change login name (`new_name`), `password`, `group`, allowed source `address` (pass empty string to remove address restriction), `comment`, or `disabled` state. " + "For toggling enabled/disabled state only, prefer `enable_user` or `disable_user`. " + "Returns the updated user detail with passwords redacted.",
21416
21875
  inputSchema: {
21417
- name: z106.string(),
21418
- new_name: z106.string().optional(),
21419
- password: z106.string().optional(),
21420
- group: z106.string().optional(),
21421
- address: z106.string().optional(),
21422
- comment: z106.string().optional(),
21423
- disabled: z106.boolean().optional()
21876
+ name: z107.string(),
21877
+ new_name: z107.string().optional(),
21878
+ password: z107.string().optional(),
21879
+ group: z107.string().optional(),
21880
+ address: z107.string().optional(),
21881
+ comment: z107.string().optional(),
21882
+ disabled: z107.boolean().optional()
21424
21883
  },
21425
21884
  async handler(a, ctx) {
21426
21885
  return runUpdateUser(a, ctx);
@@ -21431,7 +21890,7 @@ ${redactSecrets(result)}`;
21431
21890
  title: "Remove Local User Account",
21432
21891
  annotations: DESTRUCTIVE,
21433
21892
  description: "Permanently delete a local user account (`/user remove [find name=...]`). " + "Refuses to remove the built-in `admin` account. Verifies the user exists before removing. " + "To temporarily block access without deleting use `disable_user`. To end a live session without deleting the account use `disconnect_user`.",
21434
- inputSchema: { name: z106.string() },
21893
+ inputSchema: { name: z107.string() },
21435
21894
  async handler(a, ctx) {
21436
21895
  ctx.info(`Removing user: name=${a.name}`);
21437
21896
  if (a.name.toLowerCase() === "admin")
@@ -21450,7 +21909,7 @@ ${redactSecrets(result)}`;
21450
21909
  title: "Disable Local User Account",
21451
21910
  annotations: WRITE_IDEMPOTENT,
21452
21911
  description: "Disable a local user account (`/user set [find name=...] disabled=yes`), preventing new logins without deleting the account. " + "To re-enable use `enable_user`. For a full attribute update use `update_user`. To permanently delete the account use `remove_user`.",
21453
- inputSchema: { name: z106.string() },
21912
+ inputSchema: { name: z107.string() },
21454
21913
  async handler(a, ctx) {
21455
21914
  return runUpdateUser({ name: a.name, disabled: true }, ctx);
21456
21915
  }
@@ -21460,7 +21919,7 @@ ${redactSecrets(result)}`;
21460
21919
  title: "Enable Local User Account",
21461
21920
  annotations: WRITE_IDEMPOTENT,
21462
21921
  description: "Re-enable a previously disabled local user account (`/user set [find name=...] disabled=no`). " + "To disable use `disable_user`. For a full attribute update use `update_user`.",
21463
- inputSchema: { name: z106.string() },
21922
+ inputSchema: { name: z107.string() },
21464
21923
  async handler(a, ctx) {
21465
21924
  return runUpdateUser({ name: a.name, disabled: false }, ctx);
21466
21925
  }
@@ -21471,10 +21930,10 @@ ${redactSecrets(result)}`;
21471
21930
  annotations: WRITE,
21472
21931
  description: "Create a custom user group (`/user group add`) that defines a named permission policy for router access. " + "`policy` is a list of permissions to grant from: local, telnet, ssh, ftp, reboot, read, write, policy, test, winbox, password, web, sniff, sensitive, api, romon, dude, tikapp, rest-api. " + "The built-in groups (`read`, `write`, `full`) already exist on the device; this tool does not guard against those names \u2014 attempting to create a group with a duplicate name will be rejected by the device. Assign users to the new group via `add_user` or `update_user`. " + "Returns the created group's full detail.",
21473
21932
  inputSchema: {
21474
- name: z106.string(),
21475
- policy: z106.array(z106.string()),
21476
- skin: z106.string().optional(),
21477
- comment: z106.string().optional()
21933
+ name: z107.string(),
21934
+ policy: z107.array(z107.string()),
21935
+ skin: z107.string().optional(),
21936
+ comment: z107.string().optional()
21478
21937
  },
21479
21938
  async handler(a, ctx) {
21480
21939
  ctx.info(`Adding user group: name=${a.name}`);
@@ -21511,8 +21970,8 @@ ${details}`;
21511
21970
  annotations: READ,
21512
21971
  description: "List all user groups (`/user group print`), including built-in groups (`read`, `write`, `full`) and custom ones, showing their policy sets. " + "Supports optional filters: `name_filter` (name substring), `policy_filter` (policy substring). " + "To see individual user accounts use `list_users`; to see which users belong to a specific group use `list_users` with `group_filter`.",
21513
21972
  inputSchema: {
21514
- name_filter: z106.string().optional(),
21515
- policy_filter: z106.string().optional()
21973
+ name_filter: z107.string().optional(),
21974
+ policy_filter: z107.string().optional()
21516
21975
  },
21517
21976
  async handler(a, ctx) {
21518
21977
  ctx.info(`Listing user groups with filters: name=${a.name_filter}`);
@@ -21534,7 +21993,7 @@ ${result}`;
21534
21993
  title: "Get User Group Details",
21535
21994
  annotations: READ,
21536
21995
  description: "Fetch full detail for a single user group (`/user group print detail where name=...`), showing its complete policy list and skin setting. " + "For all groups use `list_user_groups`. To see which users belong to this group use `list_users` with `group_filter`.",
21537
- inputSchema: { name: z106.string() },
21996
+ inputSchema: { name: z107.string() },
21538
21997
  async handler(a, ctx) {
21539
21998
  ctx.info(`Getting user group details: name=${a.name}`);
21540
21999
  const result = await executeMikrotikCommand(`/user group print detail where name="${a.name}"`, ctx);
@@ -21551,11 +22010,11 @@ ${result}`;
21551
22010
  annotations: WRITE_IDEMPOTENT,
21552
22011
  description: "Modify a custom user group (`/user group set [find name=...]`). " + "Can change group name (`new_name`), `policy` list, `skin`, or `comment`. " + "Refuses to modify built-in groups (`read`, `write`, `full`). " + "Valid policies: local, telnet, ssh, ftp, reboot, read, write, policy, test, winbox, password, web, sniff, sensitive, api, romon, dude, tikapp, rest-api. " + "Returns the updated group detail.",
21553
22012
  inputSchema: {
21554
- name: z106.string(),
21555
- new_name: z106.string().optional(),
21556
- policy: z106.array(z106.string()).optional(),
21557
- skin: z106.string().optional(),
21558
- comment: z106.string().optional()
22013
+ name: z107.string(),
22014
+ new_name: z107.string().optional(),
22015
+ policy: z107.array(z107.string()).optional(),
22016
+ skin: z107.string().optional(),
22017
+ comment: z107.string().optional()
21559
22018
  },
21560
22019
  async handler(a, ctx) {
21561
22020
  ctx.info(`Updating user group: name=${a.name}`);
@@ -21588,7 +22047,7 @@ ${details}`;
21588
22047
  title: "Remove User Group",
21589
22048
  annotations: DESTRUCTIVE,
21590
22049
  description: "Delete a custom user group (`/user group remove [find name=...]`). " + "Refuses to remove built-in groups (`read`, `write`, `full`). " + "Checks that no users are currently assigned to the group \u2014 reassign or remove those users first via `update_user` or `remove_user`. " + "To remove individual user accounts use `remove_user`.",
21591
- inputSchema: { name: z106.string() },
22050
+ inputSchema: { name: z107.string() },
21592
22051
  async handler(a, ctx) {
21593
22052
  ctx.info(`Removing user group: name=${a.name}`);
21594
22053
  if (BUILTIN_GROUPS.includes(a.name))
@@ -21626,7 +22085,7 @@ ${result}`;
21626
22085
  title: "Disconnect Active User Session",
21627
22086
  annotations: DESTRUCTIVE,
21628
22087
  description: "Forcibly terminate an active user session (`/user active remove <user_id>`). " + "`user_id` is the `.id` from `get_active_users`. " + "Does NOT delete the user account \u2014 to delete the account use `remove_user`. To prevent future logins without deleting use `disable_user`.",
21629
- inputSchema: { user_id: z106.string() },
22088
+ inputSchema: { user_id: z107.string() },
21630
22089
  async handler(a, ctx) {
21631
22090
  ctx.info(`Disconnecting user: user_id=${a.user_id}`);
21632
22091
  const result = await executeMikrotikCommand(`/user active remove ${a.user_id}`, ctx);
@@ -21640,7 +22099,7 @@ ${result}`;
21640
22099
  title: "Export User Configuration to File",
21641
22100
  annotations: READ,
21642
22101
  description: "Export the router's user accounts and user-group configuration as a RouterOS script (`/user export file=<filename>`). " + "The file is saved on the router's flash storage as `<filename>.rsc`. If `filename` is omitted, defaults to `user_config`. " + "The exported script can be used to restore user accounts on another device.",
21643
- inputSchema: { filename: z106.string().optional() },
22102
+ inputSchema: { filename: z107.string().optional() },
21644
22103
  async handler(a, ctx) {
21645
22104
  ctx.info("Exporting user configuration");
21646
22105
  const filename = a.filename || "user_config";
@@ -21656,8 +22115,8 @@ ${result}`;
21656
22115
  annotations: WRITE,
21657
22116
  description: "Import an SSH public key for a user (`/user ssh-keys import user=... public-key-file=...`), enabling key-based SSH authentication in addition to password login. " + "`key_file` must be the path to a public key file already present on the router's filesystem. " + "To list a user's existing keys use `list_user_ssh_keys`; to remove a key use `remove_user_ssh_key`.",
21658
22117
  inputSchema: {
21659
- username: z106.string(),
21660
- key_file: z106.string()
22118
+ username: z107.string(),
22119
+ key_file: z107.string()
21661
22120
  },
21662
22121
  async handler(a, ctx) {
21663
22122
  ctx.info(`Setting SSH keys for user: ${a.username}`);
@@ -21674,7 +22133,7 @@ ${result}`;
21674
22133
  title: "List User SSH Keys",
21675
22134
  annotations: READ,
21676
22135
  description: "List SSH public keys registered for a specific user (`/user ssh-keys print where user=...`). " + "Returns each key's `.id`, which is required by `remove_user_ssh_key`. To add a key use `set_user_ssh_keys`.",
21677
- inputSchema: { username: z106.string() },
22136
+ inputSchema: { username: z107.string() },
21678
22137
  async handler(a, ctx) {
21679
22138
  ctx.info(`Listing SSH keys for user: ${a.username}`);
21680
22139
  const result = await executeMikrotikCommand(`/user ssh-keys print where user="${a.username}"`, ctx);
@@ -21690,7 +22149,7 @@ ${result}`;
21690
22149
  title: "Remove User SSH Key",
21691
22150
  annotations: DESTRUCTIVE,
21692
22151
  description: "Delete a specific SSH public key (`/user ssh-keys remove <key_id>`). " + "`key_id` is the `.id` from `list_user_ssh_keys`. " + "Does NOT disable the user's password-based login \u2014 to block all logins use `disable_user`; to delete the account use `remove_user`.",
21693
- inputSchema: { key_id: z106.string() },
22152
+ inputSchema: { key_id: z107.string() },
21694
22153
  async handler(a, ctx) {
21695
22154
  ctx.info(`Removing SSH key: key_id=${a.key_id}`);
21696
22155
  const result = await executeMikrotikCommand(`/user ssh-keys remove ${a.key_id}`, ctx);
@@ -21702,7 +22161,7 @@ ${result}`;
21702
22161
  ];
21703
22162
 
21704
22163
  // src/tools/vlan-designer.ts
21705
- import { z as z107 } from "zod";
22164
+ import { z as z108 } from "zod";
21706
22165
  function defaultRange2(subnet) {
21707
22166
  const o = subnet.split("/")[0].split(".");
21708
22167
  return `${o[0]}.${o[1]}.${o[2]}.10-${o[0]}.${o[1]}.${o[2]}.254`;
@@ -21714,18 +22173,18 @@ var vlanDesignerTools = [
21714
22173
  annotations: DANGEROUS,
21715
22174
  description: "Stands up a complete isolated network segment in one call: a VLAN interface on the bridge, its " + "gateway IP, a DHCP server + pool + network, optional internet access (srcnat masquerade), and " + "inter-VLAN isolation firewall rules \u2014 e.g. 'a guest VLAN that reaches the internet but not the " + "LAN'. DEFAULTS TO A DRY RUN (`apply=false`) showing every command grouped; set `apply=true` to " + "build it. Bridge VLAN tagging is added when `tagged_ports`/`untagged_ports` are given (requires " + "bridge vlan-filtering). Isolation drops forward traffic from this subnet to each `isolate_from` " + "subnet. Returns the plan or a build report.",
21716
22175
  inputSchema: {
21717
- vlan_id: z107.number().int().min(1).max(4094),
21718
- name: z107.string().describe("Name for the VLAN interface, e.g. 'guest'"),
21719
- subnet: z107.string().describe("CIDR for the segment, e.g. '192.168.30.0/24'"),
21720
- gateway: z107.string().describe("Router's address in the segment, e.g. '192.168.30.1'"),
21721
- bridge: z107.string().default("bridge").describe("Bridge to put the VLAN on"),
21722
- tagged_ports: z107.string().optional().describe("Comma-separated trunk/tagged ports (+ the bridge)"),
21723
- untagged_ports: z107.string().optional().describe("Comma-separated access/untagged ports"),
21724
- dhcp: z107.boolean().default(true).describe("Create a DHCP server + pool for the segment"),
21725
- dhcp_range: z107.string().optional().describe("Pool range; defaults to .10\u2013.254 of the subnet"),
21726
- internet: z107.boolean().default(true).describe("Allow internet access via srcnat masquerade"),
21727
- isolate_from: z107.array(z107.string()).optional().describe("Subnets this segment must NOT reach, e.g. ['192.168.1.0/24']"),
21728
- apply: z107.boolean().default(false).describe("false = preview (default); true = build")
22176
+ vlan_id: z108.number().int().min(1).max(4094),
22177
+ name: z108.string().describe("Name for the VLAN interface, e.g. 'guest'"),
22178
+ subnet: z108.string().describe("CIDR for the segment, e.g. '192.168.30.0/24'"),
22179
+ gateway: z108.string().describe("Router's address in the segment, e.g. '192.168.30.1'"),
22180
+ bridge: z108.string().default("bridge").describe("Bridge to put the VLAN on"),
22181
+ tagged_ports: z108.string().optional().describe("Comma-separated trunk/tagged ports (+ the bridge)"),
22182
+ untagged_ports: z108.string().optional().describe("Comma-separated access/untagged ports"),
22183
+ dhcp: z108.boolean().default(true).describe("Create a DHCP server + pool for the segment"),
22184
+ dhcp_range: z108.string().optional().describe("Pool range; defaults to .10\u2013.254 of the subnet"),
22185
+ internet: z108.boolean().default(true).describe("Allow internet access via srcnat masquerade"),
22186
+ isolate_from: z108.array(z108.string()).optional().describe("Subnets this segment must NOT reach, e.g. ['192.168.1.0/24']"),
22187
+ apply: z108.boolean().default(false).describe("false = preview (default); true = build")
21729
22188
  },
21730
22189
  async handler(a, ctx) {
21731
22190
  const prefix = a.subnet.split("/")[1] ?? "24";
@@ -21798,9 +22257,9 @@ Review partial segment (the VLAN may exist without DHCP/firewall).`;
21798
22257
  ];
21799
22258
 
21800
22259
  // src/tools/vlan.ts
21801
- import { z as z108 } from "zod";
21802
- var ArpMode2 = z108.enum(["enabled", "disabled", "proxy-arp", "reply-only"]);
21803
- var LoopProtect = z108.enum(["default", "on", "off"]);
22260
+ import { z as z109 } from "zod";
22261
+ var ArpMode2 = z109.enum(["enabled", "disabled", "proxy-arp", "reply-only"]);
22262
+ var LoopProtect = z109.enum(["default", "on", "off"]);
21804
22263
  var vlanTools = [
21805
22264
  defineTool({
21806
22265
  name: "create_vlan_interface",
@@ -21808,18 +22267,18 @@ var vlanTools = [
21808
22267
  annotations: WRITE,
21809
22268
  description: "Creates an 802.1Q VLAN sub-interface (`/interface vlan`) on a specified parent physical or bridge interface." + " Use this to segment layer-2 traffic by VLAN ID (1\u20134094); set `use_service_tag=true` for 802.1ad QinQ double-tagging." + " For listing existing VLAN interfaces use `list_vlan_interfaces`; for editing an existing VLAN use `update_vlan_interface`." + " Returns the created interface's full detail including its name, which is the identifier accepted by `get_vlan_interface`, `update_vlan_interface`, and `remove_vlan_interface`." + " ARP mode accepts: enabled (default), disabled, proxy-arp, reply-only.",
21810
22269
  inputSchema: {
21811
- name: z108.string().describe("Name for the new VLAN interface, e.g. 'vlan100'"),
21812
- vlan_id: z108.number().int().min(1).max(4094).describe("802.1Q VLAN ID (1-4094)"),
21813
- interface: z108.string().describe("Parent interface, e.g. 'ether1' or 'bridge'"),
21814
- comment: z108.string().optional(),
21815
- disabled: z108.boolean().default(false),
21816
- mtu: z108.number().int().optional(),
21817
- use_service_tag: z108.boolean().default(false).describe("Use 802.1ad service tag (QinQ)"),
22270
+ name: z109.string().describe("Name for the new VLAN interface, e.g. 'vlan100'"),
22271
+ vlan_id: z109.number().int().min(1).max(4094).describe("802.1Q VLAN ID (1-4094)"),
22272
+ interface: z109.string().describe("Parent interface, e.g. 'ether1' or 'bridge'"),
22273
+ comment: z109.string().optional(),
22274
+ disabled: z109.boolean().default(false),
22275
+ mtu: z109.number().int().optional(),
22276
+ use_service_tag: z109.boolean().default(false).describe("Use 802.1ad service tag (QinQ)"),
21818
22277
  arp: ArpMode2.default("enabled"),
21819
- arp_timeout: z108.string().optional(),
22278
+ arp_timeout: z109.string().optional(),
21820
22279
  loop_protect: LoopProtect.optional().describe("Loop protection: default, on, off"),
21821
- loop_protect_disable_time: z108.string().optional().describe("Time to disable interface on loop detection, e.g. '5m' (0 = forever)"),
21822
- loop_protect_send_interval: z108.string().optional().describe("Interval between loop-protect probe packets, e.g. '5s'")
22280
+ loop_protect_disable_time: z109.string().optional().describe("Time to disable interface on loop detection, e.g. '5m' (0 = forever)"),
22281
+ loop_protect_send_interval: z109.string().optional().describe("Interval between loop-protect probe packets, e.g. '5s'")
21823
22282
  },
21824
22283
  async handler(a, ctx) {
21825
22284
  ctx.info(`Creating VLAN interface: name=${a.name}, vlan_id=${a.vlan_id}, interface=${a.interface}`);
@@ -21839,10 +22298,10 @@ ${details}` : "VLAN interface creation completed but unable to verify.";
21839
22298
  annotations: READ,
21840
22299
  description: "Lists all 802.1Q VLAN sub-interfaces (`/interface vlan print`) with optional filters by name substring, VLAN ID, parent interface, or disabled state." + " Use to discover existing VLANs and obtain interface names for `get_vlan_interface`, `update_vlan_interface`, or `remove_vlan_interface`." + " For a single interface's full property detail use `get_vlan_interface`." + " Returns a table of matching VLAN interfaces, or a not-found message when no entries match the filters.",
21841
22300
  inputSchema: {
21842
- name_filter: z108.string().optional().describe("Partial name match"),
21843
- vlan_id_filter: z108.number().int().optional(),
21844
- interface_filter: z108.string().optional().describe("Exact parent interface name"),
21845
- disabled_only: z108.boolean().default(false)
22301
+ name_filter: z109.string().optional().describe("Partial name match"),
22302
+ vlan_id_filter: z109.number().int().optional(),
22303
+ interface_filter: z109.string().optional().describe("Exact parent interface name"),
22304
+ disabled_only: z109.boolean().default(false)
21846
22305
  },
21847
22306
  async handler(a, ctx) {
21848
22307
  ctx.info("Listing VLAN interfaces");
@@ -21866,7 +22325,7 @@ ${result}`;
21866
22325
  title: "Get VLAN Interface Details",
21867
22326
  annotations: READ,
21868
22327
  description: 'Returns full detail for a single named VLAN interface (`/interface vlan print detail where name="..."`), including VLAN ID, parent interface, ARP mode, MTU, and running state.' + " Use when you already know the exact interface name and need all properties." + " For searching across all VLANs or filtering by parent interface or VLAN ID use `list_vlan_interfaces`.",
21869
- inputSchema: { name: z108.string() },
22328
+ inputSchema: { name: z109.string() },
21870
22329
  async handler(a, ctx) {
21871
22330
  ctx.info(`Getting VLAN interface details: name=${a.name}`);
21872
22331
  const result = await executeMikrotikCommand(`/interface vlan print detail where name="${a.name}"`, ctx);
@@ -21881,19 +22340,19 @@ ${result}`;
21881
22340
  annotations: WRITE_IDEMPOTENT,
21882
22341
  description: 'Modifies properties of an existing VLAN sub-interface (`/interface vlan set [find name="..."]`).' + " Accepts any subset of the create parameters; omit fields to leave them unchanged." + " Supply the current `name` from `list_vlan_interfaces` or `get_vlan_interface`; use `new_name` to rename the interface." + " For creating a new VLAN interface use `create_vlan_interface`; to permanently delete use `remove_vlan_interface`." + " Returns the updated interface's full detail after applying the change." + " ARP mode accepts: enabled, disabled, proxy-arp, reply-only.",
21883
22342
  inputSchema: {
21884
- name: z108.string().describe("Current name of the VLAN interface to update"),
21885
- new_name: z108.string().optional(),
21886
- vlan_id: z108.number().int().min(1).max(4094).optional(),
21887
- interface: z108.string().optional(),
21888
- comment: z108.string().optional(),
21889
- disabled: z108.boolean().optional(),
21890
- mtu: z108.number().int().optional(),
21891
- use_service_tag: z108.boolean().optional(),
22343
+ name: z109.string().describe("Current name of the VLAN interface to update"),
22344
+ new_name: z109.string().optional(),
22345
+ vlan_id: z109.number().int().min(1).max(4094).optional(),
22346
+ interface: z109.string().optional(),
22347
+ comment: z109.string().optional(),
22348
+ disabled: z109.boolean().optional(),
22349
+ mtu: z109.number().int().optional(),
22350
+ use_service_tag: z109.boolean().optional(),
21892
22351
  arp: ArpMode2.optional(),
21893
- arp_timeout: z108.string().optional(),
22352
+ arp_timeout: z109.string().optional(),
21894
22353
  loop_protect: LoopProtect.optional().describe("Loop protection: default, on, off"),
21895
- loop_protect_disable_time: z108.string().optional().describe("Time to disable interface on loop detection, e.g. '5m' (0 = forever)"),
21896
- loop_protect_send_interval: z108.string().optional().describe("Interval between loop-protect probe packets, e.g. '5s'")
22354
+ loop_protect_disable_time: z109.string().optional().describe("Time to disable interface on loop detection, e.g. '5m' (0 = forever)"),
22355
+ loop_protect_send_interval: z109.string().optional().describe("Interval between loop-protect probe packets, e.g. '5s'")
21897
22356
  },
21898
22357
  async handler(a, ctx) {
21899
22358
  ctx.info(`Updating VLAN interface: name=${a.name}`);
@@ -21915,7 +22374,7 @@ ${details}`;
21915
22374
  title: "Remove VLAN Interface",
21916
22375
  annotations: DESTRUCTIVE,
21917
22376
  description: 'Permanently deletes a VLAN sub-interface (`/interface vlan remove [find name="..."]`) after confirming it exists via a count-only check.' + " Supply the interface `name` from `list_vlan_interfaces` or `get_vlan_interface`." + " Removing a VLAN interface also removes IP addresses assigned to it; firewall rules and other configuration that reference it by name are not automatically deleted and will remain as orphaned, ineffective entries." + " To disable a VLAN temporarily without deleting it use `update_vlan_interface` with `disabled=true`.",
21918
- inputSchema: { name: z108.string() },
22377
+ inputSchema: { name: z109.string() },
21919
22378
  async handler(a, ctx) {
21920
22379
  ctx.info(`Removing VLAN interface: name=${a.name}`);
21921
22380
  const count = await executeMikrotikCommand(`/interface vlan print count-only where name="${a.name}"`, ctx);
@@ -21930,7 +22389,7 @@ ${details}`;
21930
22389
  ];
21931
22390
 
21932
22391
  // src/tools/wireguard-mesh.ts
21933
- import { z as z109 } from "zod";
22392
+ import { z as z110 } from "zod";
21934
22393
  function meshAddress(prefix, index) {
21935
22394
  const [net, len = "24"] = prefix.split("/");
21936
22395
  const octets = net.split(".");
@@ -21947,16 +22406,16 @@ var wireguardMeshTools = [
21947
22406
  annotations: DANGEROUS,
21948
22407
  description: "Stands up a WireGuard VPN across several configured devices in ONE call: ensures a WireGuard " + "interface on each, reads each device's public key, assigns every device a mesh address from " + "`address_prefix`, then wires the peers between routers \u2014 `topology=full-mesh` connects every " + "pair, `hub-spoke` connects the `hub` to each spoke \u2014 distributing public keys automatically. " + "Endpoints default to each device's configured host (override per device via `endpoints`); add " + "each site's LAN subnet via `allowed_lans` to route it through the tunnel. DEFAULTS TO A DRY RUN " + "(`apply=false`) that shows the full plan; set `apply=true` to build it. Operates across " + "MULTIPLE devices, so it is not affected by the single `device` selector. SSH devices only " + "(needs to read public keys). Returns the plan, or a per-device build report.",
21949
22408
  inputSchema: {
21950
- devices: z109.array(z109.string()).min(2).describe("Configured device names to include in the mesh (2+)"),
21951
- address_prefix: z109.string().default("10.20.0.0/24").describe("Mesh subnet; each device gets <prefix>.<index+1> on its WireGuard interface"),
21952
- interface: z109.string().default("wg-mesh").describe("WireGuard interface name to create/use"),
21953
- listen_port: z109.number().int().default(13231),
21954
- topology: z109.enum(["full-mesh", "hub-spoke"]).default("full-mesh"),
21955
- hub: z109.string().optional().describe("Hub device name (required when topology=hub-spoke)"),
21956
- endpoints: z109.record(z109.string(), z109.string()).optional().describe('Per-device public endpoint host override, e.g. {"site-a":"a.example.com"}'),
21957
- allowed_lans: z109.record(z109.string(), z109.string()).optional().describe('Per-device LAN subnet to route through the tunnel, e.g. {"site-a":"192.168.10.0/24"}'),
21958
- persistent_keepalive: z109.string().default("25s"),
21959
- apply: z109.boolean().default(false).describe("false = preview the plan (default); true = build")
22409
+ devices: z110.array(z110.string()).min(2).describe("Configured device names to include in the mesh (2+)"),
22410
+ address_prefix: z110.string().default("10.20.0.0/24").describe("Mesh subnet; each device gets <prefix>.<index+1> on its WireGuard interface"),
22411
+ interface: z110.string().default("wg-mesh").describe("WireGuard interface name to create/use"),
22412
+ listen_port: z110.number().int().default(13231),
22413
+ topology: z110.enum(["full-mesh", "hub-spoke"]).default("full-mesh"),
22414
+ hub: z110.string().optional().describe("Hub device name (required when topology=hub-spoke)"),
22415
+ endpoints: z110.record(z110.string(), z110.string()).optional().describe('Per-device public endpoint host override, e.g. {"site-a":"a.example.com"}'),
22416
+ allowed_lans: z110.record(z110.string(), z110.string()).optional().describe('Per-device LAN subnet to route through the tunnel, e.g. {"site-a":"192.168.10.0/24"}'),
22417
+ persistent_keepalive: z110.string().default("25s"),
22418
+ apply: z110.boolean().default(false).describe("false = preview the plan (default); true = build")
21960
22419
  },
21961
22420
  async handler(a, ctx) {
21962
22421
  const devices = a.devices;
@@ -22047,7 +22506,7 @@ Check handshakes per device with get_wireguard_peers / list_wireguard_peers.`;
22047
22506
 
22048
22507
  // src/tools/vpn-onboard.ts
22049
22508
  import { generateKeyPairSync } from "crypto";
22050
- import { z as z110 } from "zod";
22509
+ import { z as z111 } from "zod";
22051
22510
  function generateWireGuardKeypair() {
22052
22511
  const { privateKey, publicKey } = generateKeyPairSync("x25519");
22053
22512
  const priv = privateKey.export({ type: "pkcs8", format: "der" });
@@ -22064,13 +22523,13 @@ var vpnOnboardTools = [
22064
22523
  annotations: WRITE,
22065
22524
  description: "Generates a ready-to-use WireGuard remote-access profile for one user: creates a client " + "keypair, adds the peer to the server `interface` (allowed-address = the client's tunnel IP), " + "reads the server's public key, and returns a complete client .conf to paste into the WireGuard " + "app or a QR generator. The peer is tagged `vpn-user: <user>` so revoke_wireguard_user can remove " + "it. NOTE: the returned config contains the client PRIVATE key (required to connect) \u2014 handle it " + "securely. `endpoint` is the server's public host:port; `allowed_ips` controls split vs full " + "tunnel. SSH devices only. Returns the client config.",
22066
22525
  inputSchema: {
22067
- interface: z110.string().describe("Existing WireGuard SERVER interface, e.g. 'wg-server'"),
22068
- user: z110.string().describe("User/device label, e.g. 'alice-laptop'"),
22069
- address: z110.string().describe("Tunnel IP to assign the client, e.g. '10.20.0.50'"),
22070
- endpoint: z110.string().describe("Server public endpoint host:port, e.g. 'vpn.example.com:13231'"),
22071
- dns: z110.string().optional().describe("DNS for the client, e.g. '10.20.0.1'"),
22072
- allowed_ips: z110.string().default("0.0.0.0/0").describe("Client AllowedIPs: '0.0.0.0/0' full-tunnel, or a LAN subnet for split-tunnel"),
22073
- keepalive: z110.string().default("25").describe("PersistentKeepalive seconds")
22526
+ interface: z111.string().describe("Existing WireGuard SERVER interface, e.g. 'wg-server'"),
22527
+ user: z111.string().describe("User/device label, e.g. 'alice-laptop'"),
22528
+ address: z111.string().describe("Tunnel IP to assign the client, e.g. '10.20.0.50'"),
22529
+ endpoint: z111.string().describe("Server public endpoint host:port, e.g. 'vpn.example.com:13231'"),
22530
+ dns: z111.string().optional().describe("DNS for the client, e.g. '10.20.0.1'"),
22531
+ allowed_ips: z111.string().default("0.0.0.0/0").describe("Client AllowedIPs: '0.0.0.0/0' full-tunnel, or a LAN subnet for split-tunnel"),
22532
+ keepalive: z111.string().default("25").describe("PersistentKeepalive seconds")
22074
22533
  },
22075
22534
  async handler(a, ctx) {
22076
22535
  ctx.info(`Onboarding WireGuard user '${a.user}' on ${a.interface}`);
@@ -22108,7 +22567,7 @@ ${config}`;
22108
22567
  title: "Revoke WireGuard Remote User",
22109
22568
  annotations: DESTRUCTIVE,
22110
22569
  description: "Revokes a remote-access user created by onboard_wireguard_user: removes the WireGuard peer " + "tagged `vpn-user: <user>` from the server, immediately cutting their access. Returns whether a " + "peer was removed.",
22111
- inputSchema: { user: z110.string().describe("The user label used at onboarding") },
22570
+ inputSchema: { user: z111.string().describe("The user label used at onboarding") },
22112
22571
  async handler(a, ctx) {
22113
22572
  const count = await executeMikrotikCommand(`/interface wireguard peers print count-only where comment="vpn-user: ${a.user}"`, ctx);
22114
22573
  if (count.trim() === "0")
@@ -22122,7 +22581,7 @@ ${config}`;
22122
22581
  ];
22123
22582
 
22124
22583
  // src/tools/wireguard.ts
22125
- import { z as z111 } from "zod";
22584
+ import { z as z112 } from "zod";
22126
22585
  var wireguardTools = [
22127
22586
  defineTool({
22128
22587
  name: "create_wireguard_interface",
@@ -22130,12 +22589,12 @@ var wireguardTools = [
22130
22589
  annotations: WRITE,
22131
22590
  description: "Creates a WireGuard tunnel interface (`/interface wireguard`) \u2014 the local VPN endpoint" + " with a UDP listen port and private key. Use this to establish the server-side or site-to-site" + " WireGuard interface before adding remote peers with add_wireguard_peer." + " For IPsec tunnels use create_ipsec_peer; for L2TP use create_l2tp_client;" + " for OpenVPN use create_ovpn_client. Returns the created interface's detail including" + " its name and RouterOS-generated public key.",
22132
22591
  inputSchema: {
22133
- name: z111.string(),
22134
- listen_port: z111.number().int().optional(),
22135
- private_key: z111.string().optional(),
22136
- mtu: z111.number().int().optional(),
22137
- comment: z111.string().optional(),
22138
- disabled: z111.boolean().default(false)
22592
+ name: z112.string(),
22593
+ listen_port: z112.number().int().optional(),
22594
+ private_key: z112.string().optional(),
22595
+ mtu: z112.number().int().optional(),
22596
+ comment: z112.string().optional(),
22597
+ disabled: z112.boolean().default(false)
22139
22598
  },
22140
22599
  async handler(a, ctx) {
22141
22600
  ctx.info(`Creating WireGuard interface: name=${a.name}`);
@@ -22155,9 +22614,9 @@ ${details}` : "WireGuard interface created successfully.";
22155
22614
  annotations: READ,
22156
22615
  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.",
22157
22616
  inputSchema: {
22158
- name_filter: z111.string().optional(),
22159
- disabled_only: z111.boolean().default(false),
22160
- running_only: z111.boolean().default(false)
22617
+ name_filter: z112.string().optional(),
22618
+ disabled_only: z112.boolean().default(false),
22619
+ running_only: z112.boolean().default(false)
22161
22620
  },
22162
22621
  async handler(a, ctx) {
22163
22622
  ctx.info("Listing WireGuard interfaces");
@@ -22179,7 +22638,7 @@ ${result}`;
22179
22638
  title: "Get WireGuard Interface Details",
22180
22639
  annotations: READ,
22181
22640
  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.",
22182
- inputSchema: { name: z111.string() },
22641
+ inputSchema: { name: z112.string() },
22183
22642
  async handler(a, ctx) {
22184
22643
  ctx.info(`Getting WireGuard interface details: name=${a.name}`);
22185
22644
  const result = await executeMikrotikCommand(`/interface wireguard print detail where name="${a.name}"`, ctx);
@@ -22194,7 +22653,7 @@ ${result}`;
22194
22653
  annotations: READ,
22195
22654
  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.",
22196
22655
  inputSchema: {
22197
- interface_filter: z111.string().optional().describe("Limit to one interface name, e.g. 'wg-mesh'")
22656
+ interface_filter: z112.string().optional().describe("Limit to one interface name, e.g. 'wg-mesh'")
22198
22657
  },
22199
22658
  async handler(a, ctx) {
22200
22659
  ctx.info("Reading WireGuard status (interfaces + peers)");
@@ -22219,13 +22678,13 @@ ${peerBlock}`;
22219
22678
  annotations: WRITE_IDEMPOTENT,
22220
22679
  description: "Modifies settings on an existing WireGuard tunnel interface (`/interface wireguard set [find name=]`)" + " \u2014 rename (new_name), change listen-port, rotate private-key, adjust MTU, or toggle disabled state." + " Only supplied fields are changed; omitted fields are left as-is." + " For updating remote peers use update_wireguard_peer." + " Returns the updated interface's detail.",
22221
22680
  inputSchema: {
22222
- name: z111.string(),
22223
- new_name: z111.string().optional(),
22224
- listen_port: z111.number().int().optional(),
22225
- private_key: z111.string().optional(),
22226
- mtu: z111.number().int().optional(),
22227
- comment: z111.string().optional(),
22228
- disabled: z111.boolean().optional()
22681
+ name: z112.string(),
22682
+ new_name: z112.string().optional(),
22683
+ listen_port: z112.number().int().optional(),
22684
+ private_key: z112.string().optional(),
22685
+ mtu: z112.number().int().optional(),
22686
+ comment: z112.string().optional(),
22687
+ disabled: z112.boolean().optional()
22229
22688
  },
22230
22689
  async handler(a, ctx) {
22231
22690
  ctx.info(`Updating WireGuard interface: name=${a.name}`);
@@ -22248,7 +22707,7 @@ ${details}`;
22248
22707
  title: "Remove WireGuard Interface",
22249
22708
  annotations: DESTRUCTIVE,
22250
22709
  description: "Permanently deletes a WireGuard tunnel interface (`/interface wireguard remove`) by name." + " Verifies existence first with count-only; removing the interface also removes all its associated peers." + " For removing only a specific peer without touching the interface use remove_wireguard_peer.",
22251
- inputSchema: { name: z111.string() },
22710
+ inputSchema: { name: z112.string() },
22252
22711
  async handler(a, ctx) {
22253
22712
  ctx.info(`Removing WireGuard interface: name=${a.name}`);
22254
22713
  const count = await executeMikrotikCommand(`/interface wireguard print count-only where name="${a.name}"`, ctx);
@@ -22265,7 +22724,7 @@ ${details}`;
22265
22724
  title: "Enable WireGuard Interface",
22266
22725
  annotations: WRITE_IDEMPOTENT,
22267
22726
  description: "Enables a disabled WireGuard tunnel interface (`/interface wireguard enable`) by name," + " allowing it to accept and establish tunnel connections." + " For enabling a specific peer without affecting the interface use enable_wireguard_peer." + " To undo, use disable_wireguard_interface.",
22268
- inputSchema: { name: z111.string() },
22727
+ inputSchema: { name: z112.string() },
22269
22728
  async handler(a, ctx) {
22270
22729
  ctx.info(`Enabling WireGuard interface: name=${a.name}`);
22271
22730
  const result = await executeMikrotikCommand(`/interface wireguard enable [find name="${a.name}"]`, ctx);
@@ -22279,7 +22738,7 @@ ${details}`;
22279
22738
  title: "Disable WireGuard Interface",
22280
22739
  annotations: WRITE_IDEMPOTENT,
22281
22740
  description: "Disables a WireGuard tunnel interface (`/interface wireguard disable`) by name," + " stopping all tunnel traffic through it without removing the interface or its peers." + " For disabling only a specific peer use disable_wireguard_peer." + " To re-enable use enable_wireguard_interface.",
22282
- inputSchema: { name: z111.string() },
22741
+ inputSchema: { name: z112.string() },
22283
22742
  async handler(a, ctx) {
22284
22743
  ctx.info(`Disabling WireGuard interface: name=${a.name}`);
22285
22744
  const result = await executeMikrotikCommand(`/interface wireguard disable [find name="${a.name}"]`, ctx);
@@ -22299,23 +22758,23 @@ ${details}`;
22299
22758
  ` + ` endpoint_address: remote host IP or hostname e.g. "203.0.113.1" (omit for road-warrior clients that dial in)
22300
22759
  ` + ' persistent_keepalive: seconds as string e.g. "25"',
22301
22760
  inputSchema: {
22302
- interface: z111.string(),
22303
- public_key: z111.string(),
22304
- allowed_address: z111.string().describe('CIDR, comma-separated for multiple e.g. "10.0.0.2/32" or "10.0.0.0/24,192.168.0.0/24"'),
22305
- endpoint_address: z111.string().optional().describe('remote host IP or hostname e.g. "203.0.113.1"'),
22306
- endpoint_port: z111.number().int().optional(),
22307
- preshared_key: z111.string().optional(),
22308
- persistent_keepalive: z111.string().optional().describe('seconds as string e.g. "25"'),
22309
- name: z111.string().optional().describe("optional peer name label"),
22310
- private_key: z111.string().optional().describe("peer's private key (lets the router generate this peer's client config)"),
22311
- responder: z111.boolean().optional().describe("only respond to handshakes, never initiate (for road-warrior clients)"),
22312
- client_address: z111.string().optional().describe("client tunnel address(es) for the generated client config"),
22313
- client_dns: z111.string().optional().describe("DNS server(s) written into the generated client config"),
22314
- client_endpoint: z111.string().optional().describe("server endpoint host[:port] written into the generated client config"),
22315
- client_keepalive: z111.string().optional().describe('client persistent-keepalive for the generated config e.g. "25s"'),
22316
- client_listen_port: z111.number().int().optional().describe("listen-port written into the generated client config"),
22317
- comment: z111.string().optional(),
22318
- disabled: z111.boolean().default(false)
22761
+ interface: z112.string(),
22762
+ public_key: z112.string(),
22763
+ allowed_address: z112.string().describe('CIDR, comma-separated for multiple e.g. "10.0.0.2/32" or "10.0.0.0/24,192.168.0.0/24"'),
22764
+ endpoint_address: z112.string().optional().describe('remote host IP or hostname e.g. "203.0.113.1"'),
22765
+ endpoint_port: z112.number().int().optional(),
22766
+ preshared_key: z112.string().optional(),
22767
+ persistent_keepalive: z112.string().optional().describe('seconds as string e.g. "25"'),
22768
+ name: z112.string().optional().describe("optional peer name label"),
22769
+ private_key: z112.string().optional().describe("peer's private key (lets the router generate this peer's client config)"),
22770
+ responder: z112.boolean().optional().describe("only respond to handshakes, never initiate (for road-warrior clients)"),
22771
+ client_address: z112.string().optional().describe("client tunnel address(es) for the generated client config"),
22772
+ client_dns: z112.string().optional().describe("DNS server(s) written into the generated client config"),
22773
+ client_endpoint: z112.string().optional().describe("server endpoint host[:port] written into the generated client config"),
22774
+ client_keepalive: z112.string().optional().describe('client persistent-keepalive for the generated config e.g. "25s"'),
22775
+ client_listen_port: z112.number().int().optional().describe("listen-port written into the generated client config"),
22776
+ comment: z112.string().optional(),
22777
+ disabled: z112.boolean().default(false)
22319
22778
  },
22320
22779
  async handler(a, ctx) {
22321
22780
  ctx.info(`Adding WireGuard peer: interface=${a.interface}, public_key=${a.public_key.slice(0, 12)}...`);
@@ -22335,8 +22794,8 @@ ${details}` : "WireGuard peer added successfully.";
22335
22794
  annotations: READ,
22336
22795
  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.",
22337
22796
  inputSchema: {
22338
- interface_filter: z111.string().optional(),
22339
- disabled_only: z111.boolean().default(false)
22797
+ interface_filter: z112.string().optional(),
22798
+ disabled_only: z112.boolean().default(false)
22340
22799
  },
22341
22800
  async handler(a, ctx) {
22342
22801
  ctx.info("Listing WireGuard peers");
@@ -22360,7 +22819,7 @@ ${result}`;
22360
22819
  ` + `Notes:
22361
22820
  ` + ' peer_id: the .id from list_wireguard_peers, format "*N" or "N" e.g. "*2"',
22362
22821
  inputSchema: {
22363
- peer_id: z111.string().describe('"*N" or "N" from list output e.g. "*2"')
22822
+ peer_id: z112.string().describe('"*N" or "N" from list output e.g. "*2"')
22364
22823
  },
22365
22824
  async handler(a, ctx) {
22366
22825
  ctx.info(`Getting WireGuard peer details: peer_id=${a.peer_id}`);
@@ -22382,22 +22841,22 @@ ${result}`;
22382
22841
  ` + ` persistent_keepalive: seconds as string e.g. "25"
22383
22842
  ` + ' Pass "" for endpoint_address or preshared_key to clear them.',
22384
22843
  inputSchema: {
22385
- peer_id: z111.string().describe('"*N" or "N" from list output e.g. "*2"'),
22386
- allowed_address: z111.string().optional(),
22387
- endpoint_address: z111.string().optional(),
22388
- endpoint_port: z111.number().int().optional(),
22389
- preshared_key: z111.string().optional(),
22390
- persistent_keepalive: z111.string().optional(),
22391
- name: z111.string().optional().describe("optional peer name label"),
22392
- private_key: z111.string().optional().describe("peer's private key (lets the router generate this peer's client config)"),
22393
- responder: z111.boolean().optional().describe("only respond to handshakes, never initiate"),
22394
- client_address: z111.string().optional().describe("client tunnel address(es) for the generated client config"),
22395
- client_dns: z111.string().optional().describe("DNS server(s) written into the generated client config"),
22396
- client_endpoint: z111.string().optional().describe("server endpoint host[:port] written into the generated client config"),
22397
- client_keepalive: z111.string().optional().describe('client persistent-keepalive for the generated config e.g. "25s"'),
22398
- client_listen_port: z111.number().int().optional().describe("listen-port written into the generated client config"),
22399
- comment: z111.string().optional(),
22400
- disabled: z111.boolean().optional()
22844
+ peer_id: z112.string().describe('"*N" or "N" from list output e.g. "*2"'),
22845
+ allowed_address: z112.string().optional(),
22846
+ endpoint_address: z112.string().optional(),
22847
+ endpoint_port: z112.number().int().optional(),
22848
+ preshared_key: z112.string().optional(),
22849
+ persistent_keepalive: z112.string().optional(),
22850
+ name: z112.string().optional().describe("optional peer name label"),
22851
+ private_key: z112.string().optional().describe("peer's private key (lets the router generate this peer's client config)"),
22852
+ responder: z112.boolean().optional().describe("only respond to handshakes, never initiate"),
22853
+ client_address: z112.string().optional().describe("client tunnel address(es) for the generated client config"),
22854
+ client_dns: z112.string().optional().describe("DNS server(s) written into the generated client config"),
22855
+ client_endpoint: z112.string().optional().describe("server endpoint host[:port] written into the generated client config"),
22856
+ client_keepalive: z112.string().optional().describe('client persistent-keepalive for the generated config e.g. "25s"'),
22857
+ client_listen_port: z112.number().int().optional().describe("listen-port written into the generated client config"),
22858
+ comment: z112.string().optional(),
22859
+ disabled: z112.boolean().optional()
22401
22860
  },
22402
22861
  async handler(a, ctx) {
22403
22862
  ctx.info(`Updating WireGuard peer: peer_id=${a.peer_id}`);
@@ -22423,7 +22882,7 @@ ${details}`;
22423
22882
  ` + `Notes:
22424
22883
  ` + ' peer_id: the .id from list_wireguard_peers, format "*N" or "N" e.g. "*2"',
22425
22884
  inputSchema: {
22426
- peer_id: z111.string().describe('"*N" or "N" from list output e.g. "*2"')
22885
+ peer_id: z112.string().describe('"*N" or "N" from list output e.g. "*2"')
22427
22886
  },
22428
22887
  async handler(a, ctx) {
22429
22888
  ctx.info(`Removing WireGuard peer: peer_id=${a.peer_id}`);
@@ -22445,7 +22904,7 @@ ${details}`;
22445
22904
  ` + `Notes:
22446
22905
  ` + ' peer_id: the .id from list_wireguard_peers, format "*N" or "N" e.g. "*2"',
22447
22906
  inputSchema: {
22448
- peer_id: z111.string().describe('"*N" or "N" from list output e.g. "*2"')
22907
+ peer_id: z112.string().describe('"*N" or "N" from list output e.g. "*2"')
22449
22908
  },
22450
22909
  async handler(a, ctx) {
22451
22910
  ctx.info(`Enabling WireGuard peer: peer_id=${a.peer_id}`);
@@ -22464,7 +22923,7 @@ ${details}`;
22464
22923
  ` + `Notes:
22465
22924
  ` + ' peer_id: the .id from list_wireguard_peers, format "*N" or "N" e.g. "*2"',
22466
22925
  inputSchema: {
22467
- peer_id: z111.string().describe('"*N" or "N" from list output e.g. "*2"')
22926
+ peer_id: z112.string().describe('"*N" or "N" from list output e.g. "*2"')
22468
22927
  },
22469
22928
  async handler(a, ctx) {
22470
22929
  ctx.info(`Disabling WireGuard peer: peer_id=${a.peer_id}`);
@@ -22484,14 +22943,14 @@ ${details}`;
22484
22943
  ` + ` allowed_ips: CIDRs routed through the tunnel by the client, default "0.0.0.0/0" (all IPv4 traffic; add ::/0 to also route IPv6)
22485
22944
  ` + " persistent_keepalive: seconds integer, default 25",
22486
22945
  inputSchema: {
22487
- client_private_key: z111.string(),
22488
- client_address: z111.string(),
22489
- server_public_key: z111.string(),
22490
- server_endpoint: z111.string(),
22491
- server_port: z111.number().int().default(51820),
22492
- allowed_ips: z111.string().default("0.0.0.0/0"),
22493
- dns: z111.string().optional(),
22494
- persistent_keepalive: z111.number().int().default(25)
22946
+ client_private_key: z112.string(),
22947
+ client_address: z112.string(),
22948
+ server_public_key: z112.string(),
22949
+ server_endpoint: z112.string(),
22950
+ server_port: z112.number().int().default(51820),
22951
+ allowed_ips: z112.string().default("0.0.0.0/0"),
22952
+ dns: z112.string().optional(),
22953
+ persistent_keepalive: z112.number().int().default(25)
22495
22954
  },
22496
22955
  async handler(a, ctx) {
22497
22956
  ctx.info("Generating WireGuard client configuration");
@@ -22525,7 +22984,7 @@ ${details}`;
22525
22984
  ];
22526
22985
 
22527
22986
  // src/tools/wireless.ts
22528
- import { z as z112 } from "zod";
22987
+ import { z as z113 } from "zod";
22529
22988
  var V7_WIFI = ["/interface wifi", "/interface wifiwave2"];
22530
22989
  function commandUnsupported2(result) {
22531
22990
  const t = result.toLowerCase();
@@ -22556,12 +23015,12 @@ var wirelessTools = [
22556
23015
  annotations: WRITE,
22557
23016
  description: "Creates a wireless interface (`/interface wifi`, `/interface wifiwave2`, `/interface wireless`, or `/interface wlan` \u2014 auto-detected per device). " + "Use to add a new AP or station interface to the device. " + "On RouterOS v7 accepts name, ssid, disabled, and comment; on v6/legacy also requires radio_name (e.g. `wlan1`) and optionally accepts mode (default `ap-bridge`), frequency, band, channel_width, and security_profile. " + "To modify an existing interface use update_wireless_interface; to see current interfaces use list_wireless_interfaces. " + "Returns the created interface's full detail print including its `.id`.",
22558
23017
  inputSchema: {
22559
- name: z112.string(),
22560
- ssid: z112.string().optional(),
22561
- disabled: z112.boolean().default(false),
22562
- comment: z112.string().optional(),
22563
- radio_name: z112.string().optional().describe("Required for legacy wireless systems, e.g. 'wlan1'"),
22564
- mode: z112.enum([
23018
+ name: z113.string(),
23019
+ ssid: z113.string().optional(),
23020
+ disabled: z113.boolean().default(false),
23021
+ comment: z113.string().optional(),
23022
+ radio_name: z113.string().optional().describe("Required for legacy wireless systems, e.g. 'wlan1'"),
23023
+ mode: z113.enum([
22565
23024
  "ap-bridge",
22566
23025
  "bridge",
22567
23026
  "station",
@@ -22571,8 +23030,8 @@ var wirelessTools = [
22571
23030
  "ap-bridge-wds",
22572
23031
  "alignment-only"
22573
23032
  ]).optional(),
22574
- frequency: z112.string().optional(),
22575
- band: z112.enum([
23033
+ frequency: z113.string().optional(),
23034
+ band: z113.enum([
22576
23035
  "2ghz-b",
22577
23036
  "2ghz-b/g",
22578
23037
  "2ghz-b/g/n",
@@ -22584,24 +23043,24 @@ var wirelessTools = [
22584
23043
  "5ghz-n",
22585
23044
  "5ghz-ac"
22586
23045
  ]).optional(),
22587
- channel_width: z112.enum(["20mhz", "40mhz", "80mhz", "160mhz", "20/40mhz-eC", "20/40mhz-Ce"]).optional(),
22588
- security_profile: z112.string().optional(),
22589
- mtu: z112.number().int().optional().describe("Interface MTU in bytes"),
22590
- arp: z112.enum(["disabled", "enabled", "proxy-arp", "reply-only", "local-proxy-arp"]).optional().describe("ARP mode for the interface"),
22591
- hide_ssid: z112.boolean().optional().describe("Legacy: do not broadcast the SSID in beacons (AP modes)"),
22592
- wireless_protocol: z112.string().optional().describe("Legacy: wireless protocol, e.g. '802.11', 'nv2', 'nstreme'"),
22593
- scan_list: z112.string().optional().describe("Legacy: frequencies/ranges to scan (e.g. 'default' or '5180-5320')"),
22594
- frequency_mode: z112.enum(["manual-txpower", "regulatory-domain", "superchannel"]).optional().describe("Legacy: regulatory frequency mode"),
22595
- country: z112.string().optional().describe("Legacy: regulatory country setting (e.g. 'united states')"),
22596
- antenna_gain: z112.number().int().optional().describe("Legacy: antenna gain in dBi used for tx-power calculations"),
22597
- wds_mode: z112.enum(["disabled", "dynamic", "dynamic-mesh", "static", "static-mesh"]).optional().describe("Legacy: WDS mode"),
22598
- wds_default_bridge: z112.string().optional().describe("Legacy: bridge that dynamic WDS interfaces are added to"),
22599
- default_authentication: z112.boolean().optional().describe("Legacy: allow clients not in the access-list to authenticate"),
22600
- default_forwarding: z112.boolean().optional().describe("Legacy: allow client-to-client forwarding by default"),
22601
- tx_power: z112.number().int().optional().describe("Legacy: manual transmit power in dBm"),
22602
- tx_power_mode: z112.enum(["default", "card-rates", "all-rates-fixed", "manual-table"]).optional().describe("Legacy: how tx-power is determined"),
22603
- distance: z112.string().optional().describe("Legacy: link distance ('dynamic', 'indoors', or a km value)"),
22604
- disconnect_timeout: z112.string().optional().describe("Legacy: time before a non-responding client is disconnected")
23046
+ channel_width: z113.enum(["20mhz", "40mhz", "80mhz", "160mhz", "20/40mhz-eC", "20/40mhz-Ce"]).optional(),
23047
+ security_profile: z113.string().optional(),
23048
+ mtu: z113.number().int().optional().describe("Interface MTU in bytes"),
23049
+ arp: z113.enum(["disabled", "enabled", "proxy-arp", "reply-only", "local-proxy-arp"]).optional().describe("ARP mode for the interface"),
23050
+ hide_ssid: z113.boolean().optional().describe("Legacy: do not broadcast the SSID in beacons (AP modes)"),
23051
+ wireless_protocol: z113.string().optional().describe("Legacy: wireless protocol, e.g. '802.11', 'nv2', 'nstreme'"),
23052
+ scan_list: z113.string().optional().describe("Legacy: frequencies/ranges to scan (e.g. 'default' or '5180-5320')"),
23053
+ frequency_mode: z113.enum(["manual-txpower", "regulatory-domain", "superchannel"]).optional().describe("Legacy: regulatory frequency mode"),
23054
+ country: z113.string().optional().describe("Legacy: regulatory country setting (e.g. 'united states')"),
23055
+ antenna_gain: z113.number().int().optional().describe("Legacy: antenna gain in dBi used for tx-power calculations"),
23056
+ wds_mode: z113.enum(["disabled", "dynamic", "dynamic-mesh", "static", "static-mesh"]).optional().describe("Legacy: WDS mode"),
23057
+ wds_default_bridge: z113.string().optional().describe("Legacy: bridge that dynamic WDS interfaces are added to"),
23058
+ default_authentication: z113.boolean().optional().describe("Legacy: allow clients not in the access-list to authenticate"),
23059
+ default_forwarding: z113.boolean().optional().describe("Legacy: allow client-to-client forwarding by default"),
23060
+ tx_power: z113.number().int().optional().describe("Legacy: manual transmit power in dBm"),
23061
+ tx_power_mode: z113.enum(["default", "card-rates", "all-rates-fixed", "manual-table"]).optional().describe("Legacy: how tx-power is determined"),
23062
+ distance: z113.string().optional().describe("Legacy: link distance ('dynamic', 'indoors', or a km value)"),
23063
+ disconnect_timeout: z113.string().optional().describe("Legacy: time before a non-responding client is disconnected")
22605
23064
  },
22606
23065
  async handler(a, ctx) {
22607
23066
  ctx.info(`Creating wireless interface: name=${a.name}, ssid=${a.ssid}`);
@@ -22633,9 +23092,9 @@ ${details}`;
22633
23092
  annotations: READ,
22634
23093
  description: "Lists all wireless interfaces (`/interface wifi`, `/interface wifiwave2`, `/interface wireless`, `/interface wlan`) \u2014 probes every supported command path and aggregates results. " + "Filters by name_filter (substring match), disabled_only, or running_only. " + "For full property detail on a single interface use get_wireless_interface; to see currently connected clients use get_wireless_registration_table. " + "Falls back to `/interface print` with debugging info when no wireless interfaces match, to help identify the correct path on the device.",
22635
23094
  inputSchema: {
22636
- name_filter: z112.string().optional(),
22637
- disabled_only: z112.boolean().default(false),
22638
- running_only: z112.boolean().default(false)
23095
+ name_filter: z113.string().optional(),
23096
+ disabled_only: z113.boolean().default(false),
23097
+ running_only: z113.boolean().default(false)
22639
23098
  },
22640
23099
  async handler(a, ctx) {
22641
23100
  ctx.info(`Listing wireless interfaces with filters: name=${a.name_filter}`);
@@ -22689,7 +23148,7 @@ NOTE: If you see wireless interfaces above, they might be using a different comm
22689
23148
  title: "Get Wireless Interface Details",
22690
23149
  annotations: READ,
22691
23150
  description: "Retrieves the full detail of one named wireless interface (`<auto-detected path> print detail where name=...`), " + "probing `/interface wifi`, `/interface wifiwave2`, `/interface wireless`, and `/interface wlan` in order. " + "Use when you need all properties of a single interface; for a summary of all interfaces use list_wireless_interfaces. " + "Returns the complete property set for the named interface, or an error if not found.",
22692
- inputSchema: { name: z112.string() },
23151
+ inputSchema: { name: z113.string() },
22693
23152
  async handler(a, ctx) {
22694
23153
  ctx.info(`Getting wireless interface details: name=${a.name}`);
22695
23154
  const interfaceType = await detectWirelessInterfaceType(ctx);
@@ -22708,7 +23167,7 @@ ${result}`;
22708
23167
  title: "Remove Wireless Interface",
22709
23168
  annotations: DESTRUCTIVE,
22710
23169
  description: "Permanently deletes a named wireless interface (`<auto-detected path> remove [find name=...]`). " + "Verifies the interface exists first (count-only check) and returns an error if not found. " + "This action is irreversible \u2014 to keep the interface but stop traffic use disable_wireless_interface; " + "for property changes only use update_wireless_interface.",
22711
- inputSchema: { name: z112.string() },
23170
+ inputSchema: { name: z113.string() },
22712
23171
  async handler(a, ctx) {
22713
23172
  ctx.info(`Removing wireless interface: name=${a.name}`);
22714
23173
  const interfaceType = await detectWirelessInterfaceType(ctx);
@@ -22728,7 +23187,7 @@ ${result}`;
22728
23187
  title: "Enable Wireless Interface",
22729
23188
  annotations: WRITE_IDEMPOTENT,
22730
23189
  description: "Enables a named wireless interface that is currently disabled (`<auto-detected path> enable [find name=...]`). " + "Idempotent \u2014 safe to call on an already-enabled interface. " + "For the reverse operation use disable_wireless_interface; to change other settings at the same time use update_wireless_interface. " + "Obtain the interface name from list_wireless_interfaces.",
22731
- inputSchema: { name: z112.string() },
23190
+ inputSchema: { name: z113.string() },
22732
23191
  async handler(a, ctx) {
22733
23192
  ctx.info(`Enabling wireless interface: ${a.name}`);
22734
23193
  const interfaceType = await detectWirelessInterfaceType(ctx);
@@ -22745,7 +23204,7 @@ ${result}`;
22745
23204
  title: "Disable Wireless Interface",
22746
23205
  annotations: WRITE_IDEMPOTENT,
22747
23206
  description: "Disables a named wireless interface without removing it (`<auto-detected path> disable [find name=...]`). " + "Idempotent \u2014 safe to call on an already-disabled interface. " + "For the reverse operation use enable_wireless_interface; to delete the interface permanently use remove_wireless_interface. " + "Obtain the interface name from list_wireless_interfaces.",
22748
- inputSchema: { name: z112.string() },
23207
+ inputSchema: { name: z113.string() },
22749
23208
  async handler(a, ctx) {
22750
23209
  ctx.info(`Disabling wireless interface: ${a.name}`);
22751
23210
  const interfaceType = await detectWirelessInterfaceType(ctx);
@@ -22763,8 +23222,8 @@ ${result}`;
22763
23222
  annotations: READ,
22764
23223
  description: "Scans for visible nearby wireless networks/SSIDs/APs in range (`<auto-detected path> scan <interface> duration=<n>`). " + "Use to discover external networks \u2014 not to list connected clients. " + "For currently associated client stations use get_wireless_registration_table instead. " + "`interface` is the local wireless interface name to scan from (e.g. `wlan1`); `duration` is scan time in seconds (default 5). " + "Returns raw scan output from the device.",
22765
23224
  inputSchema: {
22766
- interface: z112.string(),
22767
- duration: z112.number().int().default(5)
23225
+ interface: z113.string(),
23226
+ duration: z113.number().int().default(5)
22768
23227
  },
22769
23228
  async handler(a, ctx) {
22770
23229
  ctx.info(`Scanning wireless networks on interface: ${a.interface}`);
@@ -22786,7 +23245,7 @@ ${result}`;
22786
23245
  annotations: READ,
22787
23246
  description: "Retrieves the wireless registration table \u2014 the list of client stations currently associated to this AP (`<auto-detected path> registration-table print [where interface=...]`). " + "Each row is an actively connected wireless client with its MAC address, signal strength, and statistics. " + "To discover nearby external APs/SSIDs instead use scan_wireless_networks. " + "Optionally filter by interface name; omit to return all clients across all wireless interfaces.",
22788
23247
  inputSchema: {
22789
- interface: z112.string().optional()
23248
+ interface: z113.string().optional()
22790
23249
  },
22791
23250
  async handler(a, ctx) {
22792
23251
  ctx.info(`Getting wireless registration table for interface: ${a.interface}`);
@@ -22848,7 +23307,7 @@ For legacy systems:
22848
23307
  title: "Create Wireless Security Profile (Legacy v6 Only \u2014 Not Implemented)",
22849
23308
  annotations: WRITE,
22850
23309
  description: "Stub for creating a `/interface wireless security-profiles` entry \u2014 a RouterOS v6 concept that does not exist in v7. " + "On RouterOS v7 devices (`/interface wifi` or `/interface wifiwave2`) always returns a not-supported message; security is configured directly on the interface. " + "On v6 this is also not implemented and returns an error. " + "For v6 interface creation with a security profile use create_wireless_interface (security_profile arg); for v7, security is configured directly on the interface but is not exposed by this server's wireless tools.",
22851
- inputSchema: { name: z112.string() },
23310
+ inputSchema: { name: z113.string() },
22852
23311
  async handler(_a, ctx) {
22853
23312
  const interfaceType = await detectWirelessInterfaceType(ctx);
22854
23313
  if (interfaceType && V7_WIFI.includes(interfaceType)) {
@@ -22875,7 +23334,7 @@ For legacy systems:
22875
23334
  title: "Get Wireless Security Profile (Legacy v6 Only \u2014 Not Implemented)",
22876
23335
  annotations: READ,
22877
23336
  description: "Stub for retrieving a named `/interface wireless security-profiles` entry \u2014 a RouterOS v6 concept that does not exist in v7. " + "On RouterOS v7 devices always returns a not-supported message; on v6 also not implemented. " + "Use get_wireless_interface to inspect the security settings on a v7 wireless interface instead.",
22878
- inputSchema: { name: z112.string() },
23337
+ inputSchema: { name: z113.string() },
22879
23338
  async handler(_a, ctx) {
22880
23339
  const interfaceType = await detectWirelessInterfaceType(ctx);
22881
23340
  if (interfaceType && V7_WIFI.includes(interfaceType)) {
@@ -22889,7 +23348,7 @@ For legacy systems:
22889
23348
  title: "Remove Wireless Security Profile (Legacy v6 Only \u2014 Not Implemented)",
22890
23349
  annotations: DESTRUCTIVE,
22891
23350
  description: "Stub for deleting a named `/interface wireless security-profiles` entry \u2014 a RouterOS v6 concept that does not exist in v7. " + "On RouterOS v7 devices always returns a not-supported message; on v6 also not implemented. " + "To delete a wireless interface entirely use remove_wireless_interface; to change security settings use update_wireless_interface.",
22892
- inputSchema: { name: z112.string() },
23351
+ inputSchema: { name: z113.string() },
22893
23352
  async handler(_a, ctx) {
22894
23353
  const interfaceType = await detectWirelessInterfaceType(ctx);
22895
23354
  if (interfaceType && V7_WIFI.includes(interfaceType)) {
@@ -22904,8 +23363,8 @@ For legacy systems:
22904
23363
  annotations: WRITE,
22905
23364
  description: "Stub for assigning a named security profile to a v6 `/interface wireless` interface \u2014 a RouterOS v6 concept that does not exist in v7. " + "On RouterOS v7 devices always returns a not-supported message because security is set directly on the interface. " + "On v6 also not implemented. " + "To update a wireless interface's settings on supported versions use update_wireless_interface.",
22906
23365
  inputSchema: {
22907
- interface_name: z112.string(),
22908
- security_profile: z112.string()
23366
+ interface_name: z113.string(),
23367
+ security_profile: z113.string()
22909
23368
  },
22910
23369
  async handler(_a, ctx) {
22911
23370
  const interfaceType = await detectWirelessInterfaceType(ctx);
@@ -22946,7 +23405,7 @@ For legacy systems:
22946
23405
  title: "Remove Wireless Access List Entry (Legacy v6 Only \u2014 Not Implemented)",
22947
23406
  annotations: DESTRUCTIVE,
22948
23407
  description: "Stub for removing a `/interface wireless access-list` entry by ID \u2014 a RouterOS v6 concept. " + "On RouterOS v7 devices always returns a not-supported message; on v6 also not implemented. " + "The entry_id would take the `.id` from list_wireless_access_list if that were implemented. " + "For v7 access control use list_filter_rules and remove_filter_rule instead.",
22949
- inputSchema: { entry_id: z112.string() },
23408
+ inputSchema: { entry_id: z113.string() },
22950
23409
  async handler(_a, ctx) {
22951
23410
  const interfaceType = await detectWirelessInterfaceType(ctx);
22952
23411
  if (interfaceType && V7_WIFI.includes(interfaceType)) {
@@ -22961,31 +23420,31 @@ For legacy systems:
22961
23420
  annotations: WRITE_IDEMPOTENT,
22962
23421
  description: "Updates settings on an existing named wireless interface (`<auto-detected path> set [find name=...] ...`). " + "Supports renaming (new_name), changing SSID, toggling disabled state, and updating comment. " + "Verifies the interface exists before applying; returns 'No updates specified.' if no optional fields are provided. " + "To only enable or disable the interface prefer enable_wireless_interface or disable_wireless_interface; " + "to create a new interface use create_wireless_interface; to delete one use remove_wireless_interface. " + "Returns the updated interface's full detail print.",
22963
23422
  inputSchema: {
22964
- name: z112.string(),
22965
- new_name: z112.string().optional(),
22966
- ssid: z112.string().optional(),
22967
- disabled: z112.boolean().optional(),
22968
- comment: z112.string().optional(),
22969
- mtu: z112.number().int().optional().describe("Interface MTU in bytes"),
22970
- arp: z112.enum(["disabled", "enabled", "proxy-arp", "reply-only", "local-proxy-arp"]).optional().describe("ARP mode for the interface"),
22971
- hide_ssid: z112.boolean().optional().describe("Legacy: do not broadcast the SSID in beacons (AP modes)"),
22972
- wireless_protocol: z112.string().optional().describe("Legacy: wireless protocol, e.g. '802.11', 'nv2', 'nstreme'"),
22973
- scan_list: z112.string().optional().describe("Legacy: frequencies/ranges to scan (e.g. 'default' or '5180-5320')"),
22974
- frequency: z112.string().optional().describe("Legacy: operating frequency in MHz"),
22975
- band: z112.string().optional().describe("Legacy: band, e.g. '2ghz-b/g/n' or '5ghz-a/n/ac'"),
22976
- channel_width: z112.enum(["20mhz", "40mhz", "80mhz", "160mhz", "20/40mhz-eC", "20/40mhz-Ce"]).optional().describe("Legacy: channel width"),
22977
- frequency_mode: z112.enum(["manual-txpower", "regulatory-domain", "superchannel"]).optional().describe("Legacy: regulatory frequency mode"),
22978
- country: z112.string().optional().describe("Legacy: regulatory country setting (e.g. 'united states')"),
22979
- antenna_gain: z112.number().int().optional().describe("Legacy: antenna gain in dBi used for tx-power calculations"),
22980
- wds_mode: z112.enum(["disabled", "dynamic", "dynamic-mesh", "static", "static-mesh"]).optional().describe("Legacy: WDS mode"),
22981
- wds_default_bridge: z112.string().optional().describe("Legacy: bridge that dynamic WDS interfaces are added to"),
22982
- default_authentication: z112.boolean().optional().describe("Legacy: allow clients not in the access-list to authenticate"),
22983
- default_forwarding: z112.boolean().optional().describe("Legacy: allow client-to-client forwarding by default"),
22984
- tx_power: z112.number().int().optional().describe("Legacy: manual transmit power in dBm"),
22985
- tx_power_mode: z112.enum(["default", "card-rates", "all-rates-fixed", "manual-table"]).optional().describe("Legacy: how tx-power is determined"),
22986
- distance: z112.string().optional().describe("Legacy: link distance ('dynamic', 'indoors', or a km value)"),
22987
- disconnect_timeout: z112.string().optional().describe("Legacy: time before a non-responding client is disconnected"),
22988
- security_profile: z112.string().optional().describe("Legacy: name of the security profile to apply")
23423
+ name: z113.string(),
23424
+ new_name: z113.string().optional(),
23425
+ ssid: z113.string().optional(),
23426
+ disabled: z113.boolean().optional(),
23427
+ comment: z113.string().optional(),
23428
+ mtu: z113.number().int().optional().describe("Interface MTU in bytes"),
23429
+ arp: z113.enum(["disabled", "enabled", "proxy-arp", "reply-only", "local-proxy-arp"]).optional().describe("ARP mode for the interface"),
23430
+ hide_ssid: z113.boolean().optional().describe("Legacy: do not broadcast the SSID in beacons (AP modes)"),
23431
+ wireless_protocol: z113.string().optional().describe("Legacy: wireless protocol, e.g. '802.11', 'nv2', 'nstreme'"),
23432
+ scan_list: z113.string().optional().describe("Legacy: frequencies/ranges to scan (e.g. 'default' or '5180-5320')"),
23433
+ frequency: z113.string().optional().describe("Legacy: operating frequency in MHz"),
23434
+ band: z113.string().optional().describe("Legacy: band, e.g. '2ghz-b/g/n' or '5ghz-a/n/ac'"),
23435
+ channel_width: z113.enum(["20mhz", "40mhz", "80mhz", "160mhz", "20/40mhz-eC", "20/40mhz-Ce"]).optional().describe("Legacy: channel width"),
23436
+ frequency_mode: z113.enum(["manual-txpower", "regulatory-domain", "superchannel"]).optional().describe("Legacy: regulatory frequency mode"),
23437
+ country: z113.string().optional().describe("Legacy: regulatory country setting (e.g. 'united states')"),
23438
+ antenna_gain: z113.number().int().optional().describe("Legacy: antenna gain in dBi used for tx-power calculations"),
23439
+ wds_mode: z113.enum(["disabled", "dynamic", "dynamic-mesh", "static", "static-mesh"]).optional().describe("Legacy: WDS mode"),
23440
+ wds_default_bridge: z113.string().optional().describe("Legacy: bridge that dynamic WDS interfaces are added to"),
23441
+ default_authentication: z113.boolean().optional().describe("Legacy: allow clients not in the access-list to authenticate"),
23442
+ default_forwarding: z113.boolean().optional().describe("Legacy: allow client-to-client forwarding by default"),
23443
+ tx_power: z113.number().int().optional().describe("Legacy: manual transmit power in dBm"),
23444
+ tx_power_mode: z113.enum(["default", "card-rates", "all-rates-fixed", "manual-table"]).optional().describe("Legacy: how tx-power is determined"),
23445
+ distance: z113.string().optional().describe("Legacy: link distance ('dynamic', 'indoors', or a km value)"),
23446
+ disconnect_timeout: z113.string().optional().describe("Legacy: time before a non-responding client is disconnected"),
23447
+ security_profile: z113.string().optional().describe("Legacy: name of the security profile to apply")
22989
23448
  },
22990
23449
  async handler(a, ctx) {
22991
23450
  ctx.info(`Updating wireless interface: name=${a.name}`);
@@ -23011,7 +23470,7 @@ ${details}`;
23011
23470
  ];
23012
23471
 
23013
23472
  // src/tools/wifi-optimizer.ts
23014
- import { z as z113 } from "zod";
23473
+ import { z as z114 } from "zod";
23015
23474
  function pickBestFrequency(monitor) {
23016
23475
  const cands = [];
23017
23476
  for (const line of monitor.split(`
@@ -23032,9 +23491,9 @@ var wifiOptimizerTools = [
23032
23491
  annotations: WRITE,
23033
23492
  description: "Runs an RF survey on a wireless radio (`/interface wireless frequency-monitor`, blocking for " + "`duration`), finds the least-congested channel, and reports a before/after. DEFAULTS TO A DRY " + "RUN (`apply=false`) \u2014 it surveys and recommends; set `apply=true` to set the radio to the best " + "frequency (`/interface wireless set`). Targets the LEGACY wireless stack; for wifiwave2 " + "(`/interface wifi`) tune via those tools. Returns the per-channel usage finding and the chosen " + "frequency.",
23034
23493
  inputSchema: {
23035
- interface: z113.string().describe("Wireless interface, e.g. 'wlan1'"),
23036
- duration: z113.string().default("5").describe("Survey duration in seconds"),
23037
- apply: z113.boolean().default(false).describe("false = survey & recommend (default); true = set it")
23494
+ interface: z114.string().describe("Wireless interface, e.g. 'wlan1'"),
23495
+ duration: z114.string().default("5").describe("Survey duration in seconds"),
23496
+ apply: z114.boolean().default(false).describe("false = survey & recommend (default); true = set it")
23038
23497
  },
23039
23498
  async handler(a, ctx) {
23040
23499
  ctx.info(`Wi-Fi survey on ${a.interface} for ${a.duration}s`);
@@ -23585,6 +24044,13 @@ var moduleCatalog = [
23585
24044
  description: "Built-in RADIUS server: users, profiles, routers (NAS), limitations, sessions (`/user-manager`).",
23586
24045
  tools: userManagerTools
23587
24046
  },
24047
+ {
24048
+ label: "RADIUS & UM Dashboard",
24049
+ slug: "aaa-dashboard",
24050
+ group: "AAA",
24051
+ description: "Interactive MCP App dashboard to manage RADIUS + User Manager (servers, users, profiles, " + "limitations, NAS, assignments, sessions, settings) with full add/edit/remove.",
24052
+ tools: aaaViewTools
24053
+ },
23588
24054
  {
23589
24055
  label: "Queues / QoS",
23590
24056
  slug: "queue",
@@ -23873,7 +24339,7 @@ function selectToolModules(filter = {}, catalog = moduleCatalog) {
23873
24339
  // package.json
23874
24340
  var package_default = {
23875
24341
  name: "@usex/mikrotik-mcp",
23876
- version: "3.24.0",
24342
+ version: "3.26.0",
23877
24343
  description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
23878
24344
  keywords: [
23879
24345
  "ai",