88 "io"
99 "net/http"
1010
11+ "github.com/github/github-mcp-server/pkg/toolsets"
1112 "github.com/github/github-mcp-server/pkg/translations"
1213 "github.com/google/go-github/v69/github"
1314 "github.com/mark3labs/mcp-go/mcp"
@@ -17,69 +18,84 @@ import (
1718type GetClientFn func (context.Context ) (* github.Client , error )
1819
1920// NewServer creates a new GitHub MCP server with the specified GH client and logger.
20- func NewServer (getClient GetClientFn , version string , readOnly bool , t translations.TranslationHelperFunc ) * server.MCPServer {
21+ func NewServer (getClient GetClientFn , toolsetGroup * toolsets. ToolsetGroup , version string , readOnly bool , t translations.TranslationHelperFunc ) * server.MCPServer {
2122 // Create a new MCP server
2223 s := server .NewMCPServer (
2324 "github-mcp-server" ,
2425 version ,
2526 server .WithResourceCapabilities (true , true ),
2627 server .WithLogging ())
2728
28- // Add GitHub Resources
29- s .AddResourceTemplate (GetRepositoryResourceContent (getClient , t ))
30- s .AddResourceTemplate (GetRepositoryResourceBranchContent (getClient , t ))
31- s .AddResourceTemplate (GetRepositoryResourceCommitContent (getClient , t ))
32- s .AddResourceTemplate (GetRepositoryResourceTagContent (getClient , t ))
33- s .AddResourceTemplate (GetRepositoryResourcePrContent (getClient , t ))
34-
35- // Add GitHub tools - Issues
36- s .AddTool (GetIssue (getClient , t ))
37- s .AddTool (SearchIssues (getClient , t ))
38- s .AddTool (ListIssues (getClient , t ))
39- s .AddTool (GetIssueComments (getClient , t ))
40- if ! readOnly {
41- s .AddTool (CreateIssue (getClient , t ))
42- s .AddTool (AddIssueComment (getClient , t ))
43- s .AddTool (UpdateIssue (getClient , t ))
29+ // Add GitHub tools - Users
30+ s .AddTool (GetMe (getClient , t )) // GetMe is always exposed and not part of configurable features
31+
32+ if toolsetGroup .IsEnabled ("repos" ) {
33+ // Add GitHub Repository Resources
34+ s .AddResourceTemplate (GetRepositoryResourceContent (getClient , t ))
35+ s .AddResourceTemplate (GetRepositoryResourceBranchContent (getClient , t ))
36+ s .AddResourceTemplate (GetRepositoryResourceCommitContent (getClient , t ))
37+ s .AddResourceTemplate (GetRepositoryResourceTagContent (getClient , t ))
38+ s .AddResourceTemplate (GetRepositoryResourcePrContent (getClient , t ))
39+
40+ // Add GitHub tools - Repositories
41+ s .AddTool (SearchRepositories (getClient , t ))
42+ s .AddTool (GetFileContents (getClient , t ))
43+ s .AddTool (ListCommits (getClient , t ))
44+ if ! readOnly {
45+ s .AddTool (CreateOrUpdateFile (getClient , t ))
46+ s .AddTool (CreateRepository (getClient , t ))
47+ s .AddTool (ForkRepository (getClient , t ))
48+ s .AddTool (CreateBranch (getClient , t ))
49+ s .AddTool (PushFiles (getClient , t ))
50+ }
4451 }
4552
46- // Add GitHub tools - Pull Requests
47- s .AddTool (GetPullRequest (getClient , t ))
48- s .AddTool (ListPullRequests (getClient , t ))
49- s .AddTool (GetPullRequestFiles (getClient , t ))
50- s .AddTool (GetPullRequestStatus (getClient , t ))
51- s .AddTool (GetPullRequestComments (getClient , t ))
52- s .AddTool (GetPullRequestReviews (getClient , t ))
53- if ! readOnly {
54- s .AddTool (MergePullRequest (getClient , t ))
55- s .AddTool (UpdatePullRequestBranch (getClient , t ))
56- s .AddTool (CreatePullRequestReview (getClient , t ))
57- s .AddTool (CreatePullRequest (getClient , t ))
58- s .AddTool (UpdatePullRequest (getClient , t ))
53+ if toolsetGroup .IsEnabled ("issues" ) {
54+ // Add GitHub tools - Issues
55+ s .AddTool (GetIssue (getClient , t ))
56+ s .AddTool (SearchIssues (getClient , t ))
57+ s .AddTool (ListIssues (getClient , t ))
58+ s .AddTool (GetIssueComments (getClient , t ))
59+ if ! readOnly {
60+ s .AddTool (CreateIssue (getClient , t ))
61+ s .AddTool (AddIssueComment (getClient , t ))
62+ s .AddTool (UpdateIssue (getClient , t ))
63+ }
5964 }
6065
61- // Add GitHub tools - Repositories
62- s .AddTool (SearchRepositories (getClient , t ))
63- s .AddTool (GetFileContents (getClient , t ))
64- s .AddTool (ListCommits (getClient , t ))
65- if ! readOnly {
66- s .AddTool (CreateOrUpdateFile (getClient , t ))
67- s .AddTool (CreateRepository (getClient , t ))
68- s .AddTool (ForkRepository (getClient , t ))
69- s .AddTool (CreateBranch (getClient , t ))
70- s .AddTool (PushFiles (getClient , t ))
66+ if toolsetGroup .IsEnabled ("pull_requests" ) {
67+ // Add GitHub tools - Pull Requests
68+ s .AddTool (GetPullRequest (getClient , t ))
69+ s .AddTool (ListPullRequests (getClient , t ))
70+ s .AddTool (GetPullRequestFiles (getClient , t ))
71+ s .AddTool (GetPullRequestStatus (getClient , t ))
72+ s .AddTool (GetPullRequestComments (getClient , t ))
73+ s .AddTool (GetPullRequestReviews (getClient , t ))
74+ if ! readOnly {
75+ s .AddTool (MergePullRequest (getClient , t ))
76+ s .AddTool (UpdatePullRequestBranch (getClient , t ))
77+ s .AddTool (CreatePullRequestReview (getClient , t ))
78+ s .AddTool (CreatePullRequest (getClient , t ))
79+ }
7180 }
7281
73- // Add GitHub tools - Search
74- s .AddTool (SearchCode (getClient , t ))
75- s .AddTool (SearchUsers (getClient , t ))
82+ if toolsetGroup .IsEnabled ("search" ) {
83+ // Add GitHub tools - Search
84+ s .AddTool (SearchCode (getClient , t ))
85+ s .AddTool (SearchUsers (getClient , t ))
86+ }
7687
77- // Add GitHub tools - Users
78- s .AddTool (GetMe (getClient , t ))
88+ if toolsetGroup .IsEnabled ("code_security" ) {
89+ // Add GitHub tools - Code Scanning
90+ s .AddTool (GetCodeScanningAlert (getClient , t ))
91+ s .AddTool (ListCodeScanningAlerts (getClient , t ))
92+ }
93+
94+ if toolsetGroup .IsEnabled ("experiments" ) {
95+ s .AddTool (ListAvailableToolsets (toolsetGroup , t ))
96+ s .AddTool (EnableToolset (s , toolsetGroup , t ))
97+ }
7998
80- // Add GitHub tools - Code Scanning
81- s .AddTool (GetCodeScanningAlert (getClient , t ))
82- s .AddTool (ListCodeScanningAlerts (getClient , t ))
8399 return s
84100}
85101
@@ -119,6 +135,48 @@ func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mc
119135 }
120136}
121137
138+ func EnableToolset (s * server.MCPServer , toolsets * toolsets.ToolsetGroup , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
139+ return mcp .NewTool ("enable_toolset" ,
140+ mcp .WithDescription (t ("TOOL_ENABLE_TOOLSET_DESCRIPTION" , "Enable one of the sets of tools this MCP server provides." )),
141+ mcp .WithString ("toolset" ,
142+ mcp .Required (),
143+ mcp .Description ("The name of the toolset to enable" ),
144+ mcp .Enum ("repos" , "issues" , "search" , "pull_requests" , "code_security" , "experiments" ),
145+ ),
146+ ),
147+ func (_ context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
148+ // We need to convert the toolsets back to a map for JSON serialization
149+ toolsetName , err := requiredParam [string ](request , "toolset" )
150+ if err != nil {
151+ return mcp .NewToolResultError (err .Error ()), nil
152+ }
153+ toolsets .EnableFeature (toolsetName )
154+ // TODO s.AddTool()
155+ // TODO SEND TOOL UPDATE TO CLIENT
156+ return mcp .NewToolResultText (fmt .Sprintf ("Toolset %s enabled" , toolsetName )), nil
157+ }
158+ }
159+
160+ func ListAvailableToolsets (toolsetGroup * toolsets.ToolsetGroup , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
161+ return mcp .NewTool ("list_available_toolsets" ,
162+ mcp .WithDescription (t ("TOOL_LIST_AVAILABLE_FEATURES_DESCRIPTION" , "List all available toolsets this MCP server can offer, providing the enabled status of each." )),
163+ ),
164+ func (_ context.Context , _ mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
165+ // We need to convert the toolsetGroup back to a map for JSON serialization
166+ featureMap := make (map [string ]bool )
167+ for name := range toolsetGroup .Toolsets {
168+ featureMap [name ] = toolsetGroup .IsEnabled (name )
169+ }
170+
171+ r , err := json .Marshal (featureMap )
172+ if err != nil {
173+ return nil , fmt .Errorf ("failed to marshal features: %w" , err )
174+ }
175+
176+ return mcp .NewToolResultText (string (r )), nil
177+ }
178+ }
179+
122180// OptionalParamOK is a helper function that can be used to fetch a requested parameter from the request.
123181// It returns the value, a boolean indicating if the parameter was present, and an error if the type is wrong.
124182func OptionalParamOK [T any ](r mcp.CallToolRequest , p string ) (value T , ok bool , err error ) {
0 commit comments