@enfyra/mcp-server 0.0.123 → 0.1.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
@@ -1,6 +1,6 @@
1
1
  # Enfyra MCP Server
2
2
 
3
- Manage Enfyra instances from MCP-compatible coding tools such as **Codex**, **Claude Code**, **Cursor**, MCP Inspector, and other STDIO MCP hosts.
3
+ Manage Enfyra instances from MCP-compatible coding tools such as **Codex**, **Claude Code**, **Cursor**, **VS Code / GitHub Copilot**, **Google Antigravity**, MCP Inspector, and other STDIO MCP hosts.
4
4
 
5
5
  This package is the MCP bridge only. Assistant rules, schema behavior, dynamic script guidance, and examples are served through the MCP server itself from `src/lib/mcp-instructions.js`, `src/lib/mcp-examples.js`, and tool descriptions in `src/mcp-server-entry.mjs`.
6
6
 
@@ -12,7 +12,7 @@ From your project root:
12
12
  npx @enfyra/mcp-server config
13
13
  ```
14
14
 
15
- The config command writes project config for Codex, Claude Code, and Cursor. It preserves other MCP servers and replaces only the `enfyra` entry.
15
+ The config command writes project config for Codex, Claude Code, Cursor, VS Code / GitHub Copilot, and Google Antigravity. It preserves other MCP servers and replaces only the `enfyra` entry.
16
16
 
17
17
  Interactive setup asks for your Enfyra app/admin URL, then guides you to the token page when needed and asks for `ENFYRA_API_TOKEN`.
18
18
 
@@ -25,6 +25,8 @@ npx @enfyra/mcp-server config --yes \
25
25
  # One or more clients
26
26
  npx @enfyra/mcp-server config --codex
27
27
  npx @enfyra/mcp-server config --cursor --claude-code
28
+ npx @enfyra/mcp-server config --vscode
29
+ npx @enfyra/mcp-server config --antigravity
28
30
  ```
29
31
 
30
32
  Equivalent in this repo:
@@ -40,7 +42,9 @@ yarn mcp:config
40
42
  | Codex | `npx @enfyra/mcp-server config --codex` | `.codex/config.toml` |
41
43
  | Claude Code | `npx @enfyra/mcp-server config --claude-code` | `.mcp.json` |
42
44
  | Cursor | `npx @enfyra/mcp-server config --cursor` | `.cursor/mcp.json` |
43
- | MCP Inspector / other hosts | Paste the shared STDIO config below | Host-specific `mcpServers` config |
45
+ | VS Code / GitHub Copilot | `npx @enfyra/mcp-server config --vscode` | `.vscode/mcp.json` |
46
+ | Google Antigravity | `npx @enfyra/mcp-server config --antigravity` | `.agents/mcp_config.json` |
47
+ | MCP Inspector / other project-scoped hosts | Paste the shared STDIO config below | Host-specific project config |
44
48
 
45
49
  <details>
46
50
  <summary><strong>Codex setup</strong></summary>
@@ -108,10 +112,71 @@ Official reference: [Cursor MCP](https://cursor.com/docs/context/mcp).
108
112
 
109
113
  </details>
110
114
 
115
+ <details>
116
+ <summary><strong>VS Code / GitHub Copilot setup</strong></summary>
117
+
118
+ ```bash
119
+ npx @enfyra/mcp-server config --vscode
120
+ ```
121
+
122
+ VS Code workspace config is written to `.vscode/mcp.json`:
123
+
124
+ ```json
125
+ {
126
+ "servers": {
127
+ "enfyra": {
128
+ "type": "stdio",
129
+ "command": "npx",
130
+ "args": ["-y", "@enfyra/mcp-server"],
131
+ "env": {
132
+ "ENFYRA_API_URL": "http://localhost:3000/api",
133
+ "ENFYRA_API_TOKEN": "efy_pat_your-token"
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ Use the VS Code command `MCP: List Servers` to inspect or start the server after setup. This is a workspace config, so it stays tied to the current project.
141
+
142
+ Official references: [VS Code MCP servers](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) and [VS Code MCP configuration](https://code.visualstudio.com/docs/agents/reference/mcp-configuration).
143
+
144
+ </details>
145
+
146
+ <details>
147
+ <summary><strong>Google Antigravity setup</strong></summary>
148
+
149
+ ```bash
150
+ npx @enfyra/mcp-server config --antigravity
151
+ ```
152
+
153
+ Antigravity project config is written to `.agents/mcp_config.json`:
154
+
155
+ ```json
156
+ {
157
+ "mcpServers": {
158
+ "enfyra": {
159
+ "command": "npx",
160
+ "args": ["-y", "@enfyra/mcp-server"],
161
+ "env": {
162
+ "ENFYRA_API_URL": "http://localhost:3000/api",
163
+ "ENFYRA_API_TOKEN": "efy_pat_your-token"
164
+ }
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ Antigravity also documents a shared user config at `~/.gemini/config/mcp_config.json`; this helper intentionally writes the project-local `.agents/mcp_config.json` file so Enfyra URL and token stay scoped to the current workspace.
171
+
172
+ Official reference: [Antigravity MCP](https://antigravity.google/docs/mcp).
173
+
174
+ </details>
175
+
111
176
  <details>
112
177
  <summary><strong>Other MCP hosts and MCP Inspector</strong></summary>
113
178
 
114
- Use the shared STDIO config with any host that accepts an `mcpServers` JSON block:
179
+ Use the shared STDIO config with any project-scoped host that accepts an `mcpServers` JSON block:
115
180
 
116
181
  ```json
117
182
  {
@@ -145,11 +210,12 @@ npx @enfyra/mcp-server config [options]
145
210
  | `--app-url` | Set the Enfyra app/admin URL |
146
211
  | `--api-token`, `-t` | Set `ENFYRA_API_TOKEN` |
147
212
  | `--yes` | Non-interactive mode for CI/scripts |
148
- | `--global` | Write global/user config instead of project config |
149
213
  | `--reconfig` | Prompt for target clients again and replace the existing `enfyra` entry |
150
214
  | `--codex` | Write Codex config |
151
215
  | `--claude-code`, `--claude` | Write Claude Code config |
152
216
  | `--cursor` | Write Cursor config |
217
+ | `--vscode`, `--copilot` | Write VS Code / GitHub Copilot config |
218
+ | `--antigravity` | Write Google Antigravity config |
153
219
  | `-h`, `--help` | Show CLI help |
154
220
 
155
221
  Without a target flag, interactive mode asks which client to configure. Non-interactive mode defaults to all supported clients.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enfyra/mcp-server",
3
- "version": "0.0.123",
3
+ "version": "0.1.0",
4
4
  "description": "MCP server for Enfyra - manage Enfyra instances from MCP-compatible coding tools",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.mjs CHANGED
@@ -11,15 +11,17 @@ if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
11
11
 
12
12
  Usage:
13
13
  npx @enfyra/mcp-server Start the MCP stdio server
14
- npx @enfyra/mcp-server config [flags] Write local MCP host config
14
+ npx @enfyra/mcp-server config [flags] Write project-local MCP host config
15
15
 
16
16
  Common config flags:
17
- --codex Write ~/.codex/config.toml
17
+ --codex Write ./.codex/config.toml
18
18
  --claude-code Write ./.mcp.json
19
19
  --cursor Write ./.cursor/mcp.json
20
+ --vscode Write ./.vscode/mcp.json
21
+ --antigravity Write ./.agents/mcp_config.json
20
22
  --reconfig Prompt for host and credentials again, replacing the enfyra entry
21
23
  --yes Non-interactive
22
- -a, --api-url ENFYRA_API_URL
24
+ --app-url ENFYRA_APP_URL
23
25
  -t, --api-token ENFYRA_API_TOKEN
24
26
  -h, --help Show config help
25
27
 
@@ -3,7 +3,6 @@ import { createInterface } from 'node:readline/promises';
3
3
  import { emitKeypressEvents } from 'node:readline';
4
4
  import { stdin as input, stdout as output, cwd } from 'node:process';
5
5
  import { dirname, join } from 'node:path';
6
- import { homedir } from 'node:os';
7
6
 
8
7
  const SERVER_KEY = 'enfyra';
9
8
  const forceColor = process.env.FORCE_COLOR != null && process.env.FORCE_COLOR !== '0';
@@ -36,6 +35,16 @@ const clients = {
36
35
  path: './.cursor/mcp.json',
37
36
  color: style.cyan,
38
37
  },
38
+ vscode: {
39
+ label: 'VS Code / Copilot',
40
+ path: './.vscode/mcp.json',
41
+ color: style.blue,
42
+ },
43
+ antigravity: {
44
+ label: 'Antigravity',
45
+ path: './.agents/mcp_config.json',
46
+ color: style.yellow,
47
+ },
39
48
  };
40
49
 
41
50
  function statusIcon(kind) {
@@ -55,7 +64,7 @@ function exitCancelled() {
55
64
 
56
65
  function printHelp() {
57
66
  console.log(`${style.bold('Enfyra MCP config')}
58
- ${style.dim('Write local MCP client config for Enfyra.')}
67
+ ${style.dim('Write project-local MCP client config for Enfyra.')}
59
68
 
60
69
  ${style.bold('Usage')}
61
70
  npx @enfyra/mcp-server config [options]
@@ -64,12 +73,12 @@ ${style.bold('Supported clients')}
64
73
  Codex ./.codex/config.toml
65
74
  Claude Code ./.mcp.json
66
75
  Cursor ./.cursor/mcp.json
67
- Other MCP hosts can use the shared stdio JSON from the README.
76
+ VS Code ./.vscode/mcp.json
77
+ Antigravity ./.agents/mcp_config.json
68
78
 
69
79
  ${style.bold('Options')}
70
80
  --app-url <url> Enfyra app/admin URL, for example https://demo.enfyra.io
71
81
  --api-token, -t <secret> ENFYRA_API_TOKEN
72
- --global Write global/user config for selected clients instead of project config
73
82
  --reconfig Always choose target again in interactive mode and replace the old enfyra config for that target
74
83
  --yes Non-interactive: no prompts (CI / scripts); use CLI, env, existing file, then defaults
75
84
 
@@ -79,20 +88,23 @@ ${style.bold('Client selection')}
79
88
  --claude-code, --claude, --claude-only Only ./.mcp.json (Claude Code project scope)
80
89
  --cursor, --cursor-only Only ./.cursor/mcp.json (Cursor)
81
90
  --codex, --codex-only Only ./.codex/config.toml (Codex project scope)
91
+ --vscode, --copilot, --vscode-only Only ./.vscode/mcp.json (VS Code / Copilot)
92
+ --antigravity, --antigravity-only Only ./.agents/mcp_config.json (Antigravity)
82
93
  Passing multiple target flags writes each selected target.
83
94
 
84
95
  -h, --help Show this help
85
96
 
86
97
  ${style.bold('Interactive mode')}
87
- Choose Codex, Claude Code, Cursor, or all clients; then enter ENFYRA_APP_URL and ENFYRA_API_TOKEN.
98
+ Choose Codex, Claude Code, Cursor, VS Code, Antigravity, or all clients; then enter ENFYRA_APP_URL and ENFYRA_API_TOKEN.
88
99
  Existing Enfyra config and environment variables are used as defaults. Re-run anytime to update.
89
100
 
90
101
  ${style.bold('Examples')}
91
102
  npx @enfyra/mcp-server config
92
103
  npx @enfyra/mcp-server config --yes
93
104
  npx @enfyra/mcp-server config --codex --cursor
105
+ npx @enfyra/mcp-server config --vscode
106
+ npx @enfyra/mcp-server config --antigravity
94
107
  npx @enfyra/mcp-server config --claude-code
95
- npx @enfyra/mcp-server config --global --codex
96
108
  npx @enfyra/mcp-server config --reconfig
97
109
  npx @enfyra/mcp-server config --app-url http://localhost:3000 -t 'efy_pat_...'
98
110
  ENFYRA_APP_URL=https://demo.enfyra.io ENFYRA_API_TOKEN=efy_pat_... npx @enfyra/mcp-server config --yes
@@ -109,11 +121,14 @@ function parseArgs(argv) {
109
121
  help: false,
110
122
  yes: false,
111
123
  reconfig: false,
112
- global: false,
124
+ vscode: true,
125
+ antigravity: true,
113
126
  };
114
127
  let pickClaude = false;
115
128
  let pickCursor = false;
116
129
  let pickCodex = false;
130
+ let pickVscode = false;
131
+ let pickAntigravity = false;
117
132
  for (let i = 0; i < argv.length; i += 1) {
118
133
  const a = argv[i];
119
134
  const next = () => {
@@ -126,7 +141,6 @@ function parseArgs(argv) {
126
141
  else if (a === 'help') out.help = true;
127
142
  else if (a === '--yes') out.yes = true;
128
143
  else if (a === '--reconfig') out.reconfig = true;
129
- else if (a === '--global') out.global = true;
130
144
  else if (a === '--app-url') out.appUrl = next();
131
145
  else if (a === '--api-url' || a === '-a') {
132
146
  throw new Error(`${a} is no longer supported for setup; use --app-url instead`);
@@ -138,13 +152,17 @@ function parseArgs(argv) {
138
152
  else if (a === '--claude-only' || a === '--claude-code' || a === '--claude') pickClaude = true;
139
153
  else if (a === '--cursor-only' || a === '--cursor') pickCursor = true;
140
154
  else if (a === '--codex-only' || a === '--codex') pickCodex = true;
155
+ else if (a === '--vscode-only' || a === '--vscode' || a === '--copilot') pickVscode = true;
156
+ else if (a === '--antigravity-only' || a === '--antigravity') pickAntigravity = true;
141
157
  else throw new Error(`Unknown argument: ${a}`);
142
158
  }
143
- out.targetExplicit = pickClaude || pickCursor || pickCodex;
159
+ out.targetExplicit = pickClaude || pickCursor || pickCodex || pickVscode || pickAntigravity;
144
160
  if (out.targetExplicit) {
145
161
  out.claude = pickClaude;
146
162
  out.cursor = pickCursor;
147
163
  out.codex = pickCodex;
164
+ out.vscode = pickVscode;
165
+ out.antigravity = pickAntigravity;
148
166
  }
149
167
  return out;
150
168
  }
@@ -193,9 +211,7 @@ async function mergeMcpFile(absPath, serverEntry) {
193
211
  try {
194
212
  const raw = await readFile(absPath, 'utf8');
195
213
  const parsed = JSON.parse(raw);
196
- if (parsed && typeof parsed === 'object' && parsed.mcpServers && typeof parsed.mcpServers === 'object') {
197
- data.mcpServers = { ...parsed.mcpServers };
198
- } else if (parsed && typeof parsed === 'object') {
214
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
199
215
  data = { ...parsed, mcpServers: parsed.mcpServers && typeof parsed.mcpServers === 'object' ? { ...parsed.mcpServers } : {} };
200
216
  }
201
217
  } catch (e) {
@@ -207,6 +223,31 @@ async function mergeMcpFile(absPath, serverEntry) {
207
223
  await writeFile(absPath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
208
224
  }
209
225
 
226
+ async function mergeVscodeMcpFile(absPath, serverEntry) {
227
+ let data = { servers: {} };
228
+ try {
229
+ const raw = await readFile(absPath, 'utf8');
230
+ const parsed = JSON.parse(raw);
231
+ if (parsed && typeof parsed === 'object') {
232
+ data = {
233
+ ...parsed,
234
+ servers: parsed.servers && typeof parsed.servers === 'object' ? { ...parsed.servers } : {},
235
+ };
236
+ }
237
+ } catch (e) {
238
+ if (e.code !== 'ENOENT') throw e;
239
+ }
240
+ data.servers = {
241
+ ...data.servers,
242
+ [SERVER_KEY]: {
243
+ type: 'stdio',
244
+ ...serverEntry,
245
+ },
246
+ };
247
+ await mkdir(dirname(absPath), { recursive: true });
248
+ await writeFile(absPath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
249
+ }
250
+
210
251
  function tomlString(value) {
211
252
  return JSON.stringify(String(value ?? ''));
212
253
  }
@@ -285,50 +326,69 @@ async function readCodexEnfyraEnv(absPath) {
285
326
  return null;
286
327
  }
287
328
 
288
- function getCodexConfigPath(root, globalScope) {
289
- return globalScope ? join(homedir(), '.codex', 'config.toml') : join(root, '.codex', 'config.toml');
329
+ function getCodexConfigPath(root) {
330
+ return join(root, '.codex', 'config.toml');
331
+ }
332
+
333
+ function getClaudeConfigPath(root) {
334
+ return join(root, '.mcp.json');
335
+ }
336
+
337
+ function getCursorConfigPath(root) {
338
+ return join(root, '.cursor', 'mcp.json');
290
339
  }
291
340
 
292
- function getClaudeConfigPath(root, globalScope) {
293
- return globalScope ? join(homedir(), '.mcp.json') : join(root, '.mcp.json');
341
+ function getVscodeConfigPath(root) {
342
+ return join(root, '.vscode', 'mcp.json');
294
343
  }
295
344
 
296
- function getCursorConfigPath(root, globalScope) {
297
- return globalScope ? join(homedir(), '.cursor', 'mcp.json') : join(root, '.cursor', 'mcp.json');
345
+ function getAntigravityConfigPath(root) {
346
+ return join(root, '.agents', 'mcp_config.json');
298
347
  }
299
348
 
300
- function getClientPath(client, root, globalScope) {
301
- if (client === 'claude') return getClaudeConfigPath(root, globalScope);
302
- if (client === 'cursor') return getCursorConfigPath(root, globalScope);
303
- if (client === 'codex') return getCodexConfigPath(root, globalScope);
349
+ function getClientPath(client, root) {
350
+ if (client === 'claude') return getClaudeConfigPath(root);
351
+ if (client === 'cursor') return getCursorConfigPath(root);
352
+ if (client === 'codex') return getCodexConfigPath(root);
353
+ if (client === 'vscode') return getVscodeConfigPath(root);
354
+ if (client === 'antigravity') return getAntigravityConfigPath(root);
304
355
  throw new Error(`Unknown MCP client: ${client}`);
305
356
  }
306
357
 
307
- async function loadExistingEnfyraEnv(root, readClaude, readCursor, readCodex, globalScope) {
358
+ async function readMcpServerEnv(absPath, serverRootKey) {
359
+ try {
360
+ const raw = await readFile(absPath, 'utf8');
361
+ const j = JSON.parse(raw);
362
+ const e = j?.[serverRootKey]?.[SERVER_KEY]?.env;
363
+ if (e && typeof e === 'object' && (e.ENFYRA_API_URL || e.ENFYRA_API_TOKEN)) {
364
+ return {
365
+ apiUrl: typeof e.ENFYRA_API_URL === 'string' ? e.ENFYRA_API_URL : '',
366
+ apiToken: typeof e.ENFYRA_API_TOKEN === 'string' ? e.ENFYRA_API_TOKEN : '',
367
+ };
368
+ }
369
+ } catch {
370
+ /* */
371
+ }
372
+ return null;
373
+ }
374
+
375
+ async function loadExistingEnfyraEnv(root, readClaude, readCursor, readCodex, readVscode, readAntigravity) {
308
376
  const paths = [];
309
- if (readClaude) paths.push(getClaudeConfigPath(root, globalScope));
310
- if (readCursor) paths.push(getCursorConfigPath(root, globalScope));
311
- if (!globalScope && !readClaude && readCursor) paths.push(join(root, '.mcp.json'));
377
+ if (readClaude) paths.push({ path: getClaudeConfigPath(root), rootKey: 'mcpServers' });
378
+ if (readCursor) paths.push({ path: getCursorConfigPath(root), rootKey: 'mcpServers' });
379
+ if (!readClaude && readCursor) paths.push({ path: getClaudeConfigPath(root), rootKey: 'mcpServers' });
380
+ if (readVscode) paths.push({ path: getVscodeConfigPath(root), rootKey: 'servers' });
381
+ if (readAntigravity) paths.push({ path: getAntigravityConfigPath(root), rootKey: 'mcpServers' });
312
382
  const seen = new Set();
313
- for (const p of paths) {
314
- if (seen.has(p)) continue;
315
- seen.add(p);
316
- try {
317
- const raw = await readFile(p, 'utf8');
318
- const j = JSON.parse(raw);
319
- const e = j?.mcpServers?.[SERVER_KEY]?.env;
320
- if (e && typeof e === 'object' && (e.ENFYRA_API_URL || e.ENFYRA_API_TOKEN)) {
321
- return {
322
- apiUrl: typeof e.ENFYRA_API_URL === 'string' ? e.ENFYRA_API_URL : '',
323
- apiToken: typeof e.ENFYRA_API_TOKEN === 'string' ? e.ENFYRA_API_TOKEN : '',
324
- };
325
- }
326
- } catch {
327
- /* */
328
- }
383
+ for (const entry of paths) {
384
+ const key = `${entry.rootKey}:${entry.path}`;
385
+ if (seen.has(key)) continue;
386
+ seen.add(key);
387
+ const env = await readMcpServerEnv(entry.path, entry.rootKey);
388
+ if (env) return env;
329
389
  }
330
390
  if (readCodex) {
331
- const codex = await readCodexEnfyraEnv(getCodexConfigPath(root, globalScope));
391
+ const codex = await readCodexEnfyraEnv(getCodexConfigPath(root));
332
392
  if (codex) return codex;
333
393
  }
334
394
  return { apiUrl: '', apiToken: '' };
@@ -338,23 +398,31 @@ async function promptTargetChoice() {
338
398
  const choices = [
339
399
  {
340
400
  client: 'codex',
341
- value: { claude: false, cursor: false, codex: true },
401
+ value: { claude: false, cursor: false, codex: true, vscode: false, antigravity: false },
342
402
  },
343
403
  {
344
404
  client: 'claude',
345
- value: { claude: true, cursor: false, codex: false },
405
+ value: { claude: true, cursor: false, codex: false, vscode: false, antigravity: false },
346
406
  },
347
407
  {
348
408
  client: 'cursor',
349
- value: { claude: false, cursor: true, codex: false },
409
+ value: { claude: false, cursor: true, codex: false, vscode: false, antigravity: false },
410
+ },
411
+ {
412
+ client: 'vscode',
413
+ value: { claude: false, cursor: false, codex: false, vscode: true, antigravity: false },
414
+ },
415
+ {
416
+ client: 'antigravity',
417
+ value: { claude: false, cursor: false, codex: false, vscode: false, antigravity: true },
350
418
  },
351
419
  {
352
420
  client: 'all',
353
- value: { claude: true, cursor: true, codex: true },
421
+ value: { claude: true, cursor: true, codex: true, vscode: true, antigravity: true },
354
422
  },
355
423
  ];
356
424
  if (input.setRawMode && output.isTTY) {
357
- return promptTargetSelect(choices, 3);
425
+ return promptTargetSelect(choices, 5);
358
426
  }
359
427
 
360
428
  const rl = createInterface({ input, output });
@@ -363,23 +431,31 @@ async function promptTargetChoice() {
363
431
  + ' [1] Codex ./.codex/config.toml\n'
364
432
  + ' [2] Claude Code ./.mcp.json\n'
365
433
  + ' [3] Cursor ./.cursor/mcp.json\n'
366
- + ' [4] All [default]\n'
367
- + 'Choice [4]: ',
434
+ + ' [4] VS Code ./.vscode/mcp.json\n'
435
+ + ' [5] Antigravity ./.agents/mcp_config.json\n'
436
+ + ' [6] All [default]\n'
437
+ + 'Choice [6]: ',
368
438
  )).trim().toLowerCase();
369
439
  await rl.close();
370
- if (line === '' || line === '4' || line === 'all' || line === 'a') {
371
- return { claude: true, cursor: true, codex: true };
440
+ if (line === '' || line === '6' || line === 'all' || line === 'a') {
441
+ return { claude: true, cursor: true, codex: true, vscode: true, antigravity: true };
372
442
  }
373
443
  if (line === '1' || line === 'codex' || line === 'x') {
374
- return { claude: false, cursor: false, codex: true };
444
+ return { claude: false, cursor: false, codex: true, vscode: false, antigravity: false };
375
445
  }
376
446
  if (line === '2' || line === 'claude' || line === 'claude-code') {
377
- return { claude: true, cursor: false, codex: false };
447
+ return { claude: true, cursor: false, codex: false, vscode: false, antigravity: false };
378
448
  }
379
449
  if (line === '3' || line === 'cursor' || line === 'u') {
380
- return { claude: false, cursor: true, codex: false };
450
+ return { claude: false, cursor: true, codex: false, vscode: false, antigravity: false };
451
+ }
452
+ if (line === '4' || line === 'vscode' || line === 'vs-code' || line === 'copilot') {
453
+ return { claude: false, cursor: false, codex: false, vscode: true, antigravity: false };
454
+ }
455
+ if (line === '5' || line === 'antigravity') {
456
+ return { claude: false, cursor: false, codex: false, vscode: false, antigravity: true };
381
457
  }
382
- return { claude: true, cursor: true, codex: true };
458
+ return { claude: true, cursor: true, codex: true, vscode: true, antigravity: true };
383
459
  }
384
460
 
385
461
  async function promptTargetSelect(choices, initialIndex = 0) {
@@ -392,7 +468,7 @@ async function promptTargetSelect(choices, initialIndex = 0) {
392
468
  if (choice.client === 'all') {
393
469
  const label = active ? style.bold(style.underline('All supported clients')) : 'All supported clients';
394
470
  const paddedLabel = label + ' '.repeat(22 - 'All supported clients'.length);
395
- const hint = active ? style.cyan('Codex + Claude Code + Cursor') : style.dim('Codex + Claude Code + Cursor');
471
+ const hint = active ? style.cyan('Codex + Claude Code + Cursor + VS Code + Antigravity') : style.dim('Codex + Claude Code + Cursor + VS Code + Antigravity');
396
472
  return `${accent} ${indicator} ${paddedLabel} ${hint}`;
397
473
  }
398
474
 
@@ -545,14 +621,18 @@ export async function runLocalConfig(argv) {
545
621
  let writeClaude = opts.claude;
546
622
  let writeCursor = opts.cursor;
547
623
  let writeCodex = opts.codex;
624
+ let writeVscode = opts.vscode;
625
+ let writeAntigravity = opts.antigravity;
548
626
  if (usePrompt && (!opts.targetExplicit || opts.reconfig)) {
549
627
  const t = await promptTargetChoice();
550
628
  writeClaude = t.claude;
551
629
  writeCursor = t.cursor;
552
630
  writeCodex = t.codex;
631
+ writeVscode = t.vscode;
632
+ writeAntigravity = t.antigravity;
553
633
  }
554
634
 
555
- const existing = await loadExistingEnfyraEnv(root, writeClaude, writeCursor, writeCodex, opts.global);
635
+ const existing = await loadExistingEnfyraEnv(root, writeClaude, writeCursor, writeCodex, writeVscode, writeAntigravity);
556
636
 
557
637
  let apiUrl;
558
638
  let apiToken;
@@ -578,23 +658,32 @@ export async function runLocalConfig(argv) {
578
658
  const written = [];
579
659
 
580
660
  if (writeCodex) {
581
- const p = getClientPath('codex', root, opts.global);
661
+ const p = getClientPath('codex', root);
582
662
  await mergeCodexConfig(p, apiUrl, apiToken);
583
663
  written.push({ client: 'codex', path: p });
584
664
  }
585
665
  if (writeClaude) {
586
- const p = getClientPath('claude', root, opts.global);
666
+ const p = getClientPath('claude', root);
587
667
  await mergeMcpFile(p, serverEntry);
588
668
  written.push({ client: 'claude', path: p });
589
669
  }
590
670
  if (writeCursor) {
591
- const p = getClientPath('cursor', root, opts.global);
671
+ const p = getClientPath('cursor', root);
592
672
  await mergeMcpFile(p, serverEntry);
593
673
  written.push({ client: 'cursor', path: p });
594
674
  }
675
+ if (writeVscode) {
676
+ const p = getClientPath('vscode', root);
677
+ await mergeVscodeMcpFile(p, serverEntry);
678
+ written.push({ client: 'vscode', path: p });
679
+ }
680
+ if (writeAntigravity) {
681
+ const p = getClientPath('antigravity', root);
682
+ await mergeMcpFile(p, serverEntry);
683
+ written.push({ client: 'antigravity', path: p });
684
+ }
595
685
 
596
- const scopeLabel = opts.global ? 'global/user' : 'project';
597
- console.log(`${statusIcon('success')} ${style.bold(style.green('Enfyra MCP config updated'))} ${style.dim(`(${scopeLabel})`)}\n`);
686
+ console.log(`${statusIcon('success')} ${style.bold(style.green('Enfyra MCP config updated'))} ${style.dim('(project)')}\n`);
598
687
  for (const entry of written) {
599
688
  const meta = clients[entry.client];
600
689
  console.log(` ${style.cyan('•')} ${style.bold(meta.color(meta.label))}`);
@@ -612,6 +701,12 @@ export async function runLocalConfig(argv) {
612
701
  if (selectedClients.has('cursor')) {
613
702
  console.log(' - Cursor: restart Cursor or reload MCP, then confirm the server under Settings -> MCP.');
614
703
  }
704
+ if (selectedClients.has('vscode')) {
705
+ console.log(' - VS Code / Copilot: run MCP: List Servers or reload the workspace, then start the Enfyra server if prompted.');
706
+ }
707
+ if (selectedClients.has('antigravity')) {
708
+ console.log(' - Antigravity: reopen the workspace or reload MCP servers so ./.agents/mcp_config.json is picked up.');
709
+ }
615
710
  console.log(' - Re-run this command anytime to update the same Enfyra entries.');
616
711
  if (!apiToken) {
617
712
  console.log(`\n${statusIcon('warn')} ${style.yellow('ENFYRA_API_TOKEN is empty; tools will not authenticate until it is set.')}`);