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:
1 parent
9a43ee244d
commit
29a1ce2e54
5 files changed
+575
-55
No files matched your search
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user