fix(mcp): HTTP 模式新增 --allowed-hosts,修 LAN Host 421

Windows 实测:绑 0.0.0.0 后局域网请求被 mcp DNS 重绑定防护 421 拒。
- parse_args 加 --allowed-hosts(逗号分隔 Host 白名单,默认 None 保持
  mcp 仅 localhost 族行为)
- build_server(manager, allowed_hosts=...) 经 TransportSecuritySettings
  传入(mcp 1.29.0 API 与设计一致,无需调整)
- main() 逗号拆分(去空白、丢空段)
- 回归测试:TestClient 驱动真实 ASGI app(含 lifespan),
  Host=192.168.61.35:8972 initialize → 200;Host=evil.example.com → 421

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
chenweiandClaude committed 2026-07-30 23:55:57 +00:00
1 parent 1f78587bbd
commit 69c4d209c0
2 files changed
+70 -4

No files matched your search

+20 -3
View File
@@ -17,8 +17,19 @@ from .mcp_tools import register_tools
TOKEN_ENV = "PPCLOCK_MCP_TOKEN"
def build_server(manager: DeviceManager) -> FastMCP:
mcp = FastMCP("ppclock", stateless_http=True)
def build_server(manager: DeviceManager,
allowed_hosts: list[str] | None = None) -> FastMCP:
"""装配 FastMCP。allowed_hosts 为 HTTP Host 白名单(mcp DNS 重绑定防护):
HTTP 模式绑非回环地址时,必须把本机的局域网地址加进来
(如 ["192.168.61.35:8972", "localhost:8972"]),否则 LAN Host 请求被 421。
None 保持 mcp 默认(仅 localhost 族)。"""
if allowed_hosts is not None:
from mcp.server.transport_security import TransportSecuritySettings
mcp = FastMCP("ppclock", stateless_http=True,
transport_security=TransportSecuritySettings(
allowed_hosts=allowed_hosts))
else:
mcp = FastMCP("ppclock", stateless_http=True)
register_tools(mcp, manager)
return mcp
@@ -34,6 +45,10 @@ def parse_args(argv=None) -> argparse.Namespace:
help="守候连接秒数(覆盖设备 30-60s 广播窗口)")
p.add_argument("--idle-timeout", type=float, default=300.0,
help="空闲断开秒数;0=每次调用独立连接")
p.add_argument("--allowed-hosts", default=None,
help="HTTP Host 白名单(逗号分隔)。HTTP 模式绑非回环地址时,"
"把本机的局域网地址加进来,如 "
"192.168.61.35:8972,localhost:8972;缺省仅 localhost 族")
return p.parse_args(argv)
@@ -71,7 +86,9 @@ def main(argv=None) -> None:
manager = DeviceManager(mac=args.mac,
connect_timeout=args.connect_timeout,
idle_timeout=args.idle_timeout)
mcp = build_server(manager)
allowed_hosts = (None if args.allowed_hosts is None else
[h.strip() for h in args.allowed_hosts.split(",") if h.strip()])
mcp = build_server(manager, allowed_hosts=allowed_hosts)
if args.transport == "stdio":
try:
mcp.run(transport="stdio")