genexus-mcp 2.41.9 → 2.41.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -9
- package/cli/lib/config.js +75 -12
- package/cli/run.test.js +182 -0
- 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 +6 -6
- package/publish/tool_definitions.json +5 -4
- package/publish/worker/GxMcp.Worker.exe +0 -0
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ In practice: you point the MCP at your KB, then ask your AI assistant things lik
|
|
|
20
20
|
|
|
21
21
|
## What you can do with it
|
|
22
22
|
|
|
23
|
-
A quick map of what the agent can do against your real KB through the **
|
|
23
|
+
A quick map of what the agent can do against your real KB through the **48 tools** (details in [Tool Surface](#tool-surface)):
|
|
24
24
|
|
|
25
25
|
| Area | What the agent can do |
|
|
26
26
|
|---|---|
|
|
@@ -86,7 +86,7 @@ What you'll see (takes ~30 seconds first time, faster on re-runs):
|
|
|
86
86
|
|
|
87
87
|
### Step 2 — Register the MCP in your AI client
|
|
88
88
|
|
|
89
|
-
Step 1 auto-registers
|
|
89
|
+
Step 1 auto-registers every supported client it detects, including Claude Desktop, Claude Code, Cursor, Antigravity, Gemini CLI, OpenCode, Codex CLI, and VS Code. If yours wasn't detected, copy the JSON snippet from Step 1 into your client's MCP config manually. See the [client setup guide](TROUBLESHOOTING.md#client-setup) if unsure where that file lives.
|
|
90
90
|
|
|
91
91
|
### Step 3 — Restart your AI client, then test
|
|
92
92
|
|
|
@@ -95,6 +95,8 @@ This part trips most people: **fully close** your AI client and reopen it. Not j
|
|
|
95
95
|
- **Claude Desktop**: right-click the system-tray icon → **Quit**. Then launch it again. (Closing the window is not enough.)
|
|
96
96
|
- **Claude Code**: end the session and start a fresh one.
|
|
97
97
|
- **Cursor / Antigravity**: close all windows and reopen.
|
|
98
|
+
- **OpenCode**: fully quit and reopen it so it reloads `opencode.json` / `opencode.jsonc`.
|
|
99
|
+
- **Gemini CLI / Codex CLI**: start a new process or session.
|
|
98
100
|
|
|
99
101
|
Then paste this prompt:
|
|
100
102
|
|
|
@@ -205,7 +207,7 @@ Auto-detected and auto-configured by the installer:
|
|
|
205
207
|
| Cursor | ✅ | Restart required |
|
|
206
208
|
| Antigravity | ✅ | Restart required; detected even before its MCP config exists |
|
|
207
209
|
| Gemini CLI | ✅ | — |
|
|
208
|
-
| OpenCode (CLI) | ✅ | Reads
|
|
210
|
+
| OpenCode (CLI) | ✅ | Reads both direct and nested MCP layouts; restart required |
|
|
209
211
|
| Codex CLI | ✅ | Writes `~/.codex/config.toml` |
|
|
210
212
|
| VS Code / VS Code Insiders | ✅ | Native MCP (`User/mcp.json`); restart required |
|
|
211
213
|
| OpenCode Desktop | Detect-only | Reported as installed; add the server from the app's settings |
|
|
@@ -234,7 +236,7 @@ Still stuck? [Open an issue](https://github.com/lennix1337/Genexus18MCP/issues)
|
|
|
234
236
|
|
|
235
237
|
## Tool Surface
|
|
236
238
|
|
|
237
|
-
The worker exposes **
|
|
239
|
+
The worker exposes **48 tools** to the MCP router, grouped by capability below. Most are umbrellas with an `action` (e.g. `genexus_db action=sql_ddl`); the detailed schemas live in [`src/GxMcp.Gateway/tool_definitions.json`](src/GxMcp.Gateway/tool_definitions.json).
|
|
238
240
|
|
|
239
241
|
**Orientation & health**
|
|
240
242
|
- `genexus_whoami` — KB context, version, worker/index/database health, self-update check, next-step hints
|
|
@@ -268,7 +270,8 @@ produced by `DataSelectorStructurePart.ToString()` on U16.
|
|
|
268
270
|
- `genexus_edit_form` — semantic WebForm edits
|
|
269
271
|
- `genexus_variable` — Variables-part CRUD
|
|
270
272
|
- `genexus_create` — creation umbrella (Transaction, Procedure, Domain, SDT, API, Folder, Module, `curl_procedure` = scaffold a Procedure from a curl command, …); `object_atomic` authors definition + variables + Rules + properties + Source with preflight/read-back/rollback
|
|
271
|
-
- `
|
|
273
|
+
- `genexus_data_view` — atomically create/inspect/update/delete a root-only Business Component Transaction mapped through a native Data View to an existing physical table; validates attributes/keys first, supports optimistic versions and true no-mutation dry-runs, requires `confirm=true` for destructive delete, and reports commit/verification state separately
|
|
274
|
+
- `genexus_delete_object` — delete an object by native SDK identity; use `dryRun=true` to inspect incoming references before `confirm=true`
|
|
272
275
|
- `genexus_format` — format a code snippet with the worker's rules
|
|
273
276
|
|
|
274
277
|
**Data model & structure authoring**
|
|
@@ -427,21 +430,37 @@ Once you declare more than one KB in `Environment.KBs[]`, every tool accepts an
|
|
|
427
430
|
```
|
|
428
431
|
|
|
429
432
|
Resolution rules when `kb` is omitted:
|
|
430
|
-
-
|
|
431
|
-
-
|
|
432
|
-
-
|
|
433
|
+
- an explicit `kb` always wins; use it for parallel work or when a prompt touches more than one KB
|
|
434
|
+
- each MCP session snapshots the configured `DefaultKb` at `initialize`; `set_default` changes the current session and persists the startup fallback for future sessions
|
|
435
|
+
- `open` only starts/registers a Worker; it does not silently change another session's target. Select it with `set_default`, or pass `kb` explicitly
|
|
436
|
+
- exactly 1 KB open → uses that KB when the session has no selection
|
|
437
|
+
- 2+ KBs open with no session selection → server returns `KB_AMBIGUOUS`; choose one with `set_default` or pass `kb` explicitly
|
|
433
438
|
|
|
434
439
|
Manage the pool at runtime:
|
|
435
440
|
|
|
436
441
|
```jsonc
|
|
437
442
|
{ "tool": "genexus_kb", "arguments": { "action": "list" } }
|
|
438
|
-
// → { openKbs: [{alias, path, pid, workingSetMB, idleSeconds}], maxOpenKbs, defaultKb, declaredKbs }
|
|
443
|
+
// → { selectedKb, activeKb, openKbs: [{alias, path, pid, workingSetMB, idleSeconds}], knownKbs, maxOpenKbs, defaultKb, declaredKbs }
|
|
439
444
|
|
|
440
445
|
{ "tool": "genexus_kb", "arguments": { "action": "open", "alias": "adhoc", "path": "C:/KBs/ScratchKB" } }
|
|
441
446
|
{ "tool": "genexus_kb", "arguments": { "action": "close", "alias": "legacy" } }
|
|
442
447
|
{ "tool": "genexus_kb", "arguments": { "action": "set_default", "alias": "main" } } // persists to config.json
|
|
443
448
|
```
|
|
444
449
|
|
|
450
|
+
For OpenCode, call `genexus_whoami` once at the start of a session. Use
|
|
451
|
+
`kb.selected`, `kb.default`, `kb.openKbs`, `kb.knownKbs`, and `kb.declaredKbs`
|
|
452
|
+
to understand the target, then select the normal working KB with
|
|
453
|
+
`genexus_kb action=set_default`. Every KB-bound response also includes `kbAlias`
|
|
454
|
+
in its JSON payload, which lets OpenCode correlate text-only responses. Keep
|
|
455
|
+
`kb=<alias>` on calls that intentionally compare or update another KB.
|
|
456
|
+
|
|
457
|
+
The installer registers both OpenCode configuration layouts: the legacy direct
|
|
458
|
+
`mcp.genexus` entry used by OpenCode 1.x and the current `mcp.servers.genexus`
|
|
459
|
+
layout. `clients add --clients opencode` is only needed to repair or explicitly
|
|
460
|
+
re-register a client after installation; normal `init` handles detected clients
|
|
461
|
+
automatically. Restart OpenCode after a registration so it reloads the MCP
|
|
462
|
+
configuration.
|
|
463
|
+
|
|
445
464
|
When the pool is full and no Worker is idle, the server returns `KB_POOL_FULL` — close one explicitly or raise `Server.MaxOpenKbs`. Each Worker carries the SDK in its own process (~200–400 MB idle, up to 1–2 GB on heavy KBs), so size the pool against available RAM.
|
|
446
465
|
|
|
447
466
|
### Architecture
|
package/cli/lib/config.js
CHANGED
|
@@ -413,6 +413,7 @@ function createConfigFile(kbPath, gxPath) {
|
|
|
413
413
|
if (existing && existing.Environment) {
|
|
414
414
|
if (existing.Environment.KBs) preservedEnv.KBs = existing.Environment.KBs;
|
|
415
415
|
if (existing.Environment.ActiveKb) preservedEnv.ActiveKb = existing.Environment.ActiveKb;
|
|
416
|
+
if (existing.Environment.DefaultKb) preservedEnv.DefaultKb = existing.Environment.DefaultKb;
|
|
416
417
|
}
|
|
417
418
|
const nextConfig = {
|
|
418
419
|
...baseConfig,
|
|
@@ -868,7 +869,23 @@ function removeVsCodeServersJson(filePath) {
|
|
|
868
869
|
return true;
|
|
869
870
|
}
|
|
870
871
|
|
|
871
|
-
// OpenCode
|
|
872
|
+
// OpenCode 1.x uses `mcp.<name>`, while the current v2 config nests servers under
|
|
873
|
+
// `mcp.servers.<name>`. Keep the shape already present in the user's config so an
|
|
874
|
+
// upgrade does not silently move or disable their other MCP servers.
|
|
875
|
+
function getOpenCodeMcpContainer(cfgObj) {
|
|
876
|
+
if (!cfgObj.mcp || typeof cfgObj.mcp !== 'object' || Array.isArray(cfgObj.mcp)) {
|
|
877
|
+
cfgObj.mcp = {};
|
|
878
|
+
}
|
|
879
|
+
const nested = cfgObj.mcp.servers
|
|
880
|
+
&& typeof cfgObj.mcp.servers === 'object'
|
|
881
|
+
&& !Array.isArray(cfgObj.mcp.servers);
|
|
882
|
+
return {
|
|
883
|
+
mcp: cfgObj.mcp,
|
|
884
|
+
servers: nested ? cfgObj.mcp.servers : cfgObj.mcp,
|
|
885
|
+
nested: Boolean(nested)
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
|
|
872
889
|
function applyOpenCodeJson(filePath, launcher, targetConfigPath) {
|
|
873
890
|
const parsed = fs.existsSync(filePath) ? readJsonFileSafe(filePath) : {};
|
|
874
891
|
if (parsed === null) throw new Error('Invalid JSON');
|
|
@@ -876,14 +893,20 @@ function applyOpenCodeJson(filePath, launcher, targetConfigPath) {
|
|
|
876
893
|
// OpenCode configs carry a top-level $schema for editor validation; set it when
|
|
877
894
|
// absent (new file or a config that never had one) without clobbering a custom one.
|
|
878
895
|
if (!cfgObj.$schema) cfgObj.$schema = 'https://opencode.ai/config.json';
|
|
879
|
-
|
|
880
|
-
|
|
896
|
+
const { mcp, servers, nested } = getOpenCodeMcpContainer(cfgObj);
|
|
897
|
+
servers.genexus = {
|
|
881
898
|
type: 'local',
|
|
882
899
|
command: [launcher.command, ...(launcher.args || [])],
|
|
883
900
|
environment: { GX_CONFIG_PATH: targetConfigPath },
|
|
884
|
-
enabled: true
|
|
901
|
+
...(nested ? { disabled: false } : { enabled: true })
|
|
885
902
|
};
|
|
886
|
-
if (
|
|
903
|
+
if (servers.genexus18) delete servers.genexus18;
|
|
904
|
+
// If a config was migrated manually and contains both shapes, leave unrelated
|
|
905
|
+
// servers alone but remove our duplicate legacy entry.
|
|
906
|
+
if (nested) {
|
|
907
|
+
if (mcp.genexus) delete mcp.genexus;
|
|
908
|
+
if (mcp.genexus18) delete mcp.genexus18;
|
|
909
|
+
}
|
|
887
910
|
writeClientJson(filePath, cfgObj);
|
|
888
911
|
}
|
|
889
912
|
|
|
@@ -891,8 +914,23 @@ function removeOpenCodeJson(filePath) {
|
|
|
891
914
|
const parsed = readJsonFileSafe(filePath);
|
|
892
915
|
if (parsed === null) throw new Error('Invalid JSON');
|
|
893
916
|
const cfgObj = parsed || {};
|
|
894
|
-
if (!cfgObj.mcp ||
|
|
895
|
-
|
|
917
|
+
if (!cfgObj.mcp || typeof cfgObj.mcp !== 'object') return false;
|
|
918
|
+
let removedAny = false;
|
|
919
|
+
for (const key of ['genexus', 'genexus18']) {
|
|
920
|
+
if (cfgObj.mcp[key]) {
|
|
921
|
+
delete cfgObj.mcp[key];
|
|
922
|
+
removedAny = true;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (cfgObj.mcp.servers && typeof cfgObj.mcp.servers === 'object' && !Array.isArray(cfgObj.mcp.servers)) {
|
|
926
|
+
for (const key of ['genexus', 'genexus18']) {
|
|
927
|
+
if (cfgObj.mcp.servers[key]) {
|
|
928
|
+
delete cfgObj.mcp.servers[key];
|
|
929
|
+
removedAny = true;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (!removedAny) return false;
|
|
896
934
|
writeClientJson(filePath, cfgObj);
|
|
897
935
|
return true;
|
|
898
936
|
}
|
|
@@ -993,7 +1031,7 @@ function readClientCommandEntry(client) {
|
|
|
993
1031
|
if (client.format === 'opencode') {
|
|
994
1032
|
const parsed = readJsonFileSafe(client.path);
|
|
995
1033
|
if (!parsed || typeof parsed !== 'object') return null;
|
|
996
|
-
const entry = parsed.mcp
|
|
1034
|
+
const entry = parsed.mcp?.servers?.genexus || parsed.mcp?.genexus;
|
|
997
1035
|
if (!entry || !Array.isArray(entry.command) || entry.command.length === 0) return null;
|
|
998
1036
|
return { command: entry.command[0], args: entry.command.slice(1) };
|
|
999
1037
|
}
|
|
@@ -1044,14 +1082,34 @@ function readGeneXusVersionFromInstall(gxPath) {
|
|
|
1044
1082
|
return null;
|
|
1045
1083
|
}
|
|
1046
1084
|
|
|
1085
|
+
function normalizeKbCatalog(raw) {
|
|
1086
|
+
if (Array.isArray(raw)) {
|
|
1087
|
+
const normalized = {};
|
|
1088
|
+
for (const entry of raw) {
|
|
1089
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
1090
|
+
const name = typeof entry.alias === 'string' ? entry.alias : entry.Alias;
|
|
1091
|
+
const kbPath = typeof entry.path === 'string' ? entry.path : entry.Path;
|
|
1092
|
+
if (typeof name === 'string' && name && typeof kbPath === 'string' && kbPath) {
|
|
1093
|
+
normalized[name] = kbPath;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return normalized;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
return raw && typeof raw === 'object' ? raw : {};
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1047
1102
|
function readKbCatalog(configPath) {
|
|
1048
1103
|
if (!configPath) return { kbs: {}, activeKb: null, kbPath: null };
|
|
1049
1104
|
const cfg = readJsonFileSafe(configPath);
|
|
1050
1105
|
if (!cfg) return { kbs: {}, activeKb: null, kbPath: null };
|
|
1051
1106
|
const env = cfg.Environment || {};
|
|
1107
|
+
const activeKb = typeof env.ActiveKb === 'string' && env.ActiveKb
|
|
1108
|
+
? env.ActiveKb
|
|
1109
|
+
: (typeof env.DefaultKb === 'string' && env.DefaultKb ? env.DefaultKb : null);
|
|
1052
1110
|
return {
|
|
1053
|
-
kbs: (env.KBs
|
|
1054
|
-
activeKb
|
|
1111
|
+
kbs: normalizeKbCatalog(env.KBs),
|
|
1112
|
+
activeKb,
|
|
1055
1113
|
kbPath: typeof env.KBPath === 'string' ? env.KBPath : null
|
|
1056
1114
|
};
|
|
1057
1115
|
}
|
|
@@ -1060,8 +1118,13 @@ function writeKbCatalog(configPath, { kbs, activeKb, kbPath }) {
|
|
|
1060
1118
|
const cfg = readJsonFileSafe(configPath) || {};
|
|
1061
1119
|
cfg.Environment = cfg.Environment || {};
|
|
1062
1120
|
cfg.Environment.KBs = kbs;
|
|
1063
|
-
if (activeKb)
|
|
1064
|
-
|
|
1121
|
+
if (activeKb) {
|
|
1122
|
+
cfg.Environment.ActiveKb = activeKb;
|
|
1123
|
+
cfg.Environment.DefaultKb = activeKb;
|
|
1124
|
+
} else {
|
|
1125
|
+
delete cfg.Environment.ActiveKb;
|
|
1126
|
+
delete cfg.Environment.DefaultKb;
|
|
1127
|
+
}
|
|
1065
1128
|
if (kbPath) cfg.Environment.KBPath = kbPath;
|
|
1066
1129
|
else delete cfg.Environment.KBPath;
|
|
1067
1130
|
writeFileAtomic(configPath, JSON.stringify(cfg, null, 2));
|
package/cli/run.test.js
CHANGED
|
@@ -285,6 +285,61 @@ test('kb list shows the KB auto-registered by init', () => {
|
|
|
285
285
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
286
286
|
});
|
|
287
287
|
|
|
288
|
+
test('kb list reads gateway-style KB arrays and DefaultKb', () => {
|
|
289
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-test-'));
|
|
290
|
+
const kbA = path.join(tempRoot, 'kb-array-a');
|
|
291
|
+
const kbB = path.join(tempRoot, 'kb-array-b');
|
|
292
|
+
fs.mkdirSync(kbA, { recursive: true });
|
|
293
|
+
fs.mkdirSync(kbB, { recursive: true });
|
|
294
|
+
fs.writeFileSync(path.join(tempRoot, 'config.json'), JSON.stringify({
|
|
295
|
+
Environment: {
|
|
296
|
+
DefaultKb: 'legacy',
|
|
297
|
+
KBs: [
|
|
298
|
+
{ Alias: 'main', Path: kbA },
|
|
299
|
+
{ alias: 'legacy', path: kbB }
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
}));
|
|
303
|
+
|
|
304
|
+
const res = runCli(['kb', 'list', '--format', 'json'], { cwd: tempRoot });
|
|
305
|
+
assert.equal(res.status, 0);
|
|
306
|
+
const parsed = JSON.parse(res.stdout);
|
|
307
|
+
assert.equal(parsed.ok.activeKb, 'legacy');
|
|
308
|
+
assert.deepEqual(parsed.ok.kbs.map((entry) => entry.name), ['main', 'legacy']);
|
|
309
|
+
assert.equal(parsed.ok.kbs[1].active, true);
|
|
310
|
+
|
|
311
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('kb switch preserves gateway-style array entries', () => {
|
|
315
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-test-'));
|
|
316
|
+
const kbA = path.join(tempRoot, 'kb-switch-a');
|
|
317
|
+
const kbB = path.join(tempRoot, 'kb-switch-b');
|
|
318
|
+
fs.mkdirSync(kbA, { recursive: true });
|
|
319
|
+
fs.mkdirSync(kbB, { recursive: true });
|
|
320
|
+
const configPath = path.join(tempRoot, 'config.json');
|
|
321
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
322
|
+
Environment: {
|
|
323
|
+
DefaultKb: 'main',
|
|
324
|
+
KBs: [
|
|
325
|
+
{ Alias: 'main', Path: kbA },
|
|
326
|
+
{ Alias: 'legacy', Path: kbB }
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
}));
|
|
330
|
+
|
|
331
|
+
const res = runCli(['kb', 'switch', '--name', 'legacy', '--format', 'json'], { cwd: tempRoot });
|
|
332
|
+
assert.equal(res.status, 0);
|
|
333
|
+
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
334
|
+
assert.deepEqual(Object.keys(cfg.Environment.KBs).sort(), ['legacy', 'main']);
|
|
335
|
+
assert.equal(cfg.Environment.KBs.main, kbA);
|
|
336
|
+
assert.equal(cfg.Environment.KBs.legacy, kbB);
|
|
337
|
+
assert.equal(cfg.Environment.ActiveKb, 'legacy');
|
|
338
|
+
assert.equal(cfg.Environment.DefaultKb, 'legacy');
|
|
339
|
+
|
|
340
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
341
|
+
});
|
|
342
|
+
|
|
288
343
|
test('kb add and switch update active KB', () => {
|
|
289
344
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-test-'));
|
|
290
345
|
const kbA = path.join(tempRoot, 'kb-a');
|
|
@@ -380,6 +435,7 @@ test('kb remove of last KB clears legacy KBPath', () => {
|
|
|
380
435
|
const cfg = JSON.parse(fs.readFileSync(path.join(kbDir, 'config.json'), 'utf8'));
|
|
381
436
|
assert.equal(cfg.Environment.KBPath, undefined, 'KBPath should be cleared after removing last KB');
|
|
382
437
|
assert.equal(cfg.Environment.ActiveKb, undefined, 'ActiveKb should be cleared');
|
|
438
|
+
assert.equal(cfg.Environment.DefaultKb, undefined, 'DefaultKb should be cleared');
|
|
383
439
|
|
|
384
440
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
385
441
|
});
|
|
@@ -734,6 +790,103 @@ test('clients add tolerates a JSONC (commented) VS Code mcp.json', () => {
|
|
|
734
790
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
735
791
|
});
|
|
736
792
|
|
|
793
|
+
test('clients add preserves OpenCode 1.x direct mcp shape', () => {
|
|
794
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-opencode-v1-'));
|
|
795
|
+
const env = sandboxHomeEnv(tempRoot);
|
|
796
|
+
const cfgPath = path.join(tempRoot, 'config.json');
|
|
797
|
+
const openCodeCfg = path.join(env.XDG_CONFIG_HOME, 'opencode', 'opencode.json');
|
|
798
|
+
fs.mkdirSync(path.dirname(openCodeCfg), { recursive: true });
|
|
799
|
+
fs.writeFileSync(cfgPath, JSON.stringify({ Environment: { KBPath: tempRoot } }));
|
|
800
|
+
fs.writeFileSync(openCodeCfg, JSON.stringify({
|
|
801
|
+
mcp: { other: { type: 'local', command: ['other-tool'] } }
|
|
802
|
+
}, null, 2));
|
|
803
|
+
|
|
804
|
+
const res = runCli(['clients', 'add', '--clients', 'opencode', '--format', 'json'], {
|
|
805
|
+
env: { ...env, GX_CONFIG_PATH: cfgPath }
|
|
806
|
+
});
|
|
807
|
+
assert.equal(res.status, 0);
|
|
808
|
+
const written = JSON.parse(fs.readFileSync(openCodeCfg, 'utf8'));
|
|
809
|
+
assert.ok(written.mcp.genexus, 'direct OpenCode entry should be written');
|
|
810
|
+
assert.equal(written.mcp.genexus.enabled, true);
|
|
811
|
+
assert.equal(written.mcp.genexus.disabled, undefined);
|
|
812
|
+
assert.ok(written.mcp.other, 'unrelated direct MCP server should be preserved');
|
|
813
|
+
assert.equal(written.mcp.servers, undefined);
|
|
814
|
+
assert.deepEqual(written.mcp.genexus.environment, { GX_CONFIG_PATH: cfgPath });
|
|
815
|
+
|
|
816
|
+
const listed = runCli(['clients', '--format', 'json'], { env });
|
|
817
|
+
assert.equal(listed.status, 0);
|
|
818
|
+
const row = JSON.parse(listed.stdout).ok.clients.find((client) => client.id === 'opencode');
|
|
819
|
+
assert.ok(row && row.registered, 'clients list should read the direct OpenCode entry');
|
|
820
|
+
|
|
821
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
test('clients add preserves OpenCode v2 nested mcp.servers shape', () => {
|
|
825
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-opencode-v2-'));
|
|
826
|
+
const env = sandboxHomeEnv(tempRoot);
|
|
827
|
+
const cfgPath = path.join(tempRoot, 'config.json');
|
|
828
|
+
const openCodeCfg = path.join(env.XDG_CONFIG_HOME, 'opencode', 'opencode.json');
|
|
829
|
+
fs.mkdirSync(path.dirname(openCodeCfg), { recursive: true });
|
|
830
|
+
fs.writeFileSync(cfgPath, JSON.stringify({ Environment: { KBPath: tempRoot } }));
|
|
831
|
+
fs.writeFileSync(openCodeCfg, JSON.stringify({
|
|
832
|
+
mcp: { servers: { other: { type: 'local', command: ['other-tool'] } } }
|
|
833
|
+
}, null, 2));
|
|
834
|
+
|
|
835
|
+
const res = runCli(['clients', 'add', '--clients', 'opencode', '--format', 'json'], {
|
|
836
|
+
env: { ...env, GX_CONFIG_PATH: cfgPath }
|
|
837
|
+
});
|
|
838
|
+
assert.equal(res.status, 0);
|
|
839
|
+
const written = JSON.parse(fs.readFileSync(openCodeCfg, 'utf8'));
|
|
840
|
+
assert.ok(written.mcp.servers.genexus, 'nested OpenCode entry should be written');
|
|
841
|
+
assert.equal(written.mcp.servers.genexus.disabled, false);
|
|
842
|
+
assert.equal(written.mcp.servers.genexus.enabled, undefined);
|
|
843
|
+
assert.ok(written.mcp.servers.other, 'unrelated nested MCP server should be preserved');
|
|
844
|
+
assert.equal(written.mcp.genexus, undefined);
|
|
845
|
+
assert.deepEqual(written.mcp.servers.genexus.environment, { GX_CONFIG_PATH: cfgPath });
|
|
846
|
+
|
|
847
|
+
const listed = runCli(['clients', '--format', 'json'], { env });
|
|
848
|
+
assert.equal(listed.status, 0);
|
|
849
|
+
const row = JSON.parse(listed.stdout).ok.clients.find((client) => client.id === 'opencode');
|
|
850
|
+
assert.ok(row && row.registered, 'clients list should read the nested OpenCode entry');
|
|
851
|
+
|
|
852
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
test('init auto-registers detected OpenCode in either config layout', () => {
|
|
856
|
+
for (const [label, mcp] of [
|
|
857
|
+
['direct', { other: { type: 'local', command: ['other-tool'] } }],
|
|
858
|
+
['nested', { servers: { other: { type: 'local', command: ['other-tool'] } } }]
|
|
859
|
+
]) {
|
|
860
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), `genexus-mcp-opencode-init-${label}-`));
|
|
861
|
+
try {
|
|
862
|
+
const env = sandboxHomeEnv(tempRoot);
|
|
863
|
+
const kbDir = path.join(tempRoot, 'kb');
|
|
864
|
+
const openCodeCfg = path.join(env.XDG_CONFIG_HOME, 'opencode', 'opencode.json');
|
|
865
|
+
fs.mkdirSync(kbDir, { recursive: true });
|
|
866
|
+
fs.mkdirSync(path.dirname(openCodeCfg), { recursive: true });
|
|
867
|
+
fs.writeFileSync(openCodeCfg, JSON.stringify({ mcp }, null, 2));
|
|
868
|
+
|
|
869
|
+
const result = runCli(
|
|
870
|
+
['init', '--kb', kbDir, '--gx', testGxPath, '--no-smoke', '--format', 'json'],
|
|
871
|
+
{ cwd: kbDir, env: { ...env, ...testGatewayEnv } }
|
|
872
|
+
);
|
|
873
|
+
assert.equal(result.status, 0, `${label} OpenCode init should succeed: ${result.stderr}`);
|
|
874
|
+
|
|
875
|
+
const parsed = JSON.parse(result.stdout);
|
|
876
|
+
assert.ok(parsed.ok.clientsPatchedCount >= 1, `${label} OpenCode should be auto-registered`);
|
|
877
|
+
assert.ok(parsed.meta.patchedClients.includes('OpenCode (CLI)'));
|
|
878
|
+
|
|
879
|
+
const written = JSON.parse(fs.readFileSync(openCodeCfg, 'utf8'));
|
|
880
|
+
const entry = label === 'nested' ? written.mcp.servers.genexus : written.mcp.genexus;
|
|
881
|
+
assert.ok(entry, `${label} OpenCode entry should be present after init`);
|
|
882
|
+
assert.deepEqual(entry.environment, { GX_CONFIG_PATH: path.join(kbDir, 'config.json') });
|
|
883
|
+
assert.ok(label === 'nested' ? written.mcp.servers.other : written.mcp.other);
|
|
884
|
+
} finally {
|
|
885
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
|
|
737
890
|
test('clients add replaces a legacy genexus18 entry instead of duplicating it', () => {
|
|
738
891
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-legacy-'));
|
|
739
892
|
const env = sandboxHomeEnv(tempRoot);
|
|
@@ -825,6 +978,35 @@ test('clients remove drops the genexus entry (sandbox home)', () => {
|
|
|
825
978
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
826
979
|
});
|
|
827
980
|
|
|
981
|
+
test('clients remove drops both OpenCode config shapes and legacy key', () => {
|
|
982
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'genexus-mcp-opencode-rm-'));
|
|
983
|
+
const env = sandboxHomeEnv(tempRoot);
|
|
984
|
+
const openCodeCfg = path.join(env.XDG_CONFIG_HOME, 'opencode', 'opencode.json');
|
|
985
|
+
fs.mkdirSync(path.dirname(openCodeCfg), { recursive: true });
|
|
986
|
+
fs.writeFileSync(openCodeCfg, JSON.stringify({
|
|
987
|
+
mcp: {
|
|
988
|
+
genexus: { type: 'local', command: ['old'] },
|
|
989
|
+
genexus18: { type: 'local', command: ['older'] },
|
|
990
|
+
servers: {
|
|
991
|
+
genexus: { type: 'local', command: ['nested'] },
|
|
992
|
+
genexus18: { type: 'local', command: ['nested-old'] },
|
|
993
|
+
other: { type: 'local', command: ['other'] }
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}, null, 2));
|
|
997
|
+
|
|
998
|
+
const res = runCli(['clients', 'remove', '--clients', 'opencode', '--format', 'json'], { env });
|
|
999
|
+
assert.equal(res.status, 0);
|
|
1000
|
+
const written = JSON.parse(fs.readFileSync(openCodeCfg, 'utf8'));
|
|
1001
|
+
assert.equal(written.mcp.genexus, undefined);
|
|
1002
|
+
assert.equal(written.mcp.genexus18, undefined);
|
|
1003
|
+
assert.equal(written.mcp.servers.genexus, undefined);
|
|
1004
|
+
assert.equal(written.mcp.servers.genexus18, undefined);
|
|
1005
|
+
assert.ok(written.mcp.servers.other, 'unrelated nested MCP server should be preserved');
|
|
1006
|
+
|
|
1007
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
1008
|
+
});
|
|
1009
|
+
|
|
828
1010
|
test('compareSemver detects newer, older, equal versions', () => {
|
|
829
1011
|
assert.equal(compareSemver('1.3.1', '1.3.0'), 1);
|
|
830
1012
|
assert.equal(compareSemver('v1.4.0', '1.3.9'), 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genexus-mcp",
|
|
3
|
-
"version": "2.41.
|
|
3
|
+
"version": "2.41.11",
|
|
4
4
|
"mcpName": "io.github.lennix1337/genexus",
|
|
5
5
|
"description": "GeneXus 18 MCP server — read, edit, and analyze GeneXus knowledge base objects (transactions, web panels, procedures, SDTs) directly from Claude, Cursor, and other AI agents over the Model Context Protocol.",
|
|
6
6
|
"keywords": [
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"targets": {
|
|
8
8
|
".NETCoreApp,Version=v8.0": {},
|
|
9
9
|
".NETCoreApp,Version=v8.0/win-x64": {
|
|
10
|
-
"GxMcp.Gateway/2.41.
|
|
10
|
+
"GxMcp.Gateway/2.41.11": {
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"Newtonsoft.Json": "13.0.3",
|
|
13
13
|
"System.Management": "10.0.5",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"libraries": {
|
|
69
|
-
"GxMcp.Gateway/2.41.
|
|
69
|
+
"GxMcp.Gateway/2.41.11": {
|
|
70
70
|
"type": "project",
|
|
71
71
|
"serviceable": false,
|
|
72
72
|
"sha512": ""
|
|
Binary file
|
|
Binary file
|
package/publish/config.json
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
"HttpPort": 5000,
|
|
4
4
|
"McpStdio": true
|
|
5
5
|
},
|
|
6
|
-
"Logging": {
|
|
7
|
-
"Level": "Debug",
|
|
8
|
-
"Path": "logs"
|
|
9
|
-
},
|
|
10
6
|
"GeneXus": {
|
|
11
|
-
"
|
|
12
|
-
"
|
|
7
|
+
"InstallationPath": "C:\\\\Program Files (x86)\\\\GeneXus\\\\GeneXus18",
|
|
8
|
+
"WorkerExecutable": "C:\\Projetos\\Genexus18MCP\\publish\\\\worker\\\\GxMcp.Worker.exe"
|
|
13
9
|
},
|
|
14
10
|
"Environment": {
|
|
15
11
|
"KBPath": "C:\\\\KBs\\\\YourKB"
|
|
12
|
+
},
|
|
13
|
+
"Logging": {
|
|
14
|
+
"Level": "Debug",
|
|
15
|
+
"Path": "logs"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
[
|
|
2
|
+
{"name":"genexus_data_view","description":"Native, typed, atomic Transaction + Data View authoring over an existing physical table. action=inspect|dry_run|create|update|delete. dry_run performs validation without constructing or saving SDK objects. create/update persist the root-only Transaction and Data View, then reread and verify the committed pair; commit and verification status are reported separately. action=delete is destructive and requires confirm=true when dryRun=false. No action implicitly runs Specify, Generate, Build, Rebuild, Reorg, compilation, publish, execution, or tests; the response includes a no-DDL reorg preview.","inputSchema":{"type":"object","required":["action","transaction","dataViewName"],"additionalProperties":false,"properties":{"action":{"type":"string","enum":["inspect","dry_run","create","update","delete"]},"transaction":{"type":"string","description":"Root-only Transaction name."},"dataViewName":{"type":"string","description":"Data View name associated with the Transaction logical table."},"dataStore":{"type":"string","description":"Existing GeneXus data store category (default Default)."},"schema":{"type":"string","description":"Existing physical schema."},"table":{"type":"string","description":"Existing physical table and GeneXus table-metadata name."},"attributeMappings":{"type":"array","items":{"type":"object","required":["attribute","column"],"additionalProperties":false,"properties":{"attribute":{"type":"string"},"column":{"type":"string"},"key":{"type":"boolean","default":false}}}},"updatable":{"type":"boolean","default":true},"expectedVersion":{"type":"string","description":"Optimistic concurrency token returned by inspect/dry_run/create/update/delete preview."},"dryRun":{"type":"boolean","default":false,"description":"Forces every action to remain read-only; create/update validate their complete definition and delete returns a deletion preview."},"confirm":{"type":"boolean","description":"Required for action=delete when dryRun=false; explicitly authorizes removing the Transaction and Data View."},"rollbackOnFailure":{"type":"boolean","default":true,"description":"Atomic SDK rollback is always enforced; this flag records the caller's rollback intent."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"dry_run","transaction":"LedgerEntryView","dataViewName":"LedgerEntryDV","dataStore":"Default","schema":"APP","table":"LEDGERENTRY","updatable":true,"attributeMappings":[{"attribute":"LedgerEntryId","column":"LedgerEntryId","key":true},{"attribute":"LedgerEntryAmount","column":"LedgerEntryAmount"}],"rollbackOnFailure":true},{"action":"delete","transaction":"LedgerEntryView","dataViewName":"LedgerEntryDV","confirm":true}]} ,"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},
|
|
2
3
|
{"name":"genexus_whoami","description":"KB context + version + health (worker/index/database) + next-step hints. Call FIRST every session. Lean by default; pass verbose=true ONCE for the inline playbooks + skills catalog of verified GeneXus reference resources (navigation, GAM, SD panel, WebPanel events). For a minimal connection+index health check use genexus_doctor.","inputSchema":{"type":"object","properties":{"verbose":{"type":"boolean","description":"Include the static playbooks + skills catalog + per-tool stats/heatmap. Default false (lean health payload)."}},"additionalProperties":false,"examples":[{},{"verbose":true}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
|
|
3
4
|
{"name":"genexus_recipe","description":"Named playbooks + self-extending macros. action=list|describe|run|suggest_macro|crystallize.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Recipe key (latest version), 'name@v1' (pinned), or 'list'."},"action":{"type":"string","enum":["list","describe","run","suggest_macro","crystallize"],"description":"suggest_macro: detect repeated sequences; crystallize: save one as a recipe."},"windowMinutes":{"type":"integer","description":"suggest_macro: history window (default 30)."},"minRepetitions":{"type":"integer","description":"suggest_macro: threshold (default 3)."},"macroName":{"type":"string","description":"crystallize: proposed name from suggest_macro."},"description":{"type":"string","description":"crystallize: human-readable description."}},"examples":[{"name":"list"},{"name":"wwp_on_transaction"},{"action":"suggest_macro"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
4
5
|
{"name":"genexus_query","description":"Search objects in active KB. Prefixes: name:, type:, usedby:, parent:, parentPath:, description:. name:\"X\" (or bare quoted \"X\") demands exact-name match — use it when you know the object's name. Compact by default. See genexus://kb/tool-help/genexus_query. Requires the KB index to be Ready — check genexus_lifecycle action=status if results look empty.","inputSchema":{"type":"object","properties":{"query":{"type":"string"},"typeFilter":{"type":"string"},"domainFilter":{"type":"string"},"limit":{"type":"integer"},"inline_read_top":{"type":"integer","description":"0-3. Inline reads of top N."},"sort":{"type":"string","enum":["relevance","lastUpdate"],"description":"relevance (default) or lastUpdate (newest first; bypasses score ranking)."},"since":{"type":"string","description":"ISO-8601 UTC. Only items with lastUpdate >= since."},"modifiedBefore":{"type":"string","description":"ISO-8601 UTC. Only items with lastUpdate < modifiedBefore."},"cursor":{"type":"string","description":"Opaque token from prior nextCursor. Use with sort=lastUpdate for stable paging."},"kb":{"type":"string","description":"KB alias."},"axiCompact":{"type":"boolean","description":"Compact projection (default true).","default":true}},"required":["query"],"examples":[{"query":"type:Transaction parent:Customers"},{"query":"\"Customer\"","limit":5}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
|
|
@@ -11,18 +12,18 @@
|
|
|
11
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}},
|
|
12
13
|
{"name":"genexus_sdk_probe","description":"Dump SDK surface (types/methods/props) to docs/sdk-probe/. Use when hunting for entry points.","inputSchema":{"type":"object","properties":{"outputDir":{"type":"string","description":"Absolute path. Default <repo>/docs/sdk-probe/."}},"examples":[{}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
|
|
13
14
|
{"name":"genexus_worker_reload","description":"Reload worker. modes: soft (drain+respawn), hard (copy sourceDir+respawn). force=true: gateway kills directly. See tool-help.","inputSchema":{"type":"object","properties":{"mode":{"type":"string","enum":["soft","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_delete_object","description":"Delete an object from the KB. Irreversible — confirm=true required.
|
|
15
|
+
{"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 persisting.","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}},
|
|
15
16
|
{"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"]},"target":{"type":"string","description":"Primary object or symbol to refactor."},"newName":{"type":"string"},"objectName":{"type":"string"},"code":{"type":"string"},"procedureName":{"type":"string"},"subroutineName":{"type":"string"},"controlAttribute":{"type":"string"},"value":{"type":"string"},"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}},
|
|
16
17
|
{"name":"genexus_run_object","description":"Resolve runtime URL for an object (webRoot + aspx + encoded args). gamSession='auto'|{user,pass,...} captures GAM cookies. Does NOT open a browser. dryRun=true returns the resolved URL without performing GAM login.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"args":{"type":"array","items":{"type":"string"},"description":"Positional parameter values."},"gamSession":{"description":"'auto' (use GXMCP_GAM_USER/PASS env) or {user, pass, repository?, loginUrl?}."},"dryRun":{"type":"boolean","default":false,"description":"When true, returns the resolved URL without performing the GAM login step."},"kb":{"type":"string","description":"KB alias."}},"required":["name"],"examples":[{"name":"WPMain"},{"name":"WPCustomer","args":["42"]}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},
|
|
17
18
|
{"name":"genexus_format","description":"Format a GeneXus code snippet using worker rules.","inputSchema":{"type":"object","properties":{"code":{"type":"string"},"kb":{"type":"string","description":"KB alias."}},"required":["code"],"examples":[{"code":"for each Customer where CustomerId = &Id\n &Name = CustomerName\nendfor"}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
|
|
18
19
|
{"name":"genexus_gam","description":"GAM / integrated-security provisioning via the SDK's IIntegratedSecurityService — distinct from genexus_security (env-scan/regex audit). action=status (default) is read-only: IsEnabledIntegratedSecurity + GAM-DB-reorganize flag. action=define_api / action=deploy are DESTRUCTIVE — they create/alter GAM security tables in the KB's datastore; no default reaches them, action must be set explicitly.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["status","define_api","deploy"],"description":"status (default, read-only) | define_api | deploy."},"force":{"type":"boolean","description":"define_api only: force flag passed to DefineAPI."},"forceTableCreation":{"type":"boolean","description":"deploy only: force GAM table creation."},"rebuild":{"type":"boolean","description":"deploy only: rebuild flag."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"status"},{"action":"deploy","forceTableCreation":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},
|
|
19
20
|
{"name":"genexus_properties","description":"Read/update object properties. action=get|set|move. move snapshots every persisted part, checks baseVersion, verifies inside the SDK transaction and after commit, and rolls back divergence. Use destination for a Folder/Module or targetModule for a Module; destKind disambiguates equal names. dryRun does not save or change the token. Move never runs lifecycle, compilation, execution, or tests. Description is the WebPanel/Popup title-bar text. Use genexus_layout set_property for layout controls.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set","move"]},"name":{"type":"string"},"type":{"type":"string","description":"Optional object type disambiguator."},"control":{"type":"string","description":"Optional. Layout control name (e.g. BtnConfirmar), variable name with & prefix (e.g. &Alu2RegProf), or attribute name."},"propertyName":{"type":"string"},"value":{"type":"string"},"destination":{"type":"string","description":"move: target Folder/Module name (auto-detects kind)."},"targetModule":{"type":"string","description":"move: typed alias for a Module destination."},"destKind":{"type":"string","enum":["Folder","Module"],"description":"move: disambiguate when a Folder and Module share a name."},"dryRun":{"type":"boolean","description":"move: preview from/to and preserved parts without persisting."},"baseVersion":{"type":"string","description":"move: opaque optimistic concurrency token returned by a prior read."},"validationMode":{"type":"string","enum":["specify"],"description":"Issue #60: with action=set, run the inline Specify pass (Spec+Gen, no Compile) after the property write, returning structured spc*/gen* diagnostics in the same call. Combines with rollbackOnFailure."},"rollbackOnFailure":{"type":"boolean","description":"move: attempt compensating snapshot restoration if the SDK transaction rollback is insufficient (default true). set+validationMode=specify: request rollback after validation failure."},"kb":{"type":"string","description":"KB alias."}},"required":["action","name"],"examples":[{"action":"get","name":"MyPanel"},{"action":"move","name":"InvoiceHelper","type":"Procedure","targetModule":"operacional","baseVersion":"<versionToken from genexus_read>","dryRun":true,"rollbackOnFailure":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
20
|
-
{"name":"genexus_structure","description":"Read or write the structure/data-model of GeneXus objects.
|
|
21
|
+
{"name":"genexus_structure","description":"Read or write the structure/data-model of GeneXus objects. update_visual now snapshots every part, checks optimistic concurrency, saves and re-reads the Transaction, and restores the complete snapshot when persistence or authored-part verification fails. Default form projections are allowed to follow Structure; user-authored forms are invariant. No structure action implicitly runs Specify, Generate, Build, Rebuild, compilation, reorganization, execution, or tests. Other actions cover indexes, attributes, levels, Domains, SubType Groups and subtype consistency. For layout control trees use genexus_layout.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_visual","update_visual","get_indexes","create_index","drop_index","set_attribute","set_level","set_domain","get_logic","update_group","move_attribute","remove_attribute","check_subtypes"]},"name":{"type":"string","description":"Object name. update_visual/move_attribute: Transaction name; create_index/get_indexes: Transaction or Table name."},"module":{"type":"string","description":"update_visual/move_attribute optional: Transaction module."},"attribute":{"type":"string","description":"move_attribute: existing attribute to reorder by native SDK identity."},"before":{"type":"string","description":"move_attribute: place attribute before this attribute in the same level. Mutually exclusive with after/position."},"after":{"type":"string","description":"move_attribute: place attribute after this attribute in the same level. Mutually exclusive with before/position."},"position":{"type":"integer","minimum":0,"description":"move_attribute: zero-based final attribute position in the level. Mutually exclusive with before/after."},"level":{"type":"string","description":"move_attribute: root (default) or an unambiguous subordinate level name."},"levelPath":{"type":"array","items":{"type":"string"},"description":"move_attribute: nested level path, e.g. [\"Item\",\"Operation\"]."},"dryRun":{"type":"boolean","default":false,"description":"update_visual/move_attribute/create_index: validate and return the projected diff without changing any object, Attribute, hash, or version."},"baseVersion":{"type":"string","description":"Optimistic-concurrency token for update_visual/move_attribute/create_index. Stale state returns VersionConflict before mutation."},"expectedVersion":{"type":"string","description":"Alias of baseVersion for update_visual."},"payload":{"type":"object","description":"update_visual (Transaction):{children:[...]}; update_visual (SDT):{isCollection?,collectionItemName?,children:[{name,type?,length?,decimals?,basedOnDomain?,isCollection?,isLevel?,children?}]}. update_group:{members:[{name,subtypeOf}],remove?:[name]}. create_index:{attributes:[\"Attr\"],unique?:true,name?,order?:\"Ascending\"|\"Descending\"}. Other actions keep their existing typed payloads."},"validationMode":{"type":"string","enum":["specify"],"description":"Explicitly run the inline Specify pass after selected writes. Never runs unless requested."},"rollbackOnFailure":{"type":"boolean","default":true,"description":"update_visual, move_attribute and create_index capture snapshots and verify rollback after any save or re-read divergence."},"kb":{"type":"string","description":"KB alias."}},"required":["action","name"],"examples":[{"action":"update_visual","name":"OrderRecord","module":"Operations","dryRun":true,"expectedVersion":"<versionToken>","payload":{"children":[{"name":"OrderId"},{"name":"OrderAverageTime"}]}},{"action":"move_attribute","name":"SampleTransaction","attribute":"SampleSubtypeId","after":"SampleReferenceId","level":"root","dryRun":true},{"action":"get_indexes","name":"Customer"},{"action":"create_index","name":"Country","dryRun":true,"baseVersion":"<get_indexes versionToken>","payload":{"name":"UCountryName","attributes":["CountryName"],"unique":true},"rollbackOnFailure":true},{"action":"set_attribute","name":"CustomerBalance","payload":{"formula":"sum(SampleTransactionAmount)"}},{"action":"set_level","name":"Customer","payload":{"descriptionAttribute":"CustomerName"}},{"action":"update_group","name":"SampleSubtypeGroup","payload":{"members":[{"name":"SampleId","subtypeOf":"BaseId"}]}}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
21
22
|
{"name":"genexus_authoring","description":"Author members of object types the structure DSL doesn't cover. add_external_method / add_external_property add a method (with parameters) or property to an External Object; add_menu_option adds an option to a Menu (target = a KB object to call); add_condition adds a filter expression to a Data Selector. name = the target object.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["add_external_method","add_external_property","add_menu_option","add_condition"]},"name":{"type":"string","description":"ExternalObject / Menu / DataSelector name."},"payload":{"type":"object","description":"add_external_method:{name,returnType?,parameters?:[{name,type?,inout?}]}. add_external_property:{name,type?}. add_menu_option:{description,target?,optionCode?}. add_condition:{source}."},"kb":{"type":"string","description":"KB alias."}},"required":["action","name"],"examples":[{"action":"add_external_property","name":"MyExternalObject","payload":{"name":"apiKey","type":"Character"}},{"action":"add_menu_option","name":"MainMenu","payload":{"description":"Customers","target":"CustomerWW"}},{"action":"add_condition","name":"ActiveCustomers","payload":{"source":"CustomerActive = True"}}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
22
23
|
{"name":"genexus_layout","description":"SDK layout/WebForm ops: get_tree, set_property, find_controls, inspect_surface, scan_mutators. For object-level properties use genexus_properties; for object-level visual structure use genexus_structure get_visual.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_tree","set_property","find_controls","set_properties","inspect_surface","get_preview","scan_mutators","rename_printblock","add_printblock","list_controls","design_system"]},"name":{"type":"string"},"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"},"query":{"type":"string"},"changes":{"type":"array","items":{"type":"object","properties":{"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"}},"required":["control","propertyName","value"]}},"limit":{"type":"integer"},"currentName":{"type":"string"},"newName":{"type":"string"},"printBlockName":{"type":"string"},"height":{"type":"integer"},"kb":{"type":"string","description":"KB alias."}},"required":["action"],"examples":[{"action":"get_tree","name":"WPMain"},{"action":"find_controls","name":"WPMain","query":"Button"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
23
24
|
{"name":"genexus_doc","description":"Use to generate structured docs (wiki, sequence diagrams, health reports). Don't use for programmatic analysis (see genexus_analyze) or source (see genexus_read).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["wiki","visualize","health"]},"target":{"type":"string","description":"Object or domain name."}},"required":["action"],"examples":[{"action":"wiki","target":"Customer"},{"action":"health"}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
|
|
24
25
|
{"name":"genexus_search_source","description":"Regex/semantic search across Procedure/DataProvider/WebPanel/Transaction source. Requires the KB index to be Ready — check genexus_lifecycle action=status if results look empty.","inputSchema":{"type":"object","properties":{"callee":{"type":"string","description":"Method/function name (qualified or unqualified)."},"argMatches":{"type":"object","description":"Positional arg index to expected literal text."},"pattern":{"type":"string"},"typeFilter":{"type":"string"},"objectName":{"type":"string","description":"Restrict the scan to these exact object name(s), comma-separated. Makes a search inside one known object O(object) not O(KB), and bypasses the type whitelist."},"startIndex":{"type":"integer","description":"Legacy object-boundary resume. For a page that ended inside an object, prefer the opaque cursor returned in nextCursor."},"cursor":{"type":"string","description":"Opaque continuation token from nextCursor; preferred when a result page ended inside an object. Do not edit."},"timeoutMs":{"type":"integer","description":"Wall-clock budget (default 30000). Raise it to scan more objects per call."},"scope":{"type":"array","items":{"type":"string","enum":["source","rules","conditions","events","webForm","layout"]},"description":"Parts to scan with line-numbered context. Default [source]. webForm/layout scans the WebPanel/Transaction visual XML (control names, captions, classes, bindings)."},"fields":{"type":"array","items":{"type":"string","enum":["source","caption","description","parmNames","webForm"]},"description":"Whole-field metadata match (returns matchedValue, no line context). Default [source]. Prefer scope=[webForm] for line-context WebForm hits; use fields=[webForm] only for a coarse contains-match."},"maxResults":{"type":"integer"},"caseSensitive":{"type":"boolean"},"includeComments":{"type":"boolean"},"inline_read_top":{"type":"integer","description":"0-3. Inline reads of top N distinct objects in response."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"pattern":"ControlType=\"Radio\""},{"pattern":"for each","typeFilter":"Procedure","maxResults":20}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
|
|
25
|
-
{"name":"genexus_kb","description":"Manage open KBs and
|
|
26
|
+
{"name":"genexus_kb","description":"Manage open KBs, startup config, and the active GeneXus environment. list/open/close/set_default operate on WorkerPool; set_default selects the implicit target for calls that omit kb (an explicit kb always wins for parallel or cross-KB work); set_startup/get_startup mirror IDE 'Set As Startup Object'; get_environment reads the SDK selection; set_environment selects an environment through the SDK task used by the IDE.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","open","close","set_default","set_startup","get_startup","get_environment","set_environment"]},"alias":{"type":"string","description":"KB alias. open: auto from path basename if omitted."},"path":{"type":"string","description":"Absolute KB path (required for open if alias not in config)."},"name":{"type":"string","description":"Object name (required for set_startup)."},"environment":{"type":"string","description":"Environment name/folder (required for set_environment), e.g. development."}},"required":["action"],"examples":[{"action":"open","path":"C:\\KBs\\MyKb"},{"action":"list"},{"action":"get_environment"},{"action":"set_environment","environment":"development"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
26
27
|
{"name":"genexus_navigation","description":"View navigation report (IDE Right-click → View Navigation). latest=true returns cached report from .gx/navigation-cache; else runs fresh.","inputSchema":{"type":"object","required":["action","name"],"properties":{"action":{"type":"string","enum":["view"]},"name":{"type":"string"},"latest":{"type":"boolean","default":false,"description":"Return the most recent cached navigation if any; otherwise run fresh."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"view","name":"Customer"},{"action":"view","name":"Customer","latest":true}]},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},
|
|
27
28
|
{"name":"genexus_api","description":"Introspect REST endpoints exposed by HTTP procedures. action=list|describe|snapshot|diff_baseline. Returns endpoints + detects breaking changes vs a saved baseline.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["list","describe","snapshot","diff_baseline"]},"target":{"type":"string","description":"Procedure name (action=describe)."},"name":{"type":"string","description":"Baseline name (action=snapshot)."},"baseline":{"type":"string","description":"Baseline name under .gx/api-baselines/ or absolute path (action=diff_baseline)."},"pathPrefix":{"type":"string","description":"Folder prefix filter (action=list), e.g. 'Root Module/API/'."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"list"},{"action":"describe","target":"ApiCustomerGet"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
28
29
|
{"name":"genexus_apply_pattern","description":"Apply a pattern (e.g. WorkWithPlus) to a parent (IDE Right-click → Apply Pattern). Transaction=family-gen; WebPanel/WebComponent/SDPanel=direct-attach (pass settings.template). mode=diagnose (or dryRun=true) returns findings without mutating. Working on Smart Devices? → resources/read uri=genexus://kb/skills/sd-panel-mobile for Main object semantics. mode=actions manages typed grid actions and Action Groups, persists and re-reads PatternInstance, supports dryRun diff, and never adds security permissions automatically.","inputSchema":{"type":"object","required":["name","pattern"],"properties":{"name":{"type":"string","description":"Target KBObject name."},"pattern":{"type":"string","description":"Pattern key ('WorkWithPlus') or GUID."},"mode":{"type":"string","enum":["apply","diagnose","actions"],"description":"apply (default) mutates; diagnose returns reasons without applying. actions exposes typed WWP action-group editing."},"action":{"type":"string","enum":["list_actions","add_grid_action","update_action","move_action","remove_action"],"description":"mode=actions operation."},"group":{"type":"string"},"actionName":{"type":"string"},"newGroup":{"type":"string"},"position":{"type":"integer","minimum":0},"procedure":{"type":"string"},"selection":{"type":"string","enum":["current","multiple"]},"enabledWhen":{"type":"string"},"visibleWhen":{"type":"string"},"icon":{"type":"string"},"description":{"type":"string"},"confirmation":{"type":"string"},"confirmTitle":{"type":"string"},"settings":{"type":"object","description":"Pattern-instance settings tree."},"reapply":{"type":"boolean","default":false,"description":"Re-run on existing instance."},"validate":{"type":"boolean","default":false,"description":"After apply, build the generated host. Adds validation block (60-180s). Catches binding errors."},"dryRun":{"type":"boolean","default":false,"description":"Alias for mode=diagnose. When true, returns pattern findings without mutating. With mode=actions it previews the typed action diff."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"name":"Customer","pattern":"WorkWithPlus"},{"name":"Customer","pattern":"WorkWithPlus","mode":"diagnose"},{"name":"Customer","pattern":"WorkWithPlus","mode":"actions","action":"add_grid_action","group":"Actions","actionName":"Approve","procedure":"ApproveCustomer","dryRun":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
{"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) | 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","types_list","types_describe","types_validate","translations_import","reorg_impact","reorg_preview"]},"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."},"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}},
|
|
40
41
|
{"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"]},"name":{"type":"string"},"part":{"type":"string"},"versionId":{"type":"integer"},"snapshot":{"type":"string"},"discard":{"type":"boolean"},"dryRun":{"type":"boolean"},"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}},
|
|
41
42
|
{"name":"genexus_io","description":"IO umbrella for assets, part text exchange, screenshots, OCR. action: asset_find|asset_read|asset_write (binary asset CRUD) | export_part|import_part (object part to/from a text file) | export_unified (full object envelope JSON) | screenshot_publish (copy PNG to .gx/published-screenshots) | ocr (Tesseract stub).","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["asset_find","asset_read","asset_write","export_part","import_part","export_unified","screenshot_publish","ocr"]},"path":{"type":"string","description":"asset/screenshot_publish/ocr: file path."},"name":{"type":"string","description":"export/import/export_unified: object name."},"outputPath":{"type":"string","description":"export_part: destination file path."},"inputPath":{"type":"string","description":"import_part: source file path."},"part":{"type":"string","description":"export/import_part: part name (e.g. Source)."},"type":{"type":"string","description":"export/import/export_unified disambiguator."},"overwrite":{"type":"boolean","description":"export_part: overwrite if exists."},"pattern":{"type":"string","description":"asset_find: glob pattern."},"relativeRoot":{"type":"string","description":"asset_find: root for relative paths."},"limit":{"type":"integer","description":"asset_find: max results."},"includeContent":{"type":"boolean","description":"asset_find: inline file content."},"maxBytes":{"type":"integer","description":"asset_read: max bytes to return."},"contentBase64":{"type":"string","description":"asset_write: base64-encoded file content."},"kb":{"type":"string","description":"KB alias."}},"examples":[{"action":"asset_read","path":"C:\\KBs\\MyKb\\custom.xml"},{"action":"export_part","name":"Customer","part":"Source","outputPath":"C:\\tmp\\Customer.gxp"}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
42
|
-
{"name":"genexus_variable","description":"Variables CRUD. add accepts varName or variables[] batch; modify changes type atomically. basedOn binds a Domain by native SDK identity. length/decimals override size; collection=true declares a collection; untyped add inherits a same-named attribute.
|
|
43
|
+
{"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"]},"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}},
|
|
43
44
|
{"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"]},"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}},
|
|
44
45
|
{"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"]},"curl":{"type":"string","description":"curl_procedure: the curl command to import as a Procedure."},"type":{"type":"string"},"name":{"type":"string"},"newName":{"type":"string","description":"save_as."},"description":{"type":"string"},"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);\")."},"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."},"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"},"updateExisting":{"type":"boolean","default":false}},"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}},
|
|
45
46
|
{"name":"genexus_memory","description":"Per-KB fact store. action: save (fact required; optional target/type/tags; dedups on identical fact+object, bumping a hit count) | recall (filter by target/type/tags — ANY match; no filter returns all) | list (all, newest first) | forget (tombstone by id) | promote (lift a friction-log message into a memory, tagged 'friction') | consolidate (dreaming — merge redundant/overlapping facts within scope; dryRun=true previews, dryRun=false compacts memory.jsonl). Persisted to .gx/memory/memory.jsonl.","inputSchema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["save","recall","list","forget","promote","consolidate"]},"fact":{"type":"string","description":"save: the fact to remember."},"target":{"type":"string","description":"object name this memory is about."},"type":{"type":"string","description":"object type this memory is about."},"tags":{"type":"array","items":{"type":"string"},"description":"labels for filtering."},"id":{"type":"string","description":"forget: memory id to tombstone."},"message":{"type":"string","description":"promote: friction text to promote into a memory."},"dryRun":{"type":"boolean","description":"consolidate: true previews proposed merges without writing; false (default) applies and compacts memory.jsonl."},"kb":{"type":"string","description":"KB alias."}},"additionalProperties":false,"examples":[{"action":"save","fact":"Customer.Email must be unique","target":"Customer","type":"Transaction","tags":["validation"]},{"action":"recall","target":"Customer"},{"action":"consolidate","dryRun":true}]},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},
|
|
Binary file
|