mcp-omada
Read-only MCP server for TP-Link Omada SDN controllers, enabling querying controller, site, device, and WiFi state.
README
mcp-omada
A Model Context Protocol server for TP-Link Omada SDN controllers - read controller/site/device/WiFi state from an MCP client such as Claude Code.
This is a from-scratch implementation, sibling to mcp-mikrotik: same philosophy (structured API calls only, no generic "run any command" tool, tests against an in-memory fake instead of a real device, 100% test coverage), applied to a very different transport (HTTP + JSON instead of RouterOS's binary API) and a controller with two separate, non-interchangeable authentication mechanisms - see "Verified against real hardware" below.
Status
v0.1: read-only. Five read tools covering controller identity, sites,
devices, device detail, and per-AP WiFi summary. There is no write tool at
all yet - see "Roadmap" below for what v0.2 needs to get right before adding
one, and docs/api-notes.md for a verified write endpoint's gotchas,
documented in advance so v0.2 doesn't have to rediscover them.
Installation
Requires Python >= 3.11.
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Configuration
Configuration comes entirely from environment variables (v0.1 targets a
single controller - there is no multi-controller fleet file, unlike
mcp-mikrotik's devices.yaml).
-
Copy the example:
cp .env.example .env -
Edit
.env(or export the variables another way):Variable Default Meaning OMADA_BASE_URL(required) Controller base URL, e.g. https://192.168.1.2:8043OMADA_OMADAC_ID(auto) Controller ID; auto-discovered via GET /api/infoif unsetOMADA_SITE_ID(auto) Site to operate on; auto-selected if the controller manages exactly one site (legacy auth only - see below) OMADA_USER/OMADA_PASS- Legacy local-user login (preferred - richer field set) OMADA_CLIENT_ID/OMADA_CLIENT_SECRET- Open API client_credentials(reduced field set)OMADA_VERIFY_TLSfalseVerify the controller's TLS certificate OMADA_TIMEOUT15HTTP request timeout, in seconds OMADA_LOG_LEVELINFOLog level for the server process (stderr) Set either
OMADA_USER+OMADA_PASSorOMADA_CLIENT_ID+OMADA_CLIENT_SECRET- not partially, and if both pairs happen to be set, legacy wins (see "Verified against real hardware" for why it's the richer path). The Open API app itself is created in the controller UI: Global View > Settings > Platform Integration > Open API, mode Client, role Viewer.OMADA_VERIFY_TLSdefaults tofalse(with a startup warning) because an OC200 commonly serves a self-signed certificate on its LAN management port - strict verification would refuse to connect out of the box. Set it totrueonce the controller has a certificate you can actually validate.
Running
The server speaks MCP over stdio - it is meant to be launched by an MCP client (e.g. configured as a command in Claude Code), not run as a network service:
mcp-omada
# or, without installing the console script:
python -m mcp_omada.server
There is no HTTP transport in v0.1. If one is added later, it must default
to binding 127.0.0.1 (never 0.0.0.0) and require a bearer token from an
environment variable - see the TODO(http-transport) note at the top of
src/mcp_omada/server.py.
Tools
All read-only in v0.1.
| Tool | Description |
|---|---|
get_controller_info |
Controller identity: version, omadac_id, configured. Unauthenticated - works regardless of auth mode. |
list_sites |
Sites managed by this controller (id + name). Requires legacy auth. |
list_devices |
Devices on a site, normalized to one consistent shape regardless of auth mode - see below. |
get_device_detail |
Richest available detail for one device, by MAC (any common format accepted). |
get_wifi_summary |
Per-AP WiFi summary: parsed 2.4GHz/5GHz channel, client counts per band, radio utilization. Requires legacy auth. |
Normalization
list_devices/get_device_detail return the same field names regardless
of which auth mode is active - a field unavailable in the current mode is
null rather than omitted, so a caller never has to branch on auth mode.
The normalization itself encodes three confirmed real-hardware gotchas
(full detail in docs/api-notes.md):
connected:statusCategory == 1(primary) with fallbackstatus == 14on the legacy path;status == 1on the Open API path - the same field name (status) means something different on each path.uptime_seconds: prefersuptimeLong(legacy-only, already seconds); falls back to parsing theuptimedisplay string (e.g."1h 43m") whenuptimeLongis absent - notably, always, on the Open API path.- WiFi channel:
actualChannelis a string like"11 / 2462MHz"(irregular whitespace) - parsed into{"channel": 11, "freq_mhz": 2462}. On the 5GHz radio,channelis an internal index, not the operator-recognizable channel number -freq_mhzis the reliable value.
Verified against real hardware (OC200 v5.13.30.20)
Everything below was confirmed against a real OC200 running firmware
v5.13.30.20 on 2026-07-12 - not assumed from public docs (which are thin,
and in places silent about exactly these details). Full write-up,
including the verified-but-not-yet-exposed write endpoint and its
gotchas, in docs/api-notes.md.
| Capability | Legacy (/api/v2) |
Open API (/openapi/v1) |
|---|---|---|
Controller identity (/api/info) |
Yes (unauthenticated either way) | Yes (unauthenticated either way) |
| List sites | Yes | Not verified - no Open API sites-list endpoint was exercised; set OMADA_SITE_ID explicitly in this mode |
| List devices | Yes, rich fields | Yes, reduced fields |
| Per-device detail | Yes for AP/EAP devices (/eaps/{MAC}) |
No separate endpoint verified - returns the list row |
Per-radio WiFi detail (wp2g/wp5g) |
Yes | Absent entirely |
connected semantics |
statusCategory==1, fallback status==14 |
status==1 (different meaning, same field name) |
The two auth mechanisms (legacy session + CSRF token vs. Open API access
token) are not interchangeable - a session from one is rejected (empty
response) by the other's endpoints. See src/mcp_omada/client.py's module
docstring and docs/api-notes.md for the full login flows.
Security model
- Read-only by construction, not by a runtime flag. Unlike
mcp-mikrotik's
MIKROTIK_ALLOW_WRITEgate, v0.1 has no write tool registered at all - there is no code path to a write, guarded or otherwise, yet. - Structured HTTP, not shell commands. All controller communication
goes through
httpxwith structured URL path segments, query parameters, and JSON bodies. Nothing in this codebase builds a request by concatenating strings from caller-supplied input, so injection through a MAC address or site ID is ruled out by construction rather than by input filtering. - Input validation on top, for its own sake.
get_device_detail/get_wifi_summary'smacargument is still validated and normalized before use (src/mcp_omada/validation.py), purely to reject garbage input early with a clear error - not as an injection defense (see previous point). - No secrets in output or logs. Password, client secret, CSRF token,
session cookie, and Open API access token are never included in a log
message or an exception's own text - exceptions carry only what the
controller told us (an
errorCode/msg), never the request that was sent.Settings' credential fields are allrepr=False. - TLS verification is explicit, not silently bypassed.
OMADA_VERIFY_TLSdefaults tofalsewith a loud startup warning (not a silent downgrade) - see "Configuration" above for why an OC200 in LAN needs this by default. - Structured errors. All errors raised inside the package derive from
OmadaMCPError(src/mcp_omada/exceptions.py) and are caught at the tool boundary inserver.py, which returns a clean, structured result. Unexpected exceptions are logged server-side and returned to the caller as a generic internal-error message, never as a raw traceback.
Development
pip install -e ".[dev]"
pytest --cov=mcp_omada --cov-report=term-missing --cov-fail-under=100
ruff check .
ruff format --check .
mypy src/mcp_omada
The test suite never talks to a real controller: tests/fakes.py provides
an httpx.MockTransport-backed fake that reproduces both auth flows and
the exact JSON shapes (including the documented gotchas) confirmed against
real hardware, injected via a client_factory parameter on
build_server() - the same dependency-injection shape mcp-mikrotik's
tests/fakes.py uses for its RouterOS connection.
Roadmap
- v0.2 - guarded writes. Following mcp-mikrotik's
guard.pyALLOWLISTpattern exactly (a named, reviewable write operation per entry; a read-only gate checked before anything is touched; explicitconfirm/before-after preview):set_radio_channel(must apply thechannel-as-string +freq-filled-in write shape documented indocs/api-notes.md, or the controller silently discards the change), AP reboot, LED control. - v0.3 - clients/alerts/logs. Read tools for connected client lists, controller alerts, and device/system logs.
- v3-controller compatibility. The pre-v5 controller UI uses a
different login call and session cookie name entirely - recorded as a
historical note (not independently verified) in
docs/api-notes.md, for whoever picks this up.
License
Apache-2.0 - see LICENSE.
Related projects
No official TP-Link MCP server exists as of 2026-07; TP-Link's official
offering is the Omada Open API
(OAuth, /openapi/v1, reduced field set). Community MCP servers we know of:
- MiguelTVMS/tplink-omada-mcp — TypeScript; includes a generic "invoke arbitrary endpoint" tool.
- realtydev/omada-mcp — fork with full CRUD (60+ read/write tools).
- gaspareduard/Omada-mcp — Open-API-based, capability-gated.
How this project differs: read-only by default with no generic endpoint
escape hatch (write tools will land behind an explicit allowlist, mirroring
mcp-mikrotik), and the
legacy /api/v2 path — which Open-API-only clients cannot reach (the
Open API token is rejected there; verified against real hardware) — for the
rich per-radio/per-client data, with every field-shape gotcha documented in
docs/api-notes.md.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。