@usex/mikrotik-mcp 3.5.0 → 3.7.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.
Files changed (42) hide show
  1. package/dist/cli.js +4027 -2098
  2. package/dist/index.js +4023 -2094
  3. package/dist/ui/observability.html +2 -2
  4. package/package.json +1 -1
  5. package/schemas/README.md +1 -1
  6. package/schemas/tool-catalog.json +1539 -159
  7. package/schemas/tools/add_dhcp_client.json +33 -0
  8. package/schemas/tools/apply_traffic_shaping.json +48 -0
  9. package/schemas/tools/audit_certificate_expiry.json +16 -0
  10. package/schemas/tools/audit_firewall_hardening.json +7 -0
  11. package/schemas/tools/build_guest_hotspot.json +48 -0
  12. package/schemas/tools/build_wireguard_mesh.json +79 -0
  13. package/schemas/tools/design_network_segment.json +65 -0
  14. package/schemas/tools/force_cloud_update.json +7 -0
  15. package/schemas/tools/forecast_link_saturation.json +23 -0
  16. package/schemas/tools/forward_port.json +45 -0
  17. package/schemas/tools/generate_hotspot_vouchers.json +33 -0
  18. package/schemas/tools/get_dhcp_client.json +13 -0
  19. package/schemas/tools/get_ip_cloud.json +7 -0
  20. package/schemas/tools/harden_firewall.json +104 -0
  21. package/schemas/tools/list_dhcp_clients.json +12 -0
  22. package/schemas/tools/onboard_wireguard_user.json +39 -0
  23. package/schemas/tools/remove_dhcp_client.json +12 -0
  24. package/schemas/tools/remove_firewall_hardening.json +7 -0
  25. package/schemas/tools/remove_threat_feed.json +13 -0
  26. package/schemas/tools/remove_time_policy.json +13 -0
  27. package/schemas/tools/renew_dhcp_client.json +17 -0
  28. package/schemas/tools/request_letsencrypt_certificate.json +13 -0
  29. package/schemas/tools/revoke_wireguard_user.json +13 -0
  30. package/schemas/tools/run_failover_drill.json +33 -0
  31. package/schemas/tools/set_dhcp_client.json +24 -0
  32. package/schemas/tools/set_ip_cloud.json +16 -0
  33. package/schemas/tools/set_ip_cloud_advanced.json +13 -0
  34. package/schemas/tools/set_time_policy.json +47 -0
  35. package/schemas/tools/setup_port_knock.json +39 -0
  36. package/schemas/tools/setup_wan_failover.json +36 -0
  37. package/schemas/tools/setup_wan_loadbalance.json +32 -0
  38. package/schemas/tools/subscribe_threat_feed.json +37 -0
  39. package/schemas/tools/test_ssh_from_device.json +28 -0
  40. package/schemas/tools/test_ssh_to_device.json +7 -0
  41. package/schemas/tools/test_ssh_to_host.json +38 -0
  42. package/schemas/tools/tune_wifi_channel.json +23 -0
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "generated": "by scripts/gen-schemas.ts — do not edit by hand",
5
- "toolCount": 670,
5
+ "toolCount": 706,
6
6
  "tools": [
7
7
  {
8
8
  "name": "list_interfaces",
@@ -312,6 +312,80 @@
312
312
  "additionalProperties": false
313
313
  }
314
314
  },
315
+ {
316
+ "name": "design_network_segment",
317
+ "title": "Design Isolated VLAN Segment",
318
+ "risk": "dangerous",
319
+ "annotations": {
320
+ "destructiveHint": true,
321
+ "openWorldHint": false
322
+ },
323
+ "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 — 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.",
324
+ "inputSchema": {
325
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
326
+ "type": "object",
327
+ "properties": {
328
+ "vlan_id": {
329
+ "type": "integer",
330
+ "minimum": 1,
331
+ "maximum": 4094
332
+ },
333
+ "name": {
334
+ "type": "string",
335
+ "description": "Name for the VLAN interface, e.g. 'guest'"
336
+ },
337
+ "subnet": {
338
+ "type": "string",
339
+ "description": "CIDR for the segment, e.g. '192.168.30.0/24'"
340
+ },
341
+ "gateway": {
342
+ "type": "string",
343
+ "description": "Router's address in the segment, e.g. '192.168.30.1'"
344
+ },
345
+ "bridge": {
346
+ "default": "bridge",
347
+ "description": "Bridge to put the VLAN on",
348
+ "type": "string"
349
+ },
350
+ "tagged_ports": {
351
+ "description": "Comma-separated trunk/tagged ports (+ the bridge)",
352
+ "type": "string"
353
+ },
354
+ "untagged_ports": {
355
+ "description": "Comma-separated access/untagged ports",
356
+ "type": "string"
357
+ },
358
+ "dhcp": {
359
+ "default": true,
360
+ "description": "Create a DHCP server + pool for the segment",
361
+ "type": "boolean"
362
+ },
363
+ "dhcp_range": {
364
+ "description": "Pool range; defaults to .10–.254 of the subnet",
365
+ "type": "string"
366
+ },
367
+ "internet": {
368
+ "default": true,
369
+ "description": "Allow internet access via srcnat masquerade",
370
+ "type": "boolean"
371
+ },
372
+ "isolate_from": {
373
+ "description": "Subnets this segment must NOT reach, e.g. ['192.168.1.0/24']",
374
+ "type": "array",
375
+ "items": {
376
+ "type": "string"
377
+ }
378
+ },
379
+ "apply": {
380
+ "default": false,
381
+ "description": "false = preview (default); true = build",
382
+ "type": "boolean"
383
+ }
384
+ },
385
+ "required": ["vlan_id", "name", "subnet", "gateway", "bridge", "dhcp", "internet", "apply"],
386
+ "additionalProperties": false
387
+ }
388
+ },
315
389
  {
316
390
  "name": "create_bridge",
317
391
  "title": "Create Bridge Interface",
@@ -1084,6 +1158,38 @@
1084
1158
  "additionalProperties": false
1085
1159
  }
1086
1160
  },
1161
+ {
1162
+ "name": "tune_wifi_channel",
1163
+ "title": "Survey & Tune Wi-Fi Channel",
1164
+ "risk": "write",
1165
+ "annotations": {
1166
+ "destructiveHint": false,
1167
+ "openWorldHint": false
1168
+ },
1169
+ "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`) — 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.",
1170
+ "inputSchema": {
1171
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1172
+ "type": "object",
1173
+ "properties": {
1174
+ "interface": {
1175
+ "type": "string",
1176
+ "description": "Wireless interface, e.g. 'wlan1'"
1177
+ },
1178
+ "duration": {
1179
+ "default": "5",
1180
+ "description": "Survey duration in seconds",
1181
+ "type": "string"
1182
+ },
1183
+ "apply": {
1184
+ "default": false,
1185
+ "description": "false = survey & recommend (default); true = set it",
1186
+ "type": "boolean"
1187
+ }
1188
+ },
1189
+ "required": ["interface", "duration", "apply"],
1190
+ "additionalProperties": false
1191
+ }
1192
+ },
1087
1193
  {
1088
1194
  "name": "get_poe_monitor",
1089
1195
  "title": "Read PoE Interface Live Metrics",
@@ -2538,6 +2644,92 @@
2538
2644
  "additionalProperties": false
2539
2645
  }
2540
2646
  },
2647
+ {
2648
+ "name": "setup_wan_failover",
2649
+ "title": "Set Up Multi-WAN Failover",
2650
+ "risk": "dangerous",
2651
+ "annotations": {
2652
+ "destructiveHint": true,
2653
+ "openWorldHint": false
2654
+ },
2655
+ "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 — 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 — 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.",
2656
+ "inputSchema": {
2657
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2658
+ "type": "object",
2659
+ "properties": {
2660
+ "primary_gateway": {
2661
+ "type": "string",
2662
+ "description": "Primary WAN gateway IP (or interface name)"
2663
+ },
2664
+ "backup_gateways": {
2665
+ "minItems": 1,
2666
+ "type": "array",
2667
+ "items": {
2668
+ "type": "string"
2669
+ },
2670
+ "description": "One or more backup WAN gateways, in failover order"
2671
+ },
2672
+ "check": {
2673
+ "default": "ping",
2674
+ "description": "Gateway health-check method",
2675
+ "type": "string",
2676
+ "enum": ["ping", "arp"]
2677
+ },
2678
+ "comment_prefix": {
2679
+ "default": "wan-failover",
2680
+ "type": "string"
2681
+ },
2682
+ "apply": {
2683
+ "default": false,
2684
+ "description": "false = preview only (default); true = execute",
2685
+ "type": "boolean"
2686
+ }
2687
+ },
2688
+ "required": ["primary_gateway", "backup_gateways", "check", "comment_prefix", "apply"],
2689
+ "additionalProperties": false
2690
+ }
2691
+ },
2692
+ {
2693
+ "name": "setup_wan_loadbalance",
2694
+ "title": "Set Up Multi-WAN Load Balancing (ECMP)",
2695
+ "risk": "dangerous",
2696
+ "annotations": {
2697
+ "destructiveHint": true,
2698
+ "openWorldHint": false
2699
+ },
2700
+ "description": "Builds ECMP (equal-cost multi-path) load balancing across multiple WANs by adding ONE default route whose `gateway` lists every WAN — 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 — 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.",
2701
+ "inputSchema": {
2702
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2703
+ "type": "object",
2704
+ "properties": {
2705
+ "gateways": {
2706
+ "minItems": 2,
2707
+ "type": "array",
2708
+ "items": {
2709
+ "type": "string"
2710
+ },
2711
+ "description": "Two or more WAN gateways to balance across"
2712
+ },
2713
+ "check": {
2714
+ "default": "ping",
2715
+ "description": "Gateway health-check method",
2716
+ "type": "string",
2717
+ "enum": ["ping", "arp"]
2718
+ },
2719
+ "comment": {
2720
+ "default": "wan-ecmp",
2721
+ "type": "string"
2722
+ },
2723
+ "apply": {
2724
+ "default": false,
2725
+ "description": "false = preview only (default); true = execute",
2726
+ "type": "boolean"
2727
+ }
2728
+ },
2729
+ "required": ["gateways", "check", "comment", "apply"],
2730
+ "additionalProperties": false
2731
+ }
2732
+ },
2541
2733
  {
2542
2734
  "name": "create_dhcp_server",
2543
2735
  "title": "Create DHCP Server Instance",
@@ -2751,168 +2943,436 @@
2751
2943
  }
2752
2944
  },
2753
2945
  {
2754
- "name": "set_dns_servers",
2755
- "title": "Configure DNS Resolver Settings",
2756
- "risk": "write",
2946
+ "name": "list_dhcp_clients",
2947
+ "title": "List DHCP Clients",
2948
+ "risk": "read",
2757
2949
  "annotations": {
2758
- "destructiveHint": false,
2950
+ "readOnlyHint": true,
2951
+ "idempotentHint": true,
2759
2952
  "openWorldHint": false
2760
2953
  },
2761
- "description": "Configures the DNS resolver (`/ip dns set`) — sets upstream server IPs, enables or disables forwarding client queries to this router (allow_remote_requests), tunes cache limits (cache_size in KiB, cache_max_ttl, max_udp_packet_size, max_concurrent_queries), and optionally switches to DNS-over-HTTPS (use_doh=true + doh_server URL + verify_doh_cert). Use this when you need to CHANGE which servers the router queries or how its resolver behaves. To read current settings without changing them use get_dns_settings. Returns the updated `/ip dns print` output after applying changes.",
2954
+ "description": "Lists IPv4 DHCP clients (`/ip dhcp-client print`) — each interface configured to obtain its address/gateway/DNS from an upstream DHCP server (typically a WAN port). Shows the bound address, status, and whether a default route / peer DNS is used. Optional `interface_filter`. For one client's detail use get_dhcp_client; to add one use add_dhcp_client.",
2762
2955
  "inputSchema": {
2763
2956
  "$schema": "https://json-schema.org/draft/2020-12/schema",
2764
2957
  "type": "object",
2765
2958
  "properties": {
2766
- "servers": {
2767
- "type": "array",
2768
- "items": {
2769
- "type": "string"
2770
- },
2771
- "description": "DNS server IP addresses"
2772
- },
2773
- "allow_remote_requests": {
2774
- "default": false,
2775
- "type": "boolean"
2776
- },
2777
- "max_udp_packet_size": {
2778
- "type": "integer",
2779
- "minimum": -9007199254740991,
2780
- "maximum": 9007199254740991
2781
- },
2782
- "max_concurrent_queries": {
2783
- "type": "integer",
2784
- "minimum": -9007199254740991,
2785
- "maximum": 9007199254740991
2786
- },
2787
- "cache_size": {
2788
- "type": "integer",
2789
- "minimum": -9007199254740991,
2790
- "maximum": 9007199254740991
2791
- },
2792
- "cache_max_ttl": {
2793
- "type": "string"
2794
- },
2795
- "use_doh": {
2796
- "default": false,
2797
- "type": "boolean"
2798
- },
2799
- "doh_server": {
2959
+ "interface_filter": {
2960
+ "description": "Substring match on interface",
2800
2961
  "type": "string"
2801
- },
2802
- "verify_doh_cert": {
2803
- "default": true,
2804
- "type": "boolean"
2805
2962
  }
2806
2963
  },
2807
- "required": ["servers", "allow_remote_requests", "use_doh", "verify_doh_cert"],
2808
2964
  "additionalProperties": false
2809
2965
  }
2810
2966
  },
2811
2967
  {
2812
- "name": "get_dns_settings",
2813
- "title": "Get DNS Resolver Settings",
2968
+ "name": "get_dhcp_client",
2969
+ "title": "Get DHCP Client Detail",
2814
2970
  "risk": "read",
2815
2971
  "annotations": {
2816
2972
  "readOnlyHint": true,
2817
2973
  "idempotentHint": true,
2818
2974
  "openWorldHint": false
2819
2975
  },
2820
- "description": "Reads the current DNS resolver configuration (`/ip dns print`) upstream server IPs, allow-remote-requests flag, cache-size, cache-max-ttl, max-udp-packet-size, max-concurrent-queries, and DNS-over-HTTPS settings. To change these settings use set_dns_servers. For cache entry counts and utilization metrics use get_dns_cache_statistics. Returns the full `/ip dns print` settings block.",
2976
+ "description": "Full detail for the DHCP client on an interface (`/ip dhcp-client print detail where interface=...`) bound address, gateway, lease time, status, and options. Use list_dhcp_clients to find interfaces. Returns the detail block or a not-found message.",
2821
2977
  "inputSchema": {
2822
2978
  "$schema": "https://json-schema.org/draft/2020-12/schema",
2823
2979
  "type": "object",
2824
- "properties": {},
2980
+ "properties": {
2981
+ "interface": {
2982
+ "type": "string",
2983
+ "description": "Interface name, e.g. 'ether1'"
2984
+ }
2985
+ },
2986
+ "required": ["interface"],
2825
2987
  "additionalProperties": false
2826
2988
  }
2827
2989
  },
2828
2990
  {
2829
- "name": "add_dns_static",
2830
- "title": "Add DNS Static Record",
2991
+ "name": "add_dhcp_client",
2992
+ "title": "Add DHCP Client",
2831
2993
  "risk": "write",
2832
2994
  "annotations": {
2833
2995
  "destructiveHint": false,
2834
2996
  "openWorldHint": false
2835
2997
  },
2836
- "description": "Creates a static DNS record in `/ip dns static` the local override table that answers DNS queries before upstream resolvers are consulted. Supports A (address), CNAME (cname), MX (mx_preference + mx_exchange), TXT (text), and SRV (srv_priority + srv_weight + srv_port + srv_target) record types; provide exactly one record type's fields per call. A regexp pattern may also be set for wildcard hostname matching — for a simpler regexp-only interface use add_dns_regexp instead. To inspect existing records use list_dns_static. Returns the created entry's full detail including its `.id`.",
2998
+ "description": "Adds an IPv4 DHCP client on an interface (`/ip dhcp-client add`) so it obtains an address from an upstream DHCP server the usual way to configure a WAN port. `add_default_route` installs the received gateway; `use_peer_dns`/`use_peer_ntp` adopt the server's DNS/NTP. Returns the created client's detail.",
2837
2999
  "inputSchema": {
2838
3000
  "$schema": "https://json-schema.org/draft/2020-12/schema",
2839
3001
  "type": "object",
2840
3002
  "properties": {
2841
- "name": {
2842
- "type": "string"
2843
- },
2844
- "address": {
2845
- "type": "string"
2846
- },
2847
- "cname": {
2848
- "type": "string"
2849
- },
2850
- "mx_preference": {
2851
- "type": "integer",
2852
- "minimum": -9007199254740991,
2853
- "maximum": 9007199254740991
2854
- },
2855
- "mx_exchange": {
2856
- "type": "string"
2857
- },
2858
- "text": {
2859
- "type": "string"
2860
- },
2861
- "srv_priority": {
2862
- "type": "integer",
2863
- "minimum": -9007199254740991,
2864
- "maximum": 9007199254740991
2865
- },
2866
- "srv_weight": {
2867
- "type": "integer",
2868
- "minimum": -9007199254740991,
2869
- "maximum": 9007199254740991
2870
- },
2871
- "srv_port": {
2872
- "type": "integer",
2873
- "minimum": -9007199254740991,
2874
- "maximum": 9007199254740991
3003
+ "interface": {
3004
+ "type": "string",
3005
+ "description": "Interface to run the DHCP client on, e.g. 'ether1'"
2875
3006
  },
2876
- "srv_target": {
2877
- "type": "string"
3007
+ "add_default_route": {
3008
+ "default": true,
3009
+ "description": "Install the received default gateway",
3010
+ "type": "boolean"
2878
3011
  },
2879
- "ttl": {
2880
- "type": "string"
3012
+ "use_peer_dns": {
3013
+ "default": true,
3014
+ "type": "boolean"
2881
3015
  },
2882
- "comment": {
2883
- "type": "string"
3016
+ "use_peer_ntp": {
3017
+ "default": true,
3018
+ "type": "boolean"
2884
3019
  },
2885
3020
  "disabled": {
2886
3021
  "default": false,
2887
3022
  "type": "boolean"
2888
3023
  },
2889
- "regexp": {
3024
+ "comment": {
2890
3025
  "type": "string"
2891
3026
  }
2892
3027
  },
2893
- "required": ["name", "disabled"],
3028
+ "required": ["interface", "add_default_route", "use_peer_dns", "use_peer_ntp", "disabled"],
2894
3029
  "additionalProperties": false
2895
3030
  }
2896
3031
  },
2897
3032
  {
2898
- "name": "list_dns_static",
2899
- "title": "List DNS Static Records",
2900
- "risk": "read",
3033
+ "name": "set_dhcp_client",
3034
+ "title": "Update DHCP Client",
3035
+ "risk": "write-idempotent",
2901
3036
  "annotations": {
2902
- "readOnlyHint": true,
3037
+ "destructiveHint": false,
2903
3038
  "idempotentHint": true,
2904
3039
  "openWorldHint": false
2905
3040
  },
2906
- "description": "Lists static DNS records from `/ip dns static print` — the local resolver override table. Supports optional filters: name_filter (substring match on hostname), address_filter (substring match on IP), type_filter (exact record type, e.g. \"A\", \"CNAME\", \"MX\"), disabled_only (only disabled entries), regexp_only (only regexp-pattern entries). To retrieve full field detail for one specific entry by `.id` use get_dns_static. Returns a table of all matching static DNS records with their `.id` values.",
3041
+ "description": "Updates the DHCP client on an interface (`/ip dhcp-client set [find interface=...]`) toggle `add_default_route`, `use_peer_dns`, `use_peer_ntp`, or `disabled`. No-ops if nothing is supplied. Returns the updated detail.",
2907
3042
  "inputSchema": {
2908
3043
  "$schema": "https://json-schema.org/draft/2020-12/schema",
2909
3044
  "type": "object",
2910
3045
  "properties": {
2911
- "name_filter": {
3046
+ "interface": {
2912
3047
  "type": "string"
2913
3048
  },
2914
- "address_filter": {
2915
- "type": "string"
3049
+ "add_default_route": {
3050
+ "type": "boolean"
3051
+ },
3052
+ "use_peer_dns": {
3053
+ "type": "boolean"
3054
+ },
3055
+ "use_peer_ntp": {
3056
+ "type": "boolean"
3057
+ },
3058
+ "disabled": {
3059
+ "type": "boolean"
3060
+ }
3061
+ },
3062
+ "required": ["interface"],
3063
+ "additionalProperties": false
3064
+ }
3065
+ },
3066
+ {
3067
+ "name": "renew_dhcp_client",
3068
+ "title": "Renew / Release DHCP Lease",
3069
+ "risk": "write",
3070
+ "annotations": {
3071
+ "destructiveHint": false,
3072
+ "openWorldHint": false
3073
+ },
3074
+ "description": "Renews or releases the DHCP lease on an interface (`/ip dhcp-client renew|release`). `release=true` drops the current lease; otherwise it renews — use to re-pull WAN settings after an upstream change. Returns the client status afterward.",
3075
+ "inputSchema": {
3076
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3077
+ "type": "object",
3078
+ "properties": {
3079
+ "interface": {
3080
+ "type": "string"
3081
+ },
3082
+ "release": {
3083
+ "default": false,
3084
+ "description": "true = release the lease; false = renew",
3085
+ "type": "boolean"
3086
+ }
3087
+ },
3088
+ "required": ["interface", "release"],
3089
+ "additionalProperties": false
3090
+ }
3091
+ },
3092
+ {
3093
+ "name": "remove_dhcp_client",
3094
+ "title": "Remove DHCP Client",
3095
+ "risk": "destructive",
3096
+ "annotations": {
3097
+ "destructiveHint": true,
3098
+ "idempotentHint": true,
3099
+ "openWorldHint": false
3100
+ },
3101
+ "description": "Removes the DHCP client from an interface (`/ip dhcp-client remove [find interface=...]`) — the interface stops obtaining an address from DHCP (set a static address separately if needed). Confirms removal or reports not-found.",
3102
+ "inputSchema": {
3103
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3104
+ "type": "object",
3105
+ "properties": {
3106
+ "interface": {
3107
+ "type": "string"
3108
+ }
3109
+ },
3110
+ "required": ["interface"],
3111
+ "additionalProperties": false
3112
+ }
3113
+ },
3114
+ {
3115
+ "name": "build_guest_hotspot",
3116
+ "title": "Build Guest Hotspot (Captive Portal)",
3117
+ "risk": "dangerous",
3118
+ "annotations": {
3119
+ "destructiveHint": true,
3120
+ "openWorldHint": false
3121
+ },
3122
+ "description": "Stands up a guest hotspot with a captive sign-in portal in one call: the gateway IP, an address pool, a DHCP server, the `/ip hotspot` server + profile (with a per-guest `rate_limit` bandwidth cap and optional `dns_name`), and a walled garden (the `walled_garden` domains reachable BEFORE sign-in, e.g. your payment/login pages). DEFAULTS TO A DRY RUN (`apply=false`) showing every command; set `apply=true` to build it. Generate printable guest codes with generate_hotspot_vouchers. Returns the plan or a build report.",
3123
+ "inputSchema": {
3124
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3125
+ "type": "object",
3126
+ "properties": {
3127
+ "interface": {
3128
+ "type": "string",
3129
+ "description": "Interface/bridge the hotspot runs on, e.g. 'guest-bridge'"
3130
+ },
3131
+ "subnet": {
3132
+ "default": "10.5.50.0/24",
3133
+ "description": "Hotspot subnet",
3134
+ "type": "string"
3135
+ },
3136
+ "gateway": {
3137
+ "default": "10.5.50.1",
3138
+ "description": "Router address in the hotspot subnet",
3139
+ "type": "string"
3140
+ },
3141
+ "pool_range": {
3142
+ "description": "DHCP/hotspot pool; defaults to .10–.254",
3143
+ "type": "string"
3144
+ },
3145
+ "dns_name": {
3146
+ "description": "Captive-portal hostname, e.g. 'guest.wifi'",
3147
+ "type": "string"
3148
+ },
3149
+ "rate_limit": {
3150
+ "default": "5M/5M",
3151
+ "description": "Per-guest max-limit (rx/tx)",
3152
+ "type": "string"
3153
+ },
3154
+ "walled_garden": {
3155
+ "description": "Domains reachable before sign-in, e.g. ['*.stripe.com']",
3156
+ "type": "array",
3157
+ "items": {
3158
+ "type": "string"
3159
+ }
3160
+ },
3161
+ "apply": {
3162
+ "default": false,
3163
+ "description": "false = preview (default); true = build",
3164
+ "type": "boolean"
3165
+ }
3166
+ },
3167
+ "required": ["interface", "subnet", "gateway", "rate_limit", "apply"],
3168
+ "additionalProperties": false
3169
+ }
3170
+ },
3171
+ {
3172
+ "name": "generate_hotspot_vouchers",
3173
+ "title": "Generate Hotspot Vouchers",
3174
+ "risk": "write",
3175
+ "annotations": {
3176
+ "destructiveHint": false,
3177
+ "openWorldHint": false
3178
+ },
3179
+ "description": "Creates `count` random guest login codes as `/ip hotspot user` entries (username = password = code), each with an optional uptime limit and hotspot user profile — the printable vouchers you hand to guests. DEFAULTS TO A DRY RUN (`apply=false`) that just shows the codes it would create; set `apply=true` to add them. Returns the generated codes.",
3180
+ "inputSchema": {
3181
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3182
+ "type": "object",
3183
+ "properties": {
3184
+ "count": {
3185
+ "default": 10,
3186
+ "type": "integer",
3187
+ "minimum": 1,
3188
+ "maximum": 200
3189
+ },
3190
+ "uptime_limit": {
3191
+ "description": "Per-voucher uptime, e.g. '1h', '1d'",
3192
+ "type": "string"
3193
+ },
3194
+ "profile": {
3195
+ "description": "Hotspot user profile to assign",
3196
+ "type": "string"
3197
+ },
3198
+ "code_prefix": {
3199
+ "default": "g",
3200
+ "description": "Prefix for the generated codes",
3201
+ "type": "string"
3202
+ },
3203
+ "apply": {
3204
+ "default": false,
3205
+ "description": "false = preview codes (default); true = create",
3206
+ "type": "boolean"
3207
+ }
3208
+ },
3209
+ "required": ["count", "code_prefix", "apply"],
3210
+ "additionalProperties": false
3211
+ }
3212
+ },
3213
+ {
3214
+ "name": "set_dns_servers",
3215
+ "title": "Configure DNS Resolver Settings",
3216
+ "risk": "write",
3217
+ "annotations": {
3218
+ "destructiveHint": false,
3219
+ "openWorldHint": false
3220
+ },
3221
+ "description": "Configures the DNS resolver (`/ip dns set`) — sets upstream server IPs, enables or disables forwarding client queries to this router (allow_remote_requests), tunes cache limits (cache_size in KiB, cache_max_ttl, max_udp_packet_size, max_concurrent_queries), and optionally switches to DNS-over-HTTPS (use_doh=true + doh_server URL + verify_doh_cert). Use this when you need to CHANGE which servers the router queries or how its resolver behaves. To read current settings without changing them use get_dns_settings. Returns the updated `/ip dns print` output after applying changes.",
3222
+ "inputSchema": {
3223
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3224
+ "type": "object",
3225
+ "properties": {
3226
+ "servers": {
3227
+ "type": "array",
3228
+ "items": {
3229
+ "type": "string"
3230
+ },
3231
+ "description": "DNS server IP addresses"
3232
+ },
3233
+ "allow_remote_requests": {
3234
+ "default": false,
3235
+ "type": "boolean"
3236
+ },
3237
+ "max_udp_packet_size": {
3238
+ "type": "integer",
3239
+ "minimum": -9007199254740991,
3240
+ "maximum": 9007199254740991
3241
+ },
3242
+ "max_concurrent_queries": {
3243
+ "type": "integer",
3244
+ "minimum": -9007199254740991,
3245
+ "maximum": 9007199254740991
3246
+ },
3247
+ "cache_size": {
3248
+ "type": "integer",
3249
+ "minimum": -9007199254740991,
3250
+ "maximum": 9007199254740991
3251
+ },
3252
+ "cache_max_ttl": {
3253
+ "type": "string"
3254
+ },
3255
+ "use_doh": {
3256
+ "default": false,
3257
+ "type": "boolean"
3258
+ },
3259
+ "doh_server": {
3260
+ "type": "string"
3261
+ },
3262
+ "verify_doh_cert": {
3263
+ "default": true,
3264
+ "type": "boolean"
3265
+ }
3266
+ },
3267
+ "required": ["servers", "allow_remote_requests", "use_doh", "verify_doh_cert"],
3268
+ "additionalProperties": false
3269
+ }
3270
+ },
3271
+ {
3272
+ "name": "get_dns_settings",
3273
+ "title": "Get DNS Resolver Settings",
3274
+ "risk": "read",
3275
+ "annotations": {
3276
+ "readOnlyHint": true,
3277
+ "idempotentHint": true,
3278
+ "openWorldHint": false
3279
+ },
3280
+ "description": "Reads the current DNS resolver configuration (`/ip dns print`) — upstream server IPs, allow-remote-requests flag, cache-size, cache-max-ttl, max-udp-packet-size, max-concurrent-queries, and DNS-over-HTTPS settings. To change these settings use set_dns_servers. For cache entry counts and utilization metrics use get_dns_cache_statistics. Returns the full `/ip dns print` settings block.",
3281
+ "inputSchema": {
3282
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3283
+ "type": "object",
3284
+ "properties": {},
3285
+ "additionalProperties": false
3286
+ }
3287
+ },
3288
+ {
3289
+ "name": "add_dns_static",
3290
+ "title": "Add DNS Static Record",
3291
+ "risk": "write",
3292
+ "annotations": {
3293
+ "destructiveHint": false,
3294
+ "openWorldHint": false
3295
+ },
3296
+ "description": "Creates a static DNS record in `/ip dns static` — the local override table that answers DNS queries before upstream resolvers are consulted. Supports A (address), CNAME (cname), MX (mx_preference + mx_exchange), TXT (text), and SRV (srv_priority + srv_weight + srv_port + srv_target) record types; provide exactly one record type's fields per call. A regexp pattern may also be set for wildcard hostname matching — for a simpler regexp-only interface use add_dns_regexp instead. To inspect existing records use list_dns_static. Returns the created entry's full detail including its `.id`.",
3297
+ "inputSchema": {
3298
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3299
+ "type": "object",
3300
+ "properties": {
3301
+ "name": {
3302
+ "type": "string"
3303
+ },
3304
+ "address": {
3305
+ "type": "string"
3306
+ },
3307
+ "cname": {
3308
+ "type": "string"
3309
+ },
3310
+ "mx_preference": {
3311
+ "type": "integer",
3312
+ "minimum": -9007199254740991,
3313
+ "maximum": 9007199254740991
3314
+ },
3315
+ "mx_exchange": {
3316
+ "type": "string"
3317
+ },
3318
+ "text": {
3319
+ "type": "string"
3320
+ },
3321
+ "srv_priority": {
3322
+ "type": "integer",
3323
+ "minimum": -9007199254740991,
3324
+ "maximum": 9007199254740991
3325
+ },
3326
+ "srv_weight": {
3327
+ "type": "integer",
3328
+ "minimum": -9007199254740991,
3329
+ "maximum": 9007199254740991
3330
+ },
3331
+ "srv_port": {
3332
+ "type": "integer",
3333
+ "minimum": -9007199254740991,
3334
+ "maximum": 9007199254740991
3335
+ },
3336
+ "srv_target": {
3337
+ "type": "string"
3338
+ },
3339
+ "ttl": {
3340
+ "type": "string"
3341
+ },
3342
+ "comment": {
3343
+ "type": "string"
3344
+ },
3345
+ "disabled": {
3346
+ "default": false,
3347
+ "type": "boolean"
3348
+ },
3349
+ "regexp": {
3350
+ "type": "string"
3351
+ }
3352
+ },
3353
+ "required": ["name", "disabled"],
3354
+ "additionalProperties": false
3355
+ }
3356
+ },
3357
+ {
3358
+ "name": "list_dns_static",
3359
+ "title": "List DNS Static Records",
3360
+ "risk": "read",
3361
+ "annotations": {
3362
+ "readOnlyHint": true,
3363
+ "idempotentHint": true,
3364
+ "openWorldHint": false
3365
+ },
3366
+ "description": "Lists static DNS records from `/ip dns static print` — the local resolver override table. Supports optional filters: name_filter (substring match on hostname), address_filter (substring match on IP), type_filter (exact record type, e.g. \"A\", \"CNAME\", \"MX\"), disabled_only (only disabled entries), regexp_only (only regexp-pattern entries). To retrieve full field detail for one specific entry by `.id` use get_dns_static. Returns a table of all matching static DNS records with their `.id` values.",
3367
+ "inputSchema": {
3368
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3369
+ "type": "object",
3370
+ "properties": {
3371
+ "name_filter": {
3372
+ "type": "string"
3373
+ },
3374
+ "address_filter": {
3375
+ "type": "string"
2916
3376
  },
2917
3377
  "type_filter": {
2918
3378
  "type": "string"
@@ -3226,6 +3686,85 @@
3226
3686
  "additionalProperties": false
3227
3687
  }
3228
3688
  },
3689
+ {
3690
+ "name": "set_time_policy",
3691
+ "title": "Set Time-of-Day / Parental Policy",
3692
+ "risk": "write",
3693
+ "annotations": {
3694
+ "destructiveHint": false,
3695
+ "openWorldHint": false
3696
+ },
3697
+ "description": "Schedules per-device internet access and content blocking. Cuts internet for devices in the `list` address-list between `block_start` and `block_end` daily (a forward-drop rule toggled by two schedulers), optionally adds `addresses` to that list, and DNS-sinkholes any `block_domains` (always blocked). DEFAULTS TO A DRY RUN (`apply=false`) showing every command; set `apply=true` to install. Everything is tagged 'parental-<name>' so remove_time_policy can undo it. Returns the plan or a build report.",
3698
+ "inputSchema": {
3699
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3700
+ "type": "object",
3701
+ "properties": {
3702
+ "name": {
3703
+ "type": "string",
3704
+ "description": "Policy id, e.g. 'kids-bedtime'"
3705
+ },
3706
+ "list": {
3707
+ "default": "parental-devices",
3708
+ "description": "Address-list of the affected devices",
3709
+ "type": "string"
3710
+ },
3711
+ "addresses": {
3712
+ "description": "Device IPs to add to the list",
3713
+ "type": "array",
3714
+ "items": {
3715
+ "type": "string"
3716
+ }
3717
+ },
3718
+ "block_start": {
3719
+ "default": "22:00",
3720
+ "description": "Daily cut-off time (HH:MM)",
3721
+ "type": "string"
3722
+ },
3723
+ "block_end": {
3724
+ "default": "07:00",
3725
+ "description": "Daily restore time (HH:MM)",
3726
+ "type": "string"
3727
+ },
3728
+ "block_domains": {
3729
+ "description": "Domains to always block via DNS",
3730
+ "type": "array",
3731
+ "items": {
3732
+ "type": "string"
3733
+ }
3734
+ },
3735
+ "apply": {
3736
+ "default": false,
3737
+ "description": "false = preview (default); true = install",
3738
+ "type": "boolean"
3739
+ }
3740
+ },
3741
+ "required": ["name", "list", "block_start", "block_end", "apply"],
3742
+ "additionalProperties": false
3743
+ }
3744
+ },
3745
+ {
3746
+ "name": "remove_time_policy",
3747
+ "title": "Remove Time-of-Day / Parental Policy",
3748
+ "risk": "destructive",
3749
+ "annotations": {
3750
+ "destructiveHint": true,
3751
+ "idempotentHint": true,
3752
+ "openWorldHint": false
3753
+ },
3754
+ "description": "Removes a policy installed by set_time_policy: the schedulers, the forward-drop rule, and the DNS sinkhole entries tagged 'parental-<name>'. Address-list members added for it are also removed. Returns what was cleared.",
3755
+ "inputSchema": {
3756
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3757
+ "type": "object",
3758
+ "properties": {
3759
+ "name": {
3760
+ "type": "string",
3761
+ "description": "The policy id used when installing"
3762
+ }
3763
+ },
3764
+ "required": ["name"],
3765
+ "additionalProperties": false
3766
+ }
3767
+ },
3229
3768
  {
3230
3769
  "name": "add_ipv6_address",
3231
3770
  "title": "Add IPv6 Address to Interface",
@@ -9905,6 +10444,60 @@
9905
10444
  "additionalProperties": false
9906
10445
  }
9907
10446
  },
10447
+ {
10448
+ "name": "forward_port",
10449
+ "title": "Forward a Port (Smart Wizard)",
10450
+ "risk": "dangerous",
10451
+ "annotations": {
10452
+ "destructiveHint": true,
10453
+ "openWorldHint": false
10454
+ },
10455
+ "description": "Exposes an internal service correctly and completely in one call: the DST-NAT rule that forwards `external_port` to `internal_host:internal_port`, the matching forward-chain accept (so it passes a default-drop firewall), and — when `lan_subnet` is given — the hairpin/loopback srcnat so the service also works when reached from inside the LAN via the public IP. DEFAULTS TO A DRY RUN (`apply=false`) showing every rule; set `apply=true` to create them. All rules are tagged 'port-forward:'. Returns the plan or a build report.",
10456
+ "inputSchema": {
10457
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
10458
+ "type": "object",
10459
+ "properties": {
10460
+ "protocol": {
10461
+ "default": "tcp",
10462
+ "type": "string",
10463
+ "enum": ["tcp", "udp"]
10464
+ },
10465
+ "external_port": {
10466
+ "type": "integer",
10467
+ "minimum": 1,
10468
+ "maximum": 65535
10469
+ },
10470
+ "internal_host": {
10471
+ "type": "string",
10472
+ "description": "LAN IP of the service, e.g. '192.168.1.10'"
10473
+ },
10474
+ "internal_port": {
10475
+ "description": "Defaults to external_port",
10476
+ "type": "integer",
10477
+ "minimum": 1,
10478
+ "maximum": 65535
10479
+ },
10480
+ "wan_interface": {
10481
+ "description": "Restrict the forward to this WAN interface",
10482
+ "type": "string"
10483
+ },
10484
+ "lan_subnet": {
10485
+ "description": "LAN subnet for hairpin NAT, e.g. '192.168.1.0/24'",
10486
+ "type": "string"
10487
+ },
10488
+ "comment": {
10489
+ "type": "string"
10490
+ },
10491
+ "apply": {
10492
+ "default": false,
10493
+ "description": "false = preview (default); true = create",
10494
+ "type": "boolean"
10495
+ }
10496
+ },
10497
+ "required": ["protocol", "external_port", "internal_host", "apply"],
10498
+ "additionalProperties": false
10499
+ }
10500
+ },
9908
10501
  {
9909
10502
  "name": "add_address_list_entry",
9910
10503
  "title": "Add Firewall Address-List Entry",
@@ -10071,27 +10664,222 @@
10071
10664
  "title": "Audit IPv4 Firewall Rulesets",
10072
10665
  "risk": "read",
10073
10666
  "annotations": {
10074
- "readOnlyHint": true,
10075
- "idempotentHint": true,
10667
+ "readOnlyHint": true,
10668
+ "idempotentHint": true,
10669
+ "openWorldHint": false
10670
+ },
10671
+ "description": "Audits IPv4 firewall rulesets (`/ip firewall filter print detail`; optionally `/ip firewall nat print detail` when include_nat=true (default on); `/ip firewall mangle print detail` when include_mangle=true (default off)) — use to review, harden, or understand a router's full firewall posture in one pass. Detects unreachable/shadowed rules, overly-broad accepts, a missing default-drop, duplicate rules, and dead rules with zero packet hits. For browsing individual rules without analysis use list_filter_rules, list_nat_rules; for IPv6 filter rules use list_ipv6_filter_rules; for IPv6 NAT use list_ipv6_nat_rules; for IPv6 mangle use list_ipv6_mangle_rules. Returns a risk score and a prioritised list of plain-language findings with suggested per-rule fixes; in MCP App hosts also renders an interactive findings table with one-click disable. Scoped to IPv4 paths only — no IPv6 audit twin exists.",
10672
+ "inputSchema": {
10673
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
10674
+ "type": "object",
10675
+ "properties": {
10676
+ "include_nat": {
10677
+ "default": true,
10678
+ "description": "Also audit `/ip firewall nat`.",
10679
+ "type": "boolean"
10680
+ },
10681
+ "include_mangle": {
10682
+ "default": false,
10683
+ "description": "Also audit `/ip firewall mangle`.",
10684
+ "type": "boolean"
10685
+ }
10686
+ },
10687
+ "required": ["include_nat", "include_mangle"],
10688
+ "additionalProperties": false
10689
+ }
10690
+ },
10691
+ {
10692
+ "name": "harden_firewall",
10693
+ "title": "Harden Firewall (Security Shield)",
10694
+ "risk": "dangerous",
10695
+ "annotations": {
10696
+ "destructiveHint": true,
10697
+ "openWorldHint": false
10698
+ },
10699
+ "description": "Hardens the IPv4 firewall against common attacks by applying a chosen set of best-practice rules (DDoS/connection-rate, SSH/Winbox brute-force ladder, SYN-flood and ICMP rate limits, port-scan detection, anti-spoof/bogon, raw pre-conntrack drops, management lock-down). Pick a `preset` (basic · standard · paranoid) and/or toggle individual `protections`. DEFAULTS TO A DRY RUN (`apply=false`) that returns the exact commands grouped by protection. Applying defaults to SAFE MODE (`safe_mode=true`) so a rule that cuts your own access auto-reverts. Anti-lockout: set `trusted_sources` (an address or address-list always accepted first); input-drop protections are refused without it unless `i_accept_lockout_risk=true`. Every rule is tagged with a 'security-shield:' comment — audit with audit_firewall_hardening, undo with remove_firewall_hardening. Rules are appended; review order for an existing ruleset.",
10700
+ "inputSchema": {
10701
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
10702
+ "type": "object",
10703
+ "properties": {
10704
+ "preset": {
10705
+ "default": "standard",
10706
+ "description": "Bundle to apply; 'custom' uses only the `protections` toggles",
10707
+ "type": "string",
10708
+ "enum": ["basic", "standard", "paranoid", "custom"]
10709
+ },
10710
+ "protections": {
10711
+ "description": "Per-protection on/off, overriding/extending the preset",
10712
+ "type": "object",
10713
+ "propertyNames": {
10714
+ "type": "string",
10715
+ "enum": [
10716
+ "state_hygiene",
10717
+ "ssh_bruteforce",
10718
+ "ddos_conn_limit",
10719
+ "syn_flood",
10720
+ "icmp_limit",
10721
+ "port_scan",
10722
+ "anti_spoof",
10723
+ "raw_drops",
10724
+ "mgmt_lockdown"
10725
+ ]
10726
+ },
10727
+ "additionalProperties": {
10728
+ "type": "boolean"
10729
+ },
10730
+ "required": [
10731
+ "state_hygiene",
10732
+ "ssh_bruteforce",
10733
+ "ddos_conn_limit",
10734
+ "syn_flood",
10735
+ "icmp_limit",
10736
+ "port_scan",
10737
+ "anti_spoof",
10738
+ "raw_drops",
10739
+ "mgmt_lockdown"
10740
+ ]
10741
+ },
10742
+ "trusted_sources": {
10743
+ "description": "Address or address-list name always accepted first (anti-lockout)",
10744
+ "type": "string"
10745
+ },
10746
+ "wan_interface": {
10747
+ "description": "WAN interface (required for anti_spoof)",
10748
+ "type": "string"
10749
+ },
10750
+ "mgmt_ports": {
10751
+ "default": "22,8291,8728",
10752
+ "description": "Management TCP ports",
10753
+ "type": "string"
10754
+ },
10755
+ "ssh_ports": {
10756
+ "default": "22",
10757
+ "description": "Ports watched by the brute-force ladder",
10758
+ "type": "string"
10759
+ },
10760
+ "conn_limit": {
10761
+ "default": 100,
10762
+ "description": "Per-source TCP connection cap",
10763
+ "type": "integer",
10764
+ "minimum": -9007199254740991,
10765
+ "maximum": 9007199254740991
10766
+ },
10767
+ "syn_rate": {
10768
+ "default": "200,50:packet",
10769
+ "type": "string"
10770
+ },
10771
+ "icmp_rate": {
10772
+ "default": "50,10:packet",
10773
+ "type": "string"
10774
+ },
10775
+ "apply": {
10776
+ "default": false,
10777
+ "description": "false = preview only (default); true = apply",
10778
+ "type": "boolean"
10779
+ },
10780
+ "safe_mode": {
10781
+ "default": true,
10782
+ "description": "Apply through Safe Mode (auto-revert on lockout)",
10783
+ "type": "boolean"
10784
+ },
10785
+ "i_accept_lockout_risk": {
10786
+ "default": false,
10787
+ "type": "boolean"
10788
+ }
10789
+ },
10790
+ "required": [
10791
+ "preset",
10792
+ "mgmt_ports",
10793
+ "ssh_ports",
10794
+ "conn_limit",
10795
+ "syn_rate",
10796
+ "icmp_rate",
10797
+ "apply",
10798
+ "safe_mode",
10799
+ "i_accept_lockout_risk"
10800
+ ],
10801
+ "additionalProperties": false
10802
+ }
10803
+ },
10804
+ {
10805
+ "name": "audit_firewall_hardening",
10806
+ "title": "Audit Firewall Hardening",
10807
+ "risk": "read",
10808
+ "annotations": {
10809
+ "readOnlyHint": true,
10810
+ "idempotentHint": true,
10811
+ "openWorldHint": false
10812
+ },
10813
+ "description": "Reports which Security Shield protections are present on the firewall by scanning for the 'security-shield:' tagged rules (`/ip firewall filter` + `/ip firewall raw`), and notes the foundational checks (accept established/related, drop invalid) regardless of tag. Use to see coverage and gaps before/after harden_firewall. Complements the broader firewall-audit tools. Returns a per-protection present/absent summary.",
10814
+ "inputSchema": {
10815
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
10816
+ "type": "object",
10817
+ "properties": {},
10818
+ "additionalProperties": false
10819
+ }
10820
+ },
10821
+ {
10822
+ "name": "remove_firewall_hardening",
10823
+ "title": "Remove Firewall Hardening",
10824
+ "risk": "destructive",
10825
+ "annotations": {
10826
+ "destructiveHint": true,
10827
+ "idempotentHint": true,
10828
+ "openWorldHint": false
10829
+ },
10830
+ "description": "Removes every firewall rule that Security Shield installed — all `/ip firewall filter` and `/ip firewall raw` entries tagged with the 'security-shield:' comment (the shield's own address-lists are left, since they may be referenced elsewhere). Use to roll back harden_firewall. Does NOT touch rules you created by hand. Returns how many rules were removed.",
10831
+ "inputSchema": {
10832
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
10833
+ "type": "object",
10834
+ "properties": {},
10835
+ "additionalProperties": false
10836
+ }
10837
+ },
10838
+ {
10839
+ "name": "setup_port_knock",
10840
+ "title": "Set Up Port-Knock Guardian",
10841
+ "risk": "dangerous",
10842
+ "annotations": {
10843
+ "destructiveHint": true,
10076
10844
  "openWorldHint": false
10077
10845
  },
10078
- "description": "Audits IPv4 firewall rulesets (`/ip firewall filter print detail`; optionally `/ip firewall nat print detail` when include_nat=true (default on); `/ip firewall mangle print detail` when include_mangle=true (default off)) — use to review, harden, or understand a router's full firewall posture in one pass. Detects unreachable/shadowed rules, overly-broad accepts, a missing default-drop, duplicate rules, and dead rules with zero packet hits. For browsing individual rules without analysis use list_filter_rules, list_nat_rules; for IPv6 filter rules use list_ipv6_filter_rules; for IPv6 NAT use list_ipv6_nat_rules; for IPv6 mangle use list_ipv6_mangle_rules. Returns a risk score and a prioritised list of plain-language findings with suggested per-rule fixes; in MCP App hosts also renders an interactive findings table with one-click disable. Scoped to IPv4 paths only no IPv6 audit twin exists.",
10846
+ "description": "Hides management services behind a secret port-knock `sequence`: a source must connect to the knock ports in order to be added to an allow-list, after which the `protect_ports` (SSH/Winbox/API, default 22,8291,8728) accept it everyone else is dropped, so the ports are invisible to scanners. Generates the staged address-list ladder + accept/drop rules. DEFAULTS TO A DRY RUN (`apply=false`); set `apply=true` to install. WARNING: after applying, management is only reachable by knocking the exact sequence make sure you can knock (and keep a console/trusted path). Rules are tagged 'port-knock:'. Returns the plan or a build report.",
10079
10847
  "inputSchema": {
10080
10848
  "$schema": "https://json-schema.org/draft/2020-12/schema",
10081
10849
  "type": "object",
10082
10850
  "properties": {
10083
- "include_nat": {
10084
- "default": true,
10085
- "description": "Also audit `/ip firewall nat`.",
10086
- "type": "boolean"
10851
+ "sequence": {
10852
+ "minItems": 2,
10853
+ "type": "array",
10854
+ "items": {
10855
+ "type": "integer",
10856
+ "minimum": 1,
10857
+ "maximum": 65535
10858
+ },
10859
+ "description": "Secret knock ports, in order, e.g. [7001, 8002, 9003]"
10087
10860
  },
10088
- "include_mangle": {
10861
+ "protect_ports": {
10862
+ "default": "22,8291,8728",
10863
+ "description": "Management ports to hide",
10864
+ "type": "string"
10865
+ },
10866
+ "open_timeout": {
10867
+ "default": "1h",
10868
+ "description": "How long access stays open after the knock",
10869
+ "type": "string"
10870
+ },
10871
+ "stage_timeout": {
10872
+ "default": "10s",
10873
+ "description": "Allowed window between consecutive knocks",
10874
+ "type": "string"
10875
+ },
10876
+ "apply": {
10089
10877
  "default": false,
10090
- "description": "Also audit `/ip firewall mangle`.",
10878
+ "description": "false = preview (default); true = install",
10091
10879
  "type": "boolean"
10092
10880
  }
10093
10881
  },
10094
- "required": ["include_nat", "include_mangle"],
10882
+ "required": ["sequence", "protect_ports", "open_timeout", "stage_timeout", "apply"],
10095
10883
  "additionalProperties": false
10096
10884
  }
10097
10885
  },
@@ -10273,6 +11061,54 @@
10273
11061
  "additionalProperties": false
10274
11062
  }
10275
11063
  },
11064
+ {
11065
+ "name": "audit_certificate_expiry",
11066
+ "title": "Audit Certificate Expiry",
11067
+ "risk": "read",
11068
+ "annotations": {
11069
+ "readOnlyHint": true,
11070
+ "idempotentHint": true,
11071
+ "openWorldHint": false
11072
+ },
11073
+ "description": "Scans every certificate in the device store (`/certificate print detail`) and reports how long until each one expires — sorted by urgency, with anything ALREADY EXPIRED or expiring within `warn_within_days` (default 30) flagged first. Use this to catch a TLS cert (SSTP, API-SSL, hotspot, REST/www-ssl) before it lapses and silently breaks a service. For the full detail of one certificate use get_certificate; to obtain/renew a Let's Encrypt cert use request_letsencrypt_certificate. Returns a grouped report (expired · expiring soon · ok), plus any certs with no parseable expiry (e.g. unsigned templates).",
11074
+ "inputSchema": {
11075
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
11076
+ "type": "object",
11077
+ "properties": {
11078
+ "warn_within_days": {
11079
+ "default": 30,
11080
+ "description": "Flag certificates expiring within this many days as 'expiring soon'",
11081
+ "type": "integer",
11082
+ "minimum": -9007199254740991,
11083
+ "maximum": 9007199254740991
11084
+ }
11085
+ },
11086
+ "required": ["warn_within_days"],
11087
+ "additionalProperties": false
11088
+ }
11089
+ },
11090
+ {
11091
+ "name": "request_letsencrypt_certificate",
11092
+ "title": "Request/Renew Let's Encrypt Certificate",
11093
+ "risk": "write",
11094
+ "annotations": {
11095
+ "destructiveHint": false,
11096
+ "openWorldHint": false
11097
+ },
11098
+ "description": "Obtains (or renews) a free Let's Encrypt TLS certificate for `dns_name` using RouterOS's built-in ACME client (`/certificate enable-ssl-certificate`). The device must be reachable from the internet on TCP 80 and `dns_name` must already resolve to it (ACME http-01 challenge). On success the certificate is installed in the store and bound to the www-ssl service; re-running this for the same name renews it. To check what is installed and when it expires use audit_certificate_expiry. NOTE: this reaches the public ACME service and may take several seconds. Returns the device's output from the request.",
11099
+ "inputSchema": {
11100
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
11101
+ "type": "object",
11102
+ "properties": {
11103
+ "dns_name": {
11104
+ "type": "string",
11105
+ "description": "Public DNS name that resolves to this router, e.g. 'vpn.example.com'"
11106
+ }
11107
+ },
11108
+ "required": ["dns_name"],
11109
+ "additionalProperties": false
11110
+ }
11111
+ },
10276
11112
  {
10277
11113
  "name": "list_ip_services",
10278
11114
  "title": "List IP Management Services",
@@ -10322,7 +11158,7 @@
10322
11158
  "idempotentHint": true,
10323
11159
  "openWorldHint": false
10324
11160
  },
10325
- "description": "Updates one or more attributes of a router management service (`/ip service set`) — port number, allowed source subnets (`address`, comma-separated CIDRs), TLS certificate name, or enabled/disabled state (`disabled: true/false`). Accepts a service name such as `ssh`, `winbox`, `api`, or `api-ssl`. To toggle only the enabled state use `enable_ip_service` or `disable_ip_service` instead. Returns updated service detail on success.",
11161
+ "description": "Updates one or more attributes of a router management service (`/ip service set`) — port number, allowed source subnets (`address`, comma-separated CIDRs), TLS certificate name, or enabled/disabled state (`disabled: true/false`). Accepts a service name such as `ssh`, `winbox`, `api`, or `api-ssl`. Note: two services cannot share a port — if the chosen port is already used by another service the device rejects it (list_ip_services shows current ports). To toggle only the enabled state use `enable_ip_service` or `disable_ip_service` instead. Returns updated service detail on success.",
10326
11162
  "inputSchema": {
10327
11163
  "$schema": "https://json-schema.org/draft/2020-12/schema",
10328
11164
  "type": "object",
@@ -10400,6 +11236,88 @@
10400
11236
  "additionalProperties": false
10401
11237
  }
10402
11238
  },
11239
+ {
11240
+ "name": "get_ip_cloud",
11241
+ "title": "Get IP Cloud (DDNS) Status",
11242
+ "risk": "read",
11243
+ "annotations": {
11244
+ "readOnlyHint": true,
11245
+ "idempotentHint": true,
11246
+ "openWorldHint": false
11247
+ },
11248
+ "description": "Reads the RouterOS cloud DDNS status (`/ip cloud print`) — whether `ddns-enabled` is on, the assigned `dns-name` (`<serial>.sn.mynetname.net`), the detected public IPv4/IPv6 address, the last `update-time`, and the service `status`/warnings. Use this to find the router's stable cloud hostname or check why DDNS isn't updating. To change it use set_ip_cloud; to push an update now use force_cloud_update. Returns the full cloud status block.",
11249
+ "inputSchema": {
11250
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
11251
+ "type": "object",
11252
+ "properties": {},
11253
+ "additionalProperties": false
11254
+ }
11255
+ },
11256
+ {
11257
+ "name": "set_ip_cloud",
11258
+ "title": "Configure IP Cloud (DDNS)",
11259
+ "risk": "write-idempotent",
11260
+ "annotations": {
11261
+ "destructiveHint": false,
11262
+ "idempotentHint": true,
11263
+ "openWorldHint": false
11264
+ },
11265
+ "description": "Configures RouterOS cloud DDNS (`/ip cloud set`). Enable/disable the dynamic DNS name with `ddns_enabled`, optionally also sync the router clock from the cloud with `update_time`. When enabled the router registers a `<serial>.sn.mynetname.net` name that follows its public IP. To read the resulting name/status use get_ip_cloud; to force an immediate refresh use force_cloud_update; for the local-vs-public address behaviour use set_ip_cloud_advanced. Returns the updated cloud status.",
11266
+ "inputSchema": {
11267
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
11268
+ "type": "object",
11269
+ "properties": {
11270
+ "ddns_enabled": {
11271
+ "description": "Enable (true) or disable (false) the DDNS name",
11272
+ "type": "boolean"
11273
+ },
11274
+ "update_time": {
11275
+ "description": "Also sync the router clock from the cloud",
11276
+ "type": "boolean"
11277
+ }
11278
+ },
11279
+ "additionalProperties": false
11280
+ }
11281
+ },
11282
+ {
11283
+ "name": "force_cloud_update",
11284
+ "title": "Force IP Cloud DDNS Update",
11285
+ "risk": "write",
11286
+ "annotations": {
11287
+ "destructiveHint": false,
11288
+ "openWorldHint": false
11289
+ },
11290
+ "description": "Forces an immediate cloud DDNS refresh (`/ip cloud force-update`) — pushes the router's current public IP to the cloud now instead of waiting for the next scheduled update. Use after a WAN IP change or to test connectivity to the cloud service. Requires DDNS to be enabled (see set_ip_cloud). Returns the cloud status after the update.",
11291
+ "inputSchema": {
11292
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
11293
+ "type": "object",
11294
+ "properties": {},
11295
+ "additionalProperties": false
11296
+ }
11297
+ },
11298
+ {
11299
+ "name": "set_ip_cloud_advanced",
11300
+ "title": "Set IP Cloud Advanced Options",
11301
+ "risk": "write-idempotent",
11302
+ "annotations": {
11303
+ "destructiveHint": false,
11304
+ "idempotentHint": true,
11305
+ "openWorldHint": false
11306
+ },
11307
+ "description": "Sets advanced cloud options (`/ip cloud advanced set`). `use_local_address=true` makes the cloud DDNS publish the router's LOCAL address instead of the detected public one (useful behind a private WAN / when you reach the router over a VPN). For the main DDNS toggle use set_ip_cloud. Returns the updated cloud status.",
11308
+ "inputSchema": {
11309
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
11310
+ "type": "object",
11311
+ "properties": {
11312
+ "use_local_address": {
11313
+ "type": "boolean",
11314
+ "description": "Publish the local address (true) instead of the public address (false)"
11315
+ }
11316
+ },
11317
+ "required": ["use_local_address"],
11318
+ "additionalProperties": false
11319
+ }
11320
+ },
10403
11321
  {
10404
11322
  "name": "add_dot1x_server",
10405
11323
  "title": "Add 802.1X Server (Authenticator) Entry",
@@ -11167,65 +12085,224 @@
11167
12085
  "description": "\"*N\" or \"N\" from list output e.g. \"*2\""
11168
12086
  }
11169
12087
  },
11170
- "required": ["peer_id"],
12088
+ "required": ["peer_id"],
12089
+ "additionalProperties": false
12090
+ }
12091
+ },
12092
+ {
12093
+ "name": "generate_wireguard_client_config",
12094
+ "title": "Generate WireGuard Client Config File",
12095
+ "risk": "read",
12096
+ "annotations": {
12097
+ "readOnlyHint": true,
12098
+ "idempotentHint": true,
12099
+ "openWorldHint": false
12100
+ },
12101
+ "description": "Generates a standard wg0.conf client configuration file (`[Interface] + [Peer]` sections) entirely from the provided keys and server endpoint — runs locally, never contacts the router. Use this after registering a client's public key with add_wireguard_peer to produce the config text to distribute to that client. Returns the config text ready to save as /etc/wireguard/wg0.conf (Linux) or import into the WireGuard app, with instructions for generating client keys and bringing the tunnel up.\n\nNotes:\n 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)\n persistent_keepalive: seconds integer, default 25",
12102
+ "inputSchema": {
12103
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
12104
+ "type": "object",
12105
+ "properties": {
12106
+ "client_private_key": {
12107
+ "type": "string"
12108
+ },
12109
+ "client_address": {
12110
+ "type": "string"
12111
+ },
12112
+ "server_public_key": {
12113
+ "type": "string"
12114
+ },
12115
+ "server_endpoint": {
12116
+ "type": "string"
12117
+ },
12118
+ "server_port": {
12119
+ "default": 51820,
12120
+ "type": "integer",
12121
+ "minimum": -9007199254740991,
12122
+ "maximum": 9007199254740991
12123
+ },
12124
+ "allowed_ips": {
12125
+ "default": "0.0.0.0/0",
12126
+ "type": "string"
12127
+ },
12128
+ "dns": {
12129
+ "type": "string"
12130
+ },
12131
+ "persistent_keepalive": {
12132
+ "default": 25,
12133
+ "type": "integer",
12134
+ "minimum": -9007199254740991,
12135
+ "maximum": 9007199254740991
12136
+ }
12137
+ },
12138
+ "required": [
12139
+ "client_private_key",
12140
+ "client_address",
12141
+ "server_public_key",
12142
+ "server_endpoint",
12143
+ "server_port",
12144
+ "allowed_ips",
12145
+ "persistent_keepalive"
12146
+ ],
12147
+ "additionalProperties": false
12148
+ }
12149
+ },
12150
+ {
12151
+ "name": "build_wireguard_mesh",
12152
+ "title": "Build WireGuard Mesh / Hub-Spoke VPN",
12153
+ "risk": "dangerous",
12154
+ "annotations": {
12155
+ "destructiveHint": true,
12156
+ "openWorldHint": false
12157
+ },
12158
+ "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 — `topology=full-mesh` connects every pair, `hub-spoke` connects the `hub` to each spoke — 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.",
12159
+ "inputSchema": {
12160
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
12161
+ "type": "object",
12162
+ "properties": {
12163
+ "devices": {
12164
+ "minItems": 2,
12165
+ "type": "array",
12166
+ "items": {
12167
+ "type": "string"
12168
+ },
12169
+ "description": "Configured device names to include in the mesh (2+)"
12170
+ },
12171
+ "address_prefix": {
12172
+ "default": "10.20.0.0/24",
12173
+ "description": "Mesh subnet; each device gets <prefix>.<index+1> on its WireGuard interface",
12174
+ "type": "string"
12175
+ },
12176
+ "interface": {
12177
+ "default": "wg-mesh",
12178
+ "description": "WireGuard interface name to create/use",
12179
+ "type": "string"
12180
+ },
12181
+ "listen_port": {
12182
+ "default": 13231,
12183
+ "type": "integer",
12184
+ "minimum": -9007199254740991,
12185
+ "maximum": 9007199254740991
12186
+ },
12187
+ "topology": {
12188
+ "default": "full-mesh",
12189
+ "type": "string",
12190
+ "enum": ["full-mesh", "hub-spoke"]
12191
+ },
12192
+ "hub": {
12193
+ "description": "Hub device name (required when topology=hub-spoke)",
12194
+ "type": "string"
12195
+ },
12196
+ "endpoints": {
12197
+ "description": "Per-device public endpoint host override, e.g. {\"site-a\":\"a.example.com\"}",
12198
+ "type": "object",
12199
+ "propertyNames": {
12200
+ "type": "string"
12201
+ },
12202
+ "additionalProperties": {
12203
+ "type": "string"
12204
+ }
12205
+ },
12206
+ "allowed_lans": {
12207
+ "description": "Per-device LAN subnet to route through the tunnel, e.g. {\"site-a\":\"192.168.10.0/24\"}",
12208
+ "type": "object",
12209
+ "propertyNames": {
12210
+ "type": "string"
12211
+ },
12212
+ "additionalProperties": {
12213
+ "type": "string"
12214
+ }
12215
+ },
12216
+ "persistent_keepalive": {
12217
+ "default": "25s",
12218
+ "type": "string"
12219
+ },
12220
+ "apply": {
12221
+ "default": false,
12222
+ "description": "false = preview the plan (default); true = build",
12223
+ "type": "boolean"
12224
+ }
12225
+ },
12226
+ "required": [
12227
+ "devices",
12228
+ "address_prefix",
12229
+ "interface",
12230
+ "listen_port",
12231
+ "topology",
12232
+ "persistent_keepalive",
12233
+ "apply"
12234
+ ],
12235
+ "additionalProperties": false
12236
+ }
12237
+ },
12238
+ {
12239
+ "name": "onboard_wireguard_user",
12240
+ "title": "Onboard WireGuard Remote User",
12241
+ "risk": "write",
12242
+ "annotations": {
12243
+ "destructiveHint": false,
12244
+ "openWorldHint": false
12245
+ },
12246
+ "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) — 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.",
12247
+ "inputSchema": {
12248
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
12249
+ "type": "object",
12250
+ "properties": {
12251
+ "interface": {
12252
+ "type": "string",
12253
+ "description": "Existing WireGuard SERVER interface, e.g. 'wg-server'"
12254
+ },
12255
+ "user": {
12256
+ "type": "string",
12257
+ "description": "User/device label, e.g. 'alice-laptop'"
12258
+ },
12259
+ "address": {
12260
+ "type": "string",
12261
+ "description": "Tunnel IP to assign the client, e.g. '10.20.0.50'"
12262
+ },
12263
+ "endpoint": {
12264
+ "type": "string",
12265
+ "description": "Server public endpoint host:port, e.g. 'vpn.example.com:13231'"
12266
+ },
12267
+ "dns": {
12268
+ "description": "DNS for the client, e.g. '10.20.0.1'",
12269
+ "type": "string"
12270
+ },
12271
+ "allowed_ips": {
12272
+ "default": "0.0.0.0/0",
12273
+ "description": "Client AllowedIPs: '0.0.0.0/0' full-tunnel, or a LAN subnet for split-tunnel",
12274
+ "type": "string"
12275
+ },
12276
+ "keepalive": {
12277
+ "default": "25",
12278
+ "description": "PersistentKeepalive seconds",
12279
+ "type": "string"
12280
+ }
12281
+ },
12282
+ "required": ["interface", "user", "address", "endpoint", "allowed_ips", "keepalive"],
11171
12283
  "additionalProperties": false
11172
12284
  }
11173
12285
  },
11174
12286
  {
11175
- "name": "generate_wireguard_client_config",
11176
- "title": "Generate WireGuard Client Config File",
11177
- "risk": "read",
12287
+ "name": "revoke_wireguard_user",
12288
+ "title": "Revoke WireGuard Remote User",
12289
+ "risk": "destructive",
11178
12290
  "annotations": {
11179
- "readOnlyHint": true,
12291
+ "destructiveHint": true,
11180
12292
  "idempotentHint": true,
11181
12293
  "openWorldHint": false
11182
12294
  },
11183
- "description": "Generates a standard wg0.conf client configuration file (`[Interface] + [Peer]` sections) entirely from the provided keys and server endpoint — runs locally, never contacts the router. Use this after registering a client's public key with add_wireguard_peer to produce the config text to distribute to that client. Returns the config text ready to save as /etc/wireguard/wg0.conf (Linux) or import into the WireGuard app, with instructions for generating client keys and bringing the tunnel up.\n\nNotes:\n 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)\n persistent_keepalive: seconds integer, default 25",
12295
+ "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.",
11184
12296
  "inputSchema": {
11185
12297
  "$schema": "https://json-schema.org/draft/2020-12/schema",
11186
12298
  "type": "object",
11187
12299
  "properties": {
11188
- "client_private_key": {
11189
- "type": "string"
11190
- },
11191
- "client_address": {
11192
- "type": "string"
11193
- },
11194
- "server_public_key": {
11195
- "type": "string"
11196
- },
11197
- "server_endpoint": {
11198
- "type": "string"
11199
- },
11200
- "server_port": {
11201
- "default": 51820,
11202
- "type": "integer",
11203
- "minimum": -9007199254740991,
11204
- "maximum": 9007199254740991
11205
- },
11206
- "allowed_ips": {
11207
- "default": "0.0.0.0/0",
11208
- "type": "string"
11209
- },
11210
- "dns": {
11211
- "type": "string"
11212
- },
11213
- "persistent_keepalive": {
11214
- "default": 25,
11215
- "type": "integer",
11216
- "minimum": -9007199254740991,
11217
- "maximum": 9007199254740991
12300
+ "user": {
12301
+ "type": "string",
12302
+ "description": "The user label used at onboarding"
11218
12303
  }
11219
12304
  },
11220
- "required": [
11221
- "client_private_key",
11222
- "client_address",
11223
- "server_public_key",
11224
- "server_endpoint",
11225
- "server_port",
11226
- "allowed_ips",
11227
- "persistent_keepalive"
11228
- ],
12305
+ "required": ["user"],
11229
12306
  "additionalProperties": false
11230
12307
  }
11231
12308
  },
@@ -15353,6 +16430,63 @@
15353
16430
  "additionalProperties": false
15354
16431
  }
15355
16432
  },
16433
+ {
16434
+ "name": "apply_traffic_shaping",
16435
+ "title": "Apply Traffic-Shaping Policy",
16436
+ "risk": "write",
16437
+ "annotations": {
16438
+ "destructiveHint": false,
16439
+ "openWorldHint": false
16440
+ },
16441
+ "description": "Builds a traffic-shaping policy as a batch of `/queue simple` classes (target address, max-limit, priority) in one call — e.g. prioritize a VoIP subnet and cap a guest network. DEFAULTS TO A DRY RUN: with `apply=false` (default) it returns the exact RouterOS commands it WOULD run so the shape can be reviewed first; set `apply=true` to create the queues and verify them. Classes are created in array order, and RouterOS evaluates simple queues top-down, so list the most specific/important first. For classification by port/protocol (not address) use the firewall mangle tools to mark packets, then a queue tree. Returns the dry-run plan or the created queues.",
16442
+ "inputSchema": {
16443
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
16444
+ "type": "object",
16445
+ "properties": {
16446
+ "classes": {
16447
+ "minItems": 1,
16448
+ "type": "array",
16449
+ "items": {
16450
+ "type": "object",
16451
+ "properties": {
16452
+ "name": {
16453
+ "type": "string",
16454
+ "description": "Queue name, e.g. 'voip' or 'guest-cap'"
16455
+ },
16456
+ "target": {
16457
+ "type": "string",
16458
+ "description": "Target address/subnet this class applies to, e.g. '192.168.20.0/24'"
16459
+ },
16460
+ "max_limit": {
16461
+ "type": "string",
16462
+ "description": "RouterOS max-limit as 'upload/download', e.g. '10M/10M' (or a single value)"
16463
+ },
16464
+ "priority": {
16465
+ "default": 8,
16466
+ "description": "1 (highest) … 8 (lowest); lower number wins under contention",
16467
+ "type": "integer",
16468
+ "minimum": 1,
16469
+ "maximum": 8
16470
+ },
16471
+ "comment": {
16472
+ "type": "string"
16473
+ }
16474
+ },
16475
+ "required": ["name", "target", "max_limit", "priority"],
16476
+ "additionalProperties": false
16477
+ },
16478
+ "description": "Shaping classes to create, in priority order"
16479
+ },
16480
+ "apply": {
16481
+ "default": false,
16482
+ "description": "false = preview the commands only (default); true = create the queues",
16483
+ "type": "boolean"
16484
+ }
16485
+ },
16486
+ "required": ["classes", "apply"],
16487
+ "additionalProperties": false
16488
+ }
16489
+ },
15356
16490
  {
15357
16491
  "name": "list_queue_interfaces",
15358
16492
  "title": "List Queue Interface Assignments",
@@ -16416,6 +17550,108 @@
16416
17550
  "additionalProperties": false
16417
17551
  }
16418
17552
  },
17553
+ {
17554
+ "name": "test_ssh_to_device",
17555
+ "title": "Test SSH to a Configured Device (from the MCP host)",
17556
+ "risk": "read",
17557
+ "annotations": {
17558
+ "readOnlyHint": true,
17559
+ "idempotentHint": true,
17560
+ "openWorldHint": false
17561
+ },
17562
+ "description": "Tests the SSH connection FROM THE MACHINE RUNNING THIS MCP SERVER to a CONFIGURED device, using that device's stored host/port/credentials. Use this to diagnose why a saved device is not responding — it confirms whether the MCP host can reach the device's SSH port AND authenticate, and reports the connect latency and identity. Targets the selected/default device (the `device` selector). NOTE: this opens a real SSH connection from the MCP host, not through the usual command channel, and is SSH-only (a MAC-Telnet device has no SSH). To test an UNSAVED host with ad-hoc credentials use test_ssh_to_host; to test SSH OUTBOUND from a router use test_ssh_from_device. Returns OK/failed with the reason.",
17563
+ "inputSchema": {
17564
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
17565
+ "type": "object",
17566
+ "properties": {},
17567
+ "additionalProperties": false
17568
+ }
17569
+ },
17570
+ {
17571
+ "name": "test_ssh_to_host",
17572
+ "title": "Test SSH to Any Host (from the MCP host)",
17573
+ "risk": "read",
17574
+ "annotations": {
17575
+ "readOnlyHint": true,
17576
+ "idempotentHint": true,
17577
+ "openWorldHint": false
17578
+ },
17579
+ "description": "Tests an SSH connection FROM THE MACHINE RUNNING THIS MCP SERVER to an ARBITRARY host:port with the supplied credentials — a pre-flight check BEFORE adding a device to the config, or to verify reachability/credentials for any SSH host. Confirms TCP reachability AND authentication and reports the connect latency. Provide a `password` and/or a private-key path (`key_path`). NOTE: this opens a real SSH connection from the MCP host. For a device that is ALREADY configured use test_ssh_to_device; to test SSH outbound FROM a router use test_ssh_from_device. Returns OK/failed with the reason.",
17580
+ "inputSchema": {
17581
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
17582
+ "type": "object",
17583
+ "properties": {
17584
+ "host": {
17585
+ "type": "string",
17586
+ "description": "Target IP or hostname"
17587
+ },
17588
+ "port": {
17589
+ "default": 22,
17590
+ "type": "integer",
17591
+ "minimum": 1,
17592
+ "maximum": 65535
17593
+ },
17594
+ "username": {
17595
+ "type": "string"
17596
+ },
17597
+ "password": {
17598
+ "description": "Password auth (omit if using a key)",
17599
+ "type": "string"
17600
+ },
17601
+ "key_path": {
17602
+ "description": "Path (on the MCP host) to a private key file",
17603
+ "type": "string"
17604
+ },
17605
+ "key_passphrase": {
17606
+ "type": "string"
17607
+ },
17608
+ "command": {
17609
+ "default": "/system identity print",
17610
+ "description": "Command to run once connected (a harmless RouterOS read by default)",
17611
+ "type": "string"
17612
+ }
17613
+ },
17614
+ "required": ["host", "port", "username", "command"],
17615
+ "additionalProperties": false
17616
+ }
17617
+ },
17618
+ {
17619
+ "name": "test_ssh_from_device",
17620
+ "title": "Test SSH Outbound From a RouterOS Device",
17621
+ "risk": "write",
17622
+ "annotations": {
17623
+ "destructiveHint": false,
17624
+ "openWorldHint": false
17625
+ },
17626
+ "description": "Tests SSH OUTBOUND FROM a RouterOS device to a remote host using the device's OWN SSH client (`/system ssh-exec`) — i.e. the connection is made BY the router, not by the MCP host. Use this to check whether a router can reach and log into another host over SSH (e.g. a jump path or another router). The router's user must be able to authenticate to the target NON-interactively (a stored key on the router) — a password-only target will prompt and time out. Runs the test `command` on the remote and returns its output. To test the MCP HOST's connection to a device instead, use test_ssh_to_device (configured) or test_ssh_to_host (ad-hoc). Returns the remote command output or the device's error.",
17627
+ "inputSchema": {
17628
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
17629
+ "type": "object",
17630
+ "properties": {
17631
+ "host": {
17632
+ "type": "string",
17633
+ "description": "Remote host the ROUTER should SSH to"
17634
+ },
17635
+ "port": {
17636
+ "default": 22,
17637
+ "type": "integer",
17638
+ "minimum": 1,
17639
+ "maximum": 65535
17640
+ },
17641
+ "user": {
17642
+ "type": "string",
17643
+ "description": "Username on the remote host"
17644
+ },
17645
+ "command": {
17646
+ "default": "/system identity print",
17647
+ "description": "Command to run on the remote host as the connectivity test",
17648
+ "type": "string"
17649
+ }
17650
+ },
17651
+ "required": ["host", "port", "user", "command"],
17652
+ "additionalProperties": false
17653
+ }
17654
+ },
16419
17655
  {
16420
17656
  "name": "list_neighbors",
16421
17657
  "title": "List Discovered Neighbor Devices (MNDP/CDP/LLDP)",
@@ -17997,6 +19233,39 @@
17997
19233
  "additionalProperties": false
17998
19234
  }
17999
19235
  },
19236
+ {
19237
+ "name": "forecast_link_saturation",
19238
+ "title": "Forecast Link Saturation",
19239
+ "risk": "read",
19240
+ "annotations": {
19241
+ "readOnlyHint": true,
19242
+ "idempotentHint": true,
19243
+ "openWorldHint": false
19244
+ },
19245
+ "description": "Estimates when an interface will saturate. Samples its current throughput (`/interface monitor-traffic ... once`), computes utilization against `capacity_mbps` (the link's rated speed), and projects the trend forward at `monthly_growth_pct` to a saturation date. Note: this is a single live sample projected at an ASSUMED growth rate — feed a realistic growth figure (or watch the trend over time) for a better forecast. Returns current load, utilization, and the projected days/date to saturation.",
19246
+ "inputSchema": {
19247
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
19248
+ "type": "object",
19249
+ "properties": {
19250
+ "interface": {
19251
+ "type": "string",
19252
+ "description": "Interface to sample, e.g. 'ether1-wan'"
19253
+ },
19254
+ "capacity_mbps": {
19255
+ "default": 1000,
19256
+ "description": "Link rated speed in Mbit/s (e.g. 1000 for gig)",
19257
+ "type": "number"
19258
+ },
19259
+ "monthly_growth_pct": {
19260
+ "default": 10,
19261
+ "description": "Assumed month-over-month traffic growth %",
19262
+ "type": "number"
19263
+ }
19264
+ },
19265
+ "required": ["interface", "capacity_mbps", "monthly_growth_pct"],
19266
+ "additionalProperties": false
19267
+ }
19268
+ },
18000
19269
  {
18001
19270
  "name": "wake_on_lan",
18002
19271
  "title": "Send Wake-on-LAN Magic Packet",
@@ -18277,6 +19546,75 @@
18277
19546
  "additionalProperties": false
18278
19547
  }
18279
19548
  },
19549
+ {
19550
+ "name": "subscribe_threat_feed",
19551
+ "title": "Subscribe to Threat-Intel Feed",
19552
+ "risk": "write",
19553
+ "annotations": {
19554
+ "destructiveHint": false,
19555
+ "openWorldHint": false
19556
+ },
19557
+ "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 — 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.",
19558
+ "inputSchema": {
19559
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
19560
+ "type": "object",
19561
+ "properties": {
19562
+ "name": {
19563
+ "type": "string",
19564
+ "description": "Short feed id, e.g. 'spamhaus-drop'"
19565
+ },
19566
+ "url": {
19567
+ "type": "string",
19568
+ "description": "HTTPS URL of a RouterOS .rsc address-list file"
19569
+ },
19570
+ "address_list": {
19571
+ "default": "threat-blocklist",
19572
+ "description": "Address-list to populate",
19573
+ "type": "string"
19574
+ },
19575
+ "interval": {
19576
+ "default": "1h",
19577
+ "description": "How often to refresh the feed",
19578
+ "type": "string"
19579
+ },
19580
+ "drop": {
19581
+ "default": true,
19582
+ "description": "Also add a raw drop rule for the address-list",
19583
+ "type": "boolean"
19584
+ },
19585
+ "apply": {
19586
+ "default": false,
19587
+ "description": "false = preview (default); true = install",
19588
+ "type": "boolean"
19589
+ }
19590
+ },
19591
+ "required": ["name", "url", "address_list", "interval", "drop", "apply"],
19592
+ "additionalProperties": false
19593
+ }
19594
+ },
19595
+ {
19596
+ "name": "remove_threat_feed",
19597
+ "title": "Remove Threat-Intel Feed",
19598
+ "risk": "destructive",
19599
+ "annotations": {
19600
+ "destructiveHint": true,
19601
+ "idempotentHint": true,
19602
+ "openWorldHint": false
19603
+ },
19604
+ "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.",
19605
+ "inputSchema": {
19606
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
19607
+ "type": "object",
19608
+ "properties": {
19609
+ "name": {
19610
+ "type": "string",
19611
+ "description": "The feed id used when subscribing"
19612
+ }
19613
+ },
19614
+ "required": ["name"],
19615
+ "additionalProperties": false
19616
+ }
19617
+ },
18280
19618
  {
18281
19619
  "name": "add_user",
18282
19620
  "title": "Add Local User Account",
@@ -20119,6 +21457,48 @@
20119
21457
  "additionalProperties": false
20120
21458
  }
20121
21459
  },
21460
+ {
21461
+ "name": "run_failover_drill",
21462
+ "title": "Run Failover DR Drill",
21463
+ "risk": "dangerous",
21464
+ "annotations": {
21465
+ "destructiveHint": true,
21466
+ "openWorldHint": false
21467
+ },
21468
+ "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 — 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.",
21469
+ "inputSchema": {
21470
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
21471
+ "type": "object",
21472
+ "properties": {
21473
+ "target_type": {
21474
+ "default": "interface",
21475
+ "type": "string",
21476
+ "enum": ["interface", "route"]
21477
+ },
21478
+ "target": {
21479
+ "type": "string",
21480
+ "description": "Interface name (e.g. 'ether1-wan') or, for route, a find expression / .id"
21481
+ },
21482
+ "verify_host": {
21483
+ "type": "string",
21484
+ "description": "Host to ping during the outage to confirm the backup path, e.g. '8.8.8.8'"
21485
+ },
21486
+ "ping_count": {
21487
+ "default": 5,
21488
+ "type": "integer",
21489
+ "minimum": 1,
21490
+ "maximum": 50
21491
+ },
21492
+ "confirm": {
21493
+ "default": false,
21494
+ "description": "Must be true to actually run the drill",
21495
+ "type": "boolean"
21496
+ }
21497
+ },
21498
+ "required": ["target_type", "target", "verify_host", "ping_count", "confirm"],
21499
+ "additionalProperties": false
21500
+ }
21501
+ },
20122
21502
  {
20123
21503
  "name": "plan_changes",
20124
21504
  "title": "Plan RouterOS Changes (Dry-Run Preview)",