From c091516571374b887690d5bef8f23f89ca721d82 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 14 Mar 2025 16:50:53 +0100 Subject: [PATCH] feat: add basic code scanning methods --- README.md | 15 +++++ pkg/github/code_scanning.go | 108 ++++++++++++++++++++++++++++++++++++ pkg/github/server.go | 4 ++ 3 files changed, 127 insertions(+) create mode 100644 pkg/github/code_scanning.go diff --git a/README.md b/README.md index ccc0ddf5..3929b727 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,21 @@ and set it as the GITHUB_PERSONAL_ACCESS_TOKEN environment variable. - `page`: Page number (number, optional) - `per_page`: Results per page (number, optional) +### Code Scanning + +- **get_code_scanning_alert** - Get a code scanning alert + + - `owner`: Repository owner (string, required) + - `repo`: Repository name (string, required) + - `alert_number`: Alert number (number, required) + +- **list_code_scanning_alerts** - List code scanning alerts for a repository + - `owner`: Repository owner (string, required) + - `repo`: Repository name (string, required) + - `ref`: Git reference (string, optional) + - `state`: Alert state (string, optional) + - `severity`: Alert severity (string, optional) + ## Standard input/output server ```sh diff --git a/pkg/github/code_scanning.go b/pkg/github/code_scanning.go new file mode 100644 index 00000000..da714744 --- /dev/null +++ b/pkg/github/code_scanning.go @@ -0,0 +1,108 @@ +package github + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/google/go-github/v69/github" + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" +) + +func getCodeScanningAlert(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) { + return mcp.NewTool("get_code_scanning_alert", + mcp.WithDescription("Get details of a specific code scanning alert in a GitHub repository."), + mcp.WithString("owner", + mcp.Required(), + mcp.Description("The owner of the repository."), + ), + mcp.WithString("repo", + mcp.Required(), + mcp.Description("The name of the repository."), + ), + mcp.WithNumber("alert_number", + mcp.Required(), + mcp.Description("The number of the alert."), + ), + ), + func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + owner, _ := request.Params.Arguments["owner"].(string) + repo, _ := request.Params.Arguments["repo"].(string) + alertNumber, _ := request.Params.Arguments["alert_number"].(float64) + + alert, resp, err := client.CodeScanning.GetAlert(ctx, owner, repo, int64(alertNumber)) + if err != nil { + return nil, fmt.Errorf("failed to get alert: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != 200 { + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + return mcp.NewToolResultError(fmt.Sprintf("failed to get alert: %s", string(body))), nil + } + + r, err := json.Marshal(alert) + if err != nil { + return nil, fmt.Errorf("failed to marshal alert: %w", err) + } + + return mcp.NewToolResultText(string(r)), nil + } +} + +func listCodeScanningAlerts(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) { + return mcp.NewTool("list_code_scanning_alerts", + mcp.WithDescription("List code scanning alerts in a GitHub repository."), + mcp.WithString("owner", + mcp.Required(), + mcp.Description("The owner of the repository."), + ), + mcp.WithString("repo", + mcp.Required(), + mcp.Description("The name of the repository."), + ), + mcp.WithString("ref", + mcp.Description("The Git reference for the results you want to list."), + ), + mcp.WithString("state", + mcp.Description("State of the code scanning alerts to list. Set to closed to list only closed code scanning alerts. Default: open"), + mcp.DefaultString("open"), + ), + mcp.WithString("severity", + mcp.Description("Only code scanning alerts with this severity will be returned. Possible values are: critical, high, medium, low, warning, note, error."), + ), + ), + func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + owner, _ := request.Params.Arguments["owner"].(string) + repo, _ := request.Params.Arguments["repo"].(string) + ref, _ := request.Params.Arguments["ref"].(string) + state, _ := request.Params.Arguments["state"].(string) + severity, _ := request.Params.Arguments["severity"].(string) + + alerts, resp, err := client.CodeScanning.ListAlertsForRepo(ctx, owner, repo, &github.AlertListOptions{Ref: ref, State: state, Severity: severity}) + if err != nil { + return nil, fmt.Errorf("failed to list alerts: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != 200 { + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + return mcp.NewToolResultError(fmt.Sprintf("failed to list alerts: %s", string(body))), nil + } + + r, err := json.Marshal(alerts) + if err != nil { + return nil, fmt.Errorf("failed to marshal alerts: %w", err) + } + + return mcp.NewToolResultText(string(r)), nil + } +} diff --git a/pkg/github/server.go b/pkg/github/server.go index 2c0f3a05..fcbb58d3 100644 --- a/pkg/github/server.go +++ b/pkg/github/server.go @@ -51,6 +51,10 @@ func NewServer(client *github.Client) *server.MCPServer { // Add GitHub tools - Users s.AddTool(getMe(client)) + // Add GitHub tools - Code Scanning + s.AddTool(getCodeScanningAlert(client)) + s.AddTool(listCodeScanningAlerts(client)) + return s }