genexus-mcp 3.2.4 → 3.3.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.
@@ -44,7 +44,10 @@ function readCache() {
44
44
  try {
45
45
  const raw = fs.readFileSync(getCacheFile(), 'utf8');
46
46
  const data = JSON.parse(raw);
47
- if (data && typeof data === 'object') return data;
47
+ if (data && typeof data === 'object'
48
+ && (data.package === undefined || data.package === NPM_PACKAGE)
49
+ && (data.channel === undefined || validateChannel(data.channel))
50
+ && parseSemver(data.latestVersion)) return data;
48
51
  } catch {
49
52
  }
50
53
  return null;
@@ -54,7 +57,9 @@ function writeCache(data) {
54
57
  try {
55
58
  const file = getCacheFile();
56
59
  fs.mkdirSync(path.dirname(file), { recursive: true });
57
- fs.writeFileSync(file, JSON.stringify(data), 'utf8');
60
+ const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
61
+ fs.writeFileSync(tmp, JSON.stringify({ package: NPM_PACKAGE, channel: 'latest', ...data }), 'utf8');
62
+ fs.renameSync(tmp, file);
58
63
  } catch {
59
64
  }
60
65
  }
@@ -65,22 +70,42 @@ function stripV(v) {
65
70
 
66
71
  function parseSemver(v) {
67
72
  const s = stripV(v);
68
- const m = /^(\d+)\.(\d+)\.(\d+)/.exec(s);
73
+ const m = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(s);
69
74
  if (!m) return null;
70
- return [Number(m[1]), Number(m[2]), Number(m[3])];
75
+ return { valid: true, major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]),
76
+ prerelease: m[4] ? m[4].split('.') : [], build: m[5] ? m[5].split('.') : [] };
71
77
  }
72
78
 
73
79
  function compareSemver(a, b) {
74
80
  const pa = parseSemver(a);
75
81
  const pb = parseSemver(b);
76
- if (!pa || !pb) return 0;
77
- for (let i = 0; i < 3; i += 1) {
78
- if (pa[i] > pb[i]) return 1;
79
- if (pa[i] < pb[i]) return -1;
82
+ if (!pa || !pb) return null;
83
+ for (const key of ['major', 'minor', 'patch']) {
84
+ if (pa[key] > pb[key]) return 1;
85
+ if (pa[key] < pb[key]) return -1;
86
+ }
87
+ if (pa.prerelease.length === 0 && pb.prerelease.length > 0) return 1;
88
+ if (pa.prerelease.length > 0 && pb.prerelease.length === 0) return -1;
89
+ for (let i = 0; i < Math.max(pa.prerelease.length, pb.prerelease.length); i += 1) {
90
+ if (i >= pa.prerelease.length) return -1;
91
+ if (i >= pb.prerelease.length) return 1;
92
+ const left = pa.prerelease[i];
93
+ const right = pb.prerelease[i];
94
+ if (left === right) continue;
95
+ const leftNum = /^\d+$/.test(left);
96
+ const rightNum = /^\d+$/.test(right);
97
+ if (leftNum && rightNum) return Number(left) > Number(right) ? 1 : -1;
98
+ if (leftNum !== rightNum) return leftNum ? -1 : 1;
99
+ return left > right ? 1 : -1;
80
100
  }
81
101
  return 0;
82
102
  }
83
103
 
104
+ function validateChannel(channel) {
105
+ const value = typeof channel === 'string' ? channel.trim() : '';
106
+ return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) ? value : null;
107
+ }
108
+
84
109
  function httpGetJson(url) {
85
110
  return new Promise((resolve) => {
86
111
  let req;
@@ -112,13 +137,14 @@ function httpGetJson(url) {
112
137
  // api.github.com. Falls back to the GitHub releases API only for the default
113
138
  // channel. The release URL is derived from the version (no API call needed).
114
139
  async function fetchLatestRelease(opts = {}) {
115
- const channel = (opts && opts.channel) || 'latest';
140
+ const channel = validateChannel((opts && opts.channel) || 'latest');
141
+ if (!channel) return null;
116
142
 
117
143
  // 1. npm registry dist-tags (lightweight: just the tag → version map).
118
144
  const tags = await httpGetJson(`https://registry.npmjs.org/-/package/${NPM_PACKAGE}/dist-tags`);
119
145
  if (tags && typeof tags === 'object') {
120
146
  const v = stripV(tags[channel] || '');
121
- if (v) return { latestVersion: v, releaseUrl: releaseUrlForVersion(v), source: 'npm' };
147
+ if (parseSemver(v)) return { latestVersion: v, releaseUrl: releaseUrlForVersion(v), source: 'npm' };
122
148
  // Channel not found on npm — for non-default channels, that's a definitive "no".
123
149
  if (channel !== 'latest') return null;
124
150
  }
@@ -128,7 +154,7 @@ async function fetchLatestRelease(opts = {}) {
128
154
  const rel = await httpGetJson(`https://api.github.com/repos/${REPO}/releases/latest`);
129
155
  if (rel && typeof rel === 'object') {
130
156
  const tag = stripV(rel.tag_name || '');
131
- if (tag) {
157
+ if (parseSemver(tag)) {
132
158
  const url = typeof rel.html_url === 'string' ? rel.html_url : releaseUrlForVersion(tag);
133
159
  return { latestVersion: tag, releaseUrl: url, source: 'github' };
134
160
  }
@@ -157,7 +183,7 @@ function maybePrintCachedBanner(_opts) {
157
183
  const current = getPackageVersion();
158
184
  if (!current) return;
159
185
  const cache = readCache();
160
- if (!cache || !cache.latestVersion) return;
186
+ if (!cache || !cache.latestVersion || (cache.channel && cache.channel !== 'latest')) return;
161
187
  if (compareSemver(cache.latestVersion, current) > 0) {
162
188
  try {
163
189
  process.stderr.write(formatBanner(current, cache.latestVersion, cache.releaseUrl || null));
@@ -253,27 +279,29 @@ function detectInstallMethod() {
253
279
  // The method-appropriate upgrade plan. `auto` means no manual install step is
254
280
  // needed (the npx launcher fetches @latest on the next client start).
255
281
  function upgradePlanFor(method, channel) {
256
- const tag = channel && channel !== 'latest' ? `@${channel}` : '@latest';
282
+ const resolvedChannel = channel || 'latest';
283
+ const tag = resolvedChannel !== 'latest' ? `@${resolvedChannel}` : '@latest';
257
284
  if (method === 'npx-latest') {
258
285
  return {
259
286
  method,
287
+ channel: resolvedChannel,
260
288
  auto: true,
261
289
  steps: [
262
- 'Your clients launch via `npx genexus-mcp@latest`, which fetches the newest version on each start.',
263
- 'Just fully restart your AI client — it will pick up the new version automatically.'
290
+ `Your clients launch via \`npx ${NPM_PACKAGE}${tag}\`, which fetches the newest ${resolvedChannel} version on each start.`,
291
+ 'Just fully restart your AI client — it will pick up the selected channel automatically.'
264
292
  ],
265
- // --apply busts a stale npx cache so the next spawn is guaranteed fresh.
266
- applyCommand: { exe: process.platform === 'win32' ? 'npm.cmd' : 'npm', args: ['cache', 'clean', '--force'] },
293
+ applyCommand: null,
267
294
  restartRequired: true
268
295
  };
269
296
  }
270
297
  if (method === 'fixed-path') {
271
298
  return {
272
299
  method,
300
+ channel: resolvedChannel,
273
301
  auto: false,
274
302
  steps: [
275
- 'Your install runs the gateway from a fixed path (corporate install).',
276
- `Re-run the installer to update in place: ${INSTALL_ONE_LINER}`,
303
+ `Your install runs the gateway from a fixed path (corporate install); npm ${tag} will not update that artifact.`,
304
+ `Re-run the fixed-path installer for the resolved ${resolvedChannel} release artifact${resolvedChannel === 'latest' ? '' : ` from channel \`${resolvedChannel}\``}: ${INSTALL_ONE_LINER}`,
277
305
  'Then fully restart your AI client.'
278
306
  ],
279
307
  applyCommand: null, // self-stage is a future enhancement; installer is the path
@@ -284,6 +312,7 @@ function upgradePlanFor(method, channel) {
284
312
  const tag = channel && channel !== 'latest' ? `@${channel}` : '@latest';
285
313
  return {
286
314
  method,
315
+ channel: resolvedChannel,
287
316
  auto: false,
288
317
  steps: [
289
318
  'Antigravity launches the gateway executable bundled with the npm package, so each MCP handshake skips npx.',
@@ -297,6 +326,7 @@ function upgradePlanFor(method, channel) {
297
326
  // npm-global
298
327
  return {
299
328
  method: 'npm-global',
329
+ channel: resolvedChannel,
300
330
  auto: false,
301
331
  steps: [
302
332
  `Run: npm install -g ${NPM_PACKAGE}${tag}`,
@@ -307,23 +337,37 @@ function upgradePlanFor(method, channel) {
307
337
  };
308
338
  }
309
339
 
310
- function runCommand(exe, args) {
340
+ function runCommand(exe, args, options = {}) {
311
341
  return new Promise((resolve) => {
312
342
  let child;
343
+ let settled = false;
344
+ const finish = (result) => { if (!settled) { settled = true; resolve(result); } };
313
345
  try {
314
346
  child = spawn(exe, args, { stdio: 'inherit', windowsHide: true });
315
347
  } catch (err) {
316
- resolve({ ok: false, code: null, error: err && err.message ? err.message : 'spawn failed' });
348
+ finish({ ok: false, code: null, error: err && err.message ? err.message : 'spawn failed' });
317
349
  return;
318
350
  }
319
- child.on('error', (err) => resolve({ ok: false, code: null, error: err && err.message ? err.message : 'spawn failed' }));
320
- child.on('exit', (code) => resolve({ ok: code === 0, code }));
351
+ let timedOut = false;
352
+ const timeoutMs = Number.isFinite(options.timeoutMs) ? Math.max(1, options.timeoutMs) : 120000;
353
+ const timer = setTimeout(() => {
354
+ timedOut = true;
355
+ try { child.kill(); } catch { /* process may have exited */ }
356
+ }, timeoutMs);
357
+ child.on('error', (err) => { clearTimeout(timer); finish({ ok: false, code: null, error: err && err.message ? err.message : 'spawn failed', timedOut }); });
358
+ child.on('exit', (code, signal) => {
359
+ clearTimeout(timer);
360
+ finish({ ok: !timedOut && code === 0, code: timedOut ? -1 : code, signal: signal || null, timedOut });
361
+ });
321
362
  });
322
363
  }
323
364
 
324
365
  async function handleUpdate(options, ctx) {
325
366
  const opts = options || {};
326
- const channel = opts.channel || 'latest';
367
+ const channel = validateChannel(opts.channel || 'latest');
368
+ if (!channel) {
369
+ return { exitCode: ctx.EXIT_CODES.USAGE, envelope: { error: { code: 'invalid_channel', message: 'Channel must contain only letters, numbers, dots, underscores, or hyphens.' } } };
370
+ }
327
371
  const current = getPackageVersion();
328
372
  const result = await fetchLatestRelease({ channel });
329
373
  const mismatches = detectClientExeDrift();
@@ -346,8 +390,13 @@ async function handleUpdate(options, ctx) {
346
390
  };
347
391
  }
348
392
 
349
- writeCache({ checkedAt: Date.now(), latestVersion: result.latestVersion, releaseUrl: result.releaseUrl, source: result.source || null });
393
+ writeCache({ checkedAt: Date.now(), latestVersion: result.latestVersion, releaseUrl: result.releaseUrl, source: result.source || null, channel });
350
394
 
395
+ const currentSemver = parseSemver(current || '0.0.0');
396
+ const latestSemver = parseSemver(result.latestVersion);
397
+ if (!latestSemver || !currentSemver) {
398
+ return { exitCode: ctx.EXIT_CODES.OK, envelope: { ok: { current, latest: result.latestVersion, channel, updateAvailable: false, invalidVersion: true }, help: ['Could not compare versions because the installed or published version is invalid semver.'] } };
399
+ }
351
400
  const updateAvailable = compareSemver(result.latestVersion, current || '0.0.0') > 0;
352
401
  const plan = upgradePlanFor(install.method, channel);
353
402
 
@@ -433,5 +482,7 @@ module.exports = {
433
482
  getPackageVersion,
434
483
  detectInstallMethod,
435
484
  upgradePlanFor,
436
- fetchLatestRelease
485
+ fetchLatestRelease,
486
+ validateChannel,
487
+ runCommand
437
488
  };
package/cli/run.test.js CHANGED
@@ -6,7 +6,14 @@ const path = require('node:path');
6
6
  const os = require('node:os');
7
7
  const fs = require('node:fs');
8
8
  const { renderOutput } = require('./lib/output');
9
- const { compareSemver, detectInstallMethod, upgradePlanFor } = require('./lib/update-check');
9
+ const {
10
+ compareSemver,
11
+ parseSemver,
12
+ validateChannel,
13
+ detectInstallMethod,
14
+ upgradePlanFor,
15
+ runCommand
16
+ } = require('./lib/update-check');
10
17
  const {
11
18
  detectClientInstalled,
12
19
  readJsonFileSafe,
@@ -1727,12 +1734,37 @@ test('clients remove drops both OpenCode config shapes and legacy key', () => {
1727
1734
  fs.rmSync(tempRoot, { recursive: true, force: true });
1728
1735
  });
1729
1736
 
1730
- test('compareSemver detects newer, older, equal versions', () => {
1731
- assert.equal(compareSemver('1.3.1', '1.3.0'), 1);
1732
- assert.equal(compareSemver('v1.4.0', '1.3.9'), 1);
1733
- assert.equal(compareSemver('1.3.0', '1.3.0'), 0);
1734
- assert.equal(compareSemver('1.2.9', '1.3.0'), -1);
1735
- assert.equal(compareSemver('garbage', '1.0.0'), 0);
1737
+ test('strict semver compares prerelease precedence and ignores build metadata', () => {
1738
+ assert.deepEqual(parseSemver('v1.2.3-alpha.1+build.7'), {
1739
+ valid: true, major: 1, minor: 2, patch: 3,
1740
+ prerelease: ['alpha', '1'], build: ['build', '7']
1741
+ });
1742
+ assert.equal(compareSemver('1.0.0-alpha', '1.0.0-alpha.1'), -1);
1743
+ assert.equal(compareSemver('1.0.0', '1.0.0-rc.1'), 1);
1744
+ assert.equal(compareSemver('1.0.0+one', '1.0.0+two'), 0);
1745
+ });
1746
+
1747
+ test('semver and channel validation are explicit for malformed input', () => {
1748
+ assert.equal(parseSemver('1.2'), null);
1749
+ assert.equal(parseSemver('1.2.3-01'), null);
1750
+ assert.equal(compareSemver('garbage', '1.0.0'), null);
1751
+ assert.equal(validateChannel('latest'), 'latest');
1752
+ assert.equal(validateChannel('next-2026'), 'next-2026');
1753
+ assert.equal(validateChannel('bad channel'), null);
1754
+ assert.equal(validateChannel(''), null);
1755
+ });
1756
+
1757
+ test('npx update plan does not use npm cache clean as an update', () => {
1758
+ const plan = upgradePlanFor('npx-latest', 'latest');
1759
+ assert.equal(plan.applyCommand, null);
1760
+ assert.equal(plan.auto, true);
1761
+ });
1762
+
1763
+ test('runCommand reports exit code and kills timed out child', async () => {
1764
+ const result = await runCommand(process.execPath, ['-e', 'setTimeout(() => {}, 1000)'], { timeoutMs: 30 });
1765
+ assert.equal(result.ok, false);
1766
+ assert.equal(result.timedOut, true);
1767
+ assert.equal(typeof result.code, 'number');
1736
1768
  });
1737
1769
 
1738
1770
  test('detectInstallMethod returns fixed-path when GENEXUS_MCP_GATEWAY_EXE is set', () => {
@@ -1748,6 +1780,25 @@ test('detectInstallMethod returns fixed-path when GENEXUS_MCP_GATEWAY_EXE is set
1748
1780
  }
1749
1781
  });
1750
1782
 
1783
+ test('upgradePlanFor carries the selected channel through every install method', () => {
1784
+ const npx = upgradePlanFor('npx-latest', 'next');
1785
+ assert.equal(npx.channel, 'next');
1786
+ assert.match(npx.steps.join(' '), /npx genexus-mcp@next/);
1787
+ assert.doesNotMatch(npx.steps.join(' '), /@latest/);
1788
+
1789
+ const npm = upgradePlanFor('npm-global', 'next');
1790
+ assert.equal(npm.channel, 'next');
1791
+ assert.deepEqual(npm.applyCommand.args, ['install', '-g', 'genexus-mcp@next']);
1792
+
1793
+ const fixed = upgradePlanFor('fixed-path', 'next');
1794
+ assert.equal(fixed.channel, 'next');
1795
+ assert.match(fixed.steps.join(' '), /next/);
1796
+ assert.doesNotMatch(fixed.steps.join(' '), /npm @latest/);
1797
+
1798
+ const direct = upgradePlanFor('package-direct', 'next');
1799
+ assert.equal(direct.channel, 'next');
1800
+ });
1801
+
1751
1802
  test('upgradePlanFor encodes the per-method upgrade strategy', () => {
1752
1803
  const npx = upgradePlanFor('npx-latest', 'latest');
1753
1804
  assert.equal(npx.auto, true, 'npx@latest auto-updates on restart');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genexus-mcp",
3
- "version": "3.2.4",
3
+ "version": "3.3.1",
4
4
  "mcpName": "io.github.lennix1337/genexus",
5
5
  "description": "GeneXus 17, GeneXus 18 MCP server — read, edit, and analyze Knowledge Base objects directly from Claude, Cursor, and other AI agents over the Model Context Protocol.",
6
6
  "keywords": [
@@ -32,8 +32,10 @@
32
32
  "automation"
33
33
  ],
34
34
  "scripts": {
35
- "test": "node --test cli/run.test.js cli/lib/client-adapters.test.js cli/docs.test.js",
36
- "lint": "eslint cli scripts eslint.config.js --max-warnings=0",
35
+ "test": "node --test cli/run.test.js cli/lib/client-adapters.test.js cli/docs.test.js scripts/lint.test.js && npm run test:live-contract",
36
+ "lint": "node scripts/lint.js",
37
+ "test:live-contract": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/tests/test-live.test.ps1",
38
+ "test:upstream-drift": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/tests/test-upstream-drift.ps1",
37
39
  "test:one": "node scripts/test-one.js",
38
40
  "prepack": "node scripts/clean-package.js",
39
41
  "postinstall": "node scripts/verify-install.js"
@@ -7,7 +7,7 @@
7
7
  "targets": {
8
8
  ".NETCoreApp,Version=v10.0": {},
9
9
  ".NETCoreApp,Version=v10.0/win-x64": {
10
- "GxMcp.Gateway/3.2.4": {
10
+ "GxMcp.Gateway/3.3.1": {
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/3.2.4": {
69
+ "GxMcp.Gateway/3.3.1": {
70
70
  "type": "project",
71
71
  "serviceable": false,
72
72
  "sha512": ""
Binary file
Binary file
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "GeneXus": {
3
- "WorkerExecutable": "worker\\\\GxMcp.Worker.exe",
4
- "InstallationPath": "C:\\Program Files (x86)\\GeneXus\\GeneXus18"
3
+ "InstallationPath": "C:\\Program Files (x86)\\GeneXus\\GeneXus18",
4
+ "WorkerExecutable": "worker\\\\GxMcp.Worker.exe"
5
5
  },
6
6
  "GatewayMode": "stdio-isolated",
7
- "Environment": {
8
- "ResolutionPolicy": "strict"
9
- },
10
7
  "ConfigSchemaVersion": 2,
11
8
  "Server": {
12
- "TerseResponses": true,
13
9
  "SessionIdleTimeoutMinutes": 10,
14
10
  "EmitStructuredContent": false,
15
- "McpStdio": true,
11
+ "HttpPort": 0,
16
12
  "WorkerIdleTimeoutMinutes": 5,
17
- "HttpPort": 0
13
+ "McpStdio": true,
14
+ "TerseResponses": true
15
+ },
16
+ "Environment": {
17
+ "ResolutionPolicy": "strict"
18
18
  }
19
19
  }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": "gxmcp-release-manifest/1",
3
- "version": "3.2.4",
4
- "sourceCommit": "fe843ce956717fb6d8b312854cfbcf206cf6a257",
3
+ "version": "3.3.1",
4
+ "sourceCommit": "334159fa59789dca040f3b3be578a520ef10928d",
5
5
  "sourceCommitPolicy": "exact-tag",
6
- "generatedAtUtc": "2026-09-10T20:52:29.4129863Z",
6
+ "generatedAtUtc": "2026-09-11T17:44:21.9875381Z",
7
7
  "runtime": {
8
8
  "gateway": "net10.0-windows",
9
9
  "worker": "net48-x86",
@@ -15,33 +15,28 @@
15
15
  "2026-07-28"
16
16
  ],
17
17
  "schema": "tool_definitions.json",
18
- "schemaSha256": "19f5c0f9b52f2b387270819dea27d14423577518238e8fb8d4a1399ae515a093",
18
+ "schemaSha256": "5624f61d5e5cabd8cd1beb4f05aa1925735e407814aeafa17e860c1601dd867c",
19
19
  "provenance": "gxmcp-sbom.json",
20
20
  "artifacts": [
21
21
  {
22
22
  "path": "gxmcp-sbom.json",
23
23
  "size": 590,
24
- "sha256": "fb77983fb779ac882d1ee5c9dfb5e870c980511f7f8273eb0149ddaddbb2d42c"
24
+ "sha256": "ad4b76c1840203c5a1484021d52361d173e0b2e06c0c7b1d9b5d78894e100f10"
25
25
  },
26
26
  {
27
27
  "path": "GxMcp.Gateway.exe",
28
28
  "size": 162304,
29
- "sha256": "07fdff5db65cd0c000740fad4b862cd2f829950b161d54363dc412bbbc37f8cc"
30
- },
31
- {
32
- "path": "nexus-ide.vsix",
33
- "size": 1318671,
34
- "sha256": "9e08085bb8464326db853077de96597debaf45617e7430631baaafd21ac0c1ea"
29
+ "sha256": "90c2ea4c63d95d0c5377dc69b8365d8b8248fd0493aeeb9d244c4100b399c80d"
35
30
  },
36
31
  {
37
32
  "path": "tool_definitions.json",
38
- "size": 110245,
39
- "sha256": "19f5c0f9b52f2b387270819dea27d14423577518238e8fb8d4a1399ae515a093"
33
+ "size": 111992,
34
+ "sha256": "5624f61d5e5cabd8cd1beb4f05aa1925735e407814aeafa17e860c1601dd867c"
40
35
  },
41
36
  {
42
37
  "path": "worker/GxMcp.Worker.exe",
43
- "size": 3009024,
44
- "sha256": "76fb42fcfefd24d5e86ba78ea51de2235f0984c57294a490909970e75b27e3cf"
38
+ "size": 3083264,
39
+ "sha256": "876879949e6f6f687be2822352ab7d4746de8794dd59fa541134954ae17abdcd"
45
40
  }
46
41
  ]
47
42
  }
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "schemaVersion": "gxmcp-provenance/1",
3
- "version": "3.2.4",
4
- "sourceCommit": "fe843ce956717fb6d8b312854cfbcf206cf6a257",
3
+ "version": "3.3.1",
4
+ "sourceCommit": "334159fa59789dca040f3b3be578a520ef10928d",
5
5
  "sourceCommitPolicy": "exact-tag",
6
6
  "components": [
7
7
  {
8
8
  "name": "genexus-mcp",
9
9
  "lockfile": "package-lock.json",
10
10
  "size": 39914,
11
- "sha256": "1183548c6ad7a9d908cb237c72ea71d7fac733cbb2ab98fab4c632a33656cb2b"
11
+ "sha256": "a0741a4b780bd14c0388cb43a09a1f95d1d348b8673907788749108d2f96e144"
12
12
  },
13
13
  {
14
14
  "name": "nexus-ide",
15
15
  "lockfile": "src/nexus-ide/package-lock.json",
16
16
  "size": 274983,
17
- "sha256": "6ff45cd39685bd24e115c5afdc974df96d44cab4575ef38af5bb02828428d13d"
17
+ "sha256": "8cae436c7d7f139645cc26ed9b232fe29b75fb35bcd29cc2c05f22937d26f6a3"
18
18
  }
19
19
  ]
20
20
  }
@@ -48,7 +48,7 @@
48
48
  {"name":"genexus_transfer","description":"Real XPZ export/import over the SDK's IKnowledgeManagerService — dependency-aware, IDE Export/Import parity (NOT the filesystem copy genexus_io/kb_import do). action=export (targets[]+outputFile; includeDependencies=true exports transitive closure) | inspect (explore an .xpz, read-only) | import (apply into KB; dryRun defaults true=preview via ExploreExport; dryRun=false requires confirm=true).","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["export","inspect","import"],"description":"XPZ export, inspection, or import operation to perform."},"targets":{"type":"array","items":{"type":"string"},"description":"export: object names to export."},"outputFile":{"type":"string","description":"export: absolute .xpz output path."},"includeDependencies":{"type":"boolean","description":"export: compute transitive dependency closure (Calls, Tables) and export all referenced objects.","default":false},"file":{"type":"string","description":"inspect/import: absolute .xpz path."},"type":{"type":"string","description":"export: disambiguate object type."},"dryRun":{"type":"boolean","description":"import: true (default) previews; false applies (needs confirm)."},"confirm":{"type":"boolean","description":"import with dryRun=false: required."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"export","targets":["Customer"],"outputFile":"C:\\tmp\\cust.xpz"},{"action":"export","targets":["Customer"],"outputFile":"C:\\tmp\\cust_full.xpz","includeDependencies":true},{"action":"inspect","file":"C:\\tmp\\cust.xpz"}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},
49
49
  {"name":"genexus_deploy","description":"Deploy application over the SDK. action=list_targets (read-only, default) enumerates deployment target types (IDeploymentTargetService); action=deploy (destructive, confirm=true) runs IDeploymentService.Deploy(model).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list_targets","deploy"],"description":"Deployment-target or deployment execution operation to perform."},"confirm":{"type":"boolean","description":"deploy: required (builds + ships the app)."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"list_targets"},{"action":"deploy","confirm":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},
50
50
  {"name":"genexus_generator_reference","description":"Native typed .NET generator references used by GxExternalReference. list and dry-run actions are read-only. add/remove validate a managed assembly when a path is supplied, require optimistic baseVersion for changes, save only the native GeneratorsPart, reread it, and restore the complete generator-property snapshot on verification failure. No lifecycle action runs implicitly.","inputSchema":{"type":"object","additionalProperties":false,"required":["action","environment","generator"],"properties":{"action":{"type":"string","enum":["list","dry_run_add","add","dry_run_remove","remove"],"description":"Typed .NET generator-reference operation to perform."},"environment":{"type":"string","description":"Active GeneXus Environment name; the tool never switches environments."},"generator":{"type":"string","description":"Generator display name, e.g. Default (.NET)."},"assembly":{"type":"string","description":"Assembly file name for add/remove, e.g. SpreadsheetLibrary.dll."},"assemblyPath":{"type":"string","description":"Absolute path or KB-relative path used to validate a managed assembly. Required for add unless found in a standard model/bin location."},"baseVersion":{"type":"string","description":"Optimistic token returned by list or a dry-run. Required for a real state change."},"dryRun":{"type":"boolean","default":false,"description":"Force add/remove to remain read-only."},"rollbackOnFailure":{"type":"boolean","default":true,"description":"Restore and verify the complete prior generator-property snapshot if save/reread diverges."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"list","environment":".Net Environment","generator":"Default (.NET)"},{"action":"dry_run_add","environment":".Net Environment","generator":"Default (.NET)","assembly":"SpreadsheetLibrary.dll","assemblyPath":"C:\\KBs\\Sample\\CSharpModel\\web\\bin\\SpreadsheetLibrary.dll"},{"action":"add","environment":".Net Environment","generator":"Default (.NET)","assembly":"SpreadsheetLibrary.dll","assemblyPath":"C:\\KBs\\Sample\\CSharpModel\\web\\bin\\SpreadsheetLibrary.dll","baseVersion":"sha256:<token>","rollbackOnFailure":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false}},
51
- {"name":"genexus_wwp","description":"Typed WorkWithPlus action, tab, and grid edits with dryRun, optimistic concurrency, exact snapshots, post-save reread, projection verification, and rollback. No lifecycle operation is implicit. Settings: SDK tree and dryRun only; save refused until event isolation is certified.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["list","add_action","update_action","move_action","remove_action","add_tab","move_tab","remove_tab","add_grid_attribute","settings_templates","settings_read","settings_edit"],"description":"WorkWithPlus action-group, tab, or grid-attribute operation to perform."},"name":{"type":"string","description":"Transaction, WebPanel, or WorkWithPlus<Object> host."},"group":{"type":"string","description":"Action group name. add_action creates it when absent; update/remove use it as a filter."},"actionName":{"type":"string","description":"Action name."},"attribute":{"type":"string","description":"add_grid_attribute: GeneXus Attribute name."},"caption":{"type":"string","description":"Action or grid-column caption."},"procedure":{"type":"string","description":"add/update: associated Procedure; existence is validated against the KB."},"selection":{"type":"string","enum":["single","multiple"],"description":"add/update: execution scope for grid actions."},"enabledWhen":{"type":"string","description":"Availability condition stored verbatim; expression semantics are not validated."},"icon":{"type":"string","description":"add/update: button icon."},"description":{"type":"string","description":"add/update: tooltip or description."},"confirm":{"type":"boolean","description":"Confirmation prompt; required to delete unless dryRun."},"buttonClass":{"type":"string","description":"add/update: theme button class."},"position":{"type":"integer","minimum":0,"description":"Zero-based action or tab index."},"toGroup":{"type":"string","description":"move_action: destination group (created when absent)."},"fromGroup":{"type":"string","description":"move_action: source group (optional; searched across groups when omitted)."},"dryRun":{"type":"boolean","description":"Return a typed diff and versionToken without mutation."},"controlName":{"type":"string","description":"Tab ControlName."},"title":{"type":"string","description":"add_tab: tab title."},"children":{"type":"array","description":"add_tab controls; flat children are wrapped in a responsive table.","items":{"$ref":"#/$defs/wwpControl"}},"baseVersion":{"type":"string","description":"Optimistic-concurrency token returned by dryRun/read."},"expectedVersion":{"type":"string","description":"Alias for baseVersion."},"versionToken":{"type":"string","description":"Backward-compatible alias for baseVersion."},"kb":{"type":"string","description":"KB alias."},"template":{"description":"Settings template catalog path or unique internal name.","type":"string"},"limit":{"type":"integer","minimum":0,"description":"Settings nodes per page; 0 reads all."},"guid":{"type":"string"},"nodePath":{"description":"Settings node path returned by settings_read.","type":"string"},"entityKey":{"type":"string"},"value":{"type":"string"},"property":{"description":"Exact SDK property name.","type":"string"},"offset":{"minimum":0,"type":"integer"}},"$defs":{"wwpControl":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["variable","userAction","table"]},"name":{"type":"string"},"caption":{"type":"string"},"description":{"type":"string"},"basicType":{"type":"string"},"length":{"type":"integer","minimum":1},"decimals":{"type":"integer","minimum":0},"controlType":{"type":"string"},"controlValues":{"type":"string"},"controlEmptyItem":{"type":"boolean"},"controlEmptyItemText":{"type":"string"},"columns":{"type":"integer","minimum":1},"themeClass":{"type":"string"},"children":{"type":"array","items":{"$ref":"#/$defs/wwpControl"}}},"additionalProperties":false}},"examples":[{"action":"list","name":"SampleOrderWW"},{"action":"add_action","name":"SampleOrderWW","group":"Operations","actionName":"Retry","procedure":"RetrySampleOrder","selection":"multiple","dryRun":true},{"action":"add_tab","name":"SamplePanel","controlName":"Details","title":"Details","children":[{"type":"variable","name":"Status","basicType":"VarChar","length":40},{"type":"userAction","name":"Refresh","caption":"Refresh"}],"dryRun":true},{"action":"add_grid_attribute","name":"SampleOrderWW","attribute":"SampleOrderStatus","caption":"Status","dryRun":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},
51
+ {"name":"genexus_wwp","description":"Typed WorkWithPlus action, tab, grid, and native structural edits with dryRun, optimistic concurrency, exact snapshots, post-save reread, projection verification, and rollback. No lifecycle operation is implicit. Settings: embedded and separate WWP templates, paged reads and exact table-class dryRun; saves remain blocked.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["list","add_action","update_action","move_action","remove_action","add_tab","move_tab","remove_tab","add_grid_attribute","replace_web_component_with_user_action","settings_templates","settings_read","settings_edit"],"description":"WorkWithPlus action-group, tab, grid-attribute, or explicit native structural operation to perform."},"name":{"type":"string","description":"Transaction, WebPanel, or WorkWithPlus<Object> host."},"group":{"type":"string","description":"Action group name. add_action creates it when absent; update/remove use it as a filter."},"actionName":{"type":"string","description":"Action name."},"attribute":{"type":"string","description":"add_grid_attribute: GeneXus Attribute name."},"caption":{"type":"string","description":"Action caption. For a DropDownComponent pass the GeneXus expression used for the closed button, for example &Context.EmpresaDescricao."},"procedure":{"type":"string","description":"add/update: associated Procedure; existence is validated against the KB."},"selection":{"type":"string","enum":["single","multiple"],"description":"add/update: execution scope for grid actions."},"enabledWhen":{"type":"string","description":"Availability condition stored verbatim; expression semantics are not validated."},"icon":{"type":"string","description":"add/update: button icon."},"description":{"type":"string","description":"add/update: tooltip or description."},"confirm":{"type":"boolean","description":"Confirmation prompt; required to delete unless dryRun."},"buttonClass":{"type":"string","description":"add/update: theme button class."},"position":{"type":"integer","minimum":0,"description":"Zero-based action or tab index."},"toGroup":{"type":"string","description":"move_action: destination group (created when absent)."},"fromGroup":{"type":"string","description":"move_action: source group (optional; searched across groups when omitted)."},"dryRun":{"type":"boolean","description":"Return a typed diff and versionToken without mutation."},"controlName":{"type":"string","description":"Tab ControlName."},"title":{"type":"string","description":"add_tab: tab title."},"children":{"type":"array","description":"add_tab controls; flat children are wrapped in a responsive table.","items":{"$ref":"#/$defs/wwpControl"}},"sourceName":{"type":"string","description":"replace_web_component_with_user_action: existing WebComponent name."},"userActionName":{"type":"string","description":"replace_web_component_with_user_action: resulting UserAction name; defaults to sourceName."},"tablePath":{"type":"string","description":"replace_web_component_with_user_action: full breadcrumb including the source node, for example TableHeader > TableUserRole > EmpresaSelector."},"gxobject":{"type":"string","description":"replace_web_component_with_user_action: existing WebComponent Gxobject to retain; it must match the current node."},"controlType":{"type":"string","enum":["DropDownComponent"],"description":"replace_web_component_with_user_action: resulting UserAction ControlType."},"webComponentLoad":{"type":"string","enum":["On Web Panel load","On first click","On every click"],"description":"DropDownComponent load timing."},"trigger":{"type":"string","enum":["Click","Hover"],"description":"DropDownComponent trigger."},"baseVersion":{"type":"string","description":"Optimistic-concurrency token returned by dryRun/read."},"expectedVersion":{"type":"string","description":"Alias for baseVersion."},"versionToken":{"type":"string","description":"Backward-compatible alias for baseVersion."},"kb":{"type":"string","description":"KB alias."},"template":{"description":"Settings catalog path, wwp:<template-guid>, or unique template name.","type":"string"},"limit":{"type":"integer","minimum":0,"description":"Settings nodes per page; offset=0, limit=0 includes full separate-template XML."},"guid":{"type":"string","description":"Pattern Settings object GUID for settings_* actions."},"nodePath":{"description":"Settings node path returned by settings_read.","type":"string"},"entityKey":{"type":"string"},"value":{"type":"string"},"property":{"description":"Exact SDK property name.","type":"string"},"offset":{"minimum":0,"type":"integer"}},"$defs":{"wwpControl":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["variable","userAction","table"]},"name":{"type":"string"},"caption":{"type":"string"},"description":{"type":"string"},"basicType":{"type":"string"},"length":{"type":"integer","minimum":1},"decimals":{"type":"integer","minimum":0},"controlType":{"type":"string"},"controlValues":{"type":"string"},"controlEmptyItem":{"type":"boolean"},"controlEmptyItemText":{"type":"string"},"columns":{"type":"integer","minimum":1},"themeClass":{"type":"string"},"children":{"type":"array","items":{"$ref":"#/$defs/wwpControl"}}},"additionalProperties":false}},"examples":[{"action":"list","name":"SampleOrderWW"},{"action":"add_action","name":"SampleOrderWW","group":"Operations","actionName":"Retry","procedure":"RetrySampleOrder","selection":"multiple","dryRun":true},{"action":"add_tab","name":"SamplePanel","controlName":"Details","title":"Details","children":[{"type":"variable","name":"Status","basicType":"VarChar","length":40},{"type":"userAction","name":"Refresh","caption":"Refresh"}],"dryRun":true},{"action":"add_grid_attribute","name":"SampleOrderWW","attribute":"SampleOrderStatus","caption":"Status","dryRun":true},{"action":"replace_web_component_with_user_action","name":"WorkWithPlusWorkWithPlusMasterPage","sourceName":"EmpresaSelector","userActionName":"EmpresaSelector","tablePath":"TableHeader > TableUserRole > EmpresaSelector","gxobject":"WWP_MasterPageEmpresaSelectorWC","controlType":"DropDownComponent","caption":"&Context.EmpresaDescricao","webComponentLoad":"On every click","trigger":"Click","dryRun":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},
52
52
  {"name":"genexus_kb_diff","description":"Gateway-only filesystem comparison of two KB directories. Requires kbA and kbB aliases or paths; no active KB selection is used.","inputSchema":{"type":"object","required":["kbA","kbB"],"properties":{"kbA":{"type":"string"},"kbB":{"type":"string"}},"examples":[{"kbA":"development","kbB":"staging"}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
53
53
  {"name":"genexus_kb_import","description":"Gateway-only filesystem copy of one object between two KB directories. Requires source and object identity; run lifecycle index afterwards.","inputSchema":{"type":"object","required":["from","name","type"],"properties":{"from":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"to":{"type":"string"}},"examples":[{"from":"C:\\KBs\\source","to":"C:\\KBs\\target","name":"Customer","type":"Transaction"}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false}},
54
54
  {"name":"genexus_sandbox","description":"Gateway-only filesystem sandbox management; clone or remove a KB directory without SDK dispatch.","inputSchema":{"type":"object","required":["action","name"],"properties":{"action":{"type":"string","enum":["create","remove"]},"name":{"type":"string"},"from":{"type":"string"},"overwrite":{"type":"boolean"}},"examples":[{"action":"create","name":"probe","from":"C:\\KBs\\base"},{"action":"remove","name":"probe"}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false}},
Binary file
Binary file