@jagilber-org/index-server 1.28.1 → 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.
Files changed (34) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +15 -3
  3. package/dist/config/runtimeConfig.d.ts +1 -0
  4. package/dist/config/runtimeConfig.js +1 -0
  5. package/dist/dashboard/client/admin.html +27 -27
  6. package/dist/server/certInit.js +4 -2
  7. package/dist/server/index-server.js +102 -1
  8. package/dist/server/sdkServer.js +2 -2
  9. package/dist/server/transport.js +2 -2
  10. package/dist/services/handlers.dashboardConfig.js +1 -0
  11. package/dist/services/handlers.search.js +4 -0
  12. package/dist/services/handlers.usage.js +11 -0
  13. package/dist/services/mcpConfig/backup.d.ts +17 -0
  14. package/dist/services/mcpConfig/backup.js +114 -0
  15. package/dist/services/mcpConfig/flagCatalog.d.ts +44 -0
  16. package/dist/services/mcpConfig/flagCatalog.js +301 -0
  17. package/dist/services/mcpConfig/formats.d.ts +30 -0
  18. package/dist/services/mcpConfig/formats.js +116 -0
  19. package/dist/services/mcpConfig/index.d.ts +44 -0
  20. package/dist/services/mcpConfig/index.js +130 -0
  21. package/dist/services/mcpConfig/jsoncEdit.d.ts +2 -0
  22. package/dist/services/mcpConfig/jsoncEdit.js +29 -0
  23. package/dist/services/mcpConfig/paths.d.ts +18 -0
  24. package/dist/services/mcpConfig/paths.js +50 -0
  25. package/dist/services/mcpConfig/validate.d.ts +7 -0
  26. package/dist/services/mcpConfig/validate.js +62 -0
  27. package/package.json +3 -2
  28. package/schemas/mcp.claude.schema.json +27 -0
  29. package/schemas/mcp.copilot-cli.schema.json +28 -0
  30. package/schemas/mcp.indexServerEnv.schema.json +9 -0
  31. package/schemas/mcp.vscode.schema.json +28 -0
  32. package/scripts/build/generate-certs.mjs +1 -1
  33. package/scripts/build/setup-wizard.mjs +71 -352
  34. package/server.json +2 -2
package/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@ The format is based on Keep a Changelog and this project adheres to Semantic Ver
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.28.2] - 2026-05-06
10
+
11
+ ### Added
12
+
13
+ - Clean install setup coverage and release workflow safety fixes.
14
+
9
15
  ## [1.28.1] - 2026-05-05
10
16
 
11
17
  ### Fixed
package/README.md CHANGED
@@ -19,6 +19,14 @@ Index Server is a central knowledge base that AI agents connect to via the [Mode
19
19
 
20
20
  ---
21
21
 
22
+ ## Prerequisite: Node.js
23
+
24
+ Index Server requires **Node.js 22 or newer** before using the `npx` or source install options. Download Node.js from [nodejs.org](https://nodejs.org/) or install it with Windows Package Manager:
25
+
26
+ ```powershell
27
+ winget install nodejs
28
+ ```
29
+
22
30
  ## Quick Start Options
23
31
 
24
32
  ### Option A: MCP-native via `npx` (recommended)
@@ -39,7 +47,7 @@ npx -y @jagilber-org/index-server@latest --dashboard
39
47
 
40
48
  #### Bootstrap HTTPS for the dashboard
41
49
 
42
- Generate a self-signed TLS cert+key in one command (requires `openssl` on PATH):
50
+ Generate a self-signed TLS cert+key in one command:
43
51
 
44
52
  ```bash
45
53
  # Generate at ~/.index-server/certs/, then start with HTTPS automatically
@@ -48,8 +56,12 @@ npx -y @jagilber-org/index-server@latest --init-cert --start --dashboard
48
56
 
49
57
  `--init-cert` alone exits after generation. `--init-cert --start` continues
50
58
  into normal startup with the generated cert wired into `--dashboard-tls`
51
- automatically. See [`docs/cert_init.md`](docs/cert_init.md) for the full
52
- flag reference, security notes, and troubleshooting.
59
+ automatically.
60
+
61
+ **Prerequisite:** `openssl` must be on `PATH`. On Windows, Git for Windows
62
+ typically includes it at `C:\Program Files\Git\usr\bin\openssl.exe`. See
63
+ [`docs/cert_init.md`](docs/cert_init.md) for setup guidance, the full flag
64
+ reference, security notes, and troubleshooting.
53
65
 
54
66
  ### Option B: VS Code MCP configuration
55
67
 
@@ -51,6 +51,7 @@ interface IndexConfig {
51
51
  bodyMaxLength: number;
52
52
  autoSplitOversized: boolean;
53
53
  autoUsageTrack: boolean;
54
+ omitZeroResultQuery: boolean;
54
55
  }
55
56
  interface InstructionsManifestConfig {
56
57
  writeEnabled: boolean;
@@ -199,6 +199,7 @@ function parseIndexConfig() {
199
199
  bodyMaxLength,
200
200
  autoSplitOversized: (0, envUtils_1.getBooleanEnv)('INDEX_SERVER_AUTO_SPLIT_OVERSIZED'),
201
201
  autoUsageTrack: (0, envUtils_1.parseBooleanEnv)(process.env.INDEX_SERVER_AUTO_USAGE_TRACK, true),
202
+ omitZeroResultQuery: (0, envUtils_1.getBooleanEnv)('INDEX_SERVER_SEARCH_OMIT_ZERO_QUERY'),
202
203
  };
203
204
  }
204
205
  function parseInstructionsConfig(_mutationEnabled) {
@@ -1,30 +1,30 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
- <meta name="dashboard-build-version" content="1.28.1-0009101e">
4
+ <meta name="dashboard-build-version" content="1.28.4-0009101e">
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>Index Server Admin</title>
8
- <link rel="stylesheet" href="css/admin.css?v=1.28.1-0009101e">
9
- <script defer src="js/admin.utils.js?v=1.28.1-0009101e"></script>
10
- <script defer src="js/admin.auth.js?v=1.28.1-0009101e"></script>
11
- <script defer src="js/admin.overview.js?v=1.28.1-0009101e"></script>
12
- <script defer src="js/admin.sessions.js?v=1.28.1-0009101e"></script>
13
- <script defer src="js/admin.monitor.js?v=1.28.1-0009101e"></script>
14
- <script defer src="js/admin.events.js?v=1.28.1-0009101e"></script>
15
- <script defer src="js/admin.graph.js?v=1.28.1-0009101e"></script>
8
+ <link rel="stylesheet" href="css/admin.css?v=1.28.4-0009101e">
9
+ <script defer src="js/admin.utils.js?v=1.28.4-0009101e"></script>
10
+ <script defer src="js/admin.auth.js?v=1.28.4-0009101e"></script>
11
+ <script defer src="js/admin.overview.js?v=1.28.4-0009101e"></script>
12
+ <script defer src="js/admin.sessions.js?v=1.28.4-0009101e"></script>
13
+ <script defer src="js/admin.monitor.js?v=1.28.4-0009101e"></script>
14
+ <script defer src="js/admin.events.js?v=1.28.4-0009101e"></script>
15
+ <script defer src="js/admin.graph.js?v=1.28.4-0009101e"></script>
16
16
  <script defer src="js/marked.umd.js"></script>
17
- <script defer src="js/admin.instructions.js?v=1.28.1-0009101e"></script>
18
- <script defer src="js/admin.logs.js?v=1.28.1-0009101e"></script>
19
- <script defer src="js/admin.maintenance.js?v=1.28.1-0009101e"></script>
20
- <script defer src="js/admin.config.js?v=1.28.1-0009101e"></script>
21
- <script defer src="js/admin.performance.js?v=1.28.1-0009101e"></script>
22
- <script defer src="js/admin.instances.js?v=1.28.1-0009101e"></script>
23
- <script defer src="js/admin.embeddings.js?v=1.28.1-0009101e"></script>
24
- <script defer src="js/admin.messaging.js?v=1.28.1-0009101e"></script>
25
- <script defer src="js/admin.sqlite.js?v=1.28.1-0009101e"></script>
26
- <script defer src="js/admin.boot.js?v=1.28.1-0009101e"></script>
27
- <script defer src="js/admin.feedback.js?v=1.28.1-0009101e"></script>
17
+ <script defer src="js/admin.instructions.js?v=1.28.4-0009101e"></script>
18
+ <script defer src="js/admin.logs.js?v=1.28.4-0009101e"></script>
19
+ <script defer src="js/admin.maintenance.js?v=1.28.4-0009101e"></script>
20
+ <script defer src="js/admin.config.js?v=1.28.4-0009101e"></script>
21
+ <script defer src="js/admin.performance.js?v=1.28.4-0009101e"></script>
22
+ <script defer src="js/admin.instances.js?v=1.28.4-0009101e"></script>
23
+ <script defer src="js/admin.embeddings.js?v=1.28.4-0009101e"></script>
24
+ <script defer src="js/admin.messaging.js?v=1.28.4-0009101e"></script>
25
+ <script defer src="js/admin.sqlite.js?v=1.28.4-0009101e"></script>
26
+ <script defer src="js/admin.boot.js?v=1.28.4-0009101e"></script>
27
+ <script defer src="js/admin.feedback.js?v=1.28.4-0009101e"></script>
28
28
  </head>
29
29
  <body>
30
30
  <div class="admin-container admin-root">
@@ -909,10 +909,10 @@
909
909
  }
910
910
  }
911
911
 
912
- // Graph logic was extracted to js/admin.graph.js?v=1.28.1-0009101e
912
+ // Graph logic was extracted to js/admin.graph.js?v=1.28.4-0009101e
913
913
  // Functions available globally: reloadGraphMermaid, initGraphScopeDefaults, copyMermaidSource, toggleGraphEdit, applyGraphEdit, cancelGraphEdit, refreshDrillCategories, loadDrillInstructions, clearSelections
914
914
 
915
- <!-- overview functions moved to js/admin.overview.js?v=1.28.1-0009101e -->
915
+ <!-- overview functions moved to js/admin.overview.js?v=1.28.4-0009101e -->
916
916
 
917
917
  // Lightweight overview-level maintenance display (optional)
918
918
  // Intentionally minimal to avoid blocking overview rendering.
@@ -1097,7 +1097,7 @@
1097
1097
  }
1098
1098
 
1099
1099
  // --- Backup / Restore ---
1100
- // Extracted to js/admin.maintenance.js?v=1.28.1-0009101e
1100
+ // Extracted to js/admin.maintenance.js?v=1.28.4-0009101e
1101
1101
 
1102
1102
  async function performBackup() {
1103
1103
  try {
@@ -1163,7 +1163,7 @@
1163
1163
  }
1164
1164
 
1165
1165
  async function loadConfiguration() {
1166
- // Primary implementation in js/admin.config.js?v=1.28.1-0009101e (loaded via defer).
1166
+ // Primary implementation in js/admin.config.js?v=1.28.4-0009101e (loaded via defer).
1167
1167
  // This inline fallback only fires if the external script failed to load.
1168
1168
  if (window.__configExternalLoaded) return;
1169
1169
  try {
@@ -1223,10 +1223,10 @@
1223
1223
  return false;
1224
1224
  }
1225
1225
 
1226
- // Monitoring functions moved to js/admin.monitor.js?v=1.28.1-0009101e
1226
+ // Monitoring functions moved to js/admin.monitor.js?v=1.28.4-0009101e
1227
1227
 
1228
1228
  // ===== Log Viewer =====
1229
- // Extracted to js/admin.logs.js?v=1.28.1-0009101e
1229
+ // Extracted to js/admin.logs.js?v=1.28.4-0009101e
1230
1230
 
1231
1231
  // ===== Instruction Management =====
1232
1232
  let instructionEditing = null;
@@ -1723,7 +1723,7 @@
1723
1723
  setInterval(fetchResourceTrends, 10000);
1724
1724
  })();
1725
1725
 
1726
- // Instruction management logic extracted to js/admin.instructions.js?v=1.28.1-0009101e
1726
+ // Instruction management logic extracted to js/admin.instructions.js?v=1.28.4-0009101e
1727
1727
  // Functions exposed globally: loadInstructions, renderInstructionList, editInstruction, saveInstruction, deleteInstruction, etc.
1728
1728
 
1729
1729
  function startAutoRefresh() {
@@ -205,11 +205,13 @@ function preflightOpenssl() {
205
205
  }
206
206
  catch (e) {
207
207
  throw new certInit_types_1.CertInitError('OPENSSL_NOT_FOUND', 'openssl was not found on PATH or could not be invoked. Install OpenSSL and retry. ' +
208
- 'See https://www.openssl.org/source/ for downloads.', e);
208
+ 'On Windows, Git for Windows typically includes it at C:\\Program Files\\Git\\usr\\bin\\openssl.exe. ' +
209
+ 'See docs/cert_init.md for setup guidance.', e);
209
210
  }
210
211
  if (!result || result.status !== 0) {
211
212
  const stderr = result?.stderr?.toString().trim() ?? '';
212
- throw new certInit_types_1.CertInitError('OPENSSL_NOT_FOUND', `openssl probe failed (status=${String(result?.status)}): ${stderr || 'no output'}. Install OpenSSL and retry.`);
213
+ throw new certInit_types_1.CertInitError('OPENSSL_NOT_FOUND', `openssl probe failed (status=${String(result?.status)}): ${stderr || 'no output'}. ` +
214
+ 'Install OpenSSL and retry. See docs/cert_init.md for setup guidance.');
213
215
  }
214
216
  return result.stdout.toString().trim();
215
217
  }
@@ -97,6 +97,7 @@ const child_process_1 = require("child_process");
97
97
  const shutdownGuard_1 = require("./shutdownGuard");
98
98
  const certInit_1 = require("./certInit");
99
99
  const certInit_types_1 = require("./certInit.types");
100
+ const mcpConfig_1 = require("../services/mcpConfig");
100
101
  // Singleton shutdown guard — all exit paths funnel through this (Issue #36 fix)
101
102
  exports.shutdownGuard = (0, shutdownGuard_1.createShutdownGuard)();
102
103
  // Store in global symbol so services (indexContext) can register cleanup without circular imports
@@ -378,6 +379,87 @@ function launchSetupWizard(argv) {
378
379
  }
379
380
  process.exit(0);
380
381
  }
382
+ const MCP_CONFIG_COMMANDS = new Set(['--mcp-list', '--mcp-get', '--mcp-upsert', '--mcp-remove', '--mcp-restore', '--mcp-validate']);
383
+ function parseMcpConfigOptions(argv) {
384
+ const args = argv.slice(2);
385
+ const command = args.find(arg => MCP_CONFIG_COMMANDS.has(arg));
386
+ const options = {};
387
+ let json = false;
388
+ for (let i = 0; i < args.length; i += 1) {
389
+ const raw = args[i];
390
+ if (raw === '--target' && args[i + 1])
391
+ options.target = args[++i];
392
+ else if (raw.startsWith('--target='))
393
+ options.target = raw.slice('--target='.length);
394
+ else if (raw === '--scope' && args[i + 1])
395
+ options.scope = args[++i];
396
+ else if (raw.startsWith('--scope='))
397
+ options.scope = raw.slice('--scope='.length);
398
+ else if (raw === '--name' && args[i + 1])
399
+ options.name = args[++i];
400
+ else if (raw.startsWith('--name='))
401
+ options.name = raw.slice('--name='.length);
402
+ else if (raw === '--from-profile' && args[i + 1])
403
+ options.profile = args[++i];
404
+ else if (raw.startsWith('--from-profile='))
405
+ options.profile = raw.slice('--from-profile='.length);
406
+ else if (raw === '--backup' && args[i + 1])
407
+ options.backup = args[++i];
408
+ else if (raw.startsWith('--backup='))
409
+ options.backup = raw.slice('--backup='.length);
410
+ else if (raw === '--dry-run')
411
+ options.dryRun = true;
412
+ else if (raw === '--json')
413
+ json = true;
414
+ else if (raw === '--env' && args[i + 1]) {
415
+ const pair = args[++i];
416
+ const idx = pair.indexOf('=');
417
+ if (idx <= 0)
418
+ throw new Error(`Invalid --env value: ${pair}. Expected KEY=VALUE.`);
419
+ options.env = options.env ?? {};
420
+ options.env[pair.slice(0, idx)] = pair.slice(idx + 1);
421
+ }
422
+ }
423
+ return { command, options, json };
424
+ }
425
+ function handleMcpConfigCli(argv) {
426
+ const hasMcpCommand = argv.some(arg => MCP_CONFIG_COMMANDS.has(arg));
427
+ if (!hasMcpCommand)
428
+ return;
429
+ let json = argv.includes('--json');
430
+ try {
431
+ const parsed = parseMcpConfigOptions(argv);
432
+ const { command, options } = parsed;
433
+ json = parsed.json;
434
+ if (!command)
435
+ return;
436
+ const result = command === '--mcp-list' ? (0, mcpConfig_1.listServers)(options) :
437
+ command === '--mcp-get' ? (0, mcpConfig_1.getServer)(options) :
438
+ command === '--mcp-upsert' ? (0, mcpConfig_1.upsertServer)(options) :
439
+ command === '--mcp-remove' ? (0, mcpConfig_1.removeServer)(options) :
440
+ command === '--mcp-restore' ? (0, mcpConfig_1.restoreLatestBackup)(options) :
441
+ (0, mcpConfig_1.validateFile)(options);
442
+ if (json) {
443
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
444
+ }
445
+ else if (result.ok) {
446
+ process.stderr.write(`MCP config ${result.action} succeeded: ${result.path}\n`);
447
+ if (result.servers)
448
+ process.stderr.write(`Servers: ${result.servers.join(', ')}\n`);
449
+ if (result.validation && !result.validation.ok)
450
+ process.stderr.write(`Validation errors: ${result.validation.errors.join('; ')}\n`);
451
+ }
452
+ process.exit(result.ok ? 0 : 2);
453
+ }
454
+ catch (error) {
455
+ const message = error instanceof Error ? error.message : String(error);
456
+ if (json)
457
+ process.stdout.write(`${JSON.stringify({ ok: false, error: message }, null, 2)}\n`);
458
+ else
459
+ process.stderr.write(`MCP config command failed: ${message}\n`);
460
+ process.exit(2);
461
+ }
462
+ }
381
463
  function printHelpAndExit() {
382
464
  const help = `index-server - Model Context Protocol Server
383
465
 
@@ -390,6 +472,22 @@ SETUP:
390
472
  --setup Launch interactive configuration wizard
391
473
  --configure Alias for --setup
392
474
 
475
+ MCP CONFIGURATION CRUD:
476
+ --mcp-list List configured MCP servers for a target
477
+ --mcp-get Get one configured MCP server entry
478
+ --mcp-upsert Create or update one MCP server entry
479
+ --mcp-remove Remove one MCP server entry
480
+ --mcp-restore Restore the latest MCP config backup, or --backup PATH
481
+ --mcp-validate Validate one MCP config file
482
+ --target TARGET vscode | copilot-cli | claude (default: vscode)
483
+ --scope SCOPE repo | global for VS Code (default: repo)
484
+ --name NAME MCP server name (default: index-server)
485
+ --from-profile PROFILE default | enhanced | experimental
486
+ --env KEY=VALUE Add or override one INDEX_SERVER_* env value; repeatable
487
+ --backup PATH Restore this explicit backup path with --mcp-restore
488
+ --dry-run Validate and report without writing
489
+ --json Emit JSON result to stdout
490
+
393
491
  ADMIN DASHBOARD (Optional):
394
492
  --dashboard Enable read-only admin dashboard (default off)
395
493
  --dashboard-port=PORT Dashboard port (default 8787)
@@ -403,7 +501,9 @@ ADMIN DASHBOARD (Optional):
403
501
  Purpose: Local administrator monitoring only
404
502
 
405
503
  CERTIFICATE BOOTSTRAP (Optional, Self-Signed TLS):
406
- --init-cert Generate a self-signed TLS cert+key (requires openssl on PATH).
504
+ --init-cert Generate a self-signed TLS cert+key.
505
+ Requires openssl on PATH (Windows: C:\\Program Files\\Git\\usr\\bin).
506
+ See docs/cert_init.md for setup.
407
507
  Exits after generation unless --start is also given.
408
508
  --cert-dir PATH Output directory (default: ~/.index-server/certs)
409
509
  --cert-file PATH Override cert output path (must be under --cert-dir)
@@ -514,6 +614,7 @@ async function startDashboard(cfg) {
514
614
  }
515
615
  }
516
616
  async function main() {
617
+ handleMcpConfigCli(process.argv);
517
618
  // Run startup preflight (module/data presence). Non-fatal unless INDEX_SERVER_PREFLIGHT_STRICT=1
518
619
  try {
519
620
  (0, preflight_1.emitPreflightAndMaybeExit)();
@@ -58,7 +58,7 @@ function createSdkServer(ServerClass) {
58
58
  logging: {},
59
59
  ...(0, mcpReadOnlySurfaces_1.getReadOnlySurfaceCapabilities)(),
60
60
  };
61
- const server = new ServerClass({ name: 'index', version }, { capabilities: serverCapabilities });
61
+ const server = new ServerClass({ name: 'index-server', version }, { capabilities: serverCapabilities });
62
62
  server.__declaredVersion = version;
63
63
  // Register server with the MCP log bridge (activated later after ready)
64
64
  (0, mcpLogBridge_1.registerMcpServer)(server);
@@ -111,7 +111,7 @@ function createSdkServer(ServerClass) {
111
111
  const versionDeclared = server.__declaredVersion || server.version || '0.0.0';
112
112
  const result = {
113
113
  protocolVersion: negotiated,
114
- serverInfo: { name: 'index', version: versionDeclared },
114
+ serverInfo: { name: 'index-server', version: versionDeclared },
115
115
  capabilities: serverCapabilities,
116
116
  instructions: 'Use initialize -> tools/list -> tools/call { name, arguments }. Prompts: prompts/list and prompts/get. Resources: resources/list and resources/read. Health: tools/call health_check. Metrics: tools/call metrics_snapshot. Ping: ping.'
117
117
  };
@@ -136,7 +136,7 @@ function startTransport(opts = {}) {
136
136
  const p = params;
137
137
  return {
138
138
  protocolVersion: p?.protocolVersion || '2025-06-18',
139
- serverInfo: { name: 'index', version: VERSION },
139
+ serverInfo: { name: 'index-server', version: VERSION },
140
140
  capabilities: { roots: { listChanged: true }, tools: { listChanged: true } }
141
141
  };
142
142
  },
@@ -215,7 +215,7 @@ function startTransport(opts = {}) {
215
215
  try {
216
216
  resultPayload = initHandler ? initHandler(req.params) : {
217
217
  protocolVersion: p?.protocolVersion || '2025-06-18',
218
- serverInfo: { name: 'index', version: VERSION },
218
+ serverInfo: { name: 'index-server', version: VERSION },
219
219
  capabilities: { roots: { listChanged: true }, tools: { listChanged: true } }
220
220
  };
221
221
  }
@@ -27,6 +27,7 @@ exports.FLAG_REGISTRY = [
27
27
  { name: 'INDEX_SERVER_TEST_STRICT_VISIBILITY', category: 'instructions', description: 'Test-only strict fallback path for immediate get/query discoverability.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.1.1' },
28
28
  { name: 'INDEX_SERVER_REQUIRE_CATEGORY', category: 'instructions', description: 'Reject instructions missing category unless lax override set.', stability: 'stable', default: 'off', type: 'boolean', since: '1.0.0' },
29
29
  { name: 'INDEX_SERVER_CANONICAL_DISABLE', category: 'instructions', description: 'Disable canonical sourceHash persistence (forces runtime recompute).', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.1.1' },
30
+ { name: 'INDEX_SERVER_SEARCH_OMIT_ZERO_QUERY', category: 'instructions', description: 'Omit echoed query metadata from zero-result search responses.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.28.2' },
30
31
  { name: 'INDEX_SERVER_READ_RETRIES', category: 'instructions', description: 'Retries for post-add disk visibility checks.', stability: 'diagnostic', default: '5', type: 'number', since: '1.1.1' },
31
32
  { name: 'INDEX_SERVER_READ_BACKOFF_MS', category: 'instructions', description: 'Backoff ms between read retries.', stability: 'diagnostic', default: '10', type: 'number', since: '1.1.1' },
32
33
  // Tracing & logging advanced
@@ -635,6 +635,8 @@ async function handleInstructionsSearch(params) {
635
635
  retryResult.autoTokenized = true;
636
636
  if (retryResult.totalMatches === 0) {
637
637
  retryResult.hints = buildSearchHints(retryParams);
638
+ if ((0, runtimeConfig_1.getRuntimeConfig)().index.omitZeroResultQuery)
639
+ delete retryResult.query;
638
640
  }
639
641
  else {
640
642
  autoTrackSearchResults(retryResult.results);
@@ -646,6 +648,8 @@ async function handleInstructionsSearch(params) {
646
648
  // Attach hints on zero-result responses
647
649
  if (result.totalMatches === 0) {
648
650
  result.hints = buildSearchHints(searchParams);
651
+ if ((0, runtimeConfig_1.getRuntimeConfig)().index.omitZeroResultQuery)
652
+ delete result.query;
649
653
  }
650
654
  // Attach _meta hints on successful (non-empty) responses
651
655
  if (result.totalMatches > 0) {
@@ -2,9 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const registry_1 = require("../server/registry");
4
4
  const indexContext_1 = require("./indexContext");
5
+ const VALID_ACTIONS = new Set(['retrieved', 'applied', 'cited']);
6
+ const VALID_SIGNALS = new Set(['helpful', 'not-relevant', 'outdated', 'applied']);
7
+ function invalidParam(message) {
8
+ return Object.assign(new Error(message), { code: -32602 });
9
+ }
5
10
  (0, registry_1.registerHandler)('usage_track', (p) => {
6
11
  if (!p.id)
7
12
  return { error: 'missing id' };
13
+ if (p.action !== undefined && !VALID_ACTIONS.has(p.action)) {
14
+ throw invalidParam(`Invalid usage action "${p.action}". Expected one of: retrieved, applied, cited.`);
15
+ }
16
+ if (p.signal !== undefined && !VALID_SIGNALS.has(p.signal)) {
17
+ throw invalidParam(`Invalid usage signal "${p.signal}". Expected one of: helpful, not-relevant, outdated, applied.`);
18
+ }
8
19
  const opts = { action: p.action, signal: p.signal, comment: p.comment };
9
20
  const r = (0, indexContext_1.incrementUsage)(p.id, opts);
10
21
  if (!r)
@@ -0,0 +1,17 @@
1
+ export interface BackupManifestEntry {
2
+ originalPath: string;
3
+ backupPath: string;
4
+ sha256: string;
5
+ timestamp: string;
6
+ operation: string;
7
+ serverName?: string;
8
+ }
9
+ export interface BackupManifest {
10
+ version: 1;
11
+ entries: BackupManifestEntry[];
12
+ }
13
+ export declare function retentionLimit(): number;
14
+ export declare function atomicWriteText(filePath: string, text: string): void;
15
+ export declare function readManifest(filePath: string): BackupManifest;
16
+ export declare function createBackup(filePath: string, operation: string, serverName?: string): BackupManifestEntry | undefined;
17
+ export declare function restoreBackup(filePath: string, backupPath?: string): BackupManifestEntry;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.retentionLimit = retentionLimit;
7
+ exports.atomicWriteText = atomicWriteText;
8
+ exports.readManifest = readManifest;
9
+ exports.createBackup = createBackup;
10
+ exports.restoreBackup = restoreBackup;
11
+ const crypto_1 = __importDefault(require("crypto"));
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ function ensureDir(dir) {
15
+ if (!fs_1.default.existsSync(dir))
16
+ fs_1.default.mkdirSync(dir, { recursive: true });
17
+ }
18
+ function timestampForFile() {
19
+ return `${new Date().toISOString().replaceAll(':', '-').replaceAll('.', '-')}.${crypto_1.default.randomBytes(4).toString('hex')}`;
20
+ }
21
+ function backupDirFor(filePath) {
22
+ return path_1.default.join(path_1.default.dirname(filePath), '.mcp-backups');
23
+ }
24
+ function manifestPathFor(filePath) {
25
+ return path_1.default.join(backupDirFor(filePath), 'manifest.json');
26
+ }
27
+ function sha256(text) {
28
+ return crypto_1.default.createHash('sha256').update(text).digest('hex');
29
+ }
30
+ function retentionLimit() {
31
+ const raw = process.env.INDEX_SERVER_MCP_BACKUP_RETAIN;
32
+ const parsed = raw ? Number(raw) : 10;
33
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 10;
34
+ }
35
+ function atomicWriteText(filePath, text) {
36
+ ensureDir(path_1.default.dirname(filePath));
37
+ const tmp = path_1.default.join(path_1.default.dirname(filePath), `.${path_1.default.basename(filePath)}.${crypto_1.default.randomBytes(6).toString('hex')}.tmp`);
38
+ fs_1.default.writeFileSync(tmp, text, 'utf8');
39
+ fs_1.default.renameSync(tmp, filePath);
40
+ }
41
+ function readManifest(filePath) {
42
+ const manifestPath = manifestPathFor(filePath);
43
+ if (!fs_1.default.existsSync(manifestPath))
44
+ return { version: 1, entries: [] };
45
+ const parsed = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
46
+ if (!Array.isArray(parsed.entries))
47
+ throw new Error(`Invalid MCP backup manifest: ${manifestPath}`);
48
+ return { version: 1, entries: parsed.entries };
49
+ }
50
+ function writeManifest(filePath, manifest) {
51
+ atomicWriteText(manifestPathFor(filePath), `${JSON.stringify(manifest, null, 2)}\n`);
52
+ }
53
+ function rotateBackups(filePath, manifest, retain) {
54
+ const targetKey = path_1.default.resolve(filePath);
55
+ const matching = manifest.entries.filter(entry => path_1.default.resolve(entry.originalPath) === targetKey);
56
+ const removable = matching.slice(0, Math.max(0, matching.length - retain));
57
+ const removableSet = new Set(removable);
58
+ for (const entry of removable) {
59
+ try {
60
+ if (fs_1.default.existsSync(entry.backupPath))
61
+ fs_1.default.unlinkSync(entry.backupPath);
62
+ }
63
+ catch {
64
+ throw new Error(`Failed to remove rotated MCP backup: ${entry.backupPath}`);
65
+ }
66
+ }
67
+ return {
68
+ version: 1,
69
+ entries: manifest.entries.filter(entry => !removableSet.has(entry)),
70
+ };
71
+ }
72
+ function createBackup(filePath, operation, serverName) {
73
+ if (!fs_1.default.existsSync(filePath))
74
+ return undefined;
75
+ const content = fs_1.default.readFileSync(filePath, 'utf8');
76
+ const timestamp = timestampForFile();
77
+ const backupDir = backupDirFor(filePath);
78
+ ensureDir(backupDir);
79
+ const backupPath = path_1.default.join(backupDir, `${path_1.default.basename(filePath)}.${timestamp}.json`);
80
+ const legacyBackupPath = path_1.default.join(path_1.default.dirname(filePath), `${path_1.default.basename(filePath)}.backup.${timestamp}`);
81
+ atomicWriteText(backupPath, content);
82
+ atomicWriteText(legacyBackupPath, content);
83
+ const manifest = readManifest(filePath);
84
+ manifest.entries.push({
85
+ originalPath: filePath,
86
+ backupPath,
87
+ sha256: sha256(content),
88
+ timestamp: new Date().toISOString(),
89
+ operation,
90
+ serverName,
91
+ });
92
+ const rotated = rotateBackups(filePath, manifest, retentionLimit());
93
+ writeManifest(filePath, rotated);
94
+ return rotated.entries.find(entry => entry.backupPath === backupPath);
95
+ }
96
+ function restoreBackup(filePath, backupPath) {
97
+ const manifest = readManifest(filePath);
98
+ const resolvedBackup = backupPath
99
+ ? path_1.default.resolve(backupPath)
100
+ : [...manifest.entries].reverse().find(entry => path_1.default.resolve(entry.originalPath) === path_1.default.resolve(filePath))?.backupPath;
101
+ if (!resolvedBackup)
102
+ throw new Error(`No MCP backup available for ${filePath}`);
103
+ const entry = manifest.entries.find(item => path_1.default.resolve(item.backupPath) === path_1.default.resolve(resolvedBackup));
104
+ if (!entry)
105
+ throw new Error(`MCP backup is not present in manifest: ${resolvedBackup}`);
106
+ if (!fs_1.default.existsSync(entry.backupPath))
107
+ throw new Error(`MCP backup file not found: ${entry.backupPath}`);
108
+ const content = fs_1.default.readFileSync(entry.backupPath, 'utf8');
109
+ if (sha256(content) !== entry.sha256)
110
+ throw new Error(`MCP backup checksum mismatch: ${entry.backupPath}`);
111
+ createBackup(filePath, 'restore-preimage');
112
+ atomicWriteText(filePath, content);
113
+ return entry;
114
+ }
@@ -0,0 +1,44 @@
1
+ export type McpProfile = 'default' | 'enhanced' | 'experimental';
2
+ export interface McpDataPaths {
3
+ instructions: string;
4
+ feedback: string;
5
+ backups: string;
6
+ state: string;
7
+ auditLog: string;
8
+ logFile: string;
9
+ metrics: string;
10
+ messaging: string;
11
+ embeddings: string;
12
+ modelCache: string;
13
+ sqliteDb: string;
14
+ certs: string;
15
+ flags: string;
16
+ }
17
+ export interface McpEnvCatalogSection {
18
+ section: string;
19
+ }
20
+ export interface McpEnvCatalogVariable {
21
+ key: string;
22
+ desc: string;
23
+ active: boolean;
24
+ value: string;
25
+ defaultByProfile: Record<McpProfile, string>;
26
+ mcpEnvVisibility: 'always' | 'when-set' | 'never';
27
+ validate: string;
28
+ [key: string]: unknown;
29
+ }
30
+ export type McpEnvCatalogEntry = McpEnvCatalogSection | McpEnvCatalogVariable;
31
+ export interface McpProfileConfig {
32
+ profile: McpProfile;
33
+ root: string;
34
+ port: number;
35
+ host: string;
36
+ tls: boolean;
37
+ mutation: boolean;
38
+ logLevel: string;
39
+ }
40
+ export declare const DOCUMENTED_INDEX_SERVER_FLAGS: readonly ["INDEX_SERVER_ADD_TIMING", "INDEX_SERVER_ADMIN_API_KEY", "INDEX_SERVER_ADMIN_MAX_SESSION_HISTORY", "INDEX_SERVER_AGENT_ID", "INDEX_SERVER_ALWAYS_RELOAD", "INDEX_SERVER_ATOMIC_WRITE_BACKOFF_MS", "INDEX_SERVER_ATOMIC_WRITE_RETRIES", "INDEX_SERVER_AUTH_KEY", "INDEX_SERVER_AUTO_BACKUP", "INDEX_SERVER_AUTO_BACKUP_INTERVAL_MS", "INDEX_SERVER_AUTO_BACKUP_MAX_COUNT", "INDEX_SERVER_AUTO_SEED", "INDEX_SERVER_AUTO_SPLIT_OVERSIZED", "INDEX_SERVER_AUTO_USAGE_TRACK", "INDEX_SERVER_BACKUP_BEFORE_BULK_DELETE", "INDEX_SERVER_BACKUPS_DIR", "INDEX_SERVER_BODY_WARN_LENGTH", "INDEX_SERVER_BOOTSTRAP_AUTOCONFIRM", "INDEX_SERVER_BOOTSTRAP_TOKEN_TTL_SEC", "INDEX_SERVER_BUFFER_RING_APPEND", "INDEX_SERVER_BUFFER_RING_PRELOAD", "INDEX_SERVER_CACHE_MODE", "INDEX_SERVER_CANONICAL_DISABLE", "INDEX_SERVER_DASHBOARD", "INDEX_SERVER_DASHBOARD_GRAPH", "INDEX_SERVER_DASHBOARD_HOST", "INDEX_SERVER_DASHBOARD_PORT", "INDEX_SERVER_DASHBOARD_TLS", "INDEX_SERVER_DASHBOARD_TLS_CA", "INDEX_SERVER_DASHBOARD_TLS_CERT", "INDEX_SERVER_DASHBOARD_TLS_KEY", "INDEX_SERVER_DASHBOARD_TRIES", "INDEX_SERVER_DEBUG", "INDEX_SERVER_DIR", "INDEX_SERVER_DISABLE_EARLY_STDIN_BUFFER", "INDEX_SERVER_DISABLE_USAGE_CLAMP", "INDEX_SERVER_EMBEDDING_PATH", "INDEX_SERVER_ENABLE_INDEX_SERVER_POLLER", "INDEX_SERVER_EVENT_SILENT", "INDEX_SERVER_FATAL_EXIT_DELAY_MS", "INDEX_SERVER_FEATURES", "INDEX_SERVER_FEEDBACK_DIR", "INDEX_SERVER_FEEDBACK_MAX_ENTRIES", "INDEX_SERVER_FILE_TRACE", "INDEX_SERVER_FLAG_TOOLS_ADMIN", "INDEX_SERVER_FLAG_TOOLS_EXTENDED", "INDEX_SERVER_FLAGS_FILE", "INDEX_SERVER_FORCE_REBUILD", "INDEX_SERVER_GOV_HASH_CANON_VARIANTS", "INDEX_SERVER_GOV_HASH_HARDENING", "INDEX_SERVER_GOV_HASH_IMPORT_SET_SIZE", "INDEX_SERVER_GRAPH_INCLUDE_PRIMARY_EDGES", "INDEX_SERVER_GRAPH_LARGE_CATEGORY_CAP", "INDEX_SERVER_HEALTH_ERROR_THRESHOLD", "INDEX_SERVER_HEALTH_MEMORY_THRESHOLD", "INDEX_SERVER_HEALTH_MIN_UPTIME", "INDEX_SERVER_HEARTBEAT_MS", "INDEX_SERVER_HTTP_METRICS", "INDEX_SERVER_IDLE_KEEPALIVE_MS", "INDEX_SERVER_IDLE_READY_SENTINEL", "INDEX_SERVER_INIT_FEATURES", "INDEX_SERVER_ISSUE_317_COUNTER", "INDEX_SERVER_LEADER_PORT", "INDEX_SERVER_LEADER_URL", "INDEX_SERVER_LOAD_WARN_MS", "INDEX_SERVER_LOG_DIAG", "INDEX_SERVER_LOG_FILE", "INDEX_SERVER_LOG_JSON", "INDEX_SERVER_LOG_LEVEL", "INDEX_SERVER_LOG_MUTATION", "INDEX_SERVER_LOG_PROTOCOL", "INDEX_SERVER_LOG_SEARCH", "INDEX_SERVER_LOG_SYNC", "INDEX_SERVER_LOG_TOOLS", "INDEX_SERVER_MANIFEST_FASTLOAD", "INDEX_SERVER_MANIFEST_WRITE", "INDEX_SERVER_MAX_BULK_DELETE", "INDEX_SERVER_MAX_CONNECTIONS", "INDEX_SERVER_MAX_FILES", "INDEX_SERVER_MEMOIZE", "INDEX_SERVER_MEMOIZE_HASH", "INDEX_SERVER_MEMORY_MONITOR", "INDEX_SERVER_METRICS_DIR", "INDEX_SERVER_METRICS_FILE_STORAGE", "INDEX_SERVER_METRICS_MAX_FILES", "INDEX_SERVER_MINIMAL_DEBUG", "INDEX_SERVER_MODE", "INDEX_SERVER_MUTATION", "INDEX_SERVER_NORMALIZATION_LOG", "INDEX_SERVER_POLL_MS", "INDEX_SERVER_POLL_PROACTIVE", "INDEX_SERVER_PREFLIGHT_MODULES", "INDEX_SERVER_PREFLIGHT_STRICT", "INDEX_SERVER_PROFILE", "INDEX_SERVER_PWS_EXIT_MS", "INDEX_SERVER_RATE_LIMIT", "INDEX_SERVER_READ_BACKOFF_MS", "INDEX_SERVER_READ_RETRIES", "INDEX_SERVER_REFERENCE_MODE", "INDEX_SERVER_REQUEST_TIMEOUT", "INDEX_SERVER_REQUIRE_AUTH_ALL", "INDEX_SERVER_REQUIRE_CATEGORY", "INDEX_SERVER_RESOURCE_CAPACITY", "INDEX_SERVER_RESOURCE_SAMPLE_INTERVAL_MS", "INDEX_SERVER_SEED_VERBOSE", "INDEX_SERVER_SEMANTIC_CACHE_DIR", "INDEX_SERVER_SEMANTIC_DEVICE", "INDEX_SERVER_SEMANTIC_ENABLED", "INDEX_SERVER_SEMANTIC_LOCAL_ONLY", "INDEX_SERVER_SEMANTIC_MODEL", "INDEX_SERVER_SEARCH_OMIT_ZERO_QUERY", "INDEX_SERVER_SESSION_BACKUP_INTEGRATION", "INDEX_SERVER_SESSION_DEDUPLICATION_ENABLED", "INDEX_SERVER_SESSION_MAX_CONNECTION_HISTORY_DAYS", "INDEX_SERVER_SESSION_MAX_HISTORY_DAYS", "INDEX_SERVER_SESSION_MAX_HISTORY_ENTRIES", "INDEX_SERVER_SESSION_PERSISTENCE_DIR", "INDEX_SERVER_SESSION_PERSISTENCE_ENABLED", "INDEX_SERVER_SESSION_PERSISTENCE_INTERVAL_MS", "INDEX_SERVER_SHARED_SERVER_SENTINEL", "INDEX_SERVER_SQLITE_MIGRATE_ON_START", "INDEX_SERVER_SQLITE_PATH", "INDEX_SERVER_SQLITE_VEC_ENABLED", "INDEX_SERVER_SQLITE_VEC_PATH", "INDEX_SERVER_SQLITE_WAL", "INDEX_SERVER_STALE_THRESHOLD_MS", "INDEX_SERVER_STATE_DIR", "INDEX_SERVER_STORAGE_BACKEND", "INDEX_SERVER_STRESS_DIAG", "INDEX_SERVER_STRESS_MODE", "INDEX_SERVER_STRICT_", "INDEX_SERVER_STRICT_CREATE", "INDEX_SERVER_STRICT_REMOVE", "INDEX_SERVER_TEST_MODE", "INDEX_SERVER_TEST_STRICT_VISIBILITY", "INDEX_SERVER_TIMING_JSON", "INDEX_SERVER_TOOLCALL_APPEND_LOG", "INDEX_SERVER_TOOLCALL_CHUNK_SIZE", "INDEX_SERVER_TOOLCALL_COMPACT_MS", "INDEX_SERVER_TOOLCALL_FLUSH_MS", "INDEX_SERVER_TRACE", "INDEX_SERVER_TRACE_", "INDEX_SERVER_TRACE_ALL", "INDEX_SERVER_TRACE_BUFFER_", "INDEX_SERVER_TRACE_BUFFER_DUMP_ON_EXIT", "INDEX_SERVER_TRACE_BUFFER_FILE", "INDEX_SERVER_TRACE_BUFFER_SIZE", "INDEX_SERVER_TRACE_CALLSITE", "INDEX_SERVER_TRACE_CATEGORIES", "INDEX_SERVER_TRACE_DIR", "INDEX_SERVER_TRACE_DISPATCH_DIAG", "INDEX_SERVER_TRACE_FILE", "INDEX_SERVER_TRACE_FSYNC", "INDEX_SERVER_TRACE_LEVEL", "INDEX_SERVER_TRACE_MAX_FILE_SIZE", "INDEX_SERVER_TRACE_PERSIST", "INDEX_SERVER_TRACE_QUERY_DIAG", "INDEX_SERVER_TRACE_SESSION", "INDEX_SERVER_USAGE_FLUSH_MS", "INDEX_SERVER_USAGE_SNAPSHOT_PATH", "INDEX_SERVER_VALIDATION_MODE", "INDEX_SERVER_VERBOSE_LOGGING", "INDEX_SERVER_VISIBILITY_DIAG", "INDEX_SERVER_WORKSPACE"];
41
+ export declare function toForwardSlashes(value: string): string;
42
+ export declare function resolveDataPaths(root: string): McpDataPaths;
43
+ export declare function buildEnvCatalog(config: McpProfileConfig, paths: McpDataPaths): McpEnvCatalogEntry[];
44
+ export declare function activeEnvFromCatalog(catalog: McpEnvCatalogEntry[]): Record<string, string>;