Skip to content

SANDBOX-1567: Add stateless mode configuration#39

Merged
fbm3307 merged 18 commits into
codeready-toolchain:masterfrom
fbm3307:statelessconfig
Jan 28, 2026
Merged

SANDBOX-1567: Add stateless mode configuration#39
fbm3307 merged 18 commits into
codeready-toolchain:masterfrom
fbm3307:statelessconfig

Conversation

@fbm3307
Copy link
Copy Markdown
Contributor

@fbm3307 fbm3307 commented Jan 8, 2026

We want to run multiple replicas of the mcp server. and to be able to do that it needs to be stateless.
Adding the configuration for it.

Added e2e tests too for it

Taken inspiration from PR

Assisted by : Cursor

Summary by CodeRabbit

  • New Features

    • Added stateless mode (CLI flag + env). Disables certain change notifications, rejected for stdio transport, and honored by the MCP HTTP endpoint to support independent replicas behind a load balancer.
  • Chores

    • Standardized entrypoint flag syntax to key=value and introduced ARGOCD_MCP_SERVER_STATELESS.
    • Added two stateless replicas, a load‑balancer service, and an Nginx proxy config.
  • Tests

    • Added e2e tests validating stateless initialization, session reuse, and ListChanged semantics.

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: Feny Mehta <fbm3307@gmail.com>
@fbm3307 fbm3307 requested a review from xcoulon January 8, 2026 10:00
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 8, 2026

Walkthrough

Adds a stateless mode flag/env, threads it into server construction and the MCP HTTP handler, rejects stateless with stdio transport, exposes stateless replicas behind an nginx load‑balancer in compose, updates the container entrypoint, and expands e2e tests for stateful vs stateless behavior.

Changes

Cohort / File(s) Summary
CLI & startup
cmd/start_server.go
Adds --stateless flag/env, logs it, enforces runtime error when used with stdio transport, passes stateless into server.New, and forwards to MCP handler via StreamableHTTPOptions{Stateless: stateless}.
Server initialization
internal/server/server.go
Changes New signature to New(logger *slog.Logger, cl *argocd.Client, stateless bool) and sets ServerOptions.Capabilities so Tools.ListChanged and Prompts.ListChanged = !stateless; preserves existing initialization flow.
Container image / entrypoint
Containerfile
Adds ENV ARGOCD_MCP_SERVER_STATELESS and updates CMD to include --stateless=$ARGOCD_MCP_SERVER_STATELESS (some flags switched to key=value form).
Compose & load‑balancer config
compose.yaml, test/e2e/nginx.conf
Adds two stateless replica services and an nginx load‑balancer; nginx.conf proxies streaming requests to replicas with buffering disabled and chunked transfer enabled.
End-to-end tests
test/e2e/server_test.go, test/e2e/nginx.conf
Renames TestServerTestStatefulServer, adds TestStatelessServer, new helpers newHTTPSession(ctx, endpoint, clientName), assertListChanged, assertInitializeResponse, extends unreachable scenarios, and integrates nginx load‑balancer tests.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI
  participant StartCmd as start_server
  participant Argocd as ArgoCD_Client
  participant ServerPkg as internal/server
  participant MCPHandler as MCP_HTTP_Handler
  participant Transport as Transport

  CLI->>StartCmd: parse flags (including --stateless)
  StartCmd->>Transport: validate transport vs stateless
  alt transport == stdio and stateless == true
    Transport-->>StartCmd: error -> abort
  else
    StartCmd->>Argocd: create/obtain client
    StartCmd->>ServerPkg: New(logger, Argocd, stateless)
    ServerPkg-->>StartCmd: Server instance (capabilities set)
    StartCmd->>MCPHandler: configure StreamableHTTPOptions{Stateless: stateless}
    StartCmd->>Transport: start listening (http)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • alexeykazakov
  • mfrancisc
  • rsoaresd

Poem

🐰 I hopped through flags and env today,
I nudged the server so state could sway,
Two replicas hum behind a gate,
Tests check sessions, tools, and state,
I twitched my whiskers and hopped away.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'SANDBOX-1567: Add stateless mode configuration' clearly and concisely summarizes the main change—adding stateless mode configuration to the MCP server, which is the primary objective of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@fbm3307 fbm3307 changed the title Add stateless mode configuration SANDBOX-1567: Add stateless mode configuration Jan 12, 2026
@fbm3307 fbm3307 requested a review from alexeykazakov January 13, 2026 09:33
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@cmd/start_server.go`:
- Around line 98-110: Remove the unresolved merge markers and merge the two
variants so the HTTP server initialization uses the StreamableHTTPOptions struct
with Stateless set (ensure the call includes
&mcp.StreamableHTTPOptions{Stateless: stateless} where the mcp server is
constructed) and also re-add the metrics handler by registering
mux.Handle("/metrics", promhttp.Handler()) before the healthcheck registration;
update the block around the mcp.New/handler setup (look for
StreamableHTTPOptions, stateless, mux.Handle and promhttp.Handler references) to
contain both the options and the metrics endpoint and remove the conflict marker
lines (<<<<<<<, =======, >>>>>>>).
- Around line 63-67: Resolve the git conflict markers in the logger.Info call by
removing the conflict lines (<<<<<<< HEAD, =======, >>>>>>> master) and keep a
single logger.Info invocation that includes the fields "transport", "argocd-url"
(use that key name), "insecure" (argocdInsecure), "debug" (debug) and also
include "stateless" (stateless); update the call site (logger.Info) so it
compiles with no merge markers and uses "argocd-url" consistently.
🧹 Nitpick comments (1)
cmd/start_server.go (1)

76-78: Appropriate guard for stdio transport.

Good validation to prevent incompatible configuration. However, consider moving this check to PreRunE alongside the transport validation for consistency and earlier failure.

♻️ Optional: Move validation to PreRunE
 	PreRunE: func(_ *cobra.Command, _ []string) error {
 		if transport != "stdio" && transport != "http" {
 			return fmt.Errorf("invalid transport: choose between 'http' and 'stdio'")
 		}
+		if stateless && transport == "stdio" {
+			return fmt.Errorf("stateless mode is not supported for stdio transport")
+		}
 		return nil
 	},

Then remove lines 76-78 from RunE.

Comment thread cmd/start_server.go Outdated
Comment thread cmd/start_server.go Outdated
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/e2e/server_test.go`:
- Around line 461-476: The trailing, unused nolint directive causes a lint
failure; remove the "//nolint:gosec" comment from the return statement in
newHTTPServerCmd so the call to exec.CommandContext(ctx, "argocd-mcp-server",
args...) no longer carries the obsolete directive; ensure no other nolint
annotations remain in that function (newHTTPServerCmd) or on the
exec.CommandContext call.

Comment thread test/e2e/server_test.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/e2e/server_test.go`:
- Around line 539-552: The test currently conditionally checks
initResult.Capabilities.Tools and may skip asserting Tools.ListChanged; update
assertListChanged to require the Tools capability is present (use require.NotNil
on initResult.Capabilities.Tools) before asserting Tools.ListChanged so the test
fails loudly on regressions; keep the existing require.NotNil checks for
InitializeResult and Capabilities and the Prompts branch unchanged, and refer to
the assertListChanged function and InitializeResult/Capabilities/Tools symbols
when making the change.
🧹 Nitpick comments (1)
test/e2e/server_test.go (1)

268-358: Consider avoiding hard-coded ports to reduce E2E flakiness.
Fixed ports can collide on shared CI runners or dev machines. Consider selecting free ports or allowing env overrides so the tests are less brittle.

Comment thread test/e2e/server_test.go
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Comment thread test/e2e/server_test.go Outdated
Comment thread test/e2e/server_test.go Outdated
Comment thread test/e2e/server_test.go Outdated
Comment thread test/e2e/server_test.go Outdated
Comment thread test/e2e/nginx.conf Outdated
Comment thread test/e2e/nginx.conf Outdated
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
@fbm3307 fbm3307 requested a review from xcoulon January 23, 2026 08:32
Comment thread test/e2e/server_test.go Outdated
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
@fbm3307 fbm3307 requested a review from xcoulon January 27, 2026 07:42
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/e2e/server_test.go`:
- Around line 42-49: The composite literal for the "http" test case (and the
similar literal around the later block) is mis-indented causing gofmt failures;
fix by reformatting the composite literals so fields line up with Go formatting
conventions—either run `gofmt -w` on the file or adjust the braces/indentation
for the test case structs that initialize mcp.ClientSession via newClientSession
(look for the test case with name "http" and the later composite at lines
~241-248) so the init func, keys, and values are properly indented and the file
passes gofmt.

Comment thread test/e2e/server_test.go Outdated
Comment thread test/e2e/server_test.go Outdated
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/e2e/server_test.go`:
- Around line 293-297: The loop currently uses a short declaration which
re-declares err and shadows the earlier err; to fix it, declare variables
outside the loop (e.g., var tools *mcp.ListToolsResponse; var err error) or
reuse the existing err by replacing the := with = inside the for loop so the
call to session.ListTools(ctx, &mcp.ListToolsParams{}) assigns to the
already-declared err; update the loop that calls session.ListTools and the
variable declarations so only one err variable exists (referencing
session.ListTools, tools, and err).
🧹 Nitpick comments (1)
test/e2e/server_test.go (1)

380-394: Consider aligning strictness with assertInitializeResponse.

assertInitializeResponse uses require.NotNil for Tools capability (line 418), but assertListChanged uses a conditional check. For consistency and to fail loudly on regressions, consider aligning the validation approach.

💡 Optional: Align with assertInitializeResponse
 func assertListChanged(t *testing.T, session *mcp.ClientSession, expected bool) {
 	t.Helper()
 	initResult := session.InitializeResult()
 	require.NotNil(t, initResult, "should have initialize result")
 	require.NotNil(t, initResult.Capabilities, "should have capabilities")

-	if initResult.Capabilities.Tools != nil {
-		assert.Equal(t, expected, initResult.Capabilities.Tools.ListChanged,
-			"Tools.ListChanged should be %t", expected)
-	}
+	require.NotNil(t, initResult.Capabilities.Tools, "should have tools capability")
+	assert.Equal(t, expected, initResult.Capabilities.Tools.ListChanged,
+		"Tools.ListChanged should be %t", expected)
 	if initResult.Capabilities.Prompts != nil {

Comment thread test/e2e/server_test.go
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
@fbm3307 fbm3307 requested a review from xcoulon January 27, 2026 09:45
Comment thread test/e2e/server_test.go
Comment thread test/e2e/server_test.go Outdated
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
Signed-off-by: Feny Mehta <fbm3307@gmail.com>
@xcoulon
Copy link
Copy Markdown
Collaborator

xcoulon commented Jan 28, 2026

@fbm3307 see #48 to switch back to ubi9-minimal base image, to avoid the problem in CI where the ubi10-micro image cannot be pulled 🤷‍♂️

@fbm3307 fbm3307 merged commit 1c9fed0 into codeready-toolchain:master Jan 28, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants