Skip to content

Commit 8269bae

Browse files
authored
refactor: extract duplicate code patterns into helpers (#1855)
Addresses duplicate code analysis identifying ~50 lines of structural duplication across two patterns in `internal/server/unified.go`. ## Changes ### Pattern 1: Backend Tool Call Execution (~40 lines eliminated) Extracted `executeBackendToolCall` helper consolidating: - Connection management via `launcher.GetOrLaunchForSession` - `tools/call` request dispatch - Backend error checking - Result unmarshaling **Before:** ```go // guardBackendCaller.CallTool and callBackendTool Phase 3 each had: conn, err := launcher.GetOrLaunchForSession(us.launcher, serverID, sessionID) if err != nil { ... } response, err := conn.SendRequestWithServerID(ctx, "tools/call", ...) if err != nil { ... } if response.Error != nil { ... } var result interface{} json.Unmarshal(response.Result, &result) ``` **After:** ```go result, err := executeBackendToolCall(ctx, us.launcher, serverID, sessionID, toolName, args) ``` ### Pattern 2: Sys Tool Delegation (~10 lines eliminated) Extracted `callSysServer` helper consolidating: - Tool name marshaling - `sysServer.HandleRequest` delegation **Before:** ```go // sysInitHandler and sysListHandler each had: params, _ := json.Marshal(map[string]interface{}{ "name": toolName, "arguments": map[string]interface{}{}, }) result, err := us.sysServer.HandleRequest("tools/call", json.RawMessage(params)) ``` **After:** ```go result, err := us.callSysServer("sys_init") ``` ## Impact - Eliminates copy-paste error risk when modifying backend call or sys server logic - Follows existing helper patterns (`newErrorCallToolResult`, `parseToolArguments`) - No functional changes—pure refactoring > [!WARNING] > > <details> > <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary> > > #### I tried to connect to the following addresses, but was blocked by firewall rules: > > - `example.com` > - Triggering command: `/tmp/go-build4242760615/b336/launcher.test /tmp/go-build4242760615/b336/launcher.test -test.testlogfile=/tmp/go-build4242760615/b336/testlog.txt -test.paniconexit0 -test.timeout=10m0s -uns�� ache/go/1.25.7/x64/src/net /tmp/go-build1249286257/b024/vet.cfg ache/go/1.25.7/x64/pkg/tool/linux_amd64/vet unset position.go .12/x64/bin/git ache/go/1.25.7/x64/pkg/tool/linuHEAD -W -I /tmp/go-build1249286257/b165/ ache/go/1.25.7/x64/pkg/tool/linux_amd64/compile . --gdwarf2 --64 ache/go/1.25.7/x64/pkg/tool/linux_amd64/compile` (dns block) > - Triggering command: `/tmp/go-build2320097585/b332/launcher.test /tmp/go-build2320097585/b332/launcher.test -test.testlogfile=/tmp/go-build2320097585/b332/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 64/bin/git ortcfg /usr/sbin/bash submodules | heanode 9286257/b132/vet-v ndor/bin/git bash /usr�� runtime-runc/moby ortcfg csi/net-interface-handler aw-mcpg/internal/opt/hostedtoolcache/go/1.25.7/x64/pkg/tool/linux_amd64/asm aw-mcpg/internal-V=full` (dns block) > - `invalid-host-that-does-not-exist-12345.com` > - Triggering command: `/tmp/go-build4242760615/b318/config.test /tmp/go-build4242760615/b318/config.test -test.testlogfile=/tmp/go-build4242760615/b318/testlog.txt -test.paniconexit0 -test.timeout=10m0s --de�� c.test --debug-prefix-map 64/bin/git -I /opt/hostedtoolc-unsafeptr=false ut-1969789729.c VF78F_aidvBPXKXaKR/S4HB-3OSyH3DKHEAD` (dns block) > - Triggering command: `/tmp/go-build2320097585/b314/config.test /tmp/go-build2320097585/b314/config.test -test.testlogfile=/tmp/go-build2320097585/b314/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 17fb9960acf3cdef -importcfg ine by/b2276789a3c14/usr/bin/chronyc -w -buildmode=exe ine --ve�� 85e4fa5c5748c5dd -extld=gcc /opt/hostedtoolcache/go/1.25.7/xjson aw-mcpg/internalbase64 9286257/b151/vet-d ns /opt/hostedtoolcache/go/1.25.7/x64/pkg/tool/linu--listen` (dns block) > - `nonexistent.local` > - Triggering command: `/tmp/go-build4242760615/b336/launcher.test /tmp/go-build4242760615/b336/launcher.test -test.testlogfile=/tmp/go-build4242760615/b336/testlog.txt -test.paniconexit0 -test.timeout=10m0s -uns�� ache/go/1.25.7/x64/src/net /tmp/go-build1249286257/b024/vet.cfg ache/go/1.25.7/x64/pkg/tool/linux_amd64/vet unset position.go .12/x64/bin/git ache/go/1.25.7/x64/pkg/tool/linuHEAD -W -I /tmp/go-build1249286257/b165/ ache/go/1.25.7/x64/pkg/tool/linux_amd64/compile . --gdwarf2 --64 ache/go/1.25.7/x64/pkg/tool/linux_amd64/compile` (dns block) > - Triggering command: `/tmp/go-build2320097585/b332/launcher.test /tmp/go-build2320097585/b332/launcher.test -test.testlogfile=/tmp/go-build2320097585/b332/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 64/bin/git ortcfg /usr/sbin/bash submodules | heanode 9286257/b132/vet-v ndor/bin/git bash /usr�� runtime-runc/moby ortcfg csi/net-interface-handler aw-mcpg/internal/opt/hostedtoolcache/go/1.25.7/x64/pkg/tool/linux_amd64/asm aw-mcpg/internal-V=full` (dns block) > - `slow.example.com` > - Triggering command: `/tmp/go-build4242760615/b336/launcher.test /tmp/go-build4242760615/b336/launcher.test -test.testlogfile=/tmp/go-build4242760615/b336/testlog.txt -test.paniconexit0 -test.timeout=10m0s -uns�� ache/go/1.25.7/x64/src/net /tmp/go-build1249286257/b024/vet.cfg ache/go/1.25.7/x64/pkg/tool/linux_amd64/vet unset position.go .12/x64/bin/git ache/go/1.25.7/x64/pkg/tool/linuHEAD -W -I /tmp/go-build1249286257/b165/ ache/go/1.25.7/x64/pkg/tool/linux_amd64/compile . --gdwarf2 --64 ache/go/1.25.7/x64/pkg/tool/linux_amd64/compile` (dns block) > - Triggering command: `/tmp/go-build2320097585/b332/launcher.test /tmp/go-build2320097585/b332/launcher.test -test.testlogfile=/tmp/go-build2320097585/b332/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true 64/bin/git ortcfg /usr/sbin/bash submodules | heanode 9286257/b132/vet-v ndor/bin/git bash /usr�� runtime-runc/moby ortcfg csi/net-interface-handler aw-mcpg/internal/opt/hostedtoolcache/go/1.25.7/x64/pkg/tool/linux_amd64/asm aw-mcpg/internal-V=full` (dns block) > - `this-host-does-not-exist-12345.com` > - Triggering command: `/tmp/go-build4242760615/b345/mcp.test /tmp/go-build4242760615/b345/mcp.test -test.testlogfile=/tmp/go-build4242760615/b345/testlog.txt -test.paniconexit0 -test.timeout=10m0s -I ache/go/1.25.7/x64/src/net -I if (a[i] &amp;&amp; a[i] !~ /^[0-9]&#43;$/) exit(2); if (a[i] &lt; b[i]) exit(3); else if (a[i] &gt; b[i]) exit(0) --gdwarf-5 --64 -o ache/go/1.25.7/x64/pkg/tool/linuHEAD -o /tmp/go-build1249286257/b210/_pkg_.a -trimpath ache/go/1.25.7/x64/pkg/tool/linux_amd64/vet -p github.com/githu/tmp/go-build3632611954/b228/vet.cfg -lang=go1.25 ache/go/1.25.7/x64/pkg/tool/linux_amd64/vet` (dns block) > - Triggering command: `/tmp/go-build2320097585/b341/mcp.test /tmp/go-build2320097585/b341/mcp.test -test.testlogfile=/tmp/go-build2320097585/b341/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true /home/REDACTED/work/gh-aw-mcpg/gh-aw-mcpg/internal/strutil/truncat6662819a7d6931901c1593745131476bbash y CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt&#34;, &#34;REQUESTS_CA_BUNDLE=/etc/ssl/c--version by/bd0209c110101/opt/hostedtoolcache/go/1.25.7/x64/pkg/tool/linux_amd64/link aw-mcpg/internal-o ache/go/1.25.7/x/tmp/go-build2320097585/b314/config.test docker insp�� 551622d230bb31fc-s by/bd0209c110101-w rtificates.crt&#34;,-buildmode=exe by/a501e7528e5b9bash e330148d0ce51d5e/usr/bin/runc` (dns block) > > If you need me to access, download, or install something from one of these locations, you can either: > > - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled > - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/github/gh-aw-mcpg/settings/copilot/coding_agent) (admins only) > > </details>
2 parents a27f9a6 + b1a82a4 commit 8269bae

1 file changed

Lines changed: 52 additions & 51 deletions

File tree

internal/server/unified.go

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,21 @@ func (us *UnifiedServer) registerSysTool(name, description string, inputSchema m
469469
us.toolsMu.Unlock()
470470
}
471471

472+
// callSysServer is a helper that calls a sys tool by marshaling the tool name,
473+
// delegating to sysServer.HandleRequest, and returning the result.
474+
// This consolidates the common pattern used by sys tool handlers.
475+
func (us *UnifiedServer) callSysServer(toolName string) (interface{}, error) {
476+
params, _ := json.Marshal(map[string]interface{}{
477+
"name": toolName,
478+
"arguments": map[string]interface{}{},
479+
})
480+
result, err := us.sysServer.HandleRequest("tools/call", json.RawMessage(params))
481+
if err != nil {
482+
return nil, err
483+
}
484+
return result, nil
485+
}
486+
472487
// registerSysTools registers built-in sys tools
473488
func (us *UnifiedServer) registerSysTools() error {
474489
// Create sys_init handler
@@ -511,11 +526,7 @@ func (us *UnifiedServer) registerSysTools() error {
511526
log.Printf("Initialized session: %s", sessionID)
512527

513528
// Call sys_init
514-
params, _ := json.Marshal(map[string]interface{}{
515-
"name": "sys_init",
516-
"arguments": map[string]interface{}{},
517-
})
518-
result, err := us.sysServer.HandleRequest("tools/call", json.RawMessage(params))
529+
result, err := us.callSysServer("sys_init")
519530
if err != nil {
520531
logger.LogError("client", "MCP session initialization: sys_init call failed, session=%s, error=%v", sessionID, err)
521532
return newErrorCallToolResult(err)
@@ -554,11 +565,7 @@ func (us *UnifiedServer) registerSysTools() error {
554565
return newErrorCallToolResult(err)
555566
}
556567

557-
params, _ := json.Marshal(map[string]interface{}{
558-
"name": "sys_list_servers",
559-
"arguments": map[string]interface{}{},
560-
})
561-
result, err := us.sysServer.HandleRequest("tools/call", json.RawMessage(params))
568+
result, err := us.callSysServer("sys_list_servers")
562569
if err != nil {
563570
logger.LogError("client", "MCP sys_list_servers error, session=%s, error=%v", sessionID, err)
564571
return newErrorCallToolResult(err)
@@ -783,32 +790,25 @@ func (us *UnifiedServer) createGuardFromConfig(name string, cfg *config.GuardCon
783790
}
784791
}
785792

786-
// guardBackendCaller implements guard.BackendCaller for guards to query backend metadata
787-
type guardBackendCaller struct {
788-
server *UnifiedServer
789-
serverID string
790-
ctx context.Context
791-
}
792-
793-
func (g *guardBackendCaller) CallTool(ctx context.Context, toolName string, args interface{}) (interface{}, error) {
794-
// Make a read-only call to the backend for metadata
795-
// This bypasses DIFC checks since it's internal to the guard
796-
log.Printf("[DIFC] Guard calling backend %s tool %s for metadata", g.serverID, toolName)
797-
798-
// Get or launch backend connection (use session-aware connection for stateful backends)
799-
sessionID := g.ctx.Value(SessionIDContextKey)
800-
if sessionID == nil {
801-
sessionID = "default"
802-
}
803-
conn, err := launcher.GetOrLaunchForSession(g.server.launcher, g.serverID, sessionID.(string))
793+
// executeBackendToolCall executes a backend MCP tool call and returns the raw result.
794+
// This helper consolidates the common pattern of:
795+
// 1. Get or launch backend connection
796+
// 2. Send tools/call request
797+
// 3. Check for backend error
798+
// 4. Unmarshal and return result
799+
//
800+
// Callers are responsible for adapting the result to their specific return types
801+
// and wrapping errors as needed.
802+
func executeBackendToolCall(ctx context.Context, l *launcher.Launcher, serverID, sessionID, toolName string, args interface{}) (interface{}, error) {
803+
conn, err := launcher.GetOrLaunchForSession(l, serverID, sessionID)
804804
if err != nil {
805805
return nil, fmt.Errorf("failed to connect: %w", err)
806806
}
807807

808-
response, err := conn.SendRequestWithServerID(g.ctx, "tools/call", map[string]interface{}{
808+
response, err := conn.SendRequestWithServerID(ctx, "tools/call", map[string]interface{}{
809809
"name": toolName,
810810
"arguments": args,
811-
}, g.serverID)
811+
}, serverID)
812812
if err != nil {
813813
return nil, err
814814
}
@@ -827,6 +827,27 @@ func (g *guardBackendCaller) CallTool(ctx context.Context, toolName string, args
827827
return result, nil
828828
}
829829

830+
// guardBackendCaller implements guard.BackendCaller for guards to query backend metadata
831+
type guardBackendCaller struct {
832+
server *UnifiedServer
833+
serverID string
834+
ctx context.Context
835+
}
836+
837+
func (g *guardBackendCaller) CallTool(ctx context.Context, toolName string, args interface{}) (interface{}, error) {
838+
// Make a read-only call to the backend for metadata
839+
// This bypasses DIFC checks since it's internal to the guard
840+
log.Printf("[DIFC] Guard calling backend %s tool %s for metadata", g.serverID, toolName)
841+
842+
// Get or launch backend connection (use session-aware connection for stateful backends)
843+
sessionID := g.ctx.Value(SessionIDContextKey)
844+
if sessionID == nil {
845+
sessionID = "default"
846+
}
847+
848+
return executeBackendToolCall(g.ctx, g.server.launcher, g.serverID, sessionID.(string), toolName, args)
849+
}
850+
830851
// convertToCallToolResult converts backend result data to SDK CallToolResult format
831852
// The backend returns a JSON object with a "content" field containing an array of content items
832853
func convertToCallToolResult(data interface{}) (*sdk.CallToolResult, error) {
@@ -1004,31 +1025,11 @@ func (us *UnifiedServer) callBackendTool(ctx context.Context, serverID, toolName
10041025
}
10051026

10061027
// **Phase 3: Execute the backend call**
1007-
// Get or launch backend connection (use session-aware connection for stateful backends)
1008-
conn, err := launcher.GetOrLaunchForSession(us.launcher, serverID, sessionID)
1009-
if err != nil {
1010-
return newErrorCallToolResult(fmt.Errorf("failed to connect: %w", err))
1011-
}
1012-
1013-
response, err := conn.SendRequestWithServerID(ctx, "tools/call", map[string]interface{}{
1014-
"name": toolName,
1015-
"arguments": args,
1016-
}, serverID)
1028+
backendResult, err := executeBackendToolCall(ctx, us.launcher, serverID, sessionID, toolName, args)
10171029
if err != nil {
10181030
return newErrorCallToolResult(err)
10191031
}
10201032

1021-
// Check if the backend returned an error
1022-
if response.Error != nil {
1023-
return newErrorCallToolResult(fmt.Errorf("backend error: code=%d, message=%s", response.Error.Code, response.Error.Message))
1024-
}
1025-
1026-
// Parse the backend result
1027-
var backendResult interface{}
1028-
if err := json.Unmarshal(response.Result, &backendResult); err != nil {
1029-
return newErrorCallToolResult(fmt.Errorf("failed to parse result: %w", err))
1030-
}
1031-
10321033
// **Phase 4: Guard labels the response data (for fine-grained filtering)**
10331034
// Per spec: LabelResponse() is only called for read operations in all modes,
10341035
// and for read-write operations in filter/propagate modes.

0 commit comments

Comments
 (0)