Skip to main content

podkop-mcp

CI PyPI Python versions License: MIT

MCP server for managing Podkop and Forkop on OpenWrt over SSH.

Stop editing UCI by hand, remembering the uci add_list trap and passing base64-wrapped scripts to PowerShell. Ask your MCP client to add domains, list sections, inspect live connections, reload the service and roll back a bad change — with dry-run, backups and redaction built in.

Status: alpha. Live read-only tested against Forkop 1.0.5 and Forkop 2.0.0 (OpenWrt 25.12.6). Write/apply paths were verified in disposable OpenWrt Docker containers with real Forkop 2.0.0 and Podkop 0.7.24. Backend schema is revalidated every PODKOP_MCP_PROFILE_TTL seconds (default 30).

Why

Managing Podkop/Forkop by hand is error-prone:

  • Forkop 1.x stores domains in one space-separated string (section.domain). One uci add_list turns it into an exact-match domain list and subdomains silently stop being routed.
  • Forkop 2.x uses a multi-line section.destinations text with # comments.
  • Podkop uses a real UCI list (section.user_domains).
  • Forkop reloads asynchronously; a failed reload must be detected and rolled back.
  • Subscription URLs and proxy strings are secrets and end up in logs.

Unlike MCP servers that run on the router itself, podkop-mcp runs on the administrator's machine, talks to OpenWrt over SSH and understands Podkop/Forkop semantics: dry-run, backups, rollback and secret redaction. All of that is hidden behind typed MCP tools.

Features

  • SSH transport with a key from MCP parameters: key file, inline base64/PEM, or ~/.ssh/config alias. Host key verification, keepalive, timeouts.
  • Backend detection: Forkop 1.x, Forkop 2.x, Podkop; capabilities derived from the installed CLI.
  • Full UCI access via ubus call uci get JSON and uci for writes. Any option can be read or changed; the package is restricted to forkop/podkop.
  • Domain operations that preserve the backend format: add/remove/replace, search coverage, suggest uncovered hosts from live connections, check FakeIP.
  • Sections: list, inspect, create, update, enable/disable, delete with cascade of child subscriptions/URLTest items.
  • Subscriptions and proxy groups: list, add, delete, update, test latency, select a proxy (Forkop 2.x).
  • Monitoring: service/system status, health checks, logs, live connections, traffic rates, proxy delays.
  • Safety: mutating tools are dry-run by default; apply=true creates a router-side backup, commits UCI, reloads and verifies. On failure the backup is restored automatically. PODKOP_MCP_READ_ONLY=1 blocks all writes.
  • Secrets redacted by default: subscription URLs, proxy_string, keys, tokens. Raw export requires reveal_secrets=true.
  • Bilingual docs (English / Russian) and CI.

Support matrix

Capability Forkop 1.x Forkop 2.x Podkop
UCI package forkop forkop podkop
Domain option domain (string) destinations (multiline) user_domains (list)
Community lists yes yes yes
Rule sets no yes yes
Clash API action CLI HTTP CLI action CLI
Proxy runtime select — yes —
Read-only monitoring yes yes yes

Install

Requirements:

  • Python 3.10+ (CI tests 3.10 through 3.14)
  • OpenWrt router with Forkop or Podkop installed
  • SSH key access to the router

Install from PyPI:

pip install podkop-mcp
# or run without installing
uvx podkop-mcp

Install from source:

git clone https://github.com/mikel38487/podkop-mcp.git
cd podkop-mcp
python -m venv .venv
. .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -e .

Configure your MCP client (Claude Desktop, Cursor, DSH, etc.):

{
  "mcpServers": {
    "podkop": {
      "command": "podkop-mcp",
      "env": {
        "PODKOP_MCP_SSH_HOST": "192.168.1.1",
        "PODKOP_MCP_SSH_USER": "root",
        "PODKOP_MCP_SSH_KEY_PATH": "C:/Users/<user>/.ssh/router_key",
        "PODKOP_MCP_KNOWN_HOSTS": "C:/Users/<user>/.ssh/known_hosts",
        "PODKOP_MCP_HOST_KEY_POLICY": "reject"
      }
    }
  }
}

Or use an existing ~/.ssh/config alias:

"env": {
  "PODKOP_MCP_SSH_CONFIG_HOST": "router-home",
  "PODKOP_MCP_SSH_KEY_PATH": "C:/Users/<user>/.ssh/router_key",
  "PODKOP_MCP_HOST_KEY_POLICY": "reject"
}

Host key policy defaults to reject. Add the router key to known_hosts first (for example with a normal ssh login). accept-new is a one-time TOFU convenience for a new router; it does not protect against a MITM on the first connection.

Ask the model:

  • “Run podkop_doctor and show the backend version.”
  • “Add example.com and cdn.example.com to main; show me the diff first.”
  • “List sections with domain counts.”
  • “Which hosts in live connections are not covered by any section?”
  • “Show traffic for the last 2 seconds and the top clients.”
  • “Disable the youtube section and reload.”

Documentation

How write operations work

dry-run (default)
  → read config, compute normalized diff, show uci commands

apply=true
  → cp /etc/config/<pkg> /etc/podkop-mcp/backups/<ts>-<reason>.uci
  → uci set/add_list/delete (package restricted to forkop/podkop)
  → uci commit
  → /etc/init.d/<pkg> reload
  → poll get_status up to ~35 s
  → on failure: restore backup, commit, reload
  → return diff, backup id, health summary

No command is executed through a shell string built from user input; values are shell-quoted with shlex and the script is sent to sh via stdin. Paths are validated; no base64 applet is required on the router.

Tool overview

Group Tools
System podkop_doctor, podkop_status, podkop_system_info, podkop_health, podkop_logs, podkop_cli, podkop_core_features, podkop_import_check, podkop_installer_status, podkop_installer_log
Config podkop_config_get, podkop_config_export, podkop_option_get/set/delete, podkop_backup_create/list/restore
Sections podkop_sections_list, podkop_section_get/create/update/delete/enable, podkop_tunnel_create, podkop_child_create
Domains podkop_domains_list/add/remove/replace/find/suggest, podkop_domain_check
Lists podkop_community_lists_get/update
Connections podkop_connections_config, podkop_subscription_add/delete/update, podkop_proxies_list, podkop_proxy_select/test
Monitoring podkop_monitor_snapshot, podkop_connections_live, podkop_traffic
Service podkop_service_action, podkop_lists_update

Full reference: docs/tools.md.

Configuration reference

Variable Default Meaning
PODKOP_MCP_SSH_HOST — Router address
PODKOP_MCP_SSH_PORT 22 SSH port
PODKOP_MCP_SSH_USER root SSH user
PODKOP_MCP_SSH_KEY_PATH — Private key path
PODKOP_MCP_SSH_KEY — Inline private key (PEM)
PODKOP_MCP_SSH_KEY_B64 — Inline key, base64
PODKOP_MCP_SSH_KEY_PASSPHRASE — Key passphrase
PODKOP_MCP_SSH_PASSWORD — Password instead of a key (secret: pass it through the environment, never commit it)
PODKOP_MCP_SSH_CONFIG_HOST — Alias from ~/.ssh/config
PODKOP_MCP_KNOWN_HOSTS ~/.ssh/known_hosts Host key file
PODKOP_MCP_HOST_KEY_POLICY reject reject, accept-new, ignore
PODKOP_MCP_CONNECT_TIMEOUT 8 SSH connect timeout, seconds
PODKOP_MCP_COMMAND_TIMEOUT 45 Command timeout, seconds
PODKOP_MCP_BACKEND auto auto, forkop or podkop; the requested CLI must be installed
PODKOP_MCP_READ_ONLY 0 Block all writes
PODKOP_MCP_BACKUP_DIR /etc/podkop-mcp/backups Router-side backups
PODKOP_MCP_BACKUP_KEEP 30 Number of backups to keep
PODKOP_MCP_MAX_OUTPUT_BYTES 200000 Cap on captured command output, bytes
PODKOP_MCP_LOG_LEVEL INFO Server log level on stderr (DEBUG, INFO, WARNING, ERROR, CRITICAL)
PODKOP_MCP_PROFILE_TTL 30 Seconds before the backend profile is revalidated against the router
PODKOP_MCP_TARGETS — JSON with multiple routers
PODKOP_MCP_CONFIG — Path to a JSON file with multiple routers; takes precedence over PODKOP_MCP_TARGETS and the single-router variables
PODKOP_MCP_TARGET_NAME router Name of the single router defined by PODKOP_MCP_SSH_HOST / PODKOP_MCP_SSH_CONFIG_HOST; ignored when a JSON target list is used

To manage several routers, put them in a JSON file and point PODKOP_MCP_CONFIG at it:

{
  "targets": {
    "home": {"host": "192.0.2.10"},
    "office": {"host": "192.0.2.11", "port": 2222}
  },
  "default_target": "home"
}
"env": {
  "PODKOP_MCP_CONFIG": "~/.config/podkop-mcp/targets.json"
}

The default_target router (or the first one when it is omitted) is used when a tool call does not pass target; pass "target": "office" to use the other router. PODKOP_MCP_TARGET_NAME does not select a target from this file — it only names the single router defined by PODKOP_MCP_SSH_HOST / PODKOP_MCP_SSH_CONFIG_HOST.

Testing

python -m pytest -q                 # unit + fake router + live/docker skipped
python scripts/smoke_mcp.py         # MCP stdio handshake
PODKOP_MCP_LIVE=1 python -m pytest -q -m live   # read-only, real router

For the real SSH/UCI/apply path there is an OpenWrt Docker container: real openwrt/rootfs userland, real ubus/rpcd/dropbear/uci, with stubs for the Forkop CLI/init (no sing-box/nft).

.\scripts\openwrt-docker.ps1
. .\.tmp\openwrt-env.ps1
.\.venv\Scripts\python.exe -m pytest -q -m docker_ssh
.\scripts\openwrt-docker.ps1 -Stop

This is a container, not a VM: it shares the host kernel. Full VM instructions and limitations are in docs/testing.md. Live tests never write to the production router; write operations are tested in the Docker container and through the fake transport.

Limitations

  • MCP transport is stdio; SSE/HTTP is out of scope.
  • Reordering sections is not exposed yet (UCI order is file order); use export/edit
    • podkop_config_export if needed.
  • Podkop subscription handling is best-effort; the active target is Forkop.
  • Forkop 1.x does not support active per-proxy latency tests; use URLTest history.
  • No automatic Forkop/Podkop upgrade.

License

MIT. Forkop and Podkop are separate projects under their own licenses.

Not affiliated

This is an independent, unofficial project. It is not affiliated with, endorsed by or maintained by the Podkop, Forkop or OpenWrt projects.

Metadata

Release files for podkop-mcp 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for podkop-mcp 0.2.2
File Size Uploaded Provenance Actions
podkop_mcp-0.2.2.tar.gz 168.2 kB With provenance Details for podkop_mcp-0.2.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for podkop-mcp 0.2.2
File Interpreter ABI Platform Actions
podkop_mcp-0.2.2-py3-none-any.whl Python 3 none any Details for podkop_mcp-0.2.2-py3-none-any.whl

Total release size: 248.5 kB

Release files / podkop_mcp-0.2.2.tar.gz

Download URL podkop_mcp-0.2.2.tar.gz
Size 168.2 kB
Tags Source
SHA-256 checksum
How to use checksums
65fef3611dbbd3edc643af467f4ac1c259e841715e594c949229ce7dac574a60
BLAKE2b-256 checksum
How to use checksums
e84756965c116a63b047f60d70d2bc80be68fcd65b32eed9b6620b844d472516
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / podkop_mcp-0.2.2-py3-none-any.whl

Download URL podkop_mcp-0.2.2-py3-none-any.whl
Size 80.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ecd06767a1e4eb95226bc77ceafdb7944b4ec0d0e23f2d5685d0dd4ddb53b0e3
BLAKE2b-256 checksum
How to use checksums
7219d7963abd235f12b643599d1d6fe6e09ead405bd3f6a1a0baf052214c4ab9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Security for podkop-mcp 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for podkop_mcp-0.2.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page