import { useState, useEffect } from 'react'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Badge } from './ui/badge'; import { X, Plus, Settings as SettingsIcon, Shield, AlertTriangle, Moon, Sun, Server, Edit3, Trash2, Globe, Terminal, Zap, FolderOpen, LogIn, Key, GitBranch, Check } from 'lucide-react'; import { useTheme } from '../contexts/ThemeContext'; import ClaudeLogo from './ClaudeLogo'; import CursorLogo from './CursorLogo'; import CodexLogo from './CodexLogo'; import CredentialsSettings from './CredentialsSettings'; import GitSettings from './GitSettings'; import TasksSettings from './TasksSettings'; import LoginModal from './LoginModal'; import { authenticatedFetch } from '../utils/api'; // New settings components import AgentListItem from './settings/AgentListItem'; import AccountContent from './settings/AccountContent'; import PermissionsContent from './settings/PermissionsContent'; import McpServersContent from './settings/McpServersContent'; function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }) { const { isDarkMode, toggleDarkMode } = useTheme(); const [allowedTools, setAllowedTools] = useState([]); const [disallowedTools, setDisallowedTools] = useState([]); const [newAllowedTool, setNewAllowedTool] = useState(''); const [newDisallowedTool, setNewDisallowedTool] = useState(''); const [skipPermissions, setSkipPermissions] = useState(false); const [isSaving, setIsSaving] = useState(false); const [saveStatus, setSaveStatus] = useState(null); const [projectSortOrder, setProjectSortOrder] = useState('name'); const [mcpServers, setMcpServers] = useState([]); const [showMcpForm, setShowMcpForm] = useState(false); const [editingMcpServer, setEditingMcpServer] = useState(null); const [mcpFormData, setMcpFormData] = useState({ name: '', type: 'stdio', scope: 'user', projectPath: '', // For local scope config: { command: '', args: [], env: {}, url: '', headers: {}, timeout: 30000 }, jsonInput: '', // For JSON import importMode: 'form' // 'form' or 'json' }); const [mcpLoading, setMcpLoading] = useState(false); const [mcpTestResults, setMcpTestResults] = useState({}); const [mcpServerTools, setMcpServerTools] = useState({}); const [mcpToolsLoading, setMcpToolsLoading] = useState({}); const [activeTab, setActiveTab] = useState(initialTab); const [jsonValidationError, setJsonValidationError] = useState(''); const [selectedAgent, setSelectedAgent] = useState('claude'); // 'claude', 'cursor', or 'codex' const [selectedCategory, setSelectedCategory] = useState('account'); // 'account', 'permissions', or 'mcp' // Code Editor settings const [codeEditorTheme, setCodeEditorTheme] = useState(() => localStorage.getItem('codeEditorTheme') || 'dark' ); const [codeEditorWordWrap, setCodeEditorWordWrap] = useState(() => localStorage.getItem('codeEditorWordWrap') === 'true' ); const [codeEditorShowMinimap, setCodeEditorShowMinimap] = useState(() => localStorage.getItem('codeEditorShowMinimap') !== 'false' // Default true ); const [codeEditorLineNumbers, setCodeEditorLineNumbers] = useState(() => localStorage.getItem('codeEditorLineNumbers') !== 'false' // Default true ); const [codeEditorFontSize, setCodeEditorFontSize] = useState(() => localStorage.getItem('codeEditorFontSize') || '14' ); // Cursor-specific states const [cursorAllowedCommands, setCursorAllowedCommands] = useState([]); const [cursorDisallowedCommands, setCursorDisallowedCommands] = useState([]); const [cursorSkipPermissions, setCursorSkipPermissions] = useState(false); const [newCursorCommand, setNewCursorCommand] = useState(''); const [newCursorDisallowedCommand, setNewCursorDisallowedCommand] = useState(''); const [cursorMcpServers, setCursorMcpServers] = useState([]); // Codex-specific states const [codexMcpServers, setCodexMcpServers] = useState([]); const [codexPermissionMode, setCodexPermissionMode] = useState('default'); const [showCodexMcpForm, setShowCodexMcpForm] = useState(false); const [codexMcpFormData, setCodexMcpFormData] = useState({ name: '', type: 'stdio', config: { command: '', args: [], env: {} } }); const [editingCodexMcpServer, setEditingCodexMcpServer] = useState(null); const [codexMcpLoading, setCodexMcpLoading] = useState(false); const [showLoginModal, setShowLoginModal] = useState(false); const [loginProvider, setLoginProvider] = useState(''); const [selectedProject, setSelectedProject] = useState(null); const [claudeAuthStatus, setClaudeAuthStatus] = useState({ authenticated: false, email: null, loading: true, error: null }); const [cursorAuthStatus, setCursorAuthStatus] = useState({ authenticated: false, email: null, loading: true, error: null }); const [codexAuthStatus, setCodexAuthStatus] = useState({ authenticated: false, email: null, loading: true, error: null }); // Common tool patterns for Claude const commonTools = [ 'Bash(git log:*)', 'Bash(git diff:*)', 'Bash(git status:*)', 'Write', 'Read', 'Edit', 'Glob', 'Grep', 'MultiEdit', 'Task', 'TodoWrite', 'TodoRead', 'WebFetch', 'WebSearch' ]; // Common shell commands for Cursor const commonCursorCommands = [ 'Shell(ls)', 'Shell(mkdir)', 'Shell(cd)', 'Shell(cat)', 'Shell(echo)', 'Shell(git status)', 'Shell(git diff)', 'Shell(git log)', 'Shell(npm install)', 'Shell(npm run)', 'Shell(python)', 'Shell(node)' ]; // Fetch Cursor MCP servers const fetchCursorMcpServers = async () => { try { const response = await authenticatedFetch('/api/cursor/mcp'); if (response.ok) { const data = await response.json(); setCursorMcpServers(data.servers || []); } else { console.error('Failed to fetch Cursor MCP servers'); } } catch (error) { console.error('Error fetching Cursor MCP servers:', error); } }; const fetchCodexMcpServers = async () => { try { const configResponse = await authenticatedFetch('/api/codex/mcp/config/read'); if (configResponse.ok) { const configData = await configResponse.json(); if (configData.success && configData.servers) { setCodexMcpServers(configData.servers); return; } } const cliResponse = await authenticatedFetch('/api/codex/mcp/cli/list'); if (cliResponse.ok) { const cliData = await cliResponse.json(); if (cliData.success && cliData.servers) { const servers = cliData.servers.map(server => ({ id: server.name, name: server.name, type: server.type || 'stdio', scope: 'user', config: { command: server.command || '', args: server.args || [], env: server.env || {} } })); setCodexMcpServers(servers); } } } catch (error) { console.error('Error fetching Codex MCP servers:', error); } }; // MCP API functions const fetchMcpServers = async () => { try { // Try to read directly from config files for complete details const configResponse = await authenticatedFetch('/api/mcp/config/read'); if (configResponse.ok) { const configData = await configResponse.json(); if (configData.success && configData.servers) { setMcpServers(configData.servers); return; } } // Fallback to Claude CLI const cliResponse = await authenticatedFetch('/api/mcp/cli/list'); if (cliResponse.ok) { const cliData = await cliResponse.json(); if (cliData.success && cliData.servers) { // Convert CLI format to our format const servers = cliData.servers.map(server => ({ id: server.name, name: server.name, type: server.type, scope: 'user', config: { command: server.command || '', args: server.args || [], env: server.env || {}, url: server.url || '', headers: server.headers || {}, timeout: 30000 }, created: new Date().toISOString(), updated: new Date().toISOString() })); setMcpServers(servers); return; } } // Final fallback to direct config reading const response = await authenticatedFetch('/api/mcp/servers?scope=user'); if (response.ok) { const data = await response.json(); setMcpServers(data.servers || []); } else { console.error('Failed to fetch MCP servers'); } } catch (error) { console.error('Error fetching MCP servers:', error); } }; const saveMcpServer = async (serverData) => { try { if (editingMcpServer) { // For editing, remove old server and add new one await deleteMcpServer(editingMcpServer.id, 'user'); } // Use Claude CLI to add the server const response = await authenticatedFetch('/api/mcp/cli/add', { method: 'POST', body: JSON.stringify({ name: serverData.name, type: serverData.type, scope: serverData.scope, projectPath: serverData.projectPath, command: serverData.config?.command, args: serverData.config?.args || [], url: serverData.config?.url, headers: serverData.config?.headers || {}, env: serverData.config?.env || {} }) }); if (response.ok) { const result = await response.json(); if (result.success) { await fetchMcpServers(); // Refresh the list return true; } else { throw new Error(result.error || 'Failed to save server via Claude CLI'); } } else { const error = await response.json(); throw new Error(error.error || 'Failed to save server'); } } catch (error) { console.error('Error saving MCP server:', error); throw error; } }; const deleteMcpServer = async (serverId, scope = 'user') => { try { // Use Claude CLI to remove the server with proper scope const response = await authenticatedFetch(`/api/mcp/cli/remove/${serverId}?scope=${scope}`, { method: 'DELETE' }); if (response.ok) { const result = await response.json(); if (result.success) { await fetchMcpServers(); // Refresh the list return true; } else { throw new Error(result.error || 'Failed to delete server via Claude CLI'); } } else { const error = await response.json(); throw new Error(error.error || 'Failed to delete server'); } } catch (error) { console.error('Error deleting MCP server:', error); throw error; } }; const testMcpServer = async (serverId, scope = 'user') => { try { const response = await authenticatedFetch(`/api/mcp/servers/${serverId}/test?scope=${scope}`, { method: 'POST' }); if (response.ok) { const data = await response.json(); return data.testResult; } else { const error = await response.json(); throw new Error(error.error || 'Failed to test server'); } } catch (error) { console.error('Error testing MCP server:', error); throw error; } }; const discoverMcpTools = async (serverId, scope = 'user') => { try { const response = await authenticatedFetch(`/api/mcp/servers/${serverId}/tools?scope=${scope}`, { method: 'POST' }); if (response.ok) { const data = await response.json(); return data.toolsResult; } else { const error = await response.json(); throw new Error(error.error || 'Failed to discover tools'); } } catch (error) { console.error('Error discovering MCP tools:', error); throw error; } }; const saveCodexMcpServer = async (serverData) => { try { if (editingCodexMcpServer) { await deleteCodexMcpServer(editingCodexMcpServer.id); } const response = await authenticatedFetch('/api/codex/mcp/cli/add', { method: 'POST', body: JSON.stringify({ name: serverData.name, command: serverData.config?.command, args: serverData.config?.args || [], env: serverData.config?.env || {} }) }); if (response.ok) { const result = await response.json(); if (result.success) { await fetchCodexMcpServers(); return true; } else { throw new Error(result.error || 'Failed to save Codex MCP server'); } } else { const error = await response.json(); throw new Error(error.error || 'Failed to save server'); } } catch (error) { console.error('Error saving Codex MCP server:', error); throw error; } }; const deleteCodexMcpServer = async (serverId) => { try { const response = await authenticatedFetch(`/api/codex/mcp/cli/remove/${serverId}`, { method: 'DELETE' }); if (response.ok) { const result = await response.json(); if (result.success) { await fetchCodexMcpServers(); return true; } else { throw new Error(result.error || 'Failed to delete Codex MCP server'); } } else { const error = await response.json(); throw new Error(error.error || 'Failed to delete server'); } } catch (error) { console.error('Error deleting Codex MCP server:', error); throw error; } }; const resetCodexMcpForm = () => { setCodexMcpFormData({ name: '', type: 'stdio', config: { command: '', args: [], env: {} } }); setEditingCodexMcpServer(null); setShowCodexMcpForm(false); }; const openCodexMcpForm = (server = null) => { if (server) { setEditingCodexMcpServer(server); setCodexMcpFormData({ name: server.name, type: server.type || 'stdio', config: { command: server.config?.command || '', args: server.config?.args || [], env: server.config?.env || {} } }); } else { resetCodexMcpForm(); } setShowCodexMcpForm(true); }; const handleCodexMcpSubmit = async (e) => { e.preventDefault(); setCodexMcpLoading(true); try { if (editingCodexMcpServer) { // Delete old server first, then add new one await deleteCodexMcpServer(editingCodexMcpServer.name); } await saveCodexMcpServer(codexMcpFormData); resetCodexMcpForm(); setSaveStatus('success'); } catch (error) { alert(`Error: ${error.message}`); setSaveStatus('error'); } finally { setCodexMcpLoading(false); } }; const handleCodexMcpDelete = async (serverName) => { if (confirm('Are you sure you want to delete this MCP server?')) { try { await deleteCodexMcpServer(serverName); setSaveStatus('success'); } catch (error) { alert(`Error: ${error.message}`); setSaveStatus('error'); } } }; useEffect(() => { if (isOpen) { loadSettings(); checkClaudeAuthStatus(); checkCursorAuthStatus(); checkCodexAuthStatus(); setActiveTab(initialTab); } }, [isOpen, initialTab]); // Persist code editor settings to localStorage useEffect(() => { localStorage.setItem('codeEditorTheme', codeEditorTheme); window.dispatchEvent(new Event('codeEditorSettingsChanged')); }, [codeEditorTheme]); useEffect(() => { localStorage.setItem('codeEditorWordWrap', codeEditorWordWrap.toString()); window.dispatchEvent(new Event('codeEditorSettingsChanged')); }, [codeEditorWordWrap]); useEffect(() => { localStorage.setItem('codeEditorShowMinimap', codeEditorShowMinimap.toString()); window.dispatchEvent(new Event('codeEditorSettingsChanged')); }, [codeEditorShowMinimap]); useEffect(() => { localStorage.setItem('codeEditorLineNumbers', codeEditorLineNumbers.toString()); window.dispatchEvent(new Event('codeEditorSettingsChanged')); }, [codeEditorLineNumbers]); useEffect(() => { localStorage.setItem('codeEditorFontSize', codeEditorFontSize); window.dispatchEvent(new Event('codeEditorSettingsChanged')); }, [codeEditorFontSize]); const loadSettings = async () => { try { // Load Claude settings from localStorage const savedSettings = localStorage.getItem('claude-settings'); if (savedSettings) { const settings = JSON.parse(savedSettings); setAllowedTools(settings.allowedTools || []); setDisallowedTools(settings.disallowedTools || []); setSkipPermissions(settings.skipPermissions || false); setProjectSortOrder(settings.projectSortOrder || 'name'); } else { // Set defaults setAllowedTools([]); setDisallowedTools([]); setSkipPermissions(false); setProjectSortOrder('name'); } // Load Cursor settings from localStorage const savedCursorSettings = localStorage.getItem('cursor-tools-settings'); if (savedCursorSettings) { const cursorSettings = JSON.parse(savedCursorSettings); setCursorAllowedCommands(cursorSettings.allowedCommands || []); setCursorDisallowedCommands(cursorSettings.disallowedCommands || []); setCursorSkipPermissions(cursorSettings.skipPermissions || false); } else { // Set Cursor defaults setCursorAllowedCommands([]); setCursorDisallowedCommands([]); setCursorSkipPermissions(false); } // Load Codex settings from localStorage const savedCodexSettings = localStorage.getItem('codex-settings'); if (savedCodexSettings) { const codexSettings = JSON.parse(savedCodexSettings); setCodexPermissionMode(codexSettings.permissionMode || 'default'); } else { setCodexPermissionMode('default'); } // Load MCP servers from API await fetchMcpServers(); // Load Cursor MCP servers await fetchCursorMcpServers(); // Load Codex MCP servers await fetchCodexMcpServers(); } catch (error) { console.error('Error loading tool settings:', error); setAllowedTools([]); setDisallowedTools([]); setSkipPermissions(false); setProjectSortOrder('name'); } }; const checkClaudeAuthStatus = async () => { try { const response = await authenticatedFetch('/api/cli/claude/status'); if (response.ok) { const data = await response.json(); setClaudeAuthStatus({ authenticated: data.authenticated, email: data.email, loading: false, error: data.error || null }); } else { setClaudeAuthStatus({ authenticated: false, email: null, loading: false, error: 'Failed to check authentication status' }); } } catch (error) { console.error('Error checking Claude auth status:', error); setClaudeAuthStatus({ authenticated: false, email: null, loading: false, error: error.message }); } }; const checkCursorAuthStatus = async () => { try { const response = await authenticatedFetch('/api/cli/cursor/status'); if (response.ok) { const data = await response.json(); setCursorAuthStatus({ authenticated: data.authenticated, email: data.email, loading: false, error: data.error || null }); } else { setCursorAuthStatus({ authenticated: false, email: null, loading: false, error: 'Failed to check authentication status' }); } } catch (error) { console.error('Error checking Cursor auth status:', error); setCursorAuthStatus({ authenticated: false, email: null, loading: false, error: error.message }); } }; const checkCodexAuthStatus = async () => { try { const response = await authenticatedFetch('/api/cli/codex/status'); if (response.ok) { const data = await response.json(); setCodexAuthStatus({ authenticated: data.authenticated, email: data.email, loading: false, error: data.error || null }); } else { setCodexAuthStatus({ authenticated: false, email: null, loading: false, error: 'Failed to check authentication status' }); } } catch (error) { console.error('Error checking Codex auth status:', error); setCodexAuthStatus({ authenticated: false, email: null, loading: false, error: error.message }); } }; const handleClaudeLogin = () => { setLoginProvider('claude'); setSelectedProject(projects?.[0] || { name: 'default', fullPath: process.cwd() }); setShowLoginModal(true); }; const handleCursorLogin = () => { setLoginProvider('cursor'); setSelectedProject(projects?.[0] || { name: 'default', fullPath: process.cwd() }); setShowLoginModal(true); }; const handleCodexLogin = () => { setLoginProvider('codex'); setSelectedProject(projects?.[0] || { name: 'default', fullPath: process.cwd() }); setShowLoginModal(true); }; const handleLoginComplete = (exitCode) => { if (exitCode === 0) { setSaveStatus('success'); if (loginProvider === 'claude') { checkClaudeAuthStatus(); } else if (loginProvider === 'cursor') { checkCursorAuthStatus(); } else if (loginProvider === 'codex') { checkCodexAuthStatus(); } } }; const saveSettings = () => { setIsSaving(true); setSaveStatus(null); try { // Save Claude settings const claudeSettings = { allowedTools, disallowedTools, skipPermissions, projectSortOrder, lastUpdated: new Date().toISOString() }; // Save Cursor settings const cursorSettings = { allowedCommands: cursorAllowedCommands, disallowedCommands: cursorDisallowedCommands, skipPermissions: cursorSkipPermissions, lastUpdated: new Date().toISOString() }; // Save Codex settings const codexSettings = { permissionMode: codexPermissionMode, lastUpdated: new Date().toISOString() }; // Save to localStorage localStorage.setItem('claude-settings', JSON.stringify(claudeSettings)); localStorage.setItem('cursor-tools-settings', JSON.stringify(cursorSettings)); localStorage.setItem('codex-settings', JSON.stringify(codexSettings)); setSaveStatus('success'); setTimeout(() => { onClose(); }, 1000); } catch (error) { console.error('Error saving tool settings:', error); setSaveStatus('error'); } finally { setIsSaving(false); } }; const addAllowedTool = (tool) => { if (tool && !allowedTools.includes(tool)) { setAllowedTools([...allowedTools, tool]); setNewAllowedTool(''); } }; const removeAllowedTool = (tool) => { setAllowedTools(allowedTools.filter(t => t !== tool)); }; const addDisallowedTool = (tool) => { if (tool && !disallowedTools.includes(tool)) { setDisallowedTools([...disallowedTools, tool]); setNewDisallowedTool(''); } }; const removeDisallowedTool = (tool) => { setDisallowedTools(disallowedTools.filter(t => t !== tool)); }; // MCP form handling functions const resetMcpForm = () => { setMcpFormData({ name: '', type: 'stdio', scope: 'user', // Default to user scope projectPath: '', config: { command: '', args: [], env: {}, url: '', headers: {}, timeout: 30000 }, jsonInput: '', importMode: 'form' }); setEditingMcpServer(null); setShowMcpForm(false); setJsonValidationError(''); }; const openMcpForm = (server = null) => { if (server) { setEditingMcpServer(server); setMcpFormData({ name: server.name, type: server.type, scope: server.scope, projectPath: server.projectPath || '', config: { ...server.config }, raw: server.raw, // Store raw config for display importMode: 'form', // Always use form mode when editing jsonInput: '' }); } else { resetMcpForm(); } setShowMcpForm(true); }; const handleMcpSubmit = async (e) => { e.preventDefault(); setMcpLoading(true); try { if (mcpFormData.importMode === 'json') { // Use JSON import endpoint const response = await authenticatedFetch('/api/mcp/cli/add-json', { method: 'POST', body: JSON.stringify({ name: mcpFormData.name, jsonConfig: mcpFormData.jsonInput, scope: mcpFormData.scope, projectPath: mcpFormData.projectPath }) }); if (response.ok) { const result = await response.json(); if (result.success) { await fetchMcpServers(); // Refresh the list resetMcpForm(); setSaveStatus('success'); } else { throw new Error(result.error || 'Failed to add server via JSON'); } } else { const error = await response.json(); throw new Error(error.error || 'Failed to add server'); } } else { // Use regular form-based save await saveMcpServer(mcpFormData); resetMcpForm(); setSaveStatus('success'); } } catch (error) { alert(`Error: ${error.message}`); setSaveStatus('error'); } finally { setMcpLoading(false); } }; const handleMcpDelete = async (serverId, scope) => { if (confirm('Are you sure you want to delete this MCP server?')) { try { await deleteMcpServer(serverId, scope); setSaveStatus('success'); } catch (error) { alert(`Error: ${error.message}`); setSaveStatus('error'); } } }; const handleMcpTest = async (serverId, scope) => { try { setMcpTestResults({ ...mcpTestResults, [serverId]: { loading: true } }); const result = await testMcpServer(serverId, scope); setMcpTestResults({ ...mcpTestResults, [serverId]: result }); } catch (error) { setMcpTestResults({ ...mcpTestResults, [serverId]: { success: false, message: error.message, details: [] } }); } }; const handleMcpToolsDiscovery = async (serverId, scope) => { try { setMcpToolsLoading({ ...mcpToolsLoading, [serverId]: true }); const result = await discoverMcpTools(serverId, scope); setMcpServerTools({ ...mcpServerTools, [serverId]: result }); } catch (error) { setMcpServerTools({ ...mcpServerTools, [serverId]: { success: false, tools: [], resources: [], prompts: [] } }); } finally { setMcpToolsLoading({ ...mcpToolsLoading, [serverId]: false }); } }; const updateMcpConfig = (key, value) => { setMcpFormData(prev => ({ ...prev, config: { ...prev.config, [key]: value } })); }; const getTransportIcon = (type) => { switch (type) { case 'stdio': return ; case 'sse': return ; case 'http': return ; default: return ; } }; if (!isOpen) return null; return (

Settings

{/* Tab Navigation */}
{/* Appearance Tab */} {activeTab === 'appearance' && (
{activeTab === 'appearance' && (
{/* Theme Settings */}
Dark Mode
Toggle between light and dark themes
{/* Project Sorting */}
Project Sorting
How projects are ordered in the sidebar
{/* Code Editor Settings */}

Code Editor

{/* Editor Theme */}
Editor Theme
Default theme for the code editor
{/* Word Wrap */}
Word Wrap
Enable word wrapping by default in the editor
{/* Show Minimap */}
Show Minimap
Display a minimap for easier navigation in diff view
{/* Show Line Numbers */}
Show Line Numbers
Display line numbers in the editor
{/* Font Size */}
Font Size
Editor font size in pixels
)}
)} {/* Git Tab */} {activeTab === 'git' && } {/* Agents Tab */} {activeTab === 'agents' && (
{/* Mobile: Horizontal Agent Tabs */}
setSelectedAgent('claude')} isMobile={true} /> setSelectedAgent('cursor')} isMobile={true} /> setSelectedAgent('codex')} isMobile={true} />
{/* Desktop: Sidebar - Agent List */}
setSelectedAgent('claude')} /> setSelectedAgent('cursor')} /> setSelectedAgent('codex')} />
{/* Main Panel */}
{/* Category Tabs */}
{/* Category Content */}
{/* Account Category */} {selectedCategory === 'account' && ( )} {/* Permissions Category */} {selectedCategory === 'permissions' && selectedAgent === 'claude' && ( )} {selectedCategory === 'permissions' && selectedAgent === 'cursor' && ( )} {selectedCategory === 'permissions' && selectedAgent === 'codex' && ( )} {/* MCP Servers Category */} {selectedCategory === 'mcp' && selectedAgent === 'claude' && ( openMcpForm()} onEdit={(server) => openMcpForm(server)} onDelete={(serverId, scope) => handleMcpDelete(serverId, scope)} onTest={(serverId, scope) => handleMcpTest(serverId, scope)} onDiscoverTools={(serverId, scope) => handleMcpToolsDiscovery(serverId, scope)} testResults={mcpTestResults} serverTools={mcpServerTools} toolsLoading={mcpToolsLoading} /> )} {selectedCategory === 'mcp' && selectedAgent === 'cursor' && ( {/* TODO: Add cursor MCP form */}} onEdit={(server) => {/* TODO: Edit cursor MCP form */}} onDelete={(serverId) => {/* TODO: Delete cursor MCP */}} /> )} {selectedCategory === 'mcp' && selectedAgent === 'codex' && ( openCodexMcpForm()} onEdit={(server) => openCodexMcpForm(server)} onDelete={(serverId) => handleCodexMcpDelete(serverId)} /> )}
)} {/* MCP Server Form Modal */} {showMcpForm && (

{editingMcpServer ? 'Edit MCP Server' : 'Add MCP Server'}

{!editingMcpServer && (
)} {/* Show current scope when editing */} {mcpFormData.importMode === 'form' && editingMcpServer && (
{mcpFormData.scope === 'user' ? : } {mcpFormData.scope === 'user' ? 'User (Global)' : 'Project (Local)'} {mcpFormData.scope === 'local' && mcpFormData.projectPath && ( - {mcpFormData.projectPath} )}

Scope cannot be changed when editing an existing server

)} {/* Scope Selection - Moved to top, disabled when editing */} {mcpFormData.importMode === 'form' && !editingMcpServer && (

{mcpFormData.scope === 'user' ? 'User scope: Available across all projects on your machine' : 'Local scope: Only available in the selected project' }

{/* Project Selection for Local Scope */} {mcpFormData.scope === 'local' && !editingMcpServer && (
{mcpFormData.projectPath && (

Path: {mcpFormData.projectPath}

)}
)}
)} {/* Basic Info */}
{ setMcpFormData(prev => ({...prev, name: e.target.value})); }} placeholder="my-server" required />
{mcpFormData.importMode === 'form' && (
)}
{/* Show raw configuration details when editing */} {editingMcpServer && mcpFormData.raw && mcpFormData.importMode === 'form' && (

Configuration Details (from {editingMcpServer.scope === 'global' ? '~/.claude.json' : 'project config'})

                          {JSON.stringify(mcpFormData.raw, null, 2)}
                        
)} {/* JSON Import Mode */} {mcpFormData.importMode === 'json' && (