mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(mcp): connection health toolbar with bulk Retry All / Disconnect All actions (#2641)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
This commit is contained in:
co-authored by
Claude Opus 4.7
Steven Enamakel
parent
5a5cd43ea9
commit
32d03ca6aa
@@ -13,6 +13,7 @@ import InstallDialog from './InstallDialog';
|
||||
import InstalledServerDetail from './InstalledServerDetail';
|
||||
import InstalledServerList from './InstalledServerList';
|
||||
import McpCatalogBrowser from './McpCatalogBrowser';
|
||||
import McpConnectionHealthToolbar from './McpConnectionHealthToolbar';
|
||||
import McpInventoryPanel from './McpInventoryPanel';
|
||||
import McpServerSearch from './McpServerSearch';
|
||||
import type { ConnStatus, InstalledServer } from './types';
|
||||
@@ -139,6 +140,53 @@ const McpServersTab = () => {
|
||||
[loadInstalled, fetchStatuses]
|
||||
);
|
||||
|
||||
// Count rejected settlements and, if any, throw a descriptive error so the
|
||||
// toolbar surfaces it through its `role="alert"` region — otherwise a bulk
|
||||
// action that partially (or wholly) fails looks identical to success and
|
||||
// the user is left re-scanning the status dots. The status refresh still
|
||||
// runs first so the dots reconcile regardless of the partial failure.
|
||||
const reportBulkFailures = useCallback(
|
||||
(results: PromiseSettledResult<unknown>[], total: number) => {
|
||||
const failed = results.filter(r => r.status === 'rejected').length;
|
||||
if (failed > 0) {
|
||||
log('bulk op partial failure: %d/%d failed', failed, total);
|
||||
throw new Error(
|
||||
t('mcp.health.bulkPartialFailure')
|
||||
.replace('{failed}', String(failed))
|
||||
.replace('{total}', String(total))
|
||||
);
|
||||
}
|
||||
},
|
||||
[t]
|
||||
);
|
||||
|
||||
// Bulk Retry — iterate through errored servers, collect per-server outcomes
|
||||
// via `Promise.allSettled` so one bad apple doesn't abort the batch, then
|
||||
// refresh statuses once at the end. The toolbar shows its own disabled state
|
||||
// during the await; the next poll tick reconciles any drift. Partial/total
|
||||
// failures are surfaced via `reportBulkFailures`.
|
||||
const handleBulkReconnect = useCallback(
|
||||
async (serverIds: string[]) => {
|
||||
log('bulk reconnect ids=%o', serverIds);
|
||||
const results = await Promise.allSettled(serverIds.map(id => mcpClientsApi.connect(id)));
|
||||
await fetchStatuses();
|
||||
reportBulkFailures(results, serverIds.length);
|
||||
},
|
||||
[fetchStatuses, reportBulkFailures]
|
||||
);
|
||||
|
||||
// Bulk Disconnect — same shape as bulk reconnect. The toolbar gates this
|
||||
// behind a confirmation dialog before we get here.
|
||||
const handleBulkDisconnect = useCallback(
|
||||
async (serverIds: string[]) => {
|
||||
log('bulk disconnect ids=%o', serverIds);
|
||||
const results = await Promise.allSettled(serverIds.map(id => mcpClientsApi.disconnect(id)));
|
||||
await fetchStatuses();
|
||||
reportBulkFailures(results, serverIds.length);
|
||||
},
|
||||
[fetchStatuses, reportBulkFailures]
|
||||
);
|
||||
|
||||
const selectedServerId = rightPane.mode === 'detail' ? rightPane.serverId : null;
|
||||
const selectedServer = servers.find(s => s.server_id === selectedServerId) ?? null;
|
||||
const selectedConnStatus = statuses.find(s => s.server_id === selectedServerId);
|
||||
@@ -171,13 +219,18 @@ const McpServersTab = () => {
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex gap-4 flex-1 min-h-0">
|
||||
{/* Left pane: search + installed list */}
|
||||
{/* Left pane: health toolbar + search + installed list */}
|
||||
<div className="w-56 shrink-0 flex flex-col">
|
||||
{loadError && (
|
||||
<div className="mb-2 rounded-lg border border-coral-200 dark:border-coral-500/30 bg-coral-50 dark:bg-coral-500/10 px-3 py-2 text-xs text-coral-700 dark:text-coral-300">
|
||||
{loadError}
|
||||
</div>
|
||||
)}
|
||||
<McpConnectionHealthToolbar
|
||||
statuses={statuses}
|
||||
onReconnect={handleBulkReconnect}
|
||||
onDisconnect={handleBulkDisconnect}
|
||||
/>
|
||||
{servers.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<McpServerSearch value={searchFilter} onChange={setSearchFilter} />
|
||||
|
||||
Reference in New Issue
Block a user