genexus-mcp 2.6.2 → 2.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/axi.js +158 -23
- package/cli/index.js +18 -1
- package/cli/lib/config.js +75 -1
- package/cli/lib/update-check.js +42 -5
- package/package.json +1 -1
- package/publish/GxMcp.Gateway.deps.json +2 -2
- package/publish/GxMcp.Gateway.dll +0 -0
- package/publish/GxMcp.Gateway.exe +0 -0
- package/publish/GxMcp.Gateway.pdb +0 -0
- package/publish/tool_definitions.json +6 -5
- package/publish/worker/GxMcp.Worker.exe +0 -0
- package/publish/worker/GxMcp.Worker.pdb +0 -0
package/cli/commands/axi.js
CHANGED
|
@@ -21,7 +21,10 @@ const {
|
|
|
21
21
|
readKbCatalog,
|
|
22
22
|
addKbToConfig,
|
|
23
23
|
removeKbFromConfig,
|
|
24
|
-
switchActiveKb
|
|
24
|
+
switchActiveKb,
|
|
25
|
+
isPathLikelyAppLockerBlocked,
|
|
26
|
+
normalizeExePath,
|
|
27
|
+
readClientCommandEntry
|
|
25
28
|
} = require('../lib/config');
|
|
26
29
|
|
|
27
30
|
function resolveClientIds(options) {
|
|
@@ -176,7 +179,17 @@ async function spawnGatewayProbe({ env = process.env, spawnHoldMs, timeoutMs, la
|
|
|
176
179
|
});
|
|
177
180
|
|
|
178
181
|
child.once('error', (err) => {
|
|
179
|
-
|
|
182
|
+
const code = err && (err.code || err.errno);
|
|
183
|
+
const accessDenied = code === 'EACCES' || code === 'EPERM' || /access is denied|access denied|acesso negado/i.test(err.message || '');
|
|
184
|
+
if (accessDenied) {
|
|
185
|
+
const zone = isPathLikelyAppLockerBlocked(gatewayExePath);
|
|
186
|
+
const hint = zone
|
|
187
|
+
? ` Gateway exe is under %${zone}% — likely blocked by AppLocker/SRP. Reinstall via scripts/install.ps1 to a whitelisted path.`
|
|
188
|
+
: ' Access denied. AppLocker/SRP or AV may be blocking execution. Move the exe to a whitelisted path (see scripts/install.ps1).';
|
|
189
|
+
finish({ status: 'fail', detail: `${label} failed: ${err.message}.${hint}` });
|
|
190
|
+
} else {
|
|
191
|
+
finish({ status: 'fail', detail: `${label} failed: ${err.message}` });
|
|
192
|
+
}
|
|
180
193
|
});
|
|
181
194
|
|
|
182
195
|
child.once('spawn', () => {
|
|
@@ -304,6 +317,7 @@ async function runMcpSmokeProbe(cwd) {
|
|
|
304
317
|
|
|
305
318
|
async function handleStatus(options, ctx) {
|
|
306
319
|
const data = buildStatusData(ctx.cwd);
|
|
320
|
+
const riskyZone = isPathLikelyAppLockerBlocked(data.gatewayExePath);
|
|
307
321
|
|
|
308
322
|
const ok = options.full
|
|
309
323
|
? {
|
|
@@ -311,6 +325,7 @@ async function handleStatus(options, ctx) {
|
|
|
311
325
|
configFound: data.configFound,
|
|
312
326
|
gatewayExeFound: data.gatewayExeFound,
|
|
313
327
|
kbLooksValid: data.kbLooksValid,
|
|
328
|
+
pathSafetyWarn: !!riskyZone,
|
|
314
329
|
configSource: data.configSource,
|
|
315
330
|
configPath: data.configPath,
|
|
316
331
|
gatewayExePath: data.gatewayExePath,
|
|
@@ -321,20 +336,86 @@ async function handleStatus(options, ctx) {
|
|
|
321
336
|
ready: data.ready,
|
|
322
337
|
configFound: data.configFound,
|
|
323
338
|
gatewayExeFound: data.gatewayExeFound,
|
|
324
|
-
kbLooksValid: data.kbLooksValid
|
|
339
|
+
kbLooksValid: data.kbLooksValid,
|
|
340
|
+
pathSafetyWarn: !!riskyZone
|
|
325
341
|
};
|
|
326
342
|
|
|
343
|
+
const help = data.ready
|
|
344
|
+
? ['Run `genexus-mcp tools list --limit 10` to inspect available MCP tools.']
|
|
345
|
+
: [
|
|
346
|
+
'Run `genexus-mcp doctor` for expanded checks.',
|
|
347
|
+
'Run `genexus-mcp init --kb "<kbPath>" --gx "<geneXusPath>"` to create config.'
|
|
348
|
+
];
|
|
349
|
+
if (riskyZone) {
|
|
350
|
+
help.unshift(`Gateway exe is under %${riskyZone}% — likely AppLocker-blocked on corporate Windows. Reinstall to a whitelisted path via scripts/install.ps1.`);
|
|
351
|
+
}
|
|
352
|
+
|
|
327
353
|
return {
|
|
328
354
|
exitCode: ctx.EXIT_CODES.OK,
|
|
329
|
-
envelope: {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
355
|
+
envelope: { ok, help }
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function buildClientExeCrossCheck(packageExePath) {
|
|
360
|
+
const packageNorm = normalizeExePath(packageExePath);
|
|
361
|
+
const targets = filterClientTargets(getClientConfigTargets(), { platform: process.platform });
|
|
362
|
+
|
|
363
|
+
const mismatches = [];
|
|
364
|
+
const matches = [];
|
|
365
|
+
const errors = [];
|
|
366
|
+
let inspected = 0;
|
|
367
|
+
|
|
368
|
+
for (const client of targets) {
|
|
369
|
+
if (!fs.existsSync(client.path)) continue;
|
|
370
|
+
let entry;
|
|
371
|
+
try {
|
|
372
|
+
entry = readClientCommandEntry(client);
|
|
373
|
+
} catch (err) {
|
|
374
|
+
errors.push(`${client.name}: ${err.message || 'read failed'}`);
|
|
375
|
+
continue;
|
|
337
376
|
}
|
|
377
|
+
if (!entry || !entry.command) continue;
|
|
378
|
+
inspected += 1;
|
|
379
|
+
|
|
380
|
+
const cmd = entry.command;
|
|
381
|
+
const lowerCmd = cmd.toLowerCase();
|
|
382
|
+
|
|
383
|
+
// Skip launchers that always resolve via npm at runtime (npx, node CLI shim).
|
|
384
|
+
if (/(^|[\\/])(npx|npx\.cmd|node|node\.exe)$/i.test(lowerCmd) || /[\\/]genexus-mcp(\.cmd)?$/i.test(lowerCmd)) {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Only compare when the configured command points to an .exe.
|
|
389
|
+
if (!/\.exe$/i.test(lowerCmd)) continue;
|
|
390
|
+
|
|
391
|
+
const configuredNorm = normalizeExePath(cmd);
|
|
392
|
+
if (configuredNorm === packageNorm) {
|
|
393
|
+
matches.push(client.name);
|
|
394
|
+
} else {
|
|
395
|
+
const configuredExists = fs.existsSync(cmd);
|
|
396
|
+
mismatches.push({
|
|
397
|
+
client: client.name,
|
|
398
|
+
configured: cmd,
|
|
399
|
+
exists: configuredExists
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (inspected === 0) {
|
|
405
|
+
return { status: 'warn', detail: 'No AI client config with a direct gateway exe found (or all clients use npx). Run `genexus-mcp init --write-clients` if you want to register one.' };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (mismatches.length === 0) {
|
|
409
|
+
return {
|
|
410
|
+
status: 'pass',
|
|
411
|
+
detail: `All inspected client configs (${matches.join(', ')}) point at the npm-package gateway exe.`
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const detailParts = mismatches.map((m) => `${m.client} -> ${m.configured}${m.exists ? '' : ' (missing)'}`);
|
|
416
|
+
return {
|
|
417
|
+
status: 'warn',
|
|
418
|
+
detail: `Client(s) reference a gateway exe that is NOT this npm package's bundled exe. \`npm install -g genexus-mcp@latest\` will NOT update those instances. Bundled: ${packageExePath}. Mismatches: ${detailParts.join('; ')}. Re-run scripts/install.ps1 (or genexus-mcp init --write-clients) to resync.`
|
|
338
419
|
};
|
|
339
420
|
}
|
|
340
421
|
|
|
@@ -359,21 +440,32 @@ async function handleDoctor(options, ctx) {
|
|
|
359
440
|
const kbExists = !!(kbPath && fs.existsSync(kbPath));
|
|
360
441
|
const gxExeExists = !!(gxPath && fs.existsSync(path.join(gxPath, 'genexus.exe')));
|
|
361
442
|
|
|
443
|
+
const riskyZone = isPathLikelyAppLockerBlocked(gatewayExePath);
|
|
444
|
+
const clientCrossCheck = buildClientExeCrossCheck(gatewayExePath);
|
|
445
|
+
|
|
362
446
|
const checks = [
|
|
363
447
|
{ id: 'config_file', status: data.configFound ? 'pass' : 'fail', detail: data.configFound ? 'GX config file was found.' : 'GX config file is missing.' },
|
|
364
448
|
{ id: 'gateway_exe', status: data.gatewayExeFound ? 'pass' : 'fail', detail: data.gatewayExeFound ? 'Gateway executable is available.' : 'Gateway executable is missing.' },
|
|
449
|
+
{
|
|
450
|
+
id: 'gateway_exe_path_safety',
|
|
451
|
+
status: riskyZone ? 'warn' : 'pass',
|
|
452
|
+
detail: riskyZone
|
|
453
|
+
? `Gateway exe is under %${riskyZone}%, which is commonly blocked by AppLocker/SRP in Windows domains. If clients show "Failed to connect" / "Access denied", reinstall to a whitelisted path via scripts/install.ps1 (defaults to C:\\Tools\\GenexusMCP or %LOCALAPPDATA%\\Programs\\GenexusMCP).`
|
|
454
|
+
: 'Gateway exe is in a path unlikely to be blocked by AppLocker/SRP.'
|
|
455
|
+
},
|
|
365
456
|
{ id: 'kb_path_exists', status: kbExists ? 'pass' : 'warn', detail: kbExists ? 'Configured KB path exists.' : 'Configured KB path does not exist.' },
|
|
366
457
|
{ id: 'kb_shape', status: data.kbLooksValid ? 'pass' : 'warn', detail: data.kbLooksValid ? 'KB folder shape looks valid.' : 'KB markers were not found in configured KB path.' },
|
|
367
458
|
{ id: 'gx_installation', status: gxExeExists ? 'pass' : 'warn', detail: gxExeExists ? 'GeneXus installation has genexus.exe.' : 'Configured GeneXus installation is missing genexus.exe.' },
|
|
368
459
|
{ id: 'tool_definitions', status: toolDefsExists ? 'pass' : 'warn', detail: toolDefsExists ? `Tool definition file found (${toolCount} tools).` : 'tool_definitions.json is missing.' },
|
|
369
|
-
{ id: 'gx_env', status: process.env.GX_CONFIG_PATH ? 'pass' : 'warn', detail: process.env.GX_CONFIG_PATH ? 'GX_CONFIG_PATH env var is set.' : 'GX_CONFIG_PATH env var is not set for this process.' }
|
|
460
|
+
{ id: 'gx_env', status: process.env.GX_CONFIG_PATH ? 'pass' : 'warn', detail: process.env.GX_CONFIG_PATH ? 'GX_CONFIG_PATH env var is set.' : 'GX_CONFIG_PATH env var is not set for this process.' },
|
|
461
|
+
{ id: 'client_config_sync', status: clientCrossCheck.status, detail: clientCrossCheck.detail }
|
|
370
462
|
];
|
|
371
463
|
|
|
372
|
-
if (
|
|
464
|
+
if (data.gatewayExeFound) {
|
|
373
465
|
const probe = await probeGatewaySpawn();
|
|
374
466
|
checks.push({ id: 'gateway_spawn_probe', status: probe.status, detail: probe.detail });
|
|
375
467
|
} else {
|
|
376
|
-
checks.push({ id: 'gateway_spawn_probe', status: 'warn', detail: 'Spawn probe skipped
|
|
468
|
+
checks.push({ id: 'gateway_spawn_probe', status: 'warn', detail: 'Spawn probe skipped: gateway exe not found.' });
|
|
377
469
|
}
|
|
378
470
|
|
|
379
471
|
if (options.mcpSmoke) {
|
|
@@ -399,9 +491,6 @@ async function handleDoctor(options, ctx) {
|
|
|
399
491
|
if (checks.length > options.limit) {
|
|
400
492
|
help.push(`Run 'genexus-mcp doctor --limit ${checks.length}' for all checks.`);
|
|
401
493
|
}
|
|
402
|
-
if (!options.full) {
|
|
403
|
-
help.push('Run `genexus-mcp doctor --full` to include runtime spawn probe.');
|
|
404
|
-
}
|
|
405
494
|
if (!options.mcpSmoke) {
|
|
406
495
|
help.push('Run `genexus-mcp doctor --mcp-smoke` to execute MCP protocol smoke checks.');
|
|
407
496
|
}
|
|
@@ -774,6 +863,16 @@ async function handleLayout(subcommand, options, ctx) {
|
|
|
774
863
|
};
|
|
775
864
|
}
|
|
776
865
|
|
|
866
|
+
function buildInteractiveInitHelp(patchResult) {
|
|
867
|
+
const help = [];
|
|
868
|
+
if (patchResult.patched.length === 0) {
|
|
869
|
+
help.push('Set `GX_CONFIG_PATH` in your MCP client env to the generated config path.');
|
|
870
|
+
} else if (process.platform === 'win32' && !process.env.GENEXUS_MCP_GATEWAY_EXE) {
|
|
871
|
+
help.push('Windows + corporate AppLocker: the npx launcher resolves the gateway from %LOCALAPPDATA%\\npm-cache, which is commonly blocked. If clients fail with "Failed to connect" / Access denied, reinstall to a whitelisted path via scripts/install.ps1.');
|
|
872
|
+
}
|
|
873
|
+
return help;
|
|
874
|
+
}
|
|
875
|
+
|
|
777
876
|
async function runInteractiveInit(ctx) {
|
|
778
877
|
const defaultGx = discoverGeneXusInstallation() || 'C:\\Program Files (x86)\\GeneXus\\GeneXus18';
|
|
779
878
|
|
|
@@ -820,7 +919,7 @@ async function runInteractiveInit(ctx) {
|
|
|
820
919
|
noOp: !created.changed,
|
|
821
920
|
clientsPatchedCount: patchResult.patched.length
|
|
822
921
|
},
|
|
823
|
-
help: patchResult
|
|
922
|
+
help: buildInteractiveInitHelp(patchResult),
|
|
824
923
|
meta: {
|
|
825
924
|
patchedClients: patchResult.patched,
|
|
826
925
|
failedClients: patchResult.failed,
|
|
@@ -828,10 +927,26 @@ async function runInteractiveInit(ctx) {
|
|
|
828
927
|
}
|
|
829
928
|
}
|
|
830
929
|
};
|
|
831
|
-
} catch {
|
|
930
|
+
} catch (err) {
|
|
931
|
+
if (err && err.code === 'GATEWAY_EXE_MISSING') {
|
|
932
|
+
return {
|
|
933
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
934
|
+
envelope: operationalErrorEnvelope(
|
|
935
|
+
'GENEXUS_MCP_GATEWAY_EXE points to a path that does not exist. Client configs were NOT modified.',
|
|
936
|
+
ctx.EXIT_CODES.ERROR,
|
|
937
|
+
[
|
|
938
|
+
`Path checked: ${process.env.GENEXUS_MCP_GATEWAY_EXE}`,
|
|
939
|
+
'Either unset GENEXUS_MCP_GATEWAY_EXE (then init writes the npx-based launcher) or re-run scripts/install.ps1 to materialize the exe at that path.'
|
|
940
|
+
]
|
|
941
|
+
)
|
|
942
|
+
};
|
|
943
|
+
}
|
|
832
944
|
return {
|
|
833
945
|
exitCode: ctx.EXIT_CODES.ERROR,
|
|
834
|
-
envelope: operationalErrorEnvelope(
|
|
946
|
+
envelope: operationalErrorEnvelope(
|
|
947
|
+
sanitizeOperationalMessage(`Interactive init failed: ${err && err.message ? err.message : 'unknown error'}`),
|
|
948
|
+
ctx.EXIT_CODES.ERROR
|
|
949
|
+
)
|
|
835
950
|
};
|
|
836
951
|
} finally {
|
|
837
952
|
rl.close();
|
|
@@ -913,8 +1028,11 @@ async function handleInit(options, ctx) {
|
|
|
913
1028
|
if (patchResult.patched.length === 0 && !options.writeClients) {
|
|
914
1029
|
help.push('Run `genexus-mcp init --kb "<kbPath>" --gx "<geneXusPath>" --write-clients` to patch supported clients.');
|
|
915
1030
|
}
|
|
1031
|
+
if (patchResult.patched.length > 0 && process.platform === 'win32' && !process.env.GENEXUS_MCP_GATEWAY_EXE) {
|
|
1032
|
+
help.push('Windows + corporate AppLocker: the npx launcher resolves the gateway from %LOCALAPPDATA%\\npm-cache, which is commonly blocked. If clients fail with "Failed to connect" / Access denied, reinstall to a whitelisted path via scripts/install.ps1.');
|
|
1033
|
+
}
|
|
916
1034
|
if (verification.summary.fail > 0 || verification.summary.warn > 0) {
|
|
917
|
-
help.push('Some verification checks did not pass. Run `genexus-mcp doctor --
|
|
1035
|
+
help.push('Some verification checks did not pass. Run `genexus-mcp doctor --mcp-smoke` for details.');
|
|
918
1036
|
}
|
|
919
1037
|
if (options.noSmoke) {
|
|
920
1038
|
help.push('MCP protocol smoke was skipped (--no-smoke). Re-run `genexus-mcp doctor --mcp-smoke` to validate end-to-end.');
|
|
@@ -950,10 +1068,27 @@ async function handleInit(options, ctx) {
|
|
|
950
1068
|
}
|
|
951
1069
|
}
|
|
952
1070
|
};
|
|
953
|
-
} catch {
|
|
1071
|
+
} catch (err) {
|
|
1072
|
+
if (err && err.code === 'GATEWAY_EXE_MISSING') {
|
|
1073
|
+
return {
|
|
1074
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1075
|
+
envelope: operationalErrorEnvelope(
|
|
1076
|
+
'GENEXUS_MCP_GATEWAY_EXE points to a path that does not exist. Client configs were NOT modified.',
|
|
1077
|
+
ctx.EXIT_CODES.ERROR,
|
|
1078
|
+
[
|
|
1079
|
+
`Path checked: ${process.env.GENEXUS_MCP_GATEWAY_EXE}`,
|
|
1080
|
+
'Either unset GENEXUS_MCP_GATEWAY_EXE (then init writes the npx-based launcher) or re-run scripts/install.ps1 to materialize the exe at that path.'
|
|
1081
|
+
]
|
|
1082
|
+
)
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
954
1085
|
return {
|
|
955
1086
|
exitCode: ctx.EXIT_CODES.ERROR,
|
|
956
|
-
envelope: operationalErrorEnvelope(
|
|
1087
|
+
envelope: operationalErrorEnvelope(
|
|
1088
|
+
sanitizeOperationalMessage(`Failed to write configuration: ${err && err.message ? err.message : 'unknown error'}`),
|
|
1089
|
+
ctx.EXIT_CODES.ERROR,
|
|
1090
|
+
['Check write permissions on the target directory; on Windows, run from a path outside protected areas.']
|
|
1091
|
+
)
|
|
957
1092
|
};
|
|
958
1093
|
}
|
|
959
1094
|
}
|
package/cli/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const { spawn } = require('child_process');
|
|
2
2
|
const {
|
|
3
3
|
getGatewayExePath,
|
|
4
|
-
applyLauncherConfigOrExit
|
|
4
|
+
applyLauncherConfigOrExit,
|
|
5
|
+
isPathLikelyAppLockerBlocked
|
|
5
6
|
} = require('./lib/config');
|
|
6
7
|
const {
|
|
7
8
|
SUPPORTED_FORMATS,
|
|
@@ -294,6 +295,17 @@ function parseArgs(argv) {
|
|
|
294
295
|
return result;
|
|
295
296
|
}
|
|
296
297
|
|
|
298
|
+
function writeAppLockerHint(stderr, gatewayExePath) {
|
|
299
|
+
const riskyZone = isPathLikelyAppLockerBlocked(gatewayExePath);
|
|
300
|
+
stderr.write('[genexus-mcp] Likely cause: Windows AppLocker / SRP is blocking execution from this path.\n');
|
|
301
|
+
if (riskyZone) {
|
|
302
|
+
stderr.write(`[genexus-mcp] The gateway exe lives under %${riskyZone}% (typically restricted by corporate policy):\n[genexus-mcp] ${gatewayExePath}\n`);
|
|
303
|
+
}
|
|
304
|
+
stderr.write('[genexus-mcp] Remediation: install to a whitelisted path with:\n');
|
|
305
|
+
stderr.write('[genexus-mcp] iex (irm https://raw.githubusercontent.com/lennix1337/Genexus18MCP/main/scripts/install.ps1)\n');
|
|
306
|
+
stderr.write('[genexus-mcp] Or copy the package `publish/` folder to a path outside %APPDATA%/%LOCALAPPDATA% and point the MCP client at that exe.\n');
|
|
307
|
+
}
|
|
308
|
+
|
|
297
309
|
async function launchGateway(passthroughArgs, options) {
|
|
298
310
|
const setup = applyLauncherConfigOrExit({
|
|
299
311
|
cwd: process.cwd(),
|
|
@@ -323,6 +335,11 @@ async function launchGateway(passthroughArgs, options) {
|
|
|
323
335
|
child.on('error', (err) => {
|
|
324
336
|
if (!options.quiet) {
|
|
325
337
|
process.stderr.write(`[genexus-mcp] ERROR: Failed to start gateway process: ${err.message}\n`);
|
|
338
|
+
const code = err && (err.code || err.errno);
|
|
339
|
+
const accessDenied = code === 'EACCES' || code === 'EPERM' || /access is denied|access denied|acesso negado/i.test(err.message || '');
|
|
340
|
+
if (accessDenied) {
|
|
341
|
+
writeAppLockerHint(process.stderr, gatewayExePath);
|
|
342
|
+
}
|
|
326
343
|
}
|
|
327
344
|
resolve(EXIT_CODES.ERROR);
|
|
328
345
|
});
|
package/cli/lib/config.js
CHANGED
|
@@ -278,6 +278,18 @@ function filterClientTargets(targets, opts = {}) {
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
function patchClientConfig(targetConfigPath, opts = {}) {
|
|
281
|
+
// Validate corporate-install env var before we write it into N client configs.
|
|
282
|
+
// Otherwise we silently propagate a broken path to every AI client and the
|
|
283
|
+
// user only finds out when each one fails with "Failed to connect".
|
|
284
|
+
if (process.env.GENEXUS_MCP_GATEWAY_EXE && !fs.existsSync(process.env.GENEXUS_MCP_GATEWAY_EXE)) {
|
|
285
|
+
const err = new Error(
|
|
286
|
+
`GENEXUS_MCP_GATEWAY_EXE points to a path that does not exist: ${process.env.GENEXUS_MCP_GATEWAY_EXE}. ` +
|
|
287
|
+
`Refusing to write this into client configs. Unset the env var (to use the npx launcher) or re-run scripts/install.ps1 to materialize the exe.`
|
|
288
|
+
);
|
|
289
|
+
err.code = 'GATEWAY_EXE_MISSING';
|
|
290
|
+
throw err;
|
|
291
|
+
}
|
|
292
|
+
|
|
281
293
|
const launcher = getLauncher();
|
|
282
294
|
const onlyExisting = opts.onlyExisting !== false;
|
|
283
295
|
const candidates = filterClientTargets(getClientConfigTargets(), {
|
|
@@ -456,6 +468,65 @@ function tomlString(value) {
|
|
|
456
468
|
return `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
457
469
|
}
|
|
458
470
|
|
|
471
|
+
function isPathLikelyAppLockerBlocked(exePath) {
|
|
472
|
+
if (process.platform !== 'win32' || !exePath) return null;
|
|
473
|
+
const norm = String(exePath).toLowerCase().replace(/\\/g, '/');
|
|
474
|
+
const candidates = [
|
|
475
|
+
{ name: 'APPDATA', base: process.env.APPDATA },
|
|
476
|
+
{ name: 'LOCALAPPDATA', base: process.env.LOCALAPPDATA },
|
|
477
|
+
{ name: 'TEMP', base: process.env.TEMP },
|
|
478
|
+
{ name: 'TMP', base: process.env.TMP }
|
|
479
|
+
];
|
|
480
|
+
for (const { name, base } of candidates) {
|
|
481
|
+
if (!base) continue;
|
|
482
|
+
const baseNorm = base.toLowerCase().replace(/\\/g, '/').replace(/\/$/, '');
|
|
483
|
+
if (norm.startsWith(baseNorm + '/')) return name;
|
|
484
|
+
}
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function normalizeExePath(p) {
|
|
489
|
+
if (!p) return '';
|
|
490
|
+
let s = String(p).trim().replace(/^"|"$/g, '');
|
|
491
|
+
s = s.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
492
|
+
if (process.platform === 'win32') s = s.toLowerCase();
|
|
493
|
+
return s;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function readClientCommandEntry(client) {
|
|
497
|
+
if (!fs.existsSync(client.path)) return null;
|
|
498
|
+
try {
|
|
499
|
+
if (client.format === 'mcpServers') {
|
|
500
|
+
const parsed = readJsonFileSafe(client.path);
|
|
501
|
+
if (!parsed || typeof parsed !== 'object') return null;
|
|
502
|
+
const entry = parsed.mcpServers && parsed.mcpServers.genexus;
|
|
503
|
+
if (!entry) return null;
|
|
504
|
+
return { command: entry.command || null, args: Array.isArray(entry.args) ? entry.args : [] };
|
|
505
|
+
}
|
|
506
|
+
if (client.format === 'opencode') {
|
|
507
|
+
const parsed = readJsonFileSafe(client.path);
|
|
508
|
+
if (!parsed || typeof parsed !== 'object') return null;
|
|
509
|
+
const entry = parsed.mcp && parsed.mcp.genexus;
|
|
510
|
+
if (!entry || !Array.isArray(entry.command) || entry.command.length === 0) return null;
|
|
511
|
+
return { command: entry.command[0], args: entry.command.slice(1) };
|
|
512
|
+
}
|
|
513
|
+
if (client.format === 'codex-toml') {
|
|
514
|
+
const raw = fs.readFileSync(client.path, 'utf8');
|
|
515
|
+
// Minimal extraction: find [mcp_servers.genexus] block and pull command = "..."
|
|
516
|
+
const blockRe = /\[mcp_servers\.genexus\]([\s\S]*?)(?=\n\[|$)/;
|
|
517
|
+
const m = raw.match(blockRe);
|
|
518
|
+
if (!m) return null;
|
|
519
|
+
const cmdMatch = m[1].match(/^\s*command\s*=\s*"((?:[^"\\]|\\.)*)"/m);
|
|
520
|
+
if (!cmdMatch) return null;
|
|
521
|
+
const command = cmdMatch[1].replace(/\\\\/g, '\\').replace(/\\"/g, '"');
|
|
522
|
+
return { command, args: [] };
|
|
523
|
+
}
|
|
524
|
+
} catch {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
|
|
459
530
|
function getLocalAppDataCacheDir() {
|
|
460
531
|
if (process.platform !== 'win32') return null;
|
|
461
532
|
const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
@@ -628,5 +699,8 @@ module.exports = {
|
|
|
628
699
|
addKbToConfig,
|
|
629
700
|
removeKbFromConfig,
|
|
630
701
|
switchActiveKb,
|
|
631
|
-
applyLauncherConfigOrExit
|
|
702
|
+
applyLauncherConfigOrExit,
|
|
703
|
+
isPathLikelyAppLockerBlocked,
|
|
704
|
+
normalizeExePath,
|
|
705
|
+
readClientCommandEntry
|
|
632
706
|
};
|
package/cli/lib/update-check.js
CHANGED
|
@@ -2,6 +2,13 @@ const fs = require('fs');
|
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const https = require('https');
|
|
5
|
+
const {
|
|
6
|
+
getGatewayExePath,
|
|
7
|
+
getClientConfigTargets,
|
|
8
|
+
filterClientTargets,
|
|
9
|
+
readClientCommandEntry,
|
|
10
|
+
normalizeExePath
|
|
11
|
+
} = require('./config');
|
|
5
12
|
|
|
6
13
|
const REPO = 'lennix1337/Genexus18MCP';
|
|
7
14
|
const NPM_PACKAGE = 'genexus-mcp';
|
|
@@ -159,9 +166,37 @@ function startBackgroundUpdateCheck(opts) {
|
|
|
159
166
|
scheduleBackgroundFetch();
|
|
160
167
|
}
|
|
161
168
|
|
|
169
|
+
function detectClientExeDrift() {
|
|
170
|
+
try {
|
|
171
|
+
const packageNorm = normalizeExePath(getGatewayExePath());
|
|
172
|
+
const targets = filterClientTargets(getClientConfigTargets(), { platform: process.platform });
|
|
173
|
+
const mismatches = [];
|
|
174
|
+
for (const client of targets) {
|
|
175
|
+
if (!fs.existsSync(client.path)) continue;
|
|
176
|
+
const entry = readClientCommandEntry(client);
|
|
177
|
+
if (!entry || !entry.command) continue;
|
|
178
|
+
const cmd = entry.command;
|
|
179
|
+
// Skip launchers that resolve via npm at runtime.
|
|
180
|
+
if (/(^|[\\/])(npx|npx\.cmd|node|node\.exe)$/i.test(cmd) || /[\\/]genexus-mcp(\.cmd)?$/i.test(cmd)) continue;
|
|
181
|
+
if (!/\.exe$/i.test(cmd)) continue;
|
|
182
|
+
if (normalizeExePath(cmd) !== packageNorm) {
|
|
183
|
+
mismatches.push({ client: client.name, configured: cmd });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return mismatches;
|
|
187
|
+
} catch {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
162
192
|
async function handleUpdate(_options, ctx) {
|
|
163
193
|
const current = getPackageVersion();
|
|
164
194
|
const result = await fetchLatestRelease();
|
|
195
|
+
const mismatches = detectClientExeDrift();
|
|
196
|
+
|
|
197
|
+
const driftHelp = mismatches.length
|
|
198
|
+
? [`WARNING: ${mismatches.length} AI client(s) point at a gateway exe that is NOT this npm package — \`npm install -g ${NPM_PACKAGE}@latest\` will NOT update them. Mismatches: ${mismatches.map((m) => `${m.client} -> ${m.configured}`).join('; ')}. Re-run scripts/install.ps1 (or genexus-mcp init --write-clients) to resync.`]
|
|
199
|
+
: [];
|
|
165
200
|
|
|
166
201
|
if (!result) {
|
|
167
202
|
return {
|
|
@@ -171,9 +206,10 @@ async function handleUpdate(_options, ctx) {
|
|
|
171
206
|
current,
|
|
172
207
|
latest: null,
|
|
173
208
|
updateAvailable: false,
|
|
174
|
-
fetched: false
|
|
209
|
+
fetched: false,
|
|
210
|
+
clientDrift: mismatches
|
|
175
211
|
},
|
|
176
|
-
help: ['Could not reach GitHub releases API. Check connectivity or retry later.']
|
|
212
|
+
help: ['Could not reach GitHub releases API. Check connectivity or retry later.', ...driftHelp]
|
|
177
213
|
}
|
|
178
214
|
};
|
|
179
215
|
}
|
|
@@ -185,7 +221,7 @@ async function handleUpdate(_options, ctx) {
|
|
|
185
221
|
});
|
|
186
222
|
|
|
187
223
|
const updateAvailable = compareSemver(result.latestVersion, current || '0.0.0') > 0;
|
|
188
|
-
const
|
|
224
|
+
const baseHelp = updateAvailable
|
|
189
225
|
? [`Run: npm install -g ${NPM_PACKAGE}@latest`, result.releaseUrl ? `Release: ${result.releaseUrl}` : null].filter(Boolean)
|
|
190
226
|
: ['Already on latest version.'];
|
|
191
227
|
|
|
@@ -198,9 +234,10 @@ async function handleUpdate(_options, ctx) {
|
|
|
198
234
|
releaseUrl: result.releaseUrl,
|
|
199
235
|
updateAvailable,
|
|
200
236
|
installCommand: `npm install -g ${NPM_PACKAGE}@latest`,
|
|
201
|
-
fetched: true
|
|
237
|
+
fetched: true,
|
|
238
|
+
clientDrift: mismatches
|
|
202
239
|
},
|
|
203
|
-
help
|
|
240
|
+
help: [...baseHelp, ...driftHelp]
|
|
204
241
|
}
|
|
205
242
|
};
|
|
206
243
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genexus-mcp",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.4",
|
|
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": [
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"targets": {
|
|
8
8
|
".NETCoreApp,Version=v8.0": {},
|
|
9
9
|
".NETCoreApp,Version=v8.0/win-x64": {
|
|
10
|
-
"GxMcp.Gateway/2.6.
|
|
10
|
+
"GxMcp.Gateway/2.6.4": {
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"Newtonsoft.Json": "13.0.3",
|
|
13
13
|
"System.Management": "10.0.5",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"libraries": {
|
|
69
|
-
"GxMcp.Gateway/2.6.
|
|
69
|
+
"GxMcp.Gateway/2.6.4": {
|
|
70
70
|
"type": "project",
|
|
71
71
|
"serviceable": false,
|
|
72
72
|
"sha512": ""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
[
|
|
2
|
-
{"name":"genexus_whoami","description":"Return current KB context, GeneXus install,
|
|
2
|
+
{"name":"genexus_whoami","description":"Return current KB context, GeneXus install, MCP version, AND inline playbooks for common flows (WWP, popup, layout). Call this FIRST on every session — the `playbooks` block routes you to the right tool without exploration.","inputSchema":{"type":"object","properties":{}}},
|
|
3
|
+
{"name":"genexus_recipe","description":"Fetch a step-by-step playbook for a named flow (e.g. 'wwp_on_transaction', 'wwp_on_webpanel', 'create_popup', 'edit_pattern_instance', 'add_custom_button'). Returns prereq/steps/pitfalls. Call name='list' to enumerate available recipes.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Recipe key, or 'list' to enumerate."}},"required":["name"]}},
|
|
3
4
|
{"name":"genexus_query","description":"Search objects in the active KB. Supports prefixes (type:, usedby:, parent:, parentPath:, description:). Compact projection by default. See genexus://kb/tool-help/genexus_query for examples.","inputSchema":{"type":"object","properties":{"query":{"type":"string"},"typeFilter":{"type":"string"},"domainFilter":{"type":"string"},"limit":{"type":"integer"},"inline_read_top":{"type":"integer","description":"0-3. Inline reads of top N in response."},"kb":{"type":"string","description":"Target KB. Required when 2+ open."},"axiCompact":{"type":"boolean","description":"Default true. Returns compact projection (name,type,path). Pass false for full payload.","default":true}},"required":["query"]}},
|
|
4
5
|
{"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","description":"Legacy: matches name OR description. Prefer nameFilter/descriptionFilter."},"nameFilter":{"type":"string","description":"Substring match on object name only."},"descriptionFilter":{"type":"string","description":"Substring match on description only."},"pathPrefix":{"type":"string","description":"Folder path prefix, e.g. 'Root Module/ClickSign/'."},"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. Inline reads of top N in response."},"kb":{"type":"string","description":"Target KB. Required when 2+ open."},"axiCompact":{"type":"boolean","description":"Default true. Returns compact projection (name,type,path,parentPath). Pass false for full payload.","default":true}}}},
|
|
5
6
|
{"name":"genexus_read","description":"Read source/metadata parts of one or more objects. Pass name or targets, plus parts=[...]. Paginate large source with offset/limit. See genexus://kb/tool-help/genexus_read for examples.","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. Required when 2+ open."}}}},
|
|
6
|
-
{"name":"genexus_edit","description":"Edit an object part. Pass name or targets (mutually exclusive). Mode: full | patch. Use dryRun before persisting. See
|
|
7
|
+
{"name":"genexus_edit","description":"Edit an object part. Pass name or targets (mutually exclusive). Mode: full | patch. Use dryRun before persisting. For WWP screens edit the host WorkWithPlus<X>.PatternInstance — NOT the WebForm of the parent (gets overwritten on reapply). See recipe 'edit_pattern_instance' and 'add_custom_button' via genexus_recipe.","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. Prefer separate operation/context/content params for new code.","anyOf":[{"type":"string"},{"type":"object","properties":{"find":{"type":"string"},"replace":{"type":"string"}},"required":["find","replace"]}]},"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. Required when 2+ open."}}}},
|
|
7
8
|
{"name":"genexus_inspect","description":"Snapshot of an object: metadata, variables, structure, signature. Raw object shape without source text. For source/code use genexus_read.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"include":{"type":"array","items":{"type":"string","enum":["metadata","variables","signature","structure","parts","controls","events_repertoire","callers"]}},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}},"required":["name"]}},
|
|
8
|
-
{"name":"genexus_analyze","description":"Cross-object semantic analysis: impact, dependencies, complexity, naming, summary
|
|
9
|
+
{"name":"genexus_analyze","description":"Cross-object semantic analysis: impact, dependencies, complexity, naming, summary. See genexus://kb/tool-help/genexus_analyze for mode selection.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"mode":{"type":"string","enum":["linter","navigation","hierarchy","impact","data_context","ui_context","pattern_metadata","summary"]},"kb":{"type":"string","description":"Target KB. Required when 2+ open."},"waitForIndex":{"type":"boolean","description":"For mode=impact: when true (default) block up to 30s for the index to be Ready; when false return a status envelope immediately if the index is Cold/Reindexing.","default":true}},"required":["name","mode"]}},
|
|
9
10
|
{"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. Required when 2+ open."}},"required":["name"]}},
|
|
10
11
|
{"name":"genexus_lifecycle","description":"Build, validate, index, or poll the KB. Long ops are async with operationId. See genexus://kb/tool-help/genexus_lifecycle for actions and target formats.","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."},"compact":{"type":"boolean","description":"Status: when true (default) returns counts + top-10 errors + warning dedup. Set false for legacy full Output/Errors[]."},"force":{"type":"boolean","description":"Index: when true, clears in-memory + on-disk snapshot and runs a full SDK rescan. Use when impact analyze reports indexEdgesMissing or after adding/renaming objects."},"includeCallees":{"type":"string","enum":["none","direct","transitive"],"description":"Build: auto-expand target via call graph so callees compile before callers (fixes CS0246 from missing DLLs). Default transitive."},"buildPlanCap":{"type":"integer","description":"Build: max expanded nodes before BuildPlanTooLarge (default 200)."},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}},"required":["action"]}},
|
|
11
12
|
{"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. Required when 2+ open."}},"required":["action"]}},
|
|
12
13
|
{"name":"genexus_test","description":"Execute native GeneXus tests (GXtest).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}},"required":["name"]}},
|
|
13
|
-
{"name":"genexus_create_object","description":"Create any IDE-creatable GeneXus object (Transaction, Procedure, WebPanel, SDPanel, SDT, DataProvider, Domain, Dashboard, WorkflowDiagram, etc.). Domain takes dataType+length or enumValues. See genexus://kb/tool-help/genexus_create_object.","inputSchema":{"type":"object","properties":{"type":{"type":"string"},"name":{"type":"string"},"dataType":{"type":"string"},"length":{"type":"integer"},"decimals":{"type":"integer"},"signed":{"type":"boolean"},"description":{"type":"string"},"basedOn":{"type":"string"},"enumValues":{"type":"array","items":{"type":"object"}},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}},"required":["type","name"]}},
|
|
14
|
+
{"name":"genexus_create_object","description":"Create any IDE-creatable GeneXus object (Transaction, Procedure, WebPanel, SDPanel, SDT, DataProvider, Domain, Dashboard, WorkflowDiagram, etc.). Domain takes dataType+length or enumValues. NOT for WorkWithPlus/WWP — that is a pattern applied via genexus_apply_pattern (see recipe 'wwp_on_transaction' or 'wwp_on_webpanel'). NOT for popups with editable inputs — use genexus_create_popup. See genexus://kb/tool-help/genexus_create_object.","inputSchema":{"type":"object","properties":{"type":{"type":"string"},"name":{"type":"string"},"dataType":{"type":"string"},"length":{"type":"integer"},"decimals":{"type":"integer"},"signed":{"type":"boolean"},"description":{"type":"string"},"basedOn":{"type":"string"},"enumValues":{"type":"array","items":{"type":"object"}},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}},"required":["type","name"]}},
|
|
14
15
|
{"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
16
|
{"name":"genexus_sdk_probe","description":"Scan loaded GeneXus SDK assemblies and dump a structured map of every public type, method, property to docs/sdk-probe/. Use when investigating new SDK surface or hunting for an entry point. Outputs raw.json + INDEX.md + generators.md.","inputSchema":{"type":"object","properties":{"outputDir":{"type":"string","description":"Optional absolute path. Defaults to <repo>/docs/sdk-probe/ or %TEMP%/gxmcp_sdk_probe/."}}}},
|
|
16
17
|
{"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"]}},
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
{"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"]}},
|
|
38
39
|
{"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. Required when 2+ open."}},"required":["action","name"]}},
|
|
39
40
|
{"name":"genexus_preview","description":"Render preview of a WebPanel via headless Chrome (chrome-devtools-axi). Opens the launcher page, fills required parms, navigates to the target, captures HTML/a11y/screenshot, optionally diffs vs baseline. Returns status + captures.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Target WebPanel name."},"parms":{"type":"object","description":"Per-call launcher parms (merged over config defaults and objectParms)."},"launcher":{"type":"string","description":"Launcher page (default 'auto' = config.launcher)."},"buildFirst":{"type":"boolean","default":false,"description":"If true, dispatch a build before opening the browser."},"waitMs":{"type":"integer","default":3000,"description":"Milliseconds to wait after navigation before capture."},"capture":{"type":"array","items":{"type":"string","enum":["html","a11y","screenshot","console"]},"description":"Capture set (default ['html','a11y'])."},"diffBaseline":{"type":"boolean","default":false,"description":"Compute structural diff vs stored a11y baseline."},"updateBaseline":{"type":"boolean","default":false,"description":"Persist current a11y snapshot as the new baseline."},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}},"required":["name"]}},
|
|
40
|
-
{"name":"genexus_apply_pattern","description":"Apply a GeneXus pattern (e.g. WorkWithPlus) to a parent object. Equivalent to IDE 'Right-click → Apply Pattern'.","inputSchema":{"type":"object","required":["name","pattern"],"properties":{"name":{"type":"string","description":"Target KBObject name (Transaction, WebPanel, etc.)"},"pattern":{"type":"string","description":"Pattern key ('WorkWithPlus') or GUID"},"settings":{"type":"object","description":"Optional pattern-instance settings (deeply nested config tree from the pattern's settings dialog)"},"reapply":{"type":"boolean","default":false,"description":"Re-run on existing instance instead of first-time apply"},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}}}},
|
|
41
|
+
{"name":"genexus_apply_pattern","description":"Apply a GeneXus pattern (e.g. WorkWithPlus) to a parent object. Equivalent to IDE 'Right-click → Apply Pattern'. ALWAYS run genexus_inspect first to confirm parentType — Transaction (family-gen, no template) vs WebPanel/SDPanel (direct-attach, pass settings.template). Other types are rejected. See recipe 'wwp_on_transaction' or 'wwp_on_webpanel' via genexus_recipe.","inputSchema":{"type":"object","required":["name","pattern"],"properties":{"name":{"type":"string","description":"Target KBObject name (Transaction, WebPanel, etc.)"},"pattern":{"type":"string","description":"Pattern key ('WorkWithPlus') or GUID"},"settings":{"type":"object","description":"Optional pattern-instance settings (deeply nested config tree from the pattern's settings dialog)"},"reapply":{"type":"boolean","default":false,"description":"Re-run on existing instance instead of first-time apply"},"validate":{"type":"boolean","default":false,"description":"After apply, build the generated host to verify it compiles cleanly. Response gains a `validation` block: {status: ok|failed|timeout, errorCount, warningCount, errors[], warnings[], durationMs}. Adds 60-180s wall time but catches binding/wiring errors the LLM would only see by opening the IDE."},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}}}},
|
|
41
42
|
{"name":"genexus_edit_and_build","description":"Edit an object then rebuild its callers in one call. Returns edit diff + impact + build operationId. See genexus://kb/tool-help/genexus_edit_and_build for examples.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Target object name."},"part":{"type":"string","description":"Part to edit (Source, Rules, ...)."},"content":{"type":"string","description":"New content or unified diff."},"mode":{"type":"string","enum":["full","patch"],"default":"patch"},"type":{"type":"string","description":"Disambiguates when name matches multiple objects."},"dryRun":{"type":"boolean","default":false},"buildIncludeCallees":{"type":"string","enum":["none","direct","transitive"],"default":"direct"},"buildPlanCap":{"type":"integer","default":200},"waitForIndex":{"type":"boolean","default":true},"waitTimeoutMs":{"type":"integer","default":30000}},"required":["name","part","content"]}},
|
|
42
43
|
{"name":"genexus_create_popup","description":"W3: create a popup WebPanel with a Form type=\"layout\" body so radio/combo bindings render editable. Spec carries title, description, inputs (radio|combo|text with options), buttons, inParms, outParms.","inputSchema":{"type":"object","required":["name","spec"],"properties":{"name":{"type":"string","description":"WebPanel name. Created if missing; updated if exists."},"spec":{"type":"object","description":"Popup spec.","properties":{"title":{"type":"string"},"description":{"type":"string"},"inputs":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["radio","combo","text"]},"varName":{"type":"string"},"label":{"type":"string"},"options":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}}}},"showWhen":{"type":"string","description":"e.g. \"RespRegProf == 'S'\"; emits Refresh event toggling group visibility."}},"required":["type","varName"]}},"buttons":{"type":"array","items":{"type":"object","properties":{"caption":{"type":"string"},"event":{"type":"string"}}}},"inParms":{"type":"array","items":{"type":"string"},"description":"e.g. [\"AluAnoCad:Numeric(2)\"]"},"outParms":{"type":"array","items":{"type":"string"}}}},"kb":{"type":"string","description":"Target KB. Required when 2+ open."}}}}
|
|
43
44
|
]
|
|
Binary file
|
|
Binary file
|