mcp/server: enable server logging (#501)

mcp/server.go: implement server‑side logging

- Thread a *slog.Logger through server paths to improve debuggability
- Introduce no‑op discard handler + ensureLogger so loggers are never
nil
- Use ensured loggers in Server
- address todo comments related to adding logging support

All tests are passing and no functionality should change with this PR

Fixes https://github.com/modelcontextprotocol/go-sdk/issues/170
This commit is contained in:
Suraj Bobade 2025-10-03 00:14:32 +05:30 • committed by GitHub
commit 1dcbf62661
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -68,6 +68,10 @@ type StreamableHTTPOptions struct {
//
// [§2.1.5]: https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#sending-messages-to-the-server
JSONResponse bool
// Logger specifies the logger to use.
// If nil, do not log.
Logger *slog.Logger
}
// NewStreamableHTTPHandler returns a new [StreamableHTTPHandler].
@ -83,6 +87,11 @@ func NewStreamableHTTPHandler(getServer func(*http.Request) *Server, opts *Strea
if opts != nil {
h.opts = *opts
}
if h.opts.Logger == nil { // ensure we have a logger
h.opts.Logger = ensureLogger(nil)
}
return h
}
@ -368,6 +377,8 @@ type StreamableServerTransport struct {
// StreamableHTTPOptions.JSONResponse is exported.
jsonResponse bool
logger *slog.Logger
// connection is non-nil if and only if the transport has been connected.
connection *streamableServerConn
}
@ -382,6 +393,7 @@ func (t *StreamableServerTransport) Connect(ctx context.Context) (Connection, er
stateless: t.Stateless,
eventStore: t.EventStore,
jsonResponse: t.jsonResponse,
logger: t.logger,
incoming: make(chan jsonrpc.Message, 10),
done: make(chan struct{}),
streams: make(map[string]*stream),
@ -408,6 +420,8 @@ type streamableServerConn struct {
jsonResponse bool
eventStore EventStore
logger *slog.Logger
incoming chan jsonrpc.Message // messages from the client to the server
mu sync.Mutex // guards all fields below
@ -755,7 +769,7 @@ func (c *streamableServerConn) respondSSE(stream *stream, w http.ResponseWriter,
}
if _, err := writeEvent(w, e); err != nil {
// Connection closed or broken.
// TODO(#170): log when we add server-side logging.
c.logger.Warn("error writing event", "error", err)
return false
}
writes++
@ -774,7 +788,13 @@ func (c *streamableServerConn) respondSSE(stream *stream, w http.ResponseWriter,
// simplify.
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} else {
// TODO(#170): log when we add server-side logging
if ctx.Err() != nil {
// Client disconnected or cancelled the request.
c.logger.Error("stream context done", "error", ctx.Err())
} else {
// Some other error.
c.logger.Error("error receiving message", "error", err)
}
}
return
}