@@ -9,11 +9,15 @@ import (
9
9
"os"
10
10
"os/exec"
11
11
"slices"
12
+ "strings"
12
13
"sync"
13
14
"testing"
14
15
"time"
15
16
16
- "github.com/google/go-github/v69/github"
17
+ "github.com/github/github-mcp-server/internal/ghmcp"
18
+ "github.com/github/github-mcp-server/pkg/github"
19
+ "github.com/github/github-mcp-server/pkg/translations"
20
+ gogithub "github.com/google/go-github/v69/github"
17
21
mcpClient "github.com/mark3labs/mcp-go/client"
18
22
"github.com/mark3labs/mcp-go/mcp"
19
23
"github.com/stretchr/testify/require"
@@ -56,68 +60,90 @@ func ensureDockerImageBuilt(t *testing.T) {
56
60
require .NoError (t , buildError , "expected to build Docker image successfully" )
57
61
}
58
62
59
- // ClientOpts holds configuration options for the MCP client setup
60
- type ClientOpts struct {
61
- // Environment variables to set before starting the client
62
- EnvVars map [ string ]string
63
+ // clientOpts holds configuration options for the MCP client setup
64
+ type clientOpts struct {
65
+ // Toolsets to enable in the MCP server
66
+ enabledToolsets [ ]string
63
67
}
64
68
65
- // ClientOption defines a function type for configuring ClientOpts
66
- type ClientOption func (* ClientOpts )
69
+ // clientOption defines a function type for configuring ClientOpts
70
+ type clientOption func (* clientOpts )
67
71
68
- // WithEnvVars returns an option that adds environment variables to the client options
69
- func WithEnvVars (envVars map [string ]string ) ClientOption {
70
- return func (opts * ClientOpts ) {
71
- opts .EnvVars = envVars
72
+ // withToolsets returns an option that either sets an Env Var when executing in docker,
73
+ // or sets the toolsets in the MCP server when running in-process.
74
+ func withToolsets (toolsets []string ) clientOption {
75
+ return func (opts * clientOpts ) {
76
+ opts .enabledToolsets = toolsets
72
77
}
73
78
}
74
79
75
- // setupMCPClient sets up the test environment and returns an initialized MCP client
76
- // It handles token retrieval, Docker image building, and applying the provided options
77
- func setupMCPClient (t * testing.T , options ... ClientOption ) * mcpClient.Client {
80
+ func setupMCPClient (t * testing.T , options ... clientOption ) * mcpClient.Client {
78
81
// Get token and ensure Docker image is built
79
82
token := getE2EToken (t )
80
- ensureDockerImageBuilt (t )
81
83
82
84
// Create and configure options
83
- opts := & ClientOpts {
84
- EnvVars : make (map [string ]string ),
85
- }
85
+ opts := & clientOpts {}
86
86
87
87
// Apply all options to configure the opts struct
88
88
for _ , option := range options {
89
89
option (opts )
90
90
}
91
91
92
- // Prepare Docker arguments
93
- args := []string {
94
- "docker" ,
95
- "run" ,
96
- "-i" ,
97
- "--rm" ,
98
- "-e" ,
99
- "GITHUB_PERSONAL_ACCESS_TOKEN" , // Personal access token is all required
100
- }
92
+ // By default, we run the tests including the Docker image, but with DEBUG
93
+ // enabled, we run the server in-process, allowing for easier debugging.
94
+ var client * mcpClient.Client
95
+ if os .Getenv ("GITHUB_MCP_SERVER_E2E_DEBUG" ) == "" {
96
+ ensureDockerImageBuilt (t )
97
+
98
+ // Prepare Docker arguments
99
+ args := []string {
100
+ "docker" ,
101
+ "run" ,
102
+ "-i" ,
103
+ "--rm" ,
104
+ "-e" ,
105
+ "GITHUB_PERSONAL_ACCESS_TOKEN" , // Personal access token is all required
106
+ }
101
107
102
- // Add all environment variables to the Docker arguments
103
- for key := range opts .EnvVars {
104
- args = append (args , "-e" , key )
105
- }
108
+ // Add toolsets environment variable to the Docker arguments
109
+ if len ( opts .enabledToolsets ) > 0 {
110
+ args = append (args , "-e" , "GITHUB_TOOLSETS" )
111
+ }
106
112
107
- // Add the image name
108
- args = append (args , "github/e2e-github-mcp-server" )
113
+ // Add the image name
114
+ args = append (args , "github/e2e-github-mcp-server" )
115
+
116
+ // Construct the env vars for the MCP Client to execute docker with
117
+ dockerEnvVars := make ([]string , 0 , len (opts .enabledToolsets )+ 1 )
118
+ dockerEnvVars = append (dockerEnvVars , fmt .Sprintf ("GITHUB_PERSONAL_ACCESS_TOKEN=%s" , token ))
119
+ dockerEnvVars = append (dockerEnvVars , fmt .Sprintf ("GITHUB_TOOLSETS=%s" , strings .Join (opts .enabledToolsets , "," )))
120
+
121
+ // Create the client
122
+ t .Log ("Starting Stdio MCP client..." )
123
+ var err error
124
+ client , err = mcpClient .NewStdioMCPClient (args [0 ], dockerEnvVars , args [1 :]... )
125
+ require .NoError (t , err , "expected to create client successfully" )
126
+ } else {
127
+ // We need this because the fully compiled server has a default for the viper config, which is
128
+ // not in scope for using the MCP server directly. This probably indicates that we should refactor
129
+ // so that there is a shared setup mechanism, but let's wait till we feel more friction.
130
+ enabledToolsets := opts .enabledToolsets
131
+ if enabledToolsets == nil {
132
+ enabledToolsets = github .DefaultTools
133
+ }
134
+
135
+ ghServer , err := ghmcp .NewMCPServer (ghmcp.MCPServerConfig {
136
+ Token : token ,
137
+ EnabledToolsets : enabledToolsets ,
138
+ Translator : translations .NullTranslationHelper ,
139
+ })
140
+ require .NoError (t , err , "expected to construct MCP server successfully" )
109
141
110
- // Construct the env vars for the MCP Client to execute docker with
111
- dockerEnvVars := make ([]string , 0 , len (opts .EnvVars )+ 1 )
112
- dockerEnvVars = append (dockerEnvVars , fmt .Sprintf ("GITHUB_PERSONAL_ACCESS_TOKEN=%s" , token ))
113
- for key , value := range opts .EnvVars {
114
- dockerEnvVars = append (dockerEnvVars , fmt .Sprintf ("%s=%s" , key , value ))
142
+ t .Log ("Starting In Process MCP client..." )
143
+ client , err = mcpClient .NewInProcessClient (ghServer )
144
+ require .NoError (t , err , "expected to create in-process client successfully" )
115
145
}
116
146
117
- // Create the client
118
- t .Log ("Starting Stdio MCP client..." )
119
- client , err := mcpClient .NewStdioMCPClient (args [0 ], dockerEnvVars , args [1 :]... )
120
- require .NoError (t , err , "expected to create client successfully" )
121
147
t .Cleanup (func () {
122
148
require .NoError (t , client .Close (), "expected to close client successfully" )
123
149
})
@@ -169,7 +195,7 @@ func TestGetMe(t *testing.T) {
169
195
170
196
// Then the login in the response should match the login obtained via the same
171
197
// token using the GitHub API.
172
- ghClient := github .NewClient (nil ).WithAuthToken (getE2EToken (t ))
198
+ ghClient := gogithub .NewClient (nil ).WithAuthToken (getE2EToken (t ))
173
199
user , _ , err := ghClient .Users .Get (context .Background (), "" )
174
200
require .NoError (t , err , "expected to get user successfully" )
175
201
require .Equal (t , trimmedContent .Login , * user .Login , "expected login to match" )
@@ -181,9 +207,7 @@ func TestToolsets(t *testing.T) {
181
207
182
208
mcpClient := setupMCPClient (
183
209
t ,
184
- WithEnvVars (map [string ]string {
185
- "GITHUB_TOOLSETS" : "repos,issues" ,
186
- }),
210
+ withToolsets ([]string {"repos" , "issues" }),
187
211
)
188
212
189
213
ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
@@ -208,6 +232,8 @@ func TestToolsets(t *testing.T) {
208
232
}
209
233
210
234
func TestTags (t * testing.T ) {
235
+ t .Parallel ()
236
+
211
237
mcpClient := setupMCPClient (t )
212
238
213
239
ctx := context .Background ()
@@ -253,32 +279,32 @@ func TestTags(t *testing.T) {
253
279
// Cleanup the repository after the test
254
280
t .Cleanup (func () {
255
281
// MCP Server doesn't support deletions, but we can use the GitHub Client
256
- ghClient := github .NewClient (nil ).WithAuthToken (getE2EToken (t ))
282
+ ghClient := gogithub .NewClient (nil ).WithAuthToken (getE2EToken (t ))
257
283
t .Logf ("Deleting repository %s/%s..." , currentOwner , repoName )
258
284
_ , err := ghClient .Repositories .Delete (context .Background (), currentOwner , repoName )
259
285
require .NoError (t , err , "expected to delete repository successfully" )
260
286
})
261
287
262
288
// Then create a tag
263
289
// MCP Server doesn't support tag creation, but we can use the GitHub Client
264
- ghClient := github .NewClient (nil ).WithAuthToken (getE2EToken (t ))
290
+ ghClient := gogithub .NewClient (nil ).WithAuthToken (getE2EToken (t ))
265
291
t .Logf ("Creating tag %s/%s:%s..." , currentOwner , repoName , "v0.0.1" )
266
292
ref , _ , err := ghClient .Git .GetRef (context .Background (), currentOwner , repoName , "refs/heads/main" )
267
293
require .NoError (t , err , "expected to get ref successfully" )
268
294
269
- tagObj , _ , err := ghClient .Git .CreateTag (context .Background (), currentOwner , repoName , & github .Tag {
270
- Tag : github .Ptr ("v0.0.1" ),
271
- Message : github .Ptr ("v0.0.1" ),
272
- Object : & github .GitObject {
295
+ tagObj , _ , err := ghClient .Git .CreateTag (context .Background (), currentOwner , repoName , & gogithub .Tag {
296
+ Tag : gogithub .Ptr ("v0.0.1" ),
297
+ Message : gogithub .Ptr ("v0.0.1" ),
298
+ Object : & gogithub .GitObject {
273
299
SHA : ref .Object .SHA ,
274
- Type : github .Ptr ("commit" ),
300
+ Type : gogithub .Ptr ("commit" ),
275
301
},
276
302
})
277
303
require .NoError (t , err , "expected to create tag object successfully" )
278
304
279
- _ , _ , err = ghClient .Git .CreateRef (context .Background (), currentOwner , repoName , & github .Reference {
280
- Ref : github .Ptr ("refs/tags/v0.0.1" ),
281
- Object : & github .GitObject {
305
+ _ , _ , err = ghClient .Git .CreateRef (context .Background (), currentOwner , repoName , & gogithub .Reference {
306
+ Ref : gogithub .Ptr ("refs/tags/v0.0.1" ),
307
+ Object : & gogithub .GitObject {
282
308
SHA : tagObj .SHA ,
283
309
},
284
310
})
0 commit comments