DOCUMENTATION

Architecture

One registry · four adapters · env-var-driven secrets · clean uninstall by anchor.

Top-down map

┌─────────────────────────────────────────────────────────────────────┐
│  Agent_mcp (this repo)                                              │
│                                                                     │
│  registry/<name>/manifest.yaml   ← single source of truth           │
│    │  declares: mcp_command, requires.env, compatibility            │
│    │                                                                │
│  agent/lib/                                                         │
│    ├── manifest.py               ← shared schema (byte-id 3 repos)  │
│    ├── adapter_opencode.py       ← JSON merge into mcp object       │
│    ├── adapter_codex.py          ← TOML merge into [mcp_servers.x]  │
│    ├── adapter_cursor.py         ← JSON merge into mcpServers       │
│    ├── adapter_kimi.py           ← shell out to: kimi mcp add       │
│    └── cli.py                    ← agent-mcp list/install/...       │
│                                                                     │
└─────────────┬───────────────────────────────────────────────────────┘
              │ writes (with cp .bak.{ts} + _managed_by anchor)
              ▼
┌──────────────────┬───────────────┬──────────────┬─────────────────┐
▼                  ▼               ▼              ▼                 ▼
~/.config/opencode/  ~/.codex/      ~/.cursor/     ~/.kimi/    .agent-mcp-
  opencode.json      config.toml    mcp.json       (via CLI)   managed.json
  "mcp.<name>"      "[mcp_servers   "mcpServers.   kimi mcp     anchor record
                      .<name>]"      <name>"        list

Why this shape

Each AI CLI has its own MCP config format. Without a registry, you'd:

Secret model

All MCP secrets live in shell environment, never in files:

# In manifest.yaml — declares the ENV VAR NAMES, not values
requires:
  binaries: [npx, node]
  env: [GITHUB_TOKEN]

When writing client config, the adapter writes ${GITHUB_TOKEN} as a literal interpolation directive (opencode/cursor) or passes $GITHUB_TOKEN through to kimi mcp add -e GITHUB_TOKEN=... (which kimi then forwards into the MCP child process env). The actual token is never persisted on disk.

The schema validator includes a regex check: any manifest containing literal ghp_*, sk-[a-zA-Z0-9]{10,}, or AIza[0-9A-Za-z_-]{20,} is rejected at load time.

The opencode adapter

Merges into the mcp object in ~/.config/opencode/opencode.json:

{
  "mcp": {
    "github": {
      "type": "local",
      "enabled": true,
      "command": ["npx", "-y", "@modelcontextprotocol/server-github"],
      "environment": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" },
      "_managed_by": "agent-mcp",
      "_managed_name": "github",
      "_managed_at": "2026-05-16T..."
    }
  }
}

The codex adapter

Appends a TOML section to ~/.codex/config.toml:

[mcp_servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]

[mcp_servers.github.env]
GITHUB_TOKEN = "${GITHUB_TOKEN}"

Anchor is tracked in ~/.codex/.agent-mcp-managed.json since TOML doesn't have a clean comment-attachment story.

The cursor adapter

Merges into mcpServers in ~/.cursor/mcp.json:

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" },
      "_managed_by": "agent-mcp"
    }
  }
}

The kimi adapter

kimi has a native subcommand. We shell out and let kimi own the file format:

kimi mcp add github \
  --transport stdio \
  -e GITHUB_TOKEN="$GITHUB_TOKEN" \
  -- npx -y @modelcontextprotocol/server-github

Our anchor lives in ~/.kimi/.agent-mcp-managed.json so we know which entries to clean up on uninstall.

Backup discipline

Every write to a client config file:

  1. cp <target> <target>.bak.{YYYYMMDDTHHMMSS}
  2. prune_backups(keep=5) — keep the 5 most recent backups, delete older.

So if you panic, you can cp opencode.json.bak.20260516T... opencode.json in seconds.

Uninstall protocol

  1. Check the _managed_by anchor (or registry file for codex/kimi). Refuse if not ours.
  2. Backup the current config.
  3. Remove the entry.
  4. If the resulting config is "empty in this section", clean up the section too.
  5. Run prune_backups(keep=5) again.

Tests