Cannot authorize streamable-http MCP servers when URL path's last segment isn't mcp/sse - SSE-first fallback never triggers (httpx.ReadTimeout not caught)
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 74/100
調査の方向性
api/core/mcp/mcp_client.py の MCPClient._initialize から始め、最後のパスセグメントが mcp または sse ではない streamable-http エンドポイントに対する認証を再現してください。SSE を待機せずに streamable-http の初期化が成功すること、必要な場合には SSE フォールバックが引き続き機能すること、MCPAuthError が別のトランスポートとして再試行されないことを確認してください。
索引モデルが issue の本文から書いたものです。
説明
Title: Cannot authorize streamable-http MCP servers when URL path's last segment isn't mcp/sse — SSE-first fallback never triggers (httpx.ReadTimeout not caught)
Dify version: 1.15.0
Cloud or Self Hosted: Self Hosted (Docker)
Steps to reproduce
- Run Dify 1.15.0 via Docker (
langgenius/dify-api:1.15.0). - Go to Tools → MCP and add an MCP server whose endpoint is a streamable-http server and whose URL path does not end with
/mcpor/sse.
Example: a PandaWiki MCP share endpoint:
Here the last path segment is thehttp://<host>:8089/share/v1/mcp/<share_id>share_id, notmcp. - Confirm the server is a healthy streamable-http MCP server. A direct
POST .../initializereturns200+Mcp-Session-Idand a valid JSON-RPC initialize response:
A{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"pandawiki-mcp","version":"1.0.0"}}}GET(SSE-style) to the same URL hangs and returns nothing. - Click Authorize on the provider.
✔️ Expected Behavior
Dify detects that the server speaks streamable-http, performs the initialize handshake, fetches the tool list, and marks the provider as authorized.
❌ Actual Behavior
Clicking Authorize appears to do nothing in the UI.
- nginx access log:
(POST /console/api/workspaces/current/tool-provider/mcp/auth -> 499499= client closed the connection before the backend responded.) - api log:
core/mcp/client/sse_client.py:301 Error connecting to SSE endpoint httpcore.ReadTimeout: timed out tool_providers.py:1120 Failed to fetch MCP tools after creation - The provider stays
authed=falseintool_mcp_providers. The backend blocks up tosse_read_timeout(default 300s); the browser gives up first →499→ no feedback in the UI.
Root cause
In api/core/mcp/mcp_client.py, MCPClient._initialize picks the transport by the last segment of the URL path:
method_name = path.rstrip("/").split("/")[-1] if path else ""
if method_name in {"mcp", "sse"}:
client_factory = connection_methods[method_name]
self.connect_server(client_factory, method_name)
else:
try:
self.connect_server(sse_client, "sse") # tries SSE first
except (MCPConnectionError, ValueError): # cannot catch httpx.ReadTimeout
self.connect_server(streamablehttp_client, "mcp")
Two problems combine:
- The
elsebranch tries SSE first. A streamable-http server does not respond to the SSE GET, so the connect hangs untilsse_read_timeoutand raiseshttpx.ReadTimeout. except (MCPConnectionError, ValueError)does not catchhttpx.ReadTimeout(norhttpx.ConnectError), so the fallback tostreamablehttp_clientnever triggers. The timeout propagates and the auth call fails after a long block.
Also note the method docstring says "Initialize the client with fallback to SSE if streamable connection fails" — i.e. streamable-http is supposed to be the default — but the else branch actually tries SSE first, the opposite of the documented intent.
Impact
Any streamable-http MCP server whose URL path's last segment is not exactly mcp or sse (URLs ending with an id / token / share_id, or carrying a query string) cannot be authorized in Dify 1.15.0. This is a broad class of servers, not specific to PandaWiki.
Related: #24297 reported the same 300s symptom in 1.7.2 (attributed to the sse_client thread pool) and was closed as cant-reproduce. In 1.15.0 the 300s has a different, clearly identifiable root cause: the protocol-detection heuristic above + the except (MCPConnectionError, ValueError) that cannot catch httpx.ReadTimeout, so the SSE -> streamable fallback never runs.
Suggested fix
In the else branch, try streamable-http first and fall back to SSE on any connection failure, while letting MCPAuthError (HTTP 401) propagate so it isn't retried as a different transport:
else:
try:
self.connect_server(streamablehttp_client, "mcp")
except MCPAuthError:
raise
except Exception:
logger.debug("MCP connection failed with 'mcp', falling back to 'sse' method.")
self.connect_server(sse_client, "sse")
Notes:
MCPAuthErroris a subclass ofMCPConnectionError, so it must be caught and re-raised separately before the genericexcept Exception(otherwise a 401 would be silently retried over SSE).- Verified locally: with this change, the PandaWiki server authorizes in a couple of seconds and
list_toolsreturns correctly.
Environment
- Dify 1.15.0, Self Hosted (Docker).
SSRF_PROXY_HTTP_URL/SSRF_PROXY_HTTPS_URLare unset, so the MCP client connects directly (not via the squid SSRF proxy); the issue is unrelated to thedeny to_private_networkssquid rule.- MCP server: PandaWiki, streamable-http, public share endpoint (no auth).
- 主要言語
- TypeScript
- スター
- 157k
- フォーク
- 24.7k
- 平均マージ
- 22時間 32分
- マージ済み PR(30日)
- 611
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
langgenius/dify のほかの issue
-
Annotation Reply: a stored score threshold of 0.0 is silently replaced with 1, disabling the feature オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42639 · コメント 1 件 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
langgenius/dify#42468 · コメント 1 件 · リアクション 1 件 ·
-
🐞 bug
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
langgenius/dify#42446 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42355 · コメント 1 件 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42350 · コメント 1 件 · リアクション 1 件 ·
langgenius/dify の issue をすべて見る
似ている issue
-
calcite-components needs triage refactor
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
Esri/calcite-design-system#15203 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
難易度 1/5 1時間未満 初心者へのやさしさ 95/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
Automattic/studio#4908 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100