Tool Reviews

Stop Copy-Pasting MCP Configs: How AgentSync Keeps All Your AI Coding Agents in Sync

What is MCP config sync?

MCP config sync is the practice of keeping Model Context Protocol server configurations consistent across multiple AI coding agents (Claude Code, Cursor, Codex, Gemini) that each store configs in different formats and locations. Without sync, adding or changing an MCP server in one agent requires manually replicating the change to all others.

TL;DR

  • -agentsync treats Claude Code as the single source of truth and syncs MCP configs to Cursor, Codex, and Gemini in one command
  • -Each agent stores configs in a different format: JSON (Claude/Cursor), TOML (Codex), MDC (Cursor rules) — agentsync converts automatically
  • -Case-sensitivity bugs like 'Notion' vs 'notion' cause silent failures across agents; agentsync deduplicates on sync
  • -Install via pip/pipx/uvx; requires Python 3.9+, only 3 dependencies: Click, PyYAML, Rich
  • -Three commands: agentsync init → agentsync sync → agentsync validate

If you work with more than one AI coding agent, you already know the friction. You add a new MCP server to Claude Code, then you have to replicate that change to Cursor, then Codex, then Gemini. Each stores its config in a different format, location, and convention. Miss one and the agent silently fails to find the server. Multiply that by every MCP server in your stack and you have a maintenance problem that scales linearly with the number of tools you use.

agentsync is a CLI tool that solves this. It reads your Claude Code configuration as the single source of truth and pushes it to every other agent with one command. JSON to TOML conversion, Markdown to MDC transformation, case-insensitive deduplication — all handled automatically.

Below: the problem, the fix, and how it works internally.

The Problem: Config Drift Across AI Agents

The Model Context Protocol (MCP) is becoming the standard way AI coding agents talk to external tools — databases, APIs, documentation services, monitoring systems. A typical project might have 10-20 MCP servers configured: Supabase, GitHub, Linear, Notion, Sentry, Cloudflare, and so on.

The catch is that every agent has its own opinion about where and how to store this list:

  • Claude Code uses .mcp.json and ~/.claude.json (JSON), plus CLAUDE.md for rules.
  • Cursor uses ~/.cursor/mcp.json (JSON) and .cursor/rules/project.mdc (MDC with YAML frontmatter).
  • Codex uses ~/.codex/config.toml (TOML) and AGENTS.md (Markdown).
  • Antigravity/Gemini uses its own JSON format and only supports stdio-based servers.

When you add a server to one agent, you need to manually replicate the change to three others. When you remove one, same story. When you rename one, or change an environment variable, you touch four files in three different formats. Forget one and you get a silent failure — the agent just doesn’t see the server.

This isn’t a theoretical concern. If you have Notion defined in your global Claude config and notion (lowercase) in your project .mcp.json, you now have a case-sensitivity bug that causes duplicate entries in some agents and missing entries in others. These are the kinds of problems that waste 20 minutes of debugging when you could be shipping code.

The Solution: One Source of Truth, One Command

agentsync treats your Claude Code configuration as the canonical source and generates configs for every other agent:

pip install agentsync-cli
agentsync init       # creates agentsync.yaml
agentsync sync       # syncs to all agents
agentsync validate   # checks everything matches

Here’s the data flow:

+--------------+
|  Claude Code |  Source of Truth
|  .claude.json|  --- MCP Servers
|  .mcp.json   |  --- Rules (CLAUDE.md)
|  CLAUDE.md   |
+------+-------+
       |  agentsync sync
       +------------------+-----------------+
       v                  v                 v
+--------------+  +--------------+  +--------------+
|    Cursor    |  |    Codex     |  |  Antigravity |
|  mcp.json   |  | config.toml  |  |mcp_config.json|
| project.mdc |  |  AGENTS.md   |  |              |
+--------------+  +--------------+  +--------------+

We picked Claude Code as the source because it has the most complete configuration model — three-tier MCP server merging (global, project-specific, local override) plus Markdown-based rules. Everything else can be derived from it.

Quick Start

Install

Pick your preferred Python package manager:

pip install agentsync-cli       # pip
pipx install agentsync-cli      # pipx (recommended for CLI tools)
uvx agentsync-cli               # uv (run without installing)

Requires Python 3.9 or newer. The only runtime dependencies are Click, PyYAML, and Rich — no heavy frameworks.

Initialize

Run agentsync init in your project root. This creates an agentsync.yaml file with sensible defaults:

version: 1

source:
  type: claude
  global_config: ~/.claude.json
  project_mcp: .mcp.json
  rules_file: CLAUDE.md

targets:
  cursor:
    type: cursor
    mcp_path: ~/.cursor/mcp.json
    rules_path: .cursor/rules/project.mdc
    rules_format: mdc
    exclude_servers: []

  codex:
    type: codex
    config_path: ~/.codex/config.toml
    rules_path: AGENTS.md
    rules_format: md
    exclude_servers:
      - codex       # Codex can't call itself

  antigravity:
    type: antigravity
    mcp_path: ~/.gemini/antigravity/mcp_config.json
    protocols:
      - stdio       # Only stdio servers (no HTTP)
    exclude_servers: []

rules:
  exclude_sections:
    - "MCP Servers"
    - "Context Management & Agents"

sync:
  backup: true
  backup_dir: .agentsync/backups
  log_dir: .agentsync/logs

Edit this to match your setup. The key decisions are:

  • Which servers to exclude per target. Codex shouldn’t have its own MCP server in its config (it can’t call itself). Some agents may not support HTTP-based servers.
  • Which rules sections to exclude. Sections like “MCP Servers” or “Context Management & Agents” in your CLAUDE.md are Claude-specific and shouldn’t leak into other agents’ rules files.
  • Protocol filtering. Antigravity/Gemini only supports stdio servers, so HTTP servers are filtered out automatically when you set protocols: [stdio].

Sync

agentsync sync                  # Full sync (MCP + rules)
agentsync sync --dry-run        # Preview changes without writing
agentsync sync --mcp-only       # Only MCP server configs
agentsync sync --rules-only     # Only rules files
agentsync sync -t cursor        # Sync specific target only
agentsync sync --no-backup      # Skip creating backup files

Use --dry-run the first time — it shows exactly what would change without writing anything.

Validate

After syncing, run validation to confirm everything is consistent:

agentsync validate              # Full validation
agentsync validate -v           # Verbose (show passed checks too)
agentsync validate -t codex     # Validate specific target only

Validation checks three things: server consistency (are all expected servers present in the target?), excluded section leaks (did any Claude-specific content slip through?), and case-insensitive duplicates (is Notion colliding with notion?).

Status

agentsync status

This shows your source config health, the number of loaded servers, and whether each target is in sync or has drifted.

How the Sync Pipeline Works

When you run agentsync sync, here’s what happens step by step:

agentsync sync
  |
  +-- Load config (agentsync.yaml)
  +-- Read source (Claude Code)
  |   +-- ~/.claude.json         -> global MCP servers
  |   +-- ~/.claude.json         -> project-specific MCP servers
  |   +-- .mcp.json              -> local project override
  |   +-- CLAUDE.md              -> rules sections
  |
  +-- Deduplicate (case-insensitive)
  +-- Filter (exclude_servers, exclude_sections, protocols)
  |
  +-- Generate + Write per target
      +-- Cursor:       mcp.json + project.mdc (MDC frontmatter)
      +-- Codex:        config.toml (marker-based) + AGENTS.md
      +-- Antigravity:  mcp_config.json (stdio-only)

Three-tier MCP merge. Claude Code layers its config: global servers from ~/.claude.json form the base, project-specific servers override those, and your local .mcp.json wins over both. agentsync follows the same precedence.

Case-insensitive deduplication. After merging, the engine normalizes all server names to lowercase and deduplicates. If your global config has Notion and your local .mcp.json has notion, the local one wins. The tool logs these merges so you can audit what happened.

Per-target filtering. Each target can exclude specific servers (via exclude_servers) and filter by protocol (via protocols). For instance, Codex excludes itself, and Antigravity only gets stdio-based servers.

Format conversion. Cursor gets JSON. Codex gets TOML with marker-based insertion — existing content in config.toml is preserved, and agentsync only manages the block between # === AGENTSYNC START === and # === AGENTSYNC END === markers. Antigravity gets a simple JSON file. Rules go through Markdown section parsing and filtering, with optional MDC frontmatter generation for Cursor.

Automatic backups. Before every write, agentsync creates a timestamped backup of the existing file in .agentsync/backups/. This can be disabled with --no-backup or sync.backup: false in the config.

Architecture: Adapters All the Way Down

agentsync is built on an adapter pattern. There’s a clean separation between the sync engine (which handles orchestration, deduplication, and filtering) and the adapters (which handle format-specific reading and writing).

src/agentsync/
+-- adapters/           # Source and target adapters
|   +-- base.py         # Abstract base classes (SourceAdapter, TargetAdapter)
|   +-- claude.py       # Claude Code source adapter
|   +-- cursor.py       # Cursor target adapter
|   +-- codex.py        # Codex target adapter
|   +-- antigravity.py  # Antigravity/Gemini target adapter
+-- utils/              # Shared utilities
|   +-- backup.py       # File backup before writes
|   +-- dedup.py        # Case-insensitive deduplication
|   +-- diff.py         # Server diff display
|   +-- io.py           # File writing with WriteResult
|   +-- markdown.py     # Markdown section parsing and filtering
|   +-- output.py       # Rich CLI output (tables, summaries)
+-- cli.py              # Click CLI commands (sync, validate, init, status)
+-- config.py           # YAML config loading and validation
+-- sync.py             # Sync orchestrator
+-- validate.py         # Validation orchestrator

The two base classes define the contract:

class SourceAdapter(ABC):
    @abstractmethod
    def load_servers(self) -> dict[str, ServerConfig]: ...
    @abstractmethod
    def load_rules(self) -> list[Section]: ...

class TargetAdapter(ABC):
    @abstractmethod
    def generate_mcp(self, servers: dict[str, ServerConfig]) -> str | dict: ...
    @abstractmethod
    def generate_rules(self, sections: list[Section]) -> str: ...
    @abstractmethod
    def write(self, dry_run: bool = False) -> list[WriteResult]: ...
    @abstractmethod
    def validate(self) -> list[ValidationResult]: ...

Adding a new agent means implementing TargetAdapter (or SourceAdapter if you want a different source of truth). The sync engine doesn’t care about format specifics — it works with ServerConfig and Section objects, and each adapter handles serialization.

Adding a New Agent Adapter

Want to add support for Windsurf, Zed, Cline, or any other agent? Here’s how:

  1. Create src/agentsync/adapters/youragent.py and implement TargetAdapter.
  2. Register it in cli.py (the create_targets function).
  3. Add the type string to KNOWN_TARGET_TYPES in config.py.
  4. Write tests in tests/test_adapter_youragent.py.
  5. Update the README.

The Antigravity adapter is the simplest reference implementation at around 100 lines. It only handles MCP server config (no rules), generates JSON, and filters by protocol. A good starting point if you’re adding a JSON-based agent.

The Codex adapter is more interesting if your target uses TOML or needs marker-based insertion into existing files.

Testing and CI/CD

192 tests, 97% coverage. Tests use pytest with tmp_path fixtures — they never touch real user files. Every adapter and utility has its own test module.

CI runs on GitHub Actions with a matrix across Python 3.9 through 3.13 on both Ubuntu and macOS. The pipeline runs ruff (lint + format), mypy (type checking), and the full test suite with coverage reporting.

Releases use PyPI trusted publisher flow — push a version tag, and GitHub Actions builds the package and publishes it to PyPI without any stored API tokens. Dependabot keeps deps current.

# Run tests locally
pytest                      # All tests
pytest -v                   # Verbose
pytest --cov=agentsync      # With coverage report

Exit Codes

agentsync follows Unix conventions for exit codes, so you can plug them into scripts and CI pipelines:

CodeMeaning
0Success
1Runtime error (sync failed, validation failed)
2Configuration error (missing config, bad YAML, unknown adapter)

Contributing

agentsync is MIT-licensed and open source. New agent adapters are the easiest way to contribute.

Setup takes about a minute:

git clone https://github.com/spyrae/agentsync.git
cd agentsync
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest

ruff (lint + format), mypy (type checking), and the test suite enforce code quality. All three must pass before merging.

If you have an agent you want supported, you can either open a feature request or submit a PR with the adapter yourself. The adapter interface is intentionally small — four methods for a target adapter — so most implementations are under 150 lines.


agentsync is built by Spyrae and released under the MIT license. If you use multiple AI coding agents and you’re tired of keeping their MCP configs in sync manually, give it a try. Bug reports, feature requests, and adapter PRs are all welcome.

FAQ

If I update a server’s environment variables directly in Cursor’s mcp.json without going through Claude Code, will the next agentsync sync overwrite those changes?

Yes — sync always overwrites target files with whatever is in the Claude Code source. Any manual edits to Cursor, Codex, or Antigravity configs are treated as ephemeral. The correct workflow is to make all changes in Claude Code’s ~/.claude.json or .mcp.json, then sync. If you need agent-specific env overrides that shouldn’t propagate (e.g., a different API endpoint in Cursor), use exclude_servers to keep that server out of sync and manage it manually.

The Codex adapter uses marker-based insertion to preserve existing config.toml content. What happens if someone accidentally deletes the AGENTSYNC START/END markers?

On the next sync, agentsync cannot find the markers and appends a new managed block at the end of the file. This results in duplicate MCP server entries — the old ones without markers and the new ones inside the regenerated block. Run agentsync validate -t codex to detect this: it will report a consistency error because the servers listed in the source don’t match what it expects to find between the markers. The fix is to manually remove the duplicate entries and let agentsync sync regenerate the marked block.

Is there a risk that syncing CLAUDE.md rules to AGENTS.md could expose Claude-specific instructions that break Codex’s behavior?

Yes, which is why the rules.exclude_sections configuration exists. Sections like “MCP Servers”, “Context Management & Agents”, and any Claude Code-specific workflow instructions should be listed there. A common mistake is forgetting to exclude sections that reference Claude-specific slash commands or memory management behaviors — Codex will receive those instructions and either ignore them or behave unexpectedly. Run agentsync validate -v after the first sync to check for excluded section leaks.