genexus-mcp 3.5.0 → 3.5.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/cli/run.test.js +129 -74
- 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/config.json +11 -11
- package/publish/gxmcp-manifest.json +15 -10
- package/publish/gxmcp-sbom.json +4 -4
- package/publish/nexus-ide.vsix +0 -0
- package/publish/tool_definitions.json +2 -2
- package/publish/worker/GxMcp.Worker.exe +0 -0
package/cli/run.test.js
CHANGED
|
@@ -39,9 +39,33 @@ if (process.platform === 'win32') {
|
|
|
39
39
|
fs.chmodSync(testGatewayPath, 0o755);
|
|
40
40
|
}
|
|
41
41
|
const testGatewayEnv = { GENEXUS_MCP_GATEWAY_EXE: testGatewayPath };
|
|
42
|
+
|
|
43
|
+
const RETRYABLE_REMOVE_CODES = new Set(['EPERM', 'EBUSY', 'EACCES']);
|
|
44
|
+
function waitForRemoveRetry(delayMs) {
|
|
45
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function removeTempPath(targetPath, options = {}, deps = {}) {
|
|
49
|
+
const fsImpl = deps.fsImpl || fs;
|
|
50
|
+
const platform = deps.platform || process.platform;
|
|
51
|
+
const sleep = deps.sleep || waitForRemoveRetry;
|
|
52
|
+
const maxAttempts = platform === 'win32' ? 5 : 1;
|
|
53
|
+
|
|
54
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
55
|
+
try {
|
|
56
|
+
fsImpl.rmSync(targetPath, options);
|
|
57
|
+
return;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const retryable = RETRYABLE_REMOVE_CODES.has(error?.code);
|
|
60
|
+
if (!retryable || attempt === maxAttempts) throw error;
|
|
61
|
+
sleep(10 * (2 ** (attempt - 1)));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
42
66
|
test.after(() => {
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
removeTempPath(testGxPath, { recursive: true, force: true });
|
|
68
|
+
removeTempPath(testGatewayPath, { force: true });
|
|
45
69
|
});
|
|
46
70
|
|
|
47
71
|
function runCli(args, opts = {}) {
|
|
@@ -53,6 +77,37 @@ function runCli(args, opts = {}) {
|
|
|
53
77
|
return spawnSync(process.execPath, [cliPath, ...args], spawnOptions);
|
|
54
78
|
}
|
|
55
79
|
|
|
80
|
+
test('temporary cleanup retries transient Windows removal errors', () => {
|
|
81
|
+
let attempts = 0;
|
|
82
|
+
const sleeps = [];
|
|
83
|
+
const fakeFs = {
|
|
84
|
+
rmSync() {
|
|
85
|
+
attempts += 1;
|
|
86
|
+
if (attempts < 3) {
|
|
87
|
+
const error = new Error('file is still in use');
|
|
88
|
+
error.code = 'EPERM';
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
removeTempPath('fixture', { recursive: true, force: true }, {
|
|
95
|
+
fsImpl: fakeFs,
|
|
96
|
+
platform: 'win32',
|
|
97
|
+
sleep: (delayMs) => sleeps.push(delayMs)
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
assert.equal(attempts, 3);
|
|
101
|
+
assert.deepEqual(sleeps, [10, 20]);
|
|
102
|
+
|
|
103
|
+
const permanentError = Object.assign(new Error('invalid path'), { code: 'EINVAL' });
|
|
104
|
+
assert.throws(() => removeTempPath('fixture', {}, {
|
|
105
|
+
fsImpl: { rmSync: () => { throw permanentError; } },
|
|
106
|
+
platform: 'win32',
|
|
107
|
+
sleep: () => assert.fail('non-retryable cleanup errors must not sleep')
|
|
108
|
+
}), permanentError);
|
|
109
|
+
});
|
|
110
|
+
|
|
56
111
|
test('status returns structured json envelope with schema version', () => {
|
|
57
112
|
const result = runCli(['status', '--format', 'json']);
|
|
58
113
|
assert.equal(result.status, 0);
|
|
@@ -193,7 +248,7 @@ test('non-interactive init supports idempotent no-op', () => {
|
|
|
193
248
|
const cfgPath = path.join(kbDir, 'config.json');
|
|
194
249
|
assert.equal(fs.existsSync(cfgPath), true);
|
|
195
250
|
|
|
196
|
-
|
|
251
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
197
252
|
});
|
|
198
253
|
|
|
199
254
|
test('config create creates explicit neutral runtime without KB or client registration', () => {
|
|
@@ -214,7 +269,7 @@ test('config create creates explicit neutral runtime without KB or client regist
|
|
|
214
269
|
assert.equal(parsed.meta.clientRegistration, 'not_attempted');
|
|
215
270
|
assert.equal(parsed.meta.kbCatalog, 'not_created');
|
|
216
271
|
assert.deepEqual(JSON.parse(fs.readFileSync(output, 'utf8')), parsed.ok.config);
|
|
217
|
-
|
|
272
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
218
273
|
});
|
|
219
274
|
|
|
220
275
|
test('config create rejects missing new-format flags', () => {
|
|
@@ -254,7 +309,7 @@ test('config create does not modify client registration', () => {
|
|
|
254
309
|
assert.equal(result.status, 0);
|
|
255
310
|
assert.equal(fs.readFileSync(marker, 'utf8'), before);
|
|
256
311
|
assert.deepEqual(JSON.parse(result.stdout).ok.config, JSON.parse(fs.readFileSync(output, 'utf8')));
|
|
257
|
-
|
|
312
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
258
313
|
});
|
|
259
314
|
test('config migrate creates a neutral config with an atomic backup and read-back receipt', () => {
|
|
260
315
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-migrate-'));
|
|
@@ -277,7 +332,7 @@ test('config migrate creates a neutral config with an atomic backup and read-bac
|
|
|
277
332
|
assert.equal(JSON.parse(fs.readFileSync(target, 'utf8')).ConfigSchemaVersion, 2);
|
|
278
333
|
assert.deepEqual(parsed.ok.notMigrated, ['Environment.KBPath', 'Environment.KBs', 'Environment.DefaultKb']);
|
|
279
334
|
} finally {
|
|
280
|
-
|
|
335
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
281
336
|
}
|
|
282
337
|
});
|
|
283
338
|
|
|
@@ -299,7 +354,7 @@ test('config migrate rolls back an existing destination when read-back fails', (
|
|
|
299
354
|
assert.equal(fs.readFileSync(target, 'utf8'), original);
|
|
300
355
|
assert.equal(parsed.error.rollback.rolledBack, true);
|
|
301
356
|
} finally {
|
|
302
|
-
|
|
357
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
303
358
|
}
|
|
304
359
|
});
|
|
305
360
|
|
|
@@ -315,7 +370,7 @@ test('config migrate rejects non-migratable fields when explicitly requested', (
|
|
|
315
370
|
assert.match(parsed.error.message, /non-migratable/i);
|
|
316
371
|
assert.equal(fs.existsSync(target), false);
|
|
317
372
|
} finally {
|
|
318
|
-
|
|
373
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
319
374
|
}
|
|
320
375
|
});
|
|
321
376
|
|
|
@@ -326,7 +381,7 @@ test('whoami without config returns disconnected state', () => {
|
|
|
326
381
|
const parsed = JSON.parse(res.stdout);
|
|
327
382
|
assert.equal(parsed.ok.connected, false);
|
|
328
383
|
assert.ok(parsed.ok.reason, 'should explain why not connected');
|
|
329
|
-
|
|
384
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
330
385
|
});
|
|
331
386
|
|
|
332
387
|
test('whoami with config returns kb and geneXus details', () => {
|
|
@@ -345,7 +400,7 @@ test('whoami with config returns kb and geneXus details', () => {
|
|
|
345
400
|
assert.equal(parsed.ok.geneXus.installationPath, testGxPath);
|
|
346
401
|
assert.equal(parsed.meta.command, 'whoami');
|
|
347
402
|
|
|
348
|
-
|
|
403
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
349
404
|
});
|
|
350
405
|
|
|
351
406
|
test('uninstall --yes removes local config and reports plan', () => {
|
|
@@ -366,7 +421,7 @@ test('uninstall --yes removes local config and reports plan', () => {
|
|
|
366
421
|
assert.equal(parsed.ok.configRemoved, true);
|
|
367
422
|
assert.equal(fs.existsSync(cfgPath), false, 'config.json should be deleted');
|
|
368
423
|
|
|
369
|
-
|
|
424
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
370
425
|
});
|
|
371
426
|
|
|
372
427
|
test('uninstall --help returns usage entry', () => {
|
|
@@ -401,7 +456,7 @@ test('init auto-discovers KB from cwd when --kb is omitted', () => {
|
|
|
401
456
|
assert.equal(parsed.ok.resolved.kb.source, 'cwd');
|
|
402
457
|
assert.equal(parsed.ok.resolved.gx.source, 'flag');
|
|
403
458
|
|
|
404
|
-
|
|
459
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
405
460
|
});
|
|
406
461
|
|
|
407
462
|
test('GeneXus installation identity falls back to executable metadata', () => {
|
|
@@ -417,7 +472,7 @@ test('GeneXus installation identity falls back to executable metadata', () => {
|
|
|
417
472
|
source: 'executable-metadata'
|
|
418
473
|
});
|
|
419
474
|
} finally {
|
|
420
|
-
|
|
475
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
421
476
|
}
|
|
422
477
|
});
|
|
423
478
|
|
|
@@ -435,7 +490,7 @@ test('GeneXus installation identity ignores an invalid version file when executa
|
|
|
435
490
|
source: 'executable-metadata'
|
|
436
491
|
});
|
|
437
492
|
} finally {
|
|
438
|
-
|
|
493
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
439
494
|
}
|
|
440
495
|
});
|
|
441
496
|
|
|
@@ -449,7 +504,7 @@ test('GeneXus installation identity does not infer a major from a missing folder
|
|
|
449
504
|
source: 'unavailable'
|
|
450
505
|
});
|
|
451
506
|
} finally {
|
|
452
|
-
|
|
507
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
453
508
|
}
|
|
454
509
|
});
|
|
455
510
|
|
|
@@ -465,7 +520,7 @@ test('KB identity reads the GeneXus major from its gxw metadata', () => {
|
|
|
465
520
|
assert.equal(identity.major, '17');
|
|
466
521
|
assert.equal(identity.source, 'gxw-version');
|
|
467
522
|
} finally {
|
|
468
|
-
|
|
523
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
469
524
|
}
|
|
470
525
|
});
|
|
471
526
|
|
|
@@ -481,7 +536,7 @@ test('KB identity fails closed for malformed gxw metadata', () => {
|
|
|
481
536
|
assert.equal(identity.source, 'unavailable');
|
|
482
537
|
assert.equal(identity.reason, 'malformed-gxw');
|
|
483
538
|
} finally {
|
|
484
|
-
|
|
539
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
485
540
|
}
|
|
486
541
|
});
|
|
487
542
|
|
|
@@ -513,7 +568,7 @@ test('init rejects a known KB and SDK major mismatch before writing config', ()
|
|
|
513
568
|
assert.equal(parsed.error.code, 'sdk_kb_mismatch');
|
|
514
569
|
assert.equal(fs.existsSync(path.join(kbDir, 'config.json')), false);
|
|
515
570
|
} finally {
|
|
516
|
-
|
|
571
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
517
572
|
}
|
|
518
573
|
});
|
|
519
574
|
|
|
@@ -549,7 +604,7 @@ test('interactive init does not silently choose the primary SDK when KB metadata
|
|
|
549
604
|
} finally {
|
|
550
605
|
if (previousGeneXusHome === undefined) delete process.env.GENEXUS_HOME;
|
|
551
606
|
else process.env.GENEXUS_HOME = previousGeneXusHome;
|
|
552
|
-
|
|
607
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
553
608
|
}
|
|
554
609
|
});
|
|
555
610
|
|
|
@@ -566,7 +621,7 @@ test('init fails clearly when paths cannot be auto-discovered', () => {
|
|
|
566
621
|
assert.equal(parsed.error.code, 'usage_error');
|
|
567
622
|
assert.ok(parsed.error.message.includes('--kb'), 'error should mention --kb');
|
|
568
623
|
|
|
569
|
-
|
|
624
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
570
625
|
});
|
|
571
626
|
|
|
572
627
|
test('kb list shows the KB auto-registered by init', () => {
|
|
@@ -585,7 +640,7 @@ test('kb list shows the KB auto-registered by init', () => {
|
|
|
585
640
|
assert.equal(parsed.ok.kbs[0].active, true);
|
|
586
641
|
assert.equal(parsed.ok.kbs[0].path, kbDir);
|
|
587
642
|
|
|
588
|
-
|
|
643
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
589
644
|
});
|
|
590
645
|
|
|
591
646
|
test('kb list reads gateway-style KB arrays and DefaultKb', () => {
|
|
@@ -611,7 +666,7 @@ test('kb list reads gateway-style KB arrays and DefaultKb', () => {
|
|
|
611
666
|
assert.deepEqual(parsed.ok.kbs.map((entry) => entry.name), ['main', 'legacy']);
|
|
612
667
|
assert.equal(parsed.ok.kbs[1].active, true);
|
|
613
668
|
|
|
614
|
-
|
|
669
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
615
670
|
});
|
|
616
671
|
|
|
617
672
|
test('kb switch preserves gateway-style array entries', () => {
|
|
@@ -640,7 +695,7 @@ test('kb switch preserves gateway-style array entries', () => {
|
|
|
640
695
|
assert.equal(cfg.Environment.ActiveKb, 'legacy');
|
|
641
696
|
assert.equal(cfg.Environment.DefaultKb, 'legacy');
|
|
642
697
|
|
|
643
|
-
|
|
698
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
644
699
|
});
|
|
645
700
|
|
|
646
701
|
test('kb add and switch update active KB', () => {
|
|
@@ -668,7 +723,7 @@ test('kb add and switch update active KB', () => {
|
|
|
668
723
|
assert.equal(cfg.Environment.KBPath, kbB, 'legacy KBPath should be updated');
|
|
669
724
|
assert.equal(cfg.Environment.ActiveKb, 'bravo');
|
|
670
725
|
|
|
671
|
-
|
|
726
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
672
727
|
});
|
|
673
728
|
|
|
674
729
|
test('kb switch rejects unknown name', () => {
|
|
@@ -684,7 +739,7 @@ test('kb switch rejects unknown name', () => {
|
|
|
684
739
|
assert.equal(parsed.error.code, 'usage_error');
|
|
685
740
|
assert.ok(parsed.error.message.includes('nonexistent'));
|
|
686
741
|
|
|
687
|
-
|
|
742
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
688
743
|
});
|
|
689
744
|
|
|
690
745
|
test('kb remove deletes entry and reassigns active when applicable', () => {
|
|
@@ -703,7 +758,7 @@ test('kb remove deletes entry and reassigns active when applicable', () => {
|
|
|
703
758
|
assert.equal(parsed.ok.removed, true);
|
|
704
759
|
assert.equal(parsed.ok.activeKb, 'second', 'active should fall back to remaining KB');
|
|
705
760
|
|
|
706
|
-
|
|
761
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
707
762
|
});
|
|
708
763
|
|
|
709
764
|
test('kb switch --kb refuses to overwrite existing entry with different path', () => {
|
|
@@ -723,7 +778,7 @@ test('kb switch --kb refuses to overwrite existing entry with different path', (
|
|
|
723
778
|
const cfg = JSON.parse(fs.readFileSync(path.join(kbA, 'config.json'), 'utf8'));
|
|
724
779
|
assert.equal(cfg.Environment.KBs.Sales, kbA, 'original entry must be preserved');
|
|
725
780
|
|
|
726
|
-
|
|
781
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
727
782
|
});
|
|
728
783
|
|
|
729
784
|
test('kb remove of last KB clears legacy KBPath', () => {
|
|
@@ -740,7 +795,7 @@ test('kb remove of last KB clears legacy KBPath', () => {
|
|
|
740
795
|
assert.equal(cfg.Environment.ActiveKb, undefined, 'ActiveKb should be cleared');
|
|
741
796
|
assert.equal(cfg.Environment.DefaultKb, undefined, 'DefaultKb should be cleared');
|
|
742
797
|
|
|
743
|
-
|
|
798
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
744
799
|
});
|
|
745
800
|
|
|
746
801
|
test('kb subcommand validation: missing subcommand returns usage error', () => {
|
|
@@ -755,7 +810,7 @@ test('kb subcommand validation: missing subcommand returns usage error', () => {
|
|
|
755
810
|
const parsed = JSON.parse(res.stdout);
|
|
756
811
|
assert.equal(parsed.error.code, 'usage_error');
|
|
757
812
|
|
|
758
|
-
|
|
813
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
759
814
|
});
|
|
760
815
|
|
|
761
816
|
test('tool_definitions.json is valid and disambiguation tools have use-when guidance', () => {
|
|
@@ -850,7 +905,7 @@ test('config show truncates large raw content and suggests --full', () => {
|
|
|
850
905
|
assert.equal(parsed.meta.truncated, true);
|
|
851
906
|
assert.ok(parsed.help.some((h) => h.includes('--full')));
|
|
852
907
|
|
|
853
|
-
|
|
908
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
854
909
|
});
|
|
855
910
|
|
|
856
911
|
test('config show suppresses truncation hint when raw field is not requested', () => {
|
|
@@ -876,7 +931,7 @@ test('config show suppresses truncation hint when raw field is not requested', (
|
|
|
876
931
|
assert.equal(parsed.meta.truncated, false);
|
|
877
932
|
assert.equal(parsed.help.length, 0);
|
|
878
933
|
|
|
879
|
-
|
|
934
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
880
935
|
});
|
|
881
936
|
|
|
882
937
|
test('--fields validation returns usage error for invalid doctor field', () => {
|
|
@@ -940,7 +995,7 @@ test('doctor marks HTTP smoke not applicable for stdio-isolated runtime', () =>
|
|
|
940
995
|
assert.equal(target.baseUrl, null);
|
|
941
996
|
assert.match(target.detail, /stdio-isolated|HTTP listener is disabled/);
|
|
942
997
|
} finally {
|
|
943
|
-
|
|
998
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
944
999
|
}
|
|
945
1000
|
});
|
|
946
1001
|
|
|
@@ -975,7 +1030,7 @@ test('doctor reports a KB and SDK major mismatch', () => {
|
|
|
975
1030
|
assert.equal(check.status, 'fail');
|
|
976
1031
|
assert.match(check.detail, /KB major 17.*SDK major 18/);
|
|
977
1032
|
} finally {
|
|
978
|
-
|
|
1033
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
979
1034
|
}
|
|
980
1035
|
});
|
|
981
1036
|
|
|
@@ -1009,7 +1064,7 @@ test('doctor rejects a KB and SDK major that is outside the compatibility catalo
|
|
|
1009
1064
|
assert.match(check.detail, /KB major 19 is not supported/);
|
|
1010
1065
|
assert.match(check.detail, /Supported majors: 17, 18/);
|
|
1011
1066
|
} finally {
|
|
1012
|
-
|
|
1067
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1013
1068
|
}
|
|
1014
1069
|
});
|
|
1015
1070
|
|
|
@@ -1043,7 +1098,7 @@ test('quiet flag suppresses launcher stderr noise', () => {
|
|
|
1043
1098
|
assert.equal(result.status, 1);
|
|
1044
1099
|
assert.equal(result.stderr.trim(), '');
|
|
1045
1100
|
} finally {
|
|
1046
|
-
|
|
1101
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1047
1102
|
}
|
|
1048
1103
|
});
|
|
1049
1104
|
|
|
@@ -1070,7 +1125,7 @@ test('stdio launcher writes a breadcrumb when the gateway is missing before spaw
|
|
|
1070
1125
|
assert.match(log, /Gateway executable not found/);
|
|
1071
1126
|
assert.match(log, /missing[\\/]GxMcp\.Gateway\.exe/);
|
|
1072
1127
|
} finally {
|
|
1073
|
-
|
|
1128
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1074
1129
|
}
|
|
1075
1130
|
});
|
|
1076
1131
|
|
|
@@ -1091,7 +1146,7 @@ test('doctor surfaces the stdio error breadcrumb when one exists', () => {
|
|
|
1091
1146
|
assert.equal(check.status, 'warn');
|
|
1092
1147
|
assert.ok(check.detail.includes(logPath));
|
|
1093
1148
|
} finally {
|
|
1094
|
-
|
|
1149
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1095
1150
|
}
|
|
1096
1151
|
});
|
|
1097
1152
|
|
|
@@ -1124,7 +1179,7 @@ test('detectClientInstalled flags an agent installed via marker even with no MCP
|
|
|
1124
1179
|
assert.equal(det.hasConfig, false, 'config file does not exist yet');
|
|
1125
1180
|
assert.equal(det.markerHit, installDir);
|
|
1126
1181
|
|
|
1127
|
-
|
|
1182
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1128
1183
|
});
|
|
1129
1184
|
|
|
1130
1185
|
test('detectClientInstalled reports not-installed and lists checked paths', () => {
|
|
@@ -1140,7 +1195,7 @@ test('detectClientInstalled reports not-installed and lists checked paths', () =
|
|
|
1140
1195
|
assert.equal(det.markerHit, null);
|
|
1141
1196
|
assert.deepEqual(det.markersChecked, client.installMarkers);
|
|
1142
1197
|
|
|
1143
|
-
|
|
1198
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1144
1199
|
});
|
|
1145
1200
|
|
|
1146
1201
|
test('detectClientInstalled treats an existing config file as installed', () => {
|
|
@@ -1153,7 +1208,7 @@ test('detectClientInstalled treats an existing config file as installed', () =>
|
|
|
1153
1208
|
assert.equal(det.installed, true);
|
|
1154
1209
|
assert.equal(det.hasConfig, true);
|
|
1155
1210
|
|
|
1156
|
-
|
|
1211
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1157
1212
|
});
|
|
1158
1213
|
|
|
1159
1214
|
// Build a throwaway HOME so client-config writes never touch the real machine.
|
|
@@ -1222,7 +1277,7 @@ test('clients list reports OpenCode Desktop with shared opencode config', () =>
|
|
|
1222
1277
|
assert.equal(rowRegistered.configPath, opencodeCfg);
|
|
1223
1278
|
assert.equal(rowRegistered.registrationMode, 'automatic');
|
|
1224
1279
|
} finally {
|
|
1225
|
-
|
|
1280
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1226
1281
|
}
|
|
1227
1282
|
});
|
|
1228
1283
|
|
|
@@ -1251,7 +1306,7 @@ test('clients add patches OpenCode Desktop into shared opencode config', () => {
|
|
|
1251
1306
|
const row = JSON.parse(listRes.stdout).ok.clients.find((client) => client.id === 'opencode-desktop');
|
|
1252
1307
|
assert.ok(row && row.registered, 'OpenCode Desktop should now report registered');
|
|
1253
1308
|
} finally {
|
|
1254
|
-
|
|
1309
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1255
1310
|
}
|
|
1256
1311
|
});
|
|
1257
1312
|
|
|
@@ -1274,7 +1329,7 @@ test('OpenCode Desktop is not falsely reported as installed when only CLI config
|
|
|
1274
1329
|
assert.ok(cliRow, 'OpenCode CLI should be present in targets');
|
|
1275
1330
|
assert.equal(cliRow.installed, true, 'OpenCode CLI is detected installed via config file');
|
|
1276
1331
|
} finally {
|
|
1277
|
-
|
|
1332
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1278
1333
|
}
|
|
1279
1334
|
});
|
|
1280
1335
|
|
|
@@ -1299,7 +1354,7 @@ test('clients add still refuses a missing gateway for writable clients', () => {
|
|
|
1299
1354
|
assert.match(parsed.error.message, /GATEWAY_EXE_MISSING|does not exist/i);
|
|
1300
1355
|
assert.equal(fs.existsSync(cursorConfig), false, 'failed validation must not create a client config');
|
|
1301
1356
|
} finally {
|
|
1302
|
-
|
|
1357
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1303
1358
|
}
|
|
1304
1359
|
});
|
|
1305
1360
|
|
|
@@ -1323,7 +1378,7 @@ test('getLauncher prefers the packaged gateway for Antigravity only', () => {
|
|
|
1323
1378
|
} finally {
|
|
1324
1379
|
if (previous === undefined) delete process.env.GENEXUS_MCP_GATEWAY_EXE;
|
|
1325
1380
|
else process.env.GENEXUS_MCP_GATEWAY_EXE = previous;
|
|
1326
|
-
|
|
1381
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1327
1382
|
}
|
|
1328
1383
|
});
|
|
1329
1384
|
|
|
@@ -1353,7 +1408,7 @@ test('clients add selects a direct gateway for Antigravity and npx for other cli
|
|
|
1353
1408
|
assert.equal(antigravity.command, 'npx.cmd', 'source checkouts without publish artifacts retain the fallback');
|
|
1354
1409
|
}
|
|
1355
1410
|
} finally {
|
|
1356
|
-
|
|
1411
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1357
1412
|
}
|
|
1358
1413
|
});
|
|
1359
1414
|
|
|
@@ -1385,7 +1440,7 @@ test('clients add uses the unified Antigravity config when it already exists', (
|
|
|
1385
1440
|
}
|
|
1386
1441
|
assert.equal(fs.existsSync(path.join(tempRoot, '.gemini', 'antigravity', 'mcp_config.json')), false);
|
|
1387
1442
|
} finally {
|
|
1388
|
-
|
|
1443
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1389
1444
|
}
|
|
1390
1445
|
});
|
|
1391
1446
|
|
|
@@ -1415,7 +1470,7 @@ test('clients add registers a client into a sandbox home with backup + atomic wr
|
|
|
1415
1470
|
const baks = fs.readdirSync(path.dirname(cursorCfg)).filter((f) => f.includes('.bak'));
|
|
1416
1471
|
assert.ok(baks.length >= 1, 'a .bak backup should be created before mutating');
|
|
1417
1472
|
|
|
1418
|
-
|
|
1473
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1419
1474
|
});
|
|
1420
1475
|
|
|
1421
1476
|
test('clients add tolerates a JSONC (commented) VS Code mcp.json', () => {
|
|
@@ -1439,7 +1494,7 @@ test('clients add tolerates a JSONC (commented) VS Code mcp.json', () => {
|
|
|
1439
1494
|
assert.ok(written.servers.genexus18mcp, 'genexus18mcp server entry written');
|
|
1440
1495
|
assert.ok(written.servers.foo, 'pre-existing server preserved');
|
|
1441
1496
|
|
|
1442
|
-
|
|
1497
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1443
1498
|
});
|
|
1444
1499
|
|
|
1445
1500
|
test('clients add preserves OpenCode 1.x direct mcp shape', () => {
|
|
@@ -1470,7 +1525,7 @@ test('clients add preserves OpenCode 1.x direct mcp shape', () => {
|
|
|
1470
1525
|
const row = JSON.parse(listed.stdout).ok.clients.find((client) => client.id === 'opencode');
|
|
1471
1526
|
assert.ok(row && row.registered, 'clients list should read the direct OpenCode entry');
|
|
1472
1527
|
|
|
1473
|
-
|
|
1528
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1474
1529
|
});
|
|
1475
1530
|
|
|
1476
1531
|
test('clients add preserves OpenCode v2 nested mcp.servers shape', () => {
|
|
@@ -1501,7 +1556,7 @@ test('clients add preserves OpenCode v2 nested mcp.servers shape', () => {
|
|
|
1501
1556
|
const row = JSON.parse(listed.stdout).ok.clients.find((client) => client.id === 'opencode');
|
|
1502
1557
|
assert.ok(row && row.registered, 'clients list should read the nested OpenCode entry');
|
|
1503
1558
|
|
|
1504
|
-
|
|
1559
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1505
1560
|
});
|
|
1506
1561
|
|
|
1507
1562
|
test('init auto-registers detected OpenCode in either config layout', () => {
|
|
@@ -1534,7 +1589,7 @@ test('init auto-registers detected OpenCode in either config layout', () => {
|
|
|
1534
1589
|
assert.equal(entry.environment?.GX_CONFIG_PATH, undefined);
|
|
1535
1590
|
assert.ok(label === 'nested' ? written.mcp.servers.other : written.mcp.other);
|
|
1536
1591
|
} finally {
|
|
1537
|
-
|
|
1592
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1538
1593
|
}
|
|
1539
1594
|
}
|
|
1540
1595
|
});
|
|
@@ -1559,7 +1614,7 @@ test('init with --global-config persists GX_CONFIG_PATH into client entry', () =
|
|
|
1559
1614
|
assert.ok(written.mcp.genexus18mcp);
|
|
1560
1615
|
assert.equal(written.mcp.genexus18mcp.environment.GX_CONFIG_PATH, path.join(kbDir, 'config.json'));
|
|
1561
1616
|
} finally {
|
|
1562
|
-
|
|
1617
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1563
1618
|
}
|
|
1564
1619
|
});
|
|
1565
1620
|
|
|
@@ -1581,7 +1636,7 @@ test('clients add with --global-config persists GX_CONFIG_PATH into client entry
|
|
|
1581
1636
|
assert.ok(written.mcp.genexus18mcp);
|
|
1582
1637
|
assert.equal(written.mcp.genexus18mcp.environment.GX_CONFIG_PATH, cfgPath);
|
|
1583
1638
|
} finally {
|
|
1584
|
-
|
|
1639
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1585
1640
|
}
|
|
1586
1641
|
});
|
|
1587
1642
|
|
|
@@ -1606,7 +1661,7 @@ test('init auto-registers detected OpenCode Desktop when its marker is present',
|
|
|
1606
1661
|
const written = JSON.parse(fs.readFileSync(openCodeCfg, 'utf8'));
|
|
1607
1662
|
assert.ok(written.mcp.genexus18mcp);
|
|
1608
1663
|
} finally {
|
|
1609
|
-
|
|
1664
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1610
1665
|
}
|
|
1611
1666
|
});
|
|
1612
1667
|
|
|
@@ -1629,7 +1684,7 @@ test('clients add replaces a legacy genexus18 entry instead of duplicating it',
|
|
|
1629
1684
|
assert.ok(written.mcpServers.genexus18mcp, 'new genexus18mcp entry present');
|
|
1630
1685
|
assert.equal(written.mcpServers.genexus18, undefined, 'legacy genexus18 removed (no duplicate)');
|
|
1631
1686
|
|
|
1632
|
-
|
|
1687
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1633
1688
|
});
|
|
1634
1689
|
|
|
1635
1690
|
test('clients list flags a registered command pointing at a missing launcher as stale (.bat too)', () => {
|
|
@@ -1649,7 +1704,7 @@ test('clients list flags a registered command pointing at a missing launcher as
|
|
|
1649
1704
|
assert.equal(cursor.commandStale, true, 'missing launcher => stale');
|
|
1650
1705
|
assert.ok(parsed.help.some((h) => h.includes('missing gateway exe')), 'help should call out the stale client');
|
|
1651
1706
|
|
|
1652
|
-
|
|
1707
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1653
1708
|
});
|
|
1654
1709
|
|
|
1655
1710
|
test('clients list separates a registered node launcher without an entrypoint from semantic validity', () => {
|
|
@@ -1680,7 +1735,7 @@ test('clients list separates a registered node launcher without an entrypoint fr
|
|
|
1680
1735
|
assert.equal(codex.commandStale, true, 'known invalid launcher is actionable through the existing stale flag');
|
|
1681
1736
|
assert.match(codex.commandStaleReason, /entrypoint/i);
|
|
1682
1737
|
} finally {
|
|
1683
|
-
|
|
1738
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1684
1739
|
}
|
|
1685
1740
|
});
|
|
1686
1741
|
|
|
@@ -1745,7 +1800,7 @@ test('clients list validates existing Gateway and known npx launchers locally',
|
|
|
1745
1800
|
assert.equal(node.commandStale, false);
|
|
1746
1801
|
assert.equal(parsed.ok.summary.semanticInvalid, 0);
|
|
1747
1802
|
} finally {
|
|
1748
|
-
|
|
1803
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1749
1804
|
}
|
|
1750
1805
|
});
|
|
1751
1806
|
|
|
@@ -1767,7 +1822,7 @@ test('clients list keeps an existing unrecognized launcher indeterminate', () =>
|
|
|
1767
1822
|
assert.equal(cursor.launcherSemanticState, 'unknown');
|
|
1768
1823
|
assert.equal(cursor.commandStale, false, 'unknown commands are not treated as invalid');
|
|
1769
1824
|
} finally {
|
|
1770
|
-
|
|
1825
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1771
1826
|
}
|
|
1772
1827
|
});
|
|
1773
1828
|
|
|
@@ -1797,7 +1852,7 @@ test('clients list marks an Antigravity launcher from an old package cache as st
|
|
|
1797
1852
|
assert.equal(antigravity.commandStale, true);
|
|
1798
1853
|
assert.match(antigravity.commandStaleReason, /different package gateway/);
|
|
1799
1854
|
} finally {
|
|
1800
|
-
|
|
1855
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1801
1856
|
}
|
|
1802
1857
|
});
|
|
1803
1858
|
|
|
@@ -1811,7 +1866,7 @@ test('readJsonFileSafe parses JSONC without corrupting string values containing
|
|
|
1811
1866
|
assert.ok(parsed, 'should parse');
|
|
1812
1867
|
assert.equal(parsed.servers.x.command, 'a, ]b', 'comma inside string value preserved');
|
|
1813
1868
|
assert.deepEqual(parsed.servers.x.args, ['c,]'], 'comma inside array string preserved');
|
|
1814
|
-
|
|
1869
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1815
1870
|
});
|
|
1816
1871
|
|
|
1817
1872
|
test('readJsonFileSafe strips a genuine trailing comma', () => {
|
|
@@ -1821,7 +1876,7 @@ test('readJsonFileSafe strips a genuine trailing comma', () => {
|
|
|
1821
1876
|
const parsed = readJsonFileSafe(f);
|
|
1822
1877
|
assert.deepEqual(parsed.a, [1, 2, 3]);
|
|
1823
1878
|
assert.deepEqual(parsed.b, { x: 1 });
|
|
1824
|
-
|
|
1879
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1825
1880
|
});
|
|
1826
1881
|
|
|
1827
1882
|
test('clients add without --clients is a usage error', () => {
|
|
@@ -1847,7 +1902,7 @@ test('clients remove drops the genexus entry (sandbox home)', () => {
|
|
|
1847
1902
|
assert.equal(written.mcpServers.genexus18mcp, undefined, 'genexus18mcp removed');
|
|
1848
1903
|
assert.equal(written.mcpServers.genexus18, undefined, 'legacy genexus18 also removed');
|
|
1849
1904
|
|
|
1850
|
-
|
|
1905
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1851
1906
|
});
|
|
1852
1907
|
|
|
1853
1908
|
test('clients remove drops both OpenCode config shapes and legacy key', () => {
|
|
@@ -1880,7 +1935,7 @@ test('clients remove drops both OpenCode config shapes and legacy key', () => {
|
|
|
1880
1935
|
assert.equal(written.mcp.servers.genexus18mcp, undefined);
|
|
1881
1936
|
assert.ok(written.mcp.servers.other, 'unrelated nested MCP server should be preserved');
|
|
1882
1937
|
|
|
1883
|
-
|
|
1938
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1884
1939
|
});
|
|
1885
1940
|
|
|
1886
1941
|
test('strict semver compares prerelease precedence and ignores build metadata', () => {
|
|
@@ -1988,7 +2043,7 @@ test('gateway passthrough remains intact when no AXI subcommand is used', () =>
|
|
|
1988
2043
|
assert.equal(result.status, 0);
|
|
1989
2044
|
assert.ok(result.stdout.includes('gateway:hello,world'));
|
|
1990
2045
|
|
|
1991
|
-
|
|
2046
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
1992
2047
|
});
|
|
1993
2048
|
|
|
1994
2049
|
test('stdio launcher persists the last child stderr when the gateway exits non-zero', () => {
|
|
@@ -2017,7 +2072,7 @@ test('stdio launcher persists the last child stderr when the gateway exits non-z
|
|
|
2017
2072
|
assert.match(log, /gateway bootstrap failed/);
|
|
2018
2073
|
assert.match(log, /second line/);
|
|
2019
2074
|
} finally {
|
|
2020
|
-
|
|
2075
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
2021
2076
|
}
|
|
2022
2077
|
});
|
|
2023
2078
|
|
|
@@ -2108,7 +2163,7 @@ test('clients add refuses to overwrite a third-party HTTP MCP server without --f
|
|
|
2108
2163
|
const after = JSON.parse(fs.readFileSync(cursorCfg, 'utf8'));
|
|
2109
2164
|
assert.equal(after.mcpServers.genexus18mcp.url, 'http://localhost:8001/mcp');
|
|
2110
2165
|
|
|
2111
|
-
|
|
2166
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
2112
2167
|
});
|
|
2113
2168
|
|
|
2114
2169
|
test('clients add with --force overwrites an existing third-party entry', () => {
|
|
@@ -2136,7 +2191,7 @@ test('clients add with --force overwrites an existing third-party entry', () =>
|
|
|
2136
2191
|
assert.equal(after.mcpServers.genexus18mcp.url, undefined);
|
|
2137
2192
|
assert.ok(after.mcpServers.genexus18mcp.command);
|
|
2138
2193
|
|
|
2139
|
-
|
|
2194
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
2140
2195
|
});
|
|
2141
2196
|
|
|
2142
2197
|
test('clients add supports custom --server-name across all formats for multi-MCP coexistence', () => {
|
|
@@ -2195,7 +2250,7 @@ test('clients add supports custom --server-name across all formats for multi-MCP
|
|
|
2195
2250
|
assert.ok(codexWritten.includes('[mcp_servers.genexus]'));
|
|
2196
2251
|
assert.ok(codexWritten.includes('[mcp_servers.Gx18byLennix]'));
|
|
2197
2252
|
|
|
2198
|
-
|
|
2253
|
+
removeTempPath(tempRoot, { recursive: true, force: true });
|
|
2199
2254
|
});
|
|
2200
2255
|
|
|
2201
2256
|
test('clients add rejects invalid --server-name characters with usage error', () => {
|
|
@@ -2250,7 +2305,7 @@ test('patchClientConfig reports backup failure without claiming that client patc
|
|
|
2250
2305
|
assert.deepEqual(result.failed, [{ client: 'OpenCode (CLI)', reason: 'backup denied' }]);
|
|
2251
2306
|
assert.equal(JSON.parse(fs.readFileSync(fixture.openCodeCfg, 'utf8')).mcp.genexus18mcp, undefined);
|
|
2252
2307
|
assert.ok(JSON.parse(fs.readFileSync(fixture.vscodeCfg, 'utf8')).servers.genexus18mcp);
|
|
2253
|
-
} finally {
|
|
2308
|
+
} finally { removeTempPath(fixture.tempRoot, { recursive: true, force: true }); }
|
|
2254
2309
|
});
|
|
2255
2310
|
|
|
2256
2311
|
test('patchClientConfig preserves earlier success when a later client write fails', () => {
|
|
@@ -2264,7 +2319,7 @@ test('patchClientConfig preserves earlier success when a later client write fail
|
|
|
2264
2319
|
assert.deepEqual(result.failed, [{ client: 'VS Code', reason: 'write denied' }]);
|
|
2265
2320
|
assert.ok(JSON.parse(fs.readFileSync(fixture.openCodeCfg, 'utf8')).mcp.genexus18mcp);
|
|
2266
2321
|
assert.equal(JSON.parse(fs.readFileSync(fixture.vscodeCfg, 'utf8')).servers.genexus18mcp, undefined);
|
|
2267
|
-
} finally {
|
|
2322
|
+
} finally { removeTempPath(fixture.tempRoot, { recursive: true, force: true }); }
|
|
2268
2323
|
});
|
|
2269
2324
|
|
|
2270
2325
|
test('patchClientConfig reports post-write read-back failure as partial state', () => {
|
|
@@ -2278,7 +2333,7 @@ test('patchClientConfig reports post-write read-back failure as partial state',
|
|
|
2278
2333
|
assert.deepEqual(result.failed, [{ client: 'OpenCode (CLI)', reason: 'post-write verification failed (genexus18mcp entry not found after write)' }]);
|
|
2279
2334
|
assert.ok(JSON.parse(fs.readFileSync(fixture.openCodeCfg, 'utf8')).mcp.genexus18mcp);
|
|
2280
2335
|
assert.ok(JSON.parse(fs.readFileSync(fixture.vscodeCfg, 'utf8')).servers.genexus18mcp);
|
|
2281
|
-
} finally {
|
|
2336
|
+
} finally { removeTempPath(fixture.tempRoot, { recursive: true, force: true }); }
|
|
2282
2337
|
});
|
|
2283
2338
|
|
|
2284
2339
|
test('patchClientConfig keeps a stale same-second backup and writes a distinct backup', () => {
|
|
@@ -2293,5 +2348,5 @@ test('patchClientConfig keeps a stale same-second backup and writes a distinct b
|
|
|
2293
2348
|
const backups = fs.readdirSync(path.dirname(fixture.openCodeCfg)).filter((name) => name.startsWith(path.basename(fixture.openCodeCfg)) && name.endsWith('.bak'));
|
|
2294
2349
|
assert.equal(backups.length, 2);
|
|
2295
2350
|
assert.ok(backups.some((name) => name !== path.basename(stale)));
|
|
2296
|
-
} finally {
|
|
2351
|
+
} finally { removeTempPath(fixture.tempRoot, { recursive: true, force: true }); }
|
|
2297
2352
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genexus-mcp",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.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": [
|
|
@@ -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.5.
|
|
10
|
+
"GxMcp.Gateway/3.5.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.5.
|
|
69
|
+
"GxMcp.Gateway/3.5.1": {
|
|
70
70
|
"type": "project",
|
|
71
71
|
"serviceable": false,
|
|
72
72
|
"sha512": ""
|
|
Binary file
|
|
Binary file
|
package/publish/config.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
2
|
+
"GatewayMode": "stdio-isolated",
|
|
3
|
+
"GeneXus": {
|
|
4
|
+
"InstallationPath": "C:\\Program Files (x86)\\GeneXus\\GeneXus18",
|
|
5
|
+
"WorkerExecutable": "worker\\\\GxMcp.Worker.exe"
|
|
5
6
|
},
|
|
7
|
+
"ConfigSchemaVersion": 2,
|
|
6
8
|
"Server": {
|
|
7
|
-
"HttpPort": 0,
|
|
8
9
|
"TerseResponses": true,
|
|
9
|
-
"
|
|
10
|
+
"SessionIdleTimeoutMinutes": 10,
|
|
11
|
+
"HttpPort": 0,
|
|
10
12
|
"WorkerIdleTimeoutMinutes": 5,
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
+
"McpStdio": true,
|
|
14
|
+
"EmitStructuredContent": false
|
|
13
15
|
},
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
"InstallationPath": "C:\\Program Files (x86)\\GeneXus\\GeneXus18",
|
|
17
|
-
"WorkerExecutable": "worker\\\\GxMcp.Worker.exe"
|
|
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.5.
|
|
4
|
-
"sourceCommit": "
|
|
3
|
+
"version": "3.5.1",
|
|
4
|
+
"sourceCommit": "90927fad376deac191b99b0697911f926342bb8d",
|
|
5
5
|
"sourceCommitPolicy": "exact-tag",
|
|
6
|
-
"generatedAtUtc": "2026-09-
|
|
6
|
+
"generatedAtUtc": "2026-09-15T03:23:14.0252619Z",
|
|
7
7
|
"runtime": {
|
|
8
8
|
"gateway": "net10.0-windows",
|
|
9
9
|
"worker": "net48-x86",
|
|
@@ -15,28 +15,33 @@
|
|
|
15
15
|
"2026-07-28"
|
|
16
16
|
],
|
|
17
17
|
"schema": "tool_definitions.json",
|
|
18
|
-
"schemaSha256": "
|
|
18
|
+
"schemaSha256": "9a158c22648e40301d5feca3e9702191369e126fd02e5c9f71264c69f338f270",
|
|
19
19
|
"provenance": "gxmcp-sbom.json",
|
|
20
20
|
"artifacts": [
|
|
21
21
|
{
|
|
22
22
|
"path": "gxmcp-sbom.json",
|
|
23
23
|
"size": 590,
|
|
24
|
-
"sha256": "
|
|
24
|
+
"sha256": "cee28293a4e7b478d81a39ab20f29ee01f85c163546155d24166fef87a421a0f"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"path": "GxMcp.Gateway.exe",
|
|
28
28
|
"size": 162304,
|
|
29
|
-
"sha256": "
|
|
29
|
+
"sha256": "eff8bbb15094b244dcf1db2d4a64ead18a5e4eee9636de309e0fa8bcc16907b3"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"path": "nexus-ide.vsix",
|
|
33
|
+
"size": 1318671,
|
|
34
|
+
"sha256": "0763e99319893511ba573c54289e5cba950c0c9a41a173e91fe965b0c007b781"
|
|
30
35
|
},
|
|
31
36
|
{
|
|
32
37
|
"path": "tool_definitions.json",
|
|
33
|
-
"size":
|
|
34
|
-
"sha256": "
|
|
38
|
+
"size": 113007,
|
|
39
|
+
"sha256": "9a158c22648e40301d5feca3e9702191369e126fd02e5c9f71264c69f338f270"
|
|
35
40
|
},
|
|
36
41
|
{
|
|
37
42
|
"path": "worker/GxMcp.Worker.exe",
|
|
38
|
-
"size":
|
|
39
|
-
"sha256": "
|
|
43
|
+
"size": 3123712,
|
|
44
|
+
"sha256": "f9e01a664a8fd87a0806b9ed801615d8493e79ed8ad3ef1ddf4d42e92498107a"
|
|
40
45
|
}
|
|
41
46
|
]
|
|
42
47
|
}
|
package/publish/gxmcp-sbom.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "gxmcp-provenance/1",
|
|
3
|
-
"version": "3.5.
|
|
4
|
-
"sourceCommit": "
|
|
3
|
+
"version": "3.5.1",
|
|
4
|
+
"sourceCommit": "90927fad376deac191b99b0697911f926342bb8d",
|
|
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": "
|
|
11
|
+
"sha256": "286fe5623068b5c165d0ad7089ed7538fc18f0f670afd46b2a9f9f7f04e6ca08"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"name": "nexus-ide",
|
|
15
15
|
"lockfile": "src/nexus-ide/package-lock.json",
|
|
16
16
|
"size": 274983,
|
|
17
|
-
"sha256": "
|
|
17
|
+
"sha256": "8b56243a09aa8bdb04cc2f70bdcb4ccdacf68f4b591129bb8874bd117730009f"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
20
20
|
}
|
|
Binary file
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
{"name":"genexus_lifecycle","description":"Build, validate, index, or poll the KB. action=build_all runs the native incremental Build All for the entire selected KB (ForceRebuild=false); it rejects target, refuses silent reorganization, and returns completion evidence. action=rebuild remains the forced Rebuild All path. action=specify runs Spec+Gen only. action=build with mode=compile_check checks targets plus transitive callers without KB-wide DeveloperMenu regeneration. Long operations are async with operationId. dryRun=true previews without executing. Build results carry generateEvidence and effective_status. See genexus://kb/tool-help/genexus_lifecycle for actions and target formats.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["build","build_all","cancel","specify","rebuild","reorg","reorg_preview","validate","validate-kb","sync","index","status","result","snapshots-list","snapshots-restore","inspect","reconcile"],"description":"Lifecycle action to perform."},"mode":{"type":"string","enum":["compile_check"],"description":"With action=build: compile_check spec+gen+compiles target(s) plus their transitive callers and SKIPS the KB-wide DeveloperMenu regen (the dominant build-all cost). Fast 'did my edit break the build?' check; requires target."},"target":{"type":"string","description":"Object name(s), taskId, job_id, or op:<id>. Build accepts CSV ('Foo,Bar'); build_all is KB-global and rejects target."},"code":{"type":"string","description":"validate: in-memory code to check without persisting."},"limit":{"type":"integer","description":"validate-kb: maximum number of objects/conditions to validate."},"snapshotPath":{"type":"string","description":"snapshots-restore: path to pattern snapshot to restore."},"estimated_seconds":{"type":"integer","description":"Build: <20 sync, >=20 async (default 60)."},"wait_seconds":{"type":"integer","description":"Status/build long-poll cap, 0-600s."},"wait_until_done":{"type":"boolean","description":"Build/build_all/rebuild: block in one turn until terminal (up to wait_seconds, default 600)."},"wait":{"type":"integer","description":"Status block, 0-600s. With a build taskId (or since): event-driven, returns on change. On index status with no since: blocks until index is Ready."},"since":{"type":"string","description":"Status: prior _meta.snapshot for chained waits."},"compact":{"type":"boolean","description":"Status: counts + top-10 errors (default true)."},"force":{"type":"boolean","description":"Index: full SDK rescan (clears snapshot)."},"dryRun":{"type":"boolean","default":false,"description":"build/build_all/rebuild/index: returns the plan without executing."},"environment":{"type":"string","description":"Build: target environment name/folder. Switches active environment before building."},"includeCallees":{"type":"string","enum":["none","direct","transitive"],"description":"Build: expand call graph so callees compile first (default transitive)."},"callers":{"type":"boolean","description":"compile_check: expand transitive callers of the target (default true). Set false for a target-only check when the target is a base transaction/BC with a huge caller closure."},"callerCap":{"type":"integer","description":"compile_check: max callers to pull into the check (default 40). Closure beyond this is truncated and flagged."},"deploy":{"type":"boolean","description":"Build: force the full deploy (Theme/Image/Style/Module copy to web/bin + WebAppConfig) so the built object is runnable, not just compiled. Default false (fast compile-only path). Slower; use when you need to run/preview the object."},"buildPlanCap":{"type":"integer","description":"Build: max nodes before BuildPlanTooLarge (default 200)."},"kb":{"type":"string","description":"KB alias."},"part":{"type":"string","description":"validate: specific object part to check (e.g. Source, Rules)."},"page":{"type":"integer","description":"status/result: 1-based page number for paginated results."},"pageSize":{"type":"integer","description":"status/result: page size for paginated results (default 50)."},"page_size":{"type":"integer","description":"status/result: snake_case alias for pageSize."},"notifyOnFailure":{"type":"string","description":"build: Slack/Discord webhook URL to notify on terminal build failure."},"skipFullDeploy":{"type":"boolean","description":"build: skip full deployment stage to speed up compile pass."},"fastIncremental":{"type":"boolean","description":"build: experimental fast incremental build opt-in."},"operationKey":{"type":"string","description":"inspect/reconcile: original idempotency key; journal lookup is hash-scoped to the resolved KB."},"operationTool":{"type":"string","description":"inspect/reconcile: canonical mutating tool that owns the idempotency key, e.g. genexus_edit."},"confirmed":{"type":"boolean","description":"reconcile: must be true after independently reading the affected target; this never authorizes a retry with the same key."},"verification":{"type":"string","description":"reconcile: short operator/model verification statement. Only a hash is persisted; source and KB content are never journaled."},"observedTargetIds":{"type":"array","items":{"type":"string"},"description":"reconcile: target names observed by an independent read; send one name as a single-item array. Must match the durable target fence."},"observedRevision":{"type":"string","description":"reconcile: revision/version observed by an independent read; must match the durable revision fence."}},"required":["action"],"examples":[{"action":"build"},{"action":"build_all"},{"action":"status","target":"op:abc123"},{"action":"build","mode":"compile_check","target":"MyObject"},{"action":"inspect","operationTool":"genexus_edit","operationKey":"edit_20260905_01"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"outputSchema":{"type":"object","description":"Structured lifecycle result. Action-specific fields remain additive; text content is preserved for legacy clients.","properties":{"status":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"},"error":{"type":"string"},"operationId":{"type":"string"},"job_id":{"type":"string"},"pollTarget":{"type":"string"},"isError":{"type":"boolean"}},"additionalProperties":true}},
|
|
12
12
|
{"name":"genexus_test","description":"Execute native GeneXus tests (GXtest).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"kb":{"type":"string","description":"KB alias."}},"required":["name"],"examples":[{"name":"MyTestProc"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},
|
|
13
13
|
{"name":"genexus_sdk_probe","description":"Read the GeneXus SDK surface. mode=surface dumps types/methods/props to docs/sdk-probe/; mode=capabilities returns an in-memory capability matrix with honest signature-probe status.","inputSchema":{"type":"object","properties":{"mode":{"type":"string","enum":["surface","capabilities"],"description":"surface (default) writes the diagnostic probe; capabilities is read-only and returns status/evidence."},"outputDir":{"type":"string","description":"Absolute path for mode=surface. Default <repo>/docs/sdk-probe/."}},"examples":[{},{"mode":"capabilities"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
14
|
-
{"name":"genexus_worker_reload","description":"Reload worker. modes: soft (drain+respawn), warm (restore validated index snapshot + soft respawn), hard (copy sourceDir+respawn). force=true: gateway kills directly. See tool-help.","inputSchema":{"type":"object","properties":{"mode":{"type":"string","enum":["soft","warm","hard"]},"force":{"type":"boolean","description":"Bypass drain; gateway kills + respawns. Use when worker is wedged."},"sourceDir":{"type":"string","description":"Required for mode=hard."},"drainTimeoutMs":{"type":"integer","description":"Soft only; default 30000."}},"examples":[{"mode":"soft"},{"mode":"hard","sourceDir":"C:\\Projetos\\Genexus18MCP\\src\\GxMcp.Worker\\bin\\Debug"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},
|
|
14
|
+
{"name":"genexus_worker_reload","description":"Reload worker. modes: soft (drain+respawn), warm (restore validated index snapshot + soft respawn), hard (copy sourceDir+respawn). force=true: gateway kills directly. See tool-help.","inputSchema":{"type":"object","properties":{"mode":{"type":"string","enum":["soft","warm","hard"]},"force":{"type":"boolean","description":"Bypass drain; gateway kills + respawns. Use when worker is wedged."},"sourceDir":{"type":"string","description":"Required for mode=hard."},"drainTimeoutMs":{"type":"integer","description":"Soft only; default 30000."},"alias":{"type":"string","description":"KB alias to reload when more than one worker is open."},"kb":{"type":"string","description":"Alias of alias."}},"examples":[{"mode":"soft"},{"mode":"hard","sourceDir":"C:\\Projetos\\Genexus18MCP\\src\\GxMcp.Worker\\bin\\Debug"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},
|
|
15
15
|
{"name":"genexus_connection_recover","description":"Self-healing recovery for a broken MCP connection (connection closed, unresponsive worker). Probes every open worker, kills and respawns only the unhealthy ones (force=true targets all), confirms SDK-ready, and clears the semantic cache. Use when calls hang or return WorkerBusy.","inputSchema":{"type":"object","properties":{"force":{"type":"boolean","default":false,"description":"Recover ALL workers, even responsive ones."}},"examples":[{},{"force":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},
|
|
16
16
|
{"name":"genexus_delete_object","description":"Delete an object from the KB through its native SDK identity. Irreversible — confirm=true required. Inspects native incoming references first; dryRun=true previews without deleting it.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"},"confirm":{"type":"boolean"},"dryRun":{"type":"boolean","default":false,"description":"When true, returns the resolved object and native references without deleting it."},"expectedVersion":{"type":"string","description":"Optional optimistic-concurrency token returned by a prior dry run."},"kb":{"type":"string","description":"KB alias."}},"required":["name","confirm"],"examples":[{"name":"ObsoletePanel","type":"WebPanel","confirm":true},{"name":"TemporaryDomain","type":"Domain","confirm":true,"dryRun":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false}},
|
|
17
17
|
{"name":"genexus_refactor","description":"Run GeneXus refactor: rename, extract procedure, extract local subroutine, or WWP condition set. For KB-wide rename with call-site patching (RenameObject/RenameAttribute), the response includes a patched-site count from RefactorService. dryRun=true previews what would change without persisting.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["RenameAttribute","RenameVariable","RenameObject","ExtractProcedure","ExtractSubroutine","WWPSetCondition"],"description":"Refactoring operation to execute; rename and extraction actions change KB source or metadata."},"target":{"type":"string","description":"Primary object or symbol to refactor."},"newName":{"type":"string","description":"New name for the renamed object, attribute, or variable."},"objectName":{"type":"string","description":"RenameVariable: target object name containing the variable."},"code":{"type":"string","description":"ExtractProcedure/ExtractSubroutine: code block to extract."},"procedureName":{"type":"string","description":"ExtractProcedure: name for the newly created Procedure."},"subroutineName":{"type":"string","description":"ExtractSubroutine: name for the newly created Subroutine."},"controlAttribute":{"type":"string","description":"WWPSetCondition: control or attribute name to attach condition."},"value":{"type":"string","description":"WWPSetCondition: condition expression to set."},"type":{"type":"string","description":"RenameObject: object type (e.g. WebPanel, Transaction, Procedure) to disambiguate when several objects share the name — e.g. a WebPanel vs the same-named Table behind a Transaction. Ignored by RenameAttribute/RenameVariable."},"dryRun":{"type":"boolean","default":false,"description":"When true, returns what would be changed without persisting."},"kb":{"type":"string","description":"KB alias."}},"required":["action"],"examples":[{"action":"RenameObject","target":"Customer","newName":"Client"},{"action":"RenameAttribute","target":"CustomerId","newName":"ClientId"},{"action":"ExtractSubroutine","target":"Customer","code":"&Total = 0","subroutineName":"ResetTotal"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
{"name":"genexus_browser","description":"Headless-browser verification umbrella. action=smoke (HTTP 200 + clean console), a11y (axe), wcag (caption/tooltip lint), capture (console/network/exceptions), cross (multi-engine), preview (render WebPanel + capture HTML/a11y/screenshot, optionally diff baseline). preview uses mode=render|run (default render; run auto-resolves the KB launcher, IDE F5 parity).","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["smoke","a11y","wcag","capture","cross","preview"],"description":"Browser verification operation to execute."},"name":{"type":"string","description":"WebPanel name."},"mode":{"type":"string","enum":["render","run"],"description":"preview only."},"capture":{"type":"array","items":{"type":"string"},"description":"capture|cross|preview channels: console|network|exceptions|html|a11y|screenshot."},"browsers":{"type":"array","items":{"type":"string","enum":["chrome","firefox","safari","webkit"]},"description":"cross only."},"parms":{"type":"object","description":"preview only."},"launcher":{"type":"string","description":"preview only."},"buildFirst":{"type":"boolean","description":"preview only."},"waitMs":{"type":"integer","description":"preview only."},"diffBaseline":{"type":"boolean","description":"preview only."},"updateBaseline":{"type":"boolean","description":"preview only."},"fill":{"type":"object","description":"preview only."},"click":{"type":"string","description":"preview only."},"auth":{"type":"object","description":"preview only."},"emulate":{"type":"string","enum":["iPhone12","iPhone15Pro","iPadPro","Pixel7","desktop1920","desktop1280"],"description":"preview only."},"network":{"type":"string","enum":["fast","slow3g","fast3g","offline"],"description":"preview only."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"preview","name":"WPMain"},{"action":"cross","name":"WPMain","browsers":["chrome","firefox"]}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},
|
|
41
41
|
{"name":"genexus_db","description":"Database umbrella. action: drift_check|drift_report (Transaction↔DB schema drift) | optimize_analyze|optimize_suggest|optimize_report (static index advisor) | sql_ddl|sql_navigation (SQL for Transaction/Procedure) | sample_data (fake INSERTs) | records_query|records_insert|records_update (typed application rows through Transaction metadata; writes default to dryRun; direct SQL only and no GeneXus business rules) | types_list|types_describe|types_validate (Domains/SDT constraints) | reorg_impact (timestamp/deep ImpactDatabase) | reorg_preview (non-mutating logical↔physical impact diff plus exact SQL only when a current artifact is available) | translations_import.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["drift_check","drift_report","optimize_analyze","optimize_suggest","optimize_report","sql_ddl","sql_navigation","sample_data","records_query","records_insert","records_update","types_list","types_describe","types_validate","translations_import","reorg_impact","reorg_preview"],"description":"Database operation. records_query is read-only; records_insert/records_update default to dryRun=true and require the matching single-use v2 preview token for persistence."},"deep":{"type":"boolean","description":"reorg_impact/reorg_preview only: run ISpecifierService.ImpactDatabase (specification, build-heavy) for the authoritative AnalysisResult. Default false (cheap timestamp heuristic)."},"name":{"type":"string","description":"Object/transaction/type name."},"trn":{"type":"string","description":"sample_data: Transaction name."},"transaction":{"type":"string","description":"records_*: root-level Transaction name."},"dataStore":{"type":"string","description":"records_*: existing active-environment datastore; defaults to the SDK default."},"where":{"type":"object","description":"records_query/update: equality filters only; update requires a filter."},"values":{"type":"object","description":"records_insert/update: typed attribute values."},"fields":{"type":"array","items":{"type":"string"},"description":"records_query: optional root-attribute projection."},"databaseManagedFields":{"type":"array","items":{"type":"string"},"description":"records_*: reviewed trigger-maintained fields; excludes keys and supplied values. Included in the preview token."},"limit":{"type":"integer","description":"records_query: 1-1000; default 100."},"expectedCount":{"type":"integer","description":"records_update: defaults to exactly one matched record."},"expectedVersion":{"type":"string","description":"records_*: v2 token from the matching write dry-run; query/v1 tokens cannot authorize writes."},"versionToken":{"type":"string","description":"records_*: alias of expectedVersion."},"dryRun":{"type":"boolean","default":true,"description":"records_insert/update: validate and reread the plan without writing."},"rollbackOnFailure":{"type":"boolean","default":true,"description":"records_*: default true; validation failures roll back before commit. Post-commit compensation is refused to preserve concurrent writers."},"timeoutSeconds":{"type":"integer","description":"records_*: command timeout, 1-60 seconds."},"rows":{"type":"integer","description":"sample_data: 1-1000; default 5."},"includeSubordinated":{"type":"boolean","description":"sql_ddl only."},"levelNumber":{"type":"integer","description":"sql_navigation only."},"includeExecutionPlan":{"type":"boolean","description":"sql_navigation."},"includeIndexAdvisor":{"type":"boolean","description":"sql_navigation."},"format":{"type":"string","enum":["json","markdown"],"description":"optimize_report only."},"kind":{"type":"string","enum":["domain","sdt","all"],"description":"types_list filter."},"value":{"type":"string","description":"types_validate."},"type":{"type":"string","description":"types_validate / disambiguator."},"inputPath":{"type":"string","description":"translations_import: CSV path."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"drift_check"},{"action":"sql_ddl","name":"Customer"},{"action":"reorg_preview","name":"Customer","deep":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
42
42
|
{"name":"genexus_versioning","description":"Versioning umbrella. action: history_list|history_get|history_save|history_restore (snapshot store; restore+discard=true is IDE 'Discard changes' parity) | undo (revert last N edits) | time_travel (recover bytes from a past git commit) | blame (git blame an object part) | diff (text: mode=textVsText|currentVsText) | diff_generated (against=last-build|git-head).","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["history_list","history_get","history_save","history_restore","undo","time_travel","blame","diff","diff_generated"],"description":"History, diff, time-travel, or rollback operation to perform."},"name":{"type":"string","description":"Object name for history, diff, time_travel, or blame."},"part":{"type":"string","description":"Object part name, e.g. Source, Rules, Events."},"partName":{"type":"string","description":"Legacy alias for part on history actions."},"versionId":{"type":"integer","description":"history_get/history_restore: numerical version ID."},"snapshot":{"type":"string","description":"history_restore: snapshot label or path to restore."},"discard":{"type":"boolean","description":"history_restore: true for IDE 'Discard changes' parity."},"dryRun":{"type":"boolean","description":"history_restore/undo: preview without mutating the KB."},"last":{"type":"integer","description":"undo: default 1, max 20."},"at":{"type":"string","description":"time_travel: ISO-8601 or commit sha."},"line":{"type":"integer","description":"blame: 1-based line number to annotate."},"filePath":{"type":"string","description":"blame: absolute or KB-relative file path."},"context":{"type":"integer","description":"blame: surrounding lines of context."},"mode":{"type":"string","enum":["textVsText","currentVsText"],"description":"diff."},"left":{"type":"string","description":"diff: left-side text or snapshot label."},"right":{"type":"string","description":"diff: right-side text or snapshot label."},"against":{"type":"string","enum":["last-build","git-head"],"description":"diff_generated."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"history_list","name":"Customer"},{"action":"time_travel","name":"Customer","at":"2026-05-20T10:00:00Z"}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},
|
|
43
|
-
{"name":"genexus_io","description":"IO umbrella for assets, Object Text exchange, screenshots, OCR. action: asset_find|asset_read|asset_write | export_part|import_part (single part) | export_kb_to_text|import_text_to_kb|validate_kb_text_files|delete_kb_objects (official GeneXus for Agents Object Text parity) | export_unified|screenshot_publish|ocr.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["asset_find","asset_read","asset_write","export_part","import_part","export_kb_to_text","import_text_to_kb","validate_kb_text_files","delete_kb_objects","export_unified","screenshot_publish","ocr"],"description":"Asset, single-part, batch Object Text, screenshot, or OCR operation."},"path":{"type":"string","description":"asset/screenshot_publish/ocr: file path; Object Text import/validate may use a manifest directory."},"name":{"type":"string","description":"single object name or comma-separated selector; export_unified uses one object."},"targets":{"type":"array","items":{},"description":"Object Text selector array. Items may be names or {name,type} objects. Omit for export_kb_to_text to export the full indexed KB."},"outputPath":{"type":"string"
|
|
43
|
+
{"name":"genexus_io","description":"IO umbrella for assets, Object Text exchange, screenshots, OCR. action: asset_find|asset_read|asset_write | read_blob | export_part|import_part (single part) | export_kb_to_text|import_text_to_kb|validate_kb_text_files|delete_kb_objects (official GeneXus for Agents Object Text parity) | export_unified|screenshot_publish|ocr.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["asset_find","asset_read","asset_write","read_blob","export_part","import_part","export_kb_to_text","import_text_to_kb","validate_kb_text_files","delete_kb_objects","export_unified","screenshot_publish","ocr"],"description":"Asset, single-part, batch Object Text, screenshot, or OCR operation."},"path":{"type":"string","description":"asset/screenshot_publish/ocr: file path; Object Text import/validate may use a manifest directory."},"name":{"type":"string","description":"single object name or comma-separated selector; export_unified uses one object."},"targets":{"type":"array","items":{},"description":"Object Text selector array. Items may be names or {name,type} objects. Omit for export_kb_to_text to export the full indexed KB."},"outputPath":{"type":"string"},"inputPath":{"type":"string","description":"import_part: source file; import_text_to_kb/validate_kb_text_files: manifest or directory."},"part":{"type":"string","description":"Object Text or part-exchange part name (e.g. Source, Rules, WebForm)."},"type":{"type":"string","description":"Object type disambiguator/filter."},"module":{"type":"string","description":"Object Text module filter."},"pathPrefix":{"type":"string","description":"Object Text KB object-path prefix filter."},"overwrite":{"type":"boolean"},"confirm":{"type":"boolean","description":"delete_kb_objects: required for a real delete; dryRun remains non-mutating."},"dryRun":{"type":"boolean","description":"Object Text import/delete preview without persisting changes."},"async":{"type":"boolean","description":"Run a batch Object Text operation as a cancellable background job when supported."},"pattern":{"type":"string","description":"asset_find: glob pattern."},"relativeRoot":{"type":"string","description":"asset_find: root for relative paths."},"limit":{"type":"integer","description":"asset_find/Object Text: max results."},"includeContent":{"type":"boolean","description":"asset_find: inline file content."},"maxBytes":{"type":"integer","description":"asset_read/read_blob: inline byte limit."},"includeBase64":{"type":"boolean"},"contentBase64":{"type":"string","description":"asset_write: base64-encoded file content."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"export_kb_to_text","outputPath":"C:\\tmp\\customer-text","targets":[{"name":"Customer","type":"Transaction"}]},{"action":"validate_kb_text_files","inputPath":"C:\\tmp\\customer-text"},{"action":"delete_kb_objects","targets":["Procedure:OldProc"],"dryRun":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
44
44
|
{"name":"genexus_variable","description":"Variables CRUD. add accepts varName or variables[] batch; modify changes type atomically. basedOn binds a Domain by native SDK identity. objectType=BusinessComponent with objectName+module binds a modular BC by EntityKey/GUID and verifies it after save. length/decimals override size; collection=true declares a collection; untyped add inherits a same-named attribute. dryRun validates and previews without mutation; async returns an operationId.","inputSchema":{"type":"object","required":["action","name"],"properties":{"action":{"type":"string","enum":["add","delete","modify"],"description":"Variable-part operation to add, delete, or modify a variable."},"name":{"type":"string"},"varName":{"type":"string","description":"Single-variable form (add/delete/modify). For batch add use variables[] instead."},"variables":{"type":"array","description":"Batch add only: one object per variable — {varName, typeName?, length?, decimals?, collection?}. Added before a single save.","items":{"type":"object","required":["varName"],"properties":{"varName":{"type":"string"},"typeName":{"type":"string"},"length":{"type":"integer"},"decimals":{"type":"integer"},"collection":{"type":"boolean"},"basedOn":{"type":"string","description":"Domain name; native SDK reference."}}}},"typeName":{"type":"string","description":"Primitive (Character/Numeric/VarChar/Date/...), SDT/BC/Domain name, or WebSession. VarChar keeps VARCHAR semantics."},"objectType":{"type":"string","enum":["BusinessComponent"],"description":"add/modify: resolve an object-backed variable natively instead of interpreting typeName as a Domain."},"objectName":{"type":"string","description":"Business Component Transaction name, without module when module is passed separately."},"module":{"type":"string","description":"Module owning objectName. Required to disambiguate modular Business Components."},"expectedVersion":{"type":"string","description":"Optimistic-concurrency token for an object-backed add/modify. Stale state returns VersionConflict before mutation."},"length":{"type":"integer","description":"add/modify optional: overrides the length parsed from typeName."},"decimals":{"type":"integer","description":"add/modify optional: overrides decimals parsed from typeName."},"collection":{"type":"boolean","description":"add/modify optional: declare the variable as a collection."},"basedOn":{"type":"string","description":"modify optional: Domain."},"dryRun":{"type":"boolean","default":false,"description":"Validate and return the typed diff without changing any object, Attribute, hash, or version."},"validationMode":{"type":"string","enum":["specify"],"description":"Explicitly run the inline Specify pass after a legacy variable write. Never runs unless requested."},"rollbackOnFailure":{"type":"boolean","default":true,"description":"Object-backed variables always snapshot the complete owner and restore it on save/re-read divergence."},"async":{"type":"boolean","description":"Run as a background job. Result returns operationId/job_id for genexus_lifecycle polling."},"estimated_seconds":{"type":"integer","description":"When async=true, expected runtime used for progress/job metadata (default 30)."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"add","name":"MyPanel","varName":"&Choice","typeName":"Numeric"},{"action":"add","name":"MyProc","varName":"&OrderRecord","objectType":"BusinessComponent","objectName":"OrderRecord","module":"Operations","dryRun":true},{"action":"add","name":"MyProc","variables":[{"varName":"&ApiKey","typeName":"VarChar","length":80},{"varName":"&CmdNro","typeName":"Numeric","length":9}]},{"action":"delete","name":"MyPanel","varName":"&OldVar"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
45
45
|
{"name":"genexus_telemetry","description":"Observability umbrella. action: executions (recent invocations) | watch_event (ops mentioning an event) | friction_append|friction_tail (.gx/friction.jsonl) | learning_report (aggregate friction) | logs (worker_debug.log tail) | profile_analyze|profile_hotspots|profile_correlate (GeneXus profiler XML).","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["executions","watch_event","friction_append","friction_tail","learning_report","logs","profile_analyze","profile_hotspots","profile_correlate"],"description":"Telemetry, profiling, or friction-log operation to perform."},"target":{"type":"string","description":"executions/watch_event: object name to filter."},"event":{"type":"string","description":"watch_event: event name to match in operation payload."},"last":{"type":"integer","description":"executions/watch_event: max results."},"tool":{"type":"string","description":"friction_append: tool name that produced the friction entry."},"message":{"type":"string","description":"friction_append: description of the friction encountered."},"severity":{"type":"string","enum":["info","warn","error","critical"],"description":"friction_append: severity level."},"n":{"type":"integer","description":"friction_tail: number of recent lines to return."},"since":{"type":"string","description":"learning_report/logs: ISO-8601 start time filter."},"until":{"type":"string","description":"learning_report: ISO-8601 end time filter."},"tail":{"type":"integer","description":"logs: number of tail lines from worker_debug.log."},"filterCorrelation":{"type":"string","description":"logs: filter by correlation/operation ID."},"grep":{"type":"string","description":"logs: substring filter on log lines."},"path":{"type":"string","description":"profile_*: path to the GeneXus profiler XML file."},"top":{"type":"integer","description":"profile_hotspots: top-N hottest procedures to return."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"executions"},{"action":"friction_tail","n":20}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
46
46
|
{"name":"genexus_create","description":"Creation umbrella. action: object (Transaction/Procedure/WebPanel/SDT/API/Domain/Dashboard/Folder/Module; type='API' scaffolds a REST API object — routes use ONE HTTP-verb block per object 'Verb { route => Object; }' (mixing Get+Post blocks is unsupported); type='Folder'/'Module' create containers, and folder=<name>/module=<name> place the new object there (created in Root, then moved+verified); SDT takes optional firstItem/firstItemType; Domain takes dataType+length or enumValues; NOT WWP — use genexus_apply_pattern) | popup (popup WebPanel with Form type='layout' body) | sd_panel_create|sd_panel_inspect|sd_panel_edit | save_as (clone parts) | scaffold|translate|sample (forge) | template (kpi_header|empty_state|confirm_dialog) | object_atomic (issue #62: all-or-nothing create/update of an object with variables[], rules[], parms[], properties{} and source in ONE validated call — pre-validates every field before the first save, composes the SDK write primitives, compensates on failure, dryRun previews, validate=true runs the inline Specify pass, update mode guards concurrent writes via expectedVersion). | curl_procedure imports a REST-consumer Procedure from a curl command through the GeneXus SDK.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["object","object_atomic","popup","sd_panel_create","sd_panel_inspect","sd_panel_edit","save_as","scaffold","translate","sample","template","curl_procedure"],"description":"Object-creation or scaffold operation to perform."},"curl":{"type":"string","description":"curl_procedure: the curl command to import as a Procedure."},"type":{"type":"string","description":"Object type to create (Transaction, Procedure, WebPanel, SDT, API, Domain, Dashboard, Folder, Module)."},"name":{"type":"string","description":"Name of the object to create, clone, or update."},"newName":{"type":"string","description":"save_as."},"description":{"type":"string","description":"Human-readable description / title of the object."},"dataType":{"type":"string","description":"object Domain."},"length":{"type":"integer","description":"object Domain."},"decimals":{"type":"integer","description":"object Domain."},"signed":{"type":"boolean","description":"object Domain."},"basedOn":{"type":"string","description":"object Domain."},"enumValues":{"type":"array","items":{"type":"object"},"description":"object Domain enum."},"firstItem":{"type":"string","description":"object SDT optional: name of the first structure item to seed (SDK requires ≥1). Defaults to Item1. Use to avoid the throwaway Item1."},"firstItemType":{"type":"string","description":"object SDT optional: type of the seeded first item (Character/Numeric/VarChar/…). Defaults to VarChar."},"folder":{"type":"string","description":"object: Folder destination (created in Root, then moved+verified)."},"module":{"type":"string","description":"object: Module destination (created in Root, then moved+verified)."},"parentPath":{"type":"string","description":"object: folder/module destination (fallback when folder/module absent)."},"mode":{"type":"string","enum":["create","update","auto"],"description":"object_atomic: create (fail if exists), update (fail if missing), auto (default — create or update by existence)."},"variables":{"type":"array","items":{"type":"object","required":["varName"],"properties":{"varName":{"type":"string"},"typeName":{"type":"string"},"length":{"type":"integer"},"decimals":{"type":"integer"},"collection":{"type":"boolean"},"name":{"type":"string"},"basedOn":{"type":"string"}}},"description":"object_atomic: one object per variable — {varName, typeName?, length?, decimals?, collection?}. Validated before any save; errors are attributed to variables[N]."},"rules":{"type":"array","items":{"type":"string"},"description":"object_atomic: rule lines (e.g. \"Parm(in:&Id);\"). Send actual line breaks; literal backslash sequences \\r\\n, \\n, or \\r are rejected before the first save."},"parms":{"type":"array","items":{"type":"string"},"description":"object_atomic: parameter entries rendered into a Parm rule — bare names (\"&Id\") or prefixed (\"out:&Msg\")."},"source":{"type":"string","description":"object_atomic: Source part text. Send actual line breaks; literal backslash sequences \\r\\n, \\n, or \\r are rejected before the first save."},"properties":{"type":"object","description":"object_atomic: object-level properties applied via the genexus_properties path (propertyName → value)."},"expectedVersion":{"type":"string","description":"object_atomic update: optimistic concurrency token returned as `version` by a prior atomic create/update/read; a mismatch fails with ConcurrentModification instead of overwriting concurrent changes."},"validate":{"type":"boolean","description":"object_atomic: run the inline Specify pass (Spec+Gen) before confirming success. On failure with rollbackOnFailure=true, a freshly-created object is deleted so the operation stays all-or-nothing."},"validationMode":{"type":"string","enum":["specify"],"description":"Issue #60: with action=object, run the inline Specify pass (Spec+Gen, no Compile) after creation, returning structured spc*/gen* diagnostics in the same call. Combines with rollbackOnFailure."},"rollbackOnFailure":{"type":"boolean","description":"Issue #60: with action=object and validationMode=specify, roll back the created object when the specify pass reports errors. A fresh object has no pre-write snapshot, so rollback reports rolledBack=false — delete it via genexus_delete_object if spec fails."},"spec":{"type":"object","description":"popup."},"part":{"type":"string","description":"sd_panel_edit/template."},"content":{"type":"string","description":"sd_panel_edit/scaffold/translate."},"includePatternInstance":{"type":"boolean","description":"save_as."},"overwrite":{"type":"boolean","description":"save_as."},"dryRun":{"type":"boolean","description":"popup/save_as/template/object."},"template":{"type":"string","enum":["kpi_header","empty_state","confirm_dialog"],"description":"template."},"args":{"type":"object","description":"template."},"kb":{"type":"string","description":"KB alias."},"baseVersion":{"type":"string","description":"Optimistic concurrency versionToken for atomic create/update."},"updateExisting":{"type":"boolean","default":false,"description":"object_atomic: allow updating existing object if it already exists."}},"examples":[{"action":"object","name":"NewPanel","type":"WebPanel"},{"action":"object","name":"CustomerApi","type":"API"},{"action":"popup","name":"MyPopup","spec":{"host":"Customer"}}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
Binary file
|