fix: 双Claw能力对齐 + MCP/LLM代理绕过(P0止血)

- 新增 app/core/capability_discovery.py:统一能力发现层,Web/IM 双Claw共用单一实现
  (三级降级:用户安装 → 平台published → 相关性裁剪;MCP 自动挂载已启用服务)
- im_llm.py:IM Bot Claw 改用统一发现层,恢复 skills(12) + MCP(26工具) 能力,
  修复 #866 部署回退导致的 agents=[]/mcp=[] 回归;mcp__ 前缀分流到 call_mcp_tool
- mcp_client.py:注入 httpx_client_factory(trust_env=False) 绕过容器 HTTP_PROXY,
  修复外部 MCP(xiaobenyang.com / anysearch.com) tools/list 全部静默失败(mcp_tools 0→26)
- llm_client.py / im_bot_user.py:2026-07-31 代理劫持内部请求 + 媒体断连修复(随镜像已上线)
This commit is contained in:
QihuAI Design committed 2026-08-04 10:21:19 +08:00
1 parent 9a43ee244d
commit 29a1ce2e54
5 files changed
+575 -55

No files matched your search

+23 -2
View File
@@ -20,6 +20,7 @@ from typing import Optional
from mcp import ClientSession
from mcp.client.sse import sse_client
from mcp.client.streamable_http import streamablehttp_client
import httpx
logger = logging.getLogger(__name__)
@@ -28,18 +29,38 @@ _TOOLS_CACHE: dict = {}
_TOOLS_CACHE_TTL = 120 # 秒
def _no_proxy_httpx_client(headers=None, timeout=None, auth=None):
"""MCP 传输用 httpx 客户端工厂:强制直连、不走环境代理。
容器 run.sh 注入了 HTTP_PROXY=http://172.18.0.1:7890,该代理无法解析
外部 MCP 域名(xiaobenyang.com / anysearch.com 等),且默认 trust_env
会读取 HTTP_PROXY 导致 tools/list 全部静默失败。这里 trust_env=False
直连,与 app/core/llm_client.py:chat_complete 修复同源。
"""
kwargs: dict = {"follow_redirects": True, "trust_env": False}
kwargs["timeout"] = timeout if timeout is not None else httpx.Timeout(30.0, read=300.0)
if headers is not None:
kwargs["headers"] = headers
if auth is not None:
kwargs["auth"] = auth
return httpx.AsyncClient(**kwargs)
def _transport_cm(server: dict, timeout: float):
"""按 server['transport_type'] 选择官方 SDK 传输层。
返回异步上下文管理器,进入后产出 (read_stream, write_stream, ...)。
关键:注入 _no_proxy_httpx_client,使 MCP 流量直连(见其 docstring)。
"""
url = (server.get("url") or "").strip()
ttype = (server.get("transport_type") or "streamable_http").lower()
headers = server.get("headers") or {}
if ttype == "sse":
return sse_client(url, headers=headers, timeout=timeout)
return sse_client(url, headers=headers, timeout=timeout,
httpx_client_factory=_no_proxy_httpx_client)
# 默认 streamable_http(官方 SDK 标准传输)
return streamablehttp_client(url, headers=headers, timeout=timeout)
return streamablehttp_client(url, headers=headers, timeout=timeout,
httpx_client_factory=_no_proxy_httpx_client)
async def _session_request(