SKILL.md
---
name: ln-004-agent-config-sync
description: "Syncs skills and MCP settings from Claude Code to Gemini CLI and Codex CLI. Use when agent configs need alignment after changes."
license: MIT
---
> **Paths:** File paths (`shared/`, `references/`, `../ln-*`) are relative to skills repo root. If not found at CWD, locate this SKILL.md directory and go up one level for repo root.
# Agent Sync (Standalone Utility)
**Type:** Standalone Utility
**Category:** 0XX Shared
Synchronizes skills and MCP server configurations from Claude Code (source of truth) to Gemini CLI and Codex CLI. Creates symlinks for skills, copies/converts MCP settings.
---
## When to Use This Skill
- After adding/removing MCP servers in Claude Code settings
- After installing new plugins in Claude Code
- First-time setup of Gemini CLI or Codex CLI alongside Claude Code
- Periodic sync to keep all agents aligned
---
## Input Parameters
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| targets | No | both | `gemini`, `codex`, or `both` |
| mode | No | full | `skills` (symlinks only), `mcp` (MCP only), or `full` (both) |
| dry_run | No | false | Show planned actions without executing |
---
## Workflow
```
Detect OS → Discover Configs → Sync Skills → Sync MCP → Report
```
### Phase 0: OS Detection
| Check | Result | Impact |
|-------|--------|--------|
| `uname` or platform | win32 / darwin / linux | Junction vs symlink |
| Home directory | `$HOME` or `$USERPROFILE` | Config paths |
**Paths by OS:**
| Agent | Windows | macOS / Linux |
|-------|---------|---------------|
| **Claude** | `%USERPROFILE%\.claude.json` (primary) | `~/.claude.json` (primary) |
| **Claude** | `%USERPROFILE%\.claude\settings.json` (fallback) | `~/.claude/settings.json` (fallback) |
| **Gemini** | `%USERPROFILE%\.gemini\settings.json` | `~/.gemini/settings.json` |
| **Codex** | `%USERPROFILE%\.codex\config.toml` | `~/.codex/config.toml` |
### Phase 1: Discover Current State
1. **Read Claude settings (check both sources, merge):**
- Parse `~/.claude.json` → extract `mcpServers` block (primary source, app state file)
- Parse `~/.claude/settings.json` → extract `mcpServers` block (fallback, user settings file)
- Merge: primary overrides fallback by server name
- If no `mcpServers` in either → WARN "No MCP servers configured in Claude", skip MCP sync
2. **Read target configs (if exist):**
- Gemini: Parse `~/.gemini/settings.json` → extract existing `mcpServers`
- Codex: Parse `~/.codex/config.toml` → extract existing `[mcp_servers.*]`
3. **Detect installed plugins:**
- Glob `~/.claude/plugins/*/plugin.json` → list plugin directories
- Also check if skills repo itself is a plugin source
4. **Check existing symlinks:**
- `~/.gemini/skills` → exists? points where?
- `~/.codex/skills` → exists? points where?
5. **Show current state:**
```
Current State:
| Agent | Skills | MCP Servers | Config Exists |
|-------|--------|-------------|---------------|
| Claude (source) | 5 plugins | 4 servers | yes |
| Gemini | no link | 2 servers | yes |
| Codex | → ~/.claude/plugins | 4 servers | yes |
```
### Phase 2: Sync Skills (symlinks/junctions)
FOR EACH target IN (gemini, codex) WHERE target in targets:
1. **Determine link path:**
- Gemini: `~/.gemini/skills`
- Codex: `~/.codex/skills`
2. **Check if already linked correctly:**
- IF link exists AND points to correct source → SKIP (already synced)
- IF link exists AND points to wrong source → WARN, ask user before replacing
- IF regular directory (not link) exists → WARN "Target is a real directory, not a link. Skip to avoid data loss."
3. **Determine source:**
- IF single plugin: link directly to plugin dir
- IF multiple plugins: ask user which plugin to share
4. **Create link:**
| OS | Command |
|----|---------|
| Windows | `cmd /c mklink /J "{target_path}" "{source_path}"` |
| macOS / Linux | `ln -s "{source_path}" "{target_path}"` |
5. **Verify:** Check link exists and resolves correctly
### Phase 3: Sync MCP Settings
**Source:** `~/.claude.json` (primary) + `~/.claude/settings.json` (fallback) → merged `mcpServers`
#### 3a: Claude → Gemini (JSON → JSON)
1. Read Claude `mcpServers` as JSON object
2. Read Gemini `settings.json` (or create `{}` if missing)
3. **Merge strategy:** Claude servers override Gemini servers by key name. Gemini-only servers preserved.
4. Write updated `settings.json`
**Conversion:** Transport type mapping required:
| Claude field | Gemini field | Notes |
|---|---|---|
| `type: "http"` + `url` | `httpUrl` | Streamable HTTP |
| `type: "sse"` + `url` | `url` | SSE transport |
| `command` + `args` | `command` + `args` + `cwd` | stdio (same format) |
| `env` | `env` | Same format (`$VAR_NAME` syntax in Gemini) |
| `headers` | `headers` | Same format (JSON object, for `url`/`httpUrl`) |
**Gemini-only fields (not mapped from Claude):**
- `timeout` — request timeout in ms (default: 10min)
- `trust` — bypass tool confirmations
- `includeTools` / `excludeTools` — tool whitelist/blacklist
#### 3b: Claude → Codex (JSON → TOML)
1. Read Claude `mcpServers` as JSON object
2. Read Codex `config.toml` (or create empty if missing)
3. **Convert each server:**
| Claude JSON field | Codex TOML field | Notes |
|-------------------|------------------|-------|
| `command` | `command` | Same |
| `args` | `args` | JSON array → TOML array |
| `env` | `[mcp_servers.{name}.env]` | Nested table |
| `type: "http"` + `url` | `url` | Streamable HTTP (Codex auto-detects by `url` presence) |
| `type: "sse"` + `url` | `url` | SSE → Streamable HTTP (same `url` field) |
| `headers` | `http_headers` | **Different key name!** Static header values |
**Example conversion:**
```json
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"]
}
```
```toml
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
```
4. **Merge strategy:** Claude servers override. Codex-only servers preserved.
5. Write updated `config.toml`
**Codex-only fields (preserve as-is, no Claude equivalent):**
- `bearer_token_env_var` — Codex bearer token auth
- `enabled_tools` / `disabled_tools` — Codex tool filtering
- `startup_timeout_sec` / `tool_timeout_sec` — Codex timeouts
- `enabled` / `required` — Codex server lifecycle
- `env_vars` / `env_http_headers` — Codex env-based headers
### Phase 4: Report
```
Sync Complete:
| Action | Target | Status |
|--------|--------|--------|
| Skills symlink | Gemini | Created → ~/.claude/plugins/... |
| Skills symlink | Codex | Already linked |
| MCP sync | Gemini | 4 servers synced (2 new, 2 updated) |
| MCP sync | Codex | 4 servers synced (1 new, 3 updated) |
```
---
## Critical Rules
1. **Claude = source of truth.** Never write TO Claude settings. Read-only source.
2. **Non-destructive merge.** Target-only servers/settings preserved. Only Claude servers added/updated.
3. **No data loss.** If target is a real directory (not symlink) — warn and skip, never delete.
4. **Backup before write.** Before modifying any config file, create `.bak` copy.
5. **Dry run first.** If `dry_run=true`, show all planned actions without executing.
6. **Ask on conflict.** If symlink points to different source — ask user, don't auto-replace.
## Anti-Patterns
- Writing TO Claude settings from Gemini/Codex (reverse sync)
- Deleting target-only MCP servers during sync
- Creating symlinks inside symlinks (circular)
- Modifying config files without backup
---
## Definition of Done
- [ ] Claude settings read successfully
- [ ] Skills symlinks created/verified for each target
- [ ] MCP settings synced with format conversion (JSON→TOML for Codex)
- [ ] Backup files created before any config modification
- [ ] Report shown with all actions and warnings
---
**Version:** 1.1.0
**Last Updated:** 2026-03-09