8000 MCP Python SDK is rejecting JSON-RPC batch requests · Issue #934 · modelcontextprotocol/python-sdk · GitHub
[go: up one dir, main page]

Skip to content
MCP Python SDK is rejecting JSON-RPC batch requests #934
Open
@mjunaidca

Description

@mjunaidca

Describe the bug
The MCP Python SDK violates the MCP Specification 2025-03-26 by rejecting JSON-RPC batch requests (arrays of requests), which are explicitly supported by the protocol specification. This is a specification compliance issue that breaks interoperability and performance for multi-agent systems.

To Reproduce
Steps to reproduce the behavior:

  1. Create a server using FastMCP:
from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    name="batching-server",
    description="A simple server to demonstrate MCP batching.",
    stateless_http=True
)

@mcp.tool()
def add(a: int, b: int) -> int:
    """Adds two integers together."""
    print(f"Server: Executing add({a}, {b})")
    return a + b

@mcp.tool()
async def greet(name: str) -> str:
    """Provides a friendly greeting."""
    print(f"Server: Executing greet('{name}')")
    return f"Hello, {name}!"

# Expose the app for Uvicorn
mcp_app = mcp.streamable_http_app() 
# Run with: uvicorn server:app --host 0.0.0.0 --port 8000
  1. Send a valid JSON-RPC batch request (array):
import httpx
import asyncio

async def main():
    print("--- MCP Batch Request Client Demonstration ---")

    # --- Batch Request (array of requests) ---
    batch_request = [
        {
            "jsonrpc": "2.0",
            "method": "tools/call",
            "params": {"name": "add", "arguments": {"a": 5, "b": 10}},
            "id": 1
        },
        {
            "jsonrpc": "2.0",
            "method": "tools/call",
            "params": {"name": "greet", "arguments": {"name": "World"}},
            "id": 2
        }
    ]

    print(f"\n[Step 1: Sending batch with {len(batch_request)} requests]")

    headers = {
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream"
    }
    
    async with httpx.AsyncClient() as client:
        # Send the batch request to our compliant server
        response = await client.post("http://localhost:8000/mcp/", json=batch_request, headers=headers)
        # response.raise_for_status()

        print(f"\n[Step 2: Received batch response]")
        print(f"Status: {response.text}")
        print(f"Content-Type: {response.headers.get('content-type')}")
        
        # print(f"\n[Step 3: Successfully received {len(responses)} batch responses]")
        print("✅ JSON-RPC batching works correctly when the server properly implements the spec!")


if __name__ == "__main__":
    asyncio.run(main()) 
  1. Server returns ValidationError instead of processing the batch
  2. See error: Cannot validate array as single JSONRPCMessage

Expected behavior
According to MCP Specification 2025-03-26, the server MUST accept "an array batching one or more requests and/or notifications" and return an array of corresponding responses:

[
    {"jsonrpc": "2.0", "result": 15, "id": 1},
    {"jsonrpc": "2.0", "result": 10, "id": 2}
]

Screenshots
N/A - This is a code/protocol compliance issue

Environment:

  • OS: macOS, Linux, Windows (affects all platforms)
  • Python Version: 3.12+
  • MCP Python SDK: Current version
  • Server Framework: FastMCP/streamable_http

Code Location
The issue is in src/mcp/server/streamable_http.py:351:

# This line only accepts single messages, not arrays
message = JSONRPCMessage.model_validate(raw_message)

Additional context

Specification Violation:

  • Source: MCP Specification 2025-03-26 - Transports
  • Section 3 states the POST body MUST support "an array batching one or more requests and/or notifications"
  • The use of "MUST" indicates this is a protocol requirement, not optional

Impact:

  • Performance: Forces N HTTP requests instead of 1 batch request
  • Interoperability: Breaks compatibility with JSON-RPC 2.0 clients
  • Agent Frameworks: Prevents integration with batch-optimized systems

Suggested Fix:
Update HTTP transport to detect arrays and process each message in the batch:

if isinstance(raw_data, list):
    # Batch request - process each message
    responses = []
    for item in raw_data:
        message = JSONRPCMessage.model_validate(item)
        response = await self._process_message(message)
        if response:  # Skip notifications
            responses.append(response)
    return JSONResponse(responses)

Priority: High - Specification compliance issue blocking adoption and multi-agent system performance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0