Skip to content

Commit 500425f

Browse files
committed
Added secret scanning to the mcp
1 parent 75d71ad commit 500425f

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

pkg/github/secret_scanning.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
10+
"github.com/github/github-mcp-server/pkg/translations"
11+
"github.com/google/go-github/v69/github"
12+
"github.com/mark3labs/mcp-go/mcp"
13+
"github.com/mark3labs/mcp-go/server"
14+
)
15+
16+
func getSecretScanningAlert(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
17+
return mcp.NewTool("get_secret_scanning_alert",
18+
mcp.WithDescription(t("TOOL_GET_SECRET_SCANNING_ALERT_DESCRIPTION", "Get details of a specific secret scanning alert in a GitHub repository.")),
19+
mcp.WithString("owner",
20+
mcp.Required(),
21+
mcp.Description("The owner of the repository."),
22+
),
23+
mcp.WithString("repo",
24+
mcp.Required(),
25+
mcp.Description("The name of the repository."),
26+
),
27+
mcp.WithNumber("alert_number",
28+
mcp.Required(),
29+
mcp.Description("The number of the alert."),
30+
),
31+
),
32+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
33+
owner, err := requiredParam[string](request, "owner")
34+
if err != nil {
35+
return mcp.NewToolResultError(err.Error()), nil
36+
}
37+
repo, err := requiredParam[string](request, "repo")
38+
if err != nil {
39+
return mcp.NewToolResultError(err.Error()), nil
40+
}
41+
alertNumber, err := requiredInt(request, "alert_number")
42+
if err != nil {
43+
return mcp.NewToolResultError(err.Error()), nil
44+
}
45+
46+
alert, resp, err := client.SecretScanning.GetAlert(ctx, owner, repo, int64(alertNumber))
47+
if err != nil {
48+
return nil, fmt.Errorf("failed to get alert: %w", err)
49+
}
50+
defer func() { _ = resp.Body.Close() }()
51+
52+
if resp.StatusCode != http.StatusOK {
53+
body, err := io.ReadAll(resp.Body)
54+
if err != nil {
55+
return nil, fmt.Errorf("failed to read response body: %w", err)
56+
}
57+
return mcp.NewToolResultError(fmt.Sprintf("failed to get alert: %s", string(body))), nil
58+
}
59+
60+
r, err := json.Marshal(alert)
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to marshal alert: %w", err)
63+
}
64+
65+
return mcp.NewToolResultText(string(r)), nil
66+
}
67+
}
68+
69+
func listSecretScanningAlerts(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
70+
return mcp.NewTool("list_secret_scanning_alerts",
71+
mcp.WithDescription(t("TOOL_LIST_SECRET_SCANNING_ALERTS_DESCRIPTION", "List secret scanning alerts in a GitHub repository.")),
72+
mcp.WithString("owner",
73+
mcp.Required(),
74+
mcp.Description("The owner of the repository."),
75+
),
76+
mcp.WithString("repo",
77+
mcp.Required(),
78+
mcp.Description("The name of the repository."),
79+
),
80+
mcp.WithString("secret_type",
81+
mcp.Description("A comma-separated list of secret types to return. All default secret patterns are returned. To return generic patterns, pass the token name(s) in the parameter."),
82+
),
83+
mcp.WithString("state",
84+
mcp.Description("State of the secret scanning alerts to list. Set to open or resolved to only list secret scanning alerts in a specific state."),
85+
mcp.DefaultString("open"),
86+
),
87+
mcp.WithString("resolution",
88+
mcp.Description("A comma-separated list of resolutions. Only secret scanning alerts with one of these resolutions are listed. Valid resolutions are false_positive, wont_fix, revoked, pattern_edited, pattern_deleted or used_in_tests."),
89+
),
90+
),
91+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
92+
owner, err := requiredParam[string](request, "owner")
93+
if err != nil {
94+
return mcp.NewToolResultError(err.Error()), nil
95+
}
96+
repo, err := requiredParam[string](request, "repo")
97+
if err != nil {
98+
return mcp.NewToolResultError(err.Error()), nil
99+
}
100+
secretType, err := optionalParam[string](request, "secret_type")
101+
if err != nil {
102+
return mcp.NewToolResultError(err.Error()), nil
103+
}
104+
state, err := optionalParam[string](request, "state")
105+
if err != nil {
106+
return mcp.NewToolResultError(err.Error()), nil
107+
}
108+
resolution, err := optionalParam[string](request, "resolution")
109+
if err != nil {
110+
return mcp.NewToolResultError(err.Error()), nil
111+
}
112+
113+
alerts, resp, err := client.SecretScanning.ListAlertsForRepo(ctx, owner, repo, &github.SecretScanningAlertListOptions{SecretType: secretType, State: state, Resolution: resolution})
114+
if err != nil {
115+
return nil, fmt.Errorf("failed to list alerts: %w", err)
116+
}
117+
defer func() { _ = resp.Body.Close() }()
118+
119+
if resp.StatusCode != http.StatusOK {
120+
body, err := io.ReadAll(resp.Body)
121+
if err != nil {
122+
return nil, fmt.Errorf("failed to read response body: %w", err)
123+
}
124+
return mcp.NewToolResultError(fmt.Sprintf("failed to list alerts: %s", string(body))), nil
125+
}
126+
127+
r, err := json.Marshal(alerts)
128+
if err != nil {
129+
return nil, fmt.Errorf("failed to marshal alerts: %w", err)
130+
}
131+
132+
return mcp.NewToolResultText(string(r)), nil
133+
}
134+
}

pkg/github/server.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ func NewServer(client *github.Client, readOnly bool, t translations.TranslationH
7979
// Add GitHub tools - Code Scanning
8080
s.AddTool(getCodeScanningAlert(client, t))
8181
s.AddTool(listCodeScanningAlerts(client, t))
82+
83+
// Add GitHub tools - Secret Scanning
84+
s.AddTool(getSecretScanningAlert(client, t))
85+
s.AddTool(listSecretScanningAlerts(client, t))
86+
8287
return s
8388
}
8489

0 commit comments

Comments
 (0)