Repository navigation
[duplicate-code] Duplicate Code Pattern: Backend Request Execution in Prompt Handler #8122
Description
Part of duplicate code analysis: #8121
Summary
The prompt handler closure introduced in internal/server/tool_registry.go by the prompt passthrough feature duplicates the exact same pattern that executeBackendToolCall in internal/server/unified.go was created to centralize. Each prompt registered by registerPromptsFromBackend independently reimplements: get connection → send backend request → check RPC error → unmarshal result.
Duplication Details
Pattern: Backend Request Execution (connect → send → error-check → unmarshal)
- Severity: Medium
- Occurrences: 2 (+ the established
executeBackendToolCallhelper that was meant to own this) - Locations:
internal/server/tool_registry.go(lines 415–439): prompt handler closure insideregisterPromptsFromBackendinternal/server/unified.go(lines 214–241):executeBackendToolCallfunction (existing, purpose-built helper)
Prompt handler (new, duplicated pattern):
sessionID := us.getSessionID(ctx)
backendConn, connErr := launcher.GetOrLaunchForSession(us.launcher, serverIDCopy, sessionID)
if connErr != nil {
return nil, fmt.Errorf("failed to connect to backend %s: %w", serverIDCopy, connErr)
}
params := map[string]interface{}{
"name": promptNameCopy,
"arguments": req.Params.Arguments,
}
response, reqErr := backendConn.SendRequestWithServerID(ctx, "prompts/get", params, serverIDCopy)
if reqErr != nil {
return nil, reqErr
}
if response.Error != nil {
return nil, fmt.Errorf("backend error for prompts/get %s/%s: code=%d, message=%s",
serverIDCopy, promptNameCopy, response.Error.Code, response.Error.Message)
}
var promptResult sdk.GetPromptResult
if unmarshErr := json.Unmarshal(response.Result, &promptResult); unmarshErr != nil {
return nil, fmt.Errorf("failed to parse prompt result: %w", unmarshErr)
}
return &promptResult, nilexecuteBackendToolCall (existing, centralizing helper):
conn, err := launcher.GetOrLaunchForSession(l, serverID, sessionID)
if err != nil {
return nil, fmt.Errorf("failed to connect: %w", err)
}
response, err := conn.SendRequestWithServerID(ctx, "tools/call", map[string]interface{}{...}, 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, nilImpact Analysis
- Maintainability: Error handling and connection logic are now in two separate places. A fix (e.g., improved error messages, retry logic, logging) applied to
executeBackendToolCallwill not be applied to the prompt handler. - Bug Risk: The two implementations already diverge:
executeBackendToolCalllogscode=%ddetails before returning; the prompt handler does not. Future divergence is likely. - Code Bloat: Each new backend capability type (e.g.,
resources/read) added following the prompt handler pattern will add ~20 lines of duplicated backend-call boilerplate.
Refactoring Recommendations
-
Extract a generic
executeBackendRequest[T any]helper ininternal/server/unified.go:// executeBackendRequest sends a JSON-RPC request to a backend and unmarshals the result // into T. It centralizes: GetOrLaunchForSession → SendRequestWithServerID → error check → unmarshal. func executeBackendRequest[T any](ctx context.Context, l *launcher.Launcher, serverID, sessionID, method string, params map[string]interface{}) (T, error) { var zero T conn, err := launcher.GetOrLaunchForSession(l, serverID, sessionID) if err != nil { return zero, fmt.Errorf("failed to connect to backend %s: %w", serverID, err) } response, err := conn.SendRequestWithServerID(ctx, method, params, serverID) if err != nil { return zero, err } if response.Error != nil { return zero, fmt.Errorf("backend error %s: code=%d, message=%s", method, response.Error.Code, response.Error.Message) } var result T if err := json.Unmarshal(response.Result, &result); err != nil { return zero, fmt.Errorf("failed to parse %s result: %w", method, err) } return result, nil }
-
Refactor
executeBackendToolCallto delegate to the new helper (preserving the existing public API). -
Update the prompt handler in
registerPromptsFromBackendto call:result, err := executeBackendRequest[sdk.GetPromptResult](ctx, us.launcher, serverIDCopy, sessionID, "prompts/get", params)
Implementation Checklist
- Add
executeBackendRequest[T any]generic helper ininternal/server/unified.go - Refactor
executeBackendToolCallto delegate toexecuteBackendRequest - Update prompt handler in
registerPromptsFromBackendto useexecuteBackendRequest - Verify tests pass:
make test - Confirm no behaviour change for existing tool and prompt calls
Parent Issue
See parent analysis report: #8121
Related to #8121
Generated by Duplicate Code Detector · 698.6 AIC · ⊞ 35.7K · ◷
- expires on Jul 3, 2026, 3:44 AM UTC
Activity
github-actions commentedon Jun 27, 2026
🤖 This is an automated response from Repo Assist.
Verified. The prompt handler closure in
tool_registry.go(lines 415–439) does indeed duplicate the connect→send→error-check→unmarshal pattern thatexecuteBackendToolCallinunified.gowas built to own.One divergence already present:
executeBackendToolCalllogscode=%ddetails before returning the error; the prompt handler constructs a more descriptive message (backend error for prompts/get %s/%s) but omits the structured log call. Any future error-handling improvements (e.g., retries, timeout context) applied toexecuteBackendToolCallwould need to be separately applied to the prompt handler.The generic
executeBackendRequest[T any]helper is a sound approach for Go 1.21+. The constraintTdoesn't need to be bounded beyond whatjson.Unmarshalaccepts, soanyworks fine. The main integration question is whetherexecuteBackendToolCallitself can be refactored to delegate toexecuteBackendRequestcleanly — it currently has some logging that would need to be preserved.Happy to implement this if it would help move things forward.
Add this agentic workflows to your repo
To install this agentic workflow, run