Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Random error thrown on response #88

Open
MarkusPfundstein opened this issue Dec 4, 2024 · 6 comments
Open

Random error thrown on response #88

MarkusPfundstein opened this issue Dec 4, 2024 · 6 comments
Assignees
Labels
bug Something isn't working

Comments

@MarkusPfundstein
Copy link

Describe the bug
Sometimes, I see a stacktrace printed in the logs of my mcp server. Claude eventually succeeds to response but I think its good to investigate it.

To Reproduce
Its hard to reproduce as it does not always happen. The code in my codebase that caused it to happen is this one:

    def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:

        user_id = args.get(toolhandler.USER_ID_ARG)
        if not user_id:
            raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")

        gmail_service = gmail.GmailService(user_id=user_id)
        query = args.get('query')
        max_results = args.get('max_results', 100)
        emails = gmail_service.query_emails(query=query, max_results=max_results)

        return [
            TextContent(
                type="text",
                text=json.dumps(emails, indent=2)
            )
        ]

Called in this context

@app.call_tool()
async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
    """Handle tool calls for command line run."""
    
    if not isinstance(arguments, dict):
        raise RuntimeError("arguments must be dictionary")
    
    if toolhandler.USER_ID_ARG not in arguments:
        raise RuntimeError("user_id argument is missing in dictionary.")

    setup_oauth2(user_id=arguments.get(toolhandler.USER_ID_ARG, ""))

    tool_handler = get_tool_handler(name)
    if not tool_handler:
        raise ValueError(f"Unknown tool: {name}")

    try:
        return tool_handler.run_tool(arguments)
    except Exception as e:
        logger.error(str(e))
        raise RuntimeError(f"Caught Exception. Error: {str(e)}")

The RunTime Error is NOT thrown

Expected behavior
No stacktrace?

Desktop (please complete the following information):

  • MAC OSX
  • Python 3.13
  • mcp 1.1.0

Additional context

Here is a full log

+ Exception Group Traceback (most recent call last):
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/server/stdio.py", line 83, in stdio_server
  |     yield read_stream, write_stream
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/src/mcp_gsuite/server.py", line 161, in main
  |     await app.run(
  |     ...<3 lines>...
  |     )
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/server/__init__.py", line 423, in run
  |     async with ServerSession(
  |                ~~~~~~~~~~~~~^
  |         read_stream, write_stream, initialization_options
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |     ) as session:
  |     ^
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/shared/session.py", line 122, in __aexit__
  |     return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 763, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     )
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/shared/session.py", line 235, in _receive_loop
    |     notification = self._receive_notification_type.model_validate(
    |         message.root.model_dump(
    |             by_alias=True, mode="json", exclude_none=True
    |         )
    |     )
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/pydantic/main.py", line 627, in model_validate
    |     return cls.__pydantic_validator__.validate_python(
    |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
    |         obj, strict=strict, from_attributes=from_attributes, context=context
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    | pydantic_core._pydantic_core.ValidationError: 5 validation errors for ClientNotification
    | ProgressNotification.method
    |   Input should be 'notifications/progress' [type=literal_error, input_value='cancelled', input_type=str]
    |     For further information visit https://errors.pydantic.dev/2.10/v/literal_error
    | ProgressNotification.params.progressToken
    |   Field required [type=missing, input_value={'requestId': 20, 'reason... -2: Request timed out'}, input_type=dict]
    |     For further information visit https://errors.pydantic.dev/2.10/v/missing
    | ProgressNotification.params.progress
    |   Field required [type=missing, input_value={'requestId': 20, 'reason... -2: Request timed out'}, input_type=dict]
    |     For further information visit https://errors.pydantic.dev/2.10/v/missing
    | InitializedNotification.method
    |   Input should be 'notifications/initialized' [type=literal_error, input_value='cancelled', input_type=str]
    |     For further information visit https://errors.pydantic.dev/2.10/v/literal_error
    | RootsListChangedNotification.method
    |   Input should be 'notifications/roots/list_changed' [type=literal_error, input_value='cancelled', input_type=str]
    |     For further information visit https://errors.pydantic.dev/2.10/v/literal_error
    +------------------------------------

During handling of the above exception, another exception occurred:

  + Exception Group Traceback (most recent call last):
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/bin/mcp-gsuite", line 8, in <module>
  |     sys.exit(main())
  |              ~~~~^^
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/src/mcp_gsuite/__init__.py", line 6, in main
  |     asyncio.run(server.main())
  |     ~~~~~~~~~~~^^^^^^^^^^^^^^^
  |   File "/opt/homebrew/Cellar/[email protected]/3.13.0_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 194, in run
  |     return runner.run(main)
  |            ~~~~~~~~~~^^^^^^
  |   File "/opt/homebrew/Cellar/[email protected]/3.13.0_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 118, in run
  |     return self._loop.run_until_complete(task)
  |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  |   File "/opt/homebrew/Cellar/[email protected]/3.13.0_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py", line 721, in run_until_complete
  |     return future.result()
  |            ~~~~~~~~~~~~~^^
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/src/mcp_gsuite/server.py", line 160, in main
  |     async with stdio_server() as (read_stream, write_stream):
  |                ~~~~~~~~~~~~^^
  |   File "/opt/homebrew/Cellar/[email protected]/3.13.0_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 235, in __aexit__
  |     await self.gen.athrow(value)
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/server/stdio.py", line 80, in stdio_server
  |     async with anyio.create_task_group() as tg:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 763, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     )
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/anyio/streams/memory.py", line 242, in send
    |     self.send_nowait(item)
    |     ~~~~~~~~~~~~~~~~^^^^^^
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/anyio/streams/memory.py", line 225, in send_nowait
    |     raise WouldBlock
    | anyio.WouldBlock
    | 
    | During handling of the above exception, another exception occurred:
    | 
    | Traceback (most recent call last):
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/server/__init__.py", line 467, in run
    |     await message.respond(
    |     ...<4 lines>...
    |     )
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/shared/session.py", line 58, in respond
    |     await self._session._send_response(
    |         request_id=self.request_id, response=response
    |     )
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/shared/session.py", line 196, in _send_response
    |     await self._write_stream.send(JSONRPCMessage(jsonrpc_error))
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/anyio/streams/memory.py", line 248, in send
    |     await send_event.wait()
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 1747, in wait
    |     await self._event.wait()
    |   File "/opt/homebrew/Cellar/[email protected]/3.13.0_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/locks.py", line 213, in wait
    |     await fut
    | asyncio.exceptions.CancelledError: Cancelled by cancel scope 1040d7c50
    | 
    | During handling of the above exception, another exception occurred:
    | 
    | Exception Group Traceback (most recent call last):
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/server/stdio.py", line 83, in stdio_server
    |     yield read_stream, write_stream
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/src/mcp_gsuite/server.py", line 161, in main
    |     await app.run(
    |     ...<3 lines>...
    |     )
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/server/__init__.py", line 423, in run
    |     async with ServerSession(
    |                ~~~~~~~~~~~~~^
    |         read_stream, write_stream, initialization_options
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     ) as session:
    |     ^
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/shared/session.py", line 122, in __aexit__
    |     return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 763, in __aexit__
    |     raise BaseExceptionGroup(
    |         "unhandled errors in a TaskGroup", self._exceptions
    |     )
    | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    +-+---------------- 1 ----------------
      | Traceback (most recent call last):
      |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/mcp/shared/session.py", line 235, in _receive_loop
      |     notification = self._receive_notification_type.model_validate(
      |         message.root.model_dump(
      |             by_alias=True, mode="json", exclude_none=True
      |         )
      |     )
      |   File "/Users/markus/experiments/claude-mvp/mcp-gsuite/.venv/lib/python3.13/site-packages/pydantic/main.py", line 627, in model_validate
      |     return cls.__pydantic_validator__.validate_python(
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
      |         obj, strict=strict, from_attributes=from_attributes, context=context
      |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |     )
      |     ^
      | pydantic_core._pydantic_core.ValidationError: 5 validation errors for ClientNotification
      | ProgressNotification.method
      |   Input should be 'notifications/progress' [type=literal_error, input_value='cancelled', input_type=str]
      |     For further information visit https://errors.pydantic.dev/2.10/v/literal_error
      | ProgressNotification.params.progressToken
      |   Field required [type=missing, input_value={'requestId': 20, 'reason... -2: Request timed out'}, input_type=dict]
      |     For further information visit https://errors.pydantic.dev/2.10/v/missing
      | ProgressNotification.params.progress
      |   Field required [type=missing, input_value={'requestId': 20, 'reason... -2: Request timed out'}, input_type=dict]
      |     For further information visit https://errors.pydantic.dev/2.10/v/missing
      | InitializedNotification.method
      |   Input should be 'notifications/initialized' [type=literal_error, input_value='cancelled', input_type=str]
      |     For further information visit https://errors.pydantic.dev/2.10/v/literal_error
      | RootsListChangedNotification.method
      |   Input should be 'notifications/roots/list_changed' [type=literal_error, input_value='cancelled', input_type=str]
      |     For further information visit https://errors.pydantic.dev/2.10/v/literal_error
@michail-nikolaev
Copy link

Got the same on Windows:

During handling of the above exception, another exception occurred:

  + Exception Group Traceback (most recent call last):
  |   File "C:\Dev\mcp\servers\src\test-server\src\test_server\server.py", line 78, in <module>
  |     asyncio.run(serve())
  |   File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 194, in run
  |     return runner.run(main)
  |            ^^^^^^^^^^^^^^^^
  |   File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 118, in run
  |     return self._loop.run_until_complete(task)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\asyncio\base_events.py", line 687, in run_until_complete
  |     return future.result()
  |            ^^^^^^^^^^^^^^^
  |   File "C:\Dev\mcp\servers\src\test-server\src\test_server\server.py", line 72, in serve
  |     async with stdio_server() as (read_stream, write_stream):
  |                ^^^^^^^^^^^^^^
  |   File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\contextlib.py", line 231, in __aexit__
  |     await self.gen.athrow(value)
  |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\mcp\server\stdio.py", line 80, in stdio_server
  |     async with anyio.create_task_group() as tg:
  |                ^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 763, in __aexit__
  |     raise BaseExceptionGroup(
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Exception Group Traceback (most recent call last):
    |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\mcp\server\stdio.py", line 83, in stdio_server
    |     yield read_stream, write_stream
    |   File "C:\Dev\mcp\servers\src\test-server\src\test_server\server.py", line 73, in serve
    |     await server.run(read_stream, write_stream, options, raise_exceptions=True)
    |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\mcp\server\__init__.py", line 404, in run
    |     async with ServerSession(
    |                ^^^^^^^^^^^^^^
    |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\mcp\shared\session.py", line 122, in __aexit__
    |     return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 763, in __aexit__
    |     raise BaseExceptionGroup(
    | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    +-+---------------- 1 ----------------
      | Traceback (most recent call last):
      |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\mcp\shared\session.py", line 235, in _receive_loop
      |     notification = self._receive_notification_type.model_validate(
      |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |   File "C:\Dev\mcp\servers\src\git\.venv\Lib\site-packages\pydantic\main.py", line 627, in model_validate
      |     return cls.__pydantic_validator__.validate_python(
      |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      | pydantic_core._pydantic_core.ValidationError: 5 validation errors for ClientNotification
ProgressNotification.method
  Input should be 'notifications/progress' [type=literal_error, input_value='cancelled', input_type=str]
    For further information visit https://errors.pydantic.dev/2.10/v/literal_error
ProgressNotification.params.progressToken
  Field required [type=missing, input_value={'requestId': 25, 'reason... -2: Request timed out'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/missing
ProgressNotification.params.progress
  Field required [type=missing, input_value={'requestId': 25, 'reason... -2: Request timed out'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/missing
InitializedNotification.method
  Input should be 'notifications/initialized' [type=literal_error, input_value='cancelled', input_type=str]
    For further information visit https://errors.pydantic.dev/2.10/v/literal_error
RootsListChangedNotification.method
  Input should be 'notifications/roots/list_changed' [type=literal_error, input_value='cancelled', input_type=str]
    For further information visit https://errors.pydantic.dev/2.10/v/literal_error
      +------------------------------------

@dsp-ant dsp-ant added the bug Something isn't working label Dec 4, 2024
@dsp-ant dsp-ant self-assigned this Dec 4, 2024
@lgesuellip
Copy link

lgesuellip commented Dec 5, 2024

Hey team,

I am encountering the same error. When the server-side response is delayed due to an extended process (>10 seconds), the error occurs. Although the server successfully returns the results, it crashes afterward, blocking the next requests.
Is there any way to fix it? I am on a Mac, using Claude Desktop as a client.

@7shi
Copy link

7shi commented Dec 6, 2024

I've investigated the issue reported by @MarkusPfundstein in the mcp-obsidian project (MarkusPfundstein/mcp-obsidian#3).

I was able to reproduce this issue with a minimal MCP server implementation that simply waits for a specified number of seconds:

The behavior varies depending on the client:

With MCP Inspector (10-second timeout):

  • Server crashes after sending results if processing takes 8-10 seconds or longer
  • The crash occurs regardless of the timeout setting
  • After crashing, the server stops responding to all requests

With Claude Desktop app (1-minute timeout):

  • Same behavior: server crashes after long-running operations (8-10 seconds or more)
  • The crash appears to be related to the processing duration rather than the timeout settings

This issue seems to be specific to the Python implementation of MCP servers. For comparison, I also implemented the same functionality in TypeScript, which continues to operate normally even after timeout events.

The crash occurs at the point where the server tries to send results back to the client after a long-running operation, making it particularly problematic for operations that involve external API calls or other time-consuming tasks.

Steps to reproduce:

  1. Start MCP Inspector with the server:
    npx @modelcontextprotocol/inspector uv run python server.py
  2. Call wait_seconds with seconds=11
    • The request will timeout
  3. Try to call wait_seconds again
    • The server has crashed and won't accept any further requests

The key characteristic of this issue is that the server successfully returns results for long-running operations, but crashes afterwards, preventing it from accepting any subsequent requests. This makes it particularly problematic for scenarios involving external API calls or other time-consuming tasks, as the server becomes unusable after the first long operation.

@lgesuellip
Copy link

@7shi I am facing the same errors. Have you fixed them in some way?
Have you tried using SSE?

@7shi
Copy link

7shi commented Dec 7, 2024

@lgesuellip I don't know how to fix it. I have not tried SSE.

@dsp-ant
Copy link
Member

dsp-ant commented Dec 10, 2024

Thank you for the repro. That is very useful. That is helpful. I'll take a look. Can't promise any dates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants