Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ interface ServerListItemProps {
isConnecting: boolean
isLoadingTools?: boolean
isRefreshing?: boolean
discoveryError?: string | null
onRemove: () => void
onViewDetails: () => void
onAuthorize: () => void
Expand All @@ -84,6 +85,7 @@ function ServerListItem({
isConnecting,
isLoadingTools = false,
isRefreshing = false,
discoveryError = null,
onRemove,
onViewDetails,
onAuthorize,
Expand All @@ -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 (
<div className='flex items-center justify-between gap-3'>
Expand All @@ -117,7 +128,9 @@ function ServerListItem({
? 'Refreshing...'
: isLoadingTools && tools.length === 0
? 'Loading...'
: toolsLabel}
: showDiscoveryError
? discoveryError
: toolsLabel}
</p>
</div>
<div className='flex flex-shrink-0 items-center gap-1'>
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -646,10 +667,10 @@ export function MCP() {
: []
}
>
{error ? (
{listError ? (
<div className='flex h-full flex-col items-center justify-center gap-2'>
<p className='text-[var(--text-error)] text-xs leading-tight'>
{getErrorMessage(error, 'Failed to load MCP servers')}
{getErrorMessage(listError, 'Failed to load MCP servers')}
</p>
</div>
) : serversLoading ? null : !hasServers ? (
Expand All @@ -658,6 +679,11 @@ export function MCP() {
</SettingsEmptyState>
) : (
<div className='flex flex-col gap-2'>
{hasDiscoveryError && (
<p className='text-[var(--text-error)] text-xs leading-tight'>
{getErrorMessage(toolsError, 'Some tools could not be discovered')}
</p>
)}
{filteredServers.map((server) => {
if (!server?.id) return null
const tools = toolsByServer[server.id] || []
Expand All @@ -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)}
Expand Down
Loading