π Duplicate Code Pattern: MCP Connection Reconnect Boilerplate
Part of duplicate code analysis: #7078
Summary
internal/mcp/connection.go contains two reconnect functions β reconnectPlainJSON and reconnectSDKTransport β that share identical structural boilerplate at the start and end of each function body. The unique logic is sandwiched between identical preamble and epilogue code.
Duplication Details
Pattern: Shared lock/log/error preamble and epilogue across two reconnect methods
- Severity: Medium
- Occurrences: 2 functions in the same file
- Locations:
internal/mcp/connection.go β reconnectPlainJSON (approx. lines 321β336)
internal/mcp/connection.go β reconnectSDKTransport (approx. lines 341β396)
- Duplicated lines per function: ~7 structural lines (lock/unlock, log helpers, error return shape)
Code Sample β identical preamble in both functions:
c.sessionMu.Lock()
defer c.sessionMu.Unlock()
logConn.Printf("Session expired, reconnecting ... for serverID=%s", c.serverID)
c.logReconnectStart()
Code Sample β identical epilogue in both functions:
c.logReconnectResult(err)
if err != nil {
return fmt.Errorf("session reconnect failed: %w", err)
}
Impact Analysis
- Maintainability: Any change to the reconnect locking strategy, logging call, or error message must be applied to both functions manually.
- Bug Risk: A future engineer fixing the error message in
reconnectSDKTransport could easily miss reconnectPlainJSON (or vice versa), leading to inconsistent behaviour.
- Code Bloat: Minor β ~7 lines Γ 2 = 14 extra lines in a single file.
Refactoring Recommendations
- Extract a
withReconnectLock helper that accepts the transport-specific logic as a func() error closure
// Suggested addition in connection.go
func (c *Connection) withReconnectLock(transportName string, reconnect func() error) error {
c.sessionMu.Lock()
defer c.sessionMu.Unlock()
logConn.Printf("Session expired, reconnecting %s for serverID=%s", transportName, c.serverID)
c.logReconnectStart()
err := reconnect()
c.logReconnectResult(err)
if err != nil {
return fmt.Errorf("session reconnect failed: %w", err)
}
return nil
}
Then the two existing functions become:
func (c *Connection) reconnectPlainJSON() error {
return c.withReconnectLock("plain JSON-RPC", func() error {
sessionID, err := c.initializeHTTPSession()
if err != nil { return err }
c.httpSessionID = sessionID
return nil
})
}
- Extract to:
internal/mcp/connection.go (same file, new unexported method)
- Estimated effort: 30β60 minutes
- Benefits: Single place to modify reconnect lifecycle; compiler enforces the pattern
Implementation Checklist
Parent Issue
See parent analysis report: #7078
Related to #7078
Generated by Duplicate Code Detector Β· sonnet46 6.1M Β· β·
π Duplicate Code Pattern: MCP Connection Reconnect Boilerplate
Part of duplicate code analysis: #7078
Summary
internal/mcp/connection.gocontains two reconnect functions βreconnectPlainJSONandreconnectSDKTransportβ that share identical structural boilerplate at the start and end of each function body. The unique logic is sandwiched between identical preamble and epilogue code.Duplication Details
Pattern: Shared lock/log/error preamble and epilogue across two reconnect methods
internal/mcp/connection.goβreconnectPlainJSON(approx. lines 321β336)internal/mcp/connection.goβreconnectSDKTransport(approx. lines 341β396)Code Sample β identical preamble in both functions:
Code Sample β identical epilogue in both functions:
Impact Analysis
reconnectSDKTransportcould easily missreconnectPlainJSON(or vice versa), leading to inconsistent behaviour.Refactoring Recommendations
withReconnectLockhelper that accepts the transport-specific logic as afunc() errorclosureinternal/mcp/connection.go(same file, new unexported method)Implementation Checklist
reconnectPlainJSONandreconnectSDKTransportin fullwithReconnectLockhelpermake testto verify no regressionParent Issue
See parent analysis report: #7078
Related to #7078