Skip to content

Conversation

@therealnb
Copy link
Contributor

@therealnb therealnb commented Nov 7, 2025

Description

This PR ensures MCP servers are fully ready before notifying clients, preventing connection timing issues described in #2499.

Changes

The proxy now:

  1. Starts the container and proxy
  2. NEW: Repeatedly calls the initialize endpoint until it succeeds (HTTP 200)
  3. Waits up to 5 minutes with exponential backoff (100ms → 2s)
  4. Only then updates client configurations

Implementation Details

For streamable-http and SSE transports, the runner makes direct HTTP calls to the MCP initialize endpoint with proper headers:

  • Content-Type: application/json
  • Accept: application/json, text/event-stream (required by some MCP servers)
  • MCP-Protocol-Version: 2024-11-05

The function uses exponential backoff to avoid hammering the server, with delays capped at 2 seconds between attempts and an overall timeout of 5 minutes for slow-starting servers like mcp-optimizer.

Testing

  • ✅ Builds successfully
  • ✅ Linter passes (fixed all gosec, lll, and revive issues)
  • ✅ Manually tested with mcp-optimizer (which requires ~30s to start)
  • ✅ Backward compatible (continues with warning if initialize times out)

Fixes #2499, #2271

There is a video of this working here.

This ensures MCP servers are fully ready before notifying clients,
preventing connection timing issues.

The proxy now:
1. Starts the container and proxy
2. Repeatedly calls the initialize endpoint until it succeeds (HTTP 200)
3. Waits up to 5 minutes with exponential backoff (100ms-2s)
4. Only then updates client configurations

For streamable-http transport, the proxy calls POST /mcp with proper
headers (Accept: application/json, text/event-stream) to ensure
compatibility with servers that require both content types.

Fixes #2499
@codecov
Copy link

codecov bot commented Nov 7, 2025

Codecov Report

❌ Patch coverage is 0% with 74 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.72%. Comparing base (4ceed40) to head (1a1e44b).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/runner/runner.go 0.00% 74 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2504      +/-   ##
==========================================
- Coverage   55.84%   55.72%   -0.13%     
==========================================
  Files         312      312              
  Lines       29541    29608      +67     
==========================================
  Hits        16498    16498              
- Misses      11601    11668      +67     
  Partials     1442     1442              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@amirejaz amirejaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

./bin/thv run time --proxy-mode sse is not working, it is stuck on:
3:49PM �[34mINFO�[0m Waiting for MCP server to accept initialize requests at http://localhost:38870/messages (timeout: 5m0s).

Signed-off-by: nigel brown <nigel@stacklok.com>
Signed-off-by: nigel brown <nigel@stacklok.com>
Signed-off-by: nigel brown <nigel@stacklok.com>
Signed-off-by: nigel brown <nigel@stacklok.com>
because it waits for init. Longer timeout

Signed-off-by: nigel brown <nigel@stacklok.com>
Signed-off-by: nigel brown <nigel@stacklok.com>
@eleftherias
Copy link
Member

There is an issue with calling initialize for STDIO MCP servers multiple times. The behaviour is not determined by the spec so they may reject the request #1982. That's why ToolHive avoids calling initialize itself and only forwards the client request.

@therealnb
Copy link
Contributor Author

Discussion here.

The OSV server transport changed from sse to streamable-http on November 4, 2025 in commit 31f2d8b (PR #2441).

Signed-off-by: nigel brown <nigel@stacklok.com>
The OSV MCP server transport changed from sse to streamable-http in the
registry update on 2025-11-04 (commit 31f2d8b). This was causing test
failures with 5-minute timeouts as the tests were still trying to start
the server with --transport sse.

Changes:
- Updated all transport references from sse to streamable-http
- Changed MCP client initialization from NewMCPClientForSSE to
  NewMCPClientForStreamableHTTP
- Updated URL endpoint expectations from /sse to /mcp
- Fixed formatting issues with golangci-lint

Tests now pass: 13 Passed | 0 Failed
Signed-off-by: nigel brown <nigel@stacklok.com>
@therealnb
Copy link
Contributor Author

@amirejaz , this should skip the stdio servers 1a1e44b

@therealnb therealnb merged commit 9ace6bc into main Nov 17, 2025
29 checks passed
@therealnb therealnb deleted the fix/wait-for-initialize-before-client-update branch November 17, 2025 18:05
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

Successfully merging this pull request may close these issues.

handle impetulant client config changes

5 participants