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

MCP Python SDK Client Appends sessionId as Query Parameter #236

Open
jruokola opened this issue Feb 28, 2025 · 2 comments
Open

MCP Python SDK Client Appends sessionId as Query Parameter #236

jruokola opened this issue Feb 28, 2025 · 2 comments

Comments

@jruokola
Copy link

jruokola commented Feb 28, 2025

Bug Report: MCP Python SDK Client Appends sessionId as Query Parameter

Issue Description

The MCP Python SDK client (mcp.client.sse) incorrectly appends the session ID as a query parameter when sending messages to the /messages endpoint, which violates the Model Context Protocol schema specification. This causes standard-compliant servers to return a 400 Bad Request error.

Environment

  • MCP Python SDK Version: 1.3.0
  • Python Version: 3.12.9
  • Node.js Version: v22.14.0 (server)
  • HTTP Libraries:
    • httpx: 0.28.1
    • httpx-sse: 0.4.0
    • aiohttp: 3.11.13
    • aiohttp-sse-client: 0.2.1
  • Server Type: Express.js with MCP SDK (TypeScript)
  • Date Reported: 2025-02-28

Server Configuration

The server used in this environment is the perplexity-mcp service, a TypeScript implementation using:

  • Express.js web framework
  • MCP TypeScript SDK for Server implementation
  • SSEServerTransport from the official MCP SDK
  • Endpoints:
    • /sse - For establishing SSE connections
    • /messages - For receiving JSON-RPC messages
    • /tools/:toolName and /execute-tool - Alternative direct API endpoints

The server is configured to expect standard MCP messages according to the protocol specification, with the message endpoint expecting messages at /messages without query parameters.

Steps to Reproduce

  1. Establish an SSE connection with Python client to an TypeScript MCP server at /sse endpoint
  2. Successfully receive a session ID from the server during the connection initialization
  3. Attempt to execute a tool using client_session.call_tool() or directly via the write_stream
  4. Observe that the request is made to /messages?sessionId=<session_id> instead of just /messages

Observed Behavior

2025-02-28 01:56:33 2025-02-27 23:56:33.646 - INFO - httpx - [-] - HTTP Request: POST http://perplexity-mcp:3000/messages?sessionId=57ce9ed6-e453-4e1d-b515-7943e7341270 "HTTP/1.1 400 Bad Request"
2025-02-28 01:56:33 2025-02-27 23:56:33.647 - ERROR - mcp.client.sse - [-] - Error in post_writer: Client error '400 Bad Request' for url 'http://perplexity-mcp:3000/messages?sessionId=57ce9ed6-e453-4e1d-b515-7943e7341270'

The SDK attempts to send the message to /messages?sessionId=<session_id> rather than following the MCP specification which requires messages to be sent to /messages.

Expected Behavior

According to the MCP specification, the client should:

  1. Establish a connection to /sse
  2. Receive a session ID from the server
  3. Send messages to /messages without any query parameters
  4. Include any session identification in the message body if needed according to the JSON-RPC format

Specifically, the Base Protocol section of the specification defines:

"The protocol uses JSON-RPC 2.0 messages to establish communication between hosts, clients, and servers."

Per the Messages section of the specification, all messages should follow the JSON-RPC 2.0 format:

{
  "jsonrpc": "2.0",
  "id": "unique-request-id",
  "method": "method-name",
  "params": {
    // Method-specific parameters
  }
}

The session ID should be managed internally by the client and server, not exposed as a query parameter.

Root Cause Analysis

The MCP Python SDK client has an implementation issue in the SSE client where it:

  1. Correctly establishes the SSE connection to /sse
  2. Receives and stores the session ID internally
  3. Incorrectly appends the session ID as a query parameter when posting messages
  4. This appears to be in the post_writer method where it builds the URL with the session ID as a query parameter

Looking at the schema in the MCP specification, the tools/call method should be formatted as:

{
  "jsonrpc": "2.0",
  "id": "request-id",
  "method": "tools/call",
  "params": {
    "name": "tool-name",
    "arguments": {
      // Tool-specific arguments
    }
  }
}

The session ID is not part of this schema and should not be included in the URL.

Recommended Fix for MCP Python SDK

The Python SDK should be modified to send messages to /messages without appending the session ID as a query parameter. This would likely involve:

  1. Modifying the post_writer method in the SSE client
  2. Ensuring the session ID is managed internally or included in the message body if needed
  3. Following the JSON-RPC specification for message formatting

Impact

This issue affects all implementations that:

  1. Use the MCP Python SDK client for communication
  2. Interact with servers that strictly follow the MCP specification
  3. Need to execute tools or send messages over the established SSE connection

Without the workaround, tool execution fails with 400 Bad Request errors, preventing the intended functionality from working.

Additional Notes

This bug reveals a common challenge with protocol implementations: even small deviations from specifications can cause interoperability issues. Server implementations should consider being more lenient and accommodating different client behaviors when possible, while client libraries should strive to adhere strictly to specifications.

For now, we recommend implementing the server-side workaround described above. Or sticking with Stdio protocol. Long-term, we hope to see this issue fixed in the official MCP Python SDK.

@allenporter
Copy link
Contributor

I don't think this is accurate understanding of the protocol or the code.

First on the spec:

According to the MCP specification, the client should:

  1. Establish a connection to /sse
  2. Receive a session ID from the server
  3. Send messages to /messages without any query parameters
  4. Include any session identification in the message body if needed according to the JSON-RPC format

Where are you seeing this in the spec? My impression is that the spec does not say this. The closest section I am aware of describes HTTP with SSE and there only says that the initial SSE and it only says that the sse endpoint replies with the messages endpoint. The client does not build that url or change it. Further, the json rpc RPC spec describes is a different part of the protocol, at a higher level above transport. This is all well within the spec.

Second on the implementation: The python client is not involved in anything related to a session id. The server sets this and the session id is transparent to the client. If there is a bug here in the server sdk, its not from what was described here as far as i can tell.

@tanhaipeng
Copy link

when deploy on multi servers, call sse by domain name, session_id is missing, only call same host.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants