Skip to content

Commit 1d352f3

Browse files
xcoulonfbm3307
andauthored
build: upgrade to go-sdk v1.2.0 (#37)
see https://github.com/modelcontextprotocol/go-sdk/releases/tag/v1.2.0 also, refactor e2e tests: get rid of env vars and properly wait for MCP server to be ready --------- Signed-off-by: Xavier Coulon <xcoulon@redhat.com> Co-authored-by: Feny Mehta <fmehta@redhat.com>
1 parent 14d0337 commit 1d352f3

5 files changed

Lines changed: 76 additions & 26 deletions

File tree

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,26 @@ Example:
1919

2020
Requires [Go 1.24 (or higher)](https://go.dev/doc/install) and [Task](https://taskfile.dev/)
2121

22-
Build the binary with the following command:
22+
### Testing and Linting
23+
24+
```bash
25+
task test test-e2e lint
2326
```
27+
28+
### Building
29+
30+
Build the binary using the following command:
31+
32+
```bash
2433
task install
2534
```
2635

2736
Build the Container image with the following command:
28-
```
37+
38+
```bash
2939
task build-image
3040
```
3141

32-
3342
## Using the Argo CD MCP Server
3443

3544
### Obtaining a token to connect to Argo CD

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/argoproj/gitops-engine v0.7.1-0.20250905153922-d96c3d51e4c4
88
github.com/google/jsonschema-go v0.3.0
99
github.com/h2non/gock v1.2.0
10-
github.com/modelcontextprotocol/go-sdk v1.1.0
10+
github.com/modelcontextprotocol/go-sdk v1.2.0
1111
github.com/spf13/cobra v1.10.1
1212
github.com/stretchr/testify v1.10.0
1313
k8s.io/apimachinery v0.33.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU
239239
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
240240
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
241241
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
242-
github.com/modelcontextprotocol/go-sdk v1.1.0 h1:Qjayg53dnKC4UZ+792W21e4BpwEZBzwgRW6LrjLWSwA=
243-
github.com/modelcontextprotocol/go-sdk v1.1.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
242+
github.com/modelcontextprotocol/go-sdk v1.2.0 h1:Y23co09300CEk8iZ/tMxIX1dVmKZkzoSBZOpJwUnc/s=
243+
github.com/modelcontextprotocol/go-sdk v1.2.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
244244
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
245245
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
246246
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

test/e2e/argocd-mock/main.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"flag"
45
"fmt"
56
"log/slog"
67
"net/http"
@@ -11,13 +12,18 @@ import (
1112
)
1213

1314
func main() {
14-
listen := os.Getenv("ARGOCD_SERVER_LISTEN")
15-
token := os.Getenv("ARGOCD_SERVER_TOKEN")
16-
debug := os.Getenv("ARGOCD_SERVER_DEBUG")
15+
16+
var listen string
17+
var token string
18+
var debug bool
19+
flag.StringVar(&listen, "listen", "", "listen address")
20+
flag.StringVar(&token, "token", "", "token")
21+
flag.BoolVar(&debug, "debug", false, "debug mode")
22+
flag.Parse()
1723

1824
lvl := new(slog.LevelVar)
1925
lvl.Set(slog.LevelInfo)
20-
if debug == "true" {
26+
if debug {
2127
lvl.Set(slog.LevelDebug)
2228
}
2329
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{

test/e2e/server_test.go

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"net/http"
89
"os"
910
"os/exec"
1011
"strconv"
@@ -23,10 +24,18 @@ import (
2324
// Note: make sure you ran `task install` before running this test
2425
// ------------------------------------------------------------------------------------------------
2526

27+
const (
28+
MCPServerListen = "localhost:50081"
29+
MCPServerDebug = true
30+
ArgoCDMockListen = "localhost:50084"
31+
ArgoCDMockToken = "secure-token"
32+
ArgoCDMockDebug = true
33+
)
34+
2635
func TestServer(t *testing.T) {
2736

2837
// start the argocd mock server
29-
cmd := exec.CommandContext(context.Background(), "argocd-mock")
38+
cmd := exec.CommandContext(context.Background(), "argocd-mock", "--listen", ArgoCDMockListen, "--token", ArgoCDMockToken, "--debug", strconv.FormatBool(ArgoCDMockDebug)) //nolint:gosec // (it's ok to use `strconv.FormatBool`)
3039
cmd.Stdout = os.Stdout
3140
cmd.Stderr = os.Stderr
3241
go func() {
@@ -42,23 +51,17 @@ func TestServer(t *testing.T) {
4251
t.Logf("killed the Argo CD mock server: %v", cmd.String())
4352
}()
4453

45-
os.Setenv("MCP_SERVER_LISTEN", "localhost:50081")
46-
os.Setenv("MCP_SERVER_DEBUG", "true")
47-
os.Setenv("ARGOCD_SERVER_LISTEN", "localhost:50084")
48-
os.Setenv("ARGOCD_SERVER_TOKEN", "secure-token")
49-
os.Setenv("ARGOCD_SERVER_DEBUG", "true")
50-
5154
testdata := []struct {
5255
name string
5356
init func(*testing.T) (*mcp.ClientSession, KillMCPServerFunc)
5457
}{
5558
{
5659
name: "stdio",
57-
init: newStdioSession("localhost:50081", true, "http://localhost:50084", "secure-token"),
60+
init: newStdioSession(MCPServerListen, MCPServerDebug, "http://"+ArgoCDMockListen, ArgoCDMockToken),
5861
},
5962
{
6063
name: "http",
61-
init: newHTTPSession("localhost:50081", true, "http://localhost:50084", "secure-token"),
64+
init: newHTTPSession(MCPServerListen, MCPServerDebug, "http://"+ArgoCDMockListen, ArgoCDMockToken),
6265
},
6366
}
6467

@@ -182,11 +185,11 @@ func TestServer(t *testing.T) {
182185
}{
183186
{
184187
name: "stdio",
185-
init: newStdioSession("localhost:50081", true, "http://localhost:50085", "another-token"), // invalid URL and token for the Argo CD server
188+
init: newStdioSession(MCPServerListen, MCPServerDebug, "http://localhost:50085", "another-token"), // invalid URL and token for the Argo CD server
186189
},
187190
{
188191
name: "http",
189-
init: newHTTPSession("localhost:50081", true, "http://localhost:50085", "another-token"), // invalid URL and token for the Argo CD server
192+
init: newHTTPSession(MCPServerListen, MCPServerDebug, "http://localhost:50085", "another-token"), // invalid URL and token for the Argo CD server
190193
},
191194
}
192195

@@ -214,10 +217,10 @@ func TestServer(t *testing.T) {
214217

215218
type KillMCPServerFunc func()
216219

217-
func newStdioSession(mcpServerListen string, mcpServerDebug bool, argocdURL string, argocdToken string) func(*testing.T) (*mcp.ClientSession, KillMCPServerFunc) {
220+
func newStdioSession(mcpServerListenPort string, mcpServerDebug bool, argocdURL string, argocdToken string) func(*testing.T) (*mcp.ClientSession, KillMCPServerFunc) {
218221
return func(t *testing.T) (*mcp.ClientSession, KillMCPServerFunc) {
219222
ctx := context.Background()
220-
cmd := newServerCmd(ctx, "stdio", mcpServerListen, strconv.FormatBool(mcpServerDebug), argocdURL, argocdToken)
223+
cmd := newServerCmd(ctx, "stdio", mcpServerListenPort, strconv.FormatBool(mcpServerDebug), argocdURL, argocdToken)
221224
cl := mcp.NewClient(&mcp.Implementation{Name: "e2e-test-client", Version: "v1.0.0"}, nil)
222225
session, err := cl.Connect(ctx, &mcp.CommandTransport{Command: cmd}, nil)
223226
require.NoError(t, err)
@@ -231,6 +234,7 @@ func newHTTPSession(mcpServerListen string, mcpServerDebug bool, argocdURL strin
231234
return func(t *testing.T) (*mcp.ClientSession, KillMCPServerFunc) {
232235
ctx := context.Background()
233236
cmd := newServerCmd(ctx, "http", mcpServerListen, strconv.FormatBool(mcpServerDebug), argocdURL, argocdToken)
237+
cmd.Stderr = os.Stdout
234238
go func() {
235239
t.Logf("starting the MCP server: %v", cmd.String())
236240
if err := cmd.Run(); err != nil {
@@ -241,13 +245,16 @@ func newHTTPSession(mcpServerListen string, mcpServerDebug bool, argocdURL strin
241245
}
242246
}
243247
}()
244-
time.Sleep(5 * time.Second)
248+
t.Logf("waiting for the MCP server to start")
249+
err := waitForMCPServer(mcpServerListen)
250+
require.NoError(t, err, "failed to wait for the MCP server to start")
251+
245252
cl := mcp.NewClient(&mcp.Implementation{Name: "e2e-test-client", Version: "v1.0.0"}, nil)
246253
session, err := cl.Connect(ctx, &mcp.StreamableClientTransport{
247254
MaxRetries: 5,
248-
Endpoint: fmt.Sprintf("http://%s/mcp", os.Getenv("MCP_SERVER_LISTEN")),
255+
Endpoint: fmt.Sprintf("http://%s/mcp", mcpServerListen),
249256
}, nil)
250-
require.NoError(t, err)
257+
require.NoError(t, err, "failed to connect to the MCP server")
251258
return session, func() {
252259
t.Logf("killing the MCP server")
253260
if err := cmd.Process.Kill(); err != nil {
@@ -258,6 +265,34 @@ func newHTTPSession(mcpServerListen string, mcpServerDebug bool, argocdURL strin
258265
}
259266
}
260267

268+
func waitForMCPServer(mcpServerListen string) error {
269+
// wait until the MCP server is ready to accept connections with a timeout of 30 seconds
270+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
271+
defer cancel()
272+
for {
273+
select {
274+
case <-ctx.Done():
275+
return fmt.Errorf("timeout waiting for MCP server to start")
276+
default:
277+
}
278+
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://%s/health", mcpServerListen), nil)
279+
if err != nil {
280+
return fmt.Errorf("failed to create request: %w", err)
281+
}
282+
resp, err := http.DefaultClient.Do(req)
283+
if err != nil {
284+
time.Sleep(100 * time.Millisecond)
285+
continue
286+
}
287+
resp.Body.Close()
288+
if resp.StatusCode == http.StatusOK {
289+
break
290+
}
291+
time.Sleep(100 * time.Millisecond)
292+
}
293+
return nil
294+
}
295+
261296
func newServerCmd(ctx context.Context, transport string, mcpServerListen string, mcpServerDebug string, argocdURL string, argocdToken string) *exec.Cmd {
262297
return exec.CommandContext(ctx,
263298
"argocd-mcp-server",

0 commit comments

Comments
 (0)