
mikrus-mcp
ā 10from paulomac1000
MCP (Model Context Protocol) server for managing VPS servers via the mikr.us API and remote Linux servers over SSH. Built in Python, runs anywhere ā locally, in Docker, or as a Claude Desktop integration.
Mikrus MCP Server
MCP (Model Context Protocol) server for managing VPS servers via the mikr.us API and remote Linux servers over SSH. Built in Python, runs anywhere ā locally, in Docker, or as a Claude Desktop integration.
All tools follow the MCP Server Standards for response format, testing, and documentation.
Contents
- Requirements
- Quick Start
- Available Tools
- Tool Response Format
- Multi-server Configuration
- Security Considerations
- Claude Desktop Configuration
- Development
- Architecture
- Troubleshooting
- Notes
- License
Available Tools
Discovery
| Tool | Description |
|---|---|
list_configured_servers | List all configured servers, their types, and connection status. Use this first to discover available servers. |
describe_mikrus_capabilities | Returns the full tool catalog with capability manifests, supported transports, and schema version. Zero I/O, instant. |
mikr.us API tools (mikrus servers only)
| Tool | API endpoint | Description |
|---|---|---|
get_server_info | /info | Server info: ID, RAM, disk, expiration date, PRO plan status (cache=60s) |
list_servers | /serwery | List all servers associated with the account (cache=60s) |
get_server_stats | /stats | Usage stats: RAM, disk, uptime, load avg, processes (cache=60s) |
execute_command | /exec | Execute a shell command on the server (60s API limit) |
restart_server | /restart | Restart the VPS server |
get_logs | /logs | Last 10 task log entries from the panel |
get_log_by_id | /logs/ID | Details of a specific log entry by ID |
boost_server | /amfetamina | Temporary resource boost (+512MB RAM for 30 min, free) |
get_db_info | /db | Database access credentials (MySQL/PostgreSQL, cache=60s) |
get_ports | /porty | Assigned TCP/UDP ports (cache=60s) |
get_cloud | /cloud | Cloud services and statistics assigned to the account |
assign_domain | /domain | Assign a domain to a port (use - for auto-generated subdomain) |
System management tools (mikrus + SSH servers)
| Tool | Description |
|---|---|
read_file | Read a text file from the server (up to 200 lines) |
write_file | Write text content to a file (base64-safe transfer) |
manage_service | Manage systemd services: status, start, stop, restart, enable, disable |
analyze_disk | Disk usage overview (df -h + top-20 largest directories) |
check_port | Check if a TCP port is listening and what process uses it |
manage_process | List top processes by memory or kill a process by PID/name |
update_system | Run system updates (apt update && apt upgrade -y) |
list_directory | List directory contents (ls -la) |
tail_file | Read last N lines from a text file (max 500) |
search_in_files | Search for a pattern in files under a path (grep -r) |
get_memory_info | Show memory usage (free -h) |
get_network_info | Show network interfaces and listening TCP ports |
get_process_tree | Show running processes in a tree view (ps auxf) |
Docker tools (mikrus + SSH servers, requires Docker access)
| Tool | Description |
|---|---|
list_docker_containers | List all Docker containers with status and image |
get_docker_logs | Fetch recent logs from a Docker container |
get_docker_stats | Show resource usage stats for Docker containers |
Journal tools (mikrus + SSH servers, may require sudo_password)
| Tool | Description |
|---|---|
get_journal_logs | Fetch systemd journal logs for a specific service unit |
find_system_errors | Find error-level journal entries from the last N hours |
search_journal_logs | Search journal logs for a keyword or phrase |
Note about journal tools: If the remote user is not in the
systemd-journaloradmgroup, journal commands will fail. To fix this, addsudo_passwordto the SSH server configuration. Ifsudo_passwordis omitted, the tool will still work but returns a helpful hint for the user when privileges are insufficient.
All system tools accept an optional server parameter to target a specific configured
server (e.g. server=myssh). If omitted, the default server is used.
Tool Response Format
All 33 tools return a consistent JSON structure. Every response includes a _meta envelope with a unique request_id:
{"success": true, "data": {"server_id": "emil359", "param_ram": "1024"}, "_meta": {"request_id": "a1b2c3d4-...", "duration_ms": 42, "cached": false, "retry_safe": false}}On failure:
{"success": false, "error": "Server 'unknown' is not a mikrus server", "_meta": {"request_id": "e5f6g7h8-..."}}The success field is always a boolean. Successful responses contain a data key with the tool-specific result. Error responses contain an error key with a human-readable message. No tool raises unhandled exceptions ā errors are always returned as structured JSON.
Tools are annotated with risk-level prefixes in their descriptions:
| Prefix | Meaning | Examples |
|---|---|---|
[DANGEROUS] | Executes arbitrary shell commands | execute_command |
[WRITE] | Modifies server state or files | write_file, update_system |
[DESTRUCTIVE] | Kills processes, restarts services | manage_service (stop/restart), manage_process (kill) |
[SENSITIVE] | Returns credentials or tokens | get_db_info, get_journal_logs |
| (none) | Read-only, no side effects | get_server_info, list_servers, describe_mikrus_capabilities |
AI agents use these prefixes to decide whether to request user confirmation before invoking a tool. Tools are additionally gated behind ENABLE_WRITE_OPERATIONS ā a server-level authorization flag that must be explicitly enabled for any write, destructive, or command-execution tool to perform I/O.
Security Considerations
This MCP server grants full system access to configured servers. Treat it as a privileged remote administration tool.
SSE Transport
- By default, SSE listens on
127.0.0.1only. - Binding to
0.0.0.0requiresMCP_UNSAFE_PUBLIC_ACCESS_CONFIRMED=1. - Never expose SSE to the internet without authentication. Use a reverse proxy (nginx, Traefik) with TLS and basic auth if remote access is needed.
SSH Security
verify_host_keydefaults tofalsefor ease of use. In production, set it totrueand provide aknown_hosts_fileto prevent MITM attacks.sudo_passwordis fed viaasyncssh.create_process+ stdin, never via shell string interpolation, so it won't appear inps aux.- SSH private keys mounted into Docker must have permissions
600or400.
Input Validation
- All file paths are validated against traversal (
..) and forbidden paths (e.g./etc/shadow). - Dangerous commands (
rm -rf /,mkfs,dd if=) are blocked before execution. - Shell metacharacters (
;,|,$,`) in commands are rejected before any patterns are checked. - Service names, container names, ports, and domains are validated with strict regex patterns.
- File writes outside
/home,/var/www,/opt,/tmp,/srv,/var/logtrigger a warning.
Write Guard
- Write, destructive, and command-execution tools are gated behind
ENABLE_WRITE_OPERATIONS=1(default: disabled). - When disabled, these tools return a structured error before any I/O.
- Read-only actions (
manage_service status,manage_process list) bypass the write guard.
Credentials
- API keys and passwords are loaded from environment variables or
.env(gitignored). - Never commit credentials to version control.
Development
Setup
pip install -e ".[dev]"Run tests
# Unit tests (fast, no credentials needed) ā 310 tests
pytest tests/unit/ -q --cov=mikrus_mcp --cov-report=term
# Smoke tests ā API connectivity and response format
pytest tests/smoke/ -q
# Integration tests ā real mikr.us API
pytest tests/integration/ -q
# E2E tests ā full pipeline workflows
pytest tests/e2e/ -qAll tests use respx (HTTP mocking) or unittest.mock (SSH mocking). No real network calls are made in unit or e2e tests. Smoke and integration tests require a valid MIKRUS_API_KEY and skip gracefully otherwise.
REST Bridge (optional testing utility)
Set MCP_REST_PORT to expose an HTTP bridge that turns tool calls into REST endpoints:
MCP_PORT=8300 MCP_REST_PORT=8301 mikrus-mcp
# GET http://127.0.0.1:8301/health
# GET http://127.0.0.1:8301/tools
# POST http://127.0.0.1:8301/tools/get_server_info {"params": {}}This is a development utility for smoke testing and debugging. It is separate from the MCP SSE transport and runs on its own port. All endpoints have /api/ prefixed mirrors (GET /api/health, GET /api/tools, POST /api/tools/{name}). Tool manifest endpoints are also available at GET /tools/{name}/manifest and GET /api/tools/{name}/manifest.
Lint & type check
ruff check src/ tests/
ruff format --check src/ tests/
mypy src/
bandit -r src/In Docker
docker compose run --rm testArchitecture
src/mikrus_mcp/
āāā __init__.py # Package version
āāā __main__.py # python -m mikrus_mcp entry point
āāā config.py # Environment variable loader (dotenv) ā single-server, multi-server, SSH-only
āāā constants.py # Backward-compat re-export for tools/constants.py
āāā validators.py # Centralized input validation (path, port, service, container, domain)
āāā sanitizer.py # Log sanitization (redacts API keys, IPs, passwords, MACs)
āāā client.py # Async HTTP client (httpx) + SSH client (asyncssh)
āāā server.py # MCP server with 33 tools, stdio + SSE transport, partial startup
āāā rest_bridge.py # Optional REST bridge for smoke/e2e testing (on MCP_REST_PORT)
āāā tools/
āāā __init__.py
āāā constants.py # SSOT defaults, tool manifests, validation limits, write guard
āāā response.py # _success_response / _error_response helpers
āāā capabilities.py # describe_mikrus_capabilities introspection tool (L3+)
āāā mikrus_api.py # 12 mikr.us API tools + internal functions + registration
āāā system.py # 14 system management tools (exec, file, service, disk, etc.)
āāā container_journal.py # 6 Docker + journalctl tools + registration
āāā discovery.py # Server listing tool + registration
tests/
āāā conftest.py # Root: environment loading
āāā fixtures.py # Mock data constants
āāā _env_loader.py # Shared .env loader for conftest files
ā
āāā unit/ # Unit tests ā zero I/O, fully mocked
ā āāā conftest.py
ā āāā test_client.py
ā āāā test_config.py
ā āāā test_server_tools.py
ā āāā test_ssh_client.py
ā āāā test_multi_server.py
ā āāā test_rest_bridge.py
ā āāā test_sanitizer.py
ā āāā test_tool_registration.py
ā āāā test_capabilities.py
ā āāā test_validators.py
ā
āāā smoke/ # Smoke tests ā direct API calls (skipif no creds)
ā āāā conftest.py
ā āāā test_connectivity.py
ā āāā test_critical_tools.py
ā āāā test_response_format.py
ā
āāā integration/ # Integration tests ā real API calls
ā āāā conftest.py
ā āāā mcp_wrapper.py
ā āāā test_real_tools.py
ā
āāā e2e/ # E2E tests ā full pipeline workflows
āāā conftest.py
āāā test_server_api.pyNotes
- 33 tools total: 2 discovery + 12 mikr.us API + 19 system management tools.
- All tools return
{"success": True/False, ...}JSON format for consistent error handling. - All write operations are protected by input validation ā no shell injection possible.
- Errors are logged to
stderr, in compliance with the MCP specification. - The mikr.us
/execendpoint has a 65-second client timeout (API limit is 60s). /stats,/info,/serwery,/db, and/portyhave a 60-second API-side cache.- Tool descriptions are optimized for LLM agents ā each explains when and why to use the tool.
docker run --rm \
--env-file .env \
ghcr.io/paulomac1000/mikrus-mcp:latestBefore it works, you'll need: MIKRUS_API_KEYMIKRUS_SERVER_NAME
Requirements
- Python 3.14+ (for local use) or Docker
- A mikr.us account with an API key or any SSH-accessible Linux server
- Your server identifier (e.g.
your_srv) or SSH host
Quick Start
1. Configure environment
cp .env.example .envEdit .env with your credentials. Three modes are supported:
A ā Single-server (mikr.us API only):
MIKRUS_API_KEY=your_api_key_here
MIKRUS_SERVER_NAME=your_server_name_hereB ā Multi-server JSON (mikr.us + SSH):
MCP_SERVERS={"your_srv": {"type": "mikrus", "key": "xxx", "srv": "your_srv"}, "myssh": {"type": "ssh", "host": "srvXX.mikr.us", "port": 22, "user": "root", "password": "secret"}}
MCP_DEFAULT_SERVER=your_srvC ā SSH-only (no mikr.us account needed):
MCP_SERVERS={"prod": {"type": "ssh", "host": "prod.example.com", "user": "admin", "ssh_key": "/home/user/.ssh/id_ed25519"}}Variable name aliases (MCP_* and MIKRUS_* are interchangeable):
MCP_SERVERSāāMIKRUS_SERVERSMCP_DEFAULT_SERVERāāMIKRUS_DEFAULT_SERVER
IMPORTANT: The .env file contains your API key / passwords. It is gitignored and must never be committed.
2. Run with Docker
Two approaches are available:
Pre-built image (recommended ā no local build needed)
Pulls the published image from GitHub Container Registry:
docker run --rm \
--env-file .env \
ghcr.io/paulomac1000/mikrus-mcp:latestOr pass credentials directly:
docker run --rm \
-e MIKRUS_API_KEY=your_key \
-e MIKRUS_SERVER_NAME=your_server \
ghcr.io/paulomac1000/mikrus-mcp:latestLocal build (use when modifying the code or for development)
Build the image from source and run it:
# with docker compose (reads .env automatically)
docker compose up mikrus-mcp
# with docker compose and SSH key mount
docker compose run --rm \
-v ~/.ssh/id_ed25519:/home/appuser/.ssh/id_ed25519:ro \
mikrus-mcp
# or with plain docker build
docker build -t mikrus-mcp .
docker run --rm --env-file .env mikrus-mcpThe server communicates over stdio by default. Set MCP_PORT to enable SSE transport (see Security Considerations).
Using SSH keys in Docker: Mount your private key as a read-only volume:
docker run --rm \ --env-file .env \ -v ~/.ssh/id_ed25519:/home/appuser/.ssh/id_ed25519:ro \ ghcr.io/paulomac1000/mikrus-mcp:latestSSH keys must have permissions
600or400and be readable by theappuseruser inside the container (UID 1000). If your host user has a different UID, adjust ownership withchown 1000:1000 ~/.ssh/id_ed25519or use a less restrictive mode. Certificates can be mounted the same way.
3. Run locally (Python 3.14+)
pip install -e ".[dev]"
mikrus-mcpMulti-server Configuration
You can manage multiple servers simultaneously by using the MCP_SERVERS JSON.
You can mix mikrus and SSH servers, or use SSH servers exclusively (no mikr.us account required).
{
"your_srv": {"type": "mikrus", "key": "xxx", "srv": "your_srv"},
"myssh": {"type": "ssh", "host": "192.168.1.10", "port": 22, "user": "root", "password": "secret", "sudo_password": "secret"}
}SSH server fields
| Field | Required | Description |
|---|---|---|
type | Yes | Must be "ssh" |
host | Yes | SSH hostname or IP |
port | No | SSH port (default: 22) |
user | No | SSH username (default: root) |
password | No | SSH password (if not using key auth) |
ssh_key | No | Path to SSH private key file |
ssh_cert | No | Path to SSH certificate signed by CA |
sudo_password | No | Password for sudo -S (needed for journal tools if user lacks group privileges) |
timeout | No | SSH timeout in seconds (default: 30) |
verify_host_key | No | Verify SSH host key (default: false) |
known_hosts_file | No | Path to known_hosts file (used when verify_host_key=true) |
mikr.us API server fields
| Field | Required | Description |
|---|---|---|
type | Yes | Must be "mikrus" |
key | Yes | API key from the mikr.us panel |
srv | Yes | Server identifier (e.g. srv123) |
Usage
All tools accept an optional server parameter to target a specific configured server.
If MCP_DEFAULT_SERVER is not set, the first server in the JSON object is used as the default.
Claude Desktop Configuration
Add the following to your claude_desktop_config.json. Use absolute paths ā Claude Desktop may run from a different working directory.
With .env file (Docker):
{
"mcpServers": {
"mikrus": {
"command": "docker",
"args": [
"run",
"--rm",
"--env-file",
"/absolute/path/to/mikrus-mcp/.env",
"ghcr.io/paulomac1000/mikrus-mcp:latest"
]
}
}
}With inline credentials (Docker, no .env file):
{
"mcpServers": {
"mikrus": {
"command": "docker",
"args": [
"run",
"--rm",
"-e", "MIKRUS_API_KEY=your_key",
"-e", "MIKRUS_SERVER_NAME=your_server",
"ghcr.io/paulomac1000/mikrus-mcp:latest"
]
}
}
}Native Python (no Docker):
{
"mcpServers": {
"mikrus": {
"command": "mikrus-mcp",
"env": {
"MIKRUS_API_KEY": "your_key",
"MIKRUS_SERVER_NAME": "your_server"
}
}
}
}Multi-server with SSH key mount:
{
"mcpServers": {
"mikrus": {
"command": "docker",
"args": [
"run",
"--rm",
"-v", "/home/user/.ssh/id_ed25519:/home/appuser/.ssh/id_ed25519:ro",
"-e", "MCP_SERVERS={\"prod\":{\"type\":\"ssh\",\"host\":\"10.0.0.5\",\"user\":\"admin\",\"ssh_key\":\"/home/appuser/.ssh/id_ed25519\"}}",
"-e", "MCP_DEFAULT_SERVER=prod",
"ghcr.io/paulomac1000/mikrus-mcp:latest"
]
}
}
}After restarting Claude Desktop, the 33 mikrus tools will be available for use.
Troubleshooting
| Issue | Solution |
|---|---|
No servers available | Check that MIKRUS_API_KEY + MIKRUS_SERVER_NAME or MCP_SERVERS JSON is set correctly. |
SSH key not found | Verify the path in ssh_key is absolute and accessible from the container (mount it as a volume). |
Journal access denied | Add sudo_password to the SSH server config, or add the user to the systemd-journal / adm group. |
Port must be 1-65535 | The MCP_PORT env var must be a valid TCP port number. |
Refusing to start on 0.0.0.0 | Set MCP_HOST=127.0.0.1 or set MCP_UNSAFE_PUBLIC_ACCESS_CONFIRMED=1 (not recommended). |
| Partial startup warning | One or more servers failed to connect. Check logs ā other servers are still available. |
Licensed under MITā you can use, modify, and redistribute it under that license's terms.
License
MIT ā see LICENSE for details.