MikroTik RouterOS over the binary API, addressing rules by real .id not list position.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
One-click editor setup isnβt available for this listing yet β we donβt have a confirmed install command, and weβd rather show nothing than point your editor at the wrong package or host. Follow the projectβs own setup instructions, linked above.
mcp-name: io.github.StefanKnol/mikrotik-mcp
An MCP server for MikroTik RouterOS, over the binary API rather than by driving the CLI over SSH.
That distinction is the whole point. The CLI prints positional numbers, which
are not a rule's identity. A server that lists rules by position and then writes
by position either fails outright β where .id=3 matches nothing, so every
write reports "not found" for rules that plainly exist β or, worse, succeeds
against a different rule once the order has shifted. On a firewall that means
deleting the wrong rule.
The binary API returns the real .id (*7, *1f) on every read. So:
id, and every write takes one back;position, which is display-only and refused for
writes, with an error that explains why;remove_firewall_rule takes an optional confirm_comment and returns the
rule it deleted.Configure through the environment β ps would show a password passed as a flag:
| Variable | Default | Meaning |
|---|---|---|
MIKROTIK_HOST | β | Router address. Required. |
MIKROTIK_USERNAME | β | Required. |
MIKROTIK_PASSWORD | β | |
MIKROTIK_PORT | 8729 | 8729 for api-ssl, 8728 for plaintext. |
MIKROTIK_TLS | true | |
MIKROTIK_TLS_FINGERPRINT | β | SHA-256 of the router certificate, to pin it. |
MIKROTIK_TIMEOUT | 10 |
In an MCP client's config:
The api policy is not optional, and its absence produces a login error
identical to a wrong password β so if credentials look right and login still
fails, check the group first.
Set MIKROTIK_TLS_FINGERPRINT if you can. MikroTik's API-SSL certificate is
self-signed, so ordinary CA validation cannot succeed against a stock device;
pinning is what makes the connection authenticated rather than merely encrypted.
34 of them, covering system info, interfaces, IP addressing, firewall filter
(IPv4 and IPv6), firewall address lists, NAT, DHCP leases, DNS, routes and logs
β plus ros_list, which reads any RouterOS path and so covers everything
without a dedicated tool.
There is deliberately no generic write escape hatch.
Three things worth knowing before the first call:
id (*7), never a position. Both appear in every list
result. position is where a rule currently sits in evaluation order; it
shifts whenever anything is added, removed or moved, and it is counted across
the whole table rather than within the chain you filtered to. Passing one to
a write is rejected rather than guessed at.family
and default to ipv4, so a device can read as locked down while its IPv6
table is empty and therefore accepting everything. Listing IPv4 reports the
IPv6 rule count for exactly this reason.list_address_list_entries for a count_only first: a populated block list
can hold tens of thousands.mcphub puts MCP servers behind one
sign-in and grants them out per account. One router is one backend, at its own
/mcp/<slug>, registered in a client as its own connector.
From the registry β the better default. Add from registry, search for
mikrotik-mcp, and the hub builds the settings form from server.json and
launches uvx mikrotik-mcp when you enable the backend. It runs in its own
process and cannot read credentials the hub holds for anything else. Backends
added this way start disabled: open it, look at the tool list, then enable.
As a plugin β install this package into the hub's own environment and
mikrotik appears as a backend kind, with typed fields and a Test button that
reports the router's identity and RouterOS version. Nicer to configure, but an
in-process plugin can read every credential the hub holds. Use it when you
build your own hub image.
Either route asks for the same things. As environment variables:
| Variable | Default | |
|---|---|---|
MIKROTIK_HOST | β | Router address. Required. |
MIKROTIK_USERNAME | β | Required. A dedicated user, not admin; its group needs the api policy. |
MIKROTIK_PASSWORD | β | Required. |
MIKROTIK_PORT | 8729 | 8729 for api-ssl, 8728 for plaintext. |
MIKROTIK_TLS | true | Turning it off sends the router password over the network in the clear. |
MIKROTIK_TLS_FINGERPRINT | β | SHA-256 of the router certificate. Pinning it is what makes the TLS connection authenticated rather than merely encrypted. |
MIKROTIK_TIMEOUT | 10 | Seconds. |
No data directory: everything this server reads and writes lives on the router, so there is no local state to keep. Leave Give this server a data directory unticked.
The hub reads readOnlyHint and destructiveHint from each tool and enforces
the grant's level from those alone β a tool above the level is left out of
tools/list and refused if called anyway.
| Level | Gets | |
|---|---|---|
viewer | 17 tools | Reads the whole configuration and changes nothing. |
user | +7 tools | Adds rules and entries, and disables or re-enables them. |
admin | +10 tools | Deletes, edits in place, and reorders. |
viewer β connectivity_check, get_dns_settings, get_firewall_rule,
get_logs, get_nat_rule, list_address_list_entries, list_address_lists,
list_dhcp_leases, list_dns_adlist, list_dns_static, list_firewall_rules,
list_interfaces, list_ip_addresses, list_nat_rules, list_routes,
ros_list, system_info.
user adds add_address_list_entry, add_dns_static, add_firewall_rule,
add_nat_rule, make_lease_static, set_firewall_rule_enabled,
set_nat_rule_enabled. Each of these writes, and none of them takes anything
away: adding a rule is a write, not a destruction, and disabling one keeps the
rule, its comment and its position so that re-enabling restores exactly what
was there. Disabling is the reversible way to find out whether a rule is
responsible for something, so it sits here rather than behind admin β the
alternative would leave a user with no safe way to test at all.
admin adds the destructive ten:
| Tool | What is lost |
|---|---|
remove_firewall_rule | The rule. The returned copy is the only record. |
remove_nat_rule | The rule. |
remove_dns_static | The entry. |
remove_address_list_entry | The entry. A dynamic one returns when its rule next matches; a static one does not. |
update_firewall_rule | The previous values of whatever it sets or clears. |
update_nat_rule | The same. |
update_dns_static | The same. |
move_firewall_rule | The previous order, which is the entire semantics of a firewall. |
move_nat_rule | The same. |
set_interface_enabled | Nothing on disk β but disabling the interface the request came in through severs the only route back, and nothing here can undo that remotely. |
A test pins every one of these assignments by name, so a tool added later fails the suite until someone decides which level it belongs to.
server.json is the manifest for the official MCP registry,
validated against the published schema. It declares the uvx mikrotik-mcp
command and every MIKROTIK_* variable, marking which are required and which
are secret β so a client that browses the registry can generate a correct
settings form without knowing anything about this server.
Publishing has an order to it, because the registry verifies that whoever publishes an entry actually owns the package it points at.
1. The package must already be on PyPI. The registry fetches
pypi.org/pypi/mikrotik-mcp/<version>/json and refuses an entry whose package
does not exist. Tag a release and let CI publish it:
That needs a PyPI Trusted Publisher first, and because this project does not exist on PyPI yet it has to be a pending publisher β the per-project Publishing tab only appears once a project exists, which is the chicken-and-egg this page solves:
pypi.org β Account settings β Publishing β Add a new pending publisher
Field Value PyPI Project Name mikrotik-mcpOwner StefanKnolRepository name mikrotik-mcpWorkflow name ci.ymlEnvironment name pypi
The environment name matters: the publish job declares environment: pypi, and
PyPI rejects the upload if they disagree.
2. The README must carry the ownership token. The registry looks for
mcp-name: io.github.StefanKnol/mikrotik-mcp in the PyPI description, which is
this file β it is at the top, on its own line. That is what proves the person
publishing the registry entry controls the PyPI package.
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/mikrotik-routeros)<a href="https://allmcps.com/mcp/mikrotik-routeros"><img src="https://allmcps.com/api/badge/mikrotik-routeros?style=directory" alt="MikroTik RouterOS on AllMCPs" /></a>