Skip to main content
Glama

mikrotik-mcp

A Model Context Protocol server that runs RouterOS commands on MikroTik routers over SSH.

Node + TypeScript, no runtime toolchain of its own, and stateless: every tool call re-reads the config and opens and closes its own SSH connection. Nothing is cached, pooled or carried between calls.

It is also self-contained. A profile declares everything it needs, relative paths resolve against the config file, and nothing is inherited from the machine it happens to run on — no ssh-agent, no ~/.ssh/config, no ambient environment. Copy the config directory to a server or a container and it behaves identically.

It speaks JSON-RPC over stdio, so it drops into any MCP harness: Claude Code, Codex, opencode, Cursor, Zed, and anything else that can spawn a command.

Status: the config, policy and SSH layers are covered by tests, including real SSH handshakes against a local test server. It has not yet been run against physical MikroTik hardware — run mikrotik-mcp --test <profile> against your own router first. Please report anything RouterOS does differently.

Use it

npx -y mikrotik-mcp

The published package is plain Node ESM with no runtime toolchain of its own — Node 18+ is enough.

Client

How to add it

Claude Code

claude mcp add mikrotik -- npx -y mikrotik-mcp

Codex

[mcp_servers.mikrotik] in ~/.codex/config.toml with command = "npx", args = ["-y", "mikrotik-mcp"]

opencode

"mikrotik": { "type": "local", "command": ["npx", "-y", "mikrotik-mcp"], "enabled": true } under "mcp" in opencode.json

Anything else

"mikrotik": { "command": "npx", "args": ["-y", "mikrotik-mcp"] } under "mcpServers"

If a profile uses password auth, the client also has to pass the variable holding it — see Getting a secret to the server.

Related MCP server: mikrotik-mcp

Quick start

1. Prepare the router

Create a dedicated RouterOS user rather than reusing admin. A read-only group is the real safety mechanism — the server's own readOnly check is only a guardrail.

/user group add name=mcp-read policy=ssh,read,test
/user add name=mcp-readonly group=mcp-read
/ip service enable ssh

Then give it a key (preferred) or a password:

# key auth — upload your public key first, e.g. with scp
/user ssh-keys import public-key-file=id_mikrotik.pub user=mcp-readonly

# or password auth
/user set mcp-readonly password="..."

2. Write a config file

Start from config.example.json. For a first router, put this at ~/.mikrotik-mcp.json:

{
  "profiles": {
    "core": {
      "host": "10.0.0.1",
      "username": "mcp-readonly+ct",
      "auth": { "type": "key", "path": "./keys/mikrotik" },
      "description": "Core router"
    }
  }
}

./keys/mikrotik resolves against the config file's own directory, not the working directory — so a folder holding the config and its keys can be moved anywhere and still work.

Everything else defaults: port 22, known_hosts verification, read-only, 20 s timeout. The +ct suffix tells RouterOS to drop colour codes and paging.

auth has no default and must be declared. That is deliberate: guessing at an agent or a conventionally-named key would make the config depend on the machine it runs on, which is exactly what this server avoids.

3. Trust the host key

The first connection will be refused, on purpose, because the router is not in known_hosts yet. Either add it the normal way:

ssh-keyscan -H 10.0.0.1 >> ~/.ssh/known_hosts

…or, if you prefer to pin the key in the config, run the command once and copy the fingerprint out of the error message into hostKey.fingerprintSha256. Verify the fingerprint against the router itself (/ip ssh print shows the host key, or read it on the console) before trusting it.

4. Check it

npx -y mikrotik-mcp --check-config
Config search path (highest precedence first):
         C:\work\.mikrotik-mcp.json
  loaded C:\Users\you\.mikrotik-mcp.json

Profiles (1):
  core
    ssh       mcp-readonly+ct@10.0.0.1:22
    auth      agent
    host key  known-hosts
    writes    refused (readOnly)
    from      C:\Users\you\.mikrotik-mcp.json

This exits non-zero if a file is unparseable or no profiles were found, so it works in CI too. It reads the same files the server does, but connects to nothing.

5. Prove it against the router

--check-config never connects to anything. To test the whole path — TCP, host key, credentials, RouterOS — run:

npx -y mikrotik-mcp --test core
Connecting to 'core' (mcp-readonly+ct@10.0.0.1:22) as agent...
  host key   SHA256:9lK…  (accepted by policy 'known-hosts')
  command    /system identity print
  exit code  0
  took       412ms

  name: MikroTik-core

OK.

It runs one harmless read (/system identity print), prints FAILED. with the reason if anything goes wrong, and exits non-zero. Do this before wiring up an agent — it tells you whether a problem is in the config, the network, or the router.

6. Point your agent at it

Ask it to run /system resource print on core. It should call mikrotik_list_profiles first, then mikrotik_exec.

Configuration

defaults applies to every profile in the same file; a profile overrides it field by field.

{
  "$schema": "https://raw.githubusercontent.com/denver6000/mikrotik-mcp/main/schema/mikrotik-mcp.schema.json",
  "defaults": {
    "username": "mcp-readonly+ct",
    "auth": { "type": "agent" },
    "timeoutMs": 20000
  },
  "profiles": {
    "core": {
      "host": "10.0.0.1",
      "description": "Core router, main uplink",
      "tags": ["core", "site-a"]
    },
    "branch-a": {
      "host": "192.168.50.1",
      "port": 2222,
      "tags": ["branch", "site-b"],
      "hostKey": { "policy": "pinned", "fingerprintSha256": "SHA256:abc..." }
    },
    "lab": {
      "host": "10.20.0.1",
      "username": "admin+ct",
      "auth": { "type": "password", "passwordEnv": "MIKROTIK_LAB_PASSWORD" },
      "readOnly": false
    }
  }
}

Adding $schema gives you completion and validation in any editor that speaks JSON Schema.

Profile fields

Field

Default

Meaning

host

Hostname or IP. Required.

port

22

SSH port.

username

"admin"

RouterOS user. Append +ct ("admin+ct") to turn off colour and paging in RouterOS output.

auth

Required (on the profile or in defaults). See Authentication.

hostKey

{ "policy": "known-hosts" }

See Host key verification.

Relative paths in auth.path and hostKey.knownHostsPath resolve against the config file's directory.

readOnly

true

Refuse commands that are not recognised as read-only.

timeoutMs

20000

Connect + command timeout.

algorithms

SSH algorithm overrides for older routers. See below.

description, tags

Free-form, searchable by mikrotik_list_profiles.

Where the config is read from

Every one of these is loaded, highest precedence first. When two files define the same profile name the earlier one wins; profiles with different names merge into one list.

#

Path

1

$MIKROTIK_MCP_CONFIG — one or more explicit paths, separated by : (; on Windows)

2

.mikrotik-mcp.json in the working directory, then each parent directory

3

%APPDATA%\mikrotik-mcp\config.json (Windows)

4

$XDG_CONFIG_HOME/mikrotik-mcp/config.json, else ~/.config/mikrotik-mcp/config.json

5

~/.mikrotik-mcp.json

Which to use:

  • One personal file~/.mikrotik-mcp.json. Fine for most people; start here.

  • Per-repo file.mikrotik-mcp.json committed alongside a network-automation repo, so anyone cloning it gets the right profiles. Safe to commit: it holds no secrets. Because parents are searched too, a file at the root of a monorepo covers every package under it.

  • Both — the repo file wins for names it defines, and your personal file supplies the rest. This is how you override one router's settings locally without editing a shared file.

  • An explicit path$MIKROTIK_MCP_CONFIG for CI, or to keep several environments in separate files: MIKROTIK_MCP_CONFIG=prod.json:staging.json.

Files are re-read on every tool call, so edits take effect without restarting the server or the agent. mikrotik_list_profiles reports which file each profile came from and flags any it could not parse.

Authentication

Every profile declares an auth block. There is no implicit fallback.

{ "type": "key", "path": "./keys/mikrotik" }                     // recommended
{ "type": "key", "path": "./keys/mikrotik", "passphraseEnv": "MTK_KEY_PASS" }
{ "type": "password", "passwordEnv": "MIKROTIK_LAB_PASSWORD" }
{ "type": "agent" }                                              // opt-in; see the caveat below
{ "type": "agent", "socket": "/run/user/1000/ssh-agent" }

Secrets never go in the config file. A profile names the environment variable holding a password or passphrase, and the value is read at connect time — so config files stay safe to commit and share.

Paths expand ~ and $VARS; anything still relative resolves against the config file's directory.

Key formats

ssh2 does not accept every key file OpenSSH can. Verified against ssh2 1.17:

Format

Works

Anything ssh-keygen produces — ed25519, RSA, ECDSA, encrypted or not

yes

RSA PKCS#1 PEM (BEGIN RSA PRIVATE KEY), encrypted or not

yes

EC SEC1 PEM (BEGIN EC PRIVATE KEY)

yes

PKCS#8 PEM (BEGIN PRIVATE KEY) — any key type, including ed25519

no

In practice: generate keys with ssh-keygen -t ed25519 and you will never hit this. If you have a PKCS#8 key, --check-config names the problem and prints the conversion command rather than failing at connect time with "Unsupported key format".

Encrypted keys and the agent

Without an agent, an encrypted key's passphrase has to come from an environment variable — which runs into the subprocess problem below. So a self-contained setup in practice means a dedicated, unencrypted key file.

That is the standard automation answer, and it is safe here only because the blast radius is bounded elsewhere: the key belongs to a RouterOS user in a read-only group, it is used for nothing else, and it sits with chmod 600. On POSIX systems --check-config warns if the file is readable by group or others.

Agent auth remains supported for anyone who prefers it, but it is opt-in and not the default. It ties the profile to the environment the server was launched from: SSH_AUTH_SOCK is not always inherited by a subprocess spawned from a GUI application, and there is no agent at all in a typical container.

Getting a secret to the server

Your MCP client launches this server as a subprocess, so a variable exported in your interactive shell does not necessarily reach it. Either use agent or key auth — which need no variables, and are the reason agent is the default — or declare the variable in the client's own config. The field differs per client:

# Claude Code
claude mcp add mikrotik --env MIKROTIK_LAB_PASSWORD=... -- npx -y mikrotik-mcp
# Codex — ~/.codex/config.toml
[mcp_servers.mikrotik]
command = "npx"
args = ["-y", "mikrotik-mcp"]
env = { MIKROTIK_LAB_PASSWORD = "..." }
// opencode — note "environment", not "env"
{
  "mcp": {
    "mikrotik": {
      "type": "local",
      "command": ["npx", "-y", "mikrotik-mcp"],
      "enabled": true,
      "environment": { "MIKROTIK_LAB_PASSWORD": "..." }
    }
  }
}
// generic mcpServers config
{
  "mcpServers": {
    "mikrotik": {
      "command": "npx",
      "args": ["-y", "mikrotik-mcp"],
      "env": { "MIKROTIK_LAB_PASSWORD": "..." }
    }
  }
}

Note the trade-off: this moves the password out of the router config and into the client config, which is usually not a file you want to commit either. Key or agent auth avoids the problem entirely.

Host key verification

On by default. A router whose key is unknown is refused, and the error prints the fingerprint so you can verify it and then trust it.

{ "policy": "pinned", "fingerprintSha256": "SHA256:abc..." }      // self-contained; preferred
{ "policy": "known-hosts" }                                       // default
{ "policy": "known-hosts", "knownHostsPath": "./known_hosts" }
{ "policy": "insecure-ignore" }                                   // accept anything — lab use only

Under the default known-hosts policy, a known_hosts file beside the config is consulted first, then ~/.ssh/known_hosts as a fallback. Naming knownHostsPath explicitly uses that file only. For a config that must be portable, either ship a known_hosts next to it or pin the fingerprint — the personal-file fallback is a convenience for a workstation, not something to depend on. --check-config prints exactly which files will be consulted.

Setting fingerprintSha256 without a policy implies pinned. Hashed known_hosts entries, wildcards, [host]:port entries and negated patterns are all handled. A host that is known but presents a different key is a hard failure, never a prompt.

Older routers: legacy crypto

Modern SSH clients no longer enable the key exchange and host key algorithms that older RouterOS releases — or routers with strong crypto disabled — may be limited to. If the connection fails during handshake rather than at login, add what that router needs:

{
  "host": "10.0.0.1",
  "algorithms": {
    "kex": { "append": ["diffie-hellman-group14-sha1"] },
    "serverHostKey": { "append": ["ssh-rsa"] }
  }
}

append, prepend and remove are passed through to the SSH layer, as are plain arrays if you want to specify the whole list. Prefer upgrading RouterOS or enabling strong crypto on the router over weakening the client.

Read-only by default

Profiles are readOnly: true unless you say otherwise. Commands are classified before connecting: a command must contain a recognised read action (print, get, find, export, monitor, ping, …) and no recognised write action (set, add, remove, reboot, …). Every command in a ;- or newline-separated chain is checked, so a read cannot smuggle a write along with it. Anything the classifier does not understand is treated as a write and refused.

This is a guardrail against accidents, not a security boundary. A determined agent, or a command phrased in a way the classifier does not model, can get past it. For real enforcement, log in as a RouterOS user whose group grants read access only — the router is the only thing that can enforce that. Set readOnly: false on profiles where writes are intended.

Troubleshooting

Symptom

Cause

No profiles found

The config is not on the search path. Run --check-config — it prints every path it checked.

A path shows FAILED

The file exists but is invalid JSON or breaks the schema; the reason is printed under Problems. Unknown keys are rejected, so check for typos like porrt.

Host key for … is not in …known_hosts

Expected on first contact. Verify the printed fingerprint, then add it to known_hosts or pin it.

HOST KEY CHANGED

The router presented a different key than last time. Do not bypass this until you know why.

Environment variable … is not set

The client did not pass it to the subprocess — see Getting a secret to the server.

Refused: profile … is read-only

Intended. Set readOnly: false on that profile if writes are wanted.

declares no "auth"

Add an auth block to the profile or to defaults; there is no implicit default.

unsupported key format

PKCS#8 PEM. Convert it, or regenerate with ssh-keygen -t ed25519.

encrypted, but no "passphraseEnv"

Either set passphraseEnv, or use an unencrypted dedicated key.

All configured authentication methods failed

Wrong user, wrong key, or the RouterOS group lacks the ssh policy.

Handshake fails before any login prompt

Algorithm mismatch with an older router — see Older routers: legacy crypto.

A command fails but exit code is 0

RouterOS often reports errors in its output rather than through the exit status, so read the text, not just the status.

Output is full of escape codes

Add +ct to the username ("admin+ct").

Command line

Command

Purpose

mikrotik-mcp

Start the MCP server on stdio (what your client runs).

mikrotik-mcp --check-config

Show every config path checked, what loaded, and what each profile resolves to. Connects to nothing. Exits non-zero on a bad or empty config.

mikrotik-mcp --test <profile>

Connect to that router and run one harmless read. Exits non-zero on failure.

mikrotik-mcp --version, --help

As expected.

Tools

mikrotik_list_profiles

Search the configured routers. Returns connection details, which config file each came from, whether it is read-only, and any config problems. Never returns secrets — only the name of the variable holding one.

Input

Type

Notes

query

string, optional

Case-insensitive substring over name, host, description and tags.

mikrotik_exec

Run one RouterOS command on one router and return its output.

Input

Type

Notes

profile

string

Profile name.

command

string

e.g. /system resource print.

timeoutMs

number, optional

Overrides the profile's timeout.

Returns stdout, stderr, the exit code and the host key fingerprint, as text and as structured content. Because each call is a fresh connection with no shell state, commands must use absolute RouterOS paths (/ip address print, not print after a cd).

Develop

No extra toolchain: Node runs the TypeScript sources directly via built-in type stripping, so npm install is the whole setup. Node 22.18+ is required for development; the published package supports Node 18+.

npm install
npm run dev         # run the server on stdio, straight from src/
npm test            # unit tests + a real SSH round-trip against a local ssh2 server
npm run typecheck
npm run schema      # regenerate schema/mikrotik-mcp.schema.json from the zod schema
npm run build       # emit dist/ with tsc
npm run inspect     # open the MCP Inspector against src/

Layout

src/
  index.ts             CLI entry: arg handling + stdio transport
  server.ts            createServer() — registers every tool
  config/
    schema.ts          zod schema for the config file, and the resolved profile type
    paths.ts           where config files are looked for; ~ and $VAR expansion
    load.ts            read, validate, merge, search
    report.ts          the --check-config report
  ssh/
    exec.ts            one connection, one command, then close
    knownHosts.ts      known_hosts parsing and host key verification
    policy.ts          read-only command classification
    keyFile.ts         offline key inspection: format, fingerprint, permissions
    testConnection.ts  the --test <profile> probe
  tools/
    listProfiles.ts    mikrotik_list_profiles
    exec.ts            mikrotik_exec
scripts/
  generate-schema.ts   zod schema -> JSON Schema
test/                  node:test suites; helpers.ts builds a sandboxed config + env

Every entry point takes an injectable LookupEnvironment (env, cwd, home, platform) so tests never touch the real user's config or SSH agent.

Adding a tool

  1. Add src/tools/<name>.ts exporting a register…Tool(server, e) function.

  2. Call it from createServer() in src/server.ts.

  3. Add a case to test/server.test.ts.

Relative imports use explicit .ts extensions so Node can run the sources as-is; tsc rewrites them to .js on build. Keep stdout clean — it is the JSON-RPC channel. Log to stderr only.

Release

version lives in both package.json and SERVER_VERSION in src/server.ts; bump both.

npm test && npm run build
npm publish          # prepublishOnly rebuilds dist/

Only dist/, schema/, README.md and LICENSE are published.

License

MIT

Available Tools

2 tools
mikrotik_execRun a RouterOS command over SSHA
Destructive

Run a single RouterOS command on a configured router over SSH and return its output. Stateless: each call opens a fresh connection and closes it before returning, so there is no session, working directory or shell state carried between calls — send absolute command paths such as '/system resource print'. Profiles are read-only by default, in which case commands that would change the router are refused before connecting. Use mikrotik_list_profiles to find profile names.

ParametersJSON Schema
NameRequiredDescriptionDefault
commandYesRouterOS command, e.g. '/system resource print' or '/ip address print detail'.
profileYesProfile name from mikrotik_list_profiles.
timeoutMsNoOverride the profile's timeout for this call.

Output Schema

ParametersJSON Schema
NameRequiredDescription
okYes
stderrYes
stdoutYes
exitCodeYes
durationMsYes
hostKeyFingerprintNo

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial behavioral context beyond the annotations. It discloses statelessness (fresh connection per call, no session state) and the profile-based read-only enforcement that can refuse mutation commands. This complements the destructiveHint=true annotation by explaining the default safe path. No contradiction with annotations; readOnlyHint=false is consistent because profiles may be read-write.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences, each earning its place: purpose, statelessness, and profile guidance. It is front-loaded with the core action and immediately provides essential behavioral constraints. No redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with an output schema and moderate complexity, the description is sufficiently complete. It covers the command format, statelessness, profile selection, and the refusal behavior for read-only profiles. Minor gaps (e.g., error handling on SSH failure) are not critical given the output schema likely documents return values.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds value by giving a concrete command example ('/system resource print') and emphasizing absolute paths, which clarifies the 'command' parameter. It also ties 'profile' to mikrotik_list_profiles, enriching the schema's minimal description. timeoutMs is adequately covered by the schema; no additional description needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Run a single RouterOS command on a configured router over SSH and return its output.' The verb 'run' and resource 'RouterOS command' are specific, and it differentiates from the only sibling (mikrotik_list_profiles) by focusing on execution rather than listing. The statelessness note further clarifies scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly directs when to use this tool vs the alternative: 'Use mikrotik_list_profiles to find profile names.' It also provides critical usage guidance—'send absolute command paths'—and describes the conditional refusal behavior for read-only profiles, which tells the agent when a command might be rejected.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

mikrotik_list_profilesList MikroTik router profilesA
Read-only

Search the configured MikroTik router profiles. Config files are re-read on every call. Returns connection details and whether each profile is read-only. Never returns secrets — passwords and passphrases live in environment variables, and only the variable name is shown. Call this first to discover the profile name that mikrotik_exec needs.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoCase-insensitive substring matched against name, host, description and tags. Omit to list all.

Output Schema

ParametersJSON Schema
NameRequiredDescription
issuesYes
sourcesYesConfig files that were loaded, in precedence order.
profilesYes
searchedYesEvery path consulted, whether or not it exists.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint annotation, the description adds important behavioral detail: config files are re-read on every call, profiles can be read-only, and secrets are never returned—only environment variable names. This materially changes how an agent should interpret results and is exactly the kind of context annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence earns its place: scope, freshness, return contents, security behavior, and usage sequence are each covered without repetition. The most actionable instruction is front-loaded at the end but clearly tied to the tool's role.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool with one optional parameter and an output schema, the description is complete. It tells the agent what is returned, what is deliberately hidden, how to search, and how this tool connects to its sibling.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already documents the optional query parameter completely with 100% coverage, including case-insensitive substring matching and omission behavior. The description does not add further parameter-level detail, but the schema handles the burden, so the baseline score applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Search') and resource ('configured MikroTik router profiles'), and immediately orients the agent by naming the sibling tool it feeds ('the profile name that mikrotik_exec needs'). This makes it easy to distinguish from mikrotik_exec without inspecting schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells the agent to 'Call this first' and explains the purpose: discover the profile name required by mikrotik_exec. This is clear when-to-use guidance with a named alternative, which is sufficient given the single sibling tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedmikrotik_exec
    • First observedmikrotik_list_profiles

TDQS

A4.6/5.0

Scored across 2 tools

Disambiguation5/5

mikrotik_list_profiles and mikrotik_exec have completely distinct purposes: one discovers connection profiles, the other executes RouterOS commands. The descriptions even cross-reference each other, making misselection very unlikely.

Naming Consistency4/5

Both tools share a consistent mikrotik_ prefix and use snake_case, which is good. However, list_profiles follows a verb_noun pattern while exec is a bare verb without an object, a minor style inconsistency.

Tool Count4/5

At two tools, the count is slightly below the typical 3-15 range, but the split is sensible: one tool for discovery and one for execution. Each tool earns its place for the server's narrow purpose.

Completeness5/5

For a server whose stated purpose is running RouterOS commands on configured profiles, the surface is complete: discover profiles, then execute commands. There are no obvious missing operations, and the stateless/read-only behavior is handled clearly.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables management of MikroTik routers running RouterOS 6 and 7 via SSH, Telnet, or API with automatic command adaptation. Provides over 46 MCP tools for device management, firewall, DHCP, VPN, configuration profiles, and more.
    3
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server that sends CLI commands to MikroTik RouterOS via SSH. It allows executing any RouterOS CLI command and getting text output back.
    1
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables agents to access network device management interfaces via SSH, keeping credentials on the server side. Supports both interactive terminal sessions and command execution on devices like Huawei VRP, MikroTik, and OpenWrt.
    1
    -