-
Notifications
You must be signed in to change notification settings - Fork 334
Description
Is your feature request related to a problem? Please describe.
I'm writing an MCP server using the StreamableHTTPHandler and need more control over generating session IDs. I'm aware of the StreamableHTTPOptions.GetSessionID() string setting, but the problem with this closure is that it needs to be assigned at the wrong time in the lifecycle of the server.
In my use case, I'm creating a custom mcp.Server for each new session (using the getServer(...) function argument of NewStreamableHTTPHandler(...)). This enables me to expose different MCP tools to different sessions. Now I also want to be able to use a custom per-server logic for assigning session IDs. For instance, I would like to copy the session ID from a downstream service. Currently, this is not possible because the StreamableHTTPOptions.GetSessionID() closure is assigned to the HTTP handler only once at application startup, rather than being an option of the mcp.Server, which can be created per-session.
Describe the solution you'd like
Moving the GetSessionID() closure out of the StreamableHTTPOptions into the ServerOptions would enable me to assign a function that can use per-session information instead of a closure that would only have information available at startup.
Details: Git diff
diff --git a/mcp/server.go b/mcp/server.go
index 69808ac..1621e4b 100644
--- a/mcp/server.go
+++ b/mcp/server.go
@@ -83,6 +83,16 @@ type ServerOptions struct {
// If true, advertises the tools capability during initialization,
// even if no tools have been registered.
HasTools bool
+
+ // GetSessionID provides the next session ID to use for an incoming request.
+ // If nil, a default randomly generated ID will be used.
+ //
+ // Session IDs should be globally unique across the scope of the server,
+ // which may span multiple processes in the case of distributed servers.
+ //
+ // As a special case, if GetSessionID returns the empty string, the
+ // Mcp-Session-Id header will not be set.
+ GetSessionID func() string
}
// NewServer creates a new MCP server. The resulting server has no features:
@@ -114,6 +124,11 @@ func NewServer(impl *Implementation, options *ServerOptions) *Server {
if opts.UnsubscribeHandler != nil && opts.SubscribeHandler == nil {
panic("UnsubscribeHandler requires SubscribeHandler")
}
+
+ if opts.GetSessionID == nil {
+ opts.GetSessionID = randText
+ }
+
return &Server{
impl: impl,
opts: opts,
diff --git a/mcp/streamable.go b/mcp/streamable.go
index 8ac6f59..f359b7c 100644
--- a/mcp/streamable.go
+++ b/mcp/streamable.go
@@ -50,16 +50,6 @@ type StreamableHTTPHandler struct {
// StreamableHTTPOptions configures the StreamableHTTPHandler.
type StreamableHTTPOptions struct {
- // GetSessionID provides the next session ID to use for an incoming request.
- // If nil, a default randomly generated ID will be used.
- //
- // Session IDs should be globally unique across the scope of the server,
- // which may span multiple processes in the case of distributed servers.
- //
- // As a special case, if GetSessionID returns the empty string, the
- // Mcp-Session-Id header will not be set.
- GetSessionID func() string
-
// Stateless controls whether the session is 'stateless'.
//
// A stateless server does not validate the Mcp-Session-Id header, and uses a
@@ -92,9 +82,6 @@ func NewStreamableHTTPHandler(getServer func(*http.Request) *Server, opts *Strea
if opts != nil {
h.opts = *opts
}
- if h.opts.GetSessionID == nil {
- h.opts.GetSessionID = randText
- }
return h
}
@@ -233,7 +220,7 @@ func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Reque
if sessionID == "" {
// In stateless mode, sessionID may be nonempty even if there's no
// existing transport.
- sessionID = h.opts.GetSessionID()
+ sessionID = server.opts.GetSessionID()
}
transport = &StreamableServerTransport{
SessionID: sessionID,Describe alternatives you've considered
I've considered using the StreamableHTTPOptions.GetSessionID() closure, but it would not allow me to take any per-session information into account because it's assigned once at startup.
Another alternative could be to add more parameters to the StreamableHTTPOptions.GetSessionID() function (e.g., the request context). That would work for me, but it would also encourage people to smuggle values with the request context, which I consider to be an antipattern.
Additional context
none