add-mcp 1.11.0 → 1.13.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
@@ -50,7 +50,7 @@ MCP servers can be installed to any of these agents:
50
50
 
51
51
  | Agent | `--agent` | Project Path | Global Path |
52
52
  | ---------------------- | -------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------- |
53
- | Antigravity | `antigravity` | - | `~/.gemini/antigravity/mcp_config.json` |
53
+ | Antigravity | `antigravity` | - | `~/.gemini/config/mcp_config.json` (shared by Antigravity, Antigravity IDE, and Antigravity CLI) |
54
54
  | Cline VSCode Extension | `cline` | - | `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` |
55
55
  | Cline CLI | `cline-cli` | - | `~/.cline/data/settings/cline_mcp_settings.json` |
56
56
  | Claude Code | `claude-code` | `.mcp.json` | `~/.claude.json` |
@@ -128,6 +128,12 @@ npx add-mcp https://mcp.example.com/mcp --header "Authorization: Bearer $TOKEN"
128
128
  # (each agent only keeps the fields it supports; others are dropped with a warning)
129
129
  npx add-mcp https://mcp.example.com/mcp --timeout 30000 --scopes "read,write"
130
130
 
131
+ # Auto-approve all tools for agents that support it (Codex, Claude Code)
132
+ npx add-mcp "executor mcp" --name executor -a codex -a claude-code --auto-approve
133
+
134
+ # Auto-approve only selected tools
135
+ npx add-mcp "executor mcp" --name executor -a codex --auto-approve --approve-tool execute
136
+
131
137
  # npm package (runs via npx)
132
138
  npx add-mcp @modelcontextprotocol/server-postgres
133
139
 
@@ -164,21 +170,23 @@ npx add-mcp https://mcp.example.com/mcp -a cursor -y --gitignore
164
170
 
165
171
  ### Options
166
172
 
167
- | Option | Description |
168
- | ------------------------ | ------------------------------------------------------------------------ |
169
- | `-g, --global` | Install to user directory instead of project |
170
- | `-a, --agent <agent>` | Target specific agents (e.g., `cursor`, `claude-code`). Can be repeated. |
171
- | `-t, --transport <type>` | Transport type for remote servers: `http` (default), `sse` |
172
- | `--type <type>` | Alias for `--transport` |
173
- | `-h, --header <header>` | HTTP header for remote servers (repeatable, `Key: Value`) |
174
- | `--env <env>` | Env var for local stdio servers (repeatable, `KEY=VALUE`) |
175
- | `--timeout <ms>` | Request timeout (ms) for remote servers (capability-gated, see below) |
176
- | `--scopes <scopes>` | OAuth scopes for remote servers, comma-separated (capability-gated) |
177
- | `--oauth-scopes <scopes>`| Alias for `--scopes` |
178
- | `-n, --name <name>` | Server name (auto-inferred if not provided) |
179
- | `-y, --yes` | Skip all confirmation prompts |
180
- | `--all` | Install to all agents |
181
- | `--gitignore` | Add generated config files to `.gitignore` |
173
+ | Option | Description |
174
+ | ------------------------- | ------------------------------------------------------------------------ |
175
+ | `-g, --global` | Install to user directory instead of project |
176
+ | `-a, --agent <agent>` | Target specific agents (e.g., `cursor`, `claude-code`). Can be repeated. |
177
+ | `-t, --transport <type>` | Transport type for remote servers: `http` (default), `sse` |
178
+ | `--type <type>` | Alias for `--transport` |
179
+ | `-h, --header <header>` | HTTP header for remote servers (repeatable, `Key: Value`) |
180
+ | `--env <env>` | Env var for local stdio servers (repeatable, `KEY=VALUE`) |
181
+ | `--timeout <ms>` | Request timeout (ms) for remote servers (capability-gated, see below) |
182
+ | `--scopes <scopes>` | OAuth scopes for remote servers, comma-separated (capability-gated) |
183
+ | `--oauth-scopes <scopes>` | Alias for `--scopes` |
184
+ | `--auto-approve` | Auto-approve MCP tool calls for supported agents (Codex, Claude Code) |
185
+ | `--approve-tool <tool>` | Tool to auto-approve with `--auto-approve` (repeatable; defaults to all) |
186
+ | `-n, --name <name>` | Server name (auto-inferred if not provided) |
187
+ | `-y, --yes` | Skip all confirmation prompts |
188
+ | `--all` | Install to all agents |
189
+ | `--gitignore` | Add generated config files to `.gitignore` |
182
190
 
183
191
  #### Capability-gated fields (`--timeout`, `--scopes`)
184
192
 
@@ -186,15 +194,36 @@ Not every MCP client understands every field. `add-mcp` keeps one canonical
186
194
  server config and each agent declares which optional fields it supports, mapping
187
195
  them into that client's native shape:
188
196
 
189
- | Field | Flag | Supported by | Mapped to |
190
- | ---------- | ---------------- | --------------------------- | ---------------------------------- |
191
- | Timeout | `--timeout` | Claude Code, Gemini CLI | `timeout` (milliseconds) |
192
- | OAuth scopes | `--scopes` | Cursor, Gemini CLI | Cursor `auth.scopes`, Gemini `oauth.scopes` |
197
+ | Field | Flag | Supported by | Mapped to |
198
+ | ------------------ | ----------------------------------- | ----------------------- | -------------------------------------------------------- |
199
+ | Timeout | `--timeout` | Claude Code, Gemini CLI | `timeout` (milliseconds) |
200
+ | OAuth scopes | `--scopes` | Cursor, Gemini CLI | Cursor `auth.scopes`, Gemini `oauth.scopes` |
201
+ | Tool auto-approval | `--auto-approve` / `--approve-tool` | Codex, Claude Code | Codex approval modes; Claude Code permission allow rules |
193
202
 
194
203
  When you target an agent that does not support a field, `add-mcp` drops it from
195
204
  that agent's config and prints a warning (e.g. _"request timeout is not
196
205
  supported by VS Code; dropped from that config."_). Other agents still receive
197
- it. Both flags apply to remote servers only.
206
+ it. `--timeout` and `--scopes` apply to remote servers only; `--auto-approve`
207
+ applies to both remote and local servers.
208
+
209
+ #### Auto-approving tool calls (`--auto-approve`)
210
+
211
+ `--auto-approve` preconfigures agent-level approval so the agent doesn't prompt
212
+ before each MCP tool call — useful for servers that already gate actions
213
+ internally. Use `--approve-tool <name>` (repeatable) to approve only specific
214
+ tools; without it, all tools are approved.
215
+
216
+ - **Codex** — writes approval modes into `config.toml`: per-tool
217
+ `tools.<name>.approval_mode = "approve"`, or `default_tools_approval_mode = "approve"` for all tools.
218
+ - **Claude Code** — writes permission allow rules to a separate settings file
219
+ (`.claude/settings.local.json` for project installs, `~/.claude/settings.json`
220
+ for global), e.g. `mcp__<server>__<tool>`, or `mcp__<server>` for all tools.
221
+ The MCP server entry itself stays clean.
222
+
223
+ > Note: Claude Code's all-tools rule (`mcp__<server>`) follows the documented
224
+ > format, but a known Claude Code bug ([#34739](https://github.com/anthropics/claude-code/issues/34739))
225
+ > can still prompt for non-fully-qualified MCP rules. Listing tools explicitly
226
+ > with `--approve-tool` is the most reliable path there.
198
227
 
199
228
  ### Transport Types
200
229
 
@@ -232,14 +261,16 @@ npx add-mcp find github --all --gitignore
232
261
 
233
262
  ### Options
234
263
 
235
- | Option | Description |
236
- | --------------------- | ------------------------------------------------------------------------ |
237
- | `-g, --global` | Install to user directory instead of project |
238
- | `-a, --agent <agent>` | Target specific agents (e.g., `cursor`, `claude-code`). Can be repeated. |
239
- | `-n, --name <name>` | Server name override (defaults to the selected catalog entry name) |
240
- | `-y, --yes` | Skip confirmation prompts |
241
- | `--all` | Install to all agents |
242
- | `--gitignore` | Add generated config files to `.gitignore` |
264
+ | Option | Description |
265
+ | ----------------------- | ------------------------------------------------------------------------ |
266
+ | `-g, --global` | Install to user directory instead of project |
267
+ | `-a, --agent <agent>` | Target specific agents (e.g., `cursor`, `claude-code`). Can be repeated. |
268
+ | `-n, --name <name>` | Server name override (defaults to the selected catalog entry name) |
269
+ | `-y, --yes` | Skip confirmation prompts |
270
+ | `--auto-approve` | Auto-approve MCP tool calls for supported agents (Codex, Claude Code) |
271
+ | `--approve-tool <tool>` | Tool to auto-approve with `--auto-approve` (repeatable; defaults to all) |
272
+ | `--all` | Install to all agents |
273
+ | `--gitignore` | Add generated config files to `.gitignore` |
243
274
 
244
275
  Transport for `find`/`search` is inferred from registry metadata. The CLI prefers HTTP remotes when available and only falls back to SSE when HTTP is not available for the selected install context.
245
276
 
@@ -133,7 +133,7 @@ var { appSupport, vscodePath, gooseConfigPath } = getPlatformPaths();
133
133
  var antigravityConfigPath = join2(
134
134
  home,
135
135
  ".gemini",
136
- "antigravity",
136
+ "config",
137
137
  "mcp_config.json"
138
138
  );
139
139
  var clineCliConfigPath = join2(
@@ -250,6 +250,17 @@ function transformOpenCodeConfig(_serverName, config) {
250
250
  environment: config.env || {}
251
251
  };
252
252
  }
253
+ function applyCodexApproval(target, autoApproveTools) {
254
+ if (!autoApproveTools) return target;
255
+ if (autoApproveTools.length === 0) {
256
+ target.default_tools_approval_mode = "approve";
257
+ return target;
258
+ }
259
+ target.tools = Object.fromEntries(
260
+ autoApproveTools.map((tool) => [tool, { approval_mode: "approve" }])
261
+ );
262
+ return target;
263
+ }
253
264
  function transformCodexConfig(_serverName, config) {
254
265
  if (config.url) {
255
266
  const remoteConfig = {
@@ -259,13 +270,16 @@ function transformCodexConfig(_serverName, config) {
259
270
  if (config.headers && Object.keys(config.headers).length > 0) {
260
271
  remoteConfig.http_headers = config.headers;
261
272
  }
262
- return remoteConfig;
273
+ return applyCodexApproval(remoteConfig, config.autoApproveTools);
263
274
  }
264
- return {
265
- command: config.command,
266
- args: config.args || [],
267
- env: config.env
268
- };
275
+ return applyCodexApproval(
276
+ {
277
+ command: config.command,
278
+ args: config.args || [],
279
+ env: config.env
280
+ },
281
+ config.autoApproveTools
282
+ );
269
283
  }
270
284
  function transformCursorConfig(_serverName, config) {
271
285
  if (config.url) {
@@ -375,7 +389,7 @@ var agents = {
375
389
  supportedTransports: ["stdio", "http", "sse"],
376
390
  supportedFields: [],
377
391
  detectGlobalInstall: async () => {
378
- return existsSync(join2(home, ".gemini"));
392
+ return existsSync(join2(home, ".gemini", "config"));
379
393
  },
380
394
  transformConfig: transformAntigravityConfig
381
395
  },
@@ -418,7 +432,7 @@ var agents = {
418
432
  configKey: "mcpServers",
419
433
  format: "json",
420
434
  supportedTransports: ["stdio", "http", "sse"],
421
- supportedFields: ["timeout"],
435
+ supportedFields: ["timeout", "autoApprove"],
422
436
  detectGlobalInstall: async () => {
423
437
  return existsSync(join2(home, ".claude"));
424
438
  },
@@ -452,7 +466,7 @@ var agents = {
452
466
  configKey: "mcp_servers",
453
467
  format: "toml",
454
468
  supportedTransports: ["stdio", "http", "sse"],
455
- supportedFields: [],
469
+ supportedFields: ["autoApprove"],
456
470
  detectGlobalInstall: async () => {
457
471
  return existsSync(join2(home, ".codex"));
458
472
  },
@@ -603,6 +617,13 @@ function getAgentTypes() {
603
617
  function supportsProjectConfig(agentType) {
604
618
  return agents[agentType].localConfigPath !== void 0;
605
619
  }
620
+ function getCommonInstallScopes(agentTypes) {
621
+ if (agentTypes.length === 0) return [];
622
+ if (agentTypes.some((agentType) => !supportsProjectConfig(agentType))) {
623
+ return ["global"];
624
+ }
625
+ return ["local", "global"];
626
+ }
606
627
  function getProjectCapableAgents() {
607
628
  return Object.keys(agents).filter(
608
629
  (type) => supportsProjectConfig(type)
@@ -1181,6 +1202,15 @@ var OPTIONAL_FIELD_SPECS = {
1181
1202
  clear: (config) => {
1182
1203
  delete config.oauthScopes;
1183
1204
  }
1205
+ },
1206
+ autoApprove: {
1207
+ label: "tool auto-approval",
1208
+ // An empty array is meaningful ("approve all tools"), so presence alone
1209
+ // counts as set — unlike scopes where an empty list means "nothing".
1210
+ isSet: (config) => Array.isArray(config.autoApproveTools),
1211
+ clear: (config) => {
1212
+ delete config.autoApproveTools;
1213
+ }
1184
1214
  }
1185
1215
  };
1186
1216
  var ALL_OPTIONAL_FIELDS = Object.keys(
@@ -1193,6 +1223,7 @@ function applyFieldSupport(config, supportedFields) {
1193
1223
  const supported = new Set(supportedFields);
1194
1224
  const copy = { ...config };
1195
1225
  if (copy.oauthScopes) copy.oauthScopes = [...copy.oauthScopes];
1226
+ if (copy.autoApproveTools) copy.autoApproveTools = [...copy.autoApproveTools];
1196
1227
  const dropped = [];
1197
1228
  for (const field of ALL_OPTIONAL_FIELDS) {
1198
1229
  const spec = OPTIONAL_FIELD_SPECS[field];
@@ -1206,7 +1237,9 @@ function applyFieldSupport(config, supportedFields) {
1206
1237
 
1207
1238
  // src/installer.ts
1208
1239
  import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
1240
+ import { homedir as homedir3 } from "os";
1209
1241
  import { join as join3, dirname as dirname6, isAbsolute, relative, sep } from "path";
1242
+ var home2 = homedir3();
1210
1243
  function buildServerConfig(parsed, options = {}) {
1211
1244
  if (parsed.type === "remote") {
1212
1245
  const config2 = {
@@ -1222,6 +1255,9 @@ function buildServerConfig(parsed, options = {}) {
1222
1255
  if (options.oauthScopes && options.oauthScopes.length > 0) {
1223
1256
  config2.oauthScopes = options.oauthScopes;
1224
1257
  }
1258
+ if (options.autoApproveTools) {
1259
+ config2.autoApproveTools = options.autoApproveTools;
1260
+ }
1225
1261
  return config2;
1226
1262
  }
1227
1263
  if (parsed.type === "command") {
@@ -1243,6 +1279,9 @@ function buildServerConfig(parsed, options = {}) {
1243
1279
  if (options.env && Object.keys(options.env).length > 0) {
1244
1280
  config2.env = options.env;
1245
1281
  }
1282
+ if (options.autoApproveTools) {
1283
+ config2.autoApproveTools = options.autoApproveTools;
1284
+ }
1246
1285
  return config2;
1247
1286
  }
1248
1287
  const config = {
@@ -1252,8 +1291,66 @@ function buildServerConfig(parsed, options = {}) {
1252
1291
  if (options.env && Object.keys(options.env).length > 0) {
1253
1292
  config.env = options.env;
1254
1293
  }
1294
+ if (options.autoApproveTools) {
1295
+ config.autoApproveTools = options.autoApproveTools;
1296
+ }
1255
1297
  return config;
1256
1298
  }
1299
+ function readJsonObject(filePath) {
1300
+ if (!existsSync5(filePath)) {
1301
+ return {};
1302
+ }
1303
+ const parsed = JSON.parse(readFileSync4(filePath, "utf-8"));
1304
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1305
+ }
1306
+ function writeJsonObject(filePath, value) {
1307
+ const dir = dirname6(filePath);
1308
+ if (!existsSync5(dir)) {
1309
+ mkdirSync4(dir, { recursive: true });
1310
+ }
1311
+ writeFileSync4(filePath, `${JSON.stringify(value, null, 2)}
1312
+ `, "utf-8");
1313
+ }
1314
+ function addUniqueStrings(existing, additions) {
1315
+ const values = Array.isArray(existing) ? existing.filter((value) => typeof value === "string") : [];
1316
+ for (const addition of additions) {
1317
+ if (!values.includes(addition)) {
1318
+ values.push(addition);
1319
+ }
1320
+ }
1321
+ return values;
1322
+ }
1323
+ function claudeAutoApproveRules(serverName, tools) {
1324
+ if (tools.length === 0) {
1325
+ return [`mcp__${serverName}`];
1326
+ }
1327
+ return tools.map((tool) => `mcp__${serverName}__${tool}`);
1328
+ }
1329
+ function getClaudeCodeSettingsPath(options = {}) {
1330
+ const local = Boolean(options.local);
1331
+ const cwd = options.cwd || process.cwd();
1332
+ return local ? join3(cwd, ".claude", "settings.local.json") : join3(home2, ".claude", "settings.json");
1333
+ }
1334
+ function installClaudeCodeAutoApproval(serverName, tools, options = {}) {
1335
+ const settingsPath = getClaudeCodeSettingsPath(options);
1336
+ try {
1337
+ const settings = readJsonObject(settingsPath);
1338
+ const permissions = settings.permissions && typeof settings.permissions === "object" && !Array.isArray(settings.permissions) ? settings.permissions : {};
1339
+ permissions.allow = addUniqueStrings(
1340
+ permissions.allow,
1341
+ claudeAutoApproveRules(serverName, tools)
1342
+ );
1343
+ settings.permissions = permissions;
1344
+ writeJsonObject(settingsPath, settings);
1345
+ return { success: true, path: settingsPath };
1346
+ } catch (error) {
1347
+ return {
1348
+ success: false,
1349
+ path: settingsPath,
1350
+ error: error instanceof Error ? error.message : "Unknown error"
1351
+ };
1352
+ }
1353
+ }
1257
1354
  function updateGitignoreWithPaths(paths, options = {}) {
1258
1355
  const cwd = options.cwd || process.cwd();
1259
1356
  const gitignorePath = join3(cwd, ".gitignore");
@@ -1324,10 +1421,27 @@ function installServerForAgent(serverName, serverConfig, agentType, options = {}
1324
1421
  const configKey = getConfigKey(agent, options);
1325
1422
  const config = buildConfigWithKey(configKey, serverName, transformedConfig);
1326
1423
  writeConfig2(configPath, config, agent.format, configKey);
1424
+ const extraPaths = [];
1425
+ if (agentType === "claude-code" && gatedConfig.autoApproveTools) {
1426
+ const approval = installClaudeCodeAutoApproval(
1427
+ serverName,
1428
+ gatedConfig.autoApproveTools,
1429
+ options
1430
+ );
1431
+ if (!approval.success) {
1432
+ return {
1433
+ success: false,
1434
+ path: approval.path,
1435
+ error: approval.error
1436
+ };
1437
+ }
1438
+ extraPaths.push(approval.path);
1439
+ }
1327
1440
  return {
1328
1441
  success: true,
1329
1442
  path: configPath,
1330
- ...dropped.length > 0 ? { droppedFields: dropped } : {}
1443
+ ...dropped.length > 0 ? { droppedFields: dropped } : {},
1444
+ ...extraPaths.length > 0 ? { extraPaths } : {}
1331
1445
  };
1332
1446
  } catch (error) {
1333
1447
  return {
@@ -1480,6 +1594,7 @@ export {
1480
1594
  agents,
1481
1595
  getAgentTypes,
1482
1596
  supportsProjectConfig,
1597
+ getCommonInstallScopes,
1483
1598
  getProjectCapableAgents,
1484
1599
  detectProjectAgents,
1485
1600
  detectGlobalAgents,
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  detectProjectAgents,
9
9
  findMatchingServers,
10
10
  getAgentTypes,
11
+ getCommonInstallScopes,
11
12
  getConfigPath,
12
13
  getFindRegistries,
13
14
  getLastSelectedAgents,
@@ -23,7 +24,7 @@ import {
23
24
  selectAgentsInteractive,
24
25
  supportsProjectConfig,
25
26
  updateGitignoreWithPaths
26
- } from "./chunk-2OCTBM45.js";
27
+ } from "./chunk-3ZFFEEQJ.js";
27
28
 
28
29
  // src/index.ts
29
30
  import { program } from "commander";
@@ -783,7 +784,7 @@ async function runFind(query, options) {
783
784
  // package.json
784
785
  var package_default = {
785
786
  name: "add-mcp",
786
- version: "1.11.0",
787
+ version: "1.13.0",
787
788
  description: "Add MCP servers to your favorite coding agents with a single command.",
788
789
  author: "Andre Landgraf <andre@neon.tech>",
789
790
  license: "Apache-2.0",
@@ -974,6 +975,25 @@ function extractSubcommandOptionsFromArgv() {
974
975
  result.gitignore = true;
975
976
  continue;
976
977
  }
978
+ if (arg === "--auto-approve") {
979
+ result.autoApprove = true;
980
+ continue;
981
+ }
982
+ if (arg === "--approve-tool") {
983
+ const tools = result.approveTool ? [...result.approveTool] : [];
984
+ let j = i + 1;
985
+ while (j < argv.length) {
986
+ const value = argv[j];
987
+ if (!value || value.startsWith("-")) break;
988
+ tools.push(value);
989
+ j += 1;
990
+ }
991
+ if (tools.length > 0) {
992
+ result.approveTool = tools;
993
+ }
994
+ i = j - 1;
995
+ continue;
996
+ }
977
997
  if ((arg === "-h" || arg === "--header") && argv[i + 1]) {
978
998
  const headers = result.header ? [...result.header] : [];
979
999
  headers.push(argv[i + 1]);
@@ -1126,7 +1146,15 @@ program.name("add-mcp").description(
1126
1146
  ).option(
1127
1147
  "--scopes <scopes>",
1128
1148
  "OAuth scopes to request for remote servers (comma-separated). Only applied to agents that support it (e.g. Cursor, Gemini CLI); dropped with a warning elsewhere."
1129
- ).option("--oauth-scopes <scopes>", "Alias for --scopes").option("-y, --yes", "Skip confirmation prompts").option("--all", "Install to all agents").option("--gitignore", "Add generated project config files to .gitignore").action(async (target, options) => {
1149
+ ).option("--oauth-scopes <scopes>", "Alias for --scopes").option(
1150
+ "--auto-approve",
1151
+ "Auto-approve MCP tool calls for agents that support it (Codex, Claude Code). Dropped with a warning for other agents."
1152
+ ).option(
1153
+ "--approve-tool <tool>",
1154
+ "Tool name to auto-approve when --auto-approve is set (repeatable; defaults to all tools)",
1155
+ collect,
1156
+ []
1157
+ ).option("-y, --yes", "Skip confirmation prompts").option("--all", "Install to all agents").option("--gitignore", "Add generated project config files to .gitignore").action(async (target, options) => {
1130
1158
  await main(target, options);
1131
1159
  });
1132
1160
  program.command("list-agents").description("List all supported coding agents").action(() => {
@@ -1172,7 +1200,15 @@ program.command("find [keyword]").description(
1172
1200
  ).option("-a, --agent <agent>", "Specify agents to install to", collect, []).option(
1173
1201
  "-n, --name <name>",
1174
1202
  "Server name override (defaults to catalog entry name)"
1175
- ).option("-y, --yes", "Skip confirmation prompts").option("--all", "Install to all agents").option("--gitignore", "Add generated project config files to .gitignore").action(
1203
+ ).option("-y, --yes", "Skip confirmation prompts").option(
1204
+ "--auto-approve",
1205
+ "Auto-approve MCP tool calls for agents that support it (Codex, Claude Code)"
1206
+ ).option(
1207
+ "--approve-tool <tool>",
1208
+ "Tool name to auto-approve when --auto-approve is set (repeatable; defaults to all tools)",
1209
+ collect,
1210
+ []
1211
+ ).option("--all", "Install to all agents").option("--gitignore", "Add generated project config files to .gitignore").action(
1176
1212
  async (keyword, options) => {
1177
1213
  await runFindCommand(keyword, options);
1178
1214
  }
@@ -1183,7 +1219,15 @@ program.command("search [keyword]").description("Alias for find").option(
1183
1219
  ).option("-a, --agent <agent>", "Specify agents to install to", collect, []).option(
1184
1220
  "-n, --name <name>",
1185
1221
  "Server name override (defaults to catalog entry name)"
1186
- ).option("-y, --yes", "Skip confirmation prompts").option("--all", "Install to all agents").option("--gitignore", "Add generated project config files to .gitignore").action(
1222
+ ).option("-y, --yes", "Skip confirmation prompts").option(
1223
+ "--auto-approve",
1224
+ "Auto-approve MCP tool calls for agents that support it (Codex, Claude Code)"
1225
+ ).option(
1226
+ "--approve-tool <tool>",
1227
+ "Tool name to auto-approve when --auto-approve is set (repeatable; defaults to all tools)",
1228
+ collect,
1229
+ []
1230
+ ).option("--all", "Install to all agents").option("--gitignore", "Add generated project config files to .gitignore").action(
1187
1231
  async (keyword, options) => {
1188
1232
  await runFindCommand(keyword, options);
1189
1233
  }
@@ -1829,18 +1873,19 @@ async function main(target, options) {
1829
1873
  p2.log.warn("--scopes is only used for remote URLs, ignoring");
1830
1874
  }
1831
1875
  }
1876
+ const approveTools = [...new Set(options.approveTool ?? [])];
1877
+ const autoApproveTools = options.autoApprove || approveTools.length > 0 ? approveTools : void 0;
1832
1878
  const serverConfig = buildServerConfig(parsed, {
1833
1879
  transport: resolvedTransport,
1834
1880
  headers: headersForConfig && Object.keys(headersForConfig).length > 0 ? headersForConfig : void 0,
1835
1881
  env: envForConfig && Object.keys(envForConfig).length > 0 ? envForConfig : void 0,
1836
1882
  args: argsForConfig && argsForConfig.length > 0 ? argsForConfig : void 0,
1837
1883
  timeout: resolvedTimeout,
1838
- oauthScopes: resolvedScopes
1884
+ oauthScopes: resolvedScopes,
1885
+ autoApproveTools
1839
1886
  });
1840
1887
  let targetAgents;
1841
1888
  const allAgentTypes = getAgentTypes();
1842
- const hasExplicitAgentFlags = options.agent && options.agent.length > 0 || options.all === true;
1843
- let selectedViaPrompt = false;
1844
1889
  let agentRouting = /* @__PURE__ */ new Map();
1845
1890
  if (options.agent && options.agent.length > 0) {
1846
1891
  const resolved = [];
@@ -1900,7 +1945,7 @@ async function main(target, options) {
1900
1945
  );
1901
1946
  }
1902
1947
  } else {
1903
- const availableAgents = options.global ? allAgentTypes : getProjectCapableAgents();
1948
+ const availableAgents = allAgentTypes;
1904
1949
  p2.log.warn(
1905
1950
  options.global ? "No coding agents detected." : "No agents detected in this project."
1906
1951
  );
@@ -1911,18 +1956,14 @@ async function main(target, options) {
1911
1956
  p2.cancel("Installation cancelled");
1912
1957
  process.exit(0);
1913
1958
  }
1914
- selectedViaPrompt = true;
1915
1959
  targetAgents = selected;
1916
- for (const agent of targetAgents) {
1917
- agentRouting.set(agent, options.global ? "global" : "local");
1918
- }
1919
1960
  }
1920
1961
  } else if (options.yes) {
1921
1962
  targetAgents = detectedAgents;
1922
1963
  const agentNames = detectedAgents.map((a) => chalk.cyan(agents[a].displayName)).join(", ");
1923
1964
  p2.log.info(`Installing to: ${agentNames}`);
1924
1965
  } else {
1925
- const availableAgents = options.global ? allAgentTypes : getProjectCapableAgents();
1966
+ const availableAgents = allAgentTypes;
1926
1967
  let lastSelected;
1927
1968
  try {
1928
1969
  lastSelected = await getLastSelectedAgents();
@@ -1944,11 +1985,7 @@ async function main(target, options) {
1944
1985
  p2.cancel("Installation cancelled");
1945
1986
  process.exit(0);
1946
1987
  }
1947
- selectedViaPrompt = true;
1948
1988
  targetAgents = selected;
1949
- for (const agent of targetAgents) {
1950
- agentRouting.set(agent, options.global ? "global" : "local");
1951
- }
1952
1989
  }
1953
1990
  }
1954
1991
  const requiredTransport = isRemoteSource(parsed) ? resolvedTransport ?? "http" : "stdio";
@@ -1982,70 +2019,62 @@ async function main(target, options) {
1982
2019
  process.exit(1);
1983
2020
  }
1984
2021
  }
1985
- const hasSmartRouting = agentRouting.size > 0;
1986
2022
  if (options.global) {
2023
+ agentRouting = /* @__PURE__ */ new Map();
1987
2024
  for (const agent of targetAgents) {
1988
2025
  agentRouting.set(agent, "global");
1989
2026
  }
1990
- } else if (!hasSmartRouting) {
1991
- const selectedWithLocal = targetAgents.filter(
1992
- (a) => supportsProjectConfig(a)
1993
- );
1994
- const globalOnlySelected = targetAgents.filter(
1995
- (a) => !supportsProjectConfig(a)
1996
- );
1997
- for (const agent of globalOnlySelected) {
1998
- agentRouting.set(agent, "global");
2027
+ } else {
2028
+ const commonScopes = getCommonInstallScopes(targetAgents);
2029
+ if (commonScopes.length === 0) {
2030
+ p2.log.error("No agents selected");
2031
+ process.exit(1);
1999
2032
  }
2000
- if (selectedWithLocal.length > 0) {
2001
- let installLocally = true;
2002
- if (!options.yes) {
2003
- const scope = await p2.select({
2004
- message: "Installation scope",
2005
- options: [
2006
- {
2007
- value: true,
2008
- label: "Project",
2009
- hint: "Install in current directory (committed with your project)"
2010
- },
2011
- {
2012
- value: false,
2013
- label: "Global",
2014
- hint: "Install in home directory (available across all projects)"
2015
- }
2016
- ]
2017
- });
2018
- if (p2.isCancel(scope)) {
2019
- p2.cancel("Installation cancelled");
2020
- process.exit(0);
2021
- }
2022
- installLocally = scope;
2023
- }
2024
- for (const agent of selectedWithLocal) {
2025
- agentRouting.set(agent, installLocally ? "local" : "global");
2033
+ let installScope = commonScopes[0];
2034
+ if (commonScopes.length > 1 && !options.yes) {
2035
+ const scope = await p2.select({
2036
+ message: "Installation scope",
2037
+ options: [
2038
+ {
2039
+ value: "local",
2040
+ label: "Project",
2041
+ hint: "Install in current directory (committed with your project)"
2042
+ },
2043
+ {
2044
+ value: "global",
2045
+ label: "Global",
2046
+ hint: "Install in home directory (available across all projects)"
2047
+ }
2048
+ ]
2049
+ });
2050
+ if (p2.isCancel(scope)) {
2051
+ p2.cancel("Installation cancelled");
2052
+ process.exit(0);
2026
2053
  }
2027
- } else {
2028
- p2.log.info("Selected agents only support global installation");
2054
+ installScope = scope;
2055
+ } else if (installScope === "global") {
2056
+ p2.log.info("Selected agents require global installation");
2057
+ }
2058
+ agentRouting = /* @__PURE__ */ new Map();
2059
+ for (const agent of targetAgents) {
2060
+ agentRouting.set(agent, installScope);
2029
2061
  }
2030
2062
  }
2031
2063
  const summaryLines = [];
2032
2064
  summaryLines.push(`${chalk.cyan("Server:")} ${serverName}`);
2033
2065
  summaryLines.push(`${chalk.cyan("Type:")} ${sourceType}`);
2066
+ if (autoApproveTools) {
2067
+ summaryLines.push(
2068
+ `${chalk.cyan("Auto-approve:")} ${autoApproveTools.length === 0 ? "All tools" : autoApproveTools.join(", ")}`
2069
+ );
2070
+ }
2034
2071
  const localAgents = targetAgents.filter(
2035
2072
  (a) => agentRouting.get(a) === "local"
2036
2073
  );
2037
2074
  const globalAgents = targetAgents.filter(
2038
2075
  (a) => agentRouting.get(a) === "global"
2039
2076
  );
2040
- if (localAgents.length > 0 && globalAgents.length > 0) {
2041
- summaryLines.push(`${chalk.cyan("Scope:")} Mixed (project + global)`);
2042
- summaryLines.push(
2043
- `${chalk.cyan(" Project:")} ${localAgents.map((a) => agents[a].displayName).join(", ")}`
2044
- );
2045
- summaryLines.push(
2046
- `${chalk.cyan(" Global:")} ${globalAgents.map((a) => agents[a].displayName).join(", ")}`
2047
- );
2048
- } else if (localAgents.length > 0) {
2077
+ if (localAgents.length > 0) {
2049
2078
  summaryLines.push(`${chalk.cyan("Scope:")} Project`);
2050
2079
  summaryLines.push(
2051
2080
  `${chalk.cyan("Agents:")} ${localAgents.map((a) => agents[a].displayName).join(", ")}`
@@ -2083,6 +2112,11 @@ async function main(target, options) {
2083
2112
  resultLines.push(
2084
2113
  `${chalk.green("\u2713")} ${agent.displayName}: ${chalk.dim(shortPath)}`
2085
2114
  );
2115
+ for (const extraPath of result.extraPaths ?? []) {
2116
+ resultLines.push(
2117
+ ` ${chalk.dim("\u21B3 permissions:")} ${chalk.dim(shortenPath(extraPath))}`
2118
+ );
2119
+ }
2086
2120
  }
2087
2121
  p2.note(
2088
2122
  resultLines.join("\n"),
@@ -2118,12 +2152,15 @@ async function main(target, options) {
2118
2152
  `${describeOptionalField(field)} is not supported by ${agentNames.join(", ")}; dropped from ${agentNames.length === 1 ? "that config" : "those configs"}.`
2119
2153
  );
2120
2154
  }
2121
- if (options.gitignore && options.global) {
2155
+ if (options.gitignore && localAgents.length === 0) {
2122
2156
  p2.log.warn(
2123
2157
  "--gitignore is only supported for project-scoped installations; ignoring."
2124
2158
  );
2125
2159
  } else if (options.gitignore) {
2126
- const successfulPaths = successful.map(([_, result]) => result.path);
2160
+ const successfulPaths = successful.flatMap(([_, result]) => [
2161
+ result.path,
2162
+ ...result.extraPaths ?? []
2163
+ ]);
2127
2164
  const gitignoreUpdate = updateGitignoreWithPaths(successfulPaths);
2128
2165
  if (gitignoreUpdate.added.length > 0) {
2129
2166
  p2.log.info(
package/dist/lib.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * before its transform runs, guaranteeing that only known fields are ever
9
9
  * written to a client config.
10
10
  */
11
- type OptionalField = "timeout" | "scopes";
11
+ type OptionalField = "timeout" | "scopes" | "autoApprove";
12
12
 
13
13
  type AgentType = "antigravity" | "cline" | "cline-cli" | "claude-code" | "claude-desktop" | "codex" | "cursor" | "gemini-cli" | "goose" | "github-copilot-cli" | "mcporter" | "opencode" | "vscode" | "windsurf" | "zed";
14
14
  type ConfigFormat = "json" | "yaml" | "toml";
@@ -79,6 +79,15 @@ interface McpServerConfig {
79
79
  * their own native shape (e.g. Cursor `auth.scopes`, Gemini `oauth.scopes`).
80
80
  */
81
81
  oauthScopes?: string[];
82
+ /**
83
+ * Tools to auto-approve (skip the agent's per-call approval prompt). An empty
84
+ * array means "all tools". This is an install directive rather than a literal
85
+ * server-config key: capability-gated via `"autoApprove"` in `supportedFields`
86
+ * and each supporting agent applies it in its own way (e.g. Codex emits
87
+ * approval modes in its config; Claude Code writes permission allow rules to a
88
+ * separate settings file). It is never written into a server entry verbatim.
89
+ */
90
+ autoApproveTools?: string[];
82
91
  }
83
92
 
84
93
  interface InstallOptions {
@@ -96,6 +105,12 @@ interface InstallResult {
96
105
  * does not support them. Empty/undefined when nothing was dropped.
97
106
  */
98
107
  droppedFields?: OptionalField[];
108
+ /**
109
+ * Additional files written beyond the main config (e.g. a Claude Code
110
+ * permissions settings file for auto-approval). Surfaced so the CLI can
111
+ * report them and include them in `--gitignore`.
112
+ */
113
+ extraPaths?: string[];
99
114
  }
100
115
 
101
116
  declare const agents: Record<AgentType, AgentConfig>;
package/dist/lib.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  listInstalledServers,
11
11
  readConfig,
12
12
  removeServerFromConfig
13
- } from "./chunk-2OCTBM45.js";
13
+ } from "./chunk-3ZFFEEQJ.js";
14
14
 
15
15
  // src/lib.ts
16
16
  import { existsSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-mcp",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "Add MCP servers to your favorite coding agents with a single command.",
5
5
  "author": "Andre Landgraf <andre@neon.tech>",
6
6
  "license": "Apache-2.0",