Part of duplicate code analysis: #1661
Summary
Three functions in internal/server/unified.go contain a near-identical block for executing a backend MCP tool call over a session-aware connection. The shared logic — launching/getting a connection, sending a tools/call request, checking the error response, and unmarshalling the JSON result — is ~18 lines long and diverges only in return types and the logging context.
Duplication Details
Pattern: launch connection → SendRequestWithServerID → check error → unmarshal result
- Severity: High
- Occurrences: 3
- Locations:
internal/server/unified.go lines 569–604 (guardBackendCaller.CallTool)
internal/server/unified.go lines 703–730 (callBackendTool, Phase 3)
internal/server/unified.go lines 269–295 (registerToolsFromBackend, partial – uses tools/list)
Code in guardBackendCaller.CallTool (lines 569–604):
conn, err := launcher.GetOrLaunchForSession(g.server.launcher, g.serverID, sessionID.(string))
if err != nil {
return nil, fmt.Errorf("failed to connect: %w", err)
}
response, err := conn.SendRequestWithServerID(g.ctx, "tools/call", map[string]interface{}{
"name": toolName,
"arguments": args,
}, g.serverID)
if err != nil {
return nil, err
}
if response.Error != nil {
return nil, fmt.Errorf("backend error: code=%d, message=%s", response.Error.Code, response.Error.Message)
}
var result interface{}
if err := json.Unmarshal(response.Result, &result); err != nil {
return nil, fmt.Errorf("failed to parse result: %w", err)
}
return result, nil
Nearly identical block in callBackendTool Phase 3 (lines 703–730):
conn, err := launcher.GetOrLaunchForSession(us.launcher, serverID, sessionID)
if err != nil {
return newErrorCallToolResult(fmt.Errorf("failed to connect: %w", err))
}
response, err := conn.SendRequestWithServerID(ctx, "tools/call", map[string]interface{}{
"name": toolName,
"arguments": args,
}, serverID)
if err != nil {
return newErrorCallToolResult(err)
}
if response.Error != nil {
return newErrorCallToolResult(fmt.Errorf("backend error: code=%d, message=%s", response.Error.Code, response.Error.Message))
}
var backendResult interface{}
if err := json.Unmarshal(response.Result, &backendResult); err != nil {
return newErrorCallToolResult(fmt.Errorf("failed to parse result: %w", err))
}
The only structural differences are:
- Return type:
(interface{}, error) vs (*sdk.CallToolResult, interface{}, error)
- Error wrapper:
return nil, err vs return newErrorCallToolResult(err)
- Session resolution:
g.ctx.Value(...) vs us.getSessionID(ctx) (already resolved before the block)
Impact Analysis
- Maintainability: Any change to error message format, response error handling, or JSON unmarshal logic must be applied in both places. The current identical error string
"backend error: code=%d, message=%s" is already evidence of copy-paste.
- Bug Risk: Medium — a future fix to one branch (e.g., adding retry logic or telemetry) is likely to miss the other.
- Code Bloat: ~18 lines duplicated per instance.
Refactoring Recommendations
-
Extract executeBackendToolCall helper
- Location:
internal/server/unified.go (or new internal/server/backend_call.go)
- Signature:
func executeBackendToolCall(ctx context.Context, l *launcher.Launcher, serverID, toolName string, args interface{}) (interface{}, error)
- Returns raw
(interface{}, error) — the callers adapt the result to their specific return types.
guardBackendCaller.CallTool can call this directly.
callBackendTool calls this, then applies DIFC Phase 4–5 on top.
- Estimated effort: ~1 hour, low risk.
-
Align session ID resolution before the shared block
guardBackendCaller.CallTool extracts the session ID inline via g.ctx.Value(...).
- Extracting this to before the helper call aligns with how
callBackendTool already calls us.getSessionID(ctx) first.
Implementation Checklist
Parent Issue
See parent analysis report: #1661
Related to #1661
Generated by Duplicate Code Detector
Part of duplicate code analysis: #1661
Summary
Three functions in
internal/server/unified.gocontain a near-identical block for executing a backend MCP tool call over a session-aware connection. The shared logic — launching/getting a connection, sending atools/callrequest, checking the error response, and unmarshalling the JSON result — is ~18 lines long and diverges only in return types and the logging context.Duplication Details
Pattern: launch connection → SendRequestWithServerID → check error → unmarshal result
internal/server/unified.golines 569–604 (guardBackendCaller.CallTool)internal/server/unified.golines 703–730 (callBackendTool, Phase 3)internal/server/unified.golines 269–295 (registerToolsFromBackend, partial – usestools/list)Code in
guardBackendCaller.CallTool(lines 569–604):Nearly identical block in
callBackendToolPhase 3 (lines 703–730):The only structural differences are:
(interface{}, error)vs(*sdk.CallToolResult, interface{}, error)return nil, errvsreturn newErrorCallToolResult(err)g.ctx.Value(...)vsus.getSessionID(ctx)(already resolved before the block)Impact Analysis
"backend error: code=%d, message=%s"is already evidence of copy-paste.Refactoring Recommendations
Extract
executeBackendToolCallhelperinternal/server/unified.go(or newinternal/server/backend_call.go)func executeBackendToolCall(ctx context.Context, l *launcher.Launcher, serverID, toolName string, args interface{}) (interface{}, error)(interface{}, error)— the callers adapt the result to their specific return types.guardBackendCaller.CallToolcan call this directly.callBackendToolcalls this, then applies DIFC Phase 4–5 on top.Align session ID resolution before the shared block
guardBackendCaller.CallToolextracts the session ID inline viag.ctx.Value(...).callBackendToolalready callsus.getSessionID(ctx)first.Implementation Checklist
executeBackendToolCall(ctx, launcher, serverID, toolName, args)helperguardBackendCaller.CallToolto use helpercallBackendToolPhase 3 to use helperregisterToolsFromBackendpattern (usestools/list— only first half overlaps; can share connect+error pattern)Parent Issue
See parent analysis report: #1661
Related to #1661