fix: startup logging messages never reached clients - #139
Open
mattpodwysocki wants to merge 2 commits into
Open
Conversation
Two compounding issues: the McpServer never declared the `logging`
capability, so sendLoggingMessage was an unconditional no-op regardless of
timing; and several of those calls also ran before server.connect(),
which would drop them anyway since there's no connected client yet.
Fixed both -- declared logging: {} in capabilities, and moved startup
logging (.env status, tracing status, detected client capabilities) to
after connect(), matching the ordering @mapbox/mcp-server already uses.
Added an integration test that spawns the real built server and asserts
a real client receives at least one startup log message.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
server.server.getClientVersion()/getClientCapabilities() are only populated once the client's initialize request has been processed -- reading them synchronously right after server.connect() races that request, since connect() only waits for the transport to start, not for the handshake to finish. Confirmed live: reading capabilities immediately after connect() reliably returned undefined even for a client that declared them. Moved both reads into the existing server.server.oninitialized callback (added in the prior commit for logging-order correctness), which fires only once the handshake is fully done. Also adds client identification as a byproduct: the client's name/version from its initialize request is now logged on connect and recorded as mcp.client.name/mcp.client.version on every subsequent tool-execution trace span, matching the equivalent change in @mapbox/mcp-server. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
zmofei
approved these changes
Aug 26, 2026
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.
Summary
Two compounding issues meant startup logging (
.envload status, tracing status, detected client capabilities) never reached any client:McpServernever declared theloggingcapability in its constructor options, sosendLoggingMessagewas an unconditional no-op (the SDK checksthis._capabilities.loggingbefore doing anything) regardless of timing.sendLoggingMessagecalls ran beforeawait server.connect(transport)— a notification sent before the transport is connected has no client to reach, so those calls would have been dropped even with the capability declared correctly.Fixed both:
logging: {}to the server's declared capabilities.sendLoggingMessagecalls to run afterconnect(), matching the ordering@mapbox/mcp-server'ssrc/index.tsalready uses (with the same "now that we're connected, send all the logging messages" pattern).Follow-up commit, same root cause:
getClientVersion()/getClientCapabilities()are only populated once the client'sinitializerequest has been processed — reading them right afterconnect()(as the existing capability-gated tool registration did) reliably saw them as unset, confirmed live even for a client that explicitly declaredelicitationsupport. Both reads now happen inside aserver.server.oninitializedcallback, which only fires once the handshake is fully done. As a result:initializerequest) is now logged, e.g.Client identified as: claude-ai v1.0.0— useful for support/debugging across Claude Desktop, Cursor, VS Code, etc.mcp.client.name/mcp.client.versionon every subsequent tool-execution trace span, so OTel-backed traces can be filtered/grouped by client.ELICITATION_TOOLSis empty, but was silently broken for whenever a tool is added there).Matches the equivalent changes landing in
@mapbox/mcp-server.Test plan
test/integration/loggingOrder.test.ts) spawns the real built server and asserts a real MCP client receives at least one startup log message. Verified it fails against each fix in isolation (capability alone still drops messages sent pre-connect; ordering alone still no-ops without the capability) and passes with both applied.test/utils/tracing.test.ts) covercreateToolSpanpicking upmcp.client.name/mcp.client.versiononcesetClientInfois called.elicitationcapability: server now logsClient identified as: pretend-codex-client v4.2.0andClient capabilities detected: { "elicitation": { "form": {} } }(previouslyundefined).npx vitest run— all 641 tests passnpm run buildsucceeds🤖 Generated with Claude Code