Skip to content

Commit 1039c17

Browse files
lpcoxCopilot
andcommitted
fix: address PR review feedback
- Remove session ID from HTTP connection log line to avoid leaking sensitive data (use TruncateSessionID pattern) - Wire keepalive_interval into stdin JSON config path via StdinGatewayConfig.KeepaliveInterval and convertStdinConfig - Add nil guard to GatewayConfig.HTTPKeepaliveInterval() - Close connections during pool eviction instead of just marking state - Make Connection.Close() nil-safe for cancel function - Consolidate duplicate keepalive constants: remove DefaultHTTPKeepaliveInterval from mcp package, use single source of truth in config.DefaultKeepaliveInterval Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 97149f0 commit 1039c17

6 files changed

Lines changed: 40 additions & 33 deletions

File tree

internal/config/config_core.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ type GatewayConfig struct {
122122
// HTTPKeepaliveInterval returns the keepalive interval as a time.Duration.
123123
// A negative KeepaliveInterval disables keepalive (returns 0).
124124
func (g *GatewayConfig) HTTPKeepaliveInterval() time.Duration {
125+
if g == nil {
126+
return time.Duration(DefaultKeepaliveInterval) * time.Second
127+
}
125128
if g.KeepaliveInterval < 0 {
126129
return 0
127130
}

internal/config/config_stdin.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ type StdinConfig struct {
3232
// StdinGatewayConfig represents gateway configuration in stdin JSON format.
3333
// Uses pointers for optional fields to distinguish between unset and zero values.
3434
type StdinGatewayConfig struct {
35-
Port *int `json:"port,omitempty"`
36-
APIKey string `json:"apiKey,omitempty"`
37-
Domain string `json:"domain,omitempty"`
38-
StartupTimeout *int `json:"startupTimeout,omitempty"`
39-
ToolTimeout *int `json:"toolTimeout,omitempty"`
40-
PayloadDir string `json:"payloadDir,omitempty"`
41-
TrustedBots []string `json:"trustedBots,omitempty"`
35+
Port *int `json:"port,omitempty"`
36+
APIKey string `json:"apiKey,omitempty"`
37+
Domain string `json:"domain,omitempty"`
38+
StartupTimeout *int `json:"startupTimeout,omitempty"`
39+
ToolTimeout *int `json:"toolTimeout,omitempty"`
40+
KeepaliveInterval *int `json:"keepaliveInterval,omitempty"`
41+
PayloadDir string `json:"payloadDir,omitempty"`
42+
TrustedBots []string `json:"trustedBots,omitempty"`
4243
}
4344

4445
// StdinGuardConfig represents a guard configuration in stdin JSON format.
@@ -278,11 +279,12 @@ func convertStdinConfig(stdinCfg *StdinConfig) (*Config, error) {
278279
// Convert gateway config with defaults
279280
if stdinCfg.Gateway != nil {
280281
cfg.Gateway = &GatewayConfig{
281-
Port: intPtrOrDefault(stdinCfg.Gateway.Port, DefaultPort),
282-
APIKey: stdinCfg.Gateway.APIKey,
283-
Domain: stdinCfg.Gateway.Domain,
284-
StartupTimeout: intPtrOrDefault(stdinCfg.Gateway.StartupTimeout, DefaultStartupTimeout),
285-
ToolTimeout: intPtrOrDefault(stdinCfg.Gateway.ToolTimeout, DefaultToolTimeout),
282+
Port: intPtrOrDefault(stdinCfg.Gateway.Port, DefaultPort),
283+
APIKey: stdinCfg.Gateway.APIKey,
284+
Domain: stdinCfg.Gateway.Domain,
285+
StartupTimeout: intPtrOrDefault(stdinCfg.Gateway.StartupTimeout, DefaultStartupTimeout),
286+
ToolTimeout: intPtrOrDefault(stdinCfg.Gateway.ToolTimeout, DefaultToolTimeout),
287+
KeepaliveInterval: intPtrOrDefault(stdinCfg.Gateway.KeepaliveInterval, DefaultKeepaliveInterval),
286288
}
287289
if stdinCfg.Gateway.PayloadDir != "" {
288290
cfg.Gateway.PayloadDir = stdinCfg.Gateway.PayloadDir

internal/launcher/connection_pool.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const (
4242
// DefaultIdleTimeout is the maximum duration a STDIO-backend connection can remain unused
4343
// before being removed from the session pool. Set to 6 hours to accommodate long-running
4444
// workflow tasks (e.g. ML training, large builds) that may not make MCP calls for extended
45-
// periods. Note: this is distinct from HTTP backend keepalive (DefaultHTTPKeepaliveInterval)
45+
// periods. Note: this is distinct from HTTP backend keepalive (config.DefaultKeepaliveInterval)
4646
// which keeps the remote session alive on the HTTP server side; STDIO connections run as local
4747
// child processes whose sessions are bounded only by this pool eviction window.
4848
DefaultIdleTimeout = 6 * time.Hour
@@ -158,10 +158,12 @@ func (p *SessionConnectionPool) cleanupIdleConnections() {
158158
logPool.Printf("Cleaning up connection: backend=%s, session=%s, reason=%s, idle=%v, errors=%d",
159159
key.BackendID, key.SessionID, reason, now.Sub(metadata.LastUsedAt), metadata.ErrorCount)
160160

161-
// Close the connection if still active
161+
// Close the underlying connection to release resources (cancel context, close SDK session)
162162
if metadata.Connection != nil && metadata.State != ConnectionStateClosed {
163-
// Note: mcp.Connection doesn't have a Close method in current implementation
164-
// but we mark it as closed
163+
if err := metadata.Connection.Close(); err != nil {
164+
logPool.Printf("Error closing connection during cleanup: backend=%s, session=%s, err=%v",
165+
key.BackendID, key.SessionID, err)
166+
}
165167
metadata.State = ConnectionStateClosed
166168
}
167169

internal/mcp/connection.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,9 @@ func (c *Connection) getPrompt(params interface{}) (*Response, error) {
670670
// Close closes the connection
671671
func (c *Connection) Close() error {
672672
logConn.Printf("Closing connection: serverID=%s, isHTTP=%v", c.serverID, c.isHTTP)
673-
c.cancel()
673+
if c.cancel != nil {
674+
c.cancel()
675+
}
674676
if session := c.getSDKSession(); session != nil {
675677
return session.Close()
676678
}

internal/mcp/connection_test.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -521,18 +521,20 @@ func TestNewMCPClientWithLogger(t *testing.T) {
521521

522522
// TestNewMCPClientWithKeepalive tests that newMCPClient accepts a keepalive interval
523523
func TestNewMCPClientWithKeepalive(t *testing.T) {
524-
client := newMCPClient(nil, DefaultHTTPKeepaliveInterval)
524+
keepAlive := time.Duration(config.DefaultKeepaliveInterval) * time.Second
525+
client := newMCPClient(nil, keepAlive)
525526
require.NotNil(t, client, "newMCPClient should return a non-nil client with keepalive")
526527
}
527528

528-
// TestDefaultHTTPKeepaliveInterval verifies the keepalive constant is less than a typical
529+
// TestDefaultKeepaliveInterval verifies the config keepalive default is less than a typical
529530
// backend session timeout (30 minutes) to prevent session expiry during long agent runs.
530-
func TestDefaultHTTPKeepaliveInterval(t *testing.T) {
531+
func TestDefaultKeepaliveInterval(t *testing.T) {
531532
const typicalBackendTimeout = 30 * time.Minute
532-
assert.Less(t, DefaultHTTPKeepaliveInterval, typicalBackendTimeout,
533-
"DefaultHTTPKeepaliveInterval must be less than the typical backend session timeout to prevent expiry")
534-
assert.Greater(t, DefaultHTTPKeepaliveInterval, time.Duration(0),
535-
"DefaultHTTPKeepaliveInterval must be positive")
533+
keepAlive := time.Duration(config.DefaultKeepaliveInterval) * time.Second
534+
assert.Less(t, keepAlive, typicalBackendTimeout,
535+
"DefaultKeepaliveInterval must be less than the typical backend session timeout to prevent expiry")
536+
assert.Greater(t, keepAlive, time.Duration(0),
537+
"DefaultKeepaliveInterval must be positive")
536538
}
537539

538540
// TestNewHTTPConnectionStoresKeepalive verifies that the keepalive interval is stored on
@@ -541,15 +543,16 @@ func TestNewHTTPConnectionStoresKeepalive(t *testing.T) {
541543
ctx, cancel := context.WithCancel(context.Background())
542544
defer cancel()
543545

544-
client := newMCPClient(nil, DefaultHTTPKeepaliveInterval)
546+
keepAlive := time.Duration(config.DefaultKeepaliveInterval) * time.Second
547+
client := newMCPClient(nil, keepAlive)
545548
url := "http://example.com/mcp"
546549
headers := map[string]string{}
547550
httpClient := &http.Client{}
548551

549-
conn := newHTTPConnection(ctx, cancel, client, nil, url, headers, httpClient, HTTPTransportStreamable, "test-server", DefaultHTTPKeepaliveInterval)
552+
conn := newHTTPConnection(ctx, cancel, client, nil, url, headers, httpClient, HTTPTransportStreamable, "test-server", keepAlive)
550553

551554
require.NotNil(t, conn)
552-
assert.Equal(t, DefaultHTTPKeepaliveInterval, conn.keepAliveInterval,
555+
assert.Equal(t, keepAlive, conn.keepAliveInterval,
553556
"keepAliveInterval should be stored on the connection for use during reconnection")
554557
}
555558

internal/mcp/http_transport.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ const (
3636
// MCPProtocolVersion is the MCP protocol version used in initialization requests.
3737
const MCPProtocolVersion = "2025-11-25"
3838

39-
// DefaultHTTPKeepaliveInterval is the default interval for sending keepalive pings to HTTP backends.
40-
// This should be less than the backend's session idle timeout (typically 30 minutes for safeoutputs).
41-
// A value of 25 minutes gives a 5-minute buffer before the session would expire.
42-
const DefaultHTTPKeepaliveInterval = 25 * time.Minute
43-
4439
// requestIDCounter is used to generate unique request IDs for HTTP requests
4540
var requestIDCounter uint64
4641

@@ -196,7 +191,7 @@ func newHTTPConnection(ctx context.Context, cancel context.CancelFunc, client *s
196191
if session != nil {
197192
sessionID = session.ID()
198193
}
199-
logHTTP.Printf("Creating HTTP connection: serverID=%s, url=%s, transport=%s, headers=%d, sessionID=%s, keepAlive=%v", serverID, url, transportType, len(headers), sessionID, keepAlive)
194+
logHTTP.Printf("Creating HTTP connection: serverID=%s, url=%s, transport=%s, headers=%d, keepAlive=%v", serverID, url, transportType, len(headers), keepAlive)
200195
return &Connection{
201196
client: client,
202197
session: session,

0 commit comments

Comments
 (0)