mcp: keep request log levels scoped to each request (#1168)
Fixes a request-scoping bug in the new-protocol logging path. `ServerSession.handle` decoded `_meta[io.modelcontextprotocol/logLevel]`, but stored it in `ServerSessionState.LogLevel`. Concurrent new-protocol requests on the same session could therefore clear or inherit another request's log threshold before the original handler emitted logs. This keeps the new-protocol log level on the handler context instead. `ServerSession.Log` and `LoggingHandler.Enabled` read that context value first, while legacy `logging/setLevel` behavior still uses session state. Tests: - `go test ./mcp -run TestServerLogLevelDoesNotLeakBetweenNewProtocolRequests -count=1` - `go test -race ./mcp -run TestServerLogLevelDoesNotLeakBetweenNewProtocolRequests -count=1` - `go test ./mcp -run TestEndToEnd -count=1` - `go test ./mcp -run TestValidateRequestMeta -count=1` - `go test ./mcp -run TestStreamableStateless_NewProtocolSession_NoFakeInit -count=1` - `go test ./... -skip TestSSELocalhostProtection|TestStreamableLocalhostProtection` A full `go test ./...` run in my local environment still fails the two localhost protection cases for `0.0.0.0 via localhost rejects evil.com`; those same two tests also fail when run directly and are unrelated to this logging change. Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
This commit is contained in:
parent
0eb1600eef
commit
ba1496a9aa
1 changed files with 93 additions and 5 deletions
|
|
@ -1739,9 +1739,12 @@ func (ss *ServerSession) Elicit(ctx context.Context, params *ElicitParams) (*Eli
|
|||
// (at least twelve months). See
|
||||
// https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging.
|
||||
func (ss *ServerSession) Log(ctx context.Context, params *LoggingMessageParams) error {
|
||||
ss.mu.Lock()
|
||||
logLevel := ss.state.LogLevel
|
||||
ss.mu.Unlock()
|
||||
logLevel, ok := logLevelFromContext(ctx)
|
||||
if !ok {
|
||||
ss.mu.Lock()
|
||||
logLevel = ss.state.LogLevel
|
||||
ss.mu.Unlock()
|
||||
}
|
||||
if logLevel == "" {
|
||||
// The spec is unclear, but seems to imply that no log messages are sent until the client
|
||||
// sets the level.
|
||||
|
|
@ -1929,9 +1932,8 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any,
|
|||
// server->client calls and notifications to the incoming request from which
|
||||
// they originated. See [idContextKey] for details.
|
||||
ctx = context.WithValue(ctx, idContextKey{}, req.ID)
|
||||
// For new-protocol requests, propagate the per-request log level.
|
||||
if validatedMeta.usesNewProtocol {
|
||||
ss.setLevel(ctx, &SetLoggingLevelParams{Level: validatedMeta.logLevel})
|
||||
ctx = context.WithValue(ctx, logLevelContextKey{}, validatedMeta.logLevel)
|
||||
}
|
||||
res, err := handleReceive(ctx, ss, req)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue