-
Notifications
You must be signed in to change notification settings - Fork 41
Description
Please confirm before submission 在提交之前,请确认
- I have searched for existing issues search for existing issues, including closed ones.
我已经搜索了现有问题搜索现有问题,包括已关闭的问题。"
Dify version Dify版本
container image langgenius/dify-api:1.7.1, langgenius/dify-plugin-daemon:0.2.0-local
Plugin version 插件版本
0.2.1
HTTP with SSE or Streamable HTTP
HTTP with SSE
Problem description 问题描述
The plugin does not correctly implement the MCP protocol for notifications (, e.g., ctx.info(), see FastMCP Server Logging). This causes the connection to close unexpectedly or the agent to hang.
When a tool sends a notification before the final result, the plugin mishandles it.
Showcasing the problem with a simple FastMCP server.
Case 1: Notification w/o delay (causes agent to hang)
@mcp.tool()
async def hello(ctx: Context) -> str:
await ctx.info("Hello!") # notification is mishandled
return "Hello from the mcp-server!"Symptom: The server log is clean, but the Dify agent gets stuck processing and never completes.
Case 2: Notification with w/ delay (causes MCP server error)
@mcp.tool()
async def hello_with_delay(ctx: Context) -> str:
await ctx.info("Hello!") # notification causes the plugin to disconnect
await asyncio.sleep(1)
return "Hello from the mcp-server, with a delay!"Symptom: The plugin closes the connection after receiving the notification. When the server tries to send the final result 1 second later, it throws an anyio.ClosedResourceError, indicating the plugin has decided to close the connection before the server can respond.
Compared with case 1, it is likely that during the 1 second delay, the plugin client has mishandled the server notification, and closed the connection. Without the delay, the server is able to respond before the unexpected connection closed. This is a race condition.
Full log: 1st call w/ asyncio.sleep, plugin disconnects. 2nd call w/o asyncio.sleep, agent hangs.
INFO: Started server process [3677729]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:3000 (Press CTRL+C to quit)
INFO: *ip address*:36782 - "GET /sse HTTP/1.1" 307 Temporary Redirect
INFO: *ip address*:36782 - "GET /sse/ HTTP/1.1" 200 OK
INFO: *ip address*:36784 - "POST /messages/?session_id=246afd4a73c34b72a1b168ccaa2187d7 HTTP/1.1" 202 Accepted
INFO: *ip address*:36784 - "POST /messages/?session_id=246afd4a73c34b72a1b168ccaa2187d7 HTTP/1.1" 202 Accepted
INFO: *ip address*:36784 - "POST /messages/?session_id=246afd4a73c34b72a1b168ccaa2187d7 HTTP/1.1" 202 Accepted
[08/07/25 11:57:08] DEBUG Handler called: list_tools server.py:476
DEBUG Processing message: source=client type=request logging.py:77
method=tools/list payload={"method":
"tools/list", "params": null}
DEBUG Completed message: tools/list logging.py:81
DEBUG Request tools/list completed in 3.76ms timing.py:47
INFO: *ip address*:36784 - "POST /messages/?session_id=246afd4a73c34b72a1b168ccaa2187d7 HTTP/1.1" 202 Accepted
DEBUG Handler called: list_resources server.py:520
DEBUG Processing message: source=client type=request logging.py:77
method=resources/list
DEBUG Completed message: resources/list logging.py:81
DEBUG Request resources/list completed in 2.03ms timing.py:47
INFO: *ip address*:36784 - "POST /messages/?session_id=246afd4a73c34b72a1b168ccaa2187d7 HTTP/1.1" 202 Accepted
DEBUG Handler called: list_resource_templates server.py:565
DEBUG Processing message: source=client type=request logging.py:77
method=resources/templates/list
DEBUG Completed message: resources/templates/list logging.py:81
DEBUG Request resources/templates/list completed in timing.py:47
2.02ms
INFO: *ip address*:36784 - "POST /messages/?session_id=246afd4a73c34b72a1b168ccaa2187d7 HTTP/1.1" 202 Accepted
DEBUG Handler called: list_prompts server.py:610
DEBUG Processing message: source=client type=request logging.py:77
method=prompts/list payload={"method":
"prompts/list", "params": null}
DEBUG Completed message: prompts/list logging.py:81
DEBUG Request prompts/list completed in 2.38ms timing.py:47
INFO: *ip address*:36784 - "POST /messages/?session_id=246afd4a73c34b72a1b168ccaa2187d7 HTTP/1.1" 202 Accepted
[08/07/25 11:57:10] DEBUG Handler called: call_tool hello_with_delay with {} server.py:669
DEBUG Processing message: source=client type=request method=tools/call payload={"meta": null, "name": "hello_with_delay", "arguments": {}} logging.py:77
[08/07/25 11:57:11] DEBUG Completed message: tools/call logging.py:81
DEBUG Request tools/call completed in 1005.50ms timing.py:47
ERROR: Exception in ASGI application
+ Exception Group Traceback (most recent call last):
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py", line 403, in run_asgi
| result = await app( # type: ignore[func-returns-value]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
| return await self.app(scope, receive, send)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/applications.py", line 113, in __call__
| await self.middleware_stack(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
| raise exc
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
| await self.app(scope, receive, _send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/middleware/authentication.py", line 48, in __call__
| await self.app(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/auth/middleware/auth_context.py", line 43, in __call__
| await self.app(scope, receive, send)
| File "/home/user/mcp-server/app/middleware.py", line 37, in __call__
| await self.app(scope, receive, send_wrapper)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/fastmcp/server/http.py", line 67, in __call__
| await self.app(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
| await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
| raise exc
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
| await app(scope, receive, sender)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__
| await self.middleware_stack(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app
| await route.handle(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle
| await self.app(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/auth/middleware/bearer_auth.py", line 96, in __call__
| await self.app(scope, receive, send)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/fastmcp/server/http.py", line 131, in handle_sse
| async with sse.connect_sse(scope, receive, send) as streams:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/.local/share/uv/python/cpython-3.12.11-linux-x86_64-gnu/lib/python3.12/contextlib.py", line 231, in __aexit__
| await self.gen.athrow(value)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/sse.py", line 180, in connect_sse
| async with anyio.create_task_group() as tg:
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/sse.py", line 199, in connect_sse
| yield (read_stream, write_stream)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/fastmcp/server/http.py", line 132, in handle_sse
| await server._mcp_server.run(
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 575, in run
| async with AsyncExitStack() as stack:
| ^^^^^^^^^^^^^^^^
| File "/home/user/.local/share/uv/python/cpython-3.12.11-linux-x86_64-gnu/lib/python3.12/contextlib.py", line 754, in __aexit__
| raise exc_details[1]
| File "/home/user/.local/share/uv/python/cpython-3.12.11-linux-x86_64-gnu/lib/python3.12/contextlib.py", line 737, in __aexit__
| cb_suppress = await cb(*exc_details)
| ^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/shared/session.py", line 218, in __aexit__
| return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 586, in run
| async with anyio.create_task_group() as tg:
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
| raise BaseExceptionGroup(
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 610, in _handle_message
| await self._handle_request(message, req, session, lifespan_context, raise_exceptions)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/server/lowlevel/server.py", line 665, in _handle_request
| await message.respond(response)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/shared/session.py", line 131, in respond
| await self._session._send_response( # type: ignore[reportPrivateUsage]
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/mcp/shared/session.py", line 329, in _send_response
| await self._write_stream.send(session_message)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/anyio/streams/memory.py", line 243, in send
| self.send_nowait(item)
| File "/home/user/mcp-server/.venv/lib/python3.12/site-packages/anyio/streams/memory.py", line 212, in send_nowait
| raise ClosedResourceError
| anyio.ClosedResourceError
+------------------------------------
INFO: *ip address*:53902 - "GET /sse HTTP/1.1" 307 Temporary Redirect
INFO: *ip address*:53902 - "GET /sse/ HTTP/1.1" 200 OK
INFO: *ip address*:53904 - "POST /messages/?session_id=d4a26ac8c5fa4890aa0373b836be8259 HTTP/1.1" 202 Accepted
INFO: *ip address*:53904 - "POST /messages/?session_id=d4a26ac8c5fa4890aa0373b836be8259 HTTP/1.1" 202 Accepted
INFO: *ip address*:53904 - "POST /messages/?session_id=d4a26ac8c5fa4890aa0373b836be8259 HTTP/1.1" 202 Accepted
[08/07/25 12:00:27] DEBUG Handler called: list_tools server.py:476
DEBUG Processing message: source=client type=request method=tools/list payload={"method": "tools/list", "params": null} logging.py:77
DEBUG Completed message: tools/list logging.py:81
DEBUG Request tools/list completed in 2.41ms timing.py:47
INFO: *ip address*:53904 - "POST /messages/?session_id=d4a26ac8c5fa4890aa0373b836be8259 HTTP/1.1" 202 Accepted
DEBUG Handler called: list_resources server.py:520
DEBUG Processing message: source=client type=request method=resources/list logging.py:77
DEBUG Completed message: resources/list logging.py:81
DEBUG Request resources/list completed in 2.06ms timing.py:47
INFO: *ip address*:53904 - "POST /messages/?session_id=d4a26ac8c5fa4890aa0373b836be8259 HTTP/1.1" 202 Accepted
DEBUG Handler called: list_resource_templates server.py:565
DEBUG Processing message: source=client type=request method=resources/templates/list logging.py:77
DEBUG Completed message: resources/templates/list logging.py:81
DEBUG Request resources/templates/list completed in 1.97ms timing.py:47
INFO: *ip address*:53904 - "POST /messages/?session_id=d4a26ac8c5fa4890aa0373b836be8259 HTTP/1.1" 202 Accepted
DEBUG Handler called: list_prompts server.py:610
DEBUG Processing message: source=client type=request method=prompts/list payload={"method": "prompts/list", "params": null} logging.py:77
DEBUG Completed message: prompts/list logging.py:81
DEBUG Request prompts/list completed in 2.12ms timing.py:47
INFO: *ip address*:53904 - "POST /messages/?session_id=d4a26ac8c5fa4890aa0373b836be8259 HTTP/1.1" 202 Accepted
[08/07/25 12:00:29] DEBUG Handler called: call_tool hello with {} server.py:669
DEBUG Processing message: source=client type=request method=tools/call payload={"meta": null, "name": "hello", "arguments": {}} logging.py:77
DEBUG Completed message: tools/call logging.py:81
DEBUG Request tools/call completed in 2.88ms timing.py:47
Conclusion:
The plugin needs to be updated to correctly handle or ignore notifications/display messages received over SSE stream.
See #102, which also pointed out part of the problem.