genexus-mcp 2.3.3 → 2.3.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.
@@ -11,6 +11,9 @@ const {
11
11
  createConfigFile,
12
12
  patchClientConfig,
13
13
  unpatchClientConfig,
14
+ getClientConfigTargets,
15
+ filterClientTargets,
16
+ listSupportedClientIds,
14
17
  getLocalAppDataCacheDir,
15
18
  readGeneXusVersionFromInstall,
16
19
  discoverGeneXusInstallation,
@@ -21,6 +24,25 @@ const {
21
24
  switchActiveKb
22
25
  } = require('../lib/config');
23
26
 
27
+ function resolveClientIds(options) {
28
+ if (!options || !options.clients) return null;
29
+ return String(options.clients)
30
+ .split(',')
31
+ .map((s) => s.trim())
32
+ .filter(Boolean);
33
+ }
34
+
35
+ function validateClientIds(ids) {
36
+ if (!ids) return { ok: true };
37
+ const supported = new Set(listSupportedClientIds());
38
+ const invalid = ids.filter((id) => !supported.has(id));
39
+ if (invalid.length === 0) return { ok: true };
40
+ return {
41
+ ok: false,
42
+ message: `Unknown client id(s): ${invalid.join(', ')}. Supported: ${[...supported].join(', ')}.`
43
+ };
44
+ }
45
+
24
46
  function parseFieldSelection(raw) {
25
47
  if (!raw) return null;
26
48
  return raw.split(',').map((v) => v.trim()).filter(Boolean);
@@ -769,8 +791,24 @@ async function runInteractiveInit(ctx) {
769
791
  const gxAnswer = await question(`\n2) GeneXus installation path (default: ${defaultGx}):\n> `);
770
792
  const finalGx = String(gxAnswer || '').trim() || defaultGx;
771
793
 
794
+ const allTargets = getClientConfigTargets();
795
+ const platformTargets = filterClientTargets(allTargets, { platform: process.platform });
796
+ ctx.stderr.write('\n3) Select AI agents to register (y/N per agent; Enter accepts default):\n');
797
+ const selectedIds = [];
798
+ for (const target of platformTargets) {
799
+ const installed = fs.existsSync(target.path);
800
+ const defaultYes = installed;
801
+ const tag = installed ? 'detected' : 'not detected';
802
+ const prompt = ` - ${target.name} [${tag}] (${defaultYes ? 'Y/n' : 'y/N'}): `;
803
+ const ans = (await question(prompt)).trim().toLowerCase();
804
+ const yes = ans === '' ? defaultYes : (ans === 'y' || ans === 'yes');
805
+ if (yes) selectedIds.push(target.id);
806
+ }
807
+
772
808
  const created = createConfigFile(finalKb, finalGx);
773
- const patchResult = patchClientConfig(created.targetConfigPath);
809
+ const patchResult = selectedIds.length
810
+ ? patchClientConfig(created.targetConfigPath, { ids: selectedIds, onlyExisting: false })
811
+ : { patched: [], failed: [], skipped: [] };
774
812
 
775
813
  return {
776
814
  exitCode: ctx.EXIT_CODES.OK,
@@ -785,7 +823,8 @@ async function runInteractiveInit(ctx) {
785
823
  help: patchResult.patched.length === 0 ? ['Set `GX_CONFIG_PATH` in your MCP client env to the generated config path.'] : [],
786
824
  meta: {
787
825
  patchedClients: patchResult.patched,
788
- failedClients: patchResult.failed
826
+ failedClients: patchResult.failed,
827
+ skippedClients: patchResult.skipped || []
789
828
  }
790
829
  }
791
830
  };
@@ -843,9 +882,20 @@ async function handleInit(options, ctx) {
843
882
  const kbName = path.basename(resolution.kb.value);
844
883
  addKbToConfig(created.targetConfigPath, kbName, resolution.kb.value);
845
884
 
846
- let patchResult = { patched: [], failed: [] };
885
+ let patchResult = { patched: [], failed: [], skipped: [] };
847
886
  if (options.writeClients) {
848
- patchResult = patchClientConfig(created.targetConfigPath);
887
+ const ids = resolveClientIds(options);
888
+ const validation = validateClientIds(ids);
889
+ if (!validation.ok) {
890
+ return {
891
+ exitCode: ctx.EXIT_CODES.USAGE,
892
+ envelope: usageEnvelope(validation.message, ctx.EXIT_CODES.USAGE)
893
+ };
894
+ }
895
+ patchResult = patchClientConfig(created.targetConfigPath, {
896
+ ids,
897
+ onlyExisting: !options.allClients
898
+ });
849
899
  }
850
900
 
851
901
  const verification = await runPostInitVerification({
@@ -894,6 +944,7 @@ async function handleInit(options, ctx) {
894
944
  meta: {
895
945
  patchedClients: patchResult.patched,
896
946
  failedClients: patchResult.failed,
947
+ skippedClients: patchResult.skipped || [],
897
948
  smokeSkipped: !!options.noSmoke,
898
949
  warmed: !!warm && warm.status === 'pass'
899
950
  }
@@ -1229,11 +1280,12 @@ function commandHelpMap() {
1229
1280
  examples: ['genexus-mcp config show', 'genexus-mcp config show --full --format json']
1230
1281
  },
1231
1282
  init: {
1232
- usage: 'genexus-mcp init [--kb <path>] [--gx <path>] [--write-clients] [--no-smoke] [--warm] [--format ...] OR genexus-mcp init --interactive',
1283
+ usage: 'genexus-mcp init [--kb <path>] [--gx <path>] [--write-clients] [--clients <csv>] [--all-clients] [--no-smoke] [--warm] [--format ...] OR genexus-mcp init --interactive',
1233
1284
  examples: [
1234
1285
  'genexus-mcp init # zero-config: auto-discovers GX from registry/Program Files and KB from cwd',
1235
1286
  'genexus-mcp init --kb "C:\\KBs\\MyKB" --gx "C:\\Program Files (x86)\\GeneXus\\GeneXus18"',
1236
- 'genexus-mcp init --interactive',
1287
+ 'genexus-mcp init --interactive # prompts per detected agent (Claude Desktop/Code, Gemini CLI, Cursor, Codex CLI, OpenCode, ...)',
1288
+ 'genexus-mcp init --kb <path> --gx <path> --write-clients --clients claude-code,gemini-cli,cursor',
1237
1289
  'genexus-mcp init --kb <path> --gx <path> --no-smoke'
1238
1290
  ]
1239
1291
  },
package/cli/index.js CHANGED
@@ -39,6 +39,8 @@ const GLOBAL_DEFAULTS = {
39
39
  fields: null,
40
40
  interactive: false,
41
41
  writeClients: false,
42
+ clients: null,
43
+ allClients: false,
42
44
  mcpSmoke: false,
43
45
  noSmoke: false,
44
46
  warm: false,
@@ -186,6 +188,15 @@ function parseArgs(argv) {
186
188
  else result.unknownFlags.push('--query requires a value');
187
189
  break;
188
190
  }
191
+ case 'clients': {
192
+ const val = takeValue();
193
+ if (val) result.options.clients = val;
194
+ else result.unknownFlags.push('--clients requires a value');
195
+ break;
196
+ }
197
+ case 'all-clients':
198
+ result.options.allClients = true;
199
+ break;
189
200
  case 'action': {
190
201
  const val = takeValue();
191
202
  if (val) result.options.action = val;
package/cli/lib/config.js CHANGED
@@ -58,23 +58,73 @@ function discoverGeneXusFromRegistry() {
58
58
  }
59
59
 
60
60
  function discoverGeneXusInstallation() {
61
- const possible = [
62
- 'C:\\Program Files (x86)\\GeneXus\\GeneXus18',
63
- 'C:\\Program Files (x86)\\GeneXus\\GeneXus17',
64
- 'C:\\Program Files (x86)\\GeneXus\\GeneXus16',
65
- 'C:\\Program Files\\GeneXus\\GeneXus18',
66
- 'C:\\Program Files\\GeneXus\\GeneXus17'
67
- ];
68
-
69
- for (const candidate of possible) {
70
- if (fs.existsSync(path.join(candidate, 'genexus.exe'))) {
71
- return candidate;
72
- }
61
+ if (process.env.GENEXUS_HOME) {
62
+ const candidate = process.env.GENEXUS_HOME.replace(/[\\/]+$/, '');
63
+ if (fs.existsSync(path.join(candidate, 'genexus.exe'))) return candidate;
73
64
  }
74
65
 
75
66
  const fromRegistry = discoverGeneXusFromRegistry();
76
67
  if (fromRegistry) return fromRegistry;
77
68
 
69
+ const programDirs = [];
70
+ if (process.env['ProgramFiles(x86)']) programDirs.push(process.env['ProgramFiles(x86)']);
71
+ if (process.env.ProgramFiles) programDirs.push(process.env.ProgramFiles);
72
+ // Cover non-default drives where IT often installs SDKs.
73
+ for (const drive of ['C', 'D', 'E']) {
74
+ programDirs.push(`${drive}:\\Program Files (x86)`);
75
+ programDirs.push(`${drive}:\\Program Files`);
76
+ }
77
+
78
+ const versions = ['GeneXus18', 'GeneXus17', 'GeneXus16'];
79
+ const seen = new Set();
80
+ for (const base of programDirs) {
81
+ const root = path.join(base, 'GeneXus');
82
+ const key = root.toLowerCase();
83
+ if (seen.has(key)) continue;
84
+ seen.add(key);
85
+ for (const ver of versions) {
86
+ const candidate = path.join(root, ver);
87
+ if (fs.existsSync(path.join(candidate, 'genexus.exe'))) {
88
+ return candidate;
89
+ }
90
+ }
91
+ // Also scan any GeneXus* sibling (e.g. custom-named "GeneXus18 U10").
92
+ try {
93
+ if (fs.existsSync(root)) {
94
+ for (const entry of fs.readdirSync(root)) {
95
+ if (!/^GeneXus/i.test(entry)) continue;
96
+ const candidate = path.join(root, entry);
97
+ if (fs.existsSync(path.join(candidate, 'genexus.exe'))) {
98
+ return candidate;
99
+ }
100
+ }
101
+ }
102
+ } catch {
103
+ }
104
+ }
105
+
106
+ const fromPath = discoverGeneXusFromPath();
107
+ if (fromPath) return fromPath;
108
+
109
+ return null;
110
+ }
111
+
112
+ function discoverGeneXusFromPath() {
113
+ if (process.platform !== 'win32') return null;
114
+ try {
115
+ const { execFileSync } = require('child_process');
116
+ const out = execFileSync('where.exe', ['genexus.exe'], {
117
+ encoding: 'utf8',
118
+ stdio: ['ignore', 'pipe', 'ignore'],
119
+ windowsHide: true,
120
+ timeout: 3000
121
+ });
122
+ const first = out.split(/\r?\n/).map((s) => s.trim()).find(Boolean);
123
+ if (first && fs.existsSync(first)) {
124
+ return path.dirname(first);
125
+ }
126
+ } catch {
127
+ }
78
128
  return null;
79
129
  }
80
130
 
@@ -145,77 +195,132 @@ function createConfigFile(kbPath, gxPath) {
145
195
  };
146
196
  }
147
197
 
148
- function patchClientConfig(targetConfigPath) {
149
- const clients = getClientConfigTargets();
150
-
198
+ function getLauncher() {
151
199
  // Set by scripts/install.ps1 for fixed-path corporate installs — clients
152
200
  // launch the gateway exe directly instead of resolving via the npx cache.
153
201
  const directExe = process.env.GENEXUS_MCP_GATEWAY_EXE;
154
- const launcher = directExe
202
+ return directExe
155
203
  ? { command: directExe, args: [] }
156
204
  : { command: process.platform === 'win32' ? 'npx.cmd' : 'npx', args: ['-y', 'genexus-mcp@latest'] };
205
+ }
157
206
 
158
- const patched = [];
159
- const failed = [];
207
+ function getClientConfigTargets() {
208
+ const home = os.homedir();
209
+ const xdgConfig = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
210
+ return [
211
+ {
212
+ id: 'claude-desktop-win',
213
+ name: 'Claude Desktop (Windows)',
214
+ format: 'mcpServers',
215
+ path: path.join(home, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json'),
216
+ platforms: ['win32']
217
+ },
218
+ {
219
+ id: 'claude-desktop-mac',
220
+ name: 'Claude Desktop (macOS)',
221
+ format: 'mcpServers',
222
+ path: path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
223
+ platforms: ['darwin']
224
+ },
225
+ {
226
+ id: 'antigravity',
227
+ name: 'Antigravity',
228
+ format: 'mcpServers',
229
+ path: path.join(home, '.gemini', 'antigravity', 'mcp_config.json')
230
+ },
231
+ {
232
+ id: 'claude-code',
233
+ name: 'Claude Code',
234
+ format: 'mcpServers',
235
+ path: path.join(home, '.claude.json')
236
+ },
237
+ {
238
+ id: 'gemini-cli',
239
+ name: 'Gemini CLI',
240
+ format: 'mcpServers',
241
+ path: path.join(home, '.gemini', 'settings.json')
242
+ },
243
+ {
244
+ id: 'cursor',
245
+ name: 'Cursor',
246
+ format: 'mcpServers',
247
+ path: path.join(home, '.cursor', 'mcp.json')
248
+ },
249
+ {
250
+ id: 'opencode',
251
+ name: 'OpenCode',
252
+ format: 'opencode',
253
+ path: path.join(xdgConfig, 'opencode', 'opencode.json')
254
+ },
255
+ {
256
+ id: 'codex-cli',
257
+ name: 'Codex CLI',
258
+ format: 'codex-toml',
259
+ path: path.join(home, '.codex', 'config.toml')
260
+ }
261
+ ];
262
+ }
263
+
264
+ function listSupportedClientIds() {
265
+ return getClientConfigTargets().map((c) => c.id);
266
+ }
160
267
 
161
- for (const client of clients) {
162
- if (!fs.existsSync(client.path)) continue;
268
+ function filterClientTargets(targets, opts = {}) {
269
+ const { ids, onlyExisting, platform } = opts;
270
+ let out = targets;
271
+ if (platform) out = out.filter((c) => !c.platforms || c.platforms.includes(platform));
272
+ if (ids && ids.length) {
273
+ const set = new Set(ids);
274
+ out = out.filter((c) => set.has(c.id));
275
+ }
276
+ if (onlyExisting) out = out.filter((c) => fs.existsSync(c.path));
277
+ return out;
278
+ }
163
279
 
164
- try {
165
- const parsed = readJsonFileSafe(client.path);
166
- if (parsed === null) {
167
- failed.push({ client: client.name, reason: 'Invalid JSON' });
168
- continue;
169
- }
280
+ function patchClientConfig(targetConfigPath, opts = {}) {
281
+ const launcher = getLauncher();
282
+ const onlyExisting = opts.onlyExisting !== false;
283
+ const candidates = filterClientTargets(getClientConfigTargets(), {
284
+ ids: opts.ids,
285
+ platform: process.platform
286
+ });
170
287
 
171
- const cfgObj = parsed || {};
172
- cfgObj.mcpServers = cfgObj.mcpServers || {};
173
- cfgObj.mcpServers.genexus = { ...launcher, env: { GX_CONFIG_PATH: targetConfigPath } };
288
+ const patched = [];
289
+ const failed = [];
290
+ const skipped = [];
174
291
 
175
- fs.writeFileSync(client.path, JSON.stringify(cfgObj, null, 2));
292
+ for (const client of candidates) {
293
+ if (onlyExisting && !fs.existsSync(client.path)) {
294
+ skipped.push({ client: client.name, reason: 'not installed' });
295
+ continue;
296
+ }
297
+ try {
298
+ fs.mkdirSync(path.dirname(client.path), { recursive: true });
299
+ applyClientEntry(client, launcher, targetConfigPath);
176
300
  patched.push(client.name);
177
301
  } catch (err) {
178
302
  failed.push({ client: client.name, reason: err && err.message ? err.message : 'Unknown error' });
179
303
  }
180
304
  }
181
305
 
182
- return { patched, failed };
183
- }
184
-
185
- function getClientConfigTargets() {
186
- return [
187
- { path: path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json'), name: 'Claude Desktop (Windows)' },
188
- { path: path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'), name: 'Claude Desktop (macOS)' },
189
- { path: path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json'), name: 'Antigravity' },
190
- { path: path.join(os.homedir(), '.claude.json'), name: 'Claude Code' }
191
- ];
306
+ return { patched, failed, skipped };
192
307
  }
193
308
 
194
- function unpatchClientConfig() {
195
- const clients = getClientConfigTargets();
309
+ function unpatchClientConfig(opts = {}) {
310
+ const targets = filterClientTargets(getClientConfigTargets(), {
311
+ ids: opts.ids,
312
+ onlyExisting: true,
313
+ platform: process.platform
314
+ });
196
315
  const removed = [];
197
316
  const skipped = [];
198
317
  const failed = [];
199
318
 
200
- for (const client of clients) {
201
- if (!fs.existsSync(client.path)) continue;
202
-
319
+ for (const client of targets) {
203
320
  try {
204
- const parsed = readJsonFileSafe(client.path);
205
- if (parsed === null) {
206
- failed.push({ client: client.name, reason: 'Invalid JSON' });
207
- continue;
208
- }
209
-
210
- const cfgObj = parsed || {};
211
- if (!cfgObj.mcpServers || !cfgObj.mcpServers.genexus) {
212
- skipped.push({ client: client.name, reason: 'no genexus entry' });
213
- continue;
214
- }
215
-
216
- delete cfgObj.mcpServers.genexus;
217
- fs.writeFileSync(client.path, JSON.stringify(cfgObj, null, 2));
218
- removed.push(client.name);
321
+ const wasRemoved = removeClientEntry(client);
322
+ if (wasRemoved) removed.push(client.name);
323
+ else skipped.push({ client: client.name, reason: 'no genexus entry' });
219
324
  } catch (err) {
220
325
  failed.push({ client: client.name, reason: err && err.message ? err.message : 'Unknown error' });
221
326
  }
@@ -224,6 +329,133 @@ function unpatchClientConfig() {
224
329
  return { removed, skipped, failed };
225
330
  }
226
331
 
332
+ function applyClientEntry(client, launcher, targetConfigPath) {
333
+ switch (client.format) {
334
+ case 'mcpServers':
335
+ return applyMcpServersJson(client.path, launcher, targetConfigPath);
336
+ case 'opencode':
337
+ return applyOpenCodeJson(client.path, launcher, targetConfigPath);
338
+ case 'codex-toml':
339
+ return applyCodexToml(client.path, launcher, targetConfigPath);
340
+ default:
341
+ throw new Error(`Unknown client format: ${client.format}`);
342
+ }
343
+ }
344
+
345
+ function removeClientEntry(client) {
346
+ switch (client.format) {
347
+ case 'mcpServers':
348
+ return removeMcpServersJson(client.path);
349
+ case 'opencode':
350
+ return removeOpenCodeJson(client.path);
351
+ case 'codex-toml':
352
+ return removeCodexToml(client.path);
353
+ default:
354
+ throw new Error(`Unknown client format: ${client.format}`);
355
+ }
356
+ }
357
+
358
+ function applyMcpServersJson(filePath, launcher, targetConfigPath) {
359
+ const parsed = fs.existsSync(filePath) ? readJsonFileSafe(filePath) : {};
360
+ if (parsed === null) throw new Error('Invalid JSON');
361
+ const cfgObj = parsed || {};
362
+ cfgObj.mcpServers = cfgObj.mcpServers || {};
363
+ cfgObj.mcpServers.genexus = { ...launcher, env: { GX_CONFIG_PATH: targetConfigPath } };
364
+ fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
365
+ }
366
+
367
+ function removeMcpServersJson(filePath) {
368
+ const parsed = readJsonFileSafe(filePath);
369
+ if (parsed === null) throw new Error('Invalid JSON');
370
+ const cfgObj = parsed || {};
371
+ if (!cfgObj.mcpServers || !cfgObj.mcpServers.genexus) return false;
372
+ delete cfgObj.mcpServers.genexus;
373
+ fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
374
+ return true;
375
+ }
376
+
377
+ // OpenCode (sst/opencode) uses `mcp.<name>` with `type: 'local'` and `command: string[]`.
378
+ function applyOpenCodeJson(filePath, launcher, targetConfigPath) {
379
+ const parsed = fs.existsSync(filePath) ? readJsonFileSafe(filePath) : {};
380
+ if (parsed === null) throw new Error('Invalid JSON');
381
+ const cfgObj = parsed || {};
382
+ cfgObj.mcp = cfgObj.mcp || {};
383
+ cfgObj.mcp.genexus = {
384
+ type: 'local',
385
+ command: [launcher.command, ...(launcher.args || [])],
386
+ environment: { GX_CONFIG_PATH: targetConfigPath },
387
+ enabled: true
388
+ };
389
+ fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
390
+ }
391
+
392
+ function removeOpenCodeJson(filePath) {
393
+ const parsed = readJsonFileSafe(filePath);
394
+ if (parsed === null) throw new Error('Invalid JSON');
395
+ const cfgObj = parsed || {};
396
+ if (!cfgObj.mcp || !cfgObj.mcp.genexus) return false;
397
+ delete cfgObj.mcp.genexus;
398
+ fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
399
+ return true;
400
+ }
401
+
402
+ // Codex CLI uses TOML. We do a minimal text-merge: strip any existing
403
+ // [mcp_servers.genexus*] blocks and append fresh ones. Brittle on hand-edited
404
+ // files that put other keys after our blocks without a blank line, but
405
+ // adequate for the typical machine-managed config.
406
+ function applyCodexToml(filePath, launcher, targetConfigPath) {
407
+ let existing = '';
408
+ if (fs.existsSync(filePath)) existing = fs.readFileSync(filePath, 'utf8');
409
+ const stripped = stripCodexGenexusBlocks(existing);
410
+ const args = launcher.args || [];
411
+ const lines = [];
412
+ if (stripped.length && !stripped.endsWith('\n')) lines.push('');
413
+ if (stripped.length) lines.push('');
414
+ lines.push('[mcp_servers.genexus]');
415
+ lines.push(`command = ${tomlString(launcher.command)}`);
416
+ lines.push(`args = [${args.map(tomlString).join(', ')}]`);
417
+ lines.push('');
418
+ lines.push('[mcp_servers.genexus.env]');
419
+ lines.push(`GX_CONFIG_PATH = ${tomlString(targetConfigPath)}`);
420
+ lines.push('');
421
+ fs.writeFileSync(filePath, stripped + lines.join('\n'));
422
+ }
423
+
424
+ function removeCodexToml(filePath) {
425
+ if (!fs.existsSync(filePath)) return false;
426
+ const existing = fs.readFileSync(filePath, 'utf8');
427
+ const stripped = stripCodexGenexusBlocks(existing);
428
+ if (stripped === existing) return false;
429
+ fs.writeFileSync(filePath, stripped);
430
+ return true;
431
+ }
432
+
433
+ function stripCodexGenexusBlocks(content) {
434
+ // Walk line-by-line so values like `args = []` don't confuse the parser.
435
+ // A section ends at the next line that starts a [section] header (top-level
436
+ // tables and arrays of tables only — must begin at column 0).
437
+ const lines = content.split('\n');
438
+ const out = [];
439
+ const headerRe = /^\[\[?[A-Za-z0-9_."'-]+/;
440
+ const ourRe = /^\[\[?mcp_servers\.genexus(\.[A-Za-z0-9_."'-]+)?\]\]?\s*$/;
441
+ let inOurs = false;
442
+ for (const line of lines) {
443
+ if (headerRe.test(line)) {
444
+ inOurs = ourRe.test(line);
445
+ if (inOurs) continue;
446
+ }
447
+ if (inOurs) continue;
448
+ out.push(line);
449
+ }
450
+ // Collapse 3+ trailing blank lines we may have left behind.
451
+ return out.join('\n').replace(/\n{3,}/g, '\n\n');
452
+ }
453
+
454
+ function tomlString(value) {
455
+ const s = String(value);
456
+ return `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
457
+ }
458
+
227
459
  function getLocalAppDataCacheDir() {
228
460
  if (process.platform !== 'win32') return null;
229
461
  const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
@@ -388,6 +620,8 @@ module.exports = {
388
620
  patchClientConfig,
389
621
  unpatchClientConfig,
390
622
  getClientConfigTargets,
623
+ listSupportedClientIds,
624
+ filterClientTargets,
391
625
  getLocalAppDataCacheDir,
392
626
  readGeneXusVersionFromInstall,
393
627
  readKbCatalog,
package/cli/run.test.js CHANGED
@@ -400,7 +400,7 @@ test('tool_definitions.json is valid and disambiguation tools have use-when guid
400
400
  assert.ok(Array.isArray(defs) && defs.length > 0, 'tool defs should be a non-empty array');
401
401
 
402
402
  const byName = Object.fromEntries(defs.map((t) => [t.name, t]));
403
- const disambiguationTools = ['genexus_inspect', 'genexus_analyze', 'genexus_summarize', 'genexus_doc'];
403
+ const disambiguationTools = ['genexus_inspect', 'genexus_analyze', 'genexus_doc'];
404
404
  for (const name of disambiguationTools) {
405
405
  assert.ok(byName[name], `${name} should exist`);
406
406
  const desc = byName[name].description || '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genexus-mcp",
3
- "version": "2.3.3",
3
+ "version": "2.3.5",
4
4
  "mcpName": "io.github.lennix1337/genexus",
5
5
  "description": "GeneXus 18 MCP server — read, edit, and analyze GeneXus knowledge base objects (transactions, web panels, procedures, SDTs) directly from Claude, Cursor, and other AI agents over the Model Context Protocol.",
6
6
  "keywords": [
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "runtimeTarget": {
3
- "name": ".NETCoreApp,Version=v8.0",
3
+ "name": ".NETCoreApp,Version=v8.0/win-x64",
4
4
  "signature": ""
5
5
  },
6
6
  "compilationOptions": {},
7
7
  "targets": {
8
- ".NETCoreApp,Version=v8.0": {
9
- "GxMcp.Gateway/2.3.3": {
8
+ ".NETCoreApp,Version=v8.0": {},
9
+ ".NETCoreApp,Version=v8.0/win-x64": {
10
+ "GxMcp.Gateway/2.3.5": {
10
11
  "dependencies": {
11
12
  "Newtonsoft.Json": "13.0.3",
12
13
  "System.Management": "10.0.5",
@@ -37,15 +38,7 @@
37
38
  "System.CodeDom": "10.0.5"
38
39
  },
39
40
  "runtime": {
40
- "lib/net8.0/System.Management.dll": {
41
- "assemblyVersion": "10.0.0.5",
42
- "fileVersion": "10.0.526.15411"
43
- }
44
- },
45
- "runtimeTargets": {
46
41
  "runtimes/win/lib/net8.0/System.Management.dll": {
47
- "rid": "win",
48
- "assetType": "runtime",
49
42
  "assemblyVersion": "10.0.0.5",
50
43
  "fileVersion": "10.0.526.15411"
51
44
  }
@@ -64,15 +57,7 @@
64
57
  },
65
58
  "System.Windows.Extensions/10.0.3": {
66
59
  "runtime": {
67
- "lib/net8.0/System.Windows.Extensions.dll": {
68
- "assemblyVersion": "10.0.0.0",
69
- "fileVersion": "10.0.326.7603"
70
- }
71
- },
72
- "runtimeTargets": {
73
60
  "runtimes/win/lib/net8.0/System.Windows.Extensions.dll": {
74
- "rid": "win",
75
- "assetType": "runtime",
76
61
  "assemblyVersion": "10.0.0.0",
77
62
  "fileVersion": "10.0.326.7603"
78
63
  }
@@ -81,7 +66,7 @@
81
66
  }
82
67
  },
83
68
  "libraries": {
84
- "GxMcp.Gateway/2.3.3": {
69
+ "GxMcp.Gateway/2.3.5": {
85
70
  "type": "project",
86
71
  "serviceable": false,
87
72
  "sha512": ""
Binary file
Binary file
Binary file
@@ -12,9 +12,12 @@
12
12
  }
13
13
  ],
14
14
  "configProperties": {
15
+ "System.GC.Concurrent": true,
15
16
  "System.GC.Server": true,
16
17
  "System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
17
- "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
18
+ "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false,
19
+ "System.Runtime.TieredCompilation": true,
20
+ "System.Runtime.TieredPGO": true
18
21
  }
19
22
  }
20
23
  }
Binary file
Binary file
Binary file
@@ -1,31 +1,31 @@
1
- [
2
- {"name":"genexus_whoami","description":"Return current KB context, GeneXus install, and MCP version.","inputSchema":{"type":"object","properties":{}}},
3
- {"name":"genexus_query","description":"Search objects. Prefixes: usedby:, type:, description:, parent:, parentPath:. Triggers KB index build on first call against a fresh install — _meta.partial=true means more results will appear as indexing continues (watch notifications/progress). Literal-name queries skip the index via direct SDK lookup. genexus_read / genexus_edit / genexus_list_objects / genexus_lifecycle are index-independent and always return immediately.","inputSchema":{"type":"object","properties":{"query":{"type":"string"},"typeFilter":{"type":"string"},"domainFilter":{"type":"string"},"limit":{"type":"integer"},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["query"]}},
4
- {"name":"genexus_list_objects","description":"List objects with pagination. Feed nextOffset until hasMore=false. Returns minimal shape by default (name, type, path, parent); verbose=true for full shape.","inputSchema":{"type":"object","properties":{"filter":{"type":"string"},"limit":{"type":"integer"},"offset":{"type":"integer"},"parent":{"type":"string"},"parentPath":{"type":"string"},"typeFilter":{"type":"string"},"verbose":{"type":"boolean","description":"When true, returns full item shape (default false)."},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
5
- {"name":"genexus_read","description":"Read source/metadata parts. name or targets (mutually exclusive). Paginate with offset/limit. Use parts=[\"Source\",\"Variables\",...] to select specific parts and skip the rest.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"}},"part":{"type":"string"},"parts":{"type":"array","items":{"type":"string"},"description":"When set, only the listed parts are returned in a combined response. Mutually exclusive with part/offset/limit."},"offset":{"type":"integer"},"limit":{"type":"integer"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
6
- {"name":"genexus_edit","description":"Edit source/metadata. name or targets (mutually exclusive). Use dryRun=true with mode=patch first. Returns post_state.diff (unified diff) by default; verbose=true adds slices (±15 lines); return_post_state=false opts out.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"part":{"type":"string"},"mode":{"type":"string","enum":["full","patch","ops"]},"content":{"type":"string"},"ops":{"type":"array","items":{"type":"object","properties":{"op":{"type":"string"}},"required":["op"]}},"patch":{},"context":{"type":"string"},"operation":{"type":"string","enum":["Replace","Insert_After","Append"]},"expectedCount":{"type":"integer"},"dryRun":{"type":"boolean"},"verifyRollback":{"type":"boolean"},"targets":{"type":"array","items":{"type":"object","required":["name","content"]}},"type":{"type":"string"},"return_post_state":{"type":"boolean","description":"Set false to omit post_state.diff from the response (default true)."},"verbose":{"type":"boolean","description":"When true, adds slices (±15 lines around each hunk) to post_state (default false)."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
7
- {"name":"genexus_inspect","description":"Snapshot of an object: metadata, variables, structure, signature.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"include":{"type":"array","items":{"type":"string","enum":["metadata","variables","signature","structure","parts","controls","events_repertoire"]}},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
8
- {"name":"genexus_analyze","description":"Semantic analysis: impact, dependencies, complexity, naming, summary, explain.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"mode":{"type":"string","enum":["linter","navigation","hierarchy","impact","data_context","ui_context","pattern_metadata","summary","explain"]},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."},"code":{"type":"string","description":"Required when mode=explain."}},"required":["name","mode"]}},
9
- {"name":"genexus_inject_context","description":"Inject SDT structures and Procedure signatures of called objects.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"recursive":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
10
- {"name":"genexus_lifecycle","description":"Build/validate/index/poll. action=build is non-blocking when estimated_seconds>=20 — returns {job_id, status:running} and surfaces _meta.background_jobs on the next call. action=status accepts taskId OR job_id via target; wait_seconds>0 long-polls up to 25s.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["build","cancel","rebuild","reorg","validate","validate-kb","sync","index","status","result","snapshots-list","snapshots-restore"]},"target":{"type":"string","description":"Object name(s), taskId, job_id, or op:<operationId>."},"code":{"type":"string"},"limit":{"type":"integer"},"snapshotPath":{"type":"string"},"estimated_seconds":{"type":"integer","description":"Build: <20 forces sync fast-path (one turn); >=20 (default 60) goes async."},"wait_seconds":{"type":"integer","description":"Status: 0-25. >0 long-polls server-side until terminal or timeout."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
11
- {"name":"genexus_forge","description":"Generate new code or structures from templates or translations.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["scaffold","translate","sample"]},"type":{"type":"string"},"name":{"type":"string"},"content":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
12
- {"name":"genexus_test","description":"Execute native GeneXus tests (GXtest).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
13
- {"name":"genexus_create_object","description":"Create a new GeneXus object in the active KB.","inputSchema":{"type":"object","properties":{"type":{"type":"string","description":"e.g. Procedure, Transaction, WebPanel, SDT."},"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["type","name"]}},
14
- {"name":"genexus_logs","description":"Read worker_debug.log tail for error diagnosis or correlation.","inputSchema":{"type":"object","properties":{"lines":{"type":"integer"},"filterCorrelation":{"type":"string"},"grep":{"type":"string"}}}},
15
- {"name":"genexus_worker_reload","description":"Hot-reload worker: copy new binaries and respawn.","inputSchema":{"type":"object","properties":{"sourceDir":{"type":"string","description":"Absolute path to freshly built bin/Release."}},"required":["sourceDir"]}},
16
- {"name":"genexus_delete_object","description":"Delete an object from the KB. Irreversible — confirm=true required.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"},"confirm":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","confirm"]}},
17
- {"name":"genexus_export_object","description":"Export a GeneXus object part to a text file on disk.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"outputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"overwrite":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","outputPath"]}},
18
- {"name":"genexus_import_object","description":"Import a text file from disk into a GeneXus object part.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"inputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","inputPath"]}},
19
- {"name":"genexus_refactor","description":"Run GeneXus refactor: rename, extract procedure, or WWP condition set.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["RenameAttribute","RenameVariable","RenameObject","ExtractProcedure","WWPSetCondition"]},"target":{"type":"string","description":"Primary object or symbol to refactor."},"newName":{"type":"string"},"objectName":{"type":"string"},"code":{"type":"string"},"procedureName":{"type":"string"},"controlAttribute":{"type":"string"},"value":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
20
- {"name":"genexus_add_variable","description":"Add a variable to the Variables part of a GeneXus object.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"varName":{"type":"string"},"typeName":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","varName"]}},
21
- {"name":"genexus_format","description":"Format a GeneXus code snippet using worker rules.","inputSchema":{"type":"object","properties":{"code":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["code"]}},
22
- {"name":"genexus_properties","description":"Read or update GeneXus object properties.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set"]},"name":{"type":"string"},"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
23
- {"name":"genexus_asset","description":"Find, read, or write binary assets inside the active KB.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["find","read","write"]},"path":{"type":"string"},"includeContent":{"type":"boolean"},"maxBytes":{"type":"integer"},"pattern":{"type":"string"},"relativeRoot":{"type":"string"},"limit":{"type":"integer"},"contentBase64":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
24
- {"name":"genexus_history","description":"List, read, save, or restore GeneXus object history snapshots.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get_source","save","restore"]},"name":{"type":"string"},"versionId":{"type":"integer"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
25
- {"name":"genexus_structure","description":"Read or update visual and logical structure of GeneXus objects.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_visual","update_visual","get_indexes","get_logic"]},"name":{"type":"string"},"payload":{"type":"object","description":"Update payload for update_visual."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
26
- {"name":"genexus_layout","description":"Native SDK layout/WebForm operations: get_tree, set_property, find_controls, inspect_surface, scan_mutators.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_tree","set_property","find_controls","set_properties","inspect_surface","get_preview","scan_mutators","rename_printblock","add_printblock"]},"name":{"type":"string"},"target":{"type":"string","description":"Object name alias for backward compatibility."},"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"},"query":{"type":"string"},"changes":{"type":"array","items":{"type":"object","required":["control","propertyName","value"]}},"limit":{"type":"integer"},"currentName":{"type":"string"},"newName":{"type":"string"},"printBlockName":{"type":"string"},"height":{"type":"integer"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
27
- {"name":"genexus_doc","description":"Generate structured docs (markdown, sequence diagrams, API contracts).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["wiki","visualize","health"]},"target":{"type":"string","description":"Object or domain name."}},"required":["action"]}},
28
- {"name":"genexus_search_source","description":"Regex/semantic search across Procedure/DataProvider/WebPanel/Transaction source.","inputSchema":{"type":"object","properties":{"callee":{"type":"string","description":"Method/function name (qualified or unqualified)."},"argMatches":{"type":"object","description":"Positional arg index to expected literal text."},"pattern":{"type":"string"},"typeFilter":{"type":"string"},"scope":{"type":"array","items":{"type":"string"}},"maxResults":{"type":"integer"},"caseSensitive":{"type":"boolean"},"includeComments":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
29
- {"name":"genexus_kb","description":"Manage open KBs: list (with PID/RSS/idle), open (acquire Worker), close (release), set_default (persist alias to config.json).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","open","close","set_default"]},"alias":{"type":"string","description":"KB alias (for open/close). For open, auto-generated from path basename if omitted."},"path":{"type":"string","description":"Absolute path to the KB (required for action=open if alias is not declared in config)."}},"required":["action"]}},
30
- {"name":"genexus_sql","description":"SQL for a Transaction/Table (DDL) or a procedure For Each navigation.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["ddl","navigation"]},"name":{"type":"string"},"includeSubordinated":{"type":"boolean","description":"action=ddl only."},"levelNumber":{"type":"integer","description":"action=navigation only."},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}}
31
- ]
1
+ [
2
+ {"name":"genexus_whoami","description":"Return current KB context, GeneXus install, and MCP version.","inputSchema":{"type":"object","properties":{}}},
3
+ {"name":"genexus_query","description":"Search objects. Prefixes: usedby:, type:, description:, parent:, parentPath:. Triggers KB index build on first call against a fresh install — _meta.partial=true means more results will appear as indexing continues (watch notifications/progress). Literal-name queries skip the index via direct SDK lookup. genexus_read / genexus_edit / genexus_list_objects / genexus_lifecycle are index-independent and always return immediately.","inputSchema":{"type":"object","properties":{"query":{"type":"string"},"typeFilter":{"type":"string"},"domainFilter":{"type":"string"},"limit":{"type":"integer"},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["query"]}},
4
+ {"name":"genexus_list_objects","description":"List objects with pagination. Feed nextOffset until hasMore=false. Returns minimal shape by default (name, type, path, parent); verbose=true for full shape.","inputSchema":{"type":"object","properties":{"filter":{"type":"string"},"limit":{"type":"integer"},"offset":{"type":"integer"},"parent":{"type":"string"},"parentPath":{"type":"string"},"typeFilter":{"type":"string"},"verbose":{"type":"boolean","description":"When true, returns full item shape (default false)."},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
5
+ {"name":"genexus_read","description":"Read source/metadata parts. name or targets (mutually exclusive). Paginate with offset/limit. Use parts=[\"Source\",\"Variables\",...] to select specific parts and skip the rest.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"}},"part":{"type":"string"},"parts":{"type":"array","items":{"type":"string"},"description":"When set, only the listed parts are returned in a combined response. Mutually exclusive with part/offset/limit."},"offset":{"type":"integer"},"limit":{"type":"integer"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
6
+ {"name":"genexus_edit","description":"Edit source/metadata. name or targets (mutually exclusive). Use dryRun=true with mode=patch first. Returns post_state.diff (unified diff) by default; verbose=true adds slices (±15 lines); return_post_state=false opts out.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"part":{"type":"string"},"mode":{"type":"string","enum":["full","patch","ops"]},"content":{"type":"string"},"ops":{"type":"array","description":"RFC 6902 JSON-Patch ops. Supported: add, remove, replace, test.","items":{"type":"object","properties":{"op":{"type":"string","enum":["add","remove","replace","test"]},"path":{"type":"string"},"value":{}},"required":["op","path"]}},"patch":{"description":"Legacy string replacement OR {find,replace} JSON object OR RFC 6902 array.","anyOf":[{"type":"string"},{"type":"object","properties":{"find":{"type":"string"},"replace":{"type":"string"}},"required":["find","replace"]},{"type":"array","items":{"type":"object"}}]},"context":{"type":"string"},"operation":{"type":"string","enum":["Replace","Insert_After","Append"]},"expectedCount":{"type":"integer"},"dryRun":{"type":"boolean"},"verifyRollback":{"type":"boolean"},"targets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"content":{"type":"string"}},"required":["name","content"]}},"type":{"type":"string"},"return_post_state":{"type":"boolean","description":"Set false to omit post_state.diff from the response (default true)."},"verbose":{"type":"boolean","description":"When true, adds slices (±15 lines around each hunk) to post_state (default false)."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
7
+ {"name":"genexus_inspect","description":"Snapshot of an object: metadata, variables, structure, signature. Use when you need raw object shape (parts, controls, events repertoire) without source text — for source/code use genexus_read instead.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"include":{"type":"array","items":{"type":"string","enum":["metadata","variables","signature","structure","parts","controls","events_repertoire"]}},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
8
+ {"name":"genexus_analyze","description":"Semantic analysis: impact, dependencies, complexity, naming, summary, explain. Use when you need cross-object reasoning (callers/callees, impact of a change, navigation paths) — don't use for raw source or single-object metadata (see genexus_read / genexus_inspect).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"mode":{"type":"string","enum":["linter","navigation","hierarchy","impact","data_context","ui_context","pattern_metadata","summary","explain"]},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."},"code":{"type":"string","description":"Required when mode=explain."}},"required":["name","mode"]}},
9
+ {"name":"genexus_inject_context","description":"Inject SDT structures and Procedure signatures of called objects.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"recursive":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
10
+ {"name":"genexus_lifecycle","description":"Build/validate/index/poll. action=build is non-blocking when estimated_seconds>=20 — returns {job_id, status:running} and surfaces _meta.background_jobs on the next call. action=status accepts taskId OR job_id via target; wait_seconds>0 long-polls up to 25s.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["build","cancel","rebuild","reorg","validate","validate-kb","sync","index","status","result","snapshots-list","snapshots-restore"]},"target":{"type":"string","description":"Object name(s), taskId, job_id, or op:<operationId>."},"code":{"type":"string"},"limit":{"type":"integer"},"snapshotPath":{"type":"string"},"estimated_seconds":{"type":"integer","description":"Build: <20 forces sync fast-path (one turn); >=20 (default 60) goes async."},"wait_seconds":{"type":"integer","description":"Status: 0-25. >0 long-polls server-side until terminal or timeout."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
11
+ {"name":"genexus_forge","description":"Generate new code or structures from templates or translations.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["scaffold","translate","sample"]},"type":{"type":"string"},"name":{"type":"string"},"content":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
12
+ {"name":"genexus_test","description":"Execute native GeneXus tests (GXtest).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
13
+ {"name":"genexus_create_object","description":"Create a new GeneXus object in the active KB.","inputSchema":{"type":"object","properties":{"type":{"type":"string","description":"e.g. Procedure, Transaction, WebPanel, SDT."},"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["type","name"]}},
14
+ {"name":"genexus_logs","description":"Read worker_debug.log tail for error diagnosis or correlation.","inputSchema":{"type":"object","properties":{"lines":{"type":"integer"},"filterCorrelation":{"type":"string"},"grep":{"type":"string"}}}},
15
+ {"name":"genexus_worker_reload","description":"Hot-reload worker: copy new binaries and respawn.","inputSchema":{"type":"object","properties":{"sourceDir":{"type":"string","description":"Absolute path to freshly built bin/Release."}},"required":["sourceDir"]}},
16
+ {"name":"genexus_delete_object","description":"Delete an object from the KB. Irreversible — confirm=true required.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"},"confirm":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","confirm"]}},
17
+ {"name":"genexus_export_object","description":"Export a GeneXus object part to a text file on disk.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"outputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"overwrite":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","outputPath"]}},
18
+ {"name":"genexus_import_object","description":"Import a text file from disk into a GeneXus object part.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"inputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","inputPath"]}},
19
+ {"name":"genexus_refactor","description":"Run GeneXus refactor: rename, extract procedure, or WWP condition set.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["RenameAttribute","RenameVariable","RenameObject","ExtractProcedure","WWPSetCondition"]},"target":{"type":"string","description":"Primary object or symbol to refactor."},"newName":{"type":"string"},"objectName":{"type":"string"},"code":{"type":"string"},"procedureName":{"type":"string"},"controlAttribute":{"type":"string"},"value":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
20
+ {"name":"genexus_add_variable","description":"Add a variable to the Variables part of a GeneXus object.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"varName":{"type":"string"},"typeName":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","varName"]}},
21
+ {"name":"genexus_format","description":"Format a GeneXus code snippet using worker rules.","inputSchema":{"type":"object","properties":{"code":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["code"]}},
22
+ {"name":"genexus_properties","description":"Read or update GeneXus object properties. Note: Description is the title-bar text shown when a WebPanel/Popup is opened via .Popup().","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set"]},"name":{"type":"string"},"control":{"type":"string","description":"Optional. Layout control name (e.g. BtnConfirmar), variable name with & prefix (e.g. &Alu2RegProf), or attribute name."},"propertyName":{"type":"string"},"value":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
23
+ {"name":"genexus_asset","description":"Find, read, or write binary assets inside the active KB.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["find","read","write"]},"path":{"type":"string"},"includeContent":{"type":"boolean"},"maxBytes":{"type":"integer"},"pattern":{"type":"string"},"relativeRoot":{"type":"string"},"limit":{"type":"integer"},"contentBase64":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
24
+ {"name":"genexus_history","description":"List, read, save, or restore GeneXus object history snapshots.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get_source","save","restore"]},"name":{"type":"string"},"versionId":{"type":"integer"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
25
+ {"name":"genexus_structure","description":"Read or update visual and logical structure of GeneXus objects.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_visual","update_visual","get_indexes","get_logic"]},"name":{"type":"string"},"payload":{"type":"object","description":"Update payload for update_visual."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
26
+ {"name":"genexus_layout","description":"Native SDK layout/WebForm operations: get_tree, set_property, find_controls, inspect_surface, scan_mutators.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_tree","set_property","find_controls","set_properties","inspect_surface","get_preview","scan_mutators","rename_printblock","add_printblock"]},"name":{"type":"string"},"target":{"type":"string","description":"Object name alias for backward compatibility."},"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"},"query":{"type":"string"},"changes":{"type":"array","items":{"type":"object","properties":{"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"}},"required":["control","propertyName","value"]}},"limit":{"type":"integer"},"currentName":{"type":"string"},"newName":{"type":"string"},"printBlockName":{"type":"string"},"height":{"type":"integer"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
27
+ {"name":"genexus_doc","description":"Generate structured docs (markdown wiki, sequence diagrams, health reports). Use when producing human-readable artifacts about an object or domain — don't use for programmatic analysis (see genexus_analyze) or source reading (see genexus_read).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["wiki","visualize","health"]},"target":{"type":"string","description":"Object or domain name."}},"required":["action"]}},
28
+ {"name":"genexus_search_source","description":"Regex/semantic search across Procedure/DataProvider/WebPanel/Transaction source.","inputSchema":{"type":"object","properties":{"callee":{"type":"string","description":"Method/function name (qualified or unqualified)."},"argMatches":{"type":"object","description":"Positional arg index to expected literal text."},"pattern":{"type":"string"},"typeFilter":{"type":"string"},"scope":{"type":"array","items":{"type":"string"}},"maxResults":{"type":"integer"},"caseSensitive":{"type":"boolean"},"includeComments":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
29
+ {"name":"genexus_kb","description":"Manage open KBs: list (with PID/RSS/idle), open (acquire Worker), close (release), set_default (persist alias to config.json).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","open","close","set_default"]},"alias":{"type":"string","description":"KB alias (for open/close). For open, auto-generated from path basename if omitted."},"path":{"type":"string","description":"Absolute path to the KB (required for action=open if alias is not declared in config)."}},"required":["action"]}},
30
+ {"name":"genexus_sql","description":"SQL for a Transaction/Table (DDL) or a procedure For Each navigation.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["ddl","navigation"]},"name":{"type":"string"},"includeSubordinated":{"type":"boolean","description":"action=ddl only."},"levelNumber":{"type":"integer","description":"action=navigation only."},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}}
31
+ ]
@@ -5,7 +5,7 @@
5
5
  <handlers>
6
6
  <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
7
7
  </handlers>
8
- <aspNetCore processPath="dotnet" arguments=".\GxMcp.Gateway.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
8
+ <aspNetCore processPath=".\GxMcp.Gateway.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
9
9
  </system.webServer>
10
10
  </location>
11
11
  </configuration>
Binary file
Binary file