mcpsmgr 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Unified MCP (Model Context Protocol) server manager for multiple AI coding agent
6
6
 
7
7
  ## Problem
8
8
 
9
- Each AI coding agent (Claude Code, Codex CLI, Gemini CLI, OpenCode, Antigravity) uses its own config format for MCP servers. Managing the same servers across multiple agents means editing multiple config files manually, which is tedious and error-prone.
9
+ Each AI coding agent (Claude Code, Codex, Gemini CLI, OpenCode, Antigravity) uses its own config format for MCP servers. Managing the same servers across multiple agents means editing multiple config files manually, which is tedious and error-prone.
10
10
 
11
11
  ## Solution
12
12
 
@@ -15,7 +15,7 @@ Each AI coding agent (Claude Code, Codex CLI, Gemini CLI, OpenCode, Antigravity)
15
15
  ```
16
16
  Central Repository Agent Configs
17
17
  ┌──────────────────┐ ┌─► Claude Code (.claude.json)
18
- │ server-a (stdio)│───┼─► Codex CLI (.codex/config.toml)
18
+ │ server-a (stdio)│───┼─► Codex (.codex/config.toml)
19
19
  │ server-b (http) │ ├─► Gemini CLI (.gemini/settings.json)
20
20
  │ server-c (stdio)│ ├─► OpenCode (.opencode.json)
21
21
  └──────────────────┘ └─► Antigravity (.antigravity/config.json)
@@ -24,7 +24,7 @@ Central Repository Agent Configs
24
24
  ## Features
25
25
 
26
26
  - **Central server repository** - Define MCP servers once in `~/.mcps-manager/servers/`
27
- - **Multi-agent support** - Claude Code, Codex CLI, Gemini CLI, OpenCode, Antigravity
27
+ - **Multi-agent support** - Claude Code, Codex, Gemini CLI, OpenCode, Antigravity
28
28
  - **AI-assisted setup** - Provide a URL or GitHub repo, and GLM-5 analyzes the documentation to generate the config automatically
29
29
  - **Per-agent overrides** - Customize server config for specific agents when needed
30
30
  - **Project-level init** - Deploy selected servers to detected agents in any project
@@ -81,7 +81,7 @@ mcpsmgr sync
81
81
  | Agent | Config Location | Format |
82
82
  |---|---|---|
83
83
  | Claude Code | `.claude.json` (project) | JSON |
84
- | Codex CLI | `.codex/config.toml` (project) | TOML |
84
+ | Codex | `.codex/config.toml` (project) | TOML |
85
85
  | Gemini CLI | `.gemini/settings.json` (global) | JSON |
86
86
  | OpenCode | `.opencode.json` (project) | JSON |
87
87
  | Antigravity | `.antigravity/config.json` (project) | JSON |
package/dist/index.js CHANGED
@@ -258,7 +258,7 @@ The 5 agents and their configuration differences:
258
258
  - HTTP: { "type": "http", "url": "...", "headers": {...} }
259
259
  - IMPORTANT: Do NOT use "env" field. Environment variables will be handled separately.
260
260
 
261
- 2. **Codex CLI** (.codex/config.toml)
261
+ 2. **Codex** (.codex/config.toml)
262
262
  - TOML format: command = "...", args = [...]
263
263
  - Same key names as Claude Code but in TOML
264
264
  - IMPORTANT: Do NOT use "env" field.
@@ -304,7 +304,7 @@ After analyzing the documentation, return a JSON object with this exact structur
304
304
 
305
305
  Rules:
306
306
  - "name" should be a kebab-case identifier for the server
307
- - "default" should be the most common configuration (usually works for Claude Code, Codex CLI, Gemini CLI)
307
+ - "default" should be the most common configuration (usually works for Claude Code, Codex, Gemini CLI)
308
308
  - Only add "overrides" for agents that need DIFFERENT configuration from the default
309
309
  - OpenCode usually needs an override because its command format differs (array vs string+args)
310
310
  - "requiredEnvVars" lists environment variable names the user needs to provide values for
@@ -630,7 +630,7 @@ async function serverListCommand() {
630
630
  import { checkbox, confirm as confirm3 } from "@inquirer/prompts";
631
631
 
632
632
  // src/adapters/index.ts
633
- import { existsSync as existsSync6 } from "fs";
633
+ import { existsSync as existsSync7 } from "fs";
634
634
 
635
635
  // src/adapters/claude-code.ts
636
636
  import { join as join2 } from "path";
@@ -789,7 +789,7 @@ var claudeCodeAdapter = {
789
789
  }
790
790
  };
791
791
 
792
- // src/adapters/codex-cli.ts
792
+ // src/adapters/codex.ts
793
793
  import { join as join3 } from "path";
794
794
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
795
795
  import { existsSync as existsSync5 } from "fs";
@@ -861,9 +861,9 @@ async function writeTomlFile(filePath, data) {
861
861
  }
862
862
  await writeFile4(filePath, stringifyToml(data) + "\n", "utf-8");
863
863
  }
864
- var codexCliAdapter = {
865
- id: "codex-cli",
866
- name: "Codex CLI",
864
+ var codexAdapter = {
865
+ id: "codex",
866
+ name: "Codex",
867
867
  configPath: (projectDir) => join3(projectDir, ".codex", "config.toml"),
868
868
  isGlobal: false,
869
869
  toAgentFormat: toAgentFormat2,
@@ -879,7 +879,7 @@ var codexCliAdapter = {
879
879
  const servers = parsed["mcp_servers"] ?? {};
880
880
  if (serverName in servers) {
881
881
  throw new Error(
882
- `Conflict: "${serverName}" already exists in Codex CLI config`
882
+ `Conflict: "${serverName}" already exists in Codex config`
883
883
  );
884
884
  }
885
885
  const updated = {
@@ -1195,20 +1195,147 @@ var antigravityAdapter = {
1195
1195
  }
1196
1196
  };
1197
1197
 
1198
+ // src/adapters/openclaw.ts
1199
+ import { homedir as homedir3 } from "os";
1200
+ import { join as join7 } from "path";
1201
+
1202
+ // src/adapters/json5-file.ts
1203
+ import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir6 } from "fs/promises";
1204
+ import { existsSync as existsSync6 } from "fs";
1205
+ import { dirname as dirname3 } from "path";
1206
+ import JSON5 from "json5";
1207
+ async function readJson5File(filePath) {
1208
+ if (!existsSync6(filePath)) {
1209
+ return {};
1210
+ }
1211
+ const raw = await readFile5(filePath, "utf-8");
1212
+ return JSON5.parse(raw);
1213
+ }
1214
+ async function writeJson5File(filePath, data) {
1215
+ const dir = dirname3(filePath);
1216
+ if (!existsSync6(dir)) {
1217
+ await mkdir6(dir, { recursive: true });
1218
+ }
1219
+ await writeFile5(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
1220
+ }
1221
+
1222
+ // src/adapters/openclaw.ts
1223
+ var GLOBAL_CONFIG_PATH2 = join7(
1224
+ homedir3(),
1225
+ ".openclaw",
1226
+ "openclaw.json"
1227
+ );
1228
+ function getMcpServers(data) {
1229
+ const plugins = data["plugins"];
1230
+ const entries = plugins?.["entries"];
1231
+ const acpx = entries?.["acpx"];
1232
+ return acpx?.["mcpServers"] ?? {};
1233
+ }
1234
+ function setMcpServers(data, servers) {
1235
+ const plugins = data["plugins"] ?? {};
1236
+ const entries = plugins["entries"] ?? {};
1237
+ const acpx = entries["acpx"] ?? {};
1238
+ return {
1239
+ ...data,
1240
+ plugins: {
1241
+ ...plugins,
1242
+ entries: {
1243
+ ...entries,
1244
+ acpx: {
1245
+ ...acpx,
1246
+ enabled: true,
1247
+ mcpServers: servers
1248
+ }
1249
+ }
1250
+ }
1251
+ };
1252
+ }
1253
+ function toAgentFormat6(config) {
1254
+ if (config.transport === "stdio") {
1255
+ const { resolvedArgs, remainingEnv } = resolveEnvInArgs(
1256
+ config.args,
1257
+ config.env
1258
+ );
1259
+ const result = {
1260
+ command: config.command,
1261
+ args: resolvedArgs
1262
+ };
1263
+ if (Object.keys(remainingEnv).length > 0) {
1264
+ result["env"] = { ...remainingEnv };
1265
+ }
1266
+ return result;
1267
+ }
1268
+ const args = ["-y", "mcp-remote@latest", config.url];
1269
+ for (const [key, value] of Object.entries(config.headers)) {
1270
+ args.push("--header", `${key}: ${value}`);
1271
+ }
1272
+ return {
1273
+ command: "npx",
1274
+ args
1275
+ };
1276
+ }
1277
+ function fromAgentFormat6(_name, raw) {
1278
+ if (!raw["command"]) {
1279
+ return void 0;
1280
+ }
1281
+ const command = raw["command"];
1282
+ const rawArgs = raw["args"] ?? [];
1283
+ const env = raw["env"] ?? {};
1284
+ return { transport: "stdio", command, args: rawArgs, env };
1285
+ }
1286
+ var openclawAdapter = {
1287
+ id: "openclaw",
1288
+ name: "OpenClaw",
1289
+ configPath: () => GLOBAL_CONFIG_PATH2,
1290
+ isGlobal: true,
1291
+ toAgentFormat: toAgentFormat6,
1292
+ fromAgentFormat: fromAgentFormat6,
1293
+ async read() {
1294
+ const data = await readJson5File(GLOBAL_CONFIG_PATH2);
1295
+ return getMcpServers(data);
1296
+ },
1297
+ async write(_projectDir, serverName, config) {
1298
+ const data = await readJson5File(GLOBAL_CONFIG_PATH2);
1299
+ const servers = getMcpServers(data);
1300
+ if (serverName in servers) {
1301
+ throw new Error(
1302
+ `Conflict: "${serverName}" already exists in OpenClaw config`
1303
+ );
1304
+ }
1305
+ const updated = setMcpServers(data, {
1306
+ ...servers,
1307
+ [serverName]: toAgentFormat6(config)
1308
+ });
1309
+ await writeJson5File(GLOBAL_CONFIG_PATH2, updated);
1310
+ },
1311
+ async remove(_projectDir, serverName) {
1312
+ const data = await readJson5File(GLOBAL_CONFIG_PATH2);
1313
+ const servers = getMcpServers(data);
1314
+ const { [serverName]: _, ...rest } = servers;
1315
+ await writeJson5File(GLOBAL_CONFIG_PATH2, setMcpServers(data, rest));
1316
+ },
1317
+ async has(_projectDir, serverName) {
1318
+ const data = await readJson5File(GLOBAL_CONFIG_PATH2);
1319
+ const servers = getMcpServers(data);
1320
+ return serverName in servers;
1321
+ }
1322
+ };
1323
+
1198
1324
  // src/adapters/index.ts
1199
1325
  var allAdapters = [
1200
1326
  claudeCodeAdapter,
1201
- codexCliAdapter,
1327
+ codexAdapter,
1202
1328
  geminiCliAdapter,
1203
1329
  opencodeAdapter,
1204
- antigravityAdapter
1330
+ antigravityAdapter,
1331
+ openclawAdapter
1205
1332
  ];
1206
1333
  function detectAgents(projectDir) {
1207
1334
  return allAdapters.filter((adapter) => {
1208
1335
  if (adapter.isGlobal) {
1209
- return existsSync6(adapter.configPath(projectDir));
1336
+ return existsSync7(adapter.configPath(projectDir));
1210
1337
  }
1211
- return existsSync6(adapter.configPath(projectDir));
1338
+ return existsSync7(adapter.configPath(projectDir));
1212
1339
  });
1213
1340
  }
1214
1341