快速结论:这个报错通常出现在 Dify 自托管环境中,当你点击 MCP tool provider 的 Authorize(或创建后自动触发授权)时,后端在连接 MCP server 的阶段抛出了未被正确转换的上游 httpx 异常,最终被通用错误处理器统一包装成不透明的 500,因此前端只看到 500 Internal Server Error,拿不到真实原因。优先排查 MCP server 是否可达、URL 路径是否以 /mcp 或 /sse 结尾。
适用环境:Dify 1.17.1(1.17.0 及 main @ a3b2786b32 也复现),Self Hosted(Docker);错误发生在授权 MCP tool provider 时,与具体 MCP server 实现相关。
最快修复方案:暂无确认的一步修复方案。Issue 讨论确认当前 main 上没有 PR 端到端修复这个 500-vs-4xx 行为,此前两个尝试(PR #40111 已关闭未合并、PR #39321 仍开放未合并)只覆盖了部分路径,因此官方层面还没有直接可用的补丁。
注意事项:该 500 与 #41109 / #41328 / #42061 中 `provider_id` / `server_identifier` 混用导致的 UUID cast 失败(已由 #41360 修复)不是同一个问题——本例中 provider_id 是合法 UUID,provider 记录能被查到,失败发生在后续连接 MCP server 时。若依赖 #40111 思路自行修改,需注意它未合并,且未覆盖下文列出的两处异常泄漏点,改动前应自行验证。
问题场景
在 Dify Self Hosted(Docker)中注册一个 MCP tool provider 后,点击 MCP 详情面板里的 Authorize 按钮(控制台也会在创建后自动触发一次授权)。前端会向 /console/api/workspaces/current/tool-provider/mcp/auth 发送带 provider_id 的请求。当该 MCP server 不可达,或其 URL 路径不以 /mcp 或 /sse 结尾(例如 n8n 风格的 https://example.com/mcp/<id> 端点)时,接口返回 500。
报错原文
{"message":"Internal Server Error","code":"unknown","status":500}
触发请求示例:
curl 'http://localhost/console/api/workspaces/current/tool-provider/mcp/auth' \
-H 'content-type: application/json' \
--data-raw '{"provider_id":"5f1d2cf3-53ca-41ca-af0b-e3df05f78c1d"}'
原因分析
根据 Issue 正文与讨论,根因是 ToolMCPAuthApi.post(以及同类的 console MCP 端点)只把 MCPError / ValueError 转换成带原因的 4xx,其余异常全部落到通用 500 处理器。有两条路径会让原始 httpx 异常穿透这个过滤器:
- 路径一:
core/mcp/client/sse_client.py在捕获异常后直接raise,未做转换,导致httpx.ConnectError/RemoteProtocolError/ReadTimeout逃逸。这同时破坏了MCPClient._initialize里的 transport fallback——该 fallback 只捕获MCPConnectionError/ValueError,所以对 URL 不以/mcp结尾的 streamable-HTTP server,SSE 探测阶段的传输错误会让mcptransport 根本没有机会被尝试。 - 路径二:
core/mcp/auth/auth_flow.py::register_client在动态客户端注册被拒绝时调用response.raise_for_status()。auth()只转换RequestError,端点内层处理器只捕获MCPRefreshTokenError,因此httpx.HTTPStatusError同样以 500 逃逸。
讨论中还提到两个未能修复该问题的历史尝试:PR #40111(针对 #40001,实现了“优先 streamable-http 并捕获 httpx.TransportError 以便 SSE fallback 生效”的思路)已于 2026-08-18 被作者关闭且未合并;PR #39321(针对 #39301)是同类 fallback 修复的更早版本,目前仍开放未合并。两者都未触及以上第二处泄漏路径。
环境排查
- 确认 Dify 版本:1.17.1 / 1.17.0 /
main@ a3b2786b32 均已复现。 - 确认部署方式:Self Hosted(Docker)。
- 确认 MCP server 是否可从 Dify 容器网络内访问(连通性、DNS、端口、防火墙)。
- 确认 MCP server URL 路径是否以
/mcp或/sse结尾;n8n 风格的https://example.com/mcp/<id>是已知触发场景。 - 确认
provider_id是合法 UUID,以排除 #41109 / #41328 / #42061 描述的::uuidcast 问题(该问题已由 #41360 修复)。
解决步骤
- 先确认失败位置:用 Issue 提供的无控制台最小复现,直接构造
MCPClient指向不可达地址(如http://127.0.0.1:9/foo),观察抛出的异常类型是否为原始httpx异常而非MCPError:from core.mcp.error import MCPError from core.mcp.mcp_client import MCPClient try: with MCPClient(server_url="http://127.0.0.1:9/foo", headers={}, timeout=3, sse_read_timeout=5): pass except BaseException as e: - 核对 MCP server 的真实可达性与 URL 形态。若 URL 不以
/mcp或/sse结尾,属于 Issue 明确点名的触发条件,可优先尝试将其改为标准路径或确保服务端在该路径上响应。 - 在官方修复落地前,可优先尝试讨论中提出的补丁方向(此为推测方案,尚未合并验证):把
sse_client.py的 catch-all 与register_client中的raise_for_status()统一包装成MCPConnectionError/ValueError,使其落入except (MCPError, ValueError)分支并返回带原因的 4xx;同时按 PR #40111 思路调整MCPClient._initialize,捕获httpx.TransportError让 transport fallback 真正生效。 - 自行修改后重新构建并重启 API 服务,再回到 MCP 详情面板点击 Authorize。
验证方法
修复生效后,相同的授权请求不应再返回不透明的 {"message":"Internal Server Error","code":"unknown","status":500}。在 MCP server 不可达的场景下,预期应收到携带具体连接失败原因的 4xx 响应;在 URL 不以 /mcp 结尾的 streamable-HTTP 场景下,预期 SSE 探测失败后能自动回退到 mcp transport。若仍返回 500,说明上述两处异常泄漏点未被完全覆盖。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。

![[Bug] Chatflow with Human Input node: input box remains disabled after workflow completion in v1.16.1](https://www.chat-gpts.plus/wp-content/uploads/2026/09/40076-32d8bdf5-768x403.jpg)
