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:
- maintain the same MCP definition four times;
- drift between clients (opencode has v2 of the package, kimi still has v1);
- scatter tokens across files (security disaster);
- have no global doctor — each client gives partial feedback only.
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:
cp <target> <target>.bak.{YYYYMMDDTHHMMSS}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
- Check the
_managed_byanchor (or registry file for codex/kimi). Refuse if not ours. - Backup the current config.
- Remove the entry.
- If the resulting config is "empty in this section", clean up the section too.
- Run
prune_backups(keep=5)again.
Tests
- 28 schema tests — manifest validation, secret-literal rejection, MCP-specific fields.
- 11 adapter tests — install / uninstall round-trip, refusal to delete unmanaged entries.
- Total: 39 tests, all green.