Conversation
Adds one availability derivation and one indicator for MCP server health, starting with the servers page. The "Authorization needed" state is new. OAuth tokens are held per user per gateway, so an authorization_code server that a colleague authorized is still unusable for everyone else. The health check treats the resulting 401 as healthy on purpose, so those rows read Active with zero components today. GET /oauth/status reports the caller's own token state, and the indicator maps a missing or expired token to that state. Retires the "warning" status. Two conflicting definitions existed: unreachable but seen before, and reachable but stale over five minutes. The first is Offline by another name; the second fires on any server the health loop has not reached yet. Unreachable now splits into Offline and Connecting on whether the server has ever responded. Deletes ServerStatusBadge, which nothing rendered. Closes #6464 Closes #6465 Signed-off-by: Anna Effort <anna.effort@ibm.com>
The sources step listed every MCP server with a status it did not act on, so a user could select a server that contributes nothing and get an empty virtual server with no explanation. Sources stay selectable in every state. Marking a gateway unreachable skips the cascade that disables its components, so an offline source keeps a usable catalog and adding it produces a working virtual server whose calls fail until the server returns. Instead of blocking, each row carries the shared status indicator and a submit-time note names any selected source with nothing to add. Splits the zero-component empty state, which covered three situations with one sentence. A failed list now names itself and offers a retry, an unavailable source explains why it is empty, and a healthy source exposing nothing still reads as empty. Attributes a failed component fetch to the source that caused it, rather than failing the whole create with one unattributed error. Extracts useOAuthTokenStatuses, shared with the servers page. Closes #6404 Signed-off-by: Anna Effort <anna.effort@ibm.com>
The unreachable detail text says components stay listed from the last sync, which reads as a contradiction when the list is empty. Adds an empty-state message per availability, and drops the status keys and the ServerStatus type left unused by the shared indicator. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Signed-off-by: Anna Effort <anna.effort@ibm.com>
States with nothing to resolve open a popover rather than a modal, matching the visibility info affordance. Authorization needed starts the OAuth flow on click instead of describing it behind a dialog, and falls back to the popover where the caller has no way to run it. Turn on goes, since Activate is already in the row menu. Signed-off-by: Anna Effort <anna.effort@ibm.com>
…r-status Signed-off-by: Anna Effort <anna.effort@ibm.com>
Recast the status explanations and empty states around the server rather than ContextForge, and stop showing the last error on inactive servers, where the health loop leaves it behind from an earlier outage. Narrow the popover to max-w-xs and anchor it to the end of the status label. The source picker also needed justify-self-start: its trigger is a grid item, so it stretched to the full column and the panel anchored to the column edge. Shorten the compact label to Auth, keeping the full word for screen readers. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Stack the status label and the authorizing label in one grid cell so the wider of the two sets the width. Reserving it rather than setting a min-width keeps it right per locale, where the two labels differ by different amounts. The stack is start-aligned so the slack falls after the label, leaving the icon and text where every other row has them. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Recast the status explanations around the server rather than ContextForge, and stop showing the last error on inactive servers, where the health loop leaves it behind from an earlier outage. Narrow the popover to max-w-xs and anchor it to the end of the status label. Shorten the compact label to Auth, keeping the full word for screen readers. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Signed-off-by: Anna Effort <anna.effort@ibm.com>
…r-status Signed-off-by: Anna Effort <anna.effort@ibm.com>
There was a problem hiding this comment.
🟡 Changes recommended
OAuth status loading fails once the accumulated server list exceeds the API’s 100-ID limit.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Uses shared MCP availability states throughout virtual-server source selection and improves component-loading feedback.
Changes:
- Adds shared OAuth status loading and availability-aware empty states.
- Keeps unavailable sources selectable while warning about empty sources.
- Adds independent component errors, retries, and source-attributed creation errors.
File summaries
| File | Description |
|---|---|
src/types/server.ts |
Removes the obsolete status type. |
src/pages/Servers.tsx |
Adopts the shared OAuth-status hook. |
src/pages/CreateServer.tsx |
Adds shared statuses, retries, and attributed errors. |
src/pages/CreateServer.test.tsx |
Updates status and error tests. |
src/lib/serverStatus.ts |
Adds availability-specific empty-state messages. |
src/lib/serverStatus.test.ts |
Validates presentation message IDs. |
src/hooks/useOAuthTokenStatuses.ts |
Centralizes OAuth token-status loading. |
src/components/gateways/SourceSelection.tsx |
Adds shared statuses and empty-source warnings. |
src/components/gateways/SourceSelection.test.tsx |
Tests selection, warnings, and source names. |
src/i18n/locales/en-US/mcpServer.json |
Updates English status messages. |
src/i18n/locales/en-US/gateways.json |
Adds English source and loading messages. |
src/i18n/locales/es-ES/mcpServer.json |
Updates Spanish status messages. |
src/i18n/locales/es-ES/gateways.json |
Adds Spanish source and loading messages. |
src/i18n/locales/pt-BR/mcpServer.json |
Updates Portuguese status messages. |
src/i18n/locales/pt-BR/gateways.json |
Adds Portuguese source and loading messages. |
Review details
- Files reviewed: 15/15 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The /oauth/status route rejects more than 100 ids, and a paged-through server list can exceed that, so getOAuthStatus splits and merges. A rejected refetch after authorization skipped the status reload and surfaced as an unhandled rejection at the click handler. Signed-off-by: Anna Effort <anna.effort@ibm.com>
…r-status # Conflicts: # src/pages/Servers.tsx
Load More issues a second lookup while the first may still be running, and an earlier response carrying fewer ids would drop the rows it omits back to reachability. Signed-off-by: Anna Effort <anna.effort@ibm.com>
182a4aa to
730f2f2
Compare
|
LLM review feedback:
|
marekdano
left a comment
There was a problem hiding this comment.
Findings
🟡 Medium — Silent partial failure on multi-source component fetch
File: src/pages/CreateServer.tsx:229
getComponentsForSelectedMCPServers wraps each source's component-fetch failure in a SourceComponentsError, but since the fetches run under Promise.all, it rejects on the first source to fail — any other failing sources are never surfaced.
Failure scenario: User selects two unreachable sources, A and B, and clicks Submit/Skip. Both component fetches reject, but Promise.all settles with whichever rejects first (say A). The UI shows only "A: <error>" — there's no indication B also failed to contribute components to the new virtual server.
Suggested fix: Use Promise.allSettled and aggregate all failing sources into the error message.
🟢 Low — Stale id can lose its friendly name on submit
File: src/components/gateways/SourceSelection.tsx:144
toggleMCPServerSelection builds namesById only from the current availableMCPServers. If that list is refetched and a previously selected id drops out before submit, selectedSourceIds still holds the stale id, but namesById has no entry for it.
Failure scenario: User selects a source; the sources list refetches and that source's id is no longer present in availableMCPServers (e.g. reassigned/removed upstream). The id stays in selectedMCPServerIds. On submit, if that source's component fetch fails, getCreateServerError falls back to showing the raw id instead of the source's friendly name.
Suggested fix: Edge case, minor UX regression only — low priority, but worth tracking namesById as accumulated state rather than deriving fresh from the latest fetch.
Signed-off-by: Anna Effort <anna.effort@ibm.com>
The guard tested the availability state, but auth outranks inactive, so a disabled server whose token had also expired kept rendering the error the health loop left behind before it was turned off. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Component fetches ran under Promise.all, so only the first source to reject was named. They now settle and every failure is reported. Source names were derived from the current list, which lost the name of a selected source that a refetch had dropped. They are kept from selection time. Signed-off-by: Anna Effort <anna.effort@ibm.com>
|
Both fixed in b0a9a01.
|
…r-status Signed-off-by: Anna Effort <anna.effort@ibm.com>
marekdano
left a comment
There was a problem hiding this comment.
The PR looks good now!
LGTM 🚀
gcgoncalves
left a comment
There was a problem hiding this comment.
Since you'll have to sort out the merge conflicts anyway, here are a minor a11y suggestion on src/components/servers/ServerStatusIndicator.tsx:
<PopoverContent
align="end"
className="w-auto max-w-xs p-3"
aria-label={intl.formatMessage(
{ id: "mcpServer.status.trigger" },
{ name: server.name, status: fullLabel },
)}
>
<ServerStatusDetail ... />
</PopoverContent>Adding a label to popover content to match WCAG rules (let's screen readers know the popup purpose)
|
Suggest holding this until #150 merges, then rebasing on top of it. #150 adds useOAuthStatuses, which covers what useOAuthTokenStatuses does here and also handles request cancellation, versioning per id so one refresh cannot overwrite another, explicit loading and unavailable states, a retry path, and servers whose grant type is not authorization_code. It also rewrites SourceSelection to drop the local status helpers and use ServerStatusIndicator and that hook instead, which is most of what this PR changes in that file. If this merges first, we end up with two hooks under nearly the same name doing the same job, and #150 has to resolve against it. This branch needs a rebase regardless, since #130 squash-merged and main now carries that content under a different commit. Rebasing once on top of #150 is less work than rebasing onto main now and repeating it afterwards. The parts that do not overlap #150 carry over unchanged: the empty state strings for sources with no components, the active voice wording, and naming every failing source while keeping the names of dropped ones. cc @vishu-bh, since the hook overlap sits mostly on your side. |
|
@gcgoncalves , good call re the popover A11Y improvement - ty! Added the fix to #153, which owns that popover now and created IBM/mcp-context-forge#6989 to improve this for other popovers, also. |
|
@a-effort - please look at the failing Playwright tests. |
Stacked on #130, which adds the shared status indicator this uses. Please review that one first; this PR targets its branch so the diff stays readable.
The source picker now uses the shared derivation from #130. A source reads the same here as on the servers page, including OAuth sources the caller has not authorized yet. Auth note: this version includes a popover vs connecting to the OAuth authorize flow (IBM/mcp-context-forge#6859), because no authorize handler is wired into the picker.
Sources stay selectable in every state and components discovered from a source remain in the catalog when it goes unreachable, so a virtual server built from them still resolves. The health loop takes up to three intervals to mark a source unreachable, so this approach enables the user to move forward and fix any issues later, if there are any.
Tools, resources and prompts now fail independently, each with its own message (popover).
131.mp4
Part of IBM/mcp-context-forge#6404