mcp-sync 1.0.0

Sync canonical mcp.yaml to Antigravity, Claude Code, Codex, OpenCode, and custom targets
Documentation
# mcp-sync

**Sync canonical MCP configuration to multiple AI coding assistants**

`mcp-sync` reads a single `mcp.yaml` file and syncs the server definitions to configuration files for:

- **Antigravity** (VS Code extension)
- **Claude Code** (CLI)
- **Codex** (OpenAI CLI)
- **OpenCode**
- **Custom targets** (dynamic libraries)

## Installation

```bash
cargo install --path .
```

## Quick Start

```bash
# One-time sync to all targets
mcp-sync sync

# Watch for changes and re-sync
mcp-sync watch

# Sync only global configs
mcp-sync sync --scope global

# Dry run (show what would change)
mcp-sync sync --dry-run --verbose

# Clean up servers not in canonical file
mcp-sync sync --clean
```

## Canonical Format (`mcp.yaml`)

```yaml
version: 1

# Optional: plugins for transforming config
plugins:
  - name: env-expander
    config:
      prefix: "${"
      suffix: "}"

servers:
  # stdio server (local process)
  my-server:
    command: npx
    args: ["-y", "@modelcontextprotocol/server"]
    env:
      API_KEY: "${API_KEY}"
    cwd: /path/to/workdir
    enabled: true  # default: true

  # http server (remote)
  remote-server:
    kind: http
    url: https://mcp.example.com/v1
    headers:
      Authorization: "Bearer ${TOKEN}"
```

## CLI Options

| Option | Description |
|--------|-------------|
| `--canon <PATH or URL>` | Path or URL to canonical mcp.yaml (default: `mcp.yaml`) |
| `--scope <SCOPE>` | Sync scope: `global`, `project`, or `both` (default: `both`) |
| `--project-root <PATH>` | Project root (default: auto-detect git root) |
| `--clean` | Remove servers not present in canonical file |
| `--dry-run` | Print changes without writing files |
| `--log-level <LEVEL>` | Log level: trace, debug, info, warn, error (default: info) |
| `--plugin <PATH>` | Load additional plugin library (can be repeated) |
| `--target <PATH>` | Load custom target library (can be repeated) |
| `--only-target <NAMES>` | Sync only to specific targets (comma-separated or `all`) |

## Commands

### `sync` (default)
One-time sync to all targets.

### `watch`
Watch mcp.yaml for changes and re-sync automatically.

### `init`
Interactive wizard to create mcp.yaml:
```bash
mcp-sync init --output mcp.yaml
```

### `validate`
Validate mcp.yaml syntax and schema:
```bash
mcp-sync validate --canon mcp.yaml
```

### `diff`
Show differences between canon and current target configs:
```bash
mcp-sync diff --canon mcp.yaml
```

### `completions`
Generate shell completions:
```bash
# Bash
mcp-sync completions bash >> ~/.bashrc

# Zsh
mcp-sync completions zsh >> ~/.zshrc

# Fish
mcp-sync completions fish > ~/.config/fish/completions/mcp-sync.fish
```

## Remote Canon

Load mcp.yaml from a URL:
```bash
mcp-sync sync --canon https://example.com/team/mcp.yaml
```

## Examples

### Sync with Custom Target

```bash
# All built-in targets + custom target
mcp-sync sync --target ./my-target.dylib

# Multiple custom targets
mcp-sync sync --target ./target1.dylib --target ./target2.dylib

# Only Claude + custom target
mcp-sync sync --only-target claude --target ./my-target.dylib
```

### Sync with Custom Plugin

```bash
# Built-in env-expander + custom plugin
mcp-sync sync --plugin ./secret-injector.dylib
```

### Watch Mode with Custom Config

```bash
# Watch and sync on changes
mcp-sync watch --canon ~/my-mcp.yaml --verbose
```

### CI/CD Integration

```bash
# Sync global configs, fail on error
mcp-sync sync --scope global --canon mcp.yaml
```

## Target File Locations

### Global (User-level)

| Target | macOS | Linux | Windows |
|--------|-------|-------|---------|
| Antigravity | `~/Library/Application Support/Antigravity/User/mcp.json` | `~/.config/Antigravity/User/mcp.json` | `%APPDATA%\Antigravity\User\mcp.json` |
| Claude | `~/.claude.json` | `~/.claude.json` | `%USERPROFILE%\.claude.json` |
| Codex | `~/.codex/config.toml` | `~/.codex/config.toml` | `%APPDATA%\codex\config.toml` |
| OpenCode | `~/.config/opencode/opencode.json` | `~/.config/opencode/opencode.json` | `%APPDATA%\opencode\opencode.json` |

### Project-level
| Target | Path |
|--------|------|
| Antigravity | `.vscode/mcp.json` |
| Claude | `.mcp.json` |
| Codex | `.codex/config.toml` |
| OpenCode | `opencode.json` |

## Plugin System

Plugins can transform configurations during sync.

### Built-in: `env-expander`

Expands environment variables in config values:

```yaml
plugins:
  - name: env-expander
    config:
      prefix: "${"
      suffix: "}"
```

### Custom Plugins

Create a dynamic library that exports `create_plugin`:

```rust
use mcp_sync::{Canon, Plugin};
use anyhow::Result;
use serde_json::Value as JsonValue;

pub struct MyPlugin { /* ... */ }

impl Plugin for MyPlugin {
    fn name(&self) -> &str { "my-plugin" }
    
    fn on_load(&mut self, config: &JsonValue) -> Result<()> {
        Ok(())
    }
    
    fn transform_canon(&self, canon: &mut Canon) -> Result<()> {
        // Modify config before syncing
        Ok(())
    }
    
    fn transform_output(&self, target: &str, value: &mut JsonValue) -> Result<()> {
        // Modify target-specific output
        Ok(())
    }
}

#[unsafe(no_mangle)]
pub extern "C" fn create_plugin() -> *mut dyn Plugin {
    Box::into_raw(Box::new(MyPlugin::new()))
}
```

## Custom Targets

Create custom sync targets for unsupported tools.

### Implementation

```rust
use mcp_sync::{Canon, SyncOptions, Target};
use anyhow::Result;
use std::path::{Path, PathBuf};

pub struct MyTarget;

impl Target for MyTarget {
    fn name(&self) -> &'static str { "MyTarget" }
    
    fn global_path(&self) -> Result<PathBuf> {
        Ok(PathBuf::from("/path/to/global/config.json"))
    }
    
    fn project_path(&self, root: &Path) -> PathBuf {
        root.join(".my-target-config.json")
    }
    
    fn sync(&self, path: &Path, canon: &Canon, opts: &SyncOptions) -> Result<()> {
        // Write config in your tool's format
        Ok(())
    }
}

#[unsafe(no_mangle)]
pub extern "C" fn create_target() -> *mut dyn Target {
    Box::into_raw(Box::new(MyTarget))
}
```

### Build

```bash
# Cargo.toml
[lib]
crate-type = ["cdylib"]

[dependencies]
mcp-sync = { path = "..." }
anyhow = "1"
```

```bash
cargo build --release
# Output: target/release/libmy_target.dylib
```

### Use

```bash
mcp-sync sync --target ./target/release/libmy_target.dylib
```

## Backups

Before modifying any file, `mcp-sync` creates a timestamped backup:
```
config.json → config.json.bak.20260105-230041
```

## License

MIT