☰
DDNS MCP 服务完全指南:用 stdio 与 Streamable HTTP 把 DDNS 状态查询与同步接入 MCP 客户端
2026/10/13 7:33:01 网站建设 项目流程
  • 网络
  • CLI

【免费下载链接】DDNS

🌐自动更新域名解析到本机IP,支持dnspod,阿里DNS,CloudFlare,华为云,DNSCOM...

项目地址:https://gitcode.com/gh_mirrors/dd/DDNS
点击查看免费下载

DDNS 在 Python 标准库之上内置了一个零第三方依赖的 MCP(Model Context Protocol)服务,让 GitHub Copilot 等 MCP 客户端可以读取本地缓存的 DDNS 状态,或在人工确认后触发一次完整的 DNS 同步。本文基于 MCP 服务文档 展开,完整覆盖 stdio 与 Streamable HTTP 两种传输的启动命令、监听参数、token 认证、CORS origin 白名单与协议限制,并结合 ddns/mcp.py 与 ddns/mcp_http.py 的源码说明工具分发、凭据脱敏与取消机制的实现细节。读完后你可以把 DDNS 接入任意支持 MCP 的 Agent 客户端,并理解服务端如何保证不把 DNS 服务商凭据泄漏进协议参数。

服务定位与设计边界

DDNS 的 MCP 服务有两条明确的设计约束:

  1. 只暴露两个无参工具。客户端能做的只有查询本地缓存状态(get_ddns_status)和按既有配置执行一次同步(update_dns_records)。工具不接受域名、IP、服务商或凭据参数,避免模型绕过本地配置写入任意记录。
  2. 不返回凭据。服务不会在协议参数或结果中返回 DNS 服务商凭据。这一点在源码 ddns/mcp.py 中体现为字段白名单:
STATUS_FIELDS = ("state", "message", "last_sync", "addresses", "providers", "records")

get_ddns_status只把DashboardService.dashboard()的结果裁剪到以上六个字段返回,测试 tests/test_mcp.py 验证了即使内部状态包含token、scheduler、config_path、activities等敏感字段,最终structuredContent与文本内容中都不会出现它们。

MCP 模式下状态来自本地缓存而非实时查询 DNS 服务商,这与 DDNS 缓存机制一致:缓存默认位于临时目录的ddns.{hash}.cache,可在配置中指定路径或用false关闭。

配置文件解析规则

MCP 模式的配置解析比常规ddns运行更严格,配置路径优先级为:

-c/--config > DDNS_CONFIG > 默认本地配置路径

两条硬性限制(文档 mcp.md 明确说明):

  • 只接受一份本地配置文件,不支持远程 URL;
  • 不支持多个-c。

ddns/config/cli.py 在参数解析前就拦截多配置:

def _validate_explicit_mcp_configs(): if len(sys.argv) <= 1 or sys.argv[1] != "mcp": return config_paths = _cli_config_paths(sys.argv[2:]) if config_paths is not None and len(config_paths) != 1: sys.stderr.write("ddns mcp: exactly one local configuration file is supported.") sys.exit(2)

_local_config_path 还会检查最终路径是否包含://,是则报remote configuration files are not supported并退出。mcp子命令还受 _validate_mcp_mode_arguments 约束,只允许-c/--config、--transport、--host、--port、--http-token、--http-origin这些选项,出现未知选项会直接报错退出。stdio 模式下若在未指定-c时出现 HTTP 相关选项,_handle_mcp_command 也会拒绝:HTTP options require --transport http。

stdio 传输(默认)

stdio 模式不监听网络,是最常用的接入方式:

ddns mcp -c /etc/ddns/config.json

stdout 专用于每行一条 JSON-RPC 消息,运行日志只写入 stderr。这一约束在实现中体现得很细致:执行update_dns_records时,同步过程产生的日志会污染 stdout,ddns/mcp.py 通过临时重定向把 stdout 指向 stderr 来规避:

with _STDOUT_REDIRECT_LOCK: protocol_stdout = sys.stdout try: sys.stdout = sys.stderr status = service.sync(source="MCP", cancelled=lambda: self._is_cancelled(request_id)) finally: sys.stdout = protocol_stdout

消息读取由独立线程完成(McpServer.serve),stdin 每行解析为一个 JSON-RPC 消息;解析失败的行返回 JSON-RPC-32700 Parse error。

接入 GitHub Copilot CLI

copilot mcp add ddns -- ddns mcp -c /etc/ddns/config.json

在任意支持 MCP 的客户端中配置

{ "mcpServers": { "ddns": { "command": "ddns", "args": ["mcp", "-c", "/etc/ddns/config.json"] } } }

Windows 下 JSON 路径中的反斜杠需要写成\\,也可以将command替换为ddns可执行文件的绝对路径。

协议版本与生命周期

  • stdio 支持 MCP2026-07-28,并兼容2025-11-25的initialize生命周期(McpServer 中PROTOCOL_VERSION与LEGACY_PROTOCOL_VERSION两个常量)。
  • 现代协议请求要求params._meta携带io.modelcontextprotocol/protocolVersion、io.modelcontextprotocol/clientCapabilities,版本不匹配时返回错误码-32022 Unsupported protocol version并附支持列表。
  • 兼容模式(initialize)下,客户端必须发送含name/version的clientInfo,且需等待notifications/initialized通知到达后才能调用工具,否则返回-32600 Server is not initialized(见 _handle_legacy_request)。

Streamable HTTP 传输

启动独立 HTTP MCP

ddns mcp -c /etc/ddns/config.json --transport http

默认 endpoint 为http://127.0.0.1:9876/mcp。如果运行 Web 控制台,同一监听地址也会自动提供/mcp:ddns/web/server.py 的create_server在创建仪表盘服务器时同时挂载了McpHttpEndpoint,与 Web API 共用端口和 token。

独立模式与 Web 共用以下参数:

参数描述
--host监听地址;默认127.0.0.1,支持具体局域网地址及0.0.0.0/::
--port监听端口,默认9876;设为0时由系统分配
--http-tokenWeb API 与 HTTP MCP 共享的 token;仅允许无空格的可见 ASCII 字符
--http-origin ORIGIN允许浏览器访问/mcp的精确 HTTP(S) origin;可重复

监听配置优先级为命令行 > JSON 顶层http对象 >DDNS_HTTP_*环境变量 >127.0.0.1:9876。该优先级由 resolve_http_settings 实现:先合并环境变量DDNS_HTTP_HOST/DDNS_HTTP_PORT/DDNS_HTTP_TOKEN/DDNS_HTTP_ORIGINS(映射为http_host等键),再覆盖 JSONhttp对象,最后覆盖命令行传入值。JSON 写法示例见 JSON 配置参考:

{ "http": { "host": "127.0.0.1", "port": 9876, "token": null, "origins": ["https://ddns.example.com"] } }

参数校验同样值得注意:token 经 _normalize_token 校验必须是无空格的可见 ASCII;host拒绝包含空白、换行或路径字符的值;非回环监听且 token 为空时在 normalize_http_settings 直接抛错:A non-empty HTTP token is required for non-loopback listeners.。

认证模型

回环地址未配置 token 时,Web API(包括完整配置和服务商凭据)与/mcp均免认证;非回环或通配监听必须配置非空 token。MCP 客户端通过Authorization: Bearer <token>发送凭据。

实现上,McpHttpEndpoint._validate_access 分三步校验:

  1. 无 token 时只接受 Host 头解析出的回环地址(_host_header_is_loopback),否则返回421 invalid_host;
  2. Origin头存在且不在白名单、也不等于请求自身的直接 origin 时返回403 invalid_origin;
  3. token 比对使用常量时间比较 request_token_matches(底层hmac.compare_digest),失败返回401并附WWW-Authenticate: Bearer realm="ddns-mcp"。

CORS 与反向代理注意事项

DDNS 不提供 TLS。局域网监听必须置于受信 HTTPS 反向代理之后,并保留Authorization、Host和Origin头。浏览器通过 TLS 终止代理访问/mcp时,即使公网页面看起来同源,也必须把精确的公网 HTTPS origin 配置到http.origins或--http-origin;原生 MCP 客户端可不发送Origin。

CORS 行为在 handle_options 中实现:OPTIONS请求只在 origin 命中白名单时返回204与Access-Control-Allow-Headers: Authorization, Content-Type, MCP-Protocol-Version, Mcp-Method, Mcp-Name;POST响应则按 _cors_headers 仅对白名单 origin 附加Access-Control-Allow-Origin并带Vary: Origin。注意origins必须是精确 origin,normalize_origin 不支持通配符、路径或凭据。

请求框架约束

HTTP transport 仅支持 MCP2026-07-28,每个请求单独 POST 到/mcp,必须:

  • Content-Type: application/json;
  • 携带MCP-Protocol-Version、Mcp-Method头,调用工具时还需Mcp-Name;
  • Accept同时包含application/json和text/event-stream。

这些约束由 _read_message 与 _validate_envelope 逐条校验:Mcp-Name还支持=?base64?...?=MIME 镜像值解码(_decode_mirrored_value),头体不一致统一返回错误码-32020。请求体上限 2 MiB(MAX_BODY_SIZE),超出返回413。

当前为 tools-only 实现,返回普通 JSON:不提供 SSE、sessions、legacy HTTP、resources、prompts 或 subscriptions。方法不支持时返回405,-32601 Method not found映射为 HTTP404,协议版本错误映射为400(见 handle_post)。

可用工具

工具参数行为
get_ddns_status无读取本地配置、缓存地址、缓存记录、服务商摘要和最近同步时间,不实时查询 DNS 服务商
update_dns_records无使用配置中的当前 IP 规则,对所有已配置记录执行一次完整同步,并返回同步后的状态

工具定义集中在 ddns/mcp.py 的TOOLS元组中,两者的inputSchema都是{"type": "object", "additionalProperties": false},并带安全注解:get_ddns_status标注readOnlyHint: true、destructiveHint: false;update_dns_records标注readOnlyHint: false、destructiveHint: true,提示客户端这是有副作用的破坏性操作。update_dns_records会修改外部 DNS 状态,客户端应在调用前保留人工确认——服务器在server/discover的instructions中也声明了同样要求(McpServer._discover)。

_call_tool 对传入参数严格拒绝:任何非空arguments都会触发This tool does not accept arguments,未知工具名返回Unknown tool。每次调用通过service_factory构造DashboardService(web/service.py),update_dns_records最终走 DashboardService.sync:逐个已配置服务商执行同步,任一失败即记录failed状态并抛出DashboardOperationError,全部成功则返回与get_ddns_status相同结构的最新状态。

协议与运行限制

文档列出的运行限制,逐条都有源码或机制依据:

  • stdio 支持2026-07-28,兼容2025-11-25initialize 生命周期;HTTP 仅支持2026-07-28。独立 HTTP 服务器创建时显式把支持版本收窄为单一值(mcp_http.create_server 中supported_versions=(PROTOCOL_VERSION,))。
  • stdio 客户端取消:notifications/cancelled通知会被 _record_cancellation 记录;update_dns_records执行期间,DashboardService.sync 在每个服务商的同步前后都会检查cancelled(),服务会在 IP 获取规则、域名记录及 DNS 服务商之间停止后续处理。
  • HTTP 无 SSE 取消:每次请求是无状态 POST,客户端断开后同步可能继续完成;日志会记录MCP HTTP client disconnected before receiving its response(handle_post)。
  • 已发出的 DNS API 请求无法撤销,取消只能阻止尚未发出的部分。
  • 状态依赖本地缓存:关闭缓存后,另一进程中的后续状态请求无法还原此前的同步历史(last_sync等状态存于进程内存_last_sync_*字段),但update_dns_records调用本身仍会返回本次结果。
  • 避免多进程竞争:不要让多个 Web、MCP 或系统任务进程同时更新同一份配置。DashboardService内部有RLock保护配置读写,但这是进程内锁,跨进程并发同步仍可能互相覆盖外部 DNS 状态。

测试与验证路径

两种传输的实现都有对应的回归测试,可以作为行为验证依据:

  • tests/test_mcp.py:验证server/discover声明的版本与tools能力、tools/list的确定性输出与安全注解、状态工具只返回白名单字段且不含凭据文本;
  • tests/test_mcp_http.py:在真实回环端口上起服务,验证 Streamable HTTP 的帧校验、Mcp-Method/Mcp-Name一致性、token 与 origin 拒绝路径(401/403/421)以及 resolve_http_settings 的优先级合并。

小结

DDNS 的 MCP 服务用标准库实现了一条最小可用的「状态查询 + 人工确认同步」通道:stdio 模式一行命令即可挂进 Copilot 等本地客户端;Streamable HTTP 模式则与 Web 控制台共用端口、token 与 origin 白名单,天然适合同机管理面板与 MCP 客户端共享一个监听。配置上记住三件事即可:MCP 模式只认单份本地配置、非回环监听必须带 token、浏览器跨源访问要配精确 origin。工具层刻意无参且字段白名单化,把「能改什么」完全收敛到本地配置文件本身,这是接入 Agent 时最重要的安全边界。

  • 网络
  • CLI

【免费下载链接】DDNS

🌐自动更新域名解析到本机IP,支持dnspod,阿里DNS,CloudFlare,华为云,DNSCOM...

项目地址:https://gitcode.com/gh_mirrors/dd/DDNS
点击查看免费下载

相关推荐

上一篇:Electron.NET 热更新终极指南:无需重启应用的智能代码更新方案
下一篇:【实战指南】三步搞定NoteDiscovery云原生知识库容器化部署

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询