genexus-mcp 3.5.3 → 3.6.1

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 CHANGED
@@ -12,19 +12,21 @@
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 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.
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 native SDK or legacy compatibility driver. Native SDK paths work with the **real GeneXus SDK** and legacy paths use explicit reflection/COM adapters; neither path relies on a parsed copy of the KB.
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
21
+ ## Multi-version GeneXus support
22
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:
23
+ The same MCP distribution supports the official native SDK majors listed in the
24
+ generated compatibility document. It also includes basic, best-effort
25
+ compatibility for the legacy versions listed there through separate drivers;
26
+ that path is not equivalent to full native-SDK support. Each configured MCP
27
+ process selects one installed SDK or legacy driver with `--gx`; no separate MCP
28
+ installation is required. The commands below are examples of switching the
29
+ existing configuration, not running two majors in the same process:
28
30
 
29
31
  ```bash
30
32
  npx genexus-mcp@latest init --kb "C:\KBs\KBTeste17" --gx "C:\Program Files (x86)\GeneXus\GeneXus17Trial"
@@ -45,7 +47,9 @@ that checks every supported major against one published artifact.
45
47
 
46
48
  The Gateway reports the detected SDK through `genexus_whoami`:
47
49
 
48
- - `geneXus.supportedMajors`: explicitly validated SDK majors from the version catalog
50
+ - `geneXus.supportedMajors`: explicitly validated native SDK majors from the version catalog
51
+ - `geneXus.legacyMajors`: catalogued legacy majors handled by their compatibility drivers
52
+ - `geneXus.sdkCompatibility.supportLevel`: `native-sdk` or `basic-legacy` for the detected installation
49
53
  - `geneXus.matchedMajor`: the major detected for the configured installation
50
54
  - `geneXus.versionMatches`: whether the detected installation is in that catalog
51
55
  - `geneXus.supportedMajor`: retained as the legacy single-major alias for the catalog primary
@@ -57,15 +61,28 @@ needed. Existing tool names, arguments, and MCP client configuration formats do
57
61
  not change.
58
62
 
59
63
  <!-- BEGIN GENERATED: gx-compatibility -->
60
- Supported SDK majors: **GeneXus 17, GeneXus 18**.
64
+ Supported SDK majors: **GeneXus 16, GeneXus 17, GeneXus 18** (native SDK).
65
+ Basic legacy compatibility: **GeneXus Evolution 3, GeneXus Evolution 2, GeneXus Evolution 1, GeneXus 15, GeneXus 9.0, GeneXus 8.0** via `com-gxpublic` and `dotnet-reflection` (not the native SDK build).
61
66
  Primary SDK: **GeneXus 18**.
62
67
  Source of truth: `config/gx-versions.json`.
63
68
  <!-- END GENERATED: gx-compatibility -->
64
69
 
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.
70
+ To add another native-SDK major in the future, add it to the explicit
71
+ `supportedMajors` catalog only after compiling the Worker with that SDK and
72
+ passing the focused tests plus a live KB smoke. This prevents the server from
73
+ claiming native-SDK compatibility based only on a version string.
74
+
75
+ ### Basic legacy compatibility (not native SDK support)
76
+
77
+ Every legacy version currently declared in `legacyMajors` uses a best-effort
78
+ driver rather than the native SDK build:
79
+ - **GeneXus Evolution 1 (10.1), Evolution 2 (10.2), Evolution 3 (10.3), and GeneXus 15**: Driven via runtime reflection (`dotnet-reflection`), dynamically adapting to missing types or structural differences (such as module-less KBs without `QualifiedName`).
80
+ - **GeneXus 8.0 and GeneXus 9.0**: Driven through the classic GXPublic surface (`com-gxpublic`), detected from `gx.exe`/`gxdl32.dll` and classic `.gxi` Knowledge Bases. GXPublic is a metadata-oriented OLE DB/COM surface; this path is intentionally limited to basic metadata/core operations and does not claim native-SDK source/edit parity.
81
+ - **Graceful degradation**: Modern tools that require features introduced in newer GeneXus versions (such as `genexus_api`, `genexus_gam`, or `genexus_module`) return structured `UNSUPPORTED_IN_GENEXUS_VERSION` errors indicating the required minimum version rather than failing ungracefully.
82
+
83
+ This legacy path is intended for basic core operations where implemented; it
84
+ does not claim the same feature parity as the native SDK contract for GeneXus
85
+ 16, 17, and 18.
69
86
 
70
87
  ---
71
88
 
@@ -86,7 +103,10 @@ A quick map of what the agent can do against your real KB through the **50 tools
86
103
  | 🌿 **Versioning, transfer & teams** | KB model versions/branches, **real XPZ export/import** (dependency-aware), GXserver (Team Development) sync + **CI pipelines**, git-style history, multi-KB parallel work |
87
104
  | 🔐 **Security** | GAM / integrated-security provisioning, KB security audit + native Security Scanner |
88
105
 
89
- It works through the **native GeneXus SDK** — the same code paths the IDE uses — so edits are real and validated, not text hacks on KB files.
106
+ Native SDK support works through the **native GeneXus SDK** — the same code paths
107
+ the IDE uses — so edits are real and validated, not text hacks on KB files.
108
+ Legacy support uses the reflection or COM driver listed in the catalog and
109
+ degrades unsupported modern tools explicitly.
90
110
 
91
111
  ---
92
112
 
@@ -95,9 +115,9 @@ It works through the **native GeneXus SDK** — the same code paths the IDE uses
95
115
  Before you start, make sure you have:
96
116
 
97
117
  - ✅ **Windows** (GeneXus is Windows-only)
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
- - ✅ **GeneXus 18** installed locally (the primary supported SDK; other catalogued majors are also supported)
100
- - ✅ **A Knowledge Base created with a supported GeneXus major** and opened at least once in the IDE (so it's initialized)
118
+ - ✅ **A supported GeneXus installation** installed locally: GeneXus 16, 17, or 18 for native SDK support, or a catalogued legacy installation for basic compatibility (see [`docs/generated/supported-versions.md`](docs/generated/supported-versions.md); pass another install path explicitly when needed)
119
+ - ✅ **GeneXus 18** installed locally for the primary native-SDK path; other catalogued native and legacy versions are also supported according to their listed driver
120
+ - ✅ **A Knowledge Base created with a supported native or legacy GeneXus major** and opened at least once in the IDE (so it's initialized)
101
121
  - ✅ **Node.js 22+** — check with `node --version` in a terminal; install from [nodejs.org](https://nodejs.org/) if missing
102
122
  - ✅ **An MCP-compatible AI client** — [Claude Desktop](https://claude.ai/download), [Claude Code](https://claude.com/claude-code), Cursor, Antigravity, etc.
103
123
 
@@ -59,7 +59,9 @@ function validateClientIds(ids) {
59
59
 
60
60
  function isSupportedCatalogMajor(catalog, major) {
61
61
  if (!major) return true;
62
- return catalog.supportedMajors.some((entry) => String(entry.major) === String(major));
62
+ const inSupported = catalog.supportedMajors && catalog.supportedMajors.some((entry) => String(entry.major) === String(major));
63
+ if (inSupported) return true;
64
+ return Array.isArray(catalog.legacyMajors) && catalog.legacyMajors.some((entry) => String(entry.major) === String(major));
63
65
  }
64
66
 
65
67
  function parseFieldSelection(raw) {
@@ -176,6 +178,37 @@ function buildStatusData(cwd) {
176
178
  return { ready, configFound, gatewayExeFound, kbLooksValid, configPath, gatewayExePath, kbPath, gxPath, configSource };
177
179
  }
178
180
 
181
+ const PROBE_EXIT_GRACE_MS = 2000;
182
+
183
+ // Stop a probe child and wait for it to actually exit. On Windows child.kill()
184
+ // only signals the direct process and returns before the OS releases the
185
+ // executable image, so reporting success right after kill() left the exe file
186
+ // handle open (Issue #211: the shared gateway stub in cli/run.test.js made the
187
+ // suite teardown fail with EPERM while every assertion passed). Resolves true
188
+ // once the child exited, false when it did not within the bounded grace.
189
+ function stopProbeChild(child, graceMs = PROBE_EXIT_GRACE_MS) {
190
+ return new Promise((resolve) => {
191
+ if (!child || child.exitCode !== null || child.signalCode !== null) {
192
+ resolve(true);
193
+ return;
194
+ }
195
+ let settled = false;
196
+ const finish = (exited) => {
197
+ if (settled) return;
198
+ settled = true;
199
+ clearTimeout(timer);
200
+ resolve(exited);
201
+ };
202
+ const timer = setTimeout(() => finish(false), graceMs);
203
+ child.once('exit', () => finish(true));
204
+ try {
205
+ child.kill();
206
+ } catch {
207
+ finish(true);
208
+ }
209
+ });
210
+ }
211
+
179
212
  async function spawnGatewayProbe({ env = process.env, spawnHoldMs, timeoutMs, label, successDetail }) {
180
213
  const gatewayExePath = getGatewayExePath();
181
214
 
@@ -209,17 +242,25 @@ async function spawnGatewayProbe({ env = process.env, spawnHoldMs, timeoutMs, la
209
242
  });
210
243
 
211
244
  child.once('spawn', () => {
212
- setTimeout(() => {
213
- try { child.kill(); } catch { }
214
- finish({ status: 'pass', detail: successDetail });
245
+ setTimeout(async () => {
246
+ const exited = await stopProbeChild(child);
247
+ if (exited) {
248
+ finish({ status: 'pass', detail: successDetail });
249
+ } else {
250
+ finish({ status: 'warn', detail: `${label}: process did not exit after the stop signal; it may still hold the gateway exe.` });
251
+ }
215
252
  }, spawnHoldMs);
216
253
  });
217
254
 
218
- setTimeout(() => {
219
- if (!done) {
220
- try { child.kill(); } catch { }
221
- finish({ status: 'warn', detail: `${label} timed out; process was force-stopped.` });
222
- }
255
+ setTimeout(async () => {
256
+ if (done) return;
257
+ const exited = await stopProbeChild(child);
258
+ finish({
259
+ status: 'warn',
260
+ detail: exited
261
+ ? `${label} timed out; process was force-stopped.`
262
+ : `${label} timed out and did not exit after the stop signal; it may still hold the gateway exe.`
263
+ });
223
264
  }, timeoutMs);
224
265
  } catch (err) {
225
266
  finish({ status: 'fail', detail: `${label} threw: ${err.message}` });
@@ -562,6 +603,50 @@ function buildInProcessBuildAssemblyLoadCheck(gxPath) {
562
603
  };
563
604
  }
564
605
 
606
+ function buildGxPublicComCheck(gxMajor, gxPath) {
607
+ if (gxMajor !== '8' && gxMajor !== '9') {
608
+ return { id: 'gxpublic_com_registration', status: 'not_applicable', detail: 'Not a GeneXus 8.0/9.0 installation.' };
609
+ }
610
+ const child_process = require('child_process');
611
+ let isRegistered = false;
612
+ try {
613
+ const res = child_process.spawnSync('reg', ['query', 'HKCR\\GXPublic.GXPublic'], { encoding: 'utf8', timeout: 2000 });
614
+ isRegistered = res.status === 0;
615
+ } catch { }
616
+
617
+ if (isRegistered) {
618
+ return { id: 'gxpublic_com_registration', status: 'pass', detail: 'GXPublic COM automation server (GXPublic.GXPublic) is registered.' };
619
+ }
620
+ const dllHint = gxPath ? path.join(gxPath, 'GxPublic.dll') : 'GxPublic.dll';
621
+ return {
622
+ id: 'gxpublic_com_registration',
623
+ status: 'warn',
624
+ detail: `GXPublic COM component is not registered. Run 'regsvr32 "${dllHint}"' to enable GeneXus ${gxMajor}.0 COM automation.`
625
+ };
626
+ }
627
+
628
+ function buildLegacyIdeLockCheck(gxMajor) {
629
+ if (gxMajor !== '8' && gxMajor !== '9' && gxMajor !== '10.1' && gxMajor !== '10.2') {
630
+ return { id: 'legacy_ide_lock', status: 'not_applicable', detail: 'Lock check not applicable for modern GeneXus versions.' };
631
+ }
632
+ const child_process = require('child_process');
633
+ let ideRunning = false;
634
+ try {
635
+ const res = child_process.spawnSync('tasklist', ['/FI', 'IMAGENAME eq gx.exe', '/NH'], { encoding: 'utf8', timeout: 2000 });
636
+ if (res.stdout && res.stdout.toLowerCase().includes('gx.exe')) {
637
+ ideRunning = true;
638
+ }
639
+ } catch { }
640
+ if (ideRunning) {
641
+ return {
642
+ id: 'legacy_ide_lock',
643
+ status: 'warn',
644
+ detail: 'GeneXus IDE (gx.exe) process is currently running. Legacy engines hold exclusive file locks on the KB.'
645
+ };
646
+ }
647
+ return { id: 'legacy_ide_lock', status: 'pass', detail: 'No conflicting GeneXus IDE process (gx.exe) detected.' };
648
+ }
649
+
565
650
  function redactConfig(cfg) {
566
651
  // Replace absolute paths with `<redacted:hash8>` so the structure is preserved
567
652
  // but filesystem layout, usernames, and KB names are not leaked. Hash is stable
@@ -710,7 +795,7 @@ async function handleDoctor(options, ctx) {
710
795
  const kbPath = data.kbPath;
711
796
  const gxPath = data.gxPath;
712
797
  const kbExists = !!(kbPath && fs.existsSync(kbPath));
713
- const gxExeExists = !!(gxPath && fs.existsSync(path.join(gxPath, 'genexus.exe')));
798
+ const gxExeExists = !!(gxPath && (fs.existsSync(path.join(gxPath, 'genexus.exe')) || fs.existsSync(path.join(gxPath, 'gx.exe'))));
714
799
  const kbSdkCompatibility = kbExists && gxExeExists
715
800
  ? compareGeneXusKbAndInstallation(kbPath, gxPath)
716
801
  : null;
@@ -731,15 +816,18 @@ async function handleDoctor(options, ctx) {
731
816
  compatibilityDetail = `${unsupportedCompatibilityMajor} is not supported by this MCP distribution. Supported majors: ${catalog.supportedMajors.map((entry) => entry.major).join(', ')}.`;
732
817
  } else if (kbSdkCompatibility.status === 'mismatch') {
733
818
  compatibilityStatus = 'fail';
734
- compatibilityDetail = `KB major ${kbSdkCompatibility.kb.major} does not match GeneXus SDK major ${kbSdkCompatibility.gx.major}. Re-run init with the matching --gx path.`;
819
+ compatibilityDetail = `KB major ${kbSdkCompatibility.kb.major} does not match GeneXus SDK major ${kbSdkCompatibility.gx.major}${kbSdkCompatibility.gx.version ? ` (${kbSdkCompatibility.gx.version})` : ''}. Re-run init with the matching --gx path.`;
735
820
  } else if (kbSdkCompatibility.status === 'match') {
736
821
  compatibilityStatus = 'pass';
737
- compatibilityDetail = `KB major ${kbSdkCompatibility.kb.major} matches GeneXus SDK major ${kbSdkCompatibility.gx.major}.`;
822
+ compatibilityDetail = `KB major ${kbSdkCompatibility.kb.major} matches GeneXus SDK major ${kbSdkCompatibility.gx.major}${kbSdkCompatibility.gx.version ? ` (${kbSdkCompatibility.gx.version})` : ''}.`;
738
823
  } else {
739
824
  compatibilityDetail = `KB/SDK major compatibility could not be verified (KB: ${kbSdkCompatibility.kb.source}; SDK: ${kbSdkCompatibility.gx.source}).`;
740
825
  }
741
826
  }
742
827
 
828
+ const gxIdent = gxPath ? readGeneXusInstallationIdentity(gxPath) : null;
829
+ const gxVersionLabel = gxIdent && gxIdent.version ? ` (GeneXus ${gxIdent.major || ''} version ${gxIdent.version})` : (gxIdent && gxIdent.major ? ` (GeneXus ${gxIdent.major})` : '');
830
+
743
831
  const riskyZone = isPathLikelyAppLockerBlocked(gatewayExePath);
744
832
  const clientCrossCheck = buildClientExeCrossCheck(gatewayExePath);
745
833
 
@@ -767,7 +855,7 @@ async function handleDoctor(options, ctx) {
767
855
  // Same logic for the GeneXus install: missing genexus.exe at a configured path
768
856
  // guarantees a worker crash on first MCP call. Promote from warn to fail so init
769
857
  // exits non-zero and the caller (install.ps1, AI client) actually sees the problem.
770
- { 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.') },
858
+ { id: 'gx_installation', status: gxExeExists ? 'pass' : (gxPath ? 'fail' : 'warn'), detail: gxExeExists ? `GeneXus installation has ${gxPath && fs.existsSync(path.join(gxPath, 'gx.exe')) && !fs.existsSync(path.join(gxPath, 'genexus.exe')) ? 'gx.exe' : 'genexus.exe'}${gxVersionLabel}.` : (gxPath ? `Configured GeneXus installation is missing executable at: ${gxPath}` : 'No GeneXus installation path is configured.') },
771
859
  {
772
860
  id: 'kb_sdk_compatibility',
773
861
  status: compatibilityStatus,
@@ -784,6 +872,13 @@ async function handleDoctor(options, ctx) {
784
872
  const inProcessLoad = buildInProcessBuildAssemblyLoadCheck(gxPath);
785
873
  checks.push({ id: 'in_process_build_assembly_load', status: inProcessLoad.status, detail: inProcessLoad.detail });
786
874
 
875
+ // Legacy GeneXus checks (GX 8.0, 9.0, Ev1, Ev2)
876
+ const gxMajor = (gxIdent && gxIdent.major) || (kbSdkCompatibility && kbSdkCompatibility.gx && kbSdkCompatibility.gx.major) || null;
877
+ const comCheck = buildGxPublicComCheck(gxMajor, gxPath);
878
+ checks.push({ id: 'gxpublic_com_registration', status: comCheck.status, detail: comCheck.detail });
879
+ const ideLockCheck = buildLegacyIdeLockCheck(gxMajor);
880
+ checks.push({ id: 'legacy_ide_lock', status: ideLockCheck.status, detail: ideLockCheck.detail });
881
+
787
882
  // Client registration summary — one line answering "are my AI agents wired up?".
788
883
  const clientRows = clientsStatus();
789
884
  const installedRows = clientRows.filter((r) => r.installed);
@@ -1344,6 +1439,7 @@ function buildClientLauncherHelp(patchResult) {
1344
1439
  }
1345
1440
  if (patched.length > 0 && process.platform === 'win32' && !process.env.GENEXUS_MCP_GATEWAY_EXE) {
1346
1441
  help.push('Windows launcher paths may resolve under the npm cache and be blocked by AppLocker/SRP. Use scripts/install.ps1 for a stable whitelisted path.');
1442
+ help.push('Working from a local checkout? Register the clients with the checkout gateway from the repo root instead: `$env:GENEXUS_MCP_GATEWAY_EXE="<repoRoot>\\publish\\GxMcp.Gateway.exe"; node cli\\run.js clients add --clients <ids>` (or re-run `.\\install.ps1`). Avoid `npx @latest clients add` there, because it rewrites the registration to this npm package\'s launcher; validate with the same checkout CLI (`node cli\\run.js clients` / `doctor`).');
1347
1443
  }
1348
1444
  return help;
1349
1445
  }
@@ -1939,6 +2035,7 @@ async function probeWorkerStartup({ configPath, observeMs = 2500 }) {
1939
2035
  };
1940
2036
 
1941
2037
  let child;
2038
+ let stopping = false;
1942
2039
  try {
1943
2040
  child = spawn(gatewayExePath, [], {
1944
2041
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -1957,6 +2054,9 @@ async function probeWorkerStartup({ configPath, observeMs = 2500 }) {
1957
2054
  });
1958
2055
 
1959
2056
  child.once('exit', (code) => {
2057
+ // The observation timeout stops the gateway itself; that deliberate
2058
+ // signal is reported by the timeout branch, not as a crash.
2059
+ if (stopping) return;
1960
2060
  if (code === 0) {
1961
2061
  finish({ status: 'pass', detail: 'Worker smoke: gateway exited cleanly during observation window.' });
1962
2062
  } else {
@@ -1970,10 +2070,15 @@ async function probeWorkerStartup({ configPath, observeMs = 2500 }) {
1970
2070
  }
1971
2071
  });
1972
2072
 
1973
- setTimeout(() => {
1974
- try { child.kill(); } catch { }
1975
- // Still alive after observeMs → worker bootstrapped without crashing.
1976
- finish({ status: 'pass', detail: `Worker smoke: gateway stayed alive for ${observeMs}ms with KB and GX configured.` });
2073
+ setTimeout(async () => {
2074
+ stopping = true;
2075
+ const exited = await stopProbeChild(child);
2076
+ if (exited) {
2077
+ // Still alive after observeMs → worker bootstrapped without crashing.
2078
+ finish({ status: 'pass', detail: `Worker smoke: gateway stayed alive for ${observeMs}ms with KB and GX configured.` });
2079
+ } else {
2080
+ finish({ status: 'warn', detail: 'Worker smoke: gateway did not exit after the stop signal; it may still hold the gateway exe.' });
2081
+ }
1977
2082
  }, observeMs);
1978
2083
  });
1979
2084
  }
@@ -2150,11 +2255,12 @@ async function handleClients(subcommand, options, ctx) {
2150
2255
  if (invalidLaunchers.length > 0) {
2151
2256
  help.push(`These clients have a known-invalid MCP launcher (check command and args) — re-register: genexus-mcp clients add --clients ${invalidLaunchers.map((r) => r.id).join(',')}${serverName !== DEFAULT_MCP_SERVER_NAME ? ` --server-name ${serverName}` : ''}`);
2152
2257
  }
2153
- const otherStale = stale.filter((r) =>
2154
- r.launcherStructuralState !== 'missing' && r.launcherSemanticState !== 'invalid'
2155
- );
2156
- if (otherStale.length > 0) {
2157
- help.push(`These clients have a stale launcher — re-register: genexus-mcp clients add --clients ${otherStale.map((r) => r.id).join(',')}${serverName !== DEFAULT_MCP_SERVER_NAME ? ` --server-name ${serverName}` : ''}`);
2258
+ // A launcher that exists but is not this CLI's gateway is a different working
2259
+ // install (checkout, fixed-path, other package cache), so it is reported as
2260
+ // informational drift instead of a stale registration that needs repair.
2261
+ const driftedLaunchers = rows.filter((r) => r.launcherPathDrift && !r.commandStale);
2262
+ if (driftedLaunchers.length > 0) {
2263
+ help.push(`Note: ${driftedLaunchers.map((r) => r.name).join(', ')} point at a gateway that is not this CLI's (informational — the launcher exists and is valid). Re-register with this CLI only if you want them to track it: genexus-mcp clients add --clients ${driftedLaunchers.map((r) => r.id).join(',')}${serverName !== DEFAULT_MCP_SERVER_NAME ? ` --server-name ${serverName}` : ''}`);
2158
2264
  }
2159
2265
  const semanticValidCount = rows.filter((r) => r.launcherSemanticState === 'valid').length;
2160
2266
  const semanticInvalidCount = rows.filter((r) => r.launcherSemanticState === 'invalid').length;
@@ -78,10 +78,16 @@ class OpenCodeJsoncAdapter extends BaseClientAdapter {
78
78
  this.config = configModule || require('./config');
79
79
  }
80
80
  apply(client, launcher, targetConfigPath, opts = {}) {
81
- return this.config.applyOpenCodeJson(client.path, launcher, targetConfigPath, opts);
81
+ return this.config.applyOpenCodeJson(client.path, launcher, targetConfigPath, {
82
+ ...opts,
83
+ alternatePaths: client.alternatePaths || []
84
+ });
82
85
  }
83
86
  remove(client, opts = {}) {
84
- return this.config.removeOpenCodeJson(client.path, opts);
87
+ return this.config.removeOpenCodeJson(client.path, {
88
+ ...opts,
89
+ alternatePaths: client.alternatePaths || []
90
+ });
85
91
  }
86
92
  read(client, serverName) {
87
93
  if (!fs.existsSync(client.path)) return null;