Skip to content

Commit e6e4a21

Browse files
feat: enable secret protection
1 parent 75d71ad commit e6e4a21

File tree

2 files changed

+157
-1
lines changed

2 files changed

+157
-1
lines changed

pkg/github/repositories.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package github
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
67
"fmt"
@@ -610,3 +611,153 @@ func pushFiles(client *github.Client, t translations.TranslationHelperFunc) (too
610611
return mcp.NewToolResultText(string(r)), nil
611612
}
612613
}
614+
615+
func securityFeatureToggle(isEnabled bool) map[string]string {
616+
if isEnabled {
617+
return map[string]string{"status": "enabled"}
618+
}
619+
return map[string]string{"status": "disabled"}
620+
}
621+
622+
func toggleSecretProtectionFeatures(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
623+
return mcp.NewTool("toggle_secret_protection_features",
624+
mcp.WithDescription(t("TOOL_TOGGLE_SECRET_PROTECTION_FEATURES_DESCRIPTION", "Enable or disable Secret Protection features for a repository")),
625+
mcp.WithString("owner",
626+
mcp.Required(),
627+
mcp.Description("Repository owner"),
628+
),
629+
mcp.WithString("repo",
630+
mcp.Required(),
631+
mcp.Description("Repository name"),
632+
),
633+
mcp.WithBoolean("secret_scanning",
634+
mcp.Required(),
635+
mcp.Description("Enable or disable secret scanning"),
636+
),
637+
mcp.WithBoolean("secret_scanning_push_protection",
638+
mcp.Required(),
639+
mcp.Description("Enable or disable secret scanning push protection"),
640+
),
641+
mcp.WithBoolean("secret_scanning_ai_detection",
642+
mcp.Required(),
643+
mcp.Description("Enable or disable secret scanning AI detection"),
644+
),
645+
),
646+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
647+
owner, err := requiredParam[string](request, "owner")
648+
if err != nil {
649+
return mcp.NewToolResultError(err.Error()), nil
650+
}
651+
repo, err := requiredParam[string](request, "repo")
652+
if err != nil {
653+
return mcp.NewToolResultError(err.Error()), nil
654+
}
655+
secretScanningEnabled, err := requiredParam[bool](request, "secret_scanning")
656+
if err != nil {
657+
return mcp.NewToolResultError(err.Error()), nil
658+
}
659+
pushProtectionEnabled, err := optionalParam[bool](request, "secret_scanning_push_protection")
660+
if err != nil {
661+
return mcp.NewToolResultError(err.Error()), nil
662+
}
663+
aiDetectionEnabled, err := optionalParam[bool](request, "secret_scanning_ai_detection")
664+
if err != nil {
665+
return mcp.NewToolResultError(err.Error()), nil
666+
}
667+
668+
securityAndAnalysis := map[string]map[string]string{
669+
"secret_scanning": securityFeatureToggle(secretScanningEnabled),
670+
"secret_scanning_push_protection": securityFeatureToggle(pushProtectionEnabled),
671+
"secret_scanning_ai_detection": securityFeatureToggle(aiDetectionEnabled),
672+
}
673+
674+
requestBody := map[string]interface{}{
675+
"security_and_analysis": securityAndAnalysis,
676+
}
677+
678+
jsonBody, err := json.Marshal(requestBody)
679+
if err != nil {
680+
return nil, fmt.Errorf("failed to marshal request body: %w", err)
681+
}
682+
683+
req, err := http.NewRequest(
684+
"PATCH",
685+
fmt.Sprintf("%srepos/%s/%s", client.BaseURL.String(), owner, repo),
686+
bytes.NewBuffer(jsonBody),
687+
)
688+
if err != nil {
689+
return nil, fmt.Errorf("failed to create request: %w", err)
690+
}
691+
692+
resp, err := client.Client().Do(req)
693+
if err != nil {
694+
return nil, fmt.Errorf("failed to send request: %w", err)
695+
}
696+
defer func() { _ = resp.Body.Close() }()
697+
698+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
699+
body, err := io.ReadAll(resp.Body)
700+
if err != nil {
701+
return nil, fmt.Errorf("failed to read response body: %w", err)
702+
}
703+
return mcp.NewToolResultError(fmt.Sprintf("failed to toggle Secret Protection Features: %s", string(body))), nil
704+
}
705+
706+
return mcp.NewToolResultText("Secret Protection features toggled successfully"), nil
707+
}
708+
}
709+
710+
// getRepositorySettings creates a tool to get repository settings including security features
711+
func getRepositorySettings(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
712+
return mcp.NewTool("get_repository_settings",
713+
mcp.WithDescription(t("TOOL_GET_REPOSITORY_SETTINGS_DESCRIPTION", "Get repository settings including security features")),
714+
mcp.WithString("owner",
715+
mcp.Required(),
716+
mcp.Description("Repository owner"),
717+
),
718+
mcp.WithString("repo",
719+
mcp.Required(),
720+
mcp.Description("Repository name"),
721+
),
722+
),
723+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
724+
owner, err := requiredParam[string](request, "owner")
725+
if err != nil {
726+
return mcp.NewToolResultError(err.Error()), nil
727+
}
728+
repo, err := requiredParam[string](request, "repo")
729+
if err != nil {
730+
return mcp.NewToolResultError(err.Error()), nil
731+
}
732+
733+
req, err := http.NewRequest(
734+
"GET",
735+
fmt.Sprintf("%srepos/%s/%s", client.BaseURL.String(), owner, repo),
736+
nil,
737+
)
738+
if err != nil {
739+
return nil, fmt.Errorf("failed to create request: %w", err)
740+
}
741+
742+
resp, err := client.Client().Do(req)
743+
if err != nil {
744+
return nil, fmt.Errorf("failed to send request: %w", err)
745+
}
746+
defer func() { _ = resp.Body.Close() }()
747+
748+
if resp.StatusCode != http.StatusOK {
749+
body, err := io.ReadAll(resp.Body)
750+
if err != nil {
751+
return nil, fmt.Errorf("failed to read response body: %w", err)
752+
}
753+
return mcp.NewToolResultError(fmt.Sprintf("failed to get security analysis settings: %s", string(body))), nil
754+
}
755+
756+
body, err := io.ReadAll(resp.Body)
757+
if err != nil {
758+
return nil, fmt.Errorf("failed to read response body: %w", err)
759+
}
760+
761+
return mcp.NewToolResultText(string(body)), nil
762+
}
763+
}

pkg/github/server.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"net/http"
10+
"reflect"
1011
"strings"
1112

1213
"github.com/github/github-mcp-server/pkg/translations"
@@ -58,6 +59,7 @@ func NewServer(client *github.Client, readOnly bool, t translations.TranslationH
5859
}
5960

6061
// Add GitHub tools - Repositories
62+
s.AddTool(getRepositorySettings(client, t))
6163
s.AddTool(searchRepositories(client, t))
6264
s.AddTool(getFileContents(client, t))
6365
s.AddTool(listCommits(client, t))
@@ -67,6 +69,7 @@ func NewServer(client *github.Client, readOnly bool, t translations.TranslationH
6769
s.AddTool(forkRepository(client, t))
6870
s.AddTool(createBranch(client, t))
6971
s.AddTool(pushFiles(client, t))
72+
s.AddTool(toggleSecretProtectionFeatures(client, t))
7073
}
7174

7275
// Add GitHub tools - Search
@@ -157,7 +160,9 @@ func requiredParam[T comparable](r mcp.CallToolRequest, p string) (T, error) {
157160
return zero, fmt.Errorf("parameter %s is not of type %T", p, zero)
158161
}
159162

160-
if r.Params.Arguments[p].(T) == zero {
163+
// Check if the parameter is not empty, i.e: non-zero value
164+
// Note: This check is not applicable for bool type, as false is a valid value
165+
if r.Params.Arguments[p].(T) == zero && reflect.TypeOf(zero).Kind() != reflect.Bool {
161166
return zero, fmt.Errorf("missing required parameter: %s", p)
162167

163168
}

0 commit comments

Comments
 (0)