Skip to content

memory.Client.Call() does not speak MCP Streamable HTTP transport #19

@yvonnedevlinrh

Description

@yvonnedevlinrh

Summary

memory.Client.Call() in internal/memory/proxy.go sends a bare http.Post() to the Dewey MCP endpoint (http://localhost:3333/mcp/). The MCP Streamable HTTP transport requires two things that Call() does not provide:

  1. An Accept header containing both application/json and text/event-stream
  2. A proper MCP session lifecycle: initialize handshake before tools/call invocations

Without these, Dewey returns HTTP 400 (Accept must contain both '"'"'application/json'"'"' and '"'"'text/event-stream'"'"'). This causes all memory proxy tools - hivemind_store, hivemind_find, and the deprecated hivemind_* stubs - to fail with DEWEY_UNAVAILABLE even when Dewey is healthy and running.

To Reproduce

  1. Start Dewey with HTTP transport: dewey --http :3333
  2. Verify Dewey is healthy: curl -s -X POST http://localhost:3333/mcp/ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '"'"'{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}},"id":1}'"'"' - should return a success response
  3. Test memory.Client directly against the running Dewey:
    client := memory.NewClient("http://localhost:3333/mcp/")
    err := client.Health()
    // Returns: dewey unavailable: HTTP 400: Accept must contain both
    // '"'"'application/json'"'"' and '"'"'text/event-stream'"'"'
  4. All three methods fail the same way:
    • client.Health() - DEWEY_UNAVAILABLE
    • client.Find("test", "", 5) - DEWEY_UNAVAILABLE
    • client.Store("test", "tag") - DEWEY_UNAVAILABLE

Expected behavior

When Dewey is running and healthy, hivemind_store and hivemind_find should successfully proxy requests to Dewey and return results.

Root Cause

memory.Client.Call() (internal/memory/proxy.go:60-100) uses c.http.Post(c.url, "application/json", ...) which:

  • Does not set the Accept: application/json, text/event-stream header required by the MCP Streamable HTTP transport
  • Does not handle SSE-formatted responses (event: message\ndata: {json}\n\n)
  • Sends bare JSON-RPC method names like store_learning and semantic_search directly, but MCP requires wrapping these in tools/call with a prior initialize handshake
  • Does not manage MCP session state (session ID from Mcp-Session-Id response header)

The same root cause was partially addressed for the doctor health check in #16, where checkDewey() was fixed to use a standalone MCP initialize probe. However, the underlying memory.Client was not fixed, leaving all tool proxy operations broken.

Affected Consumers

Consumer File Impact
hivemind_store tool internal/tools/memory/tools.go:52 Calls client.Store() - Call("store_learning", ...) - fails with HTTP 400
hivemind_find tool internal/tools/memory/tools.go:90 Calls client.Find() - Call("semantic_search", ...) - fails with HTTP 400
Health() method internal/memory/proxy.go:104 Calls Call("dewey_health", ...) - fails with HTTP 400
MCP server startup cmd/replicator/serve.go:45 Creates memClient used by all memory tools

Suggested Fix

Update memory.Client.Call() to speak proper MCP Streamable HTTP:

  1. Use http.NewRequest instead of http.Post to set the required Accept: application/json, text/event-stream header
  2. Manage MCP session lifecycle: send initialize on first call, cache the Mcp-Session-Id for subsequent calls
  3. Wrap tool method names in the MCP tools/call envelope (e.g., store_learning becomes {"method": "tools/call", "params": {"name": "store_learning", ...}})
  4. Parse SSE-formatted responses to extract JSON-RPC data from data: lines
  5. Update tests in internal/memory/proxy_test.go to use MCP-compatible mock handlers

Location

  • Client implementation: internal/memory/proxy.go:58-100
  • Tool registrations: internal/tools/memory/tools.go
  • MCP server wiring: cmd/replicator/serve.go:45-46
  • Tests: internal/memory/proxy_test.go'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions