diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx index 9708b1b1ff9..97d64680a7d 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx @@ -71,6 +71,7 @@ interface ServerListItemProps { isConnecting: boolean isLoadingTools?: boolean isRefreshing?: boolean + discoveryError?: string | null onRemove: () => void onViewDetails: () => void onAuthorize: () => void @@ -84,6 +85,7 @@ function ServerListItem({ isConnecting, isLoadingTools = false, isRefreshing = false, + discoveryError = null, onRemove, onViewDetails, onAuthorize, @@ -95,8 +97,17 @@ function ServerListItem({ server.lastError, server.authType ) + // A live discovery failure whose stored status hasn't caught up yet would otherwise read as + // "0 tools"; surface it directly so a failed row reads as failed, not empty. + const showDiscoveryError = + Boolean(discoveryError) && + tools.length === 0 && + server.connectionStatus !== 'error' && + server.connectionStatus !== 'disconnected' const hasConnectionIssue = - server.connectionStatus === 'error' || server.connectionStatus === 'disconnected' + server.connectionStatus === 'error' || + server.connectionStatus === 'disconnected' || + showDiscoveryError return (
@@ -117,7 +128,9 @@ function ServerListItem({ ? 'Refreshing...' : isLoadingTools && tools.length === 0 ? 'Loading...' - : toolsLabel} + : showDiscoveryError + ? discoveryError + : toolsLabel}

@@ -386,7 +399,15 @@ export function MCP() { return issues } - const error = toolsError || serversError + // Only a failure to load the server LIST replaces the list. A tool-discovery failure + // (`toolsError`) must not blank the page — the servers still render, each row surfacing its + // own discovery state via `toolsStateByServer`, with a non-blocking notice above the list. + const listError = serversError + // Any per-server discovery failure — even a partial one where other servers succeeded (which + // suppresses the aggregate `toolsError`) — so the notice below still surfaces it. + const hasDiscoveryError = + Boolean(toolsError) || + Array.from(toolsStateByServer.values()).some((state) => state.error != null) const hasServers = servers && servers.length > 0 const showNoResults = searchTerm.trim() && filteredServers.length === 0 && servers.length > 0 @@ -646,10 +667,10 @@ export function MCP() { : [] } > - {error ? ( + {listError ? (

- {getErrorMessage(error, 'Failed to load MCP servers')} + {getErrorMessage(listError, 'Failed to load MCP servers')}

) : serversLoading ? null : !hasServers ? ( @@ -658,6 +679,11 @@ export function MCP() { ) : (
+ {hasDiscoveryError && ( +

+ {getErrorMessage(toolsError, 'Some tools could not be discovered')} +

+ )} {filteredServers.map((server) => { if (!server?.id) return null const tools = toolsByServer[server.id] || [] @@ -679,6 +705,9 @@ export function MCP() { refreshServerMutation.isPending && refreshServerMutation.variables?.serverId === server.id } + discoveryError={ + serverToolsState?.error ? getErrorMessage(serverToolsState.error) : null + } onRemove={() => handleRemoveServer(server.id)} onViewDetails={() => handleViewDetails(server.id)} onAuthorize={() => startOauthForServer(server.id)}