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)
}