modelcontextprotocol / modelcontextprotocol/python-sdk

`stdio_server` uses unbuffered memory streams which can cause server to block and become unresponsive

Aberta
#1,333 1 comentário 1 reação 0 responsáveis Ver no GitHub

Ninguém assumiu esta issue ainda.

bug P2 ready for work
Linguagem predominante
Python
Estrelas
24.3k
Forks
4k
Merge médio
1d 1h
PRs com merge (30d)
31

Descrição

Initial Checks
Description

The MCP Python SDK's stdio_server becomes unresponsive during slow message processing operations, causing ping and/or new requests to timeout.

Observed Behaviour
  • Server appears to 'freeze' and becomes unresponsive after running for extended periods
  • Ping requests timeout during slow operations
  • New requests cannot be processed while the server is handling long-running operations
  • After 67 minutes of operation with requests every 10 seconds, the server became completely unresponsive
Expected Behaviour
  • Server should remain responsive to new requests (like pings) even while processing slow operations
  • Ping requests should not timeout due to message processing delays
  • The server should handle concurrent requests without blocking
Suspected Root Cause

The stdio_server uses max_buffer_size=0 (supplied to anyio.create_memory_object_stream) by default, which creates synchronous handoff between the stdin reader and message processor.

See: https://github.com/modelcontextprotocol/python-sdk/blob/543961968c0634e93d919d509cce23a1d6a56c21/src/mcp/server/stdio.py#L57-L58

When the message processor is slow or blocked, the stdin reader cannot read new messages from stdin, making the server appear unresponsive.

Example Code
  import anyio
  import pytest


  @pytest.mark.anyio
  async def test_server_becomes_unresponsive_with_slow_processor():
      """Demonstrates how server becomes unresponsive during slow processing."""

      # Simulates stdio_server with default max_buffer_size=0 (synchronous handoff)
      send_stream, receive_stream = anyio.create_memory_object_stream(0)

      async def stdin_reader():
          # First message gets through
          await send_stream.send("request_1")

          # Second message (like a ping) blocks until first is fully processed
          await send_stream.send("ping")  # This will block for entire processing time!

      async def message_processor():
          # Process first message
          msg = await receive_stream.receive()

          # Simulate slow processing (database query, API call, etc.)
          await anyio.sleep(0.1)  # 100ms processing time

          # During this time, stdin_reader is completely blocked
          # No new messages (including pings) can be read!

          ping = await receive_stream.receive()  # Finally unblocks stdin_reader

      async with anyio.create_task_group() as tg:
          tg.start_soon(message_processor)
          await anyio.sleep(0.01)  # Let processor start waiting
          tg.start_soon(stdin_reader)

      send_stream.close()
      receive_stream.close()
Proposed Solution

Allow users to configure max_buffer_size > 0 to enable buffering, with a default value (0) that preserves the current behaviour.

async def stdio_server(
    stdin: anyio.AsyncFile[str] | None = None,
    stdout: anyio.AsyncFile[str] | None = None,
    max_buffer_size: int = 0, 
):
    # ...
    read_stream_writer, read_stream = anyio.create_memory_object_stream(max_buffer_size)
    write_stream, write_stream_reader = anyio.create_memory_object_stream(max_buffer_size)
Python & MCP Python SDK
  • Python: 3.12
  • MCP Python SDK: 1.12.4
  • OS: macOS
Additional Context

The issue manifests in long-running servers where message processing can occasionally be slow (database queries, file operations, API calls).

The fix should make max_buffer_size configurable so users can add buffering to prevent the stdin reader from blocking during slow operations.

Guia de contribuição

Abrir o guia de contribuição

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Direção de pesquisa

Comece pela configuração referenciada de memory-stream em src/mcp/server/stdio.py e inspecione como max_buffer_size é passado para ambos os streams. Torne o tamanho do buffer configurável conforme descrito, preservando o valor padrão atual, e verifique, usando o exemplo assíncrono fornecido, que um valor positivo permite o buffering da entrada durante um processamento lento.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
python
Domínio
backend
Tipo de issue
Bug
Dificuldade
2/5
Tempo estimado
1-3 horas
Status de atividade
Estagnada
Clareza
Claramente especificada
Facilidade para iniciantes
52/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.