ai-agent-config 2.6.6 → 2.7.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
@@ -61,14 +61,14 @@ ai-agent update
61
61
 
62
62
  ## Supported Platforms
63
63
 
64
- | Platform | Skills Path |
65
- |----------|-------------|
66
- | Claude Code | `~/.claude/skills/` |
67
- | Antigravity IDE | `~/.gemini/antigravity/skills/` |
68
- | Cursor | `~/.cursor/skills/` |
69
- | Windsurf | `~/.windsurf/skills/` |
70
- | Codex CLI | `~/.codex/skills/` |
71
- | GitHub Copilot | `~/.github/copilot-instructions.md` |
64
+ | Platform | Skills Path | MCP Support |
65
+ |----------|-------------|-------------|
66
+ | Claude Code | `~/.claude/skills/` | `claude_desktop_config.json` |
67
+ | Antigravity IDE | `~/.gemini/antigravity/skills/` | `mcp_config.json` |
68
+ | Cursor | `~/.cursor/skills/` | - |
69
+ | Windsurf | `~/.windsurf/skills/` | - |
70
+ | Codex CLI | `~/.codex/skills/` | - |
71
+ | GitHub Copilot | `~/.github/copilot-instructions.md` | - |
72
72
 
73
73
  ## Secret Management
74
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-agent-config",
3
- "version": "2.6.6",
3
+ "version": "2.7.0",
4
4
  "description": "Universal skill & workflow manager for AI coding assistants with bi-directional GitHub sync",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -233,10 +233,10 @@ function installToPlatform(platform, options = {}) {
233
233
  }
234
234
  }
235
235
 
236
- // Install MCP servers (Antigravity only for now)
237
- if (platform.name === "antigravity") {
236
+ // Install MCP servers to platforms with MCP support
237
+ if (platform.mcpConfigPath) {
238
238
  try {
239
- results.mcpServers = mcpInstaller.installMcpServers({ force });
239
+ results.mcpServers = mcpInstaller.installMcpServers({ force, platform });
240
240
  } catch (error) {
241
241
  console.warn(` ⚠️ MCP install failed: ${error.message}`);
242
242
  }
@@ -110,116 +110,141 @@ function collectBitwardenEnvs() {
110
110
  }
111
111
 
112
112
  /**
113
- * Install MCP servers to Antigravity's mcp_config.json
114
- * This is the "pull" flow - installs structure without resolved secrets
115
- * @param {Object} options - { force: boolean }
116
- * @returns {{ added: number, skipped: number, servers: string[] }}
113
+ * Write MCP servers to a platform's config file
114
+ * @param {string} configPath - Path to platform's MCP config file
115
+ * @param {Array} servers - MCP server configs to install
116
+ * @param {Object} options - { force, platformName }
117
+ * @returns {{ added: number, skipped: number }}
117
118
  */
118
- function installMcpServers(options = {}) {
119
- const { force = false } = options;
120
- const antigravity = platforms.getByName("antigravity");
121
-
122
- if (!antigravity || !antigravity.mcpConfigPath) {
123
- return { added: 0, skipped: 0, servers: [] };
124
- }
119
+ function writeMcpToPlatformConfig(configPath, servers, options = {}) {
120
+ const { force = false, platformName = "" } = options;
125
121
 
126
- const servers = getAvailableMcpServers().filter((s) => s.enabled !== false);
127
- if (servers.length === 0) {
128
- return { added: 0, skipped: 0, servers: [] };
129
- }
130
-
131
- // Read existing mcp_config.json
132
- let mcpConfig = { mcpServers: {} };
133
- if (fs.existsSync(antigravity.mcpConfigPath)) {
122
+ // Read existing config — preserve ALL existing keys (e.g. Claude's "preferences")
123
+ let config = { mcpServers: {} };
124
+ if (fs.existsSync(configPath)) {
134
125
  try {
135
- mcpConfig = JSON.parse(fs.readFileSync(antigravity.mcpConfigPath, "utf-8"));
136
- if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
126
+ config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
127
+ if (!config.mcpServers) config.mcpServers = {};
137
128
  } catch {
138
- mcpConfig = { mcpServers: {} };
129
+ config = { mcpServers: {} };
139
130
  }
140
131
  }
141
132
 
142
133
  let added = 0;
143
134
  let skipped = 0;
144
- const serverNames = [];
145
135
 
146
136
  for (const server of servers) {
147
- const existing = mcpConfig.mcpServers[server.name];
137
+ const existing = config.mcpServers[server.name];
148
138
 
149
139
  if (existing && !force) {
150
140
  skipped++;
151
- serverNames.push(server.name);
152
141
  continue;
153
142
  }
154
143
 
155
- // Build server entry for Antigravity format
156
144
  const entry = {
157
145
  command: server.command,
158
146
  args: server.args,
159
147
  };
160
148
 
161
- // Preserve existing env if not forcing
149
+ // Preserve existing env
162
150
  if (existing && existing.env) {
163
151
  entry.env = existing.env;
164
152
  }
165
153
 
166
- if (server.disabledTools && server.disabledTools.length > 0) {
154
+ // disabledTools: only add for platforms that support it (not Claude)
155
+ if (platformName !== "claude" && server.disabledTools && server.disabledTools.length > 0) {
167
156
  entry.disabledTools = server.disabledTools;
168
157
  }
169
158
 
170
- mcpConfig.mcpServers[server.name] = entry;
159
+ config.mcpServers[server.name] = entry;
171
160
  added++;
172
- serverNames.push(server.name);
173
161
  }
174
162
 
175
- // Write back
163
+ // Write back (create directory if needed)
176
164
  if (added > 0) {
177
- const configDir = path.dirname(antigravity.mcpConfigPath);
165
+ const configDir = path.dirname(configPath);
178
166
  if (!fs.existsSync(configDir)) {
179
167
  fs.mkdirSync(configDir, { recursive: true });
180
168
  }
181
- fs.writeFileSync(antigravity.mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
169
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
182
170
  }
183
171
 
184
- return { added, skipped, servers: serverNames };
172
+ return { added, skipped };
185
173
  }
186
174
 
187
175
  /**
188
- * Install MCP servers with resolved secrets to Antigravity
189
- * This is the "secrets sync" flow - updates env with real values
190
- * @param {Object} resolvedSecrets - Map of envVar resolvedValue
191
- * @returns {{ installed: number, servers: Array<{ name: string, secretsCount: number }> }}
176
+ * Install MCP servers to platforms' config files
177
+ * This is the "pull" flow - installs structure without resolved secrets
178
+ * @param {Object} options - { force: boolean, platform: Object|null }
179
+ * @returns {{ added: number, skipped: number, servers: string[] }}
192
180
  */
193
- function installMcpServersWithSecrets(resolvedSecrets) {
194
- const antigravity = platforms.getByName("antigravity");
181
+ function installMcpServers(options = {}) {
182
+ const { force = false, platform = null } = options;
195
183
 
196
- if (!antigravity || !antigravity.mcpConfigPath) {
197
- return { installed: 0, servers: [] };
184
+ const servers = getAvailableMcpServers().filter((s) => s.enabled !== false);
185
+ if (servers.length === 0) {
186
+ return { added: 0, skipped: 0, servers: [] };
198
187
  }
199
188
 
200
- const allServers = getAvailableMcpServers().filter((s) => s.enabled !== false);
201
- if (allServers.length === 0) {
202
- return { installed: 0, servers: [] };
189
+ // Determine target platforms
190
+ const targetPlatforms = [];
191
+ if (platform) {
192
+ // Single platform specified
193
+ if (platform.mcpConfigPath) targetPlatforms.push(platform);
194
+ } else {
195
+ // All detected platforms with MCP support
196
+ for (const p of platforms.detectAll()) {
197
+ const full = platforms.getByName(p.name);
198
+ if (full && full.mcpConfigPath) targetPlatforms.push(full);
199
+ }
203
200
  }
204
201
 
205
- // Read existing mcp_config.json
206
- let mcpConfig = { mcpServers: {} };
207
- if (fs.existsSync(antigravity.mcpConfigPath)) {
202
+ if (targetPlatforms.length === 0) {
203
+ return { added: 0, skipped: 0, servers: [] };
204
+ }
205
+
206
+ let totalAdded = 0;
207
+ let totalSkipped = 0;
208
+ const serverNames = [...new Set(servers.map((s) => s.name))];
209
+
210
+ for (const p of targetPlatforms) {
211
+ const result = writeMcpToPlatformConfig(p.mcpConfigPath, servers, {
212
+ force,
213
+ platformName: p.name,
214
+ });
215
+ totalAdded += result.added;
216
+ totalSkipped += result.skipped;
217
+ }
218
+
219
+ return { added: totalAdded, skipped: totalSkipped, servers: serverNames };
220
+ }
221
+
222
+ /**
223
+ * Write MCP servers with resolved secrets to a platform's config file
224
+ * @param {string} configPath - Path to platform's MCP config file
225
+ * @param {Array} servers - MCP server configs
226
+ * @param {Object} resolvedSecrets - Map of bitwardenItem → resolvedValue
227
+ * @param {string} platformName - Platform name for disabledTools handling
228
+ * @returns {{ installed: number, servers: Array<{ name: string, secretsCount: number }> }}
229
+ */
230
+ function writeMcpWithSecretsToPlatformConfig(configPath, servers, resolvedSecrets, platformName) {
231
+ // Read existing config — preserve ALL existing keys
232
+ let config = { mcpServers: {} };
233
+ if (fs.existsSync(configPath)) {
208
234
  try {
209
- mcpConfig = JSON.parse(fs.readFileSync(antigravity.mcpConfigPath, "utf-8"));
210
- if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
235
+ config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
236
+ if (!config.mcpServers) config.mcpServers = {};
211
237
  } catch {
212
- mcpConfig = { mcpServers: {} };
238
+ config = { mcpServers: {} };
213
239
  }
214
240
  }
215
241
 
216
242
  let installed = 0;
217
243
  const serverResults = [];
218
244
 
219
- for (const server of allServers) {
220
- const existing = mcpConfig.mcpServers[server.name] || {};
245
+ for (const server of servers) {
246
+ const existing = config.mcpServers[server.name] || {};
221
247
 
222
- // Build entry: keep existing custom fields (disabledTools user may have customized)
223
248
  const entry = {
224
249
  command: server.command,
225
250
  args: server.args,
@@ -246,29 +271,71 @@ function installMcpServersWithSecrets(resolvedSecrets) {
246
271
  serverResults.push({ name: server.name, secretsCount: 0 });
247
272
  }
248
273
 
249
- // Preserve user-customized disabledTools from existing config
250
- if (existing.disabledTools) {
251
- entry.disabledTools = existing.disabledTools;
252
- } else if (server.disabledTools && server.disabledTools.length > 0) {
253
- entry.disabledTools = server.disabledTools;
274
+ // disabledTools: only for platforms that support it (not Claude)
275
+ if (platformName !== "claude") {
276
+ if (existing.disabledTools) {
277
+ entry.disabledTools = existing.disabledTools;
278
+ } else if (server.disabledTools && server.disabledTools.length > 0) {
279
+ entry.disabledTools = server.disabledTools;
280
+ }
254
281
  }
255
282
 
256
- mcpConfig.mcpServers[server.name] = entry;
283
+ config.mcpServers[server.name] = entry;
257
284
  installed++;
258
285
  }
259
286
 
260
287
  // Write back
261
288
  if (installed > 0) {
262
- const configDir = path.dirname(antigravity.mcpConfigPath);
289
+ const configDir = path.dirname(configPath);
263
290
  if (!fs.existsSync(configDir)) {
264
291
  fs.mkdirSync(configDir, { recursive: true });
265
292
  }
266
- fs.writeFileSync(antigravity.mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
293
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
267
294
  }
268
295
 
269
296
  return { installed, servers: serverResults };
270
297
  }
271
298
 
299
+ /**
300
+ * Install MCP servers with resolved secrets to all MCP-capable platforms
301
+ * This is the "secrets sync" flow - updates env with real values
302
+ * @param {Object} resolvedSecrets - Map of bitwardenItem → resolvedValue
303
+ * @returns {{ installed: number, servers: Array<{ name: string, secretsCount: number }> }}
304
+ */
305
+ function installMcpServersWithSecrets(resolvedSecrets) {
306
+ const allServers = getAvailableMcpServers().filter((s) => s.enabled !== false);
307
+ if (allServers.length === 0) {
308
+ return { installed: 0, servers: [] };
309
+ }
310
+
311
+ // All detected platforms with MCP support
312
+ const targetPlatforms = [];
313
+ for (const p of platforms.detectAll()) {
314
+ const full = platforms.getByName(p.name);
315
+ if (full && full.mcpConfigPath) targetPlatforms.push(full);
316
+ }
317
+
318
+ if (targetPlatforms.length === 0) {
319
+ return { installed: 0, servers: [] };
320
+ }
321
+
322
+ let totalInstalled = 0;
323
+ let aggregatedServers = [];
324
+
325
+ for (const p of targetPlatforms) {
326
+ const result = writeMcpWithSecretsToPlatformConfig(
327
+ p.mcpConfigPath, allServers, resolvedSecrets, p.name
328
+ );
329
+ totalInstalled += result.installed;
330
+ // Use server results from last platform (they're the same servers)
331
+ if (result.servers.length > 0) {
332
+ aggregatedServers = result.servers;
333
+ }
334
+ }
335
+
336
+ return { installed: totalInstalled, servers: aggregatedServers };
337
+ }
338
+
272
339
  module.exports = {
273
340
  getMcpServersDir,
274
341
  validateMcpConfig,
@@ -276,5 +343,6 @@ module.exports = {
276
343
  collectBitwardenEnvs,
277
344
  installMcpServers,
278
345
  installMcpServersWithSecrets,
346
+ writeMcpToPlatformConfig,
279
347
  SKIP_FOLDERS,
280
348
  };
@@ -32,6 +32,15 @@ const SUPPORTED = [
32
32
  get commandsPath() {
33
33
  return path.join(HOME, this.configDir, this.commandsDir);
34
34
  },
35
+ get mcpConfigPath() {
36
+ if (process.platform === "darwin") {
37
+ return path.join(HOME, "Library", "Application Support", "Claude", "claude_desktop_config.json");
38
+ } else if (process.platform === "win32") {
39
+ return path.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
40
+ } else {
41
+ return path.join(HOME, ".config", "Claude", "claude_desktop_config.json");
42
+ }
43
+ },
35
44
  detect() {
36
45
  return fs.existsSync(this.configPath);
37
46
  },