This commit implements a three-layer fix to restore the "Sync to other side"
feature when adding or editing MCP servers across Claude and Codex.
Root Cause Analysis:
1. Frontend issue: New MCP entries had their `enabled` field deleted
2. Backend issue: Default value was `false` when `enabled` was missing
3. Core issue: MCP entries were never copied to the other app's servers
Changes:
Frontend (McpFormModal.tsx):
- Set `enabled=true` by default for new MCP entries
- Preserve existing `enabled` state when editing
Backend (services/mcp.rs):
- Change default value from `unwrap_or(false)` to `unwrap_or(true)`
- Implement cross-app MCP replication when `sync_other_side=true`
- Clone MCP entry to other app's servers before syncing to live files
Impact:
- "Sync to Codex" checkbox now correctly adds MCP to both Claude and Codex
- "Sync to Claude" checkbox now correctly adds MCP to both Codex and Claude
- Both config.json and live files (~/.claude.json, ~/.codex/config.toml) are updated
- Fixes regression introduced during project restructure
Tested:
- TypeScript type checking passed
- Rust clippy linting passed
- Manual testing: MCP sync works bidirectionally
Rename `AppType` to `AppId` across the entire frontend codebase to better
reflect its purpose as an application identifier rather than a type category.
This aligns frontend naming with backend command parameter conventions.
Changes:
- Rename type `AppType` to `AppId` in src/lib/api/types.ts
- Remove `AppType` export from src/lib/api/index.ts
- Update all component props from `appType` to `appId` (43 files)
- Update all variable names from `appType` to `appId`
- Synchronize documentation (CHANGELOG, refactoring plans)
- Update test files and MSW mocks
BREAKING CHANGE: `AppType` type is no longer exported. Use `AppId` instead.
All component props have been renamed from `appType` to `appId`.
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
Remove size="sm" from MCP panel buttons to use default text-sm instead of text-xs, ensuring visual consistency with buttons throughout the application.
Enhance visual consistency across MCP dialogs with better spacing
and appropriate icons for add/edit actions.
Changes:
- McpFormModal: Use Plus icon for "Add" button, Save icon for "Edit" button
- McpPanel: Balance spacing around server info line (py-4 for info section,
pb-4 for content area) to create equal visual weight between header and
server list
The MCP panel now has consistent vertical rhythm with 1rem spacing above
and below the server count info line.
Remove custom styling from DialogFooter components across the application
to ensure consistent appearance and behavior. All dialogs now follow the
unified layout pattern defined in the base Dialog component.
Changes:
- Remove custom className overrides from DialogFooter in:
- EndpointSpeedTest.tsx
- CodexQuickWizardModal.tsx
- CodexCommonConfigModal.tsx
- ClaudeConfigEditor.tsx
- Fix McpWizardModal content area padding (remove -mx-6 negative margin)
- Fix McpPanel to use DialogFooter component instead of custom div
All dialogs now consistently use:
- DialogHeader: px-6 pt-6 pb-4 with border and background (built-in)
- Content area: flex-1 overflow-y-auto px-6 py-4
- DialogFooter: px-6 pb-6 pt-4 with border and background (built-in)
This ensures proper spacing, alignment, and visual consistency across
all modal dialogs in the application.
Before optimization:
- McpPanel.tsx: 298 lines (component + business logic)
After optimization:
- McpPanel.tsx: 234 lines (-21%, UI focused)
- useMcpActions.ts: 137 lines (business logic)
Benefits:
✅ Separation of concerns: UI vs business logic
✅ Reusability: MCP operations can be used in other components
✅ Testability: business logic can be tested independently
✅ Consistency: follows same pattern as useProviderActions
✅ Optimistic updates: toggle enabled status with rollback on error
✅ Unified error handling: all MCP errors use toast notifications
Technical details:
- Extract reload, toggleEnabled, saveServer, deleteServer
- Implement optimistic UI updates for toggle
- Centralize error handling and toast messages
- Remove duplicate error handling code from component
- Remove unused useDarkMode hook (now using shadcn theme-provider)
- Clean up MCP components (remove redundant code)
- Add restart API to settings
- Minor type improvements
Extract all MCP form validation logic into a reusable custom hook to
improve code organization and enable reuse across components.
Changes:
- Create useMcpValidation hook with 4 validation functions:
* validateJson: basic JSON structure validation
* formatTomlError: unified TOML error formatting with i18n
* validateTomlConfig: complete TOML validation with required fields
* validateJsonConfig: complete JSON validation with structure checks
- Update McpFormModal to use the hook instead of inline validation
- Simplify validation calls throughout the component
- Reduce code duplication while maintaining all functionality
Benefits:
- Validation logic can be reused in other MCP-related components
- Easier to test validation in isolation
- Better separation of concerns
- McpFormModal remains at 699 lines (original: 767), kept cohesive
The component stays as one piece since its 700 lines represent a
single, cohesive form feature rather than multiple unrelated concerns.
Convert all MCP-related modal windows to use the unified shadcn/ui Dialog
component for consistency with the rest of the application.
Changes:
- McpPanel: Replace custom modal with Dialog component
- Update props from onClose to open/onOpenChange pattern
- Use DialogContent, DialogHeader, DialogTitle components
- Remove custom backdrop and close button (handled by Dialog)
- McpFormModal: Migrate form modal to Dialog
- Wrap entire form in Dialog component structure
- Use DialogFooter for action buttons
- Apply variant="mcp" to maintain green button styling
- Remove unused X icon import
- McpWizardModal: Convert wizard to Dialog
- Replace custom modal structure with Dialog components
- Use Button component with variant="mcp" for consistency
- Remove unused isLinux and X icon imports
- App.tsx: Update McpPanel usage
- Remove conditional rendering wrapper
- Pass open and onOpenChange props directly
- dialog.tsx: Fix dialog overlay and content styling
- Change overlay from bg-background/80 to bg-black/50 for consistency
- Change content from bg-background to explicit bg-white dark:bg-gray-900
- Ensures opaque backgrounds matching MCP panel style
Benefits:
- Unified dialog behavior across the application
- Consistent styling and animations
- Better accessibility with Radix UI primitives
- Reduced code duplication
- Maintains MCP-specific green color scheme
All dialogs now share the same base styling while preserving their unique
content and functionality.
Restore the vibrant color palette from the pre-refactoring version while
maintaining shadcn/ui component architecture and modern design patterns.
## Color Scheme Restoration
### Button Component
- **default variant**: Blue primary (`bg-blue-500`) - matches old `primary`
- **destructive variant**: Red (`bg-red-500`) - matches old `danger`
- **secondary variant**: Gray text (`text-gray-500`) - matches old `secondary`
- **ghost variant**: Transparent hover (`hover:bg-gray-100`) - matches old `ghost`
- **mcp variant**: Emerald green (`bg-emerald-500`) - matches old `mcp`
- Updated border-radius to `rounded-lg` for consistency
### CSS Variables
- Set `--primary` to blue (`hsl(217 91% 60%)` ≈ `bg-blue-500`)
- Added complete shadcn/ui theme variables for light/dark modes
- Maintained semantic color tokens for maintainability
### Component-Specific Colors
- **"Currently Using" badge**: Green (`bg-green-500/10 text-green-500`)
- **Delete button hover**: Red (`hover:text-red-500 hover:bg-red-100`)
- **MCP button**: Emerald green with minimum width (`min-w-[80px]`)
- **Links/URLs**: Blue (`text-blue-500`)
## Benefits
- ✅ Restored original vibrant UI (blue, green, red accents)
- ✅ Maintained shadcn/ui component system (accessibility, animations)
- ✅ Easy global theming via CSS variables
- ✅ Consistent design language across all components
- ✅ Code formatted with Prettier (shadcn/ui standards)
## Files Changed
- `src/index.css`: Added shadcn/ui theme variables with blue primary
- `src/components/ui/button.tsx`: Restored all original button color variants
- `src/components/providers/ProviderCard.tsx`: Green badge for current provider
- `src/components/providers/ProviderActions.tsx`: Red hover for delete button
- `src/components/mcp/McpPanel.tsx`: Use `mcp` variant for consistency
- `src/App.tsx`: MCP button with emerald color and wider width
The UI now matches the original colorful design while leveraging modern
shadcn/ui components for better maintainability and user experience.
- Move sync checkbox to footer alongside save/cancel buttons for better visibility
- Add real-time conflict detection to check for duplicate MCP IDs on other side
- Display amber warning icon when sync would overwrite existing config
- Add i18n keys for overwrite warning (zh: "将覆盖 {{target}} 中的同名配置")
- Update UI layout to use justify-between for better spacing
- Add syncOtherSide parameter to upsert_mcp_server_in_config command
- Implement checkbox UI in McpFormModal for cross-app sync
- Automatically sync enabled MCP servers to both Claude and Codex when option is checked
- Add i18n support for sync option labels and hints
Allow MCP wizard to load and edit existing server configuration:
- Parse current config (JSON/TOML) and pass to wizard as initial data
- Auto-detect server type (stdio/http) and populate form fields
- Convert objects (env/headers) to multi-line text format for editing
- Improves UX by avoiding manual re-entry of existing settings
- Derive Default trait instead of manual implementation for McpRoot and ProviderManager
- Remove redundant closures in codex_config.rs and config.rs
- Simplify match statements to if let patterns in migration.rs and lib.rs
- Remove unnecessary type conversions and borrows in lib.rs
- Fix i18n key inconsistency: sequentialThinking → sequential-thinking
- Format TypeScript files to match Prettier style
All clippy warnings resolved, code passes all quality checks.
- Add i18n keys for all 5 MCP preset descriptions (fetch, time, memory, sequential-thinking, context7)
- Refactor mcpPresets.ts to use i18n keys instead of hardcoded Chinese descriptions
- Add getMcpPresetWithDescription() helper function for dynamic translation
- Update McpFormModal to use translated descriptions when applying presets
- Fix inconsistent key naming: sequentialThinking -> sequential-thinking
This ensures MCP preset descriptions automatically switch language based on user's locale setting.
- Add enabledCount calculation using useMemo for performance
- Display format: "X MCP server(s) configured · Y enabled"
- Add i18n keys: mcp.enabledCount for zh and en
- Real-time updates when toggling server enabled status
- Separate MCP server metadata from connection spec for cleaner architecture
- Add comprehensive server entry fields: name, description, tags, homepage, docs
- Remove legacy format compatibility logic from extract_server_spec
- Implement data validation and filtering in get_servers_snapshot_for
- Add strict id consistency check in upsert_in_config_for
- Enhance import logic with defensive programming for corrupted data
- Simplify frontend by removing normalization logic (moved to backend)
- Improve error messages with contextual information
- Add comprehensive i18n support for new metadata fields
- chore(deps): switch TOML dependency from @iarna/toml to smol-toml and update lockfile
- feat(mcp): add TOML editing/validation for Codex while keeping JSON for Claude; support auto ID extraction from TOML and JSON->TOML conversion for wizard output; add pre-submit required checks (stdio.command / http.url)
- refactor(mcp): unify JSON/TOML validation errors via i18n; add formatTomlError for consistent, localized messages; consolidate state into formConfig/configError
- feat(i18n): add TOML labels/placeholders and error keys (tomlConfig, tomlPlaceholder, tomlInvalid)
- feat(utils): introduce tomlUtils with parse/stringify/validate/convert helpers using smol-toml; provide tomlToMcpServer, mcpServerToToml, extractIdFromToml, validateToml
- build: confirm Vite no longer externalizes Node builtins during build; renderer builds without 'Module 'stream' has been externalized' warning
- Add MCP title field to wizard (required)
- Remove working directory (cwd) field and related logic
- Update wizard callback to return both title and JSON
- Optimize placeholder text for better user guidance:
- Command: "npx or uvx"
- Args: "arg1\narg2"
- Env: "KEY1=value1\nKEY2=value2"
- Headers: "Authorization: Bearer your_token_here\n..."
- Simplify args label by removing "(one per line)" hint
- Update parent component to handle title from wizard
- Add min-h-[400px] to form fields container
- Ensures consistent height between stdio and http modes
- Eliminates visual jump when toggling between types
- Hide 'Use Configuration Wizard' button when preset MCP is selected
- Show wizard button only in custom mode or edit mode
- Prevents user confusion by avoiding wizard access when preset configs are loaded
- Update MCP preset selector to match provider form layout
- Change button size from xs to sm (px-4 py-2) for consistency
- Use emerald-500/600 for selected state (MCP brand color)
- Add preset description text below buttons
- Fix cancel button hover effect in footer (gray-200/gray-700)
- Fix missing space in zh i18n: "应用到 Claude Code 插件"
- Unify McpFormModal width to max-w-3xl (consistent with provider form)
- Add scrollable container with max-h-[90vh] to McpFormModal
- Add footer section to both McpFormModal and McpPanel
- Add "Done" button with emerald theme and check icon to McpPanel footer
- Add i18n keys for "common.done" (zh: "完成", en: "Done")
- Apply consistent footer styling across all modals
- Add fixed height (h-16) to MCP list items for visual consistency
- Implement optimistic UI updates in handleToggle to prevent flicker
- Remove reload() call that caused loading state transitions
- Add automatic rollback on API failure with error notifications
- Move MCP presets into Add modal, no auto-seeding into list
- Replace env-required presets with context7 (no env needed)
- Remove requiresEnv checks/prompts from list and form
- Keep Docs button; maintain clean list UI
- Support both TOML schemas: [mcp.servers.<id>] and [mcp_servers.<id>]
- Non-destructive merge of imported servers (enabled=true only)
- Preserve existing TOML schema when syncing (prefer mcp_servers)
- Remove both mcp and mcp_servers when no enabled items
feat(ui): auto-import Codex MCP on panel init (app=codex)
chore(tauri): add import_mcp_from_codex command and register
chore(types): expose window.api.importMcpFromCodex and typings
fix(ui): remove unused variable for typecheck
- Make MCP panel app-aware; pass appType from App and call APIs with current app
- Show active app in title: “MCP Management · Claude Code/Codex”
- Add sync_enabled_to_codex: project enabled servers from SSOT to ~/.codex/config.toml as [mcp.servers.*]
- Sync on enable/disable, delete, and provider switch (post live write)
- Add Tauri command sync_enabled_mcp_to_codex and expose window.api.syncEnabledMcpToCodex()
- Fix Rust borrow scopes in switch_provider to avoid E0502
- Add TS declarations for new Codex sync API
- Add MCP-specific green button style (buttonStyles.mcp)
- Unify MCP panel and form buttons with emerald theme
- Adjust MCP entry button width to match AppSwitcher (px-3)
- Reduce JSON editor height from h-64 to h-48
- Update translations: "Add/Edit Server" → "Add/Edit MCP"
- Change form label to "MCP Title (Unique)" for clarity
- Move config wizard button to right side of JSON label
- Fix McpListItem enabled state check (explicit true check)
- Display red hint next to title when ID exists
- Disable Add/Save button and prevent submit on duplicate
- Accept existing IDs via prop for real-time validation
- Remove overwrite confirmation dialog on add
- i18n: add duplicate-ID error strings and remove unused overwrite prompt
- files:
- src/components/mcp/McpFormModal.tsx
- src/components/mcp/McpPanel.tsx
- src/i18n/locales/en.json
- src/i18n/locales/zh.json
- Initialize formDescription from initialData.description when editing
- Save formDescription to server object before submitting
- Display only description in list items, hide technical details
- Show empty space when description is not available
- Reduce max width from 4xl (896px) to 3xl (768px)
- Adjust max height from 90vh to 85vh with min-height 600px
- Remove config path display, keep only server count
- Remove McpStatus state, calculate count directly from servers
- Simplify reload function by removing redundant status updates
- Backend: reject "sse" in validators; accept missing type as stdio; require url only for http (mcp.rs, claude_mcp.rs)
- Frontend: McpServer.type narrowed to "stdio" | "http" (optional) (src/types.ts)
- UI: avoid undefined in list item details (McpListItem)
- Claude-only sync after delete to update ~/.claude.json (commands.rs)
Notes:
- Ran typecheck and cargo check: both pass
- Clippy shows advisory warnings unrelated to this change
- Prettier check warns on a few files; limited scope changes kept minimal
- Add McpConfig to MultiAppConfig and persist MCP servers in ~/.cc-switch/config.json
- Add Tauri commands: get_mcp_config, upsert_mcp_server_in_config, delete_mcp_server_in_config, set_mcp_enabled, sync_enabled_mcp_to_claude, import_mcp_from_claude
- Only write enabled MCPs to ~/.claude.json (mcpServers) and strip UI-only fields (enabled/source)
- Frontend: update API wrappers and MCP panel to read/write via config.json; seed presets on first open; import from ~/.claude.json
- Fix warnings (remove unused mut, dead code)
- Verified with cargo check and pnpm typecheck
- Show not-installed MCP presets directly in the list, consistent with existing UI (no modal)
- Toggle now supports enabling presets by writing to ~/.claude.json (mcpServers) and refreshing list
- Keep installed MCP entries unchanged (edit/delete/toggle)
fix(mcp): robust error handling and pre-submit validation
- Use extractErrorMessage in MCP panel and form to surface backend details
- Prevent pasting full config (with mcpServers) into single-server JSON field
- Add required-field checks: stdio requires non-empty command; http requires non-empty url
i18n: add messages for single-server validation and preset labels
chore: add data-only MCP presets file (no new dependencies)
- Increase notification z-index to z-[80] to prevent overlay issues
- Make MCP save operation async with proper error propagation
- Display specific backend error messages in form validation
- Ensure errors are visible in both form and panel layers
- Replace deprecated 'sse' type with 'http' (as per Claude Code official docs)
- Add HTTP-specific fields: url (required) and headers (optional)
- Implement dynamic UI: show different fields based on selected type
- Improve args input: support multi-line input (one argument per line)
- Add headers parsing supporting both 'KEY: VALUE' and 'KEY=VALUE' formats
- Update backend validation to enforce type-specific required fields
- Update i18n translations (zh/en) with new HTTP-related labels
- Simplify McpFormModal to 3 inputs: title (required), description (optional), and JSON config (optional)
- Add JSON validation similar to ProviderForm (must be object, real-time error display)
- Create McpWizardModal component for quick configuration:
- 5 input fields: type (stdio/sse), command (required), args, cwd, env
- Real-time JSON preview
- Emerald theme color (consistent with MCP button)
- Z-index 70 (above McpFormModal's 60)
- Add "or use configuration wizard" link next to JSON config label
- Update i18n translations (zh/en) for form and wizard
- All changes pass TypeScript typecheck and Prettier formatting
- Redesign MCP panel to match main interface style
- Add toggle switch for each MCP server to enable/disable
- Use emerald theme color consistent with MCP button
- Create card-based layout with one MCP per row
- Add dedicated form modal for add/edit operations
- Implement proper empty state with friendly prompts
- Add comprehensive i18n support (zh/en)
- Extend McpServer type to support enabled field
- Backend already supports enabled field via serde_json::Value
Components:
- McpPanel: Main panel container with header and list
- McpListItem: Card-based list item with toggle and actions
- McpFormModal: Independent modal for add/edit forms
- McpToggle: Emerald-themed toggle switch component
All changes passed TypeScript type checking and production build.
- Read/write ~/.claude.json (preserve unknown fields) for mcpServers
- Remove settings.local.json and mcp.json handling
- Drop enableAllProjectMcpServers command and UI toggle
- Update types, Tauri APIs, and MCP panel to reflect new status fields
- Keep atomic write and command validation behaviors
- Add McpPanel with enable toggle, server list and add/edit form
- Quick template: mcp-fetch (uvx mcp-server-fetch)
- Command validation UI and open ~/.claude shortcut
- Wire MCP button in App to open panel