feat: opt-in heartbeat liveness detection with fail-fast connection errors #4

Merged
w33ble merged 6 commits from feat/heartbeat into main 2026-09-29 12:03:53 -06:00
Collaborator

Summary

  • Opt-in heartbeat liveness detection (--heartbeat, --heartbeat-interval, --heartbeat-timeout): idle connections get an MCP ping, a missed pong drops into the normal reconnect path. Ping responses are never forwarded to stdout.
  • Reconnect with backoff and session re-establishment: replays stored initialize, flushes buffered stdin, honors --max-disconnected-time / --initial-retry-interval.
  • Fail fast instead of hanging: probe 401/403 is a fatal auth error, probe 404 reports endpoint-not-found, probe 5xx reports remote server error; auth failures on write/replay paths bypass the retry loop; any failure before the first live session exits non-zero.

Why

With bad/missing credentials the bridge logged connected, got Forbidden on the initialize write, then retried forever without ever answering the client, so mcp-cli timed out and appeared hung (reproduced against the pit endpoint). Failures now surface immediately on stderr with a clear message; post-connect transient drops still retry as before.

Verification

go build ./..., go vet ./..., golangci-lint run (0 issues), go test ./... all pass. Verified live via mcp-cli against the pit endpoint: whoami returns the client identity.

## Summary - Opt-in heartbeat liveness detection (`--heartbeat`, `--heartbeat-interval`, `--heartbeat-timeout`): idle connections get an MCP ping, a missed pong drops into the normal reconnect path. Ping responses are never forwarded to stdout. - Reconnect with backoff and session re-establishment: replays stored `initialize`, flushes buffered stdin, honors `--max-disconnected-time` / `--initial-retry-interval`. - Fail fast instead of hanging: probe 401/403 is a fatal auth error, probe 404 reports endpoint-not-found, probe 5xx reports remote server error; auth failures on write/replay paths bypass the retry loop; any failure before the first live session exits non-zero. ## Why With bad/missing credentials the bridge logged `connected`, got `Forbidden` on the `initialize` write, then retried forever without ever answering the client, so mcp-cli timed out and appeared hung (reproduced against the pit endpoint). Failures now surface immediately on stderr with a clear message; post-connect transient drops still retry as before. ## Verification `go build ./...`, `go vet ./...`, `golangci-lint run` (0 issues), `go test ./...` all pass. Verified live via mcp-cli against the pit endpoint: `whoami` returns the client identity.
feat: opt-in heartbeat liveness detection
All checks were successful
CI / Build, vet, test, lint (pull_request) Successful in 57s
32e4ecea26
Disabled by default. With --heartbeat, the bridge pings the
remote after --heartbeat-interval (default 30s) with no traffic
in either direction; a pong missing past --heartbeat-timeout
(default 10s) drops the connection into the normal reconnect
path. Ping responses are matched by exact ID and never forwarded
to the client. All flags have MCP_BRIDGE_* env fallbacks.
test: cover heartbeat tick, abandon, and validation paths
All checks were successful
CI / Build, vet, test, lint (pull_request) Successful in 1m0s
0405595e45
fix: fail fast on auth and probe failures instead of retrying forever
All checks were successful
CI / Build, vet, test, lint (pull_request) Successful in 2m50s
bd871dddce
w33ble approved these changes 2026-09-29 12:02:20 -06:00
w33ble merged commit ff8d6fbd2b into main 2026-09-29 12:03:53 -06:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
w33ble/mcp-ferryman!4
No description provided.