Skip to content

Commit bb4e2d9

Browse files
committed
add toolset default to make configuration easier
1 parent 99acea6 commit bb4e2d9

File tree

5 files changed

+216
-3
lines changed

5 files changed

+216
-3
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,9 @@ docker run -i --rm \
359359
ghcr.io/github/github-mcp-server
360360
```
361361

362-
### The "all" Toolset
362+
### Special toolsets
363+
364+
#### "all" toolset
363365

364366
The special toolset `all` can be provided to enable all available toolsets regardless of any other configuration:
365367

@@ -373,6 +375,19 @@ Or using the environment variable:
373375
GITHUB_TOOLSETS="all" ./github-mcp-server
374376
```
375377

378+
#### "default" toolset
379+
380+
The default toolset `default` is the configuration that gets passed to the server if no toolsets are specified.
381+
382+
It consists of:
383+
- ...
384+
385+
To keep the default configuration and add additional toolsets, just specify "default, <additional-toolset>"
386+
387+
```bash
388+
GITHUB_TOOLSETS="default,stargazers" ./github-mcp-server
389+
```
390+
376391
### Available Toolsets
377392

378393
The following sets of tools are available (all are on by default):

cmd/github-mcp-server/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ var (
4545
return fmt.Errorf("failed to unmarshal toolsets: %w", err)
4646
}
4747

48+
// No passed toolsets configuration means we enable the default toolset
4849
if len(enabledToolsets) == 0 {
49-
enabledToolsets = github.GetDefaultToolsetIDs()
50+
enabledToolsets = []string{github.ToolsetMetadataDefault.ID}
5051
}
5152

5253
stdioServerConfig := ghmcp.StdioServerConfig{

internal/ghmcp/server.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,14 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
111111
// filter "all" from the enabled toolsets
112112
enabledToolsets = make([]string, 0, len(cfg.EnabledToolsets))
113113
for _, toolset := range cfg.EnabledToolsets {
114-
if toolset != "all" {
114+
if toolset != github.ToolsetMetadataAll.ID {
115115
enabledToolsets = append(enabledToolsets, toolset)
116116
}
117117
}
118118
}
119119

120+
enabledToolsets = transformDefault(enabledToolsets)
121+
120122
// Generate instructions based on enabled toolsets
121123
instructions := github.GenerateInstructions(enabledToolsets)
122124

@@ -470,3 +472,34 @@ func (t *bearerAuthTransport) RoundTrip(req *http.Request) (*http.Response, erro
470472
req.Header.Set("Authorization", "Bearer "+t.token)
471473
return t.transport.RoundTrip(req)
472474
}
475+
476+
// transformDefault replaces "default" in the enabled toolsets with the actual default toolset IDs.
477+
// If "default" is present, it removes it and adds the default toolset IDs from GetDefaultToolsetIDs().
478+
// Duplicates are removed from the final result.
479+
func transformDefault(enabledToolsets []string) []string {
480+
hasDefault := false
481+
result := make([]string, 0, len(enabledToolsets))
482+
seen := make(map[string]bool)
483+
484+
// First pass: check if "default" exists and collect non-default toolsets
485+
for _, toolset := range enabledToolsets {
486+
if toolset == github.ToolsetMetadataDefault.ID {
487+
hasDefault = true
488+
} else if !seen[toolset] {
489+
result = append(result, toolset)
490+
seen[toolset] = true
491+
}
492+
}
493+
494+
// If "default" was found, add the default toolset IDs
495+
if hasDefault {
496+
for _, defaultToolset := range github.GetDefaultToolsetIDs() {
497+
if !seen[defaultToolset] {
498+
result = append(result, defaultToolset)
499+
seen[defaultToolset] = true
500+
}
501+
}
502+
}
503+
504+
return result
505+
}

internal/ghmcp/server_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package ghmcp
2+
3+
import (
4+
"testing"
5+
6+
"github.com/github/github-mcp-server/pkg/github"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestTransformDefault(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
input []string
15+
expected []string
16+
}{
17+
{
18+
name: "empty slice",
19+
input: []string{},
20+
expected: []string{},
21+
},
22+
{
23+
name: "default only",
24+
input: []string{"default"},
25+
expected: []string{
26+
"context",
27+
"repos",
28+
"issues",
29+
"pull_requests",
30+
"users",
31+
},
32+
},
33+
{
34+
name: "default with additional toolsets",
35+
input: []string{"default", "actions", "gists"},
36+
expected: []string{
37+
"actions",
38+
"gists",
39+
"context",
40+
"repos",
41+
"issues",
42+
"pull_requests",
43+
"users",
44+
},
45+
},
46+
{
47+
name: "default with overlapping toolsets",
48+
input: []string{"default", "issues", "actions"},
49+
expected: []string{
50+
"issues",
51+
"actions",
52+
"context",
53+
"repos",
54+
"pull_requests",
55+
"users",
56+
},
57+
},
58+
{
59+
name: "no default present",
60+
input: []string{"actions", "gists", "notifications"},
61+
expected: []string{"actions", "gists", "notifications"},
62+
},
63+
{
64+
name: "duplicate toolsets without default",
65+
input: []string{"actions", "gists", "actions"},
66+
expected: []string{"actions", "gists"},
67+
},
68+
{
69+
name: "duplicate toolsets with default",
70+
input: []string{"actions", "default", "actions", "issues"},
71+
expected: []string{
72+
"actions",
73+
"issues",
74+
"context",
75+
"repos",
76+
"pull_requests",
77+
"users",
78+
},
79+
},
80+
{
81+
name: "multiple defaults (edge case)",
82+
input: []string{"default", "actions", "default"},
83+
expected: []string{
84+
"actions",
85+
"context",
86+
"repos",
87+
"issues",
88+
"pull_requests",
89+
"users",
90+
},
91+
},
92+
{
93+
name: "all default toolsets already present with default",
94+
input: []string{"context", "repos", "issues", "pull_requests", "users", "default"},
95+
expected: []string{
96+
"context",
97+
"repos",
98+
"issues",
99+
"pull_requests",
100+
"users",
101+
},
102+
},
103+
}
104+
105+
for _, tt := range tests {
106+
t.Run(tt.name, func(t *testing.T) {
107+
result := transformDefault(tt.input)
108+
109+
// Check that the result has the correct length
110+
require.Len(t, result, len(tt.expected), "result length should match expected length")
111+
112+
// Create a map for easier comparison since order might vary
113+
resultMap := make(map[string]bool)
114+
for _, toolset := range result {
115+
resultMap[toolset] = true
116+
}
117+
118+
expectedMap := make(map[string]bool)
119+
for _, toolset := range tt.expected {
120+
expectedMap[toolset] = true
121+
}
122+
123+
// Check that both maps contain the same toolsets
124+
assert.Equal(t, expectedMap, resultMap, "result should contain all expected toolsets without duplicates")
125+
126+
// Verify no duplicates in result
127+
assert.Len(t, resultMap, len(result), "result should not contain duplicates")
128+
129+
// Verify "default" is not in the result
130+
assert.False(t, resultMap["default"], "result should not contain 'default'")
131+
})
132+
}
133+
}
134+
135+
func TestTransformDefaultWithActualDefaults(t *testing.T) {
136+
// This test verifies that the function uses the actual default toolsets from GetDefaultToolsetIDs()
137+
input := []string{"default"}
138+
result := transformDefault(input)
139+
140+
defaultToolsets := github.GetDefaultToolsetIDs()
141+
142+
// Check that result contains all default toolsets
143+
require.Len(t, result, len(defaultToolsets), "result should contain all default toolsets")
144+
145+
resultMap := make(map[string]bool)
146+
for _, toolset := range result {
147+
resultMap[toolset] = true
148+
}
149+
150+
for _, defaultToolset := range defaultToolsets {
151+
assert.True(t, resultMap[defaultToolset], "result should contain default toolset: %s", defaultToolset)
152+
}
153+
154+
// Verify "default" is not in the result
155+
assert.False(t, resultMap["default"], "result should not contain 'default'")
156+
}

pkg/github/tools.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ type ToolsetMetadata struct {
2323
}
2424

2525
var (
26+
ToolsetMetadataAll = ToolsetMetadata{
27+
ID: "all",
28+
Description: "Special toolset that enables all available toolsets",
29+
}
30+
ToolsetMetadataDefault = ToolsetMetadata{
31+
ID: "default",
32+
Description: "Special toolset that enables the default toolset configuration. When no toolsets are specified, this is the set that is enabled",
33+
}
2634
ToolsetMetadataContext = ToolsetMetadata{
2735
ID: "context",
2836
Description: "Tools that provide context about the current user and GitHub context you are operating in",

0 commit comments

Comments
 (0)