Skip to content

Commit ac162ee

Browse files
Fix SonarQube high-severity issues: string duplication and cognitive complexity
- Fixed string literal duplication in pkg/github/search.go * Added constants for 'failed to read response body: %w' and 'failed to marshal response: %w' * Replaced 4 duplicate occurrences with constant references - Reduced cognitive complexity in cmd/mcpcurl/main.go * Refactored main() function from complexity 18 to under 15 * Extracted schema loading logic into loadSchemaAndGenerateCommands() function - Reduced cognitive complexity in pkg/github/pullrequests.go * Refactored GetPullRequestFiles handler from complexity 19 to under 15 * Extracted parameter handling into getPullRequestFilesHandler() function * Extracted API call logic into fetchAndMarshalPullRequestFiles() function All tests pass and linting is clean. Co-Authored-By: Eashan Sinha <eashan.sinha@codeium.com>
1 parent f88456f commit ac162ee

File tree

3 files changed

+109
-73
lines changed

3 files changed

+109
-73
lines changed

cmd/mcpcurl/main.go

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -168,37 +168,59 @@ func main() {
168168
// Execute the root command once to parse flags
169169
_ = rootCmd.ParseFlags(os.Args[1:])
170170

171+
// Load schema and generate tool commands
172+
if err := loadSchemaAndGenerateCommands(); err != nil {
173+
_, _ = fmt.Fprintf(os.Stderr, "Warning: failed to load schema: %v\n", err)
174+
}
175+
176+
// Execute
177+
if err := rootCmd.Execute(); err != nil {
178+
_, _ = fmt.Fprintf(os.Stderr, "Error executing command: %v\n", err)
179+
os.Exit(1)
180+
}
181+
}
182+
183+
// loadSchemaAndGenerateCommands fetches the schema from the server and generates tool commands
184+
func loadSchemaAndGenerateCommands() error {
171185
// Get pretty flag
172186
prettyPrint, err := rootCmd.Flags().GetBool("pretty")
173187
if err != nil {
174-
_, _ = fmt.Fprintf(os.Stderr, "Error getting pretty flag: %v\n", err)
175-
os.Exit(1)
188+
return fmt.Errorf("failed to get pretty flag: %w", err)
176189
}
190+
177191
// Get server command
178192
serverCmd, err := rootCmd.Flags().GetString("stdio-server-cmd")
179-
if err == nil && serverCmd != "" {
180-
// Fetch schema from server
181-
jsonRequest, err := buildJSONRPCRequest("tools/list", "", nil)
182-
if err == nil {
183-
response, err := executeServerCommand(serverCmd, jsonRequest)
184-
if err == nil {
185-
// Parse the schema response
186-
var schemaResp SchemaResponse
187-
if err := json.Unmarshal([]byte(response), &schemaResp); err == nil {
188-
// Add all the generated commands as subcommands of tools
189-
for _, tool := range schemaResp.Result.Tools {
190-
addCommandFromTool(toolsCmd, &tool, prettyPrint)
191-
}
192-
}
193-
}
194-
}
193+
if err != nil {
194+
return fmt.Errorf("failed to get stdio-server-cmd: %w", err)
195195
}
196196

197-
// Execute
198-
if err := rootCmd.Execute(); err != nil {
199-
_, _ = fmt.Fprintf(os.Stderr, "Error executing command: %v\n", err)
200-
os.Exit(1)
197+
if serverCmd == "" {
198+
return nil // No server command provided
201199
}
200+
201+
// Fetch schema from server
202+
jsonRequest, err := buildJSONRPCRequest("tools/list", "", nil)
203+
if err != nil {
204+
return fmt.Errorf("failed to build JSON-RPC request: %w", err)
205+
}
206+
207+
response, err := executeServerCommand(serverCmd, jsonRequest)
208+
if err != nil {
209+
return fmt.Errorf("failed to execute server command: %w", err)
210+
}
211+
212+
// Parse the schema response
213+
var schemaResp SchemaResponse
214+
if err := json.Unmarshal([]byte(response), &schemaResp); err != nil {
215+
return fmt.Errorf("failed to unmarshal schema response: %w", err)
216+
}
217+
218+
// Add all the generated commands as subcommands of tools
219+
for _, tool := range schemaResp.Result.Tools {
220+
addCommandFromTool(toolsCmd, &tool, prettyPrint)
221+
}
222+
223+
return nil
202224
}
203225

204226
// addCommandFromTool creates a cobra command from a tool schema

pkg/github/pullrequests.go

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -601,56 +601,65 @@ func GetPullRequestFiles(getClient GetClientFn, t translations.TranslationHelper
601601
WithPagination(),
602602
),
603603
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
604-
owner, err := RequiredParam[string](request, "owner")
605-
if err != nil {
606-
return mcp.NewToolResultError(err.Error()), nil
607-
}
608-
repo, err := RequiredParam[string](request, "repo")
609-
if err != nil {
610-
return mcp.NewToolResultError(err.Error()), nil
611-
}
612-
pullNumber, err := RequiredInt(request, "pullNumber")
613-
if err != nil {
614-
return mcp.NewToolResultError(err.Error()), nil
615-
}
616-
pagination, err := OptionalPaginationParams(request)
617-
if err != nil {
618-
return mcp.NewToolResultError(err.Error()), nil
619-
}
604+
return getPullRequestFilesHandler(ctx, getClient, request)
605+
}
606+
}
620607

621-
client, err := getClient(ctx)
622-
if err != nil {
623-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
624-
}
625-
opts := &github.ListOptions{
626-
PerPage: pagination.perPage,
627-
Page: pagination.page,
628-
}
629-
files, resp, err := client.PullRequests.ListFiles(ctx, owner, repo, pullNumber, opts)
630-
if err != nil {
631-
return ghErrors.NewGitHubAPIErrorResponse(ctx,
632-
"failed to get pull request files",
633-
resp,
634-
err,
635-
), nil
636-
}
637-
defer func() { _ = resp.Body.Close() }()
608+
func getPullRequestFilesHandler(ctx context.Context, getClient GetClientFn, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
609+
owner, err := RequiredParam[string](request, "owner")
610+
if err != nil {
611+
return mcp.NewToolResultError(err.Error()), nil
612+
}
613+
repo, err := RequiredParam[string](request, "repo")
614+
if err != nil {
615+
return mcp.NewToolResultError(err.Error()), nil
616+
}
617+
pullNumber, err := RequiredInt(request, "pullNumber")
618+
if err != nil {
619+
return mcp.NewToolResultError(err.Error()), nil
620+
}
621+
pagination, err := OptionalPaginationParams(request)
622+
if err != nil {
623+
return mcp.NewToolResultError(err.Error()), nil
624+
}
638625

639-
if resp.StatusCode != http.StatusOK {
640-
body, err := io.ReadAll(resp.Body)
641-
if err != nil {
642-
return nil, fmt.Errorf("failed to read response body: %w", err)
643-
}
644-
return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request files: %s", string(body))), nil
645-
}
626+
client, err := getClient(ctx)
627+
if err != nil {
628+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
629+
}
646630

647-
r, err := json.Marshal(files)
648-
if err != nil {
649-
return nil, fmt.Errorf("failed to marshal response: %w", err)
650-
}
631+
return fetchAndMarshalPullRequestFiles(ctx, client, owner, repo, pullNumber, pagination)
632+
}
651633

652-
return mcp.NewToolResultText(string(r)), nil
634+
func fetchAndMarshalPullRequestFiles(ctx context.Context, client *github.Client, owner, repo string, pullNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) {
635+
opts := &github.ListOptions{
636+
PerPage: pagination.perPage,
637+
Page: pagination.page,
638+
}
639+
files, resp, err := client.PullRequests.ListFiles(ctx, owner, repo, pullNumber, opts)
640+
if err != nil {
641+
return ghErrors.NewGitHubAPIErrorResponse(ctx,
642+
"failed to get pull request files",
643+
resp,
644+
err,
645+
), nil
646+
}
647+
defer func() { _ = resp.Body.Close() }()
648+
649+
if resp.StatusCode != http.StatusOK {
650+
body, err := io.ReadAll(resp.Body)
651+
if err != nil {
652+
return nil, fmt.Errorf("failed to read response body: %w", err)
653653
}
654+
return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request files: %s", string(body))), nil
655+
}
656+
657+
r, err := json.Marshal(files)
658+
if err != nil {
659+
return nil, fmt.Errorf("failed to marshal response: %w", err)
660+
}
661+
662+
return mcp.NewToolResultText(string(r)), nil
654663
}
655664

656665
// GetPullRequestStatus creates a tool to get the combined status of all status checks for a pull request.

pkg/github/search.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import (
1313
"github.com/mark3labs/mcp-go/server"
1414
)
1515

16+
const (
17+
errReadResponseBody = "failed to read response body: %w"
18+
errMarshalResponse = "failed to marshal response: %w"
19+
)
20+
1621
// SearchRepositories creates a tool to search for GitHub repositories.
1722
func SearchRepositories(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
1823
return mcp.NewTool("search_repositories",
@@ -61,14 +66,14 @@ func SearchRepositories(getClient GetClientFn, t translations.TranslationHelperF
6166
if resp.StatusCode != 200 {
6267
body, err := io.ReadAll(resp.Body)
6368
if err != nil {
64-
return nil, fmt.Errorf("failed to read response body: %w", err)
69+
return nil, fmt.Errorf(errReadResponseBody, err)
6570
}
6671
return mcp.NewToolResultError(fmt.Sprintf("failed to search repositories: %s", string(body))), nil
6772
}
6873

6974
r, err := json.Marshal(result)
7075
if err != nil {
71-
return nil, fmt.Errorf("failed to marshal response: %w", err)
76+
return nil, fmt.Errorf(errMarshalResponse, err)
7277
}
7378

7479
return mcp.NewToolResultText(string(r)), nil
@@ -141,14 +146,14 @@ func SearchCode(getClient GetClientFn, t translations.TranslationHelperFunc) (to
141146
if resp.StatusCode != 200 {
142147
body, err := io.ReadAll(resp.Body)
143148
if err != nil {
144-
return nil, fmt.Errorf("failed to read response body: %w", err)
149+
return nil, fmt.Errorf(errReadResponseBody, err)
145150
}
146151
return mcp.NewToolResultError(fmt.Sprintf("failed to search code: %s", string(body))), nil
147152
}
148153

149154
r, err := json.Marshal(result)
150155
if err != nil {
151-
return nil, fmt.Errorf("failed to marshal response: %w", err)
156+
return nil, fmt.Errorf(errMarshalResponse, err)
152157
}
153158

154159
return mcp.NewToolResultText(string(r)), nil
@@ -215,7 +220,7 @@ func userOrOrgHandler(accountType string, getClient GetClientFn) server.ToolHand
215220
if resp.StatusCode != 200 {
216221
body, err := io.ReadAll(resp.Body)
217222
if err != nil {
218-
return nil, fmt.Errorf("failed to read response body: %w", err)
223+
return nil, fmt.Errorf(errReadResponseBody, err)
219224
}
220225
return mcp.NewToolResultError(fmt.Sprintf("failed to search %ss: %s", accountType, string(body))), nil
221226
}
@@ -251,7 +256,7 @@ func userOrOrgHandler(accountType string, getClient GetClientFn) server.ToolHand
251256

252257
r, err := json.Marshal(minimalResp)
253258
if err != nil {
254-
return nil, fmt.Errorf("failed to marshal response: %w", err)
259+
return nil, fmt.Errorf(errMarshalResponse, err)
255260
}
256261
return mcp.NewToolResultText(string(r)), nil
257262
}

0 commit comments

Comments
 (0)