genexus-mcp 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -6
- package/cli/commands/axi.js +234 -22
- package/cli/lib/config.js +299 -40
- package/cli/run.test.js +233 -3
- package/config/gx-versions.json +20 -0
- package/package.json +5 -2
- package/publish/GxMcp.Gateway.deps.json +2 -2
- package/publish/GxMcp.Gateway.dll +0 -0
- package/publish/GxMcp.Gateway.exe +0 -0
- package/publish/config/gx-versions.json +20 -0
- package/publish/config.json +8 -8
- package/publish/gxmcp-manifest.json +9 -9
- package/publish/gxmcp-sbom.json +4 -4
- package/publish/nexus-ide.vsix +0 -0
- package/publish/worker/GxMcp.Worker.exe +0 -0
- package/publish/worker/config/gx-versions.json +20 -0
- package/publish/worker/gx-versions.json +20 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GeneXus MCP Server — GeneXus
|
|
1
|
+
# GeneXus MCP Server — Multi-version GeneXus for Claude, Cursor, and AI Agents
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/genexus-mcp)
|
|
4
4
|
[](https://www.npmjs.com/package/genexus-mcp)
|
|
@@ -12,12 +12,63 @@
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
**GeneXus MCP Server** lets AI agents — Claude Desktop, Claude Code, Cursor, Antigravity, and any MCP-compatible client — read, edit, analyze, and refactor objects inside a
|
|
15
|
+
**GeneXus MCP Server** lets AI agents — Claude Desktop, Claude Code, Cursor, Antigravity, and any MCP-compatible client — read, edit, analyze, and refactor objects inside a Knowledge Base supported by the selected GeneXus SDK. It talks to the **native GeneXus SDK**, so the agent works with the *real* KB, not a copy or a parsed approximation.
|
|
16
16
|
|
|
17
17
|
In practice: you point the MCP at your KB, then ask your AI assistant things like *"list all transactions with attribute CustomerId"*, *"add a rule to the Order transaction that validates the total"*, or *"refactor this procedure to use the new SDT"* — and it does it.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## Multi-version SDK support
|
|
22
|
+
|
|
23
|
+
The same MCP distribution supports the SDK majors listed in the generated
|
|
24
|
+
compatibility document. Each configured MCP process selects one installed SDK
|
|
25
|
+
with `--gx`; no separate MCP installation is required. The commands below are
|
|
26
|
+
examples of switching the existing configuration, not running two majors in
|
|
27
|
+
the same process:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx genexus-mcp@latest init --kb "C:\KBs\KBTeste17" --gx "C:\Program Files (x86)\GeneXus\GeneXus17Trial"
|
|
31
|
+
# To switch this MCP configuration to GX18:
|
|
32
|
+
npx genexus-mcp@latest init --kb "C:\KBs\MyGX18KB" --gx "C:\Program Files (x86)\GeneXus\GeneXus18"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
After switching the SDK or KB, fully restart the AI client so it reloads the
|
|
36
|
+
MCP process and its tool schemas. If GX17 and GX18 must run simultaneously,
|
|
37
|
+
use separate MCP configurations and ports.
|
|
38
|
+
|
|
39
|
+
`init` also reads the KB `.gxw` major and the selected `GeneXus.exe` metadata.
|
|
40
|
+
It aborts before writing `config.json` when the majors conflict or an automatic
|
|
41
|
+
selection cannot be verified. `genexus-mcp doctor` exposes the same result as
|
|
42
|
+
the `kb_sdk_compatibility` check. For a disposable fixture, the
|
|
43
|
+
[live-KB harness](docs/live-kb-test-harness.md) includes a catalog-driven matrix
|
|
44
|
+
that checks every supported major against one published artifact.
|
|
45
|
+
|
|
46
|
+
The Gateway reports the detected SDK through `genexus_whoami`:
|
|
47
|
+
|
|
48
|
+
- `geneXus.supportedMajors`: explicitly validated SDK majors from the version catalog
|
|
49
|
+
- `geneXus.matchedMajor`: the major detected for the configured installation
|
|
50
|
+
- `geneXus.versionMatches`: whether the detected installation is in that catalog
|
|
51
|
+
- `geneXus.supportedMajor`: retained as the legacy single-major alias for the catalog primary
|
|
52
|
+
|
|
53
|
+
The Worker isolates version-sensitive SDK members behind compatibility adapters.
|
|
54
|
+
For example, Design System helper methods that differ between SDK majors are
|
|
55
|
+
replaced field-by-field by parsing the native `Tokens` and `Styles` parts when
|
|
56
|
+
needed. Existing tool names, arguments, and MCP client configuration formats do
|
|
57
|
+
not change.
|
|
58
|
+
|
|
59
|
+
<!-- BEGIN GENERATED: gx-compatibility -->
|
|
60
|
+
Supported SDK majors: **GeneXus 17, GeneXus 18**.
|
|
61
|
+
Primary SDK: **GeneXus 18**.
|
|
62
|
+
Source of truth: `config/gx-versions.json`.
|
|
63
|
+
<!-- END GENERATED: gx-compatibility -->
|
|
64
|
+
|
|
65
|
+
To add another GeneXus major in the future, add it to the explicit version
|
|
66
|
+
catalog only after compiling the Worker with that SDK and passing the focused
|
|
67
|
+
tests plus a live KB smoke. This prevents the server from claiming compatibility
|
|
68
|
+
based only on a version string.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
21
72
|
## What you can do with it
|
|
22
73
|
|
|
23
74
|
A quick map of what the agent can do against your real KB through the **50 tools** (details in [Tool Surface](#tool-surface)):
|
|
@@ -44,8 +95,8 @@ It works through the **native GeneXus SDK** — the same code paths the IDE uses
|
|
|
44
95
|
Before you start, make sure you have:
|
|
45
96
|
|
|
46
97
|
- ✅ **Windows** (GeneXus is Windows-only)
|
|
47
|
-
- ✅ **GeneXus
|
|
48
|
-
- ✅ **A
|
|
98
|
+
- ✅ **A supported GeneXus SDK** installed locally (see [`docs/generated/supported-versions.md`](docs/generated/supported-versions.md); pass another install path explicitly when needed)
|
|
99
|
+
- ✅ **A Knowledge Base created with a supported GeneXus major** and opened at least once in the IDE (so it's initialized)
|
|
49
100
|
- ✅ **Node.js 18+** — check with `node --version` in a terminal; install from [nodejs.org](https://nodejs.org/) if missing
|
|
50
101
|
- ✅ **An MCP-compatible AI client** — [Claude Desktop](https://claude.ai/download), [Claude Code](https://claude.com/claude-code), Cursor, Antigravity, etc.
|
|
51
102
|
|
|
@@ -445,7 +496,7 @@ The installer writes a `config.json` for you. To customize networking, timeouts,
|
|
|
445
496
|
"HttpPort": 5000,
|
|
446
497
|
"BindAddress": "127.0.0.1",
|
|
447
498
|
"SessionIdleTimeoutMinutes": 10,
|
|
448
|
-
"WorkerIdleTimeoutMinutes":
|
|
499
|
+
"WorkerIdleTimeoutMinutes": 60,
|
|
449
500
|
"MaxOpenKbs": 3
|
|
450
501
|
},
|
|
451
502
|
"GeneXus": {
|
|
@@ -540,7 +591,7 @@ This repo ships a set of **agent skills** under `.gemini/skills/` that any MCP-c
|
|
|
540
591
|
|---|---|
|
|
541
592
|
| `genexus-mastery` | This repository's preferred MCP workflow + multi-KB usage |
|
|
542
593
|
| `genexus18-guidelines` | Local engineering rules layered on top of Nexa |
|
|
543
|
-
| `nexa` | Full
|
|
594
|
+
| `nexa` | Full reference set for the primary GeneXus SDK: every object type, command, type, property — imported from the official [`genexuslabs/genexus-skills`](https://github.com/genexuslabs/genexus-skills) |
|
|
544
595
|
| `frontend/chameleon-controls-library` | 58 Chameleon UI component specs |
|
|
545
596
|
| `frontend/mercury-design-system` | Mercury tokens, bundles, theming |
|
|
546
597
|
| `frontend/design-system-builder` | Authoring custom design systems |
|
package/cli/commands/axi.js
CHANGED
|
@@ -17,7 +17,10 @@ const {
|
|
|
17
17
|
filterClientTargets,
|
|
18
18
|
listSupportedClientIds,
|
|
19
19
|
getLocalAppDataCacheDir,
|
|
20
|
-
|
|
20
|
+
readGeneXusInstallationIdentity,
|
|
21
|
+
readGeneXusKbIdentity,
|
|
22
|
+
compareGeneXusKbAndInstallation,
|
|
23
|
+
getGeneXusVersionCatalog,
|
|
21
24
|
discoverGeneXusInstallation,
|
|
22
25
|
discoverKnowledgeBase,
|
|
23
26
|
discoverKnowledgeBases,
|
|
@@ -51,6 +54,11 @@ function validateClientIds(ids) {
|
|
|
51
54
|
};
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
function isSupportedCatalogMajor(catalog, major) {
|
|
58
|
+
if (!major) return true;
|
|
59
|
+
return catalog.supportedMajors.some((entry) => String(entry.major) === String(major));
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
function parseFieldSelection(raw) {
|
|
55
63
|
if (!raw) return null;
|
|
56
64
|
return raw.split(',').map((v) => v.trim()).filter(Boolean);
|
|
@@ -127,9 +135,9 @@ function usageEnvelope(message, exitCode) {
|
|
|
127
135
|
};
|
|
128
136
|
}
|
|
129
137
|
|
|
130
|
-
function operationalErrorEnvelope(message, exitCode, help = []) {
|
|
138
|
+
function operationalErrorEnvelope(message, exitCode, help = [], code = 'operation_error') {
|
|
131
139
|
return {
|
|
132
|
-
error: { code
|
|
140
|
+
error: { code, message: sanitizeOperationalMessage(message) },
|
|
133
141
|
help,
|
|
134
142
|
meta: { exitCode }
|
|
135
143
|
};
|
|
@@ -572,8 +580,8 @@ async function buildSupportDump({ checks, summary, data, gatewayExePath, ctx })
|
|
|
572
580
|
writeEntry('config.redacted.json', redactConfig(cfg));
|
|
573
581
|
}
|
|
574
582
|
|
|
575
|
-
let
|
|
576
|
-
try {
|
|
583
|
+
let gxIdentity = { version: null, major: null, source: 'unavailable' };
|
|
584
|
+
try { gxIdentity = readGeneXusInstallationIdentity(data.gxPath); } catch { }
|
|
577
585
|
|
|
578
586
|
writeEntry('environment.json', {
|
|
579
587
|
platform: process.platform,
|
|
@@ -586,7 +594,9 @@ async function buildSupportDump({ checks, summary, data, gatewayExePath, ctx })
|
|
|
586
594
|
configSource: data.configSource,
|
|
587
595
|
kbConfigured: !!data.kbPath,
|
|
588
596
|
gxConfigured: !!data.gxPath,
|
|
589
|
-
gxVersion,
|
|
597
|
+
gxVersion: gxIdentity.version,
|
|
598
|
+
gxMajor: gxIdentity.major,
|
|
599
|
+
gxDetectionSource: gxIdentity.source,
|
|
590
600
|
envFlags: {
|
|
591
601
|
GX_CONFIG_PATH: !!process.env.GX_CONFIG_PATH,
|
|
592
602
|
GENEXUS_MCP_GATEWAY_EXE: !!process.env.GENEXUS_MCP_GATEWAY_EXE,
|
|
@@ -674,6 +684,34 @@ async function handleDoctor(options, ctx) {
|
|
|
674
684
|
const gxPath = data.gxPath;
|
|
675
685
|
const kbExists = !!(kbPath && fs.existsSync(kbPath));
|
|
676
686
|
const gxExeExists = !!(gxPath && fs.existsSync(path.join(gxPath, 'genexus.exe')));
|
|
687
|
+
const kbSdkCompatibility = kbExists && gxExeExists
|
|
688
|
+
? compareGeneXusKbAndInstallation(kbPath, gxPath)
|
|
689
|
+
: null;
|
|
690
|
+
const catalog = getGeneXusVersionCatalog();
|
|
691
|
+
|
|
692
|
+
let unsupportedCompatibilityMajor = null;
|
|
693
|
+
let compatibilityStatus = 'warn';
|
|
694
|
+
let compatibilityDetail = 'KB/SDK major compatibility check was skipped until both configured paths exist.';
|
|
695
|
+
if (kbSdkCompatibility) {
|
|
696
|
+
if (!isSupportedCatalogMajor(catalog, kbSdkCompatibility.kb.major)) {
|
|
697
|
+
unsupportedCompatibilityMajor = `KB major ${kbSdkCompatibility.kb.major}`;
|
|
698
|
+
} else if (!isSupportedCatalogMajor(catalog, kbSdkCompatibility.gx.major)) {
|
|
699
|
+
unsupportedCompatibilityMajor = `GeneXus SDK major ${kbSdkCompatibility.gx.major}`;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (unsupportedCompatibilityMajor) {
|
|
703
|
+
compatibilityStatus = 'fail';
|
|
704
|
+
compatibilityDetail = `${unsupportedCompatibilityMajor} is not supported by this MCP distribution. Supported majors: ${catalog.supportedMajors.map((entry) => entry.major).join(', ')}.`;
|
|
705
|
+
} else if (kbSdkCompatibility.status === 'mismatch') {
|
|
706
|
+
compatibilityStatus = 'fail';
|
|
707
|
+
compatibilityDetail = `KB major ${kbSdkCompatibility.kb.major} does not match GeneXus SDK major ${kbSdkCompatibility.gx.major}. Re-run init with the matching --gx path.`;
|
|
708
|
+
} else if (kbSdkCompatibility.status === 'match') {
|
|
709
|
+
compatibilityStatus = 'pass';
|
|
710
|
+
compatibilityDetail = `KB major ${kbSdkCompatibility.kb.major} matches GeneXus SDK major ${kbSdkCompatibility.gx.major}.`;
|
|
711
|
+
} else {
|
|
712
|
+
compatibilityDetail = `KB/SDK major compatibility could not be verified (KB: ${kbSdkCompatibility.kb.source}; SDK: ${kbSdkCompatibility.gx.source}).`;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
677
715
|
|
|
678
716
|
const riskyZone = isPathLikelyAppLockerBlocked(gatewayExePath);
|
|
679
717
|
const clientCrossCheck = buildClientExeCrossCheck(gatewayExePath);
|
|
@@ -703,6 +741,11 @@ async function handleDoctor(options, ctx) {
|
|
|
703
741
|
// guarantees a worker crash on first MCP call. Promote from warn to fail so init
|
|
704
742
|
// exits non-zero and the caller (install.ps1, AI client) actually sees the problem.
|
|
705
743
|
{ id: 'gx_installation', status: gxExeExists ? 'pass' : (gxPath ? 'fail' : 'warn'), detail: gxExeExists ? 'GeneXus installation has genexus.exe.' : (gxPath ? `Configured GeneXus installation is missing genexus.exe at: ${gxPath}` : 'No GeneXus installation path is configured.') },
|
|
744
|
+
{
|
|
745
|
+
id: 'kb_sdk_compatibility',
|
|
746
|
+
status: compatibilityStatus,
|
|
747
|
+
detail: compatibilityDetail
|
|
748
|
+
},
|
|
706
749
|
{ id: 'tool_definitions', status: toolDefsExists ? 'pass' : 'warn', detail: toolDefsExists ? `Tool definition file found (${toolCount} tools) at ${toolDefPath}.` : (process.env.GENEXUS_MCP_TOOL_DEFINITIONS ? `tool_definitions.json missing at GENEXUS_MCP_TOOL_DEFINITIONS=${toolDefPath}. Unset the env var or point it at a valid file.` : `tool_definitions.json missing. Expected at ${toolDefPath} (next to the gateway exe). The csproj should copy it on publish — reinstall via scripts/install.ps1, or set GENEXUS_MCP_TOOL_DEFINITIONS to override.`) },
|
|
707
750
|
{ 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.' },
|
|
708
751
|
{ id: 'client_config_sync', status: clientCrossCheck.status, detail: clientCrossCheck.detail }
|
|
@@ -1188,27 +1231,60 @@ function buildInteractiveInitHelp(patchResult) {
|
|
|
1188
1231
|
}
|
|
1189
1232
|
|
|
1190
1233
|
async function runInteractiveInit(ctx) {
|
|
1191
|
-
const
|
|
1234
|
+
const catalog = getGeneXusVersionCatalog();
|
|
1235
|
+
const primary = catalog.supportedMajors.find((entry) => String(entry.major) === String(catalog.primaryMajor));
|
|
1192
1236
|
|
|
1193
1237
|
if (!ctx.options.quiet) {
|
|
1194
1238
|
ctx.stderr.write('GeneXus MCP setup wizard\n\n');
|
|
1195
1239
|
}
|
|
1196
1240
|
|
|
1197
|
-
const rl = readline.createInterface({ input: process.stdin, output: ctx.stderr });
|
|
1241
|
+
const rl = readline.createInterface({ input: ctx.input || process.stdin, output: ctx.stderr });
|
|
1198
1242
|
const question = (text) => new Promise((resolve) => rl.question(text, (answer) => resolve(answer)));
|
|
1199
1243
|
|
|
1200
1244
|
try {
|
|
1201
1245
|
const kbAnswer = await question(`1) Knowledge Base folder path (default: ${ctx.cwd}):\n> `);
|
|
1202
1246
|
const finalKb = String(kbAnswer || '').trim() || ctx.cwd;
|
|
1203
1247
|
|
|
1248
|
+
if (!fs.existsSync(finalKb)) {
|
|
1249
|
+
return {
|
|
1250
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1251
|
+
envelope: operationalErrorEnvelope(
|
|
1252
|
+
`KB path does not exist on disk. Aborted before writing config.`,
|
|
1253
|
+
ctx.EXIT_CODES.ERROR,
|
|
1254
|
+
[`Path checked: ${finalKb}`, 'Create the KB in GeneXus first, then re-run init.']
|
|
1255
|
+
)
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
const kbIdentity = readGeneXusKbIdentity(finalKb);
|
|
1260
|
+
const defaultGx = discoverGeneXusInstallation(kbIdentity.major)
|
|
1261
|
+
|| primary?.defaultInstallPath
|
|
1262
|
+
|| catalog.supportedMajors[0]?.defaultInstallPath
|
|
1263
|
+
|| '';
|
|
1204
1264
|
const gxAnswer = await question(`\n2) GeneXus installation path (default: ${defaultGx}):\n> `);
|
|
1205
|
-
const
|
|
1265
|
+
const explicitGx = String(gxAnswer || '').trim();
|
|
1266
|
+
const finalGx = explicitGx || defaultGx;
|
|
1267
|
+
|
|
1268
|
+
if (!kbIdentity.major && !explicitGx) {
|
|
1269
|
+
return {
|
|
1270
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1271
|
+
envelope: operationalErrorEnvelope(
|
|
1272
|
+
'Cannot safely use the default GeneXus SDK because the KB major could not be determined.',
|
|
1273
|
+
ctx.EXIT_CODES.ERROR,
|
|
1274
|
+
[
|
|
1275
|
+
`KB detection reason: ${kbIdentity.reason || 'unknown'}`,
|
|
1276
|
+
'Enter the installation path explicitly, or open the KB once in GeneXus so its .gxw metadata is initialized.'
|
|
1277
|
+
],
|
|
1278
|
+
'sdk_selection_required'
|
|
1279
|
+
)
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1206
1282
|
|
|
1207
1283
|
if (!fs.existsSync(path.join(finalGx, 'genexus.exe'))) {
|
|
1208
|
-
const suggested = discoverGeneXusInstallation();
|
|
1284
|
+
const suggested = discoverGeneXusInstallation(kbIdentity.major);
|
|
1209
1285
|
const help = [`Path checked: ${finalGx}`];
|
|
1210
1286
|
if (suggested && suggested.toLowerCase() !== finalGx.toLowerCase()) {
|
|
1211
|
-
help.push(`Detected a
|
|
1287
|
+
help.push(`Detected a matching GeneXus${kbIdentity.major ? ` ${kbIdentity.major}` : ''} install at: ${suggested}`);
|
|
1212
1288
|
help.push('Re-run `genexus-mcp init --interactive` and accept the detected path, or pass --gx explicitly.');
|
|
1213
1289
|
}
|
|
1214
1290
|
return {
|
|
@@ -1221,13 +1297,57 @@ async function runInteractiveInit(ctx) {
|
|
|
1221
1297
|
};
|
|
1222
1298
|
}
|
|
1223
1299
|
|
|
1224
|
-
|
|
1300
|
+
const compatibility = compareGeneXusKbAndInstallation(finalKb, finalGx);
|
|
1301
|
+
const supportedGxMajor = isSupportedCatalogMajor(catalog, compatibility.gx.major);
|
|
1302
|
+
if (!supportedGxMajor) {
|
|
1225
1303
|
return {
|
|
1226
1304
|
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1227
1305
|
envelope: operationalErrorEnvelope(
|
|
1228
|
-
`
|
|
1306
|
+
`GeneXus SDK major ${compatibility.gx.major} is not supported by this MCP distribution.`,
|
|
1229
1307
|
ctx.EXIT_CODES.ERROR,
|
|
1230
|
-
[`
|
|
1308
|
+
[`Supported majors: ${catalog.supportedMajors.map((entry) => entry.major).join(', ')}`],
|
|
1309
|
+
'sdk_unsupported'
|
|
1310
|
+
)
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
const supportedKbMajor = isSupportedCatalogMajor(catalog, compatibility.kb.major);
|
|
1314
|
+
if (!supportedKbMajor) {
|
|
1315
|
+
return {
|
|
1316
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1317
|
+
envelope: operationalErrorEnvelope(
|
|
1318
|
+
`KB major ${compatibility.kb.major} is not supported by this MCP distribution.`,
|
|
1319
|
+
ctx.EXIT_CODES.ERROR,
|
|
1320
|
+
[`Supported majors: ${catalog.supportedMajors.map((entry) => entry.major).join(', ')}`],
|
|
1321
|
+
'sdk_kb_unsupported'
|
|
1322
|
+
)
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
if (compatibility.status === 'mismatch') {
|
|
1326
|
+
return {
|
|
1327
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1328
|
+
envelope: operationalErrorEnvelope(
|
|
1329
|
+
`GeneXus SDK major ${compatibility.gx.major} does not match KB major ${compatibility.kb.major}. Aborted before writing config.`,
|
|
1330
|
+
ctx.EXIT_CODES.ERROR,
|
|
1331
|
+
[
|
|
1332
|
+
`KB version: ${compatibility.kb.version || 'unknown'}`,
|
|
1333
|
+
`SDK version: ${compatibility.gx.version || 'unknown'}`,
|
|
1334
|
+
'Choose the GeneXus installation that matches the KB major, or pass --gx explicitly.',
|
|
1335
|
+
],
|
|
1336
|
+
'sdk_kb_mismatch'
|
|
1337
|
+
)
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
if (compatibility.kb.major && !compatibility.gx.major) {
|
|
1341
|
+
return {
|
|
1342
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1343
|
+
envelope: operationalErrorEnvelope(
|
|
1344
|
+
`The selected GeneXus installation version could not be verified against KB major ${compatibility.kb.major}.`,
|
|
1345
|
+
ctx.EXIT_CODES.ERROR,
|
|
1346
|
+
[
|
|
1347
|
+
`Path checked: ${finalGx}`,
|
|
1348
|
+
'Use a standard GeneXus installation containing GeneXus.exe metadata, or provide a verified matching SDK path.'
|
|
1349
|
+
],
|
|
1350
|
+
'sdk_identity_unresolved'
|
|
1231
1351
|
)
|
|
1232
1352
|
};
|
|
1233
1353
|
}
|
|
@@ -1333,8 +1453,12 @@ async function handleInit(options, ctx) {
|
|
|
1333
1453
|
}
|
|
1334
1454
|
}
|
|
1335
1455
|
|
|
1456
|
+
const discoveredKbIdentity = resolution.kb.value
|
|
1457
|
+
? readGeneXusKbIdentity(resolution.kb.value)
|
|
1458
|
+
: { version: null, major: null, source: 'unavailable', reason: 'missing-kb-path' };
|
|
1459
|
+
|
|
1336
1460
|
if (!resolution.gx.value) {
|
|
1337
|
-
const fromDisco = discoverGeneXusInstallation();
|
|
1461
|
+
const fromDisco = discoverGeneXusInstallation(discoveredKbIdentity.major);
|
|
1338
1462
|
if (fromDisco) {
|
|
1339
1463
|
resolution.gx.value = fromDisco;
|
|
1340
1464
|
resolution.gx.source = 'auto-discovery';
|
|
@@ -1379,12 +1503,14 @@ async function handleInit(options, ctx) {
|
|
|
1379
1503
|
`Path checked: ${resolution.gx.value}`,
|
|
1380
1504
|
`Source: --${resolution.gx.source === 'flag' ? 'gx flag' : resolution.gx.source}`
|
|
1381
1505
|
];
|
|
1382
|
-
const suggested = resolution.gx.source === 'flag'
|
|
1506
|
+
const suggested = resolution.gx.source === 'flag'
|
|
1507
|
+
? discoverGeneXusInstallation(discoveredKbIdentity.major)
|
|
1508
|
+
: null;
|
|
1383
1509
|
if (suggested && suggested.toLowerCase() !== resolution.gx.value.toLowerCase()) {
|
|
1384
1510
|
help.push(`Detected a working GeneXus install at: ${suggested}`);
|
|
1385
1511
|
help.push(`Re-run: genexus-mcp init --kb "${resolution.kb.value}" --gx "${suggested}"`);
|
|
1386
1512
|
} else {
|
|
1387
|
-
help.push('Omit --gx to let init auto-discover via registry / Program Files
|
|
1513
|
+
help.push('Omit --gx to let init auto-discover a catalog-listed installation via registry / Program Files.');
|
|
1388
1514
|
}
|
|
1389
1515
|
return {
|
|
1390
1516
|
exitCode: ctx.EXIT_CODES.ERROR,
|
|
@@ -1410,6 +1536,77 @@ async function handleInit(options, ctx) {
|
|
|
1410
1536
|
};
|
|
1411
1537
|
}
|
|
1412
1538
|
|
|
1539
|
+
const compatibility = compareGeneXusKbAndInstallation(resolution.kb.value, resolution.gx.value);
|
|
1540
|
+
const catalog = getGeneXusVersionCatalog();
|
|
1541
|
+
const supportedGxMajor = isSupportedCatalogMajor(catalog, compatibility.gx.major);
|
|
1542
|
+
const supportedKbMajor = isSupportedCatalogMajor(catalog, compatibility.kb.major);
|
|
1543
|
+
if (!supportedGxMajor) {
|
|
1544
|
+
return {
|
|
1545
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1546
|
+
envelope: operationalErrorEnvelope(
|
|
1547
|
+
`GeneXus SDK major ${compatibility.gx.major} is not supported by this MCP distribution.`,
|
|
1548
|
+
ctx.EXIT_CODES.ERROR,
|
|
1549
|
+
[`Supported majors: ${catalog.supportedMajors.map((entry) => entry.major).join(', ')}`],
|
|
1550
|
+
'sdk_unsupported'
|
|
1551
|
+
)
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
if (!supportedKbMajor) {
|
|
1555
|
+
return {
|
|
1556
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1557
|
+
envelope: operationalErrorEnvelope(
|
|
1558
|
+
`KB major ${compatibility.kb.major} is not supported by this MCP distribution.`,
|
|
1559
|
+
ctx.EXIT_CODES.ERROR,
|
|
1560
|
+
[`Supported majors: ${catalog.supportedMajors.map((entry) => entry.major).join(', ')}`],
|
|
1561
|
+
'sdk_kb_unsupported'
|
|
1562
|
+
)
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
if (compatibility.status === 'mismatch') {
|
|
1566
|
+
return {
|
|
1567
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1568
|
+
envelope: operationalErrorEnvelope(
|
|
1569
|
+
`GeneXus SDK major ${compatibility.gx.major} does not match KB major ${compatibility.kb.major}. Init aborted before writing config.`,
|
|
1570
|
+
ctx.EXIT_CODES.ERROR,
|
|
1571
|
+
[
|
|
1572
|
+
`KB version: ${compatibility.kb.version || 'unknown'}`,
|
|
1573
|
+
`SDK version: ${compatibility.gx.version || 'unknown'}`,
|
|
1574
|
+
`Path checked: ${resolution.gx.value}`,
|
|
1575
|
+
'Choose the GeneXus installation that matches the KB major, or pass --gx explicitly.'
|
|
1576
|
+
],
|
|
1577
|
+
'sdk_kb_mismatch'
|
|
1578
|
+
)
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
if (resolution.gx.source === 'auto-discovery' && !compatibility.kb.major) {
|
|
1582
|
+
return {
|
|
1583
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1584
|
+
envelope: operationalErrorEnvelope(
|
|
1585
|
+
'Cannot safely auto-select a GeneXus SDK because the KB major could not be determined.',
|
|
1586
|
+
ctx.EXIT_CODES.ERROR,
|
|
1587
|
+
[
|
|
1588
|
+
`KB detection reason: ${compatibility.kb.reason || 'unknown'}`,
|
|
1589
|
+
'Pass --gx explicitly with the installation that created this KB.'
|
|
1590
|
+
],
|
|
1591
|
+
'sdk_selection_required'
|
|
1592
|
+
)
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
if (compatibility.kb.major && !compatibility.gx.major) {
|
|
1596
|
+
return {
|
|
1597
|
+
exitCode: ctx.EXIT_CODES.ERROR,
|
|
1598
|
+
envelope: operationalErrorEnvelope(
|
|
1599
|
+
`The selected GeneXus installation version could not be verified against KB major ${compatibility.kb.major}.`,
|
|
1600
|
+
ctx.EXIT_CODES.ERROR,
|
|
1601
|
+
[
|
|
1602
|
+
`Path checked: ${resolution.gx.value}`,
|
|
1603
|
+
'Use a standard GeneXus installation containing GeneXus.exe metadata, or provide a verified matching SDK path.'
|
|
1604
|
+
],
|
|
1605
|
+
'sdk_identity_unresolved'
|
|
1606
|
+
)
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1413
1610
|
try {
|
|
1414
1611
|
const created = createConfigFile(resolution.kb.value, resolution.gx.value);
|
|
1415
1612
|
const kbName = path.basename(resolution.kb.value);
|
|
@@ -1492,8 +1689,19 @@ async function handleInit(options, ctx) {
|
|
|
1492
1689
|
noOp: !created.changed,
|
|
1493
1690
|
clientsPatchedCount: patchResult.patched.length,
|
|
1494
1691
|
resolved: {
|
|
1495
|
-
kb: {
|
|
1496
|
-
|
|
1692
|
+
kb: {
|
|
1693
|
+
path: resolution.kb.value,
|
|
1694
|
+
source: resolution.kb.source,
|
|
1695
|
+
major: compatibility.kb.major,
|
|
1696
|
+
version: compatibility.kb.version
|
|
1697
|
+
},
|
|
1698
|
+
gx: {
|
|
1699
|
+
path: resolution.gx.value,
|
|
1700
|
+
source: resolution.gx.source,
|
|
1701
|
+
major: compatibility.gx.major,
|
|
1702
|
+
version: compatibility.gx.version,
|
|
1703
|
+
detectionSource: compatibility.gx.source
|
|
1704
|
+
}
|
|
1497
1705
|
},
|
|
1498
1706
|
verification: {
|
|
1499
1707
|
summary: verification.summary,
|
|
@@ -1661,7 +1869,7 @@ async function handleWhoami(options, ctx) {
|
|
|
1661
1869
|
const kbName = kbPath ? path.basename(kbPath) : null;
|
|
1662
1870
|
const kbExists = !!(kbPath && fs.existsSync(kbPath));
|
|
1663
1871
|
const kbValid = data.kbLooksValid;
|
|
1664
|
-
const
|
|
1872
|
+
const gxIdentity = readGeneXusInstallationIdentity(gxPath);
|
|
1665
1873
|
|
|
1666
1874
|
const ok = {
|
|
1667
1875
|
connected: true,
|
|
@@ -1673,7 +1881,9 @@ async function handleWhoami(options, ctx) {
|
|
|
1673
1881
|
},
|
|
1674
1882
|
geneXus: {
|
|
1675
1883
|
installationPath: gxPath,
|
|
1676
|
-
version:
|
|
1884
|
+
version: gxIdentity.version,
|
|
1885
|
+
major: gxIdentity.major,
|
|
1886
|
+
detectionSource: gxIdentity.source
|
|
1677
1887
|
},
|
|
1678
1888
|
config: {
|
|
1679
1889
|
path: data.configPath,
|
|
@@ -1684,7 +1894,9 @@ async function handleWhoami(options, ctx) {
|
|
|
1684
1894
|
const help = [];
|
|
1685
1895
|
if (!kbExists) help.push('Configured KB path does not exist on disk.');
|
|
1686
1896
|
if (kbExists && !kbValid) help.push('KB path exists but does not look like a GeneXus KB (no `.gxw` or `KnowledgeBase.Connection`).');
|
|
1687
|
-
if (!
|
|
1897
|
+
if (!gxIdentity.version && !gxIdentity.major) {
|
|
1898
|
+
help.push('Could not determine the GeneXus major from version metadata or the installation path.');
|
|
1899
|
+
}
|
|
1688
1900
|
|
|
1689
1901
|
return { exitCode: ctx.EXIT_CODES.OK, envelope: { ok, help } };
|
|
1690
1902
|
}
|