Skip to content

Bug: Plugin fails to handle MCP notifications #110

@mdrkrg

Description

@mdrkrg

Please confirm before submission 在提交之前,请确认

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions