feat: show catalog add failure error on the card - #153
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The add-error live region retains stale content and cannot reannounce an identical retry failure.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Adds card-level catalog registration errors and introduces reusable MCP server/OAuth status presentation.
Changes:
- Displays add failures on the affected catalog card with accessible announcements.
- Adds OAuth-aware server statuses and authorization actions.
- Adds localized strings and comprehensive component/API tests.
| File | Description |
|---|---|
src/types/server.ts |
Adds status and OAuth response types. |
src/pages/Servers.tsx |
Loads OAuth statuses and handles authorization. |
src/pages/Servers.test.tsx |
Tests authorization status refreshes. |
src/pages/ServerCatalog.tsx |
Adds card-level registration errors. |
src/pages/ServerCatalog.test.tsx |
Tests card errors and announcements. |
src/lib/serverStatus.ts |
Centralizes server availability classification. |
src/lib/serverStatus.test.ts |
Tests availability states. |
src/i18n/locales/pt-BR/mcpServer.json |
Adds Portuguese status messages. |
src/i18n/locales/es-ES/mcpServer.json |
Adds Spanish status messages. |
src/i18n/locales/en-US/mcpServer.json |
Adds English status messages. |
src/components/ui/status-indicator.tsx |
Adds reusable status indicator. |
src/components/ui/status-indicator.test.tsx |
Tests indicator behavior. |
src/components/servers/ServerStatusIndicator.tsx |
Adds OAuth-aware server status UI. |
src/components/servers/ServerStatusIndicator.test.tsx |
Tests server status interactions. |
src/components/servers/ServerStatusDetail.tsx |
Displays status details and errors. |
src/components/servers/ServerStatusBadge.tsx |
Removes superseded status badge. |
src/components/servers/ServerStatusBadge.test.tsx |
Removes obsolete badge tests. |
src/components/servers/ServersTable.tsx |
Integrates the new status indicator. |
src/components/servers/ServersTable.test.tsx |
Updates status table tests. |
src/components/server-catalog/CatalogResults.tsx |
Renders card error indicators. |
src/api/servers.ts |
Adds batched OAuth status retrieval. |
src/api/servers.test.ts |
Tests OAuth status batching. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Extracts the presentational shell of ServerStatusIndicator so the catalog card can reuse the icon, muted label and popover without inheriting the server availability model. Open-auth add failures now render an error indicator in the card footer next to Add, with the reason in a popover, instead of a notification above the grid that pushed the cards down. The failures that have no card left to carry them, and every other action outcome, still use the page stack. Signed-off-by: Anna Effort <anna.effort@ibm.com>
5e4c2bb to
0958e67
Compare
Radix gives PopoverContent role="dialog" without a name, so the shared indicator's popover announced as an unnamed dialog. Adds contentAriaLabel alongside the existing triggerAriaLabel and supplies it from both callers. The trigger's string is not reused: it ends in "Show details", which reads as an instruction to open what is already open and repeats the status text. Signed-off-by: Anna Effort <anna.effort@ibm.com>
The label uses the foreground token instead of muted, and the indicator sits 8px further from the view button. The muted label stays the default on StatusIndicator, so the servers page and the source picker are unchanged. Signed-off-by: Anna Effort <anna.effort@ibm.com>
The live region kept the previous message, so a second identical failure set the same string, produced no DOM change and announced nothing. Emptying it when the retry starts also drops the stale error once an add succeeds. Signed-off-by: Anna Effort <anna.effort@ibm.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The reusable component permits interactive popovers with unnamed dialogs, creating an accessibility gap.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (1)
Resolved since last review (1)
The two reporters were trailing positional params, so wanting onAddFailure alone meant passing undefined for reportNotification. Signed-off-by: Anna Effort <anna.effort@ibm.com>
The card error tests all went through a thrown request. A resolved
{ success: false } response takes a different branch and is the only one
that shows the backend's own message.
Signed-off-by: Anna Effort <anna.effort@ibm.com>
marekdano
left a comment
There was a problem hiding this comment.
Findings
🔴 Critical — fix before merge
1. Screen-reader announcement isn't scoped per server
File: src/pages/ServerCatalog.tsx:498
addErrorAnnouncement is a single page-wide string shared by every server. If two Add failures happen in quick succession (or an open-auth failure overlaps another dialog submit, since clearAddError runs unconditionally on every flow), the later call can overwrite/blank the string before assistive tech announces the earlier one — React batching means the intermediate value may never even hit the DOM. The first failure is silently never announced, though the card still shows it visually.
2. Stale error can resurface on card remount
File: src/components/server-catalog/CatalogResults.tsx:449
addErrors lives in parent state and is only cleared by a fresh add attempt on that
exact server id. If server A's add fails, then a search/filter unmounts and later remounts its card (e.g. a filter round-trip), the old error badge reappears even though nothing changed and no new attempt was made.
3. Announcement drops the actual failure reason
File: src/pages/ServerCatalog.tsx:500
The new aria-live text is just "Error adding {name}" — the previous role="alert"
notification spoke the real reason (e.g. "Remote server error..."). Screen-reader users
now have to separately find and activate a popover to learn why: a regression at the
moment of failure.
🟡 Medium — cleanup, worth doing but not blocking
4. New parallel error-reporting mechanism instead of extending the existing one
File: src/pages/ServerCatalog.tsx:79
RegistrationReporting.onAddFailure + addErrors + addErrorAnnouncement duplicates what could have been a target option on the existing RegistrationNotification stack. Forces two unrelated call sites (handleApiKeySubmit, handleOAuthSubmit) to wrap an unchanged callback just to match the new shape, and a call site that forgets onAddFailure silently falls back to the old stack with no compile-time signal.
5. Duplicated Tailwind class strings
File: src/components/servers/ServerStatusIndicator.tsx:65
The authorize branch now hardcodes its own copy of the layout/trigger classes that used to be shared with the Popover branch (moved into status-indicator.tsx). Future styling changes will silently miss this branch.
6. addErrors hand-rolls a pattern usePendingIds already generalizes
File: src/pages/ServerCatalog.tsx:413
Same file already uses usePendingIds three times for other per-server state; this is a fourth hand-written spread/delete implementation of the same idea.
Announce the reason alongside the name, and leave the announcement alone when a different server starts an add, whose failure is still on its card. Signed-off-by: Anna Effort <anna.effort@ibm.com>
|
Thanks for reviewing @marekdano! Changes added in f790586. Details: From LLM-assisted analysis of the feedback, the recommendation is to move forward with these changes: Feedback item 3: implemented.
Feedback item 5: implemented. Feedback item 1: partly implemented.
From LLM-assisted analysis of the feedback, the recommendation is to not move forward with these changes: Feedback item 2: not implemented.
Feedback item 4: not implemented.
Feedback item 6: not implemented.
One thing to flag: #160 and #153 will collide. Both add Lmk if I'm missing something, though pls! 🙌 |
Closing the popover clears the error, so the badge does not need a dismiss control in a card footer with no room for one. Signed-off-by: Anna Effort <anna.effort@ibm.com>
Closing the popover unmounts the trigger focus returns to, dropping it on the body. Focus moves to Add, and only where it was otherwise lost. 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 🚀
#153 landed its own copy of the status indicator shell, so the two extractions collided. Keeps main's labelClassName, size and onOpenChange alongside the shared trigger classes, the icon export and the popover name fallback from this branch, and carries forward the removal of the unused interactive prop. Signed-off-by: Anna Effort <anna.effort@ibm.com>

Summary:
Add failure errors (for open MCP servers) now appear on the card that failed with more info available in a popover.
153.mp4
This fixes the current behavior where errors push down the cards and may be far from the card they relate to (ux issue).
Closes IBM/mcp-context-forge#6463
Notes:
The presentational part of
ServerStatusIndicatormoves intoui/status-indicator, so the card reuses the icon, label and popover without inheriting the server availability model.Some outcomes stay on the page stack, either because no card is left to carry them or because the message belongs elsewhere: a 404 removes the card, a 409 is reported as success, and the API key and OAuth flows keep the notification inside their dialog.
The announcement uses a bare
aria-liveregion rather thanrole="status", sinceCatalogResultsalready has one for the result count and a second breaks its accessible name lookups.The
ui/status-indicatorextraction this builds on is under review in [#160](#160). Once #160 merges, I will rebase this onto it, and this PR will contain only the catalog feature.