Two defects in the Server Settings key/value editors, found while reading ServerSettingsForm.
1. The Custom Headers hint is factually backwards
The hint reads:
Headers sent with every HTTP request to this server. If OAuth is configured below, the Authorization header is owned by the OAuth flow and any value set here is ignored.
That is the opposite of what happens. In @modelcontextprotocol/client@2.x, both SSEClientTransport._commonHeaders() and the streamable-HTTP equivalent do:
if (token) headers["Authorization"] = `Bearer ${token}`;
...
const extraHeaders = normalizeHeaders(this._requestInit?.headers);
return new Headers({ ...headers, ...extraHeaders });
The custom headers are spread after the OAuth bearer, so a custom Authorization value overrides the OAuth access token rather than being ignored.
Why it matters: a user who set a bearer by hand before configuring OAuth is told, by the UI, that the stale value is harmless. It is not — it silently suppresses the token the OAuth flow just obtained, and the resulting 401 has no visible cause.
Expected: the hint tells the user a custom Authorization wins, so they know to remove it once OAuth is configured.
2. Key/value rows are indistinguishable to a screen reader
KeyValueRows renders Custom Headers, Request Metadata and Environment Variables. Nothing associates a control with its row:
- The key and value inputs carry only a
placeholder ("Key" / "Value"), which is not a label.
- Every remove button announces as "X".
- Every clear button announces as "Clear".
So a settings form with three headers and two metadata entries presents ten identically-named text boxes, five identical "X" buttons and up to ten identical "Clear" buttons, with no way to tell which row any of them belongs to.
Expected: each control names the entity and the row it belongs to. The row number has to be part of it, not just a fallback for a blank key — two rows can carry the same key while being edited, and a server can persist duplicate metadata keys.
Scope
Both are in clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx. No behavior change beyond the copy and the accessible names.
Two defects in the Server Settings key/value editors, found while reading
ServerSettingsForm.1. The Custom Headers hint is factually backwards
The hint reads:
That is the opposite of what happens. In
@modelcontextprotocol/client@2.x, bothSSEClientTransport._commonHeaders()and the streamable-HTTP equivalent do:The custom headers are spread after the OAuth bearer, so a custom
Authorizationvalue overrides the OAuth access token rather than being ignored.Why it matters: a user who set a bearer by hand before configuring OAuth is told, by the UI, that the stale value is harmless. It is not — it silently suppresses the token the OAuth flow just obtained, and the resulting 401 has no visible cause.
Expected: the hint tells the user a custom
Authorizationwins, so they know to remove it once OAuth is configured.2. Key/value rows are indistinguishable to a screen reader
KeyValueRowsrenders Custom Headers, Request Metadata and Environment Variables. Nothing associates a control with its row:placeholder("Key" / "Value"), which is not a label.So a settings form with three headers and two metadata entries presents ten identically-named text boxes, five identical "X" buttons and up to ten identical "Clear" buttons, with no way to tell which row any of them belongs to.
Expected: each control names the entity and the row it belongs to. The row number has to be part of it, not just a fallback for a blank key — two rows can carry the same key while being edited, and a server can persist duplicate metadata keys.
Scope
Both are in
clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx. No behavior change beyond the copy and the accessible names.