Skip to content

fix(mcp): run checkForUpdates async for mcp and serve subcommands#273

Closed
jgarrone82 wants to merge 2 commits intoGentleman-Programming:mainfrom
jgarrone82:fix/269-mcp-update-check-async
Closed

fix(mcp): run checkForUpdates async for mcp and serve subcommands#273
jgarrone82 wants to merge 2 commits intoGentleman-Programming:mainfrom
jgarrone82:fix/269-mcp-update-check-async

Conversation

@jgarrone82
Copy link
Copy Markdown
Contributor

@jgarrone82 jgarrone82 commented Apr 28, 2026

Problem

checkForUpdates runs synchronously in main() before cmdMCP is called (cmd/engram/main.go:534). This means the MCP server does not start listening until the GitHub API call completes or times out.

// main.go:534 — runs BEFORE cmdMCP
if result := checkForUpdates(version); ... {
    fmt.Fprintln(os.Stderr, result.Message)
}
cmdMCP(cfg) // ← never reached until the HTTP call above finishes

Root cause

  • Uses http.DefaultClient with no transport-level timeout (only a 2s context.WithTimeout)
  • On Windows, DNS resolution and TCP operations can hang at the OS level beyond the context deadline — Go cancels the context but the underlying WSARecv call may not unblock immediately
  • Claude Code has a stream idle timeout of idleDeadlineMs=300000 (5 minutes). If MCP never starts responding, Claude Code waits the full 5 minutes then kills the connection

Observed impact

  • Measured MCP startup of 6417ms vs 1775ms baseline in debug logs (Windows 11)
  • Suspected root cause of intermittent 5-minute hangs reported by users on Windows

Fix

Run the update check in a goroutine so cmdMCP starts immediately:

go func() {
    if result := checkForUpdates(version); result.Status != versioncheck.StatusUpToDate && result.Message != "" {
        fmt.Fprintln(os.Stderr, result.Message)
    }
}()
cmdMCP(cfg)

Interactive subcommands (save, search, tui, etc.) keep the synchronous check so the update message appears before command output.

Environment

Closes #269

Jorge Ariel Garrone added 2 commits April 28, 2026 08:34
Moves the update check to a goroutine for long-running server subcommands
(mcp, serve) so the server starts immediately without blocking on a GitHub
API network request.

On Windows, http.DefaultClient with a Go context timeout does not guarantee
the underlying OS socket (WSARecv) is cancelled within the deadline. With
Claude Code's stream idle timeout of 300s, a hung update check means MCP
never starts and the client waits the full 5 minutes before giving up.

Interactive subcommands (save, search, tui, etc.) keep the synchronous
check so the update message appears before command output.

Closes Gentleman-Programming#269
@Alan-TheGentleman
Copy link
Copy Markdown
Collaborator

Thanks for chasing #269. The direction is right, but I am closing this PR rather than merging the stale branch as-is because I am rebuilding the startup fix on current main with the rest of the issue batch. I will reference this PR in the replacement work so the original context is not lost. Closing the PR, not rejecting the underlying fix.

@jgarrone82 jgarrone82 deleted the fix/269-mcp-update-check-async branch April 29, 2026 06:49
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.

bug(mcp): checkForUpdates blocks MCP startup on every invocation

2 participants