feat(web): display negotiated MCP protocol version on ServerCard (#1324)#1442
Conversation
Add optional protocolVersion to ConnectionState and render it as a dimmed "MCP <version>" label at the end of the transport-badge/ description row on ServerCard, shown only when connected. The value is plumbed through useInspectorClient as part of #1324. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plumb the MCP protocol version negotiated during initialize from the InspectorClient through to the ServerCard so the active connected card displays "MCP <version>". The SDK Client doesn't expose protocolVersion; it stamps it onto the transport via setProtocolVersion. MessageTrackingTransport now captures it (and forwards to the base transport when present), so it works for stdio too — whose base transport has no setProtocolVersion. - core: MessageTrackingTransport captures + exposes protocolVersion; InspectorClient stores it, exposes getProtocolVersion(), dispatches a protocolVersionChange event, and clears it on disconnect. - core: useInspectorClient subscribes and returns protocolVersion. - web: App reads it from the hook (drops the hard-coded "2025-06-18") and InspectorView splices it onto the active server's connection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude review |
|
Claude finished @cliffhall's task in 1m 57s —— View job Review — surface negotiated MCP protocol version (#1442)
Nice, well-scoped change. The transport-capture approach is the right one given the SDK doesn't expose Worth considering1. The memo gained if (connectionStatus !== "connected" || !serverInfo || !protocolVersion)
return undefined;
In practice this is safe today: the transport is always wrapped in Minor notes (non-blocking)
Overall this looks correct and ready; the only thing I'd genuinely weigh is decoupling the |
…review)
Gate the initializeResult memo on serverInfo only, not protocolVersion.
initializeResult also drives the connected ViewHeader and Connection Info
modal, so requiring the version risked hiding the whole connected surface
in the edge case where it's absent. protocolVersion now flows through as
the optional field it already is everywhere downstream ('' reads as
unknown; the ServerCard label and modal value both tolerate it).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the thorough review! Addressed the actionable item; notes on the rest below. 1. Good catch, and you're right that this matters: Minor notes:
|
|
@claude review |
|
Claude finished @cliffhall's task in 1m 10s —— View job Review — surface negotiated MCP protocol version (#1442)
The follow-up commit The decoupling fix is right
The new Verified the core plumbing
Notes from the prior review — all resolved as discussed
One tiny redundancy (non-blocking, no action needed): both Clean, well-tested, ready to merge. |
…#1324 review) initializeResult is derived from connectionStatus in App's memo, so a truthy initializeResult.protocolVersion already implies a live connection — the extra connectionStatus === 'connected' guard in the splice was redundant. ServerCard keeps its own status guard (dumb- component robustness). No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks — glad the decoupling landed cleanly. I went ahead and removed the one tiny redundancy you flagged, since it was trivially safe to do ( Redundant connected-check in the |
|
@claude review |
|
Claude finished @cliffhall's task in 47s —— View job Review — surface negotiated MCP protocol version (#1442)
The third pass looks good. Commit The
|
Summary
Closes #1324.
Surfaces the MCP protocol version negotiated during
initialize(e.g.2025-06-18) end-to-end, and displays it as a dimmedMCP <version>label at the end of the transport row on the active connectedServerCard.Previously
App.tsxhard-codedprotocolVersion: "2025-06-18"when building theInitializeResultfor the connected header, becauseuseInspectorClientdidn't expose the real value. This PR removes that hard-code and plumbs the real negotiated value through.How it works
The SDK
Clientdoesn't exposeprotocolVersion— it stamps it onto the transport viatransport.setProtocolVersion(...)after the handshake. So:MessageTrackingTransportnow implementssetProtocolVersionas a concrete method (rather than delegating the base transport's optional one). It captures the negotiated version and forwards to the base transport when present. This is key for stdio, whose base transport has nosetProtocolVersion— the SDK only calls it because the wrapper always defines it.InspectorClientreads it off the transport infetchServerInfo(), stores it, exposesgetProtocolVersion(), dispatches a newprotocolVersionChangeevent, and clears it on disconnect.useInspectorClientsubscribes to the event and returnsprotocolVersion.App.tsxreads it from the hook (drops the hard-code; theinitializeResultmemo now gates on it, and it's dispatched alongsideserverInfoso it's always present when connected).InspectorViewsplicesinitializeResult.protocolVersiononto the active server'sconnection, whichServerCardrenders.Testing
MessageTrackingTransport: captures the version, forwards to a base that definessetProtocolVersion, and works when the base omits it (stdio path).useInspectorClient: initial value +protocolVersionChangesubscription.getProtocolVersion()returns a real date-shaped version after connect and clears on disconnect — proves the stdio capture path end-to-end.InspectorView/ServerCard: the active connected card showsMCP <version>; nothing shows while disconnected or when none was negotiated.npm run validate(lint/build/format/typecheck + unit & integration coverage gate) andnpm run test:storybookboth green.🤖 Generated with Claude Code