Conversation
…sionId The logging/setLevel handler stores the level under the transport's session id, but sendLoggingMessage(params) looked it up under an undefined key. On session-bearing transports (Streamable HTTP, SSE) the client's level was therefore ignored and every message was sent, while the same call filtered correctly on stdio. Default the lookup to the connected transport's session id. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 446fb93 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Server.sendLoggingMessage(params)(andMcpServer.sendLoggingMessage) now honors the level the client set withlogging/setLevelon Streamable HTTP and SSE, as it already did on stdio.Motivation and Context
The built-in
logging/setLevelhandler stores the client's level under the transport's session id (ctx.sessionId).sendLoggingMessagelooks the level up under its ownsessionIdargument. That argument is optional, and the documented example (McpServer_sendLoggingMessage_basic) leaves it out. So:undefinedand the lookup ofundefinedfinds it. The filter works.'<session-id>'and the lookup ofundefinedmisses.isMessageIgnoredthen returnsfalse, and every message is sent whatever level the client asked for.On HTTP the client's
setLevelhas no effect, and nothing reports it.ctx.mcpReq.log()does not have this problem because it readsctx.sessionId. That leaves two logging APIs on the same server that disagree.Spec (2025-11-25): the
SetLevelRequestlevel is "The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher" (schema.ts L1509). The logging page shows the same thing in its message flow: afterlogging/setLevel (error)the server "Only sends error level and above".The fix is one line in
isMessageIgnored. When nosessionIdis passed, it uses the connected transport's session id, which is the key the handler wrote. AServeris connected to one transport, so there is exactly one such id. An explicitsessionIdworks as before, and sessionless transports still look upundefined, so nothing changes for them. This finishes what #917 started, which made the lookup work for sessionless (stdio) servers.How Has This Been Tested?
I added a regression test to
test/integration/test/server.test.ts, next to the existing "should respect log level for transport with sessionId" test. It uses the same session-bearing in-memory pair and callssendLoggingMessage(params)without asessionId, the way the JSDoc example does. It fails onmain(thedebugmessage gets through) and passes with the fix.I also ran a temporary scenario through the e2e harness (
wire()across every transport), not committed, to reproduce this over real transports. The client setserror, then the server callssendLoggingMessage({level:'debug'})andsendLoggingMessage({level:'error'}).Evidence
Temporary e2e-harness scenario, before the fix:
After the fix:
Committed test, before the fix (with
server.tsfromorigin/main):After the fix:
The full
test-e2erun passes every test (2641 passed | 147 expected fail). It still exits non-zero because of two unhandledREQUEST_TIMEOUTrejections from the fake-timerprotocol:timeout:max-totalscenario inscenarios/protocol.test.ts. That scenario is also flaky on unpatchedmainin my local runs, and it doesn't touch logging.Breaking Changes
None for callers that pass
sessionId, or for sessionless transports. On Streamable HTTP and SSE, a server that callssendLoggingMessage(params)without asessionIdnow stops sending messages below the level the client set withlogging/setLevel. That filtering is what the client asked for.Types of changes
Checklist
Additional context
There is a changeset (
@modelcontextprotocol/serverpatch). The logging API is@deprecatedunder SEP-2577, but it "remains functional during the deprecation window", and this is the path the JSDoc example uses. python-sdk leaves filtering to the application's ownlogging/setLevelhandler on the legacy era, so it has no equivalent to line up with. This fix only makes TS's own automatic handling (#882, #917) work the same on every transport.Dedup evidence (searched 2026-09-25T00:22Z to 00:45Z UTC)
sendLoggingMessage sessionIdreturned 0 results,setLoggingLevel ignored0,logging level streamable http0,log level not respected0 andlogging setLevel session0.isMessageIgnoredreturned Automatic handling of logging level #882 and Fix automatic log level handling for sessionless connections #917 (the original feature and the sessionless fix, both merged in 2025), fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone #2330 (ctx.log delivery) and unrelated spec syncs.sendLoggingMessage levelreturned sendLoggingMessage with Streamable HTTP - logs are not received #766 (closed: logs not received before the default handler existed), fix(server,client): per-request ctx.log delivery; 2026-era HTTP cancel via stream close; skip unused entry clone #2330 and docs PRs. None covers the omitted-sessionId lookup on session transports.packages/server/src/server/server.ts: fix(spec): freeze 2026-07-28 release references #2858, feat: server and client extensions, request middleware (use), acceptResultType #2820, fix(server): validate low-level tool inputs #2634, fix(server): throw when resource subscribe capability is missing #2550, fix(server): tag list_changed notifications with the in-flight request id #2522, refactor(core): extract connection-scoped state into a private Connection owner #2470, fix(server): restore v1 transport lifecycle parity: single-use stateless transports, fail-fast on double connect #2421, fix(server): disable listChanged capability in V1x protocol mode (closes #1819) #1953 and feat(core): add opt-in periodic ping for connection health monitoring #1717. I checked each diff forisMessageIgnored,_loggingLevelsorLOG_LEVEL_SEVERITY, and none of them changes those.server.ts.Disclosure: prepared with AI assistance (Claude Code); I reviewed the change and take responsibility for it.
🤖 Generated with Claude Code