import React, { useMemo } from 'react' import { getMcpConfigsByScope } from 'src/services/mcp/config.js' import type { ConfigScope } from 'src/services/mcp/types.js' import { describeMcpConfigFilePath, getScopeLabel, } from 'src/services/mcp/utils.js' import type { ValidationError } from 'src/utils/settings/validation.js' import { Box, Link, Text } from '../../ink.js' function McpConfigErrorSection({ scope, parsingErrors, warnings, }: { scope: ConfigScope parsingErrors: ValidationError[] warnings: ValidationError[] }): React.ReactNode { const hasErrors = parsingErrors.length > 0 const hasWarnings = warnings.length > 0 if (!hasErrors && !hasWarnings) { return null } return ( {(hasErrors || hasWarnings) && ( [{hasErrors ? 'Failed to parse' : 'Contains warnings'}]{' '} )} {getScopeLabel(scope)} Location: {describeMcpConfigFilePath(scope)} {parsingErrors.map((error, i) => { const serverName = error.mcpErrorMetadata?.serverName return ( [Error] {' '} {serverName && `[${serverName}] `} {error.path && error.path !== '' ? `${error.path}: ` : ''} {error.message} ) })} {warnings.map((warning, i) => { const serverName = warning.mcpErrorMetadata?.serverName return ( [Warning] {' '} {serverName && `[${serverName}] `} {warning.path && warning.path !== '' ? `${warning.path}: ` : ''} {warning.message} ) })} ) } export function McpParsingWarnings(): React.ReactNode { // Config files don't change during dialog lifetime; read once on mount // to avoid blocking file IO on every re-render. const scopes = useMemo( () => [ { scope: 'user', config: getMcpConfigsByScope('user') }, { scope: 'project', config: getMcpConfigsByScope('project') }, { scope: 'local', config: getMcpConfigsByScope('local') }, { scope: 'enterprise', config: getMcpConfigsByScope('enterprise') }, ] satisfies Array<{ scope: ConfigScope config: { errors: ValidationError[] } }>, [], ) const hasParsingErrors = scopes.some( ({ config }) => filterErrors(config.errors, 'fatal').length > 0, ) const hasWarnings = scopes.some( ({ config }) => filterErrors(config.errors, 'warning').length > 0, ) if (!hasParsingErrors && !hasWarnings) { return null } return ( MCP Config Diagnostics For help configuring MCP servers, see:{' '} https://code.claude.com/docs/en/mcp {scopes.map(({ scope, config }) => ( ))} {/* TODO: Add additional diagnostic sections: * - Duplicate Server Names (check for servers with same name across scopes) * This section should include: * - File paths where each server is defined * - More detailed location info for user/local scopes * - Approved / disabled status of servers */} ) } function filterErrors( errors: ValidationError[], severity: 'fatal' | 'warning', ): ValidationError[] { return errors.filter(e => e.mcpErrorMetadata?.severity === severity) }