Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0dff8b8

Browse files
feat: partition tools by product/feature
1 parent4cf96ab commit0dff8b8

File tree

5 files changed

+517
-48
lines changed

5 files changed

+517
-48
lines changed

‎cmd/github-mcp-server/main.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
stdlog"log"
1010
"os"
1111
"os/signal"
12+
"strings"
1213
"syscall"
1314

15+
"github.com/github/github-mcp-server/pkg/features"
1416
"github.com/github/github-mcp-server/pkg/github"
1517
iolog"github.com/github/github-mcp-server/pkg/log"
1618
"github.com/github/github-mcp-server/pkg/translations"
@@ -46,13 +48,20 @@ var (
4648
iferr!=nil {
4749
stdlog.Fatal("Failed to initialize logger:",err)
4850
}
51+
enabledFeatures:=viper.GetStringSlice("features")
52+
features,err:=initFeatures(enabledFeatures)
53+
iferr!=nil {
54+
stdlog.Fatal("Failed to initialize features:",err)
55+
}
56+
4957
logCommands:=viper.GetBool("enable-command-logging")
5058
cfg:=runConfig{
5159
readOnly:readOnly,
5260
logger:logger,
5361
logCommands:logCommands,
5462
exportTranslations:exportTranslations,
5563
prettyPrintJSON:prettyPrintJSON,
64+
features:features,
5665
}
5766
iferr:=runStdioServer(cfg);err!=nil {
5867
stdlog.Fatal("failed to run stdio server:",err)
@@ -61,10 +70,45 @@ var (
6170
}
6271
)
6372

73+
funcinitFeatures(passedFeatures []string) (*features.FeatureSet,error) {
74+
// Create a new feature set
75+
fs:=features.NewFeatureSet()
76+
77+
// Define all available features with their default state (disabled)
78+
fs.AddFeature("repos","Repository related tools",false)
79+
fs.AddFeature("issues","Issues related tools",false)
80+
fs.AddFeature("search","Search related tools",false)
81+
fs.AddFeature("pull_requests","Pull request related tools",false)
82+
fs.AddFeature("code_security","Code security related tools",false)
83+
fs.AddFeature("experiments","Experimental features that are not considered stable yet",false)
84+
85+
// fs.AddFeature("actions", "GitHub Actions related tools", false)
86+
// fs.AddFeature("projects", "GitHub Projects related tools", false)
87+
// fs.AddFeature("secret_protection", "Secret protection related tools", false)
88+
// fs.AddFeature("gists", "Gist related tools", false)
89+
90+
// Env gets precedence over command line flags
91+
ifenvFeats:=os.Getenv("GITHUB_FEATURES");envFeats!="" {
92+
passedFeatures= []string{}
93+
// Split envFeats by comma, trim whitespace, and add to the slice
94+
for_,feature:=rangestrings.Split(envFeats,",") {
95+
passedFeatures=append(passedFeatures,strings.TrimSpace(feature))
96+
}
97+
}
98+
99+
// Enable the requested features
100+
iferr:=fs.EnableFeatures(passedFeatures);err!=nil {
101+
returnnil,err
102+
}
103+
104+
returnfs,nil
105+
}
106+
64107
funcinit() {
65108
cobra.OnInitialize(initConfig)
66109

67110
// Add global flags that will be shared by all commands
111+
rootCmd.PersistentFlags().StringSlice("features", []string{"repos","issues","pull_requests","search"},"A comma separated list of groups of tools to enable, defaults to issues/repos/search")
68112
rootCmd.PersistentFlags().Bool("read-only",false,"Restrict the server to read-only operations")
69113
rootCmd.PersistentFlags().String("log-file","","Path to log file")
70114
rootCmd.PersistentFlags().Bool("enable-command-logging",false,"When enabled, the server will log all command requests and responses to the log file")
@@ -73,6 +117,7 @@ func init() {
73117
rootCmd.PersistentFlags().Bool("pretty-print-json",false,"Pretty print JSON output")
74118

75119
// Bind flag to viper
120+
_=viper.BindPFlag("features",rootCmd.PersistentFlags().Lookup("features"))
76121
_=viper.BindPFlag("read-only",rootCmd.PersistentFlags().Lookup("read-only"))
77122
_=viper.BindPFlag("log-file",rootCmd.PersistentFlags().Lookup("log-file"))
78123
_=viper.BindPFlag("enable-command-logging",rootCmd.PersistentFlags().Lookup("enable-command-logging"))
@@ -113,6 +158,7 @@ type runConfig struct {
113158
logCommandsbool
114159
exportTranslationsbool
115160
prettyPrintJSONbool
161+
features*features.FeatureSet
116162
}
117163

118164
// JSONPrettyPrintWriter is a Writer that pretty prints input to indented JSON
@@ -158,7 +204,7 @@ func runStdioServer(cfg runConfig) error {
158204
t,dumpTranslations:=translations.TranslationHelper()
159205

160206
// Create
161-
ghServer:=github.NewServer(ghClient,version,cfg.readOnly,t)
207+
ghServer:=github.NewServer(ghClient,cfg.features,version,cfg.readOnly,t)
162208
stdioServer:=server.NewStdioServer(ghServer)
163209

164210
stdLogger:=stdlog.New(cfg.logger.Writer(),"stdioserver",0)

‎pkg/features/features.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package features
2+
3+
import"fmt"
4+
5+
typeFeaturestruct {
6+
Namestring
7+
Descriptionstring
8+
Enabledbool
9+
}
10+
11+
typeFeatureSetstruct {
12+
Featuresmap[string]Feature
13+
everythingOnbool
14+
}
15+
16+
funcNewFeatureSet()*FeatureSet {
17+
return&FeatureSet{
18+
Features:make(map[string]Feature),
19+
everythingOn:false,
20+
}
21+
}
22+
23+
func (fs*FeatureSet)AddFeature(namestring,descriptionstring,enabledbool) {
24+
fs.Features[name]=Feature{
25+
Name:name,
26+
Description:description,
27+
Enabled:enabled,
28+
}
29+
}
30+
31+
func (fs*FeatureSet)IsEnabled(namestring)bool {
32+
// If everythingOn is true, all features are enabled
33+
iffs.everythingOn {
34+
returntrue
35+
}
36+
37+
feature,exists:=fs.Features[name]
38+
if!exists {
39+
returnfalse
40+
}
41+
returnfeature.Enabled
42+
}
43+
44+
func (fs*FeatureSet)EnableFeatures(names []string)error {
45+
for_,name:=rangenames {
46+
err:=fs.EnableFeature(name)
47+
iferr!=nil {
48+
returnerr
49+
}
50+
}
51+
returnnil
52+
}
53+
54+
func (fs*FeatureSet)EnableFeature(namestring)error {
55+
// Special case for "everything"
56+
ifname=="everything" {
57+
fs.everythingOn=true
58+
returnnil
59+
}
60+
61+
feature,exists:=fs.Features[name]
62+
if!exists {
63+
returnfmt.Errorf("feature %s does not exist",name)
64+
}
65+
feature.Enabled=true
66+
fs.Features[name]=feature
67+
returnnil
68+
}

‎pkg/features/features_test.go

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
package features
2+
3+
import (
4+
"testing"
5+
)
6+
7+
funcTestNewFeatureSet(t*testing.T) {
8+
fs:=NewFeatureSet()
9+
iffs==nil {
10+
t.Fatal("Expected NewFeatureSet to return a non-nil pointer")
11+
}
12+
iffs.Features==nil {
13+
t.Fatal("Expected Features map to be initialized")
14+
}
15+
iflen(fs.Features)!=0 {
16+
t.Fatalf("Expected Features map to be empty, got %d items",len(fs.Features))
17+
}
18+
iffs.everythingOn {
19+
t.Fatal("Expected everythingOn to be initialized as false")
20+
}
21+
}
22+
23+
funcTestAddFeature(t*testing.T) {
24+
fs:=NewFeatureSet()
25+
26+
// Test adding a feature
27+
fs.AddFeature("test-feature","A test feature",true)
28+
29+
// Verify feature was added correctly
30+
iflen(fs.Features)!=1 {
31+
t.Errorf("Expected 1 feature, got %d",len(fs.Features))
32+
}
33+
34+
feature,exists:=fs.Features["test-feature"]
35+
if!exists {
36+
t.Fatal("Feature was not added to the map")
37+
}
38+
39+
iffeature.Name!="test-feature" {
40+
t.Errorf("Expected feature name to be 'test-feature', got '%s'",feature.Name)
41+
}
42+
43+
iffeature.Description!="A test feature" {
44+
t.Errorf("Expected feature description to be 'A test feature', got '%s'",feature.Description)
45+
}
46+
47+
if!feature.Enabled {
48+
t.Error("Expected feature to be enabled")
49+
}
50+
51+
// Test adding another feature
52+
fs.AddFeature("another-feature","Another test feature",false)
53+
54+
iflen(fs.Features)!=2 {
55+
t.Errorf("Expected 2 features, got %d",len(fs.Features))
56+
}
57+
58+
// Test overriding existing feature
59+
fs.AddFeature("test-feature","Updated description",false)
60+
61+
feature=fs.Features["test-feature"]
62+
iffeature.Description!="Updated description" {
63+
t.Errorf("Expected feature description to be updated to 'Updated description', got '%s'",feature.Description)
64+
}
65+
66+
iffeature.Enabled {
67+
t.Error("Expected feature to be disabled after update")
68+
}
69+
}
70+
71+
funcTestIsEnabled(t*testing.T) {
72+
fs:=NewFeatureSet()
73+
74+
// Test with non-existent feature
75+
iffs.IsEnabled("non-existent") {
76+
t.Error("Expected IsEnabled to return false for non-existent feature")
77+
}
78+
79+
// Test with disabled feature
80+
fs.AddFeature("disabled-feature","A disabled feature",false)
81+
iffs.IsEnabled("disabled-feature") {
82+
t.Error("Expected IsEnabled to return false for disabled feature")
83+
}
84+
85+
// Test with enabled feature
86+
fs.AddFeature("enabled-feature","An enabled feature",true)
87+
if!fs.IsEnabled("enabled-feature") {
88+
t.Error("Expected IsEnabled to return true for enabled feature")
89+
}
90+
}
91+
92+
funcTestEnableFeature(t*testing.T) {
93+
fs:=NewFeatureSet()
94+
95+
// Test enabling non-existent feature
96+
err:=fs.EnableFeature("non-existent")
97+
iferr==nil {
98+
t.Error("Expected error when enabling non-existent feature")
99+
}
100+
101+
// Test enabling feature
102+
fs.AddFeature("test-feature","A test feature",false)
103+
104+
iffs.IsEnabled("test-feature") {
105+
t.Error("Expected feature to be disabled initially")
106+
}
107+
108+
err=fs.EnableFeature("test-feature")
109+
iferr!=nil {
110+
t.Errorf("Expected no error when enabling feature, got: %v",err)
111+
}
112+
113+
if!fs.IsEnabled("test-feature") {
114+
t.Error("Expected feature to be enabled after EnableFeature call")
115+
}
116+
117+
// Test enabling already enabled feature
118+
err=fs.EnableFeature("test-feature")
119+
iferr!=nil {
120+
t.Errorf("Expected no error when enabling already enabled feature, got: %v",err)
121+
}
122+
}
123+
124+
funcTestEnableFeatures(t*testing.T) {
125+
fs:=NewFeatureSet()
126+
127+
// Prepare features
128+
fs.AddFeature("feature1","Feature 1",false)
129+
fs.AddFeature("feature2","Feature 2",false)
130+
131+
// Test enabling multiple features
132+
err:=fs.EnableFeatures([]string{"feature1","feature2"})
133+
iferr!=nil {
134+
t.Errorf("Expected no error when enabling features, got: %v",err)
135+
}
136+
137+
if!fs.IsEnabled("feature1") {
138+
t.Error("Expected feature1 to be enabled")
139+
}
140+
141+
if!fs.IsEnabled("feature2") {
142+
t.Error("Expected feature2 to be enabled")
143+
}
144+
145+
// Test with non-existent feature in the list
146+
err=fs.EnableFeatures([]string{"feature1","non-existent"})
147+
iferr==nil {
148+
t.Error("Expected error when enabling list with non-existent feature")
149+
}
150+
151+
// Test with empty list
152+
err=fs.EnableFeatures([]string{})
153+
iferr!=nil {
154+
t.Errorf("Expected no error with empty feature list, got: %v",err)
155+
}
156+
157+
// Test enabling everything through EnableFeatures
158+
fs=NewFeatureSet()
159+
err=fs.EnableFeatures([]string{"everything"})
160+
iferr!=nil {
161+
t.Errorf("Expected no error when enabling 'everything', got: %v",err)
162+
}
163+
164+
if!fs.everythingOn {
165+
t.Error("Expected everythingOn to be true after enabling 'everything' via EnableFeatures")
166+
}
167+
}
168+
169+
funcTestEnableEverything(t*testing.T) {
170+
fs:=NewFeatureSet()
171+
172+
// Add a disabled feature
173+
fs.AddFeature("test-feature","A test feature",false)
174+
175+
// Verify it's disabled
176+
iffs.IsEnabled("test-feature") {
177+
t.Error("Expected feature to be disabled initially")
178+
}
179+
180+
// Enable "everything"
181+
err:=fs.EnableFeature("everything")
182+
iferr!=nil {
183+
t.Errorf("Expected no error when enabling 'everything', got: %v",err)
184+
}
185+
186+
// Verify everythingOn was set
187+
if!fs.everythingOn {
188+
t.Error("Expected everythingOn to be true after enabling 'everything'")
189+
}
190+
191+
// Verify the previously disabled feature is now enabled
192+
if!fs.IsEnabled("test-feature") {
193+
t.Error("Expected feature to be enabled when everythingOn is true")
194+
}
195+
196+
// Verify a non-existent feature is also enabled
197+
if!fs.IsEnabled("non-existent") {
198+
t.Error("Expected non-existent feature to be enabled when everythingOn is true")
199+
}
200+
}
201+
202+
funcTestIsEnabledWithEverythingOn(t*testing.T) {
203+
fs:=NewFeatureSet()
204+
205+
// Enable "everything"
206+
err:=fs.EnableFeature("everything")
207+
iferr!=nil {
208+
t.Errorf("Expected no error when enabling 'everything', got: %v",err)
209+
}
210+
211+
// Test that any feature name returns true with IsEnabled
212+
if!fs.IsEnabled("some-feature") {
213+
t.Error("Expected IsEnabled to return true for any feature when everythingOn is true")
214+
}
215+
216+
if!fs.IsEnabled("another-feature") {
217+
t.Error("Expected IsEnabled to return true for any feature when everythingOn is true")
218+
}
219+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp