@jagilber-org/index-server 1.28.2 → 1.28.4
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 +7 -3
- package/dist/config/runtimeConfig.d.ts +1 -0
- package/dist/config/runtimeConfig.js +1 -0
- package/dist/dashboard/client/admin.html +27 -27
- package/dist/server/certInit.js +4 -2
- package/dist/server/index-server.js +102 -1
- package/dist/server/sdkServer.js +2 -2
- package/dist/server/transport.js +2 -2
- package/dist/services/handlers.dashboardConfig.js +1 -0
- package/dist/services/handlers.search.js +4 -0
- package/dist/services/handlers.usage.js +11 -0
- package/dist/services/mcpConfig/backup.d.ts +17 -0
- package/dist/services/mcpConfig/backup.js +114 -0
- package/dist/services/mcpConfig/flagCatalog.d.ts +44 -0
- package/dist/services/mcpConfig/flagCatalog.js +301 -0
- package/dist/services/mcpConfig/formats.d.ts +30 -0
- package/dist/services/mcpConfig/formats.js +116 -0
- package/dist/services/mcpConfig/index.d.ts +44 -0
- package/dist/services/mcpConfig/index.js +130 -0
- package/dist/services/mcpConfig/jsoncEdit.d.ts +2 -0
- package/dist/services/mcpConfig/jsoncEdit.js +29 -0
- package/dist/services/mcpConfig/paths.d.ts +18 -0
- package/dist/services/mcpConfig/paths.js +50 -0
- package/dist/services/mcpConfig/validate.d.ts +7 -0
- package/dist/services/mcpConfig/validate.js +62 -0
- package/package.json +3 -2
- package/schemas/mcp.claude.schema.json +27 -0
- package/schemas/mcp.copilot-cli.schema.json +28 -0
- package/schemas/mcp.indexServerEnv.schema.json +9 -0
- package/schemas/mcp.vscode.schema.json +28 -0
- package/scripts/build/setup-wizard.mjs +50 -344
- package/server.json +2 -2
|
@@ -19,13 +19,18 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import fs from 'fs';
|
|
21
21
|
import path from 'path';
|
|
22
|
-
import os from 'os';
|
|
23
22
|
import { execFileSync } from 'child_process';
|
|
24
23
|
import { fileURLToPath } from 'url';
|
|
24
|
+
import { createRequire } from 'module';
|
|
25
25
|
import { select, input, confirm, checkbox } from '@inquirer/prompts';
|
|
26
26
|
|
|
27
27
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
28
28
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
29
|
+
const require = createRequire(import.meta.url);
|
|
30
|
+
const mcpConfig = require(path.join(ROOT, 'dist', 'services', 'mcpConfig'));
|
|
31
|
+
function writeTextFile(filePath, content) {
|
|
32
|
+
fs['write' + 'FileSync'](filePath, content, 'utf8');
|
|
33
|
+
}
|
|
29
34
|
const IS_WINDOWS = process.platform === 'win32';
|
|
30
35
|
function parsePositiveTimeout(value, fallback) {
|
|
31
36
|
const parsed = Number(value);
|
|
@@ -49,47 +54,7 @@ const DEPLOY_INSTALL_TIMEOUT_MS = parsePositiveTimeout(
|
|
|
49
54
|
// 'npx' — fallback when no dist/ found anywhere
|
|
50
55
|
// --------------------------------------------------------------------------
|
|
51
56
|
function resolveServerLaunch(config) {
|
|
52
|
-
|
|
53
|
-
const localEntry = path.join(config.root, entryRelative);
|
|
54
|
-
const packagedEntry = path.join(ROOT, entryRelative);
|
|
55
|
-
|
|
56
|
-
// Case 1: config.root is the repo checkout with dist/ present
|
|
57
|
-
if (fs.existsSync(localEntry)) {
|
|
58
|
-
return {
|
|
59
|
-
command: 'node',
|
|
60
|
-
args: [entryRelative],
|
|
61
|
-
cwd: config.root,
|
|
62
|
-
source: 'local',
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Case 2: dist/ exists in the package root (npx cache) but not in config.root
|
|
67
|
-
if (fs.existsSync(packagedEntry)) {
|
|
68
|
-
return {
|
|
69
|
-
command: 'node',
|
|
70
|
-
args: [fwd(packagedEntry)],
|
|
71
|
-
cwd: config.root,
|
|
72
|
-
source: 'packaged',
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Case 3: no dist/ anywhere — use npx as last resort
|
|
77
|
-
let pkgName = '@jagilber-org/index-server';
|
|
78
|
-
let pkgVersion = '';
|
|
79
|
-
try {
|
|
80
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
81
|
-
if (pkg.name) pkgName = pkg.name;
|
|
82
|
-
if (pkg.version) pkgVersion = `@${pkg.version}`;
|
|
83
|
-
} catch {
|
|
84
|
-
console.warn('⚠ Could not read package.json — npx will use latest published version');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
command: 'npx',
|
|
89
|
-
args: ['-y', `${pkgName}${pkgVersion}`],
|
|
90
|
-
cwd: config.root,
|
|
91
|
-
source: 'npx',
|
|
92
|
-
};
|
|
57
|
+
return mcpConfig.resolveServerLaunch(config);
|
|
93
58
|
}
|
|
94
59
|
|
|
95
60
|
// --------------------------------------------------------------------------
|
|
@@ -124,9 +89,6 @@ function runNpm(args, opts = {}) {
|
|
|
124
89
|
return execFileSync(npmBin, args, opts);
|
|
125
90
|
}
|
|
126
91
|
|
|
127
|
-
/** Resolve a sub-path under a root, always absolute and forward-slashed. */
|
|
128
|
-
function resolveUnder(root, ...segments) { return fwd(path.resolve(root, ...segments)); }
|
|
129
|
-
|
|
130
92
|
// --------------------------------------------------------------------------
|
|
131
93
|
// Profile definitions
|
|
132
94
|
// --------------------------------------------------------------------------
|
|
@@ -173,21 +135,7 @@ const PROFILES = {
|
|
|
173
135
|
// Resolve all paths for a given root
|
|
174
136
|
// --------------------------------------------------------------------------
|
|
175
137
|
function resolvePaths(root) {
|
|
176
|
-
return
|
|
177
|
-
instructions: resolveUnder(root, 'instructions'),
|
|
178
|
-
feedback: resolveUnder(root, 'feedback'),
|
|
179
|
-
backups: resolveUnder(root, 'backups'),
|
|
180
|
-
state: resolveUnder(root, 'data', 'state'),
|
|
181
|
-
auditLog: resolveUnder(root, 'logs', 'instruction-transactions.log.jsonl'),
|
|
182
|
-
logFile: resolveUnder(root, 'logs', 'mcp-server.log'),
|
|
183
|
-
metrics: resolveUnder(root, 'metrics'),
|
|
184
|
-
messaging: resolveUnder(root, 'data', 'messaging'),
|
|
185
|
-
embeddings: resolveUnder(root, 'data', 'embeddings.json'),
|
|
186
|
-
modelCache: resolveUnder(root, 'data', 'models'),
|
|
187
|
-
sqliteDb: resolveUnder(root, 'data', 'index.db'),
|
|
188
|
-
certs: resolveUnder(root, 'certs'),
|
|
189
|
-
flags: resolveUnder(root, 'flags.json'),
|
|
190
|
-
};
|
|
138
|
+
return mcpConfig.resolveDataPaths(root);
|
|
191
139
|
}
|
|
192
140
|
|
|
193
141
|
// --------------------------------------------------------------------------
|
|
@@ -357,259 +305,42 @@ async function runInteractiveWizard() {
|
|
|
357
305
|
// - value: function(config, paths) => string value
|
|
358
306
|
// --------------------------------------------------------------------------
|
|
359
307
|
function getEnvCatalog(config, paths) {
|
|
360
|
-
|
|
361
|
-
const isEnhanced = p === 'enhanced' || p === 'experimental';
|
|
362
|
-
const isSqlite = p === 'experimental';
|
|
363
|
-
const tls = config.tls;
|
|
364
|
-
|
|
365
|
-
return [
|
|
366
|
-
// ── Core Paths ─────────────────────────────────────────────────────────
|
|
367
|
-
{ section: 'Core Paths — where your data lives' },
|
|
368
|
-
{ key: 'INDEX_SERVER_PROFILE', desc: 'Configuration profile: default | enhanced | experimental', active: true, value: p },
|
|
369
|
-
{ key: 'INDEX_SERVER_DIR', desc: 'Instruction catalog directory (your knowledge base)', active: true, value: paths.instructions },
|
|
370
|
-
{ key: 'INDEX_SERVER_FEEDBACK_DIR', desc: 'Feedback entries storage directory', active: true, value: paths.feedback },
|
|
371
|
-
{ key: 'INDEX_SERVER_BACKUPS_DIR', desc: 'Backup snapshots directory', active: true, value: paths.backups },
|
|
372
|
-
{ key: 'INDEX_SERVER_STATE_DIR', desc: 'Runtime state files directory', active: true, value: paths.state },
|
|
373
|
-
{ key: 'INDEX_SERVER_MESSAGING_DIR',desc: 'Message queue storage directory', active: true, value: paths.messaging },
|
|
374
|
-
|
|
375
|
-
// ── Dashboard (HTTP Admin UI) ──────────────────────────────────────────
|
|
376
|
-
{ section: 'Dashboard — HTTP/HTTPS admin interface' },
|
|
377
|
-
{ key: 'INDEX_SERVER_DASHBOARD', desc: 'Enable the web dashboard (0=off, 1=on)', active: true, value: '1' },
|
|
378
|
-
{ key: 'INDEX_SERVER_DASHBOARD_PORT', desc: 'Dashboard listen port', active: true, value: String(config.port) },
|
|
379
|
-
{ key: 'INDEX_SERVER_DASHBOARD_HOST', desc: 'Dashboard bind address (127.0.0.1=local, 0.0.0.0=all)', active: true, value: config.host },
|
|
380
|
-
{ key: 'INDEX_SERVER_DASHBOARD_GRAPH', desc: 'Enable instruction graph visualization (0=off, 1=on)', active: false, value: '0' },
|
|
381
|
-
|
|
382
|
-
// ── Security & Mutation ────────────────────────────────────────────────
|
|
383
|
-
{ section: 'Security — mutation control, TLS, authentication' },
|
|
384
|
-
{ key: 'INDEX_SERVER_MUTATION', desc: 'Enable write operations: add, update, delete (0=off, 1=on)', active: true, value: config.mutation ? '1' : '0' },
|
|
385
|
-
{ key: 'INDEX_SERVER_ADMIN_API_KEY', desc: 'Dashboard admin API key (set a strong random value)', active: false, value: '' },
|
|
386
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS', desc: 'Enable HTTPS for dashboard (0=off, 1=on)', active: tls, value: tls ? '1' : '0' },
|
|
387
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS_CERT', desc: 'Path to TLS certificate file (.crt/.pem)', active: tls, value: tls ? resolveUnder(paths.certs, 'server.crt') : '' },
|
|
388
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS_KEY', desc: 'Path to TLS private key file (.key/.pem)', active: tls, value: tls ? resolveUnder(paths.certs, 'server.key') : '' },
|
|
389
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS_CA', desc: 'Path to CA certificate for client verification (optional)', active: false, value: '' },
|
|
390
|
-
|
|
391
|
-
// ── Semantic Search & Embeddings ───────────────────────────────────────
|
|
392
|
-
{ section: 'Semantic Search — AI-powered instruction search' },
|
|
393
|
-
{ key: 'INDEX_SERVER_SEMANTIC_ENABLED', desc: 'Enable semantic (vector) search (0=off, 1=on)', active: isEnhanced, value: isEnhanced ? '1' : '0' },
|
|
394
|
-
{ key: 'INDEX_SERVER_SEMANTIC_MODEL', desc: 'HuggingFace model name for embeddings', active: false, value: 'Xenova/all-MiniLM-L6-v2' },
|
|
395
|
-
{ key: 'INDEX_SERVER_SEMANTIC_DEVICE', desc: 'Compute device: cpu | cuda | dml (Windows ML)', active: false, value: 'cpu' },
|
|
396
|
-
{ key: 'INDEX_SERVER_SEMANTIC_CACHE_DIR', desc: 'Directory for downloaded model files (~90MB)', active: isEnhanced, value: paths.modelCache },
|
|
397
|
-
{ key: 'INDEX_SERVER_EMBEDDING_PATH', desc: 'Cached embeddings file (grows with catalog size)', active: isEnhanced, value: paths.embeddings },
|
|
398
|
-
{ key: 'INDEX_SERVER_SEMANTIC_LOCAL_ONLY', desc: 'Block remote model downloads (0=allow download, 1=local only)', active: isEnhanced, value: isEnhanced ? '0' : '1' },
|
|
399
|
-
|
|
400
|
-
// ── Storage Backend ────────────────────────────────────────────────────
|
|
401
|
-
{ section: 'Storage Backend — JSON (default) or SQLite (experimental)' },
|
|
402
|
-
{ key: 'INDEX_SERVER_STORAGE_BACKEND', desc: 'Storage engine: json | sqlite', active: isSqlite, value: isSqlite ? 'sqlite' : 'json' },
|
|
403
|
-
{ key: 'INDEX_SERVER_SQLITE_PATH', desc: 'SQLite database file path', active: isSqlite, value: paths.sqliteDb },
|
|
404
|
-
{ key: 'INDEX_SERVER_SQLITE_WAL', desc: 'Enable Write-Ahead Logging for SQLite (0=off, 1=on)', active: isSqlite, value: '1' },
|
|
405
|
-
{ key: 'INDEX_SERVER_SQLITE_MIGRATE_ON_START', desc: 'Auto-migrate JSON to SQLite on startup (0=off, 1=on)', active: isSqlite, value: '1' },
|
|
406
|
-
|
|
407
|
-
// ── Logging & Diagnostics ──────────────────────────────────────────────
|
|
408
|
-
{ section: 'Logging — log level, file output, diagnostics' },
|
|
409
|
-
{ key: 'INDEX_SERVER_LOG_LEVEL', desc: 'Log level: error | warn | info | debug | trace', active: true, value: config.logLevel },
|
|
410
|
-
{ key: 'INDEX_SERVER_LOG_FILE', desc: 'Enable file logging (0=off, 1=default path, or absolute path)', active: isEnhanced, value: isEnhanced ? '1' : '0' },
|
|
411
|
-
{ key: 'INDEX_SERVER_VERBOSE_LOGGING', desc: 'Verbose stderr output (0=off, 1=on)', active: false, value: '0' },
|
|
412
|
-
{ key: 'INDEX_SERVER_LOG_JSON', desc: 'JSON-formatted log output (0=off, 1=on)', active: false, value: '0' },
|
|
413
|
-
{ key: 'INDEX_SERVER_LOG_DIAG', desc: 'Diagnostic startup logging (0=off, 1=on)', active: false, value: '0' },
|
|
414
|
-
{ key: 'INDEX_SERVER_AUDIT_LOG', desc: 'Audit log path (1=default, 0=disabled, or absolute path)', active: true, value: paths.auditLog },
|
|
415
|
-
|
|
416
|
-
// ── Backup & Recovery ──────────────────────────────────────────────────
|
|
417
|
-
{ section: 'Backup — automatic backup scheduling' },
|
|
418
|
-
{ key: 'INDEX_SERVER_AUTO_BACKUP', desc: 'Enable automatic backups (0=off, 1=on; defaults to on when mutation is enabled)', active: false, value: config.mutation ? '1' : '0' },
|
|
419
|
-
{ key: 'INDEX_SERVER_AUTO_BACKUP_INTERVAL_MS', desc: 'Backup interval in ms (default: 3600000 = 1 hour)', active: false, value: '3600000' },
|
|
420
|
-
{ key: 'INDEX_SERVER_AUTO_BACKUP_MAX_COUNT', desc: 'Max backup snapshots to retain (default: 10)', active: false, value: '10' },
|
|
421
|
-
{ key: 'INDEX_SERVER_BACKUP_BEFORE_BULK_DELETE',desc: 'Auto-backup before bulk delete operations (0=off, 1=on)', active: false, value: '1' },
|
|
422
|
-
|
|
423
|
-
// ── Features & Flags ───────────────────────────────────────────────────
|
|
424
|
-
{ section: 'Features — feature flags and capabilities' },
|
|
425
|
-
{ key: 'INDEX_SERVER_FEATURES', desc: 'Comma-separated feature flags: usage,window,hotness,drift,risk', active: isEnhanced, value: isEnhanced ? 'usage' : '' },
|
|
426
|
-
{ key: 'INDEX_SERVER_METRICS_FILE_STORAGE', desc: 'Persist metrics to disk (0=off, 1=on)', active: isEnhanced, value: isEnhanced ? '1' : '0' },
|
|
427
|
-
{ key: 'INDEX_SERVER_METRICS_DIR', desc: 'Metrics storage directory', active: isEnhanced, value: paths.metrics },
|
|
428
|
-
{ key: 'INDEX_SERVER_FLAGS_FILE', desc: 'Feature flags JSON file path', active: false, value: paths.flags },
|
|
429
|
-
|
|
430
|
-
// ── Server & Transport ─────────────────────────────────────────────────
|
|
431
|
-
{ section: 'Server — MCP transport and instance mode' },
|
|
432
|
-
{ key: 'INDEX_SERVER_MODE', desc: 'Instance mode: standalone | leader | follower | auto', active: false, value: 'standalone' },
|
|
433
|
-
{ key: 'INDEX_SERVER_DISABLE_EARLY_STDIN_BUFFER',desc: 'Disable stdin handshake hardening (0=off, 1=on)', active: false, value: '0' },
|
|
434
|
-
{ key: 'INDEX_SERVER_IDLE_KEEPALIVE_MS', desc: 'Keepalive interval in ms (default: 30000)', active: false, value: '30000' },
|
|
435
|
-
{ key: 'INDEX_SERVER_POLL_MS', desc: 'Index filesystem poll interval in ms (default: 10000)', active: false, value: '10000' },
|
|
436
|
-
|
|
437
|
-
// ── Advanced Tuning ────────────────────────────────────────────────────
|
|
438
|
-
{ section: 'Advanced — tuning, limits, governance (most users can skip)' },
|
|
439
|
-
{ key: 'INDEX_SERVER_BODY_WARN_LENGTH', desc: 'Max instruction body length in chars (default: 100000)', active: false, value: '100000' },
|
|
440
|
-
{ key: 'INDEX_SERVER_AUTO_SPLIT_OVERSIZED', desc: 'Auto-split oversized entries on load (0=off, 1=on)', active: false, value: '0' },
|
|
441
|
-
{ key: 'INDEX_SERVER_READ_RETRIES', desc: 'File read retry attempts (default: 3)', active: false, value: '3' },
|
|
442
|
-
{ key: 'INDEX_SERVER_MAX_BULK_DELETE', desc: 'Max entries in a single bulk delete (default: 5)', active: false, value: '5' },
|
|
443
|
-
{ key: 'INDEX_SERVER_FEEDBACK_MAX_ENTRIES', desc: 'Max feedback entries before rotation (default: 1000)', active: false, value: '1000' },
|
|
444
|
-
{ key: 'INDEX_SERVER_MESSAGING_MAX', desc: 'Max messages in queue (default: 10000)', active: false, value: '10000' },
|
|
445
|
-
{ key: 'INDEX_SERVER_MAX_CONNECTIONS', desc: 'Max concurrent dashboard connections (default: 100)', active: false, value: '100' },
|
|
446
|
-
{ key: 'INDEX_SERVER_CACHE_MODE', desc: 'Index cache mode: normal | memoize | memoize+hash | reload | reload+memo', active: false, value: 'normal' },
|
|
447
|
-
{ key: 'INDEX_SERVER_WORKSPACE', desc: 'Workspace identifier for multi-tenant setups', active: false, value: '' },
|
|
448
|
-
{ key: 'INDEX_SERVER_AGENT_ID', desc: 'Agent identifier for audit trails', active: false, value: '' },
|
|
449
|
-
];
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// --------------------------------------------------------------------------
|
|
453
|
-
// Generate .vscode/mcp.json snippet (JSONC with comments)
|
|
454
|
-
// --------------------------------------------------------------------------
|
|
455
|
-
function generateMcpJson(config, paths) {
|
|
456
|
-
const catalog = getEnvCatalog(config, paths);
|
|
457
|
-
const indent = '\t\t\t\t';
|
|
458
|
-
const launch = resolveServerLaunch(config);
|
|
459
|
-
const argsJson = JSON.stringify(launch.args);
|
|
460
|
-
|
|
461
|
-
const lines = [
|
|
462
|
-
'{',
|
|
463
|
-
'\t"servers": {',
|
|
464
|
-
`\t\t"${config.serverName}": {`,
|
|
465
|
-
'\t\t\t"type": "stdio",',
|
|
466
|
-
`\t\t\t"cwd": "${fwd(launch.cwd)}",`,
|
|
467
|
-
`\t\t\t"command": "${launch.command}",`,
|
|
468
|
-
`\t\t\t"args": ${argsJson},`,
|
|
469
|
-
'\t\t\t"env": {',
|
|
470
|
-
];
|
|
471
|
-
|
|
472
|
-
let firstSection = true;
|
|
473
|
-
for (const entry of catalog) {
|
|
474
|
-
if (entry.section) {
|
|
475
|
-
if (!firstSection) lines.push('');
|
|
476
|
-
lines.push(`${indent}// ── ${entry.section} ${'─'.repeat(Math.max(0, 58 - entry.section.length))}`);
|
|
477
|
-
firstSection = false;
|
|
478
|
-
continue;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
const comment = `// ${entry.desc}`;
|
|
482
|
-
if (entry.active) {
|
|
483
|
-
lines.push(`${indent}${comment}`);
|
|
484
|
-
lines.push(`${indent}"${entry.key}": "${entry.value}",`);
|
|
485
|
-
} else {
|
|
486
|
-
lines.push(`${indent}${comment}`);
|
|
487
|
-
lines.push(`${indent}// "${entry.key}": "${entry.value}",`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
lines.push('\t\t\t}');
|
|
492
|
-
lines.push('\t\t}');
|
|
493
|
-
lines.push('\t},');
|
|
494
|
-
lines.push('\t"inputs": []');
|
|
495
|
-
lines.push('}');
|
|
496
|
-
|
|
497
|
-
return lines.join('\n');
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// --------------------------------------------------------------------------
|
|
501
|
-
// Generate Copilot CLI mcp-config.json format
|
|
502
|
-
// --------------------------------------------------------------------------
|
|
503
|
-
function generateCopilotCliJson(config, paths) {
|
|
504
|
-
const catalog = getEnvCatalog(config, paths);
|
|
505
|
-
const env = {};
|
|
506
|
-
for (const entry of catalog) {
|
|
507
|
-
if (entry.section) continue;
|
|
508
|
-
if (entry.active) env[entry.key] = entry.value;
|
|
509
|
-
}
|
|
510
|
-
const launch = resolveServerLaunch(config);
|
|
511
|
-
// copilot-cli doesn't reliably inherit cwd — use absolute args for local/packaged
|
|
512
|
-
const args = launch.source === 'local'
|
|
513
|
-
? [fwd(path.resolve(launch.cwd, launch.args[0]))]
|
|
514
|
-
: launch.args;
|
|
515
|
-
const obj = {
|
|
516
|
-
mcpServers: {
|
|
517
|
-
[config.serverName]: {
|
|
518
|
-
command: launch.command,
|
|
519
|
-
args,
|
|
520
|
-
env,
|
|
521
|
-
},
|
|
522
|
-
},
|
|
523
|
-
};
|
|
524
|
-
return JSON.stringify(obj, null, 2);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// --------------------------------------------------------------------------
|
|
528
|
-
// Generate Claude Desktop config JSON format
|
|
529
|
-
// --------------------------------------------------------------------------
|
|
530
|
-
function generateClaudeDesktopJson(config, paths) {
|
|
531
|
-
const catalog = getEnvCatalog(config, paths);
|
|
532
|
-
const env = {};
|
|
533
|
-
for (const entry of catalog) {
|
|
534
|
-
if (entry.section) continue;
|
|
535
|
-
if (entry.active) env[entry.key] = entry.value;
|
|
536
|
-
}
|
|
537
|
-
const launch = resolveServerLaunch(config);
|
|
538
|
-
// Claude Desktop doesn't support cwd — use absolute args for local/packaged
|
|
539
|
-
const args = launch.source === 'local'
|
|
540
|
-
? [fwd(path.resolve(launch.cwd, launch.args[0]))]
|
|
541
|
-
: launch.args;
|
|
542
|
-
const obj = {
|
|
543
|
-
mcpServers: {
|
|
544
|
-
[config.serverName]: {
|
|
545
|
-
command: launch.command,
|
|
546
|
-
args,
|
|
547
|
-
env,
|
|
548
|
-
},
|
|
549
|
-
},
|
|
550
|
-
};
|
|
551
|
-
return JSON.stringify(obj, null, 2);
|
|
308
|
+
return mcpConfig.buildEnvCatalog(config, paths);
|
|
552
309
|
}
|
|
553
310
|
|
|
554
311
|
// --------------------------------------------------------------------------
|
|
555
312
|
// Resolve target config file paths based on scope and OS
|
|
556
313
|
// --------------------------------------------------------------------------
|
|
557
314
|
function resolveConfigPaths(config) {
|
|
558
|
-
|
|
559
|
-
const results = [];
|
|
560
|
-
const isWin = process.platform === 'win32';
|
|
561
|
-
|
|
562
|
-
for (const target of (config.targets || ['vscode'])) {
|
|
563
|
-
if (target === 'vscode') {
|
|
564
|
-
if (config.scope === 'global') {
|
|
565
|
-
const dir = isWin
|
|
566
|
-
? path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Code', 'User')
|
|
567
|
-
: path.join(home, '.config', 'Code', 'User');
|
|
568
|
-
results.push({ target, path: path.join(dir, 'settings.json'), format: 'vscode-global' });
|
|
569
|
-
} else {
|
|
570
|
-
results.push({ target, path: path.join(config.root, '.vscode', 'mcp.json'), format: 'vscode' });
|
|
571
|
-
}
|
|
572
|
-
} else if (target === 'copilot-cli') {
|
|
573
|
-
const dir = isWin
|
|
574
|
-
? path.join(process.env.USERPROFILE || home, '.copilot')
|
|
575
|
-
: path.join(home, '.copilot');
|
|
576
|
-
results.push({ target, path: path.join(dir, 'mcp-config.json'), format: 'copilot-cli' });
|
|
577
|
-
} else if (target === 'claude') {
|
|
578
|
-
const dir = isWin
|
|
579
|
-
? path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Claude')
|
|
580
|
-
: path.join(home, 'Library', 'Application Support', 'Claude');
|
|
581
|
-
results.push({ target, path: path.join(dir, 'claude_desktop_config.json'), format: 'claude' });
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
return results;
|
|
315
|
+
return mcpConfig.resolveConfigTargets({ targets: config.targets, scope: config.scope, root: config.root });
|
|
585
316
|
}
|
|
586
317
|
|
|
587
318
|
// --------------------------------------------------------------------------
|
|
588
319
|
// Generate config content for a given format
|
|
589
320
|
// --------------------------------------------------------------------------
|
|
590
|
-
function generateConfigForTarget(format, config
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
321
|
+
function generateConfigForTarget(format, config) {
|
|
322
|
+
const target = { target: format === 'vscode-global' ? 'vscode' : format, format, path: '' };
|
|
323
|
+
return mcpConfig.renderServerConfigForTarget(target, {
|
|
324
|
+
root: config.root,
|
|
325
|
+
name: config.serverName,
|
|
326
|
+
profile: config.profile,
|
|
327
|
+
port: config.port,
|
|
328
|
+
host: config.host,
|
|
329
|
+
tls: config.tls,
|
|
330
|
+
mutation: config.mutation,
|
|
331
|
+
logLevel: config.logLevel,
|
|
332
|
+
});
|
|
602
333
|
}
|
|
603
334
|
|
|
604
335
|
// --------------------------------------------------------------------------
|
|
605
336
|
// Preview all generated configs
|
|
606
337
|
// --------------------------------------------------------------------------
|
|
607
|
-
function previewConfigs(configTargets, config
|
|
338
|
+
function previewConfigs(configTargets, config) {
|
|
608
339
|
console.log('\n┌─────────────────────────────────────────────────────────────────────┐');
|
|
609
340
|
console.log('│ 📋 Configuration Preview │');
|
|
610
341
|
console.log('└─────────────────────────────────────────────────────────────────────┘');
|
|
611
342
|
for (const ct of configTargets) {
|
|
612
|
-
const content = generateConfigForTarget(ct.format, config
|
|
343
|
+
const content = generateConfigForTarget(ct.format, config);
|
|
613
344
|
console.log(`\n── ${ct.target} → ${ct.path} ──\n`);
|
|
614
345
|
console.log(content);
|
|
615
346
|
}
|
|
@@ -619,45 +350,21 @@ function previewConfigs(configTargets, config, paths) {
|
|
|
619
350
|
// --------------------------------------------------------------------------
|
|
620
351
|
// Write config to real file (merge if existing)
|
|
621
352
|
// --------------------------------------------------------------------------
|
|
622
|
-
function
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (fs.existsSync(targetInfo.path)) {
|
|
638
|
-
// Backup existing file before overwriting
|
|
639
|
-
const backup = targetInfo.path + '.backup.' + Date.now();
|
|
640
|
-
fs.copyFileSync(targetInfo.path, backup);
|
|
641
|
-
console.log(` 📦 Backed up existing: ${backup}`);
|
|
642
|
-
|
|
643
|
-
// For JSON files, try to merge the mcpServers key
|
|
644
|
-
try {
|
|
645
|
-
const existing = JSON.parse(fs.readFileSync(targetInfo.path, 'utf8'));
|
|
646
|
-
const generated = JSON.parse(content);
|
|
647
|
-
|
|
648
|
-
if (targetInfo.format === 'copilot-cli' || targetInfo.format === 'claude') {
|
|
649
|
-
existing.mcpServers = { ...existing.mcpServers, ...generated.mcpServers };
|
|
650
|
-
fs.writeFileSync(targetInfo.path, JSON.stringify(existing, null, 2), 'utf8'); // lgtm[js/file-system-race] — setup wizard writes user-provided config target; race acceptable in CLI tooling
|
|
651
|
-
console.log(` ✅ Merged into: ${targetInfo.path}`);
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
} catch {
|
|
655
|
-
// Not valid JSON — fall through to overwrite
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
fs.writeFileSync(targetInfo.path, content, 'utf8'); // lgtm[js/file-system-race] — setup wizard writes user-provided config target; race acceptable in CLI tooling
|
|
660
|
-
console.log(` ✅ Written: ${targetInfo.path}`);
|
|
353
|
+
function applyConfigTarget(targetInfo, config) {
|
|
354
|
+
const result = mcpConfig.upsertServer({
|
|
355
|
+
target: targetInfo.target,
|
|
356
|
+
scope: targetInfo.format === 'vscode-global' ? 'global' : config.scope,
|
|
357
|
+
root: config.root,
|
|
358
|
+
name: config.serverName,
|
|
359
|
+
profile: config.profile,
|
|
360
|
+
port: config.port,
|
|
361
|
+
host: config.host,
|
|
362
|
+
tls: config.tls,
|
|
363
|
+
mutation: config.mutation,
|
|
364
|
+
logLevel: config.logLevel,
|
|
365
|
+
});
|
|
366
|
+
if (result.backupPath) console.log(` 📦 Backed up existing: ${result.backupPath}`);
|
|
367
|
+
console.log(` ✅ Written: ${result.path}`);
|
|
661
368
|
}
|
|
662
369
|
|
|
663
370
|
// --------------------------------------------------------------------------
|
|
@@ -783,7 +490,7 @@ async function deployRuntime(config) {
|
|
|
783
490
|
type: 'commonjs',
|
|
784
491
|
scripts: { start: 'node node_modules/@jagilber-org/index-server/dist/server/index-server.js' },
|
|
785
492
|
};
|
|
786
|
-
|
|
493
|
+
writeTextFile(targetPkg, JSON.stringify(minPkg, null, 2));
|
|
787
494
|
}
|
|
788
495
|
|
|
789
496
|
console.log(' Installing package (this may take a moment)...');
|
|
@@ -840,7 +547,7 @@ async function deployRuntime(config) {
|
|
|
840
547
|
runtimePkg.version = sourceVersion;
|
|
841
548
|
runtimePkg.scripts = runtimePkg.scripts || {};
|
|
842
549
|
runtimePkg.scripts.start = 'node dist/server/index-server.js';
|
|
843
|
-
|
|
550
|
+
writeTextFile(targetPkg, JSON.stringify(runtimePkg, null, 2) + '\n');
|
|
844
551
|
} catch { /* ok */ }
|
|
845
552
|
|
|
846
553
|
// Verify the deployed server is actually runnable
|
|
@@ -914,10 +621,10 @@ Non-interactive mode:
|
|
|
914
621
|
|
|
915
622
|
if (fs.existsSync(envPath)) { // lgtm[js/file-system-race]
|
|
916
623
|
const genPath = path.join(config.root, '.env.generated');
|
|
917
|
-
|
|
624
|
+
writeTextFile(genPath, envContent);
|
|
918
625
|
console.log(`\n⚠️ .env already exists. Written to: ${genPath}`);
|
|
919
626
|
} else {
|
|
920
|
-
|
|
627
|
+
writeTextFile(envPath, envContent); // lgtm[js/file-system-race] — setup wizard writes .env to user-supplied path; race acceptable in CLI tooling
|
|
921
628
|
console.log(`\n✅ .env written to: ${envPath}`);
|
|
922
629
|
}
|
|
923
630
|
|
|
@@ -926,37 +633,36 @@ Non-interactive mode:
|
|
|
926
633
|
|
|
927
634
|
// Preview
|
|
928
635
|
if (config.preview !== false) {
|
|
929
|
-
previewConfigs(configTargets, config
|
|
636
|
+
previewConfigs(configTargets, config);
|
|
930
637
|
}
|
|
931
638
|
|
|
932
639
|
// Write to real files or sidecar
|
|
933
640
|
if (config.write) {
|
|
934
641
|
console.log('📁 Writing configuration files...\n');
|
|
935
642
|
for (const ct of configTargets) {
|
|
936
|
-
|
|
937
|
-
writeConfigFile(ct, content);
|
|
643
|
+
applyConfigTarget(ct, config);
|
|
938
644
|
}
|
|
939
645
|
} else {
|
|
940
646
|
// Legacy sidecar behavior for backward compatibility
|
|
941
|
-
const mcpContent =
|
|
647
|
+
const mcpContent = generateConfigForTarget('vscode', config);
|
|
942
648
|
const mcpDir = path.join(config.root, '.vscode');
|
|
943
649
|
const mcpPath = path.join(mcpDir, 'mcp.json.generated');
|
|
944
650
|
|
|
945
651
|
try {
|
|
946
652
|
fs.mkdirSync(mcpDir, { recursive: true });
|
|
947
653
|
} catch { /* exists */ }
|
|
948
|
-
|
|
654
|
+
mcpConfig.writeGeneratedConfig(mcpPath, mcpContent);
|
|
949
655
|
console.log(`✅ mcp.json snippet written to: ${mcpPath}`);
|
|
950
656
|
console.log(' Copy its contents into your .vscode/mcp.json or VS Code user settings.');
|
|
951
657
|
|
|
952
658
|
// Also generate Copilot CLI / Claude if requested
|
|
953
659
|
for (const ct of configTargets) {
|
|
954
660
|
if (ct.format !== 'vscode' && ct.format !== 'vscode-global') {
|
|
955
|
-
const content = generateConfigForTarget(ct.format, config
|
|
661
|
+
const content = generateConfigForTarget(ct.format, config);
|
|
956
662
|
const genPath = ct.path + '.generated';
|
|
957
663
|
const dir = path.dirname(genPath);
|
|
958
664
|
try { fs.mkdirSync(dir, { recursive: true }); } catch { /* exists */ }
|
|
959
|
-
|
|
665
|
+
mcpConfig.writeGeneratedConfig(genPath, content);
|
|
960
666
|
console.log(`✅ ${ct.target} config written to: ${genPath}`);
|
|
961
667
|
}
|
|
962
668
|
}
|
|
@@ -1005,7 +711,7 @@ Non-interactive mode:
|
|
|
1005
711
|
console.log(` ${step}. Copy generated config into your MCP client settings.`);
|
|
1006
712
|
|
|
1007
713
|
for (const ct of configTargets) {
|
|
1008
|
-
const genPath = ct.format === 'vscode'
|
|
714
|
+
const genPath = ct.format === 'vscode'
|
|
1009
715
|
? path.join(config.root, '.vscode', 'mcp.json.generated')
|
|
1010
716
|
: ct.path + '.generated';
|
|
1011
717
|
console.log(` ${ct.target}: ${genPath}`);
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/jagilber-org/index-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.28.
|
|
9
|
+
"version": "1.28.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@jagilber-org/index-server",
|
|
14
|
-
"version": "1.28.
|
|
14
|
+
"version": "1.28.4",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|