@jagilber-org/index-server 1.28.2 → 1.28.5

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 (33) hide show
  1. package/CONTRIBUTING.md +3 -2
  2. package/README.md +29 -10
  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 +315 -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/setup-wizard.mjs +50 -344
  33. package/server.json +2 -2
@@ -0,0 +1,130 @@
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.resolveServerLaunch = exports.resolveDataPaths = exports.resolveConfigTargets = exports.buildEnvCatalog = void 0;
7
+ exports.listServers = listServers;
8
+ exports.getServer = getServer;
9
+ exports.upsertServer = upsertServer;
10
+ exports.removeServer = removeServer;
11
+ exports.restoreLatestBackup = restoreLatestBackup;
12
+ exports.restoreServer = restoreServer;
13
+ exports.validateFile = validateFile;
14
+ exports.renderServerConfigForTarget = renderServerConfigForTarget;
15
+ exports.writeGeneratedConfig = writeGeneratedConfig;
16
+ const fs_1 = __importDefault(require("fs"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const backup_1 = require("./backup");
19
+ const formats_1 = require("./formats");
20
+ Object.defineProperty(exports, "resolveServerLaunch", { enumerable: true, get: function () { return formats_1.resolveServerLaunch; } });
21
+ const flagCatalog_1 = require("./flagCatalog");
22
+ Object.defineProperty(exports, "buildEnvCatalog", { enumerable: true, get: function () { return flagCatalog_1.buildEnvCatalog; } });
23
+ Object.defineProperty(exports, "resolveDataPaths", { enumerable: true, get: function () { return flagCatalog_1.resolveDataPaths; } });
24
+ const paths_1 = require("./paths");
25
+ Object.defineProperty(exports, "resolveConfigTargets", { enumerable: true, get: function () { return paths_1.resolveConfigTargets; } });
26
+ const validate_1 = require("./validate");
27
+ const backup_2 = require("./backup");
28
+ function firstTarget(options) {
29
+ return (0, paths_1.resolveConfigTargets)({
30
+ target: options.target,
31
+ targets: options.targets,
32
+ scope: options.scope,
33
+ root: options.root,
34
+ })[0];
35
+ }
36
+ function defaultName(options) {
37
+ return options.name ?? 'index-server';
38
+ }
39
+ function buildConfig(options) {
40
+ const profile = options.profile ?? 'default';
41
+ const tls = options.tls ?? (profile === 'enhanced' || profile === 'experimental');
42
+ return {
43
+ profile,
44
+ root: path_1.default.resolve(options.root ?? process.env.INDEX_SERVER_MCP_CONFIG_ROOT ?? process.cwd()),
45
+ port: options.port ?? 8787,
46
+ host: options.host ?? '127.0.0.1',
47
+ tls,
48
+ mutation: options.mutation ?? true,
49
+ logLevel: options.logLevel ?? (profile === 'experimental' ? 'debug' : 'info'),
50
+ serverName: defaultName(options),
51
+ };
52
+ }
53
+ function readExistingText(filePath, format) {
54
+ if (fs_1.default.existsSync(filePath))
55
+ return fs_1.default.readFileSync(filePath, 'utf8');
56
+ return (0, formats_1.renderConfig)(format, '__placeholder__', {
57
+ command: 'node',
58
+ args: ['dist/server/index-server.js'],
59
+ }).split('__placeholder__').join('index-server');
60
+ }
61
+ function listServers(options = {}) {
62
+ const target = firstTarget(options);
63
+ const config = (0, formats_1.readConfigFile)(target.format, target.path);
64
+ const servers = Object.keys((0, formats_1.getServerMap)(config, target.format));
65
+ return { ok: true, action: 'list', ...target, servers };
66
+ }
67
+ function getServer(options = {}) {
68
+ const target = firstTarget(options);
69
+ const config = (0, formats_1.readConfigFile)(target.format, target.path);
70
+ const server = (0, formats_1.getServerMap)(config, target.format)[defaultName(options)] ?? null;
71
+ return { ok: true, action: 'get', ...target, name: defaultName(options), server };
72
+ }
73
+ function upsertServer(options = {}) {
74
+ const target = firstTarget(options);
75
+ const config = buildConfig(options);
76
+ const paths = (0, flagCatalog_1.resolveDataPaths)(config.root);
77
+ const entry = (0, formats_1.buildServerEntry)(target.format, config, paths, options.env);
78
+ const existingText = fs_1.default.existsSync(target.path) ? fs_1.default.readFileSync(target.path, 'utf8') : '';
79
+ if (existingText)
80
+ (0, validate_1.assertValidConfigObject)(target.format, (0, formats_1.parseConfigText)(target.format, existingText), 'read');
81
+ const nextText = (0, formats_1.upsertConfigText)(target.format, existingText, config.serverName, entry);
82
+ const nextConfig = (0, formats_1.parseConfigText)(target.format, nextText);
83
+ (0, validate_1.assertValidConfigObject)(target.format, nextConfig, 'write');
84
+ if (!options.dryRun) {
85
+ const backup = (0, backup_1.createBackup)(target.path, 'upsert', config.serverName);
86
+ (0, backup_2.atomicWriteText)(target.path, nextText);
87
+ (0, validate_1.assertValidConfigObject)(target.format, (0, formats_1.readConfigFile)(target.format, target.path), 'post-write');
88
+ return { ok: true, action: 'upsert', ...target, name: config.serverName, server: entry, backupPath: backup?.backupPath };
89
+ }
90
+ return { ok: true, action: 'upsert', ...target, name: config.serverName, server: entry, dryRun: true };
91
+ }
92
+ function removeServer(options = {}) {
93
+ const target = firstTarget(options);
94
+ const name = defaultName(options);
95
+ const existingText = readExistingText(target.path, target.format);
96
+ (0, validate_1.assertValidConfigObject)(target.format, (0, formats_1.parseConfigText)(target.format, existingText), 'read');
97
+ const nextText = (0, formats_1.removeConfigText)(target.format, existingText, name);
98
+ (0, validate_1.assertValidConfigObject)(target.format, (0, formats_1.parseConfigText)(target.format, nextText), 'write');
99
+ if (!options.dryRun) {
100
+ const backup = (0, backup_1.createBackup)(target.path, 'remove', name);
101
+ (0, backup_2.atomicWriteText)(target.path, nextText);
102
+ (0, validate_1.assertValidConfigObject)(target.format, (0, formats_1.readConfigFile)(target.format, target.path), 'post-write');
103
+ return { ok: true, action: 'remove', ...target, name, backupPath: backup?.backupPath };
104
+ }
105
+ return { ok: true, action: 'remove', ...target, name, dryRun: true };
106
+ }
107
+ function restoreLatestBackup(options = {}) {
108
+ const target = firstTarget(options);
109
+ const restored = (0, backup_1.restoreBackup)(target.path, options.backup);
110
+ (0, validate_1.assertValidConfigObject)(target.format, (0, formats_1.readConfigFile)(target.format, target.path), 'restore');
111
+ return { ok: true, action: 'restore', ...target, backupPath: restored.backupPath };
112
+ }
113
+ function restoreServer(options = {}) {
114
+ return restoreLatestBackup(options);
115
+ }
116
+ function validateFile(options = {}) {
117
+ const target = firstTarget(options);
118
+ const config = (0, formats_1.readConfigFile)(target.format, target.path);
119
+ const validation = (0, validate_1.validateConfigObject)(target.format, config);
120
+ return { ok: validation.ok, action: 'validate', ...target, validation };
121
+ }
122
+ function renderServerConfigForTarget(target, options = {}) {
123
+ const config = buildConfig({ ...options, target: target.target, scope: target.format === 'vscode-global' ? 'global' : options.scope });
124
+ const paths = (0, flagCatalog_1.resolveDataPaths)(config.root);
125
+ const entry = (0, formats_1.buildServerEntry)(target.format, config, paths, options.env);
126
+ return (0, formats_1.renderConfig)(target.format, config.serverName, entry);
127
+ }
128
+ function writeGeneratedConfig(filePath, content) {
129
+ (0, backup_2.atomicWriteText)(filePath, content);
130
+ }
@@ -0,0 +1,2 @@
1
+ export declare function parseJsonc(text: string): Record<string, unknown>;
2
+ export declare function applyJsoncEdit(text: string, editPath: Array<string | number>, value: unknown): string;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseJsonc = parseJsonc;
4
+ exports.applyJsoncEdit = applyJsoncEdit;
5
+ const jsonc_parser_1 = require("jsonc-parser");
6
+ function formatErrors(errors) {
7
+ return errors.map(error => `${error.error} at offset ${error.offset}`).join(', ');
8
+ }
9
+ function parseJsonc(text) {
10
+ const errors = [];
11
+ const parsed = (0, jsonc_parser_1.parse)(text, errors, { allowTrailingComma: true, disallowComments: false });
12
+ if (errors.length > 0)
13
+ throw new Error(`Invalid JSONC: ${formatErrors(errors)}`);
14
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
15
+ throw new Error('Invalid JSONC: expected object root');
16
+ }
17
+ return parsed;
18
+ }
19
+ function applyJsoncEdit(text, editPath, value) {
20
+ const errors = [];
21
+ (0, jsonc_parser_1.parse)(text, errors, { allowTrailingComma: true, disallowComments: false });
22
+ if (errors.length > 0)
23
+ throw new Error(`Invalid JSONC: ${formatErrors(errors)}`);
24
+ const edits = (0, jsonc_parser_1.modify)(text, editPath, value, {
25
+ formattingOptions: { insertSpaces: true, tabSize: 2, eol: '\n' },
26
+ getInsertionIndex: properties => properties.length,
27
+ });
28
+ return (0, jsonc_parser_1.applyEdits)(text, edits);
29
+ }
@@ -0,0 +1,18 @@
1
+ export type McpClientTarget = 'vscode' | 'copilot-cli' | 'claude';
2
+ export type McpConfigFormat = 'vscode' | 'vscode-global' | 'copilot-cli' | 'claude';
3
+ export type McpScope = 'repo' | 'global';
4
+ export interface ResolveTargetOptions {
5
+ targets?: McpClientTarget[];
6
+ target?: McpClientTarget;
7
+ scope?: McpScope;
8
+ root?: string;
9
+ home?: string;
10
+ appData?: string;
11
+ }
12
+ export interface McpTargetInfo {
13
+ target: McpClientTarget;
14
+ format: McpConfigFormat;
15
+ path: string;
16
+ }
17
+ export declare function defaultConfigRoot(): string;
18
+ export declare function resolveConfigTargets(options?: ResolveTargetOptions): McpTargetInfo[];
@@ -0,0 +1,50 @@
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.defaultConfigRoot = defaultConfigRoot;
7
+ exports.resolveConfigTargets = resolveConfigTargets;
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ function envValue(key) {
11
+ const value = process.env[key];
12
+ return value && value.length > 0 ? value : undefined;
13
+ }
14
+ function defaultConfigRoot() {
15
+ return path_1.default.resolve(envValue('INDEX_SERVER_MCP_CONFIG_ROOT') ?? process.cwd());
16
+ }
17
+ function resolveConfigTargets(options = {}) {
18
+ const pathApi = process.platform === 'win32' ? path_1.default.win32 : path_1.default.posix;
19
+ const root = pathApi.resolve(options.root ?? defaultConfigRoot());
20
+ const scope = options.scope ?? 'repo';
21
+ const home = options.home ?? os_1.default.homedir();
22
+ const appData = options.appData ?? envValue('APPDATA') ?? pathApi.join(home, 'AppData', 'Roaming');
23
+ const targets = options.targets ?? (options.target ? [options.target] : ['vscode']);
24
+ const isWin = process.platform === 'win32';
25
+ const isMac = process.platform === 'darwin';
26
+ const results = [];
27
+ for (const target of targets) {
28
+ if (target === 'vscode') {
29
+ if (scope === 'global') {
30
+ const dir = isWin ? pathApi.join(appData, 'Code', 'User') : pathApi.join(home, '.config', 'Code', 'User');
31
+ results.push({ target, format: 'vscode-global', path: pathApi.join(dir, 'mcp.json') });
32
+ }
33
+ else {
34
+ results.push({ target, format: 'vscode', path: pathApi.join(root, '.vscode', 'mcp.json') });
35
+ }
36
+ }
37
+ else if (target === 'copilot-cli') {
38
+ results.push({ target, format: 'copilot-cli', path: pathApi.join(home, '.copilot', 'mcp-config.json') });
39
+ }
40
+ else if (target === 'claude') {
41
+ const dir = isWin
42
+ ? pathApi.join(appData, 'Claude')
43
+ : isMac
44
+ ? pathApi.join(home, 'Library', 'Application Support', 'Claude')
45
+ : pathApi.join(home, '.config', 'Claude');
46
+ results.push({ target, format: 'claude', path: pathApi.join(dir, 'claude_desktop_config.json') });
47
+ }
48
+ }
49
+ return results;
50
+ }
@@ -0,0 +1,7 @@
1
+ import type { McpConfigFormat } from './paths';
2
+ export interface ValidationResult {
3
+ ok: boolean;
4
+ errors: string[];
5
+ }
6
+ export declare function validateConfigObject(format: McpConfigFormat, config: Record<string, unknown>): ValidationResult;
7
+ export declare function assertValidConfigObject(format: McpConfigFormat, config: Record<string, unknown>, phase: string): void;
@@ -0,0 +1,62 @@
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.validateConfigObject = validateConfigObject;
7
+ exports.assertValidConfigObject = assertValidConfigObject;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const ajv_1 = __importDefault(require("ajv"));
11
+ const ajv_formats_1 = __importDefault(require("ajv-formats"));
12
+ const flagCatalog_1 = require("./flagCatalog");
13
+ const formats_1 = require("./formats");
14
+ function schemaRoot() {
15
+ return path_1.default.resolve(__dirname, '..', '..', '..', 'schemas');
16
+ }
17
+ function schemaPathForFormat(format) {
18
+ if (format === 'vscode' || format === 'vscode-global')
19
+ return path_1.default.join(schemaRoot(), 'mcp.vscode.schema.json');
20
+ if (format === 'copilot-cli')
21
+ return path_1.default.join(schemaRoot(), 'mcp.copilot-cli.schema.json');
22
+ return path_1.default.join(schemaRoot(), 'mcp.claude.schema.json');
23
+ }
24
+ function loadJson(filePath) {
25
+ return JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
26
+ }
27
+ function createAjv() {
28
+ const ajv = new ajv_1.default({ allErrors: true, strict: false });
29
+ (0, ajv_formats_1.default)(ajv);
30
+ ajv.addSchema(loadJson(path_1.default.join(schemaRoot(), 'mcp.indexServerEnv.schema.json')), 'mcp.indexServerEnv.schema.json');
31
+ return ajv;
32
+ }
33
+ function collectAjvErrors(validate) {
34
+ return (validate.errors ?? []).map((error) => `${error.instancePath || '/'} ${error.message ?? 'failed validation'}`);
35
+ }
36
+ function validateConfigObject(format, config) {
37
+ const ajv = createAjv();
38
+ const schema = loadJson(schemaPathForFormat(format));
39
+ const validate = ajv.compile(schema);
40
+ const valid = validate(config);
41
+ const errors = valid ? [] : collectAjvErrors(validate);
42
+ try {
43
+ for (const entry of Object.values((0, formats_1.getServerMap)(config, format))) {
44
+ if (!entry.env)
45
+ continue;
46
+ for (const key of Object.keys(entry.env)) {
47
+ if (key.startsWith('INDEX_SERVER_') && !flagCatalog_1.DOCUMENTED_INDEX_SERVER_FLAGS.includes(key)) {
48
+ errors.push(`env contains unsupported INDEX_SERVER key: ${key}`);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ catch (error) {
54
+ errors.push(error instanceof Error ? error.message : String(error));
55
+ }
56
+ return { ok: errors.length === 0, errors };
57
+ }
58
+ function assertValidConfigObject(format, config, phase) {
59
+ const result = validateConfigObject(format, config);
60
+ if (!result.ok)
61
+ throw new Error(`MCP config validation failed during ${phase}: ${result.errors.join('; ')}`);
62
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@jagilber-org/index-server",
3
- "version": "1.28.2",
3
+ "version": "1.28.5",
4
4
  "mcpName": "io.github.jagilber-org/index-server",
5
5
  "description": "MCP instruction indexing server with search, CRUD, validation, and cross-repo knowledge promotion.",
6
6
  "publishConfig": {
7
- "registry": "https://registry.npmjs.org/",
7
+ "registry": "https://npm.pkg.github.com",
8
8
  "access": "public"
9
9
  },
10
10
  "type": "commonjs",
@@ -134,6 +134,7 @@
134
134
  "ajv-formats": "^2.1.1",
135
135
  "express": "^5.2.1",
136
136
  "express-rate-limit": "^8.3.2",
137
+ "jsonc-parser": "^3.3.1",
137
138
  "safe-regex2": "^5.1.1",
138
139
  "sqlite-vec": "0.1.9",
139
140
  "ws": "^8.19.0",
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "mcp.claude.schema.json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["mcpServers"],
7
+ "properties": {
8
+ "mcpServers": {
9
+ "type": "object",
10
+ "additionalProperties": {
11
+ "type": "object",
12
+ "additionalProperties": false,
13
+ "required": ["command", "args"],
14
+ "properties": {
15
+ "type": { "type": "string" },
16
+ "command": { "type": "string", "minLength": 1 },
17
+ "args": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
18
+ "cwd": { "type": "string" },
19
+ "env": {
20
+ "type": "object",
21
+ "additionalProperties": { "type": "string" }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "mcp.copilot-cli.schema.json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["mcpServers"],
7
+ "properties": {
8
+ "mcpServers": {
9
+ "type": "object",
10
+ "additionalProperties": {
11
+ "type": "object",
12
+ "additionalProperties": false,
13
+ "required": ["command", "args"],
14
+ "properties": {
15
+ "type": { "type": "string" },
16
+ "command": { "type": "string", "minLength": 1 },
17
+ "args": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
18
+ "cwd": { "type": "string" },
19
+ "tools": { "type": "array", "items": { "type": "string" } },
20
+ "env": {
21
+ "type": "object",
22
+ "additionalProperties": { "type": "string" }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "mcp.indexServerEnv.schema.json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "patternProperties": {
7
+ "^INDEX_SERVER_[A-Z0-9_]+$": { "type": "string" }
8
+ }
9
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "mcp.vscode.schema.json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["servers"],
7
+ "properties": {
8
+ "servers": {
9
+ "type": "object",
10
+ "additionalProperties": {
11
+ "type": "object",
12
+ "additionalProperties": false,
13
+ "required": ["type", "command", "args"],
14
+ "properties": {
15
+ "type": { "const": "stdio" },
16
+ "command": { "type": "string", "minLength": 1 },
17
+ "args": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
18
+ "cwd": { "type": "string" },
19
+ "env": {
20
+ "type": "object",
21
+ "additionalProperties": { "type": "string" }
22
+ }
23
+ }
24
+ }
25
+ },
26
+ "inputs": { "type": "array" }
27
+ }
28
+ }