Enterprise·
advanced
·15 min read·Sep 12, 2026
By Rad Tome·Lead AI Systems Architect

Standardizing Cross-Client MCP Configurations

Eliminating configuration drift across Cursor, Windsurf, Claude Desktop, VS Code, and OpenAI Codex CLI with unified Single-Source-of-Truth (SSOT) schema synchronization.

configurationcursorwindsurfclaude-desktopcodex-clidevops
Interactive Tool
1-Click Export

Generate & Validate Multi-Client MCP Config

One-click export with environment variables & path locators for Claude Desktop, Cursor, Windsurf, and OpenAI Codex CLI.

Open in Generator

Standardizing Cross-Client MCP Configurations

As Model Context Protocol (MCP) adoption spreads across software engineering organizations, developers increasingly use diverse AI host environments: Cursor for rapid full-stack editing, Windsurf for multi-file cascading flows, Claude Desktop for high-level architectural planning, OpenAI Codex CLI for automated shell scripts, and VS Code for enterprise-governed workflows.

However, each client mandates its own configuration file format, file system path, and schema structure. While Claude Desktop and Cursor use JSON-based mcpServers keys, OpenAI Codex CLI uses TOML tables ([mcp_servers]), and Windsurf uses custom config directories.

This fragmentation causes severe configuration drift: team members run mismatched tool versions, leak plaintext API keys in ad-hoc configs, and struggle to replicate identical agent capabilities across different tools.

This guide provides a production blueprint for managing a Single Source of Truth (SSOT) configuration in YAML, with an automated transpiler and drift-detection CLI.


1. The Cross-Client Configuration Landscape

Each major MCP client runtime stores configuration in distinct OS locations and schema layouts:

mermaid
graph TD
    SSOT[Unified SSOT: .mcp-registry.yaml] --> CLI[mcp-sync Transpiler CLI]
    
    CLI -->|Generate JSON| Cursor[.cursor/mcp.json]
    CLI -->|Generate JSON| Claude[claude_desktop_config.json]
    CLI -->|Generate JSON| Windsurf[~/.codeium/windsurf/mcp_config.json]
    CLI -->|Generate TOML| Codex[~/.codex/config.toml]
    CLI -->|Generate JSON| VSCode[.vscode/mcp.json]

Path and Format Matrix

Host ClientOperating System PathsFormatTop-Level Key
Claude DesktopmacOS: ~/Library/Application Support/Claude/claude_desktop_config.json<br>Windows: %APPDATA%\Claude\claude_desktop_config.jsonJSONmcpServers
Cursor IDEWorkspace: .cursor/mcp.json<br>Global: ~/.cursor/mcp.jsonJSONmcpServers
Windsurf~/.codeium/windsurf/mcp_config.jsonJSONmcpServers
OpenAI Codex CLI~/.codex/config.toml or ./config.tomlTOML[mcp_servers]
VS Code (MCP).vscode/mcp.jsonJSONservers

2. Defining the Single Source of Truth (.mcp-registry.yaml)

Define all enterprise MCP servers in a central, declarative YAML manifest placed in your repository root or provisioned globally via dotfiles:

yaml
version: "1.0"
registry_name: "enterprise-engineering-mcp"

defaults:
  env:
    NODE_ENV: "production"
    LOG_LEVEL: "info"

servers:
  filesystem:
    description: "Sandboxed workspace filesystem reader"
    transport: "stdio"
    command: "npx"
    args:
      - "-y"
      - "@modelcontextprotocol/server-filesystem"
      - "${WORKSPACE_ROOT}"
    env:
      FILE_ACCESS_TIER: "restricted"
    clients: ["cursor", "windsurf", "claude_desktop", "vscode"]

  enterprise-db:
    description: "Read-only production PostgreSQL analytics pool"
    transport: "sse"
    url: "https://mcp-gateway.internal.enterprise.com/sse"
    headers:
      Authorization: "Bearer ${ENTERPRISE_MCP_TOKEN}"
      X-Developer-Email: "${USER_EMAIL}"
    clients: ["cursor", "windsurf", "codex", "claude_desktop"]

  github-actions:
    description: "CI/CD pipeline workflow auditor"
    transport: "stdio"
    command: "docker"
    args:
      - "run"
      - "-i"
      - "--rm"
      - "-e"
      - "GITHUB_TOKEN"
      - "registry.enterprise.internal/ai/mcp-github:v1.2"
    env:
      GITHUB_TOKEN: "${GITHUB_PERSONAL_ACCESS_TOKEN}"
    clients: ["codex", "cursor"]

3. Automated Transpiler & Drift Detector (Node.js)

Below is an automated transpiler script (scripts/mcp-sync.mjs) that parses the SSOT YAML, interpolates environment variables safely, and generates exact configuration files for each client:

javascript
import fs from 'fs';
import path from 'path';
import os from 'os';
import yaml from 'js-yaml';

const WORKSPACE_DIR = process.cwd();
const REGISTRY_PATH = path.join(WORKSPACE_DIR, '.mcp-registry.yaml');

if (!fs.existsSync(REGISTRY_PATH)) {
  console.error(`Error: Missing ${REGISTRY_PATH}`);
  process.exit(1);
}

const rawConfig = fs.readFileSync(REGISTRY_PATH, 'utf8');
const parsed = yaml.load(rawConfig);

// Helper: Safely substitute environment variables
function interpolateEnv(str) {
  if (typeof str !== 'string') return str;
  return str.replace(/\$\{([^}]+)\}/g, (_, key) => {
    if (key === 'WORKSPACE_ROOT') return WORKSPACE_DIR;
    return process.env[key] || `\${${key}}`;
  });
}

function processObjectEnv(obj) {
  const result = {};
  for (const [k, v] of Object.entries(obj || {})) {
    result[k] = interpolateEnv(v);
  }
  return result;
}

// 1. Generate Standard JSON Client Format (Cursor / Claude / Windsurf)
function generateJsonConfig(targetClient) {
  const mcpServers = {};

  for (const [id, srv] of Object.entries(parsed.servers)) {
    if (!srv.clients.includes(targetClient)) continue;

    if (srv.transport === 'stdio') {
      mcpServers[id] = {
        command: srv.command,
        args: srv.args.map(interpolateEnv),
        env: processObjectEnv({ ...parsed.defaults.env, ...srv.env })
      };
    } else if (srv.transport === 'sse') {
      mcpServers[id] = {
        url: interpolateEnv(srv.url),
        headers: processObjectEnv(srv.headers)
      };
    }
  }

  return JSON.stringify({ mcpServers }, null, 2);
}

// 2. Generate TOML Format (OpenAI Codex CLI)
function generateCodexToml() {
  const lines = ['# Auto-generated by mcp-sync. DO NOT EDIT DIRECTLY.\n'];

  for (const [id, srv] of Object.entries(parsed.servers)) {
    if (!srv.clients.includes('codex')) continue;

    const tomlId = id.replace(/-/g, '_');
    lines.push(`[mcp_servers.${tomlId}]`);

    if (srv.transport === 'stdio') {
      lines.push(`command = "${srv.command}"`);
      const argsStr = srv.args.map(a => `"${interpolateEnv(a)}"`).join(', ');
      lines.push(`args = [${argsStr}]`);

      const mergedEnv = { ...parsed.defaults.env, ...srv.env };
      if (Object.keys(mergedEnv).length > 0) {
        lines.push(`\n[mcp_servers.${tomlId}.env]`);
        for (const [k, v] of Object.entries(mergedEnv)) {
          lines.push(`${k} = "${interpolateEnv(v)}"`);
        }
      }
    } else if (srv.transport === 'sse') {
      lines.push(`url = "${interpolateEnv(srv.url)}"`);
      if (srv.headers?.Authorization) {
        lines.push(`bearer_token = "${interpolateEnv(srv.headers.Authorization).replace('Bearer ', '')}"`);
      }
    }
    lines.push('');
  }

  return lines.join('\n');
}

// 3. Write Targets
const targets = [
  { client: 'cursor', path: path.join(WORKSPACE_DIR, '.cursor', 'mcp.json'), content: generateJsonConfig('cursor') },
  { client: 'windsurf', path: path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'), content: generateJsonConfig('windsurf') },
  { client: 'codex', path: path.join(WORKSPACE_DIR, 'config.toml'), content: generateCodexToml() }
];

// OS-Specific Claude Desktop Path
const claudePath = process.platform === 'darwin'
  ? path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
  : path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');

targets.push({ client: 'claude_desktop', path: claudePath, content: generateJsonConfig('claude_desktop') });

for (const target of targets) {
  try {
    const dir = path.dirname(target.path);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    fs.writeFileSync(target.path, target.content, 'utf8');
    console.log(`✓ Synchronized [${target.client}] config -> ${target.path}`);
  } catch (err) {
    console.warn(`! Skipped [${target.client}]: ${err.message}`);
  }
}

4. Generated Target Artifacts

Output: .cursor/mcp.json

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/workspace/repo"
      ],
      "env": {
        "NODE_ENV": "production",
        "LOG_LEVEL": "info",
        "FILE_ACCESS_TIER": "restricted"
      }
    },
    "enterprise-db": {
      "url": "https://mcp-gateway.internal.enterprise.com/sse",
      "headers": {
        "Authorization": "Bearer eyJhbGci...",
        "X-Developer-Email": "jane@enterprise.com"
      }
    }
  }
}

Output: config.toml (OpenAI Codex CLI)

toml
# Auto-generated by mcp-sync. DO NOT EDIT DIRECTLY.

[mcp_servers.enterprise_db]
url = "https://mcp-gateway.internal.enterprise.com/sse"
bearer_token = "eyJhbGci..."

[mcp_servers.github_actions]
command = "docker"
args = ["run", "-i", "--rm", "-e", "GITHUB_TOKEN", "registry.enterprise.internal/ai/mcp-github:v1.2"]

[mcp_servers.github_actions.env]
NODE_ENV = "production"
LOG_LEVEL = "info"
GITHUB_TOKEN = "ghp_secure_token_991"

5. CI/CD Drift Verification in GitHub Actions

To prevent developers from manually tampering with generated configurations, enforce a pull request lint check:

yaml
name: Verify MCP Configuration Sync

on: [push, pull_request]

jobs:
  verify-sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm install js-yaml
      - name: Run MCP Transpiler
        run: node scripts/mcp-sync.mjs
      - name: Assert No Git Drift
        run: |
          git diff --exit-code .cursor/mcp.json config.toml || (echo "Error: MCP configuration drift detected. Run 'node scripts/mcp-sync.mjs' and commit changes." && exit 1)

Related Configuration & Client Setup Guides

Ready to Deploy?

Build your full agent toolstack in the Visual Generator

Combine Standardizing Cross-Client MCP Configurations with databases, search APIs, and memory graphs in a single configuration file.

Customize in Generator

Standardizing Cross-Client MCP Configurations FAQ

What is the Standardizing Cross-Client MCP Configurations?

Eliminating configuration drift across Cursor, Windsurf, Claude Desktop, VS Code, and OpenAI Codex CLI with unified Single-Source-of-Truth (SSOT) schema synchronization.

How do I configure Standardizing Cross-Client MCP Configurations in Claude Desktop or Cursor?

You can copy the configuration JSON from our guide or launch the interactive MCP Codex Config Generator at https://mcp-codex.com/generator to export valid configs in 1 click.

Can I use Standardizing Cross-Client MCP Configurations with the OpenAI Codex CLI?

Yes, OpenAI Codex CLI supports Model Context Protocol. You can add it directly to ~/.codex/config.toml or pass arguments to codex mcp add.

RT

Written by Rad Tome

Lead AI Systems Architect & Founder, MCP Codex

@RadTome

Specializing in Model Context Protocol (MCP) integrations, autonomous AI agent orchestration, and distributed developer toolchains. Researches and benchmarks production MCP client-server architectures across OpenAI Codex, Claude, and Cursor.

Editorial Integrity: All configurations, schemas, and commands verified against live GitHub repositories and tested in local sandbox runtimes.

Related Guides