feat(controller): make MCP stateless mode configurable via KAGENT_MCP_STATELESS#1854
feat(controller): make MCP stateless mode configurable via KAGENT_MCP_STATELESS#1854onematchfox wants to merge 2 commits into
KAGENT_MCP_STATELESS#1854Conversation
…P_STATELESS` The `StreamableHTTPHandler` stores sessions in memory and returns 404 for any `Mcp-Session-Id` it doesn't recognise. With multiple replicas, a request may land on a different replica than the one that created the session. `KAGENT_MCP_STATELESS=true` enables stateless mode, which skips session-ID validation and creates a temporary session per request, making any replica capable of handling any request. This enables use of the MCP when the network path does/can not provide sticky session routing based on the `Mcp-Session-Id` header. Server->client initiated streaming is disabled in stateless mode, but that is not needed here — both tools are request/response and A2A conversation continuity is carried in the `context_id` field of the `invoke_agent` input. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in configuration to run the controller’s MCP StreamableHTTPHandler in stateless mode to better support multi-replica deployments where requests may not have sticky routing by session header.
Changes:
- Register a new controller env var
KAGENT_MCP_STATELESSto toggle MCP stateless behavior. - When enabled, construct the MCP HTTP handler with
StreamableHTTPOptions{Stateless: true}.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| go/core/pkg/env/kagent.go | Adds KAGENT_MCP_STATELESS env var registration and description under controller-scoped env vars. |
| go/core/internal/mcp/mcp_handler.go | Wires the env var into MCP handler creation by passing StreamableHTTPOptions to the MCP SDK handler. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var httpOpts *mcpsdk.StreamableHTTPOptions | ||
| if env.KagentMCPStateless.Get() { | ||
| httpOpts = &mcpsdk.StreamableHTTPOptions{Stateless: true} | ||
| } |
supreme-gg-gg
left a comment
There was a problem hiding this comment.
this lgtm overall. I agree with copilot we should add test coverage though. Perhaps not e2e test since it could be flaky and also make CI run even longer, I think integration test that spins up two handlers (simulating two replicas) and verifying stateful fails while stateless succeeds would be good
EItanya
left a comment
There was a problem hiding this comment.
I wonder if we should actually just make it stateless by default always. Realistically this will not be stateful without a bunch more work on our side
Yeah, initially I was going to just make this the default. However, as mentioned in the comment, stateless mode does disable server-side notifications which, I am introducing in #1856 😅 |
Honestly I don't think this is worth it - we'd ultimately just be validating the SDK behaviour rather than kagent's code. The behaviours that the tests would exercise are implemented entirely within the SDK which has it's own test suite. This also gets a little complex because the feature (within the context of Kagent) is enabled/disabled via an env var - which, to then test the 3 lines of configuration glue in KAgent would require setting up tests with that environment variable set/not-set. If you really want to, I can setup a test that ensures that the |
supreme-gg-gg
left a comment
There was a problem hiding this comment.
Honestly I don't think this is worth it - we'd ultimately just be validating the SDK behaviour rather than kagent's code. The behaviours that the tests would exercise are implemented entirely within the SDK which has it's own test suite.
Sure that's reasonable. This lgtm
The
StreamableHTTPHandlerstores sessions in memory and returns404for anyMcp-Session-Idit doesn't recognise. With multiple controller replicas, a request may land on a different replica than the one that created the session.KAGENT_MCP_STATELESS=trueenables stateless mode, which skips session-ID validation and creates a temporary session per request, making any replica capable of handling any request. This enables use of the MCP when the network path does/can not provide sticky session routing based on theMcp-Session-Idheader.Server->client initiated streaming is disabled in stateless mode, but that is not needed here — both tools are request/response and A2A conversation continuity is carried in the
context_idfield of theinvoke_agentinput.