Files

3.8 KiB

name, description, version, author, license, platforms, prerequisites, metadata
name description version author license platforms prerequisites metadata
saiop-mikrotik-mcp MikroTik (RouterOS) read-only MCP access for rt-alpha — TLS debugging notes and the read-only verification method. 1.0.0 Claude Code (SAIOP ops session) MIT
linux
env_vars commands
MIKROTIK_PASSWORD
uv
hermes
tags
SAIOP
MCP
MikroTik
RouterOS
network
security

MikroTik (rt-alpha) read-only MCP access

When to use

Querying the ATU network's MikroTik router (rt-alpha, 169.239.248.166) for read-only diagnostics (interfaces, routes, DHCP leases, firewall rules) via Hermes.

What's wired up

A custom single-tool MCP server (/opt/ai-stack/hermes/scripts/mikrotik_mcp.py, run via uv run --with mcp --with librouteros) connects via the RouterOS API over TLS (port 8729) as a dedicated claudeuser account.

Two independent layers, same pattern as postgres-mcp/proxmox-mcp:

  1. The claudeuser RouterOS account itself is read-only — independently verified, not assumed: a real write attempt (/interface/set to change a comment) returned TrapError: not enough permissions (9) directly from the device's permission system.
  2. The MCP tool also only accepts paths ending in /print, /getall, or /export (rejects anything else before even attempting the call) — defense-in-depth on top of #1, not the primary boundary.

TLS gotchas hit getting here (useful if this recurs for other network gear)

  • Initial connection attempts failed with a TLS handshake failure. Root cause: RouterOS's api-ssl service had no certificate assigned, so it fell back to anonymous Diffie-Hellman ciphers (ADH-AES256-SHA256) — meaning no server authentication at all. Confirmed via raw openssl s_client -cipher "ALL:@SECLEVEL=0" -tls1_2, which negotiated successfully where every normal client library failed (modern TLS stacks don't enable anonymous ciphers the same permissive way the CLI does, even with matching settings — spent real effort confirming this was the device's config, not a bug in our approach, before escalating to ask for a router-side fix).
  • The actual fix was on the device, not the client: assigning a real certificate to the api-ssl service (/certificate add + /certificate sign + /ip service set api-ssl certificate=...). Once that landed, standard TLS negotiated cleanly — no special cipher/version pinning needed in the client at all.
  • Even with a real cert, it had its own separate issues (expired, CN mismatch vs. the hostname used to connect) — handled with verify_mode = ssl.CERT_NONE in the client, since we already have independent assurance of which device we're talking to (the ~1ms ping latency confirms it's on a directly-attached local segment, not reachable for interception the way a public-internet path would be). Worth a routine cert renewal on the network side regardless.
  • librouteros's Api.__call__ takes the RouterOS path positionally, not as a cmd= keyword (api("/interface/print"), not api(cmd="/interface/print")) — easy mistake, produces a confusing TypeError: missing 1 required positional argument.

Why a custom server instead of mcp-server-mikrotik

That package exists and is actively maintained, but it's the wrong shape for this need: it connects via SSH and an interactive shell, uses RouterOS's own "Safe Mode" (a revert-on-disconnect safety net for live config changes, not a read-only restriction), and exposes broad read-write scope across firewall/DHCP/DNS/NAT/backup management. Writing ~70 lines against the already-verified API connection was less work and matches the actual need (read-only diagnostics) far more precisely — same reasoning as the proxmox-mcp custom server earlier in this project.