@usex/mikrotik-mcp 5.2.0 → 5.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usex/mikrotik-mcp",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "description": "MCP server for MikroTik RouterOS — 780+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
5
5
  "keywords": [
6
6
  "ai",
@@ -56,7 +56,7 @@
56
56
  "prepack": "bunup && chmod +x dist/cli.js && bun run build:ui && bun run gen",
57
57
  "auth-check": "bun run --bun src/cli.ts auth-check",
58
58
  "gen:schemas": "bun run --bun scripts/gen-schemas.ts && vp fmt --write schemas",
59
- "gen:docs": "bun run --bun scripts/gen-tool-docs.ts",
59
+ "gen:docs": "bun run --bun scripts/gen-tool-docs.ts && vp fmt --write docs/tools-reference.md",
60
60
  "gen": "bun run --bun gen:schemas && bun run --bun gen:docs",
61
61
  "start": "bun run --bun src/cli.ts serve",
62
62
  "discover": "bun run --bun scripts/discover-macs.ts",
@@ -74,6 +74,7 @@
74
74
  "prepublish:security": "npm run security:all",
75
75
  "test": "vp test run",
76
76
  "test:watch": "vp test",
77
+ "test:coverage": "vp test --ui --coverage --watch",
77
78
  "release": "release-it",
78
79
  "test:types": "tsc --noEmit",
79
80
  "check": "vp check",
@@ -104,6 +105,8 @@
104
105
  "@types/react-dom": "^19.2.4",
105
106
  "@types/ssh2": "^1.15.5",
106
107
  "@types/update-notifier": "^6.0.8",
108
+ "@vitest/coverage-v8": "4.1.10",
109
+ "@vitest/ui": "4.1.10",
107
110
  "bunup": "^0.16.32",
108
111
  "class-variance-authority": "^0.7.1",
109
112
  "clsx": "^2.1.1",
@@ -47,8 +47,20 @@ identical on both ends):
47
47
  accepted from {{peer_address}} on the input chain, and add a NAT _bypass_
48
48
  (accept/no-nat) rule so {{local_subnet}}→{{remote_subnet}} traffic is NOT
49
49
  masqueraded. Apply firewall edits under `enable_safe_mode`.
50
- 7. **Verify** — `get_ipsec_active_peers` and `get_ipsec_installed_sa` to confirm
51
- the tunnel established; `ping` a remote host with src-address in {{local_subnet}}.
50
+ 7. **MTU / MSS** — policy-mode IPsec has no interface whose MTU you can lower, so
51
+ MSS clamping is the _only_ lever, and skipping it is the classic "tunnel is up
52
+ but large transfers hang" failure. ESP tunnel mode costs ~73 bytes (more with
53
+ NAT-T/UDP-4500). Add `create_mangle_rule`: `chain=forward`, `protocol=tcp`,
54
+ `tcp_flags=syn`, `tcp_mss=1400-65535`, `action=change-mss`,
55
+ `new_mss=clamp-to-pmtu` — or a fixed `new_mss=1360` when the path MTU is known
56
+ and PMTU discovery is unreliable. Endpoints size their MSS from their own LAN
57
+ MTU and set DF; without the clamp those packets are dropped in transit and the
58
+ ICMP "fragmentation needed" is usually filtered, so the sender never learns.
59
+ 8. **Verify** — `get_ipsec_active_peers` and `get_ipsec_installed_sa` to confirm
60
+ the tunnel established; `ping` a remote host with src-address in
61
+ {{local_subnet}}. Then repeat with size 1400 and `do-not-fragment` — small
62
+ pings passing while large ones fail means the MSS clamp in step 7 is missing or
63
+ not matching.
52
64
 
53
65
  Present the matching parameter set for the remote engineer and the exact tool
54
66
  calls before applying. Never echo the pre-shared key back in plaintext beyond
@@ -33,7 +33,19 @@ Build order:
33
33
  4. **Enable the server** — `set_l2tp_server` with `enabled=true`,
34
34
  `default_profile=l2tp-profile`, `use_ipsec=required`, and a strong
35
35
  `ipsec_secret` (this is the IPsec pre-shared key clients enter).
36
- `authentication=mschap2`.
36
+ `authentication=mschap2`. Also set on the same call:
37
+ - `max_mtu`/`max_mru` = **1400** (L2TP + IPsec ESP overhead easily exceeds
38
+ 100 bytes; leaving 1450 causes the "connects fine, transfers stall" symptom),
39
+ - `keepalive_timeout` = **30** — dead client sessions otherwise linger and hold
40
+ their pool address until the default timeout expires.
41
+
42
+ The `change_tcp_mss=yes` set on the profile in step 2 makes MSS follow the
43
+ negotiated MTU automatically — that is the PPP-family equivalent of a
44
+ `change-mss` mangle rule, and it is why L2TP does not need one. If clients
45
+ still stall on large transfers, add the mangle rule anyway
46
+ (`create_mangle_rule`: `chain=forward`, `protocol=tcp`, `tcp_flags=syn`,
47
+ `tcp_mss=1400-65535`, `action=change-mss`, `new_mss=clamp-to-pmtu`).
48
+
37
49
  5. **Firewall** — accept UDP 500, UDP 4500, UDP 1701, and IP protocol 50 (ESP)
38
50
  on the input chain from the internet; allow the {{vpn_pool}} range to reach the
39
51
  LAN/internet in the forward chain as required. Apply under `enable_safe_mode`,
@@ -123,11 +123,14 @@ Per side, under Safe Mode (`enable_safe_mode` `device=<name>` → edits → veri
123
123
  WireGuard adds ~60 bytes of overhead. Small pings work but large flows (TLS, file
124
124
  transfer) stall if MTU is wrong:
125
125
 
126
- - Set the wg interface MTU to **1420** (1412 if the WAN is PPPoE) via
126
+ - Set the wg interface MTU to **1420** (1412 if the WAN is PPPoE, 1280 if the path
127
+ is unknown/multi-hop — 1280 is the always-safe floor) via
127
128
  `update_wireguard_interface`.
128
- - Clamp TCP MSS on the `forward` chain (`create_filter_rule` mangle
129
- `action=change-mss new-mss=clamp-to-pmtu tcp-flags=syn`) or set it per the
130
- interface MTU, so TCP sessions negotiate a size that fits.
129
+ - Clamp TCP MSS with `create_mangle_rule` (**not** `create_filter_rule` — only the
130
+ mangle table has `change-mss`): `chain=forward`, `protocol=tcp`,
131
+ `tcp_flags=syn`, `tcp_mss=1400-65535`, `action=change-mss`,
132
+ `new_mss=clamp-to-pmtu`. WireGuard has no per-interface MSS option, so this
133
+ mangle rule is the only place to fix MSS.
131
134
 
132
135
  ## 8. Verify end to end
133
136
 
@@ -42,5 +42,26 @@ Steps:
42
42
  5. **Client config** — call `generate_wireguard_client_config` with the server
43
43
  public key, {{endpoint}}, the listen port, and the assigned client address, and
44
44
  present the resulting `[Interface]/[Peer]` config for the user to import.
45
+ 6. **Keepalive** — set `persistent_keepalive` to `25` on every peer
46
+ (`add_wireguard_peer`/`update_wireguard_peer`) and `client_keepalive=25s` in the
47
+ generated client config. Roaming clients sit behind NAT; a UDP mapping typically
48
+ expires after ~30 s of silence, after which the server cannot reach the client
49
+ until the client speaks first. 25 s keeps the mapping alive.
50
+ 7. **MTU / MSS** — the step most WireGuard deployments skip, and the reason a VPN
51
+ "works" for logins but stalls on file transfers, photo/video uploads and some
52
+ web pages:
53
+ - WireGuard adds ~60 bytes. Set the interface MTU to **1420**
54
+ (`update_wireguard_interface`); use **1412** on a PPPoE WAN, or **1280** when
55
+ the path is unknown — 1280 always fits.
56
+ - MTU alone is not enough. Endpoints derive their TCP MSS from _their own_ NIC
57
+ MTU, so a 1460-byte-MSS session still emits oversized packets with DF set;
58
+ mid-path routers drop rather than fragment and the ICMP "fragmentation needed"
59
+ is often filtered — a PMTU black hole. Clamp it with `create_mangle_rule`:
60
+ `chain=forward`, `protocol=tcp`, `tcp_flags=syn`, `tcp_mss=1400-65535`,
61
+ `action=change-mss`, `new_mss=clamp-to-pmtu`. WireGuard has no per-interface
62
+ MSS setting, so mangle is the only place this can be fixed.
63
+ - Verify: `ping` a host across the tunnel with size 1400 and `do-not-fragment`.
64
+ Small pings succeeding while this fails is the black-hole signature.
45
65
 
46
- Report the server public key, the peer you added, and the full client config.
66
+ Report the server public key, the peer you added, the MTU/MSS/keepalive values
67
+ applied, and the full client config.
package/schemas/README.md CHANGED
@@ -7,7 +7,7 @@ edit by hand — regenerate instead.
7
7
  | File | Contents |
8
8
  | -------------------- | ------------------------------------------------------------------------------------------ |
9
9
  | `config.schema.json` | The runtime configuration object (env vars / CLI flags). |
10
- | `tool-catalog.json` | Every one of the 885 tools: `name`, `risk`, `title`, `description`, and input JSON Schema. |
10
+ | `tool-catalog.json` | Every one of the 891 tools: `name`, `risk`, `title`, `description`, and input JSON Schema. |
11
11
  | `tools/<name>.json` | The input JSON Schema for a single tool. |
12
12
 
13
13
  `risk` is derived from the MCP tool annotations:
@@ -686,6 +686,59 @@
686
686
  "default": false,
687
687
  "type": "boolean"
688
688
  },
689
+ "access": {
690
+ "default": {
691
+ "enabled": false,
692
+ "devices": [],
693
+ "denyDevices": [],
694
+ "tools": [],
695
+ "denyTools": []
696
+ },
697
+ "type": "object",
698
+ "properties": {
699
+ "enabled": {
700
+ "default": false,
701
+ "type": "boolean"
702
+ },
703
+ "maxRisk": {
704
+ "type": "string",
705
+ "enum": ["READ", "WRITE", "WRITE_IDEMPOTENT", "DESTRUCTIVE", "DANGEROUS"]
706
+ },
707
+ "devices": {
708
+ "default": [],
709
+ "type": "array",
710
+ "items": {
711
+ "type": "string"
712
+ }
713
+ },
714
+ "denyDevices": {
715
+ "default": [],
716
+ "type": "array",
717
+ "items": {
718
+ "type": "string"
719
+ }
720
+ },
721
+ "tools": {
722
+ "default": [],
723
+ "type": "array",
724
+ "items": {
725
+ "type": "string"
726
+ }
727
+ },
728
+ "denyTools": {
729
+ "default": [],
730
+ "type": "array",
731
+ "items": {
732
+ "type": "string"
733
+ }
734
+ },
735
+ "label": {
736
+ "type": "string"
737
+ }
738
+ },
739
+ "required": ["enabled", "devices", "denyDevices", "tools", "denyTools"],
740
+ "additionalProperties": false
741
+ },
689
742
  "tools": {
690
743
  "default": {
691
744
  "enabledModules": [],
@@ -765,6 +818,7 @@
765
818
  "attacks",
766
819
  "ssh",
767
820
  "readOnly",
821
+ "access",
768
822
  "tools",
769
823
  "memory",
770
824
  "disableUpdateCheck"
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "generated": "by scripts/gen-schemas.ts — do not edit by hand",
5
- "toolCount": 885,
5
+ "toolCount": 891,
6
6
  "tools": [
7
7
  {
8
8
  "name": "find_tools",
@@ -24868,7 +24868,7 @@
24868
24868
  "idempotentHint": true,
24869
24869
  "openWorldHint": false
24870
24870
  },
24871
- "description": "Autonomously investigate a network problem across all diagnostic dimensions: connectivity (ping), interface state & error counters, routing table & BGP/OSPF neighbors, firewall rules & hit counters, NAT & connection tracking, ARP/DHCP state, DNS resolution, CPU/memory pressure, system logs, and VPN tunnel state. Correlates the evidence to deliver ranked root-cause hypotheses with confidence levels, plain-language explanations, and exact RouterOS fix commands. Pass an IP address, hostname, or symptom description as the target. For hop-by-hop path analysis use `trace_path`; for log-specific investigation use `correlate_events`; for fix commands only use `suggest_fix`.",
24871
+ "description": "Autonomously investigate a network problem across all diagnostic dimensions: connectivity (ping), interface state & error counters, routing table & BGP/OSPF neighbors, firewall rules & hit counters, NAT & connection tracking, ARP/DHCP state, DNS resolution, CPU/memory pressure, system logs, and VPN tunnel state — including the tunnel MTU / TCP-MSS-clamp / WireGuard-keepalive checks that catch a PMTU black hole (tunnel pings fine but large transfers, uploads and some HTTPS hang). Correlates the evidence to deliver ranked root-cause hypotheses with confidence levels, plain-language explanations, and exact RouterOS fix commands. Pass an IP address, hostname, or symptom description as the target. For hop-by-hop path analysis use `trace_path`; for log-specific investigation use `correlate_events`; for fix commands only use `suggest_fix`.",
24872
24872
  "inputSchema": {
24873
24873
  "$schema": "https://json-schema.org/draft/2020-12/schema",
24874
24874
  "type": "object",
@@ -25223,6 +25223,198 @@
25223
25223
  "additionalProperties": false
25224
25224
  }
25225
25225
  },
25226
+ {
25227
+ "name": "map_l2_fabric",
25228
+ "title": "Map Hosts to Physical Switch Ports",
25229
+ "risk": "read",
25230
+ "annotations": {
25231
+ "readOnlyHint": true,
25232
+ "idempotentHint": true,
25233
+ "openWorldHint": false
25234
+ },
25235
+ "description": "Builds a PORT-LEVEL Layer-2 map: which hosts are behind each bridge port, joined from the bridge host table (`/interface bridge host`), ARP, DHCP leases and the neighbour cache so every MAC gets a real name — hostname, RouterOS identity, IP or hardware vendor. Also classifies each port as access (one host), uplink (many hosts, or a discovered network device behind it) or hybrid. Use this to answer 'what is plugged into ether5', to find unexpected devices on the fabric, or to spot an unmanaged switch someone added. This is different from list_neighbors, which only sees MNDP/CDP/LLDP speakers — the bridge host table sees EVERY host that has passed a frame, including printers, IoT and laptops. Read-only.",
25236
+ "inputSchema": {
25237
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
25238
+ "type": "object",
25239
+ "properties": {
25240
+ "interface_filter": {
25241
+ "description": "Only show ports whose name contains this substring.",
25242
+ "type": "string"
25243
+ },
25244
+ "role": {
25245
+ "description": "Only show ports classified with this role.",
25246
+ "type": "string",
25247
+ "enum": ["access", "uplink", "hybrid"]
25248
+ },
25249
+ "summary_only": {
25250
+ "description": "Show per-port counts without listing individual hosts.",
25251
+ "type": "boolean"
25252
+ }
25253
+ },
25254
+ "additionalProperties": false
25255
+ }
25256
+ },
25257
+ {
25258
+ "name": "locate_host_port",
25259
+ "title": "Find Which Port a Host Is Plugged Into",
25260
+ "risk": "read",
25261
+ "annotations": {
25262
+ "readOnlyHint": true,
25263
+ "idempotentHint": true,
25264
+ "openWorldHint": false
25265
+ },
25266
+ "description": "Locates a specific host on the Layer-2 fabric and reports the exact bridge port it is reachable through. Accepts a MAC address (any separator style), an IP address, or a hostname/identity substring. This is the 'my printer is 192.168.1.40, which switch port is it on?' lookup — it resolves the IP to a MAC via ARP/DHCP and then to a port via the bridge host table. Returns every match, since a MAC can legitimately appear behind an uplink as well as on its access port. Read-only.",
25267
+ "inputSchema": {
25268
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
25269
+ "type": "object",
25270
+ "properties": {
25271
+ "query": {
25272
+ "type": "string",
25273
+ "description": "MAC address, IP address, or hostname/identity substring to locate."
25274
+ }
25275
+ },
25276
+ "required": ["query"],
25277
+ "additionalProperties": false
25278
+ }
25279
+ },
25280
+ {
25281
+ "name": "audit_known_vulnerabilities",
25282
+ "title": "Audit Device Against Known RouterOS Advisories",
25283
+ "risk": "read",
25284
+ "annotations": {
25285
+ "readOnlyHint": true,
25286
+ "idempotentHint": true,
25287
+ "openWorldHint": false
25288
+ },
25289
+ "description": "Cross-references the RouterOS version this device runs against published security advisories (CVEs), then RANKS each match by whether the affected surface is actually reachable — reading `/ip service` to see if the vulnerable service is enabled and whether it is restricted to a management subnet. This is the difference between 'you are behind on patches' and 'this specific hole is open to the internet right now'. Returns each matching advisory with its severity, CVSS, effective risk score, exposure, the exact version that fixes it, and the remediation steps. Read-only — reads `/system resource`, `/ip service` and `/system package`, changes nothing. Use `firmware_check` for 'is there a newer release'; use this for 'does it matter'. Set `all_devices` to audit the whole fleet at once.",
25290
+ "inputSchema": {
25291
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
25292
+ "type": "object",
25293
+ "properties": {
25294
+ "all_devices": {
25295
+ "description": "Audit every configured device instead of just the target device.",
25296
+ "type": "boolean"
25297
+ },
25298
+ "min_severity": {
25299
+ "description": "Only report findings at or above this severity.",
25300
+ "type": "string",
25301
+ "enum": ["critical", "high", "medium", "low"]
25302
+ },
25303
+ "include_mitigated": {
25304
+ "description": "Include advisories whose affected service is disabled (default false — these are matches on version alone and are not currently reachable).",
25305
+ "type": "boolean"
25306
+ }
25307
+ },
25308
+ "additionalProperties": false
25309
+ }
25310
+ },
25311
+ {
25312
+ "name": "list_security_advisories",
25313
+ "title": "List the Bundled RouterOS Advisory Dataset",
25314
+ "risk": "read",
25315
+ "annotations": {
25316
+ "readOnlyHint": true,
25317
+ "idempotentHint": true,
25318
+ "openWorldHint": false
25319
+ },
25320
+ "description": "Lists the RouterOS security advisories this server checks against, with their affected version ranges, severity, CVSS, exposure conditions and remediation. Contacts NO device — it is the reference dataset itself, useful for answering 'what does the audit actually cover?' and for checking whether a specific CVE is in scope before trusting a clean audit result. Use audit_known_vulnerabilities to evaluate these against a real device.",
25321
+ "inputSchema": {
25322
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
25323
+ "type": "object",
25324
+ "properties": {
25325
+ "id_filter": {
25326
+ "description": "Match an advisory id (substring, e.g. `2023`).",
25327
+ "type": "string"
25328
+ }
25329
+ },
25330
+ "additionalProperties": false
25331
+ }
25332
+ },
25333
+ {
25334
+ "name": "get_access_scope",
25335
+ "title": "Show the Active Access Scope",
25336
+ "risk": "read",
25337
+ "annotations": {
25338
+ "readOnlyHint": true,
25339
+ "idempotentHint": true,
25340
+ "openWorldHint": false
25341
+ },
25342
+ "description": "Reports the access scope currently enforced on this session: the maximum risk tier that may be invoked, which devices may be targeted, which tool-name globs are allowed or denied, and when the scope expires. Also lists recent DENIED calls with the rule that blocked each one. Call this after an 'Access denied' result to see exactly what the boundary is instead of guessing, and before attempting a batch of writes to confirm they are permitted. Contacts no device.",
25343
+ "inputSchema": {
25344
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
25345
+ "type": "object",
25346
+ "properties": {
25347
+ "denials": {
25348
+ "description": "How many recent denials to include (default 10, 0 to omit).",
25349
+ "type": "integer",
25350
+ "minimum": 0,
25351
+ "maximum": 200
25352
+ }
25353
+ },
25354
+ "additionalProperties": false
25355
+ }
25356
+ },
25357
+ {
25358
+ "name": "narrow_access_scope",
25359
+ "title": "Narrow This Session's Access Scope",
25360
+ "risk": "write-idempotent",
25361
+ "annotations": {
25362
+ "destructiveHint": false,
25363
+ "idempotentHint": true,
25364
+ "openWorldHint": false
25365
+ },
25366
+ "description": "Voluntarily restricts what THIS session may do for the rest of its life — lower the risk ceiling, limit which devices may be targeted, restrict tool names, or set an expiry. Useful before handing control to an unattended loop, or to self-limit to read-only while investigating a production issue. This operation is STRICTLY ONE-WAY: every field can only make the scope narrower, and there is no tool that widens it again — only the operator's server configuration sets the ceiling, and restarting the session is the only way back. Values that would widen the scope are silently clamped to the current one. Contacts no device.",
25367
+ "inputSchema": {
25368
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
25369
+ "type": "object",
25370
+ "properties": {
25371
+ "max_risk": {
25372
+ "description": "Highest risk tier to allow from now on. Ignored if it is higher than the current ceiling. READ is the most restrictive.",
25373
+ "type": "string",
25374
+ "enum": ["READ", "WRITE", "WRITE_IDEMPOTENT", "DESTRUCTIVE", "DANGEROUS"]
25375
+ },
25376
+ "devices": {
25377
+ "description": "Restrict to these device keys (intersected with any current restriction).",
25378
+ "type": "array",
25379
+ "items": {
25380
+ "type": "string"
25381
+ }
25382
+ },
25383
+ "deny_devices": {
25384
+ "description": "Device keys to forbid outright (added to any existing denials).",
25385
+ "type": "array",
25386
+ "items": {
25387
+ "type": "string"
25388
+ }
25389
+ },
25390
+ "tools": {
25391
+ "description": "Tool-name globs to permit, e.g. ['list_*','get_*','diagnose'].",
25392
+ "type": "array",
25393
+ "items": {
25394
+ "type": "string"
25395
+ }
25396
+ },
25397
+ "deny_tools": {
25398
+ "description": "Tool-name globs to forbid (added to any existing denials).",
25399
+ "type": "array",
25400
+ "items": {
25401
+ "type": "string"
25402
+ }
25403
+ },
25404
+ "expires_in_minutes": {
25405
+ "description": "Expire the scope after this many minutes (max 7 days).",
25406
+ "type": "integer",
25407
+ "exclusiveMinimum": 0,
25408
+ "maximum": 10080
25409
+ },
25410
+ "label": {
25411
+ "description": "A note recorded with the scope, shown in denials.",
25412
+ "type": "string"
25413
+ }
25414
+ },
25415
+ "additionalProperties": false
25416
+ }
25417
+ },
25226
25418
  {
25227
25419
  "name": "get_bandwidth_server",
25228
25420
  "title": "Get Bandwidth Test Server Settings",
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "audit_known_vulnerabilities",
4
+ "type": "object",
5
+ "properties": {
6
+ "all_devices": {
7
+ "description": "Audit every configured device instead of just the target device.",
8
+ "type": "boolean"
9
+ },
10
+ "min_severity": {
11
+ "description": "Only report findings at or above this severity.",
12
+ "type": "string",
13
+ "enum": ["critical", "high", "medium", "low"]
14
+ },
15
+ "include_mitigated": {
16
+ "description": "Include advisories whose affected service is disabled (default false — these are matches on version alone and are not currently reachable).",
17
+ "type": "boolean"
18
+ }
19
+ },
20
+ "additionalProperties": false
21
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "get_access_scope",
4
+ "type": "object",
5
+ "properties": {
6
+ "denials": {
7
+ "description": "How many recent denials to include (default 10, 0 to omit).",
8
+ "type": "integer",
9
+ "minimum": 0,
10
+ "maximum": 200
11
+ }
12
+ },
13
+ "additionalProperties": false
14
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "list_security_advisories",
4
+ "type": "object",
5
+ "properties": {
6
+ "id_filter": {
7
+ "description": "Match an advisory id (substring, e.g. `2023`).",
8
+ "type": "string"
9
+ }
10
+ },
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "locate_host_port",
4
+ "type": "object",
5
+ "properties": {
6
+ "query": {
7
+ "type": "string",
8
+ "description": "MAC address, IP address, or hostname/identity substring to locate."
9
+ }
10
+ },
11
+ "required": ["query"],
12
+ "additionalProperties": false
13
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "map_l2_fabric",
4
+ "type": "object",
5
+ "properties": {
6
+ "interface_filter": {
7
+ "description": "Only show ports whose name contains this substring.",
8
+ "type": "string"
9
+ },
10
+ "role": {
11
+ "description": "Only show ports classified with this role.",
12
+ "type": "string",
13
+ "enum": ["access", "uplink", "hybrid"]
14
+ },
15
+ "summary_only": {
16
+ "description": "Show per-port counts without listing individual hosts.",
17
+ "type": "boolean"
18
+ }
19
+ },
20
+ "additionalProperties": false
21
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "narrow_access_scope",
4
+ "type": "object",
5
+ "properties": {
6
+ "max_risk": {
7
+ "description": "Highest risk tier to allow from now on. Ignored if it is higher than the current ceiling. READ is the most restrictive.",
8
+ "type": "string",
9
+ "enum": ["READ", "WRITE", "WRITE_IDEMPOTENT", "DESTRUCTIVE", "DANGEROUS"]
10
+ },
11
+ "devices": {
12
+ "description": "Restrict to these device keys (intersected with any current restriction).",
13
+ "type": "array",
14
+ "items": {
15
+ "type": "string"
16
+ }
17
+ },
18
+ "deny_devices": {
19
+ "description": "Device keys to forbid outright (added to any existing denials).",
20
+ "type": "array",
21
+ "items": {
22
+ "type": "string"
23
+ }
24
+ },
25
+ "tools": {
26
+ "description": "Tool-name globs to permit, e.g. ['list_*','get_*','diagnose'].",
27
+ "type": "array",
28
+ "items": {
29
+ "type": "string"
30
+ }
31
+ },
32
+ "deny_tools": {
33
+ "description": "Tool-name globs to forbid (added to any existing denials).",
34
+ "type": "array",
35
+ "items": {
36
+ "type": "string"
37
+ }
38
+ },
39
+ "expires_in_minutes": {
40
+ "description": "Expire the scope after this many minutes (max 7 days).",
41
+ "type": "integer",
42
+ "exclusiveMinimum": 0,
43
+ "maximum": 10080
44
+ },
45
+ "label": {
46
+ "description": "A note recorded with the scope, shown in denials.",
47
+ "type": "string"
48
+ }
49
+ },
50
+ "additionalProperties": false
51
+ }