Conversation
Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/676bd495-37a5-4043-81c2-368d29e3d51a Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR removes the hardcoded http.Client.Timeout cap from HTTP MCP backend connections so that long-running backend tools/call executions are governed by request context deadlines (e.g., the gateway toolTimeout) rather than a fixed ~120s transport timeout.
Changes:
- Removed the hardcoded
http.Client.TimeoutfromNewHTTPConnection, keepingconnect_timeout-based connect/setup bounds in the transport. - Updated the HTTP connection unit test to assert the client-level timeout is unset (
0) instead of120s. - Clarified documentation around
connect_timeoutvstoolTimeout.
Show a summary per file
| File | Description |
|---|---|
internal/mcp/connection.go |
Removes the hardcoded HTTP client overall timeout to defer end-to-end timing to request contexts. |
internal/mcp/http_connection_test.go |
Updates/renames the unit test to validate the client timeout is unset. |
test/integration/http_error_test.go |
Adjusts skipped test messaging/comments to remove references to a 120s client timeout. |
docs/CONFIGURATION.md |
Clarifies semantics of connect_timeout and toolTimeout for HTTP backends. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
internal/mcp/connection.go:220
- Removing http.Client.Timeout makes request cancellation depend entirely on contexts, but several HTTP/SKD code paths still use a non-deadlined context (e.g., SendRequest delegates with context.Background, SDK transports pass c.ctx into ListTools/CallTool, and plain JSON initialize uses context.Background()). This can both (a) make toolTimeout budgets ineffective for streamable/SSE HTTP backends and (b) allow hung requests to block indefinitely. Consider threading the per-request ctx from SendRequestWithServerID into all SDK session calls and plain-JSON initialization, and/or ensuring callers always supply a context with a deadline when invoking backend requests (including tools/list during startup).
// Create an HTTP client with connect/setup timeouts.
// Do not set http.Client.Timeout: request execution should be controlled by
// per-request context deadlines (for example the gateway tool timeout budget),
// while connectTimeout continues to bound transport establishment/fallback.
httpClient := &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: connectTimeout,
}).DialContext,
MaxIdleConns: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: connectTimeout,
},
- Files reviewed: 4/4 changed files
- Comments generated: 2
| // Verify overall HTTP client timeout is unset. End-to-end request budgets | ||
| // should come from context deadlines, not a hardcoded client timeout. | ||
| assert.Zero(t, conn.httpClient.Timeout) |
There was a problem hiding this comment.
This test asserts the client-level timeout is zero, but it doesn't verify the replacement behavior: that request context deadlines actually cap long-running backend calls. Adding a test that issues a request with a short context timeout and confirms it aborts (for both plain JSON-RPC and an SDK transport, if feasible) would guard against regressions now that there is no transport-wide timeout.
| | `domain` | Gateway domain (`"localhost"`, `"host.docker.internal"`, or `"${VAR}"`) | (unset) | | ||
| | `startupTimeout` | Seconds to wait for backend startup | `30` | | ||
| | `toolTimeout` | Seconds to wait for tool execution | `60` | | ||
| | `toolTimeout` | Seconds to wait for tool execution (applies to backend request execution via request context deadlines, including HTTP backends) | `60` | |
There was a problem hiding this comment.
The doc now states toolTimeout applies to backend request execution via request context deadlines, including HTTP backends. That only holds if those per-request contexts are actually propagated into all backend request paths (notably SDK-based HTTP transports and tools/list calls during startup). If some paths still use a connection-scoped context or context.Background(), this line may overpromise; consider clarifying the scope or updating the implementation to ensure toolTimeout consistently governs HTTP backends.
| | `toolTimeout` | Seconds to wait for tool execution (applies to backend request execution via request context deadlines, including HTTP backends) | `60` | | |
| | `toolTimeout` | Seconds to wait for tool execution and backend requests that honor per-request context deadlines | `60` | |
The ToolTimeout config value was never consumed to create a context deadline — removing http.Client.Timeout (120s) would leave HTTP backend calls unbounded. Fix by adding context.WithTimeout(ctx, toolTimeout) in callBackendTool as the primary enforcement point. - Add context.WithTimeout in callBackendTool using cfg.Gateway.ToolTimeout - Nil-check cfg.Gateway before accessing ToolTimeout (it's a pointer) - Add TestCallBackendTool_ToolTimeoutEnforcedViaContext proving deadline - Update CONFIGURATION.md to accurately describe enforcement scope Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lpcox
left a comment
There was a problem hiding this comment.
Review & Fix Summary
The Copilot reviewer's suppressed comment about context propagation was actually critical — ToolTimeout was config-only and never consumed to create a context deadline. Removing http.Client.Timeout (120s) without wiring toolTimeout would leave HTTP backend calls unbounded.
Fix (32b8c76)
- Added
context.WithTimeout(ctx, toolTimeout)incallBackendToolas the primary enforcement point - Nil-checks
cfg.Gatewaybefore accessingToolTimeout(it's a pointer) - Added
TestCallBackendTool_ToolTimeoutEnforcedViaContextproving context deadline cancellation works (mock backend sleeps 3s, timeout is 1s → deadline exceeded) - Updated
CONFIGURATION.mdto accurately describe enforcement scope
All checks pass (make agent-finished ✓).
HTTP MCP backend
tools/callrequests were effectively capped at ~120s due to a hardcodedhttp.Client.Timeout, even when gateway/tool budgets were configured higher. This madetoolTimeoutnon-authoritative for long-running HTTP backend calls and surfaced misleading transport timeout failures.Timeout model correction (HTTP backend transport)
http.Client.TimeoutfromNewHTTPConnection.connect_timeoutbehavior for connection/setup phases (DialContext,ResponseHeaderTimeout).toolTimeoutbudgets), not a fixed transport ceiling.Behavioral test update
0) instead of120s, matching context-driven execution timeout semantics.Docs clarification
connect_timeoutis per-attempt transport setup/fallback timeout.toolTimeoutgoverns tool execution budget for backend requests, including HTTP backends.Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
example.com/tmp/go-build915870139/b514/launcher.test /tmp/go-build915870139/b514/launcher.test -test.testlogfile=/tmp/go-build915870139/b514/testlog.txt -test.paniconexit0 -test.timeout=10m0s .cfg�� ache/go/1.25.8/x-errorsas 64/src/text/temp-ifaceassert x_amd64/vet -p vendor/golang.or-o -lang=go1.25 x_amd64/vet om/s�� .cfg -I x_amd64/vet --gdwarf-5 7082204/b245/ -o x_amd64/vet(dns block)/tmp/go-build1119629889/b514/launcher.test /tmp/go-build1119629889/b514/launcher.test -test.testlogfile=/tmp/go-build1119629889/b514/testlog.txt -test.paniconexit0 -test.timeout=10m0s EN3 io.containerd.ru/run/containerd/io.containerd.runtime.v2.task/moby/f51a7715e232ec8a9315d8b288126git x_amd64/vet /usr/bin/bash by/ae163377ad462bash(dns block)invalid-host-that-does-not-exist-12345.com/tmp/go-build915870139/b496/config.test /tmp/go-build915870139/b496/config.test -test.testlogfile=/tmp/go-build915870139/b496/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp/go-build915870139/b396/vet.cfg @v1.1.3/cpu/cpu.go om/tetratelabs/wazero@v1.11.0/internal/platform/google.golang.org/grpc/internal/serviceconfig x_amd64/vet -pthread ternal/engine/wa-atomic =0 x_amd64/vet Db3z�� 7082204/b252/_pk-errorsas 7082204/b151/ x_amd64/vet abis telabs/wazero/in-atomic p=/opt/hostedtoo-bool x_amd64/vet(dns block)nonexistent.local/tmp/go-build915870139/b514/launcher.test /tmp/go-build915870139/b514/launcher.test -test.testlogfile=/tmp/go-build915870139/b514/testlog.txt -test.paniconexit0 -test.timeout=10m0s .cfg�� ache/go/1.25.8/x-errorsas 64/src/text/temp-ifaceassert x_amd64/vet -p vendor/golang.or-o -lang=go1.25 x_amd64/vet om/s�� .cfg -I x_amd64/vet --gdwarf-5 7082204/b245/ -o x_amd64/vet(dns block)/tmp/go-build1119629889/b514/launcher.test /tmp/go-build1119629889/b514/launcher.test -test.testlogfile=/tmp/go-build1119629889/b514/testlog.txt -test.paniconexit0 -test.timeout=10m0s EN3 io.containerd.ru/run/containerd/io.containerd.runtime.v2.task/moby/f51a7715e232ec8a9315d8b288126git x_amd64/vet /usr/bin/bash by/ae163377ad462bash(dns block)slow.example.com/tmp/go-build915870139/b514/launcher.test /tmp/go-build915870139/b514/launcher.test -test.testlogfile=/tmp/go-build915870139/b514/testlog.txt -test.paniconexit0 -test.timeout=10m0s .cfg�� ache/go/1.25.8/x-errorsas 64/src/text/temp-ifaceassert x_amd64/vet -p vendor/golang.or-o -lang=go1.25 x_amd64/vet om/s�� .cfg -I x_amd64/vet --gdwarf-5 7082204/b245/ -o x_amd64/vet(dns block)/tmp/go-build1119629889/b514/launcher.test /tmp/go-build1119629889/b514/launcher.test -test.testlogfile=/tmp/go-build1119629889/b514/testlog.txt -test.paniconexit0 -test.timeout=10m0s EN3 io.containerd.ru/run/containerd/io.containerd.runtime.v2.task/moby/f51a7715e232ec8a9315d8b288126git x_amd64/vet /usr/bin/bash by/ae163377ad462bash(dns block)this-host-does-not-exist-12345.com/tmp/go-build915870139/b523/mcp.test /tmp/go-build915870139/b523/mcp.test -test.testlogfile=/tmp/go-build915870139/b523/testlog.txt -test.paniconexit0 -test.timeout=10m0s 64/s�� .cfg -I x_amd64/vet --gdwarf-5 .io/otel -o x_amd64/vet .cfg�� /oidc/errors.go /oidc/provider.go x_amd64/vet . g/grpc/internal/-qE --64 x_amd64/vet(dns block)/tmp/go-build1119629889/b523/mcp.test /tmp/go-build1119629889/b523/mcp.test -test.testlogfile=/tmp/go-build1119629889/b523/testlog.txt -test.paniconexit0 -test.timeout=10m0s --ve�� =0 y /usr/bin/docker ntime.v2.task/mogit(dns block)If you need me to access, download, or install something from one of these locations, you can either: