Chapter 5 Β· ADVANCED

Unified MCP Management

πŸ“„ 05_unified_mcp_management.md 🏷 Advanced

Chapter 5: Unified MCP Management

Welcome to the fifth chapter of the cc-switch tutorial!

In the previous chapter, SQLite Persistence & Schema, we built a permanent memory for our application. We can now remember settings and logs even after the computer restarts.

Now we are going to use that database to solve a massive headache for AI developers: managing MCP Servers.

The Problem: Configuration Chaos

MCP (Model Context Protocol) is a standard that lets AI models (like Claude or Gemini) talk to your local toolsβ€”like reading files, querying a database, or searching your git history.

But there is a catch. Each AI tool on your computer has its own configuration file.

If you download a new MCP tool (like a "Postgres Viewer"), you have to manually edit three different files to let your AI assistants use it. If you change the password, you have to update it three times. It's tedious and error-prone.

The Solution: The Universal Remote

Unified MCP Management acts like a Universal Remote Control for your AI tools.

Instead of configuring the "Postgres Viewer" separately for every app, you add it to cc-switch once. Then, you simply toggle switches to "sync" that configuration to Claude, Codex, or Gemini.

flowchart TD A[MCP Server: Postgres Viewer] --> B(cc-switch Hub) B -->|Auto-Sync| C[Claude Config] B -->|Auto-Sync| D[VS Code Settings] B -->|Auto-Sync| E[Gemini Config]

Key Concepts

1. The Central Inventory

This is the master list of all MCP servers you have installed. We store this in our SQLite Persistence & Schema. It holds the command to run the server (e.g., node server.js) and its environment variables (e.g., API_KEY).

2. The Syncer

The Syncer is the worker bee. When you make a change in cc-switch, the Syncer immediately:

  1. Locates the configuration file for the target app (e.g., Claude).
  2. Reads the file.
  3. Injects or updates the MCP server details.
  4. Saves the file.

3. The Toggle

This is the user interface. It’s a simple "On/Off" switch for each app.


Usage: The Unified Panel

From the user's perspective, managing servers happens in the UnifiedMcpPanel.

Let's look at how the React frontend handles the complexity. The user sees a list of servers and a row of icons representing apps (Claude, Codex, etc.).

Toggling a Server

When a user clicks an icon to enable a server for an app, we trigger a mutation.

// src/components/mcp/UnifiedMcpPanel.tsx

// This function runs when you click an app icon (like the Claude logo)
const handleToggleApp = async (
  serverId: string,
  app: AppId,
  enabled: boolean,
) => {
  try {
    // 1. Send command to Rust backend
    await toggleAppMutation.mutateAsync({ serverId, app, enabled });
  } catch (error) {
    // 2. Show error if sync fails
    toast.error(t("common.error"), { description: String(error) });
  }
};

Beginner Note: mutateAsync sends a message to the Rust backend saying: "Hey, the user wants Server X enabled for Claude. Please make it happen."

Displaying the List

We use a custom component UnifiedMcpListItem to show the server details and the toggle buttons.

// src/components/mcp/UnifiedMcpPanel.tsx

// Inside the render loop
<UnifiedMcpListItem
  key={id}
  id={id}
  server={server}
  // Connect the toggle action to our handler
  onToggleApp={handleToggleApp}
  onEdit={handleEdit}
  onDelete={handleDelete}
/>

Internal Implementation: The Sync Process

What happens inside the Rust backend when you toggle that switch?

Sequence Diagram

sequenceDiagram participant UI as React Frontend participant Rust as Backend API participant Mod as MCP Module participant File as Claude Config.json UI->>Rust: Toggle "Postgres" for Claude = ON Rust->>Mod: sync_enabled_to_claude() Mod->>File: Read Config File Note over Mod: Parse JSON content Mod->>Mod: Add "Postgres" to "mcpServers" list Mod->>File: Write Config File Rust-->>UI: Success

The Module Structure

We organize our backend logic by "Target App". Each app (Claude, Codex, Gemini) has its own module because their configuration files look different.

// src-tauri/src/mcp/mod.rs

// We create separate modules for each target application
mod claude;   // Logic to edit claude_desktop_config.json
mod codex;    // Logic to edit VS Code settings
mod gemini;   // Logic for Gemini

// We verify the server configuration is valid
mod validation; 

// We expose simple functions to the rest of the app
pub use claude::sync_enabled_to_claude;
pub use codex::sync_enabled_to_codex;

The Sync Logic (Conceptual)

While the actual file parsing code is detailed, the logic follows a simple pattern. Here is a simplified version of what sync_enabled_to_claude does:

// Simplified logic inside src-tauri/src/mcp/claude.rs

pub fn sync_enabled_to_claude(servers: Vec<McpServer>) -> Result<()> {
    // 1. Find where Claude stores its config file
    let config_path = get_claude_config_path();

    // 2. Read the existing JSON
    let mut config_json = read_json_file(&config_path)?;

    // 3. Update the "mcpServers" section
    // We convert our internal server format to Claude's format
    config_json["mcpServers"] = convert_to_claude_format(servers);

    // 4. Save the file back to disk
    write_json_file(&config_path, config_json)?;

    Ok(())
}

Beginner Note: This function is a "Translator". It translates the data from our database format into the specific JSON format that Claude understands, and puts it in the file where Claude expects to find it.

Why This Matters

  1. Safety: You don't have to manually edit JSON files anymore. A misplaced comma in a JSON file can break your AI tool. cc-switch handles the writing programmatically, ensuring the syntax is always correct.
  2. Portability: If you switch from Claude to Gemini, your tools follow you. You just toggle the switch in the UI.
  3. Discovery: By centralizing the list, you can see all your available tools in one place, rather than having them hidden in obscure text files scattered around your hard drive.

Summary

In this chapter, we built the Unified MCP Management system:

  1. We created a Central Inventory of tools in our UI.
  2. We built a Sync Engine in Rust that knows how to talk to different apps (Claude, Codex, Gemini).
  3. We implemented a Toggle System so users can enable tools with a single click.

Now we have a fully functional system! We can route traffic, handle failovers, adapt protocols, persist settings, and manage external tools.

But... how do we know if it's working well? Are we saving money? Which provider is failing the most?

In the final chapter, we will visualize our data.

Next Chapter: Usage Analytics Dashboard


Generated by Code IQ