fix(web): render the connected header when a modern server omits serverInfo (#1773)

A modern-era server/discover makes serverInfo optional (SHOULD, not MUST); a conforming modern server that omits it left initializeResult undefined, collapsing the entire connected header + tab bar so the server was uninspectable. Gate initializeResult on connectionStatus alone with a catalog-name fallback, and thread a serverInfoReported flag so the Connection Info modal reports the omission truthfully instead of presenting the inferred name as server-sent. Closes #1772.
This commit is contained in:
Cliff Hall 2026-07-25 11:40:31 -04:00 • committed by GitHub
commit 820d38c438
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -40,6 +40,16 @@ export interface OAuthDetails {
export interface ConnectionInfoContentProps {
initializeResult: InitializeResult;
/**
* Whether the server actually reported `serverInfo`. When false (a modern
* server that omitted the optional `_meta` stamp), `initializeResult`'s name is
* a client-side catalog fallback, so the Server Implementation section renders
* "not reported" rather than presenting the inferred name as server-sent — the
* exact fact a user opens this modal to check (#1772). Defaults to `true`
* (server-reported), which is the norm and what fixtures with a real
* `serverInfo` want.
*/
serverInfoReported?: boolean;
clientCapabilities: ClientCapabilities;
transport: ServerType;
/**
@ -63,6 +73,12 @@ const ValueText = Text.withProps({
fw: 600,
});
// Shown for Name/Version when the server didn't report `serverInfo` — an em dash
// plus an explicit note so the client-side catalog fallback is never mistaken
// for a value the server sent. Exported so tests/stories assert against it
// rather than re-typing the copy.
export const SERVER_INFO_NOT_REPORTED_LABEL = "— (not reported by server)";
const SectionHeading = Title.withProps({
// `order: 3` (not 5) keeps the heading level one below the modal's `h2`
// `Modal.Title`, so the outline doesn't skip a level (axe `heading-order`);
@ -178,6 +194,7 @@ function getCapabilityEntries(
export function ConnectionInfoContent({
initializeResult,
serverInfoReported = true,
clientCapabilities,
transport,
protocolEra,
@ -188,6 +205,17 @@ export function ConnectionInfoContent({
const { serverInfo, protocolVersion, capabilities, instructions } =
initializeResult;
// Only trust `serverInfo` when the server actually reported it; otherwise the
// name is a catalog fallback. Both rows use `||` (not `??`) so a reported-but-
// empty name/version reads as unknown ("—") rather than a blank row —
// `initialize` mandates the fields, not non-empty values.
const displayName = serverInfoReported
? serverInfo.name || "—"
: SERVER_INFO_NOT_REPORTED_LABEL;
const displayVersion = serverInfoReported
? serverInfo.version || "—"
: SERVER_INFO_NOT_REPORTED_LABEL;
const serverCaps = getCapabilityEntries(capabilities, SERVER_CAPABILITY_KEYS);
const clientCaps = getCapabilityEntries(
clientCapabilities,
@ -200,13 +228,13 @@ export function ConnectionInfoContent({
<SectionHeading>Server Implementation</SectionHeading>
<SimpleGrid cols={2}>
<Text size="sm">Name</Text>
<ValueText>{serverInfo.name}</ValueText>
<ValueText>{displayName}</ValueText>
<Text size="sm">Version</Text>
<ValueText>{serverInfo.version ?? "—"}</ValueText>
<ValueText>{displayVersion}</ValueText>
<Text size="sm">Protocol</Text>
<ValueText>{protocolVersion}</ValueText>
<ValueText>{protocolVersion || "—"}</ValueText>
<Text size="sm">Transport</Text>
<Badge variant="outline">{transport}</Badge>