@saiteja1123/mcp-server 1.1.5 → 1.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saiteja1123/mcp-server",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Vibesecur MCP security scanner — universal account config, multi-project CRU, folder locking, cross-IDE",
package/src/cli.js CHANGED
@@ -18,7 +18,6 @@
18
18
 
19
19
  import fs from 'fs/promises';
20
20
  import path from 'path';
21
- import os from 'os';
22
21
  import { createRequire } from 'module';
23
22
  import { fileURLToPath } from 'url';
24
23
  import {
@@ -30,6 +29,16 @@ import {
30
29
  isRuntimeCompatibleLock,
31
30
  } from './lock.mjs';
32
31
  import { pingBackend, requestServerBind, verifyInstallBinding } from './api-scan.mjs';
32
+ import {
33
+ DEFAULT_API_BASE,
34
+ NPM_PACKAGE_NAME,
35
+ buildLegacyEnv,
36
+ buildUniversalEnv,
37
+ formatIdeConfigBlocks,
38
+ IDE_CONFIG_TARGETS,
39
+ isUniversalMode,
40
+ publishedServerCmd,
41
+ } from './mcp-config.mjs';
33
42
  import {
34
43
  DeepScanRuntime,
35
44
  LocalDeepScanStore,
@@ -41,8 +50,7 @@ import {
41
50
 
42
51
  const require = createRequire(import.meta.url);
43
52
  const mcpPkg = require('../package.json');
44
- const NPM_PACKAGE_NAME = mcpPkg.name || '@saiteja1123/mcp-server';
45
- const DEFAULT_API_BASE = 'https://vibesecur.onrender.com';
53
+ const NPM_PACKAGE = NPM_PACKAGE_NAME;
46
54
 
47
55
  const serverPath = fileURLToPath(new URL('./server.js', import.meta.url));
48
56
 
@@ -85,17 +93,25 @@ function allowLocalFallback() {
85
93
  function serverCmd() {
86
94
  const normalizedPath = serverPath.replace(/\\/g, '/');
87
95
  if (normalizedPath.includes('/node_modules/')) {
88
- return { command: 'npx', args: ['-y', NPM_PACKAGE_NAME, 'start'] };
96
+ return publishedServerCmd();
89
97
  }
90
98
  return { command: 'node', args: [serverPath] };
91
99
  }
92
100
 
93
101
  function buildEnv(folder, token, apiBase) {
94
- return {
95
- VIBESECUR_INSTALL_TOKEN: token,
96
- VIBESECUR_BOUND_ROOT: folder,
97
- VIBESECUR_API_BASE: apiBase || DEFAULT_API_BASE,
98
- };
102
+ return buildLegacyEnv(folder, token, apiBase);
103
+ }
104
+
105
+ function printIdeBlocks(sc, env, options = {}) {
106
+ for (const block of formatIdeConfigBlocks(sc, env, options)) {
107
+ if (block.title === 'Mode') {
108
+ console.log(`${DIM}${block.body}${RESET}\n`);
109
+ continue;
110
+ }
111
+ console.log(`${BOLD}-- ${block.title} --${RESET}`);
112
+ console.log(block.body);
113
+ console.log();
114
+ }
99
115
  }
100
116
 
101
117
  function printLocalDiagnosticNotice(folder) {
@@ -137,20 +153,30 @@ async function createServerIssuedLock({ folder, account, authToken, apiBase, act
137
153
  return { ok: false, remote };
138
154
  }
139
155
 
140
- function printIdeBlocks(sc, env) {
141
- console.log(`${BOLD}-- Cursor (~/.cursor/mcp.json) --${RESET}`);
142
- console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
156
+ function printIdeBlocksLegacy(sc, env) {
157
+ printIdeBlocks(sc, env, { universal: false });
158
+ }
159
+
160
+ async function runUniversalSetup({ apiBase, authToken, folder, write }) {
161
+ const sc = serverCmd();
162
+ const env = buildUniversalEnv(apiBase, authToken);
143
163
 
144
- console.log(`\n${BOLD}-- VS Code (project .vscode/mcp.json) --${RESET}`);
145
- console.log(JSON.stringify({ servers: { vibesecur: { type: 'stdio', command: sc.command, args: sc.args, env } } }, null, 2));
164
+ console.log(ok('Universal account MCP config (works in Cursor, Windsurf, VS Code, Claude Desktop, Continue)'));
165
+ console.log(dim('No per-folder bind required. Use projectUpsert in the IDE before scanning a new codebase.\n'));
146
166
 
147
- console.log(`\n${BOLD}-- Windsurf (~/.codeium/windsurf/mcp_config.json) --${RESET}`);
148
- console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
167
+ console.log(`${BOLD}Next:${RESET} paste ONE block below into your IDE MCP settings, then reload the IDE.\n`);
168
+ printIdeBlocks(sc, env, { universal: true });
149
169
 
150
- console.log(`\n${BOLD}-- Generic env (paste into your MCP client) --${RESET}`);
151
- console.log(`command: ${sc.command} ${sc.args.join(' ')}`);
152
- Object.entries(env).forEach(([k, v]) => console.log(` ${k}=${v}`));
153
- console.log();
170
+ if (write) {
171
+ const w = String(write).toLowerCase();
172
+ const allowed = ['cursor', 'vscode', 'windsurf', 'claude', 'all'];
173
+ if (!allowed.includes(w)) {
174
+ console.log(err(`Invalid --write=${write}. Use: ${allowed.join(' | ')}`));
175
+ process.exit(1);
176
+ }
177
+ await writeIdeConfig(w, sc, env, folder);
178
+ console.log(dim('Reload your IDE so it picks up the new MCP config.'));
179
+ }
154
180
  }
155
181
 
156
182
  async function mergeJsonFile(filePath, merger) {
@@ -166,40 +192,34 @@ async function mergeJsonFile(filePath, merger) {
166
192
  await fs.writeFile(filePath, JSON.stringify(next, null, 2), 'utf8');
167
193
  }
168
194
 
169
- async function writeIdeConfig(which, sc, env) {
170
- const entry = { command: sc.command, args: sc.args, env };
171
- const vscodeEntry = { type: 'stdio', command: sc.command, args: sc.args, env };
172
-
173
- if (which === 'cursor' || which === 'all') {
174
- const file = path.join(os.homedir(), '.cursor', 'mcp.json');
175
- await mergeJsonFile(file, (data) => {
176
- const out = { ...data };
177
- out.mcpServers = { ...(out.mcpServers || {}), vibesecur: entry };
178
- return out;
179
- });
180
- console.log(ok(`Wrote Cursor MCP config: ${file}`));
181
- }
182
-
183
- if (which === 'vscode' || which === 'all') {
184
- const folder = env.VIBESECUR_BOUND_ROOT;
185
- const file = path.join(folder, '.vscode', 'mcp.json');
186
- await mergeJsonFile(file, (data) => {
195
+ async function writeIdeConfig(which, sc, env, folder = process.cwd()) {
196
+ const writeOne = async (id) => {
197
+ const target = IDE_CONFIG_TARGETS[id];
198
+ if (!target) return false;
199
+ if (target.needsFolder && !folder) {
200
+ console.log(err(`Skipping ${id}: pass a project folder for .vscode/mcp.json`));
201
+ return false;
202
+ }
203
+ const filePath = target.needsFolder ? target.path(folder) : target.path();
204
+ const built = target.build(sc, env);
205
+ const entry = built[target.mergeKey][target.entryKey];
206
+ await mergeJsonFile(filePath, (data) => {
187
207
  const out = { ...data };
188
- out.servers = { ...(out.servers || {}), vibesecur: vscodeEntry };
208
+ out[target.mergeKey] = { ...(out[target.mergeKey] || {}), [target.entryKey]: entry };
189
209
  return out;
190
210
  });
191
- console.log(ok(`Wrote VS Code MCP config: ${file}`));
192
- }
211
+ console.log(ok(`Wrote ${target.label}: ${filePath}`));
212
+ return true;
213
+ };
193
214
 
194
- if (which === 'windsurf' || which === 'all') {
195
- const file = path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
196
- await mergeJsonFile(file, (data) => {
197
- const out = { ...data };
198
- out.mcpServers = { ...(out.mcpServers || {}), vibesecur: entry };
199
- return out;
200
- });
201
- console.log(ok(`Wrote Windsurf MCP config: ${file}`));
215
+ if (which === 'all') {
216
+ await writeOne('cursor');
217
+ await writeOne('windsurf');
218
+ await writeOne('claude');
219
+ await writeOne('vscode');
220
+ return;
202
221
  }
222
+ await writeOne(which);
203
223
  }
204
224
 
205
225
  function createDeepScanRuntime(folder) {
@@ -544,24 +564,38 @@ async function cmdStatus() {
544
564
 
545
565
  async function cmdConfig() {
546
566
  const folder = path.resolve(arg || process.cwd());
547
- const lock = await readLock(folder);
548
- const token = lock?.installToken || 'YOUR_INSTALL_TOKEN';
549
567
  const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
568
+ const authToken = flags['auth-token'] || process.env.VIBESECUR_AUTH_TOKEN || '';
550
569
 
551
570
  console.log(`\n${h('Vibesecur MCP - IDE Config Snippets')}`);
571
+
572
+ if (isUniversalMode(flags, authToken)) {
573
+ if (!authToken) {
574
+ console.log(err('Universal config requires VIBESECUR_AUTH_TOKEN or --auth-token=<jwt>'));
575
+ process.exit(1);
576
+ }
577
+ console.log(`Mode: ${BOLD}universal (account-wide)${RESET}`);
578
+ console.log(`API: ${apiBase}\n`);
579
+ const sc = serverCmd();
580
+ printIdeBlocks(sc, buildUniversalEnv(apiBase, authToken), { universal: true });
581
+ return;
582
+ }
583
+
584
+ console.log(`Mode: ${BOLD}legacy (single folder)${RESET}`);
552
585
  console.log(`Folder: ${folder}`);
553
- if (!lock) console.log(err(`No lock found. Run: vibesecur-mcp bind ${folder}`));
586
+ const lock = await readLock(folder);
587
+ const token = lock?.installToken || 'YOUR_INSTALL_TOKEN';
588
+ if (!lock) console.log(err(`No lock found. Run: vibesecur-mcp init ${folder} --mode=legacy`));
554
589
  else if (!isRuntimeCompatibleLock(lock)) {
555
590
  console.log(err('Existing lock is local diagnostic-only and will not start the MCP runtime.'));
556
- console.log(`Run: VIBESECUR_AUTH_TOKEN=<jwt> vibesecur-mcp bind ${folder}`);
591
+ console.log(`Run: VIBESECUR_AUTH_TOKEN=<jwt> vibesecur-mcp init --mode=universal`);
557
592
  console.log();
558
593
  return;
559
594
  }
560
595
  console.log();
561
596
 
562
- const env = buildEnv(folder, token, apiBase);
563
597
  const sc = serverCmd();
564
- printIdeBlocks(sc, env);
598
+ printIdeBlocks(sc, buildEnv(folder, token, apiBase), { universal: false });
565
599
  }
566
600
 
567
601
  async function cmdInit() {
@@ -570,13 +604,23 @@ async function cmdInit() {
570
604
  const account = process.env.VIBESECUR_ACCOUNT || 'anonymous';
571
605
  const skipBind = flags['skip-bind'] === true || flags['skip-bind'] === 'true';
572
606
  const canFallbackLocal = allowLocalFallback();
607
+ const authToken = flags['auth-token'] || process.env.VIBESECUR_AUTH_TOKEN || '';
608
+ const write = flags.write;
573
609
 
574
610
  console.log(`\n${h('Vibesecur MCP - Init')}`);
575
611
  console.log(`Project folder: ${folder}`);
576
612
  console.log(`API base: ${apiBase}`);
577
- console.log(`Package: ${NPM_PACKAGE_NAME}\n`);
613
+ console.log(`Package: ${NPM_PACKAGE_NAME}@${mcpPkg.version}\n`);
578
614
 
579
- const authToken = flags['auth-token'] || process.env.VIBESECUR_AUTH_TOKEN || '';
615
+ if (isUniversalMode(flags, authToken)) {
616
+ if (!authToken) {
617
+ console.log(err('Universal init requires VIBESECUR_AUTH_TOKEN or --auth-token=<jwt> from the dashboard login.'));
618
+ console.log(dim('Example: npx -y @saiteja1123/mcp-server init --auth-token=<jwt> --write=all'));
619
+ process.exit(1);
620
+ }
621
+ await runUniversalSetup({ apiBase, authToken, folder, write });
622
+ return;
623
+ }
580
624
 
581
625
  let lock = await readLock(folder);
582
626
  if (lock && !isRuntimeCompatibleLock(lock) && !skipBind) {
@@ -615,16 +659,15 @@ async function cmdInit() {
615
659
  const sc = serverCmd();
616
660
 
617
661
  console.log(`\n${BOLD}Next:${RESET} paste ONE block below into your IDE MCP settings, then reload the IDE.\n`);
618
- printIdeBlocks(sc, env);
662
+ printIdeBlocks(sc, env, { universal: false });
619
663
 
620
- const write = flags.write;
621
664
  if (write) {
622
665
  const w = String(write).toLowerCase();
623
- if (!['cursor', 'vscode', 'windsurf', 'all'].includes(w)) {
624
- console.log(err(`Invalid --write=${write}. Use: cursor | vscode | windsurf | all`));
666
+ if (!['cursor', 'vscode', 'windsurf', 'claude', 'all'].includes(w)) {
667
+ console.log(err(`Invalid --write=${write}. Use: cursor | vscode | windsurf | claude | all`));
625
668
  process.exit(1);
626
669
  }
627
- await writeIdeConfig(w, sc, env);
670
+ await writeIdeConfig(w, sc, env, folder);
628
671
  console.log(dim('Reload your IDE so it picks up the new MCP config.'));
629
672
  }
630
673
  }
@@ -632,28 +675,42 @@ async function cmdInit() {
632
675
  async function cmdDoctor() {
633
676
  const folder = path.resolve(arg || process.cwd());
634
677
  const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
678
+ const authToken = flags['auth-token'] || process.env.VIBESECUR_AUTH_TOKEN || '';
679
+ const universal = isUniversalMode(flags, authToken);
635
680
 
636
681
  console.log(`\n${h('Vibesecur MCP - Doctor')}`);
637
682
  console.log(`Folder: ${folder}`);
638
- console.log(`API base: ${apiBase}\n`);
683
+ console.log(`API base: ${apiBase}`);
684
+ console.log(`Mode: ${universal ? 'universal (account-wide)' : 'legacy (single folder)'}\n`);
639
685
 
640
- const lock = await readLock(folder);
641
- if (!lock) {
642
- console.log(err('No lock file. Run: vibesecur-mcp init'));
686
+ if (universal) {
687
+ if (authToken) {
688
+ console.log(ok('VIBESECUR_AUTH_TOKEN present (universal mode ready)'));
689
+ } else {
690
+ console.log(err('VIBESECUR_AUTH_TOKEN missing — set it in IDE MCP env or pass --auth-token=<jwt>'));
691
+ }
692
+ console.log(dim('Per-project locks are optional in universal mode. Use projectUpsert before scanning a codebase.\n'));
643
693
  } else {
644
- console.log(ok('Lock file present'));
645
- const diag = await diagnosticLock(folder);
646
- console.log(diag.healthy ? ok('Lock diagnostic: healthy') : err('Lock diagnostic: issues'));
647
- console.log(diag.runtimeCompatible ? ok('Runtime binding: server-issued') : err('Runtime binding: not server-issued'));
648
- if (diag.issues?.length) diag.issues.forEach((i) => console.log(` - ${i}`));
694
+ const lock = await readLock(folder);
695
+ if (!lock) {
696
+ console.log(err('No lock file. Run: vibesecur-mcp init --auth-token=<jwt> for universal mode'));
697
+ } else {
698
+ console.log(ok('Lock file present'));
699
+ const diag = await diagnosticLock(folder);
700
+ console.log(diag.healthy ? ok('Lock diagnostic: healthy') : err('Lock diagnostic: issues'));
701
+ console.log(diag.runtimeCompatible ? ok('Runtime binding: server-issued') : err('Runtime binding: not server-issued'));
702
+ if (diag.issues?.length) diag.issues.forEach((i) => console.log(` - ${i}`));
703
+ }
649
704
  }
650
705
 
651
706
  const tokenEnv = process.env.VIBESECUR_INSTALL_TOKEN;
652
707
  const rootEnv = process.env.VIBESECUR_BOUND_ROOT;
653
- console.log(`\n${BOLD}Shell env (optional; IDE sets these when MCP runs):${RESET}`);
654
- console.log(` VIBESECUR_INSTALL_TOKEN: ${tokenEnv ? dim('set') : dim('not set in this shell (normal)')}`);
655
- console.log(` VIBESECUR_BOUND_ROOT: ${rootEnv ? dim('set') : dim('not set in this shell (normal)')}`);
656
- console.log(` VIBESECUR_API_BASE: ${process.env.VIBESECUR_API_BASE ? dim(process.env.VIBESECUR_API_BASE) : dim(`${DEFAULT_API_BASE} (default)`)}`);
708
+ const authEnv = process.env.VIBESECUR_AUTH_TOKEN;
709
+ console.log(`\n${BOLD}Shell env (IDE injects these when MCP runs):${RESET}`);
710
+ console.log(` VIBESECUR_AUTH_TOKEN: ${authEnv ? dim('set') : dim('not set in this shell')}`);
711
+ console.log(` VIBESECUR_INSTALL_TOKEN: ${tokenEnv ? dim('set') : dim('not set (OK in universal mode)')}`);
712
+ console.log(` VIBESECUR_BOUND_ROOT: ${rootEnv ? dim('set') : dim('not set (OK in universal mode)')}`);
713
+ console.log(` VIBESECUR_API_BASE: ${process.env.VIBESECUR_API_BASE ? dim(process.env.VIBESECUR_API_BASE) : dim(`${DEFAULT_API_BASE} (default)`)}`);
657
714
 
658
715
  const ping = await pingBackend(apiBase);
659
716
  if (ping.skipped) {
@@ -669,18 +726,19 @@ async function cmdDoctor() {
669
726
  }
670
727
 
671
728
  console.log(`\n${BOLD}CLI:${RESET} ${NPM_PACKAGE_NAME}@${mcpPkg.version}`);
672
- console.log(dim('If IDE still fails: run init again, paste fresh config, reload IDE.\n'));
729
+ console.log(dim('Universal setup: npx -y @saiteja1123/mcp-server init --auth-token=<jwt> --write=all --api-base=URL'));
730
+ console.log(dim('If IDE still fails: reload IDE after pasting config, then run projectUpsert for your codebase.\n'));
673
731
  }
674
732
 
675
733
  function usage() {
676
734
  console.log(`\n${h('Vibesecur MCP')}`);
677
- console.log(` ${dim('Quick start:')} vibesecur-mcp init`);
678
- console.log(' init [folder] server-bind + print IDE configs [--api-base=URL] [--auth-token=JWT] [--write=cursor|vscode|windsurf|all] [--skip-bind] [--allow-local-fallback=true]');
679
- console.log(' doctor [folder] check lock + backend [--api-base=URL]');
680
- console.log(' bind <folder> server-issued bind [--api-base=URL] [--auth-token=JWT] [--allow-local-fallback=true]');
681
- console.log(' rebind <folder> rotate token via server [--api-base=URL] [--auth-token=JWT] [--allow-local-fallback=true]');
682
- console.log(' status [folder] lock health');
683
- console.log(' config [folder] print snippets only');
735
+ console.log(` ${dim('Quick start (universal):')} VIBESECUR_AUTH_TOKEN=<jwt> npx -y ${NPM_PACKAGE_NAME} init --write=all`);
736
+ console.log(' init [folder] print IDE configs [--auth-token=JWT] [--api-base=URL] [--mode=universal|legacy] [--write=cursor|vscode|windsurf|claude|all]');
737
+ console.log(' doctor [folder] check env + backend [--auth-token=JWT] [--api-base=URL] [--mode=universal]');
738
+ console.log(' bind <folder> legacy single-folder bind [--auth-token=JWT]');
739
+ console.log(' rebind <folder> rotate legacy folder token');
740
+ console.log(' status [folder] lock health (legacy mode)');
741
+ console.log(' config [folder] print IDE snippets [--auth-token=JWT] [--mode=universal]');
684
742
  console.log(' deep-scan-start [folder] create and run local Deep Scan Project Map, deterministic scan, metadata-only test plan, Ralph tasks, and rerun comparison [--demo=true] [--human-approval=true] [--previous-run-id=<runId>] [--previous-scan-uri=<uri> --previous-scan-hash=<sha256>]');
685
743
  console.log(' deep-scan-status <runId> [folder] inspect local Deep Scan status');
686
744
  console.log(' deep-scan-approve <runId> [folder] record approval metadata');
@@ -0,0 +1,161 @@
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import { createRequire } from 'module';
4
+
5
+ const require = createRequire(import.meta.url);
6
+ const mcpPkg = require('../package.json');
7
+
8
+ export const NPM_PACKAGE_NAME = mcpPkg.name || '@saiteja1123/mcp-server';
9
+ export const DEFAULT_API_BASE = 'https://vibesecur.onrender.com';
10
+ export const PUBLISHED_SERVER_ARGS = ['-y', `${NPM_PACKAGE_NAME}@${mcpPkg.version}`, 'start'];
11
+
12
+ /** Stdio MCP launch via pinned npm package (works in every IDE). */
13
+ export function publishedServerCmd() {
14
+ return { command: 'npx', args: [...PUBLISHED_SERVER_ARGS] };
15
+ }
16
+
17
+ /** Legacy single-folder install env. */
18
+ export function buildLegacyEnv(folder, installToken, apiBase) {
19
+ return {
20
+ VIBESECUR_INSTALL_TOKEN: installToken,
21
+ VIBESECUR_BOUND_ROOT: folder,
22
+ VIBESECUR_API_BASE: apiBase || DEFAULT_API_BASE,
23
+ };
24
+ }
25
+
26
+ /** Account-wide universal install env — one config for all projects/IDEs. */
27
+ export function buildUniversalEnv(apiBase, authToken = '<paste-auth-token-from-dashboard>') {
28
+ const env = {
29
+ VIBESECUR_API_BASE: apiBase || DEFAULT_API_BASE,
30
+ VIBESECUR_AUTH_TOKEN: authToken,
31
+ };
32
+ return env;
33
+ }
34
+
35
+ export function isUniversalMode(flags = {}, authToken = '') {
36
+ const mode = String(flags.mode || flags['config-mode'] || '').toLowerCase();
37
+ if (mode === 'legacy' || mode === 'folder') return false;
38
+ if (mode === 'universal' || mode === 'account') return true;
39
+ return !!authToken;
40
+ }
41
+
42
+ export function buildMcpServerEntry(sc, env) {
43
+ return { command: sc.command, args: sc.args, env };
44
+ }
45
+
46
+ export function buildCursorConfig(sc, env) {
47
+ return { mcpServers: { vibesecur: buildMcpServerEntry(sc, env) } };
48
+ }
49
+
50
+ export function buildWindsurfConfig(sc, env) {
51
+ return { mcpServers: { vibesecur: buildMcpServerEntry(sc, env) } };
52
+ }
53
+
54
+ export function buildClaudeDesktopConfig(sc, env) {
55
+ return { mcpServers: { vibesecur: buildMcpServerEntry(sc, env) } };
56
+ }
57
+
58
+ export function buildVsCodeMcpConfig(sc, env) {
59
+ return {
60
+ servers: {
61
+ vibesecur: { type: 'stdio', command: sc.command, args: sc.args, env },
62
+ },
63
+ };
64
+ }
65
+
66
+ export function buildContinueMcpConfig(sc, env) {
67
+ return {
68
+ experimental: {
69
+ modelContextProtocolServers: [
70
+ {
71
+ name: 'vibesecur',
72
+ transport: {
73
+ type: 'stdio',
74
+ command: sc.command,
75
+ args: sc.args,
76
+ env,
77
+ },
78
+ },
79
+ ],
80
+ },
81
+ };
82
+ }
83
+
84
+ export const IDE_CONFIG_TARGETS = {
85
+ cursor: {
86
+ label: 'Cursor (~/.cursor/mcp.json)',
87
+ path: () => path.join(os.homedir(), '.cursor', 'mcp.json'),
88
+ build: buildCursorConfig,
89
+ mergeKey: 'mcpServers',
90
+ entryKey: 'vibesecur',
91
+ },
92
+ windsurf: {
93
+ label: 'Windsurf (~/.codeium/windsurf/mcp_config.json)',
94
+ path: () => path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
95
+ build: buildWindsurfConfig,
96
+ mergeKey: 'mcpServers',
97
+ entryKey: 'vibesecur',
98
+ },
99
+ claude: {
100
+ label: 'Claude Desktop (%APPDATA%/Claude/claude_desktop_config.json)',
101
+ path: () => path.join(
102
+ process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'),
103
+ 'Claude',
104
+ 'claude_desktop_config.json',
105
+ ),
106
+ build: buildClaudeDesktopConfig,
107
+ mergeKey: 'mcpServers',
108
+ entryKey: 'vibesecur',
109
+ },
110
+ vscode: {
111
+ label: 'VS Code (project .vscode/mcp.json)',
112
+ path: (folder) => path.join(folder, '.vscode', 'mcp.json'),
113
+ build: buildVsCodeMcpConfig,
114
+ mergeKey: 'servers',
115
+ entryKey: 'vibesecur',
116
+ needsFolder: true,
117
+ },
118
+ };
119
+
120
+ export function formatIdeConfigBlocks(sc, env, { universal = false } = {}) {
121
+ const lines = [];
122
+ const note = universal
123
+ ? 'Universal account config — works across projects. Use projectUpsert before scanning a new codebase.'
124
+ : 'Single-folder legacy config — bound to one project root.';
125
+
126
+ lines.push({ title: 'Mode', body: note });
127
+
128
+ for (const [id, target] of Object.entries(IDE_CONFIG_TARGETS)) {
129
+ if (id === 'vscode') {
130
+ lines.push({
131
+ id,
132
+ title: target.label,
133
+ body: JSON.stringify(target.build(sc, env), null, 2),
134
+ });
135
+ continue;
136
+ }
137
+ lines.push({
138
+ id,
139
+ title: target.label,
140
+ body: JSON.stringify(target.build(sc, env), null, 2),
141
+ });
142
+ }
143
+
144
+ lines.push({
145
+ id: 'continue',
146
+ title: 'Continue.dev (~/.continue/config.json fragment)',
147
+ body: JSON.stringify(buildContinueMcpConfig(sc, env), null, 2),
148
+ });
149
+
150
+ lines.push({
151
+ id: 'generic',
152
+ title: 'Generic stdio MCP client',
153
+ body: [
154
+ `command: ${sc.command}`,
155
+ `args: ${sc.args.join(' ')}`,
156
+ ...Object.entries(env).map(([k, v]) => `${k}=${v}`),
157
+ ].join('\n'),
158
+ });
159
+
160
+ return lines;
161
+ }