@saiteja1123/mcp-server 1.1.2 → 1.1.3

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.2",
3
+ "version": "1.1.3",
4
4
  "private": false,
5
5
  "description": "Vibesecur MCP security scanner - one-folder locking, cross-IDE, Cursor/VSCode/Windsurf",
6
6
  "type": "module",
@@ -16,6 +16,8 @@
16
16
  "start": "node ./src/server.js",
17
17
  "dev": "node --watch ./src/server.js",
18
18
  "bind": "node ./src/cli.js bind",
19
+ "init": "node ./src/cli.js init",
20
+ "doctor": "node ./src/cli.js doctor",
19
21
  "test": "node --input-type=module --eval \"import('./src/server.js')\""
20
22
  },
21
23
  "exports": {
package/src/api-scan.mjs CHANGED
@@ -19,12 +19,47 @@ export function getProjectHashForPath(rootPath) {
19
19
  return sha256Hex(`vibesecur:mcp:${resolved}`);
20
20
  }
21
21
 
22
- function normalizeApiBase(raw) {
22
+ export function normalizeApiBase(raw) {
23
23
  if (!raw || typeof raw !== 'string') return '';
24
24
  const u = raw.trim().replace(/\/$/, '');
25
25
  return u.endsWith('/api/v1') ? u : `${u}/api/v1`;
26
26
  }
27
27
 
28
+ /** GET origin (no /api/v1) for lightweight health checks. */
29
+ export function getApiOriginFromBase(raw) {
30
+ const normalized = normalizeApiBase(raw);
31
+ if (!normalized) return '';
32
+ return normalized.replace(/\/api\/v1\/?$/, '') || normalized;
33
+ }
34
+
35
+ /**
36
+ * Quick reachability check (GET app root). Does not run a scan.
37
+ * @param {string} [baseUrl] - e.g. https://vibesecur.onrender.com (optional /api/v1 ok)
38
+ */
39
+ export async function pingBackend(baseUrl) {
40
+ const raw = (
41
+ baseUrl
42
+ || process.env.VIBESECUR_API_BASE
43
+ || process.env.VIBESECUR_API_URL
44
+ || 'https://vibesecur.onrender.com'
45
+ ).trim();
46
+ const origin = getApiOriginFromBase(raw);
47
+ if (!origin) {
48
+ return { ok: false, skipped: true, message: 'No API base URL' };
49
+ }
50
+ const url = origin.endsWith('/') ? origin.slice(0, -1) : origin;
51
+ try {
52
+ const ctrl = typeof AbortSignal !== 'undefined' && AbortSignal.timeout
53
+ ? AbortSignal.timeout(12000)
54
+ : undefined;
55
+ const res = await fetch(`${url}/`, { method: 'GET', signal: ctrl });
56
+ const ok = true;
57
+ return { ok, status: res.status, url: `${url}/` };
58
+ } catch (e) {
59
+ return { ok: false, error: e.message };
60
+ }
61
+ }
62
+
28
63
  export async function postRemoteLocalScan({
29
64
  code,
30
65
  lang = 'auto',
package/src/cli.js CHANGED
@@ -1,19 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * vibesecur-mcp CLI
4
- * Usage:
5
- * vibesecur-mcp bind <folder>
6
- * vibesecur-mcp rebind <folder>
7
- * vibesecur-mcp status [folder]
8
- * vibesecur-mcp config <folder>
9
- * vibesecur-mcp start
4
+ *
5
+ * Quick start: npx -y @saiteja1123/mcp-server init
6
+ *
7
+ * Commands:
8
+ * init [folder] bind folder + print IDE configs (optional --write=...)
9
+ * doctor [folder] check lock, env hints, backend reachability
10
+ * bind <folder>
11
+ * rebind <folder>
12
+ * status [folder]
13
+ * config [folder]
14
+ * start MCP stdio server
15
+ *
16
+ * Flags (init): --api-base=URL --skip-bind --write=cursor|vscode|windsurf|all
10
17
  */
11
18
 
19
+ import fs from 'fs/promises';
12
20
  import path from 'path';
21
+ import os from 'os';
22
+ import { createRequire } from 'module';
13
23
  import { fileURLToPath } from 'url';
14
24
  import { createLock, rebindLock, diagnosticLock, readLock } from './lock.mjs';
25
+ import { pingBackend } from './api-scan.mjs';
15
26
 
16
- const [, , command, arg] = process.argv;
27
+ const require = createRequire(import.meta.url);
28
+ const mcpPkg = require('../package.json');
29
+ const NPM_PACKAGE_NAME = mcpPkg.name || '@saiteja1123/mcp-server';
30
+ const DEFAULT_API_BASE = 'https://vibesecur.onrender.com';
31
+
32
+ const serverPath = fileURLToPath(new URL('./server.js', import.meta.url));
17
33
 
18
34
  const BOLD = '\x1b[1m';
19
35
  const GREEN = '\x1b[32m';
@@ -27,15 +43,106 @@ function ok(t) { return `${GREEN}✓ ${t}${RESET}`; }
27
43
  function err(t) { return `${RED}✗ ${t}${RESET}`; }
28
44
  function dim(t) { return `${DIM}${t}${RESET}`; }
29
45
 
30
- const serverPath = fileURLToPath(new URL('./server.js', import.meta.url));
46
+ function parseArgv(argv) {
47
+ const flags = {};
48
+ const positional = [];
49
+ for (const a of argv) {
50
+ if (a.startsWith('--')) {
51
+ const eq = a.indexOf('=');
52
+ if (eq === -1) flags[a.slice(2)] = true;
53
+ else flags[a.slice(2, eq)] = a.slice(eq + 1);
54
+ } else {
55
+ positional.push(a);
56
+ }
57
+ }
58
+ return { flags, positional };
59
+ }
60
+
61
+ const { flags, positional } = parseArgv(process.argv.slice(2));
62
+ const command = positional[0];
63
+ const arg = positional[1];
31
64
 
32
65
  function serverCmd() {
33
- if (serverPath.includes('node_modules')) {
34
- return { command: 'npx', args: ['-y', '@vibesecur/mcp-server', 'start'] };
66
+ const normalizedPath = serverPath.replace(/\\/g, '/');
67
+ if (normalizedPath.includes('/node_modules/')) {
68
+ return { command: 'npx', args: ['-y', NPM_PACKAGE_NAME, 'start'] };
35
69
  }
36
70
  return { command: 'node', args: [serverPath] };
37
71
  }
38
72
 
73
+ function buildEnv(folder, token, apiBase) {
74
+ return {
75
+ VIBESECUR_INSTALL_TOKEN: token,
76
+ VIBESECUR_BOUND_ROOT: folder,
77
+ VIBESECUR_API_BASE: apiBase || DEFAULT_API_BASE,
78
+ };
79
+ }
80
+
81
+ function printIdeBlocks(sc, env) {
82
+ console.log(`${BOLD}-- Cursor (~/.cursor/mcp.json) --${RESET}`);
83
+ console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
84
+
85
+ console.log(`\n${BOLD}-- VS Code (project .vscode/mcp.json) --${RESET}`);
86
+ console.log(JSON.stringify({ servers: { vibesecur: { type: 'stdio', command: sc.command, args: sc.args, env } } }, null, 2));
87
+
88
+ console.log(`\n${BOLD}-- Windsurf (~/.codeium/windsurf/mcp_config.json) --${RESET}`);
89
+ console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
90
+
91
+ console.log(`\n${BOLD}-- Generic env (paste into your MCP client) --${RESET}`);
92
+ console.log(`command: ${sc.command} ${sc.args.join(' ')}`);
93
+ Object.entries(env).forEach(([k, v]) => console.log(` ${k}=${v}`));
94
+ console.log();
95
+ }
96
+
97
+ async function mergeJsonFile(filePath, merger) {
98
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
99
+ let data = {};
100
+ try {
101
+ const raw = await fs.readFile(filePath, 'utf8');
102
+ data = JSON.parse(raw);
103
+ } catch {
104
+ data = {};
105
+ }
106
+ const next = merger(data);
107
+ await fs.writeFile(filePath, JSON.stringify(next, null, 2), 'utf8');
108
+ }
109
+
110
+ async function writeIdeConfig(which, sc, env) {
111
+ const entry = { command: sc.command, args: sc.args, env };
112
+ const vscodeEntry = { type: 'stdio', command: sc.command, args: sc.args, env };
113
+
114
+ if (which === 'cursor' || which === 'all') {
115
+ const file = path.join(os.homedir(), '.cursor', 'mcp.json');
116
+ await mergeJsonFile(file, (data) => {
117
+ const out = { ...data };
118
+ out.mcpServers = { ...(out.mcpServers || {}), vibesecur: entry };
119
+ return out;
120
+ });
121
+ console.log(ok(`Wrote Cursor MCP config: ${file}`));
122
+ }
123
+
124
+ if (which === 'vscode' || which === 'all') {
125
+ const folder = env.VIBESECUR_BOUND_ROOT;
126
+ const file = path.join(folder, '.vscode', 'mcp.json');
127
+ await mergeJsonFile(file, (data) => {
128
+ const out = { ...data };
129
+ out.servers = { ...(out.servers || {}), vibesecur: vscodeEntry };
130
+ return out;
131
+ });
132
+ console.log(ok(`Wrote VS Code MCP config: ${file}`));
133
+ }
134
+
135
+ if (which === 'windsurf' || which === 'all') {
136
+ const file = path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
137
+ await mergeJsonFile(file, (data) => {
138
+ const out = { ...data };
139
+ out.mcpServers = { ...(out.mcpServers || {}), vibesecur: entry };
140
+ return out;
141
+ });
142
+ console.log(ok(`Wrote Windsurf MCP config: ${file}`));
143
+ }
144
+ }
145
+
39
146
  async function cmdBind() {
40
147
  const folder = path.resolve(arg || process.cwd());
41
148
  const account = process.env.VIBESECUR_ACCOUNT || 'anonymous';
@@ -89,46 +196,118 @@ async function cmdConfig() {
89
196
  const folder = path.resolve(arg || process.cwd());
90
197
  const lock = await readLock(folder);
91
198
  const token = lock?.installToken || 'YOUR_INSTALL_TOKEN';
199
+ const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
92
200
 
93
201
  console.log(`\n${h('Vibesecur MCP - IDE Config Snippets')}`);
94
202
  console.log(`Folder: ${folder}`);
95
203
  if (!lock) console.log(err(`No lock found. Run: vibesecur-mcp bind ${folder}`));
96
204
  console.log();
97
205
 
98
- const env = {
99
- VIBESECUR_INSTALL_TOKEN: token,
100
- VIBESECUR_BOUND_ROOT: folder,
101
- VIBESECUR_API_BASE: 'https://api.vibesecur.com',
102
- };
206
+ const env = buildEnv(folder, token, apiBase);
103
207
  const sc = serverCmd();
208
+ printIdeBlocks(sc, env);
209
+ }
104
210
 
105
- console.log(`${BOLD}-- Cursor (.cursor/mcp.json) --${RESET}`);
106
- console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
211
+ async function cmdInit() {
212
+ const folder = path.resolve(arg || process.cwd());
213
+ const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
214
+ const account = process.env.VIBESECUR_ACCOUNT || 'anonymous';
215
+ const skipBind = flags['skip-bind'] === true || flags['skip-bind'] === 'true';
107
216
 
108
- console.log(`\n${BOLD}-- VS Code (.vscode/mcp.json) --${RESET}`);
109
- console.log(JSON.stringify({ servers: { vibesecur: { type: 'stdio', command: sc.command, args: sc.args, env } } }, null, 2));
217
+ console.log(`\n${h('Vibesecur MCP - Init')}`);
218
+ console.log(`Project folder: ${folder}`);
219
+ console.log(`API base: ${apiBase}`);
220
+ console.log(`Package: ${NPM_PACKAGE_NAME}\n`);
110
221
 
111
- console.log(`\n${BOLD}-- Windsurf (~/.codeium/windsurf/mcp_config.json) --${RESET}`);
112
- console.log(JSON.stringify({ mcpServers: { vibesecur: { command: sc.command, args: sc.args, env } } }, null, 2));
222
+ let lock = await readLock(folder);
223
+ if (!lock) {
224
+ if (skipBind) {
225
+ console.log(err('No lock in this folder. Run without --skip-bind, or: vibesecur-mcp bind <folder>'));
226
+ process.exit(1);
227
+ }
228
+ lock = await createLock({ rootPath: folder, account });
229
+ console.log(ok(`Created lock: ${path.join(folder, '.vibesecur', 'lock.json')}`));
230
+ } else {
231
+ console.log(ok('Existing lock found (reusing token). Use rebind to rotate.'));
232
+ }
113
233
 
114
- console.log(`\n${BOLD}-- Generic / Claude Desktop / Continue.dev --${RESET}`);
115
- console.log(`command: ${sc.command} ${sc.args.join(' ')}`);
116
- Object.entries(env).forEach(([k, v]) => console.log(` ${k}=${v}`));
117
- console.log();
234
+ const env = buildEnv(folder, lock.installToken, apiBase);
235
+ const sc = serverCmd();
236
+
237
+ console.log(`\n${BOLD}Next:${RESET} paste ONE block below into your IDE MCP settings, then reload the IDE.\n`);
238
+ printIdeBlocks(sc, env);
239
+
240
+ const write = flags.write;
241
+ if (write) {
242
+ const w = String(write).toLowerCase();
243
+ if (!['cursor', 'vscode', 'windsurf', 'all'].includes(w)) {
244
+ console.log(err(`Invalid --write=${write}. Use: cursor | vscode | windsurf | all`));
245
+ process.exit(1);
246
+ }
247
+ await writeIdeConfig(w, sc, env);
248
+ console.log(dim('Reload your IDE so it picks up the new MCP config.'));
249
+ }
250
+ }
251
+
252
+ async function cmdDoctor() {
253
+ const folder = path.resolve(arg || process.cwd());
254
+ const apiBase = (flags['api-base'] || process.env.VIBESECUR_API_BASE || DEFAULT_API_BASE).trim();
255
+
256
+ console.log(`\n${h('Vibesecur MCP - Doctor')}`);
257
+ console.log(`Folder: ${folder}`);
258
+ console.log(`API base: ${apiBase}\n`);
259
+
260
+ const lock = await readLock(folder);
261
+ if (!lock) {
262
+ console.log(err('No lock file. Run: vibesecur-mcp init'));
263
+ } else {
264
+ console.log(ok('Lock file present'));
265
+ const diag = await diagnosticLock(folder);
266
+ console.log(diag.healthy ? ok('Lock diagnostic: healthy') : err('Lock diagnostic: issues'));
267
+ if (diag.issues?.length) diag.issues.forEach((i) => console.log(` - ${i}`));
268
+ }
269
+
270
+ const tokenEnv = process.env.VIBESECUR_INSTALL_TOKEN;
271
+ const rootEnv = process.env.VIBESECUR_BOUND_ROOT;
272
+ console.log(`\n${BOLD}Shell env (optional; IDE sets these when MCP runs):${RESET}`);
273
+ console.log(` VIBESECUR_INSTALL_TOKEN: ${tokenEnv ? dim('set') : dim('not set in this shell (normal)')}`);
274
+ console.log(` VIBESECUR_BOUND_ROOT: ${rootEnv ? dim('set') : dim('not set in this shell (normal)')}`);
275
+ console.log(` VIBESECUR_API_BASE: ${process.env.VIBESECUR_API_BASE ? dim(process.env.VIBESECUR_API_BASE) : dim(`${DEFAULT_API_BASE} (default)`)}`);
276
+
277
+ const ping = await pingBackend(apiBase);
278
+ if (ping.skipped) {
279
+ console.log(`\n${err('Backend ping skipped')}`);
280
+ } else if (ping.ok) {
281
+ const note = ping.status >= 400 ? ' (server responded; check path if unexpected)' : '';
282
+ console.log(`\n${ok(`Backend responded HTTP ${ping.status}${note}`)}`);
283
+ console.log(dim(` ${ping.url || ''}`));
284
+ } else {
285
+ console.log(`\n${err('Backend not reachable')}`);
286
+ if (ping.status !== undefined) console.log(` HTTP ${ping.status}`);
287
+ if (ping.error) console.log(` ${ping.error}`);
288
+ }
289
+
290
+ console.log(`\n${BOLD}CLI:${RESET} ${NPM_PACKAGE_NAME}@${mcpPkg.version}`);
291
+ console.log(dim('If IDE still fails: run init again, paste fresh config, reload IDE.\n'));
118
292
  }
119
293
 
120
294
  function usage() {
121
295
  console.log(`\n${h('Vibesecur MCP')}`);
122
- console.log(' bind <folder> bind install to a project folder');
123
- console.log(' rebind <folder> revoke old token, issue new lock');
124
- console.log(' status [folder] check lock health');
125
- console.log(' config <folder> print IDE config snippets');
126
- console.log(' start start MCP server (stdio)\n');
296
+ console.log(` ${dim('Quick start:')} vibesecur-mcp init`);
297
+ console.log(' init [folder] bind + print IDE configs [--api-base=URL] [--write=cursor|vscode|windsurf|all] [--skip-bind]');
298
+ console.log(' doctor [folder] check lock + backend [--api-base=URL]');
299
+ console.log(' bind <folder> create lock only');
300
+ console.log(' rebind <folder> rotate token');
301
+ console.log(' status [folder] lock health');
302
+ console.log(' config [folder] print snippets only');
303
+ console.log(' start MCP server (stdio)\n');
127
304
  process.exit(1);
128
305
  }
129
306
 
130
307
  try {
131
308
  switch (command) {
309
+ case 'init': await cmdInit(); break;
310
+ case 'doctor': await cmdDoctor(); break;
132
311
  case 'bind': await cmdBind(); break;
133
312
  case 'rebind': await cmdRebind(); break;
134
313
  case 'status': await cmdStatus(); break;