mcp: improvements for 'stateless' streamable servers; 'distributed' mode

Several improvements for the stateless streamable mode, plus support for
a 'distributed' (or rather, distributable) version of the stateless
server.

- Add a 'Stateless' option to StreamableHTTPOptions and
  StreamableServerTransport, which controls stateless behavior.
  GetSessionID may still return a non-empty session ID.
- Audit validation of stateless mode to allow requests with a session
  id. Propagate this session ID to the temporary connection.
- Peek at requests to allow 'initialize' requests to go through to the
  session, so that version negotiation can occur (FIXME: add tests).

Fixes #284
For #148
This commit is contained in:
Rob Findley 2025-08-15 03:13:53 +00:00 • committed by Robert Findley
commit 98e67fdf62
2 changed files with 203 additions and 37 deletions

View file

@ -739,7 +739,23 @@ func (c *Connection) processResult(from any, req *incomingRequest, result any, e
// write is used by all things that write outgoing messages, including replies.
// it makes sure that writes are atomic
func (c *Connection) write(ctx context.Context, msg Message) error {
err := c.writer.Write(ctx, msg)
var err error
// Fail writes immediately if the connection is shutting down.
//
// TODO(rfindley): should we allow cancellation notifations through? It could
// be the case that writes can still succeed.
c.updateInFlight(func(s *inFlightState) {
err = s.shuttingDown(ErrServerClosing)
})
if err == nil {
err = c.writer.Write(ctx, msg)
}
// For rejected requests, we don't set the writeErr (which would break the
// connection). They can just be returned to the caller.
if errors.Is(err, ErrRejected) {
return err
}
if err != nil && ctx.Err() == nil {
// The call to Write failed, and since ctx.Err() is nil we can't attribute

View file

@ -37,6 +37,17 @@ var (
ErrServerClosing = NewError(-32004, "server is closing")
// ErrClientClosing is a dummy error returned for calls initiated while the client is closing.
ErrClientClosing = NewError(-32003, "client is closing")
// The following errors have special semantics for MCP transports
// ErrRejected may be wrapped to return errors from calls to Writer.Write
// that signal that the request was rejected by the transport layer as
// invalid.
//
// Such failures do not indicate that the connection is broken, but rather
// should be returned to the caller to indicate that the specific request is
// invalid in the current context.
ErrRejected = NewError(-32004, "rejected by transport")
)
const wireVersion = "2.0"