From c2f6508447717a976a02244c84e1360ff75a180f Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 7 Oct 2026 17:15:56 -0400 Subject: [PATCH] MCP: a server sends log records only when its own client asked for them The log queue is process-wide. Any server draining it after a request, whether or not its client called logging/setLevel, could take another session's records; under parallel tests that made log_records_go_out_as_notifications_once_asked_for flaky. Co-Authored-By: Claude Opus 5.5 --- crates/mcp/src/server.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/mcp/src/server.rs b/crates/mcp/src/server.rs index 07df56155..477b25565 100644 --- a/crates/mcp/src/server.rs +++ b/crates/mcp/src/server.rs @@ -33,6 +33,9 @@ vectorcraft://swatch/{name} read one thing at a time instead of the whole docume pub struct Server { backend: Box, initialized: bool, + /// Whether this server's client asked for log records (`logging/setLevel`). The queue is + /// process-wide, so a server whose client didn't ask leaves it to the one that did. + logging: bool, } fn response(id: Value, result: Value) -> Value { @@ -45,7 +48,7 @@ fn error(id: Value, code: i64, message: impl Into) -> Value { impl Server { pub fn new(backend: Box) -> Self { - Self { backend, initialized: false } + Self { backend, initialized: false, logging: false } } pub fn backend(&mut self) -> &mut dyn Backend { @@ -63,6 +66,9 @@ impl Server { /// line produced them rather than before it. [`serve`] drains this itself; a caller driving /// [`handle_line`] by hand does the same. pub fn take_notifications(&mut self) -> Vec { + if !self.logging { + return vec![]; + } crate::logging::drain() .into_iter() .map(|record| json!({"jsonrpc": "2.0", "method": "notifications/message", "params": record}).to_string()) @@ -197,6 +203,7 @@ impl Server { Some(given) => (INVALID_PARAMS, format!("unknown level `{given}`")), None => (INVALID_PARAMS, "`level` must be a severity name or null".to_string()), })?; + self.logging = level.is_some(); log::debug!("client set logging to {}", level.unwrap_or("off")); Ok(json!({})) }