graphiti icon indicating copy to clipboard operation
graphiti copied to clipboard

Server Errors During Baseline SSE Test (am I doing something wrong?)

Open CaliLuke opened this issue 9 months ago • 2 comments

note: clarified and formatted by AI, but the person asking is human!

This bug was identified during baseline testing for a project aimed at modifying the graphiti/mcp_server/graphiti_mcp_server.py script to support multiple LLM providers (OpenAI and Gemini) based on configuration. The goal is to establish a working baseline using the original script before introducing modifications. This test specifically focuses on the OpenAI provider using SSE transport.

Summary

When running the original graphiti/mcp_server/graphiti_mcp_server.py script with SSE transport (--transport sse) and interacting with it using a standard MCP client flow, the server encounters internal validation and runtime errors upon receiving POST requests (initialize, tools/call) on the dynamically provided /messages/... endpoint. This prevents successful processing of client requests.

Environment

  • Server Script: graphiti/mcp_server/graphiti_mcp_server.py (baseline version from repository)
  • Library: mcp==1.5.0 (as specified in graphiti/mcp_server/uv.lock)
  • Core Library: graphiti-core==0.8.2 (with temporary debug logging added to utils/bulk_utils.py)
  • Python: 3.11
  • Virtual Env: /path/to/tools/mcp-graphiti/.venv (managed with uv)
  • Transport: SSE
  • LLM Config: OpenAI (MODEL_NAME=gpt-4o-mini)
  • Database: Neo4j (cleared before test)
  • Client: Custom test harness (mcp_test_harness.py) developed during testing, implementing the standard MCP SSE client flow.

Reproduction Steps

  1. Prepare Environment:
    • Ensure Neo4j is running and accessible.
    • Activate the virtual environment: source /path/to/tools/mcp-graphiti/.venv/bin/activate
    • Export necessary environment variables: NEO4J_PASSWORD, OPENAI_API_KEY, MODEL_NAME="gpt-4o-mini".
    • Clear Neo4j database: MATCH (n) DETACH DELETE n;
  2. Start Server:
    • From the repository root (/path/to/Documents/Code/graphiti-debug), run:
      uv run python graphiti/mcp_server/graphiti_mcp_server.py --transport sse
      
    • Observe server logs (listens on 0.0.0.0:8000).
  3. Run Client (Test Harness):
    • In a separate terminal (with venv activated), run the debugged test harness:
      python /path/to/tools/mcp-graphiti/mcp_test_harness.py http://localhost:8000/sse "OpenAI Baseline Test" "Testing the original server script with OpenAI."
      

Expected Behavior

  1. Client connects via GET to http://localhost:8000/sse.
  2. Server sends an "endpoint" event with data like /messages/?session_id=....
  3. Client sends initialize request via POST to the received endpoint URL (e.g., http://localhost:8000/messages/?session_id=...).
  4. Server returns 202 Accepted for the POST request.
  5. Server sends the initialize result via a "message" event on the original /sse stream.
  6. Client receives the initialize result, confirms initialization.
  7. Client sends tools/call request (for add_episode) via POST to the same endpoint URL.
  8. Server returns 202 Accepted for the POST request.
  9. Server processes the add_episode request (including calls to graphiti-core and Neo4j).
  10. Server potentially sends tools/result or other notifications via the /sse stream.
  11. Data corresponding to the "OpenAI Baseline Test" episode appears in Neo4j.
  12. Temporary debug logs from graphiti_core/utils/bulk_utils.py appear in server output.

Actual Behavior

  1. Steps 1-5 occur as expected according to client logs.
  2. Server Error 1: When processing the initialize POST request, the server logs a pydantic_core._pydantic_core.ValidationError: 23 validation errors for ClientRequest. The traceback indicates the validation occurs in mcp/shared/session.py in the _receive_loop and fails because the initialize payload (a ServerRequest type) is being validated against the ClientRequest schema union.
  3. Steps 6-8 occur as expected according to client logs (client receives initialize result on /sse, sends tools/call POST, server returns 202 Accepted).
  4. Server Error 2: When processing the tools/call POST request, the server logs a RuntimeError: Received request before initialization was complete. This is likely because the previous ValidationError prevented the server session from correctly transitioning to an initialized state.
  5. The add_episode logic does not execute.
  6. No data is added to Neo4j.
  7. No temporary debug logs from graphiti_core/utils/bulk_utils.py appear in server output.
  8. The client script finishes, reporting success based on receiving 202 Accepted for both POST requests (as per latest harness logic).

Analysis / Hypothesis

The root cause of the ValidationError appears to be a mismatch in how the mcp==1.5.0 library handles validation for POST requests received during an SSE session in this specific setup.

  • Traceback suggests the mcp.server.sse.SseServerTransport.handle_post_message function receives the POST request on the /messages/... endpoint. It likely performs an initial validation/parsing against a generic message type (e.g., JSONRPCMessage).
  • This parsed object seems to be passed to the mcp.shared.session.ServerSession._receive_loop.
  • The _receive_loop, however, expects to perform validation itself using the specific ServerRequest schema union (which includes InitializeRequest, CallToolRequest, etc.).
  • When _receive_loop attempts to validate the already parsed generic object against the specific ServerRequest schema, a ValidationError occurs because the structures don't match perfectly at this stage (e.g., the generic object lacks the specific fields required by InitializeRequest.params).
  • This validation failure prevents the server session state from being correctly updated to "initialized".
  • Consequently, when the subsequent tools/call POST request arrives and is processed by the session loop, it triggers the RuntimeError: Received request before initialization was complete.

While the graphiti/mcp_server/graphiti_mcp_server.py script appears to use the library's public API correctly, this internal validation mismatch within the library's SSE handling for POST requests prevents the baseline script from functioning as expected with SSE transport. It's unclear if this is an inherent library bug for SSE POST handling or if there's a subtle configuration nuance missed in the baseline script that avoids this path in other working SSE implementations (like the custom Gemini script).

Conclusion / Question

The identified validation mismatch within the mcp==1.5.0 library's SSE transport layer prevents the baseline graphiti/mcp_server/graphiti_mcp_server.py script, as currently configured, from successfully processing client requests received via POST on the /messages/... endpoint.

Question for Developers: Given that other SSE implementations using this library reportedly work, what is the correct way to configure or use FastMCP and ServerSession in mcp==1.5.0 (or 1.6.0) to avoid this validation mismatch for POST requests received during an SSE session? Is there a specific configuration pattern in the server script or a nuance in the library interaction that the baseline script is missing?

attached is my test harness, maybe it will help identify the mistake I'm making.

mcp_test_harness.py.zip

CaliLuke avatar Apr 10 '25 06:04 CaliLuke

Thanks for the detailed bug report. I'm investigating.

danielchalef avatar Apr 11 '25 21:04 danielchalef

@CaliLuke it doesn't appear your harness is correctly waiting for the MCP connection to be established prior to using a tool.

It looks like you're using httpx_sse rather than using the MCP SSE client. Please would you try using the MCP SDK?

See: https://modelcontextprotocol.io/docs/concepts/transports#python-client

async with sse_client("http://localhost:8000/sse") as streams:
    async with ClientSession(streams[0], streams[1]) as session:
        await session.initialize()

danielchalef avatar Apr 11 '25 22:04 danielchalef