genexus-mcp 2.3.3 → 2.3.4
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/commands/axi.js +58 -6
- package/cli/index.js +11 -0
- package/cli/lib/config.js +294 -60
- package/cli/run.test.js +1 -1
- 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/GxMcp.Gateway.pdb +0 -0
- package/publish/tool_definitions.json +31 -31
- package/publish/worker/GxMcp.Worker.exe +0 -0
- package/publish/worker/GxMcp.Worker.pdb +0 -0
package/cli/commands/axi.js
CHANGED
|
@@ -11,6 +11,9 @@ const {
|
|
|
11
11
|
createConfigFile,
|
|
12
12
|
patchClientConfig,
|
|
13
13
|
unpatchClientConfig,
|
|
14
|
+
getClientConfigTargets,
|
|
15
|
+
filterClientTargets,
|
|
16
|
+
listSupportedClientIds,
|
|
14
17
|
getLocalAppDataCacheDir,
|
|
15
18
|
readGeneXusVersionFromInstall,
|
|
16
19
|
discoverGeneXusInstallation,
|
|
@@ -21,6 +24,25 @@ const {
|
|
|
21
24
|
switchActiveKb
|
|
22
25
|
} = require('../lib/config');
|
|
23
26
|
|
|
27
|
+
function resolveClientIds(options) {
|
|
28
|
+
if (!options || !options.clients) return null;
|
|
29
|
+
return String(options.clients)
|
|
30
|
+
.split(',')
|
|
31
|
+
.map((s) => s.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function validateClientIds(ids) {
|
|
36
|
+
if (!ids) return { ok: true };
|
|
37
|
+
const supported = new Set(listSupportedClientIds());
|
|
38
|
+
const invalid = ids.filter((id) => !supported.has(id));
|
|
39
|
+
if (invalid.length === 0) return { ok: true };
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
message: `Unknown client id(s): ${invalid.join(', ')}. Supported: ${[...supported].join(', ')}.`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
24
46
|
function parseFieldSelection(raw) {
|
|
25
47
|
if (!raw) return null;
|
|
26
48
|
return raw.split(',').map((v) => v.trim()).filter(Boolean);
|
|
@@ -769,8 +791,24 @@ async function runInteractiveInit(ctx) {
|
|
|
769
791
|
const gxAnswer = await question(`\n2) GeneXus installation path (default: ${defaultGx}):\n> `);
|
|
770
792
|
const finalGx = String(gxAnswer || '').trim() || defaultGx;
|
|
771
793
|
|
|
794
|
+
const allTargets = getClientConfigTargets();
|
|
795
|
+
const platformTargets = filterClientTargets(allTargets, { platform: process.platform });
|
|
796
|
+
ctx.stderr.write('\n3) Select AI agents to register (y/N per agent; Enter accepts default):\n');
|
|
797
|
+
const selectedIds = [];
|
|
798
|
+
for (const target of platformTargets) {
|
|
799
|
+
const installed = fs.existsSync(target.path);
|
|
800
|
+
const defaultYes = installed;
|
|
801
|
+
const tag = installed ? 'detected' : 'not detected';
|
|
802
|
+
const prompt = ` - ${target.name} [${tag}] (${defaultYes ? 'Y/n' : 'y/N'}): `;
|
|
803
|
+
const ans = (await question(prompt)).trim().toLowerCase();
|
|
804
|
+
const yes = ans === '' ? defaultYes : (ans === 'y' || ans === 'yes');
|
|
805
|
+
if (yes) selectedIds.push(target.id);
|
|
806
|
+
}
|
|
807
|
+
|
|
772
808
|
const created = createConfigFile(finalKb, finalGx);
|
|
773
|
-
const patchResult =
|
|
809
|
+
const patchResult = selectedIds.length
|
|
810
|
+
? patchClientConfig(created.targetConfigPath, { ids: selectedIds, onlyExisting: false })
|
|
811
|
+
: { patched: [], failed: [], skipped: [] };
|
|
774
812
|
|
|
775
813
|
return {
|
|
776
814
|
exitCode: ctx.EXIT_CODES.OK,
|
|
@@ -785,7 +823,8 @@ async function runInteractiveInit(ctx) {
|
|
|
785
823
|
help: patchResult.patched.length === 0 ? ['Set `GX_CONFIG_PATH` in your MCP client env to the generated config path.'] : [],
|
|
786
824
|
meta: {
|
|
787
825
|
patchedClients: patchResult.patched,
|
|
788
|
-
failedClients: patchResult.failed
|
|
826
|
+
failedClients: patchResult.failed,
|
|
827
|
+
skippedClients: patchResult.skipped || []
|
|
789
828
|
}
|
|
790
829
|
}
|
|
791
830
|
};
|
|
@@ -843,9 +882,20 @@ async function handleInit(options, ctx) {
|
|
|
843
882
|
const kbName = path.basename(resolution.kb.value);
|
|
844
883
|
addKbToConfig(created.targetConfigPath, kbName, resolution.kb.value);
|
|
845
884
|
|
|
846
|
-
let patchResult = { patched: [], failed: [] };
|
|
885
|
+
let patchResult = { patched: [], failed: [], skipped: [] };
|
|
847
886
|
if (options.writeClients) {
|
|
848
|
-
|
|
887
|
+
const ids = resolveClientIds(options);
|
|
888
|
+
const validation = validateClientIds(ids);
|
|
889
|
+
if (!validation.ok) {
|
|
890
|
+
return {
|
|
891
|
+
exitCode: ctx.EXIT_CODES.USAGE,
|
|
892
|
+
envelope: usageEnvelope(validation.message, ctx.EXIT_CODES.USAGE)
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
patchResult = patchClientConfig(created.targetConfigPath, {
|
|
896
|
+
ids,
|
|
897
|
+
onlyExisting: !options.allClients
|
|
898
|
+
});
|
|
849
899
|
}
|
|
850
900
|
|
|
851
901
|
const verification = await runPostInitVerification({
|
|
@@ -894,6 +944,7 @@ async function handleInit(options, ctx) {
|
|
|
894
944
|
meta: {
|
|
895
945
|
patchedClients: patchResult.patched,
|
|
896
946
|
failedClients: patchResult.failed,
|
|
947
|
+
skippedClients: patchResult.skipped || [],
|
|
897
948
|
smokeSkipped: !!options.noSmoke,
|
|
898
949
|
warmed: !!warm && warm.status === 'pass'
|
|
899
950
|
}
|
|
@@ -1229,11 +1280,12 @@ function commandHelpMap() {
|
|
|
1229
1280
|
examples: ['genexus-mcp config show', 'genexus-mcp config show --full --format json']
|
|
1230
1281
|
},
|
|
1231
1282
|
init: {
|
|
1232
|
-
usage: 'genexus-mcp init [--kb <path>] [--gx <path>] [--write-clients] [--no-smoke] [--warm] [--format ...] OR genexus-mcp init --interactive',
|
|
1283
|
+
usage: 'genexus-mcp init [--kb <path>] [--gx <path>] [--write-clients] [--clients <csv>] [--all-clients] [--no-smoke] [--warm] [--format ...] OR genexus-mcp init --interactive',
|
|
1233
1284
|
examples: [
|
|
1234
1285
|
'genexus-mcp init # zero-config: auto-discovers GX from registry/Program Files and KB from cwd',
|
|
1235
1286
|
'genexus-mcp init --kb "C:\\KBs\\MyKB" --gx "C:\\Program Files (x86)\\GeneXus\\GeneXus18"',
|
|
1236
|
-
'genexus-mcp init --interactive',
|
|
1287
|
+
'genexus-mcp init --interactive # prompts per detected agent (Claude Desktop/Code, Gemini CLI, Cursor, Codex CLI, OpenCode, ...)',
|
|
1288
|
+
'genexus-mcp init --kb <path> --gx <path> --write-clients --clients claude-code,gemini-cli,cursor',
|
|
1237
1289
|
'genexus-mcp init --kb <path> --gx <path> --no-smoke'
|
|
1238
1290
|
]
|
|
1239
1291
|
},
|
package/cli/index.js
CHANGED
|
@@ -39,6 +39,8 @@ const GLOBAL_DEFAULTS = {
|
|
|
39
39
|
fields: null,
|
|
40
40
|
interactive: false,
|
|
41
41
|
writeClients: false,
|
|
42
|
+
clients: null,
|
|
43
|
+
allClients: false,
|
|
42
44
|
mcpSmoke: false,
|
|
43
45
|
noSmoke: false,
|
|
44
46
|
warm: false,
|
|
@@ -186,6 +188,15 @@ function parseArgs(argv) {
|
|
|
186
188
|
else result.unknownFlags.push('--query requires a value');
|
|
187
189
|
break;
|
|
188
190
|
}
|
|
191
|
+
case 'clients': {
|
|
192
|
+
const val = takeValue();
|
|
193
|
+
if (val) result.options.clients = val;
|
|
194
|
+
else result.unknownFlags.push('--clients requires a value');
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case 'all-clients':
|
|
198
|
+
result.options.allClients = true;
|
|
199
|
+
break;
|
|
189
200
|
case 'action': {
|
|
190
201
|
const val = takeValue();
|
|
191
202
|
if (val) result.options.action = val;
|
package/cli/lib/config.js
CHANGED
|
@@ -58,23 +58,73 @@ function discoverGeneXusFromRegistry() {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
function discoverGeneXusInstallation() {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
'
|
|
64
|
-
'C:\\Program Files (x86)\\GeneXus\\GeneXus16',
|
|
65
|
-
'C:\\Program Files\\GeneXus\\GeneXus18',
|
|
66
|
-
'C:\\Program Files\\GeneXus\\GeneXus17'
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
for (const candidate of possible) {
|
|
70
|
-
if (fs.existsSync(path.join(candidate, 'genexus.exe'))) {
|
|
71
|
-
return candidate;
|
|
72
|
-
}
|
|
61
|
+
if (process.env.GENEXUS_HOME) {
|
|
62
|
+
const candidate = process.env.GENEXUS_HOME.replace(/[\\/]+$/, '');
|
|
63
|
+
if (fs.existsSync(path.join(candidate, 'genexus.exe'))) return candidate;
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
const fromRegistry = discoverGeneXusFromRegistry();
|
|
76
67
|
if (fromRegistry) return fromRegistry;
|
|
77
68
|
|
|
69
|
+
const programDirs = [];
|
|
70
|
+
if (process.env['ProgramFiles(x86)']) programDirs.push(process.env['ProgramFiles(x86)']);
|
|
71
|
+
if (process.env.ProgramFiles) programDirs.push(process.env.ProgramFiles);
|
|
72
|
+
// Cover non-default drives where IT often installs SDKs.
|
|
73
|
+
for (const drive of ['C', 'D', 'E']) {
|
|
74
|
+
programDirs.push(`${drive}:\\Program Files (x86)`);
|
|
75
|
+
programDirs.push(`${drive}:\\Program Files`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const versions = ['GeneXus18', 'GeneXus17', 'GeneXus16'];
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
for (const base of programDirs) {
|
|
81
|
+
const root = path.join(base, 'GeneXus');
|
|
82
|
+
const key = root.toLowerCase();
|
|
83
|
+
if (seen.has(key)) continue;
|
|
84
|
+
seen.add(key);
|
|
85
|
+
for (const ver of versions) {
|
|
86
|
+
const candidate = path.join(root, ver);
|
|
87
|
+
if (fs.existsSync(path.join(candidate, 'genexus.exe'))) {
|
|
88
|
+
return candidate;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Also scan any GeneXus* sibling (e.g. custom-named "GeneXus18 U10").
|
|
92
|
+
try {
|
|
93
|
+
if (fs.existsSync(root)) {
|
|
94
|
+
for (const entry of fs.readdirSync(root)) {
|
|
95
|
+
if (!/^GeneXus/i.test(entry)) continue;
|
|
96
|
+
const candidate = path.join(root, entry);
|
|
97
|
+
if (fs.existsSync(path.join(candidate, 'genexus.exe'))) {
|
|
98
|
+
return candidate;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const fromPath = discoverGeneXusFromPath();
|
|
107
|
+
if (fromPath) return fromPath;
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function discoverGeneXusFromPath() {
|
|
113
|
+
if (process.platform !== 'win32') return null;
|
|
114
|
+
try {
|
|
115
|
+
const { execFileSync } = require('child_process');
|
|
116
|
+
const out = execFileSync('where.exe', ['genexus.exe'], {
|
|
117
|
+
encoding: 'utf8',
|
|
118
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
119
|
+
windowsHide: true,
|
|
120
|
+
timeout: 3000
|
|
121
|
+
});
|
|
122
|
+
const first = out.split(/\r?\n/).map((s) => s.trim()).find(Boolean);
|
|
123
|
+
if (first && fs.existsSync(first)) {
|
|
124
|
+
return path.dirname(first);
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
78
128
|
return null;
|
|
79
129
|
}
|
|
80
130
|
|
|
@@ -145,77 +195,132 @@ function createConfigFile(kbPath, gxPath) {
|
|
|
145
195
|
};
|
|
146
196
|
}
|
|
147
197
|
|
|
148
|
-
function
|
|
149
|
-
const clients = getClientConfigTargets();
|
|
150
|
-
|
|
198
|
+
function getLauncher() {
|
|
151
199
|
// Set by scripts/install.ps1 for fixed-path corporate installs — clients
|
|
152
200
|
// launch the gateway exe directly instead of resolving via the npx cache.
|
|
153
201
|
const directExe = process.env.GENEXUS_MCP_GATEWAY_EXE;
|
|
154
|
-
|
|
202
|
+
return directExe
|
|
155
203
|
? { command: directExe, args: [] }
|
|
156
204
|
: { command: process.platform === 'win32' ? 'npx.cmd' : 'npx', args: ['-y', 'genexus-mcp@latest'] };
|
|
205
|
+
}
|
|
157
206
|
|
|
158
|
-
|
|
159
|
-
const
|
|
207
|
+
function getClientConfigTargets() {
|
|
208
|
+
const home = os.homedir();
|
|
209
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
210
|
+
return [
|
|
211
|
+
{
|
|
212
|
+
id: 'claude-desktop-win',
|
|
213
|
+
name: 'Claude Desktop (Windows)',
|
|
214
|
+
format: 'mcpServers',
|
|
215
|
+
path: path.join(home, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json'),
|
|
216
|
+
platforms: ['win32']
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'claude-desktop-mac',
|
|
220
|
+
name: 'Claude Desktop (macOS)',
|
|
221
|
+
format: 'mcpServers',
|
|
222
|
+
path: path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
|
|
223
|
+
platforms: ['darwin']
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: 'antigravity',
|
|
227
|
+
name: 'Antigravity',
|
|
228
|
+
format: 'mcpServers',
|
|
229
|
+
path: path.join(home, '.gemini', 'antigravity', 'mcp_config.json')
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
id: 'claude-code',
|
|
233
|
+
name: 'Claude Code',
|
|
234
|
+
format: 'mcpServers',
|
|
235
|
+
path: path.join(home, '.claude.json')
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
id: 'gemini-cli',
|
|
239
|
+
name: 'Gemini CLI',
|
|
240
|
+
format: 'mcpServers',
|
|
241
|
+
path: path.join(home, '.gemini', 'settings.json')
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: 'cursor',
|
|
245
|
+
name: 'Cursor',
|
|
246
|
+
format: 'mcpServers',
|
|
247
|
+
path: path.join(home, '.cursor', 'mcp.json')
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: 'opencode',
|
|
251
|
+
name: 'OpenCode',
|
|
252
|
+
format: 'opencode',
|
|
253
|
+
path: path.join(xdgConfig, 'opencode', 'opencode.json')
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: 'codex-cli',
|
|
257
|
+
name: 'Codex CLI',
|
|
258
|
+
format: 'codex-toml',
|
|
259
|
+
path: path.join(home, '.codex', 'config.toml')
|
|
260
|
+
}
|
|
261
|
+
];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function listSupportedClientIds() {
|
|
265
|
+
return getClientConfigTargets().map((c) => c.id);
|
|
266
|
+
}
|
|
160
267
|
|
|
161
|
-
|
|
162
|
-
|
|
268
|
+
function filterClientTargets(targets, opts = {}) {
|
|
269
|
+
const { ids, onlyExisting, platform } = opts;
|
|
270
|
+
let out = targets;
|
|
271
|
+
if (platform) out = out.filter((c) => !c.platforms || c.platforms.includes(platform));
|
|
272
|
+
if (ids && ids.length) {
|
|
273
|
+
const set = new Set(ids);
|
|
274
|
+
out = out.filter((c) => set.has(c.id));
|
|
275
|
+
}
|
|
276
|
+
if (onlyExisting) out = out.filter((c) => fs.existsSync(c.path));
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
163
279
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
280
|
+
function patchClientConfig(targetConfigPath, opts = {}) {
|
|
281
|
+
const launcher = getLauncher();
|
|
282
|
+
const onlyExisting = opts.onlyExisting !== false;
|
|
283
|
+
const candidates = filterClientTargets(getClientConfigTargets(), {
|
|
284
|
+
ids: opts.ids,
|
|
285
|
+
platform: process.platform
|
|
286
|
+
});
|
|
170
287
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
288
|
+
const patched = [];
|
|
289
|
+
const failed = [];
|
|
290
|
+
const skipped = [];
|
|
174
291
|
|
|
175
|
-
|
|
292
|
+
for (const client of candidates) {
|
|
293
|
+
if (onlyExisting && !fs.existsSync(client.path)) {
|
|
294
|
+
skipped.push({ client: client.name, reason: 'not installed' });
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
fs.mkdirSync(path.dirname(client.path), { recursive: true });
|
|
299
|
+
applyClientEntry(client, launcher, targetConfigPath);
|
|
176
300
|
patched.push(client.name);
|
|
177
301
|
} catch (err) {
|
|
178
302
|
failed.push({ client: client.name, reason: err && err.message ? err.message : 'Unknown error' });
|
|
179
303
|
}
|
|
180
304
|
}
|
|
181
305
|
|
|
182
|
-
return { patched, failed };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function getClientConfigTargets() {
|
|
186
|
-
return [
|
|
187
|
-
{ path: path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json'), name: 'Claude Desktop (Windows)' },
|
|
188
|
-
{ path: path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'), name: 'Claude Desktop (macOS)' },
|
|
189
|
-
{ path: path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json'), name: 'Antigravity' },
|
|
190
|
-
{ path: path.join(os.homedir(), '.claude.json'), name: 'Claude Code' }
|
|
191
|
-
];
|
|
306
|
+
return { patched, failed, skipped };
|
|
192
307
|
}
|
|
193
308
|
|
|
194
|
-
function unpatchClientConfig() {
|
|
195
|
-
const
|
|
309
|
+
function unpatchClientConfig(opts = {}) {
|
|
310
|
+
const targets = filterClientTargets(getClientConfigTargets(), {
|
|
311
|
+
ids: opts.ids,
|
|
312
|
+
onlyExisting: true,
|
|
313
|
+
platform: process.platform
|
|
314
|
+
});
|
|
196
315
|
const removed = [];
|
|
197
316
|
const skipped = [];
|
|
198
317
|
const failed = [];
|
|
199
318
|
|
|
200
|
-
for (const client of
|
|
201
|
-
if (!fs.existsSync(client.path)) continue;
|
|
202
|
-
|
|
319
|
+
for (const client of targets) {
|
|
203
320
|
try {
|
|
204
|
-
const
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const cfgObj = parsed || {};
|
|
211
|
-
if (!cfgObj.mcpServers || !cfgObj.mcpServers.genexus) {
|
|
212
|
-
skipped.push({ client: client.name, reason: 'no genexus entry' });
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
delete cfgObj.mcpServers.genexus;
|
|
217
|
-
fs.writeFileSync(client.path, JSON.stringify(cfgObj, null, 2));
|
|
218
|
-
removed.push(client.name);
|
|
321
|
+
const wasRemoved = removeClientEntry(client);
|
|
322
|
+
if (wasRemoved) removed.push(client.name);
|
|
323
|
+
else skipped.push({ client: client.name, reason: 'no genexus entry' });
|
|
219
324
|
} catch (err) {
|
|
220
325
|
failed.push({ client: client.name, reason: err && err.message ? err.message : 'Unknown error' });
|
|
221
326
|
}
|
|
@@ -224,6 +329,133 @@ function unpatchClientConfig() {
|
|
|
224
329
|
return { removed, skipped, failed };
|
|
225
330
|
}
|
|
226
331
|
|
|
332
|
+
function applyClientEntry(client, launcher, targetConfigPath) {
|
|
333
|
+
switch (client.format) {
|
|
334
|
+
case 'mcpServers':
|
|
335
|
+
return applyMcpServersJson(client.path, launcher, targetConfigPath);
|
|
336
|
+
case 'opencode':
|
|
337
|
+
return applyOpenCodeJson(client.path, launcher, targetConfigPath);
|
|
338
|
+
case 'codex-toml':
|
|
339
|
+
return applyCodexToml(client.path, launcher, targetConfigPath);
|
|
340
|
+
default:
|
|
341
|
+
throw new Error(`Unknown client format: ${client.format}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function removeClientEntry(client) {
|
|
346
|
+
switch (client.format) {
|
|
347
|
+
case 'mcpServers':
|
|
348
|
+
return removeMcpServersJson(client.path);
|
|
349
|
+
case 'opencode':
|
|
350
|
+
return removeOpenCodeJson(client.path);
|
|
351
|
+
case 'codex-toml':
|
|
352
|
+
return removeCodexToml(client.path);
|
|
353
|
+
default:
|
|
354
|
+
throw new Error(`Unknown client format: ${client.format}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function applyMcpServersJson(filePath, launcher, targetConfigPath) {
|
|
359
|
+
const parsed = fs.existsSync(filePath) ? readJsonFileSafe(filePath) : {};
|
|
360
|
+
if (parsed === null) throw new Error('Invalid JSON');
|
|
361
|
+
const cfgObj = parsed || {};
|
|
362
|
+
cfgObj.mcpServers = cfgObj.mcpServers || {};
|
|
363
|
+
cfgObj.mcpServers.genexus = { ...launcher, env: { GX_CONFIG_PATH: targetConfigPath } };
|
|
364
|
+
fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function removeMcpServersJson(filePath) {
|
|
368
|
+
const parsed = readJsonFileSafe(filePath);
|
|
369
|
+
if (parsed === null) throw new Error('Invalid JSON');
|
|
370
|
+
const cfgObj = parsed || {};
|
|
371
|
+
if (!cfgObj.mcpServers || !cfgObj.mcpServers.genexus) return false;
|
|
372
|
+
delete cfgObj.mcpServers.genexus;
|
|
373
|
+
fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// OpenCode (sst/opencode) uses `mcp.<name>` with `type: 'local'` and `command: string[]`.
|
|
378
|
+
function applyOpenCodeJson(filePath, launcher, targetConfigPath) {
|
|
379
|
+
const parsed = fs.existsSync(filePath) ? readJsonFileSafe(filePath) : {};
|
|
380
|
+
if (parsed === null) throw new Error('Invalid JSON');
|
|
381
|
+
const cfgObj = parsed || {};
|
|
382
|
+
cfgObj.mcp = cfgObj.mcp || {};
|
|
383
|
+
cfgObj.mcp.genexus = {
|
|
384
|
+
type: 'local',
|
|
385
|
+
command: [launcher.command, ...(launcher.args || [])],
|
|
386
|
+
environment: { GX_CONFIG_PATH: targetConfigPath },
|
|
387
|
+
enabled: true
|
|
388
|
+
};
|
|
389
|
+
fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function removeOpenCodeJson(filePath) {
|
|
393
|
+
const parsed = readJsonFileSafe(filePath);
|
|
394
|
+
if (parsed === null) throw new Error('Invalid JSON');
|
|
395
|
+
const cfgObj = parsed || {};
|
|
396
|
+
if (!cfgObj.mcp || !cfgObj.mcp.genexus) return false;
|
|
397
|
+
delete cfgObj.mcp.genexus;
|
|
398
|
+
fs.writeFileSync(filePath, JSON.stringify(cfgObj, null, 2));
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Codex CLI uses TOML. We do a minimal text-merge: strip any existing
|
|
403
|
+
// [mcp_servers.genexus*] blocks and append fresh ones. Brittle on hand-edited
|
|
404
|
+
// files that put other keys after our blocks without a blank line, but
|
|
405
|
+
// adequate for the typical machine-managed config.
|
|
406
|
+
function applyCodexToml(filePath, launcher, targetConfigPath) {
|
|
407
|
+
let existing = '';
|
|
408
|
+
if (fs.existsSync(filePath)) existing = fs.readFileSync(filePath, 'utf8');
|
|
409
|
+
const stripped = stripCodexGenexusBlocks(existing);
|
|
410
|
+
const args = launcher.args || [];
|
|
411
|
+
const lines = [];
|
|
412
|
+
if (stripped.length && !stripped.endsWith('\n')) lines.push('');
|
|
413
|
+
if (stripped.length) lines.push('');
|
|
414
|
+
lines.push('[mcp_servers.genexus]');
|
|
415
|
+
lines.push(`command = ${tomlString(launcher.command)}`);
|
|
416
|
+
lines.push(`args = [${args.map(tomlString).join(', ')}]`);
|
|
417
|
+
lines.push('');
|
|
418
|
+
lines.push('[mcp_servers.genexus.env]');
|
|
419
|
+
lines.push(`GX_CONFIG_PATH = ${tomlString(targetConfigPath)}`);
|
|
420
|
+
lines.push('');
|
|
421
|
+
fs.writeFileSync(filePath, stripped + lines.join('\n'));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function removeCodexToml(filePath) {
|
|
425
|
+
if (!fs.existsSync(filePath)) return false;
|
|
426
|
+
const existing = fs.readFileSync(filePath, 'utf8');
|
|
427
|
+
const stripped = stripCodexGenexusBlocks(existing);
|
|
428
|
+
if (stripped === existing) return false;
|
|
429
|
+
fs.writeFileSync(filePath, stripped);
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function stripCodexGenexusBlocks(content) {
|
|
434
|
+
// Walk line-by-line so values like `args = []` don't confuse the parser.
|
|
435
|
+
// A section ends at the next line that starts a [section] header (top-level
|
|
436
|
+
// tables and arrays of tables only — must begin at column 0).
|
|
437
|
+
const lines = content.split('\n');
|
|
438
|
+
const out = [];
|
|
439
|
+
const headerRe = /^\[\[?[A-Za-z0-9_."'-]+/;
|
|
440
|
+
const ourRe = /^\[\[?mcp_servers\.genexus(\.[A-Za-z0-9_."'-]+)?\]\]?\s*$/;
|
|
441
|
+
let inOurs = false;
|
|
442
|
+
for (const line of lines) {
|
|
443
|
+
if (headerRe.test(line)) {
|
|
444
|
+
inOurs = ourRe.test(line);
|
|
445
|
+
if (inOurs) continue;
|
|
446
|
+
}
|
|
447
|
+
if (inOurs) continue;
|
|
448
|
+
out.push(line);
|
|
449
|
+
}
|
|
450
|
+
// Collapse 3+ trailing blank lines we may have left behind.
|
|
451
|
+
return out.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function tomlString(value) {
|
|
455
|
+
const s = String(value);
|
|
456
|
+
return `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
457
|
+
}
|
|
458
|
+
|
|
227
459
|
function getLocalAppDataCacheDir() {
|
|
228
460
|
if (process.platform !== 'win32') return null;
|
|
229
461
|
const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
@@ -388,6 +620,8 @@ module.exports = {
|
|
|
388
620
|
patchClientConfig,
|
|
389
621
|
unpatchClientConfig,
|
|
390
622
|
getClientConfigTargets,
|
|
623
|
+
listSupportedClientIds,
|
|
624
|
+
filterClientTargets,
|
|
391
625
|
getLocalAppDataCacheDir,
|
|
392
626
|
readGeneXusVersionFromInstall,
|
|
393
627
|
readKbCatalog,
|
package/cli/run.test.js
CHANGED
|
@@ -400,7 +400,7 @@ test('tool_definitions.json is valid and disambiguation tools have use-when guid
|
|
|
400
400
|
assert.ok(Array.isArray(defs) && defs.length > 0, 'tool defs should be a non-empty array');
|
|
401
401
|
|
|
402
402
|
const byName = Object.fromEntries(defs.map((t) => [t.name, t]));
|
|
403
|
-
const disambiguationTools = ['genexus_inspect', 'genexus_analyze', '
|
|
403
|
+
const disambiguationTools = ['genexus_inspect', 'genexus_analyze', 'genexus_doc'];
|
|
404
404
|
for (const name of disambiguationTools) {
|
|
405
405
|
assert.ok(byName[name], `${name} should exist`);
|
|
406
406
|
const desc = byName[name].description || '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genexus-mcp",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
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": [
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"compilationOptions": {},
|
|
7
7
|
"targets": {
|
|
8
8
|
".NETCoreApp,Version=v8.0": {
|
|
9
|
-
"GxMcp.Gateway/2.3.
|
|
9
|
+
"GxMcp.Gateway/2.3.4": {
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"Newtonsoft.Json": "13.0.3",
|
|
12
12
|
"System.Management": "10.0.5",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"libraries": {
|
|
84
|
-
"GxMcp.Gateway/2.3.
|
|
84
|
+
"GxMcp.Gateway/2.3.4": {
|
|
85
85
|
"type": "project",
|
|
86
86
|
"serviceable": false,
|
|
87
87
|
"sha512": ""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
[
|
|
2
|
-
{"name":"genexus_whoami","description":"Return current KB context, GeneXus install, and MCP version.","inputSchema":{"type":"object","properties":{}}},
|
|
3
|
-
{"name":"genexus_query","description":"Search objects. Prefixes: usedby:, type:, description:, parent:, parentPath:. Triggers KB index build on first call against a fresh install — _meta.partial=true means more results will appear as indexing continues (watch notifications/progress). Literal-name queries skip the index via direct SDK lookup. genexus_read / genexus_edit / genexus_list_objects / genexus_lifecycle are index-independent and always return immediately.","inputSchema":{"type":"object","properties":{"query":{"type":"string"},"typeFilter":{"type":"string"},"domainFilter":{"type":"string"},"limit":{"type":"integer"},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["query"]}},
|
|
4
|
-
{"name":"genexus_list_objects","description":"List objects with pagination. Feed nextOffset until hasMore=false. Returns minimal shape by default (name, type, path, parent); verbose=true for full shape.","inputSchema":{"type":"object","properties":{"filter":{"type":"string"},"limit":{"type":"integer"},"offset":{"type":"integer"},"parent":{"type":"string"},"parentPath":{"type":"string"},"typeFilter":{"type":"string"},"verbose":{"type":"boolean","description":"When true, returns full item shape (default false)."},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
5
|
-
{"name":"genexus_read","description":"Read source/metadata parts. name or targets (mutually exclusive). Paginate with offset/limit. Use parts=[\"Source\",\"Variables\",...] to select specific parts and skip the rest.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"}},"part":{"type":"string"},"parts":{"type":"array","items":{"type":"string"},"description":"When set, only the listed parts are returned in a combined response. Mutually exclusive with part/offset/limit."},"offset":{"type":"integer"},"limit":{"type":"integer"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
6
|
-
{"name":"genexus_edit","description":"Edit source/metadata. name or targets (mutually exclusive). Use dryRun=true with mode=patch first. Returns post_state.diff (unified diff) by default; verbose=true adds slices (±15 lines); return_post_state=false opts out.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"part":{"type":"string"},"mode":{"type":"string","enum":["full","patch","ops"]},"content":{"type":"string"},"ops":{"type":"array","items":{"type":"object","properties":{"op":{"type":"string"}},"required":["op"]}},"patch":{},"context":{"type":"string"},"operation":{"type":"string","enum":["Replace","Insert_After","Append"]},"expectedCount":{"type":"integer"},"dryRun":{"type":"boolean"},"verifyRollback":{"type":"boolean"},"targets":{"type":"array","items":{"type":"object","required":["name","content"]}},"type":{"type":"string"},"return_post_state":{"type":"boolean","description":"Set false to omit post_state.diff from the response (default true)."},"verbose":{"type":"boolean","description":"When true, adds slices (±15 lines around each hunk) to post_state (default false)."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
7
|
-
{"name":"genexus_inspect","description":"Snapshot of an object: metadata, variables, structure, signature.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"include":{"type":"array","items":{"type":"string","enum":["metadata","variables","signature","structure","parts","controls","events_repertoire"]}},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
|
|
8
|
-
{"name":"genexus_analyze","description":"Semantic analysis: impact, dependencies, complexity, naming, summary, explain.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"mode":{"type":"string","enum":["linter","navigation","hierarchy","impact","data_context","ui_context","pattern_metadata","summary","explain"]},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."},"code":{"type":"string","description":"Required when mode=explain."}},"required":["name","mode"]}},
|
|
9
|
-
{"name":"genexus_inject_context","description":"Inject SDT structures and Procedure signatures of called objects.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"recursive":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
|
|
10
|
-
{"name":"genexus_lifecycle","description":"Build/validate/index/poll. action=build is non-blocking when estimated_seconds>=20 — returns {job_id, status:running} and surfaces _meta.background_jobs on the next call. action=status accepts taskId OR job_id via target; wait_seconds>0 long-polls up to 25s.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["build","cancel","rebuild","reorg","validate","validate-kb","sync","index","status","result","snapshots-list","snapshots-restore"]},"target":{"type":"string","description":"Object name(s), taskId, job_id, or op:<operationId>."},"code":{"type":"string"},"limit":{"type":"integer"},"snapshotPath":{"type":"string"},"estimated_seconds":{"type":"integer","description":"Build: <20 forces sync fast-path (one turn); >=20 (default 60) goes async."},"wait_seconds":{"type":"integer","description":"Status: 0-25. >0 long-polls server-side until terminal or timeout."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
11
|
-
{"name":"genexus_forge","description":"Generate new code or structures from templates or translations.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["scaffold","translate","sample"]},"type":{"type":"string"},"name":{"type":"string"},"content":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
12
|
-
{"name":"genexus_test","description":"Execute native GeneXus tests (GXtest).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
|
|
13
|
-
{"name":"genexus_create_object","description":"Create a new GeneXus object in the active KB.","inputSchema":{"type":"object","properties":{"type":{"type":"string","description":"e.g. Procedure, Transaction, WebPanel, SDT."},"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["type","name"]}},
|
|
14
|
-
{"name":"genexus_logs","description":"Read worker_debug.log tail for error diagnosis or correlation.","inputSchema":{"type":"object","properties":{"lines":{"type":"integer"},"filterCorrelation":{"type":"string"},"grep":{"type":"string"}}}},
|
|
15
|
-
{"name":"genexus_worker_reload","description":"Hot-reload worker: copy new binaries and respawn.","inputSchema":{"type":"object","properties":{"sourceDir":{"type":"string","description":"Absolute path to freshly built bin/Release."}},"required":["sourceDir"]}},
|
|
16
|
-
{"name":"genexus_delete_object","description":"Delete an object from the KB. Irreversible — confirm=true required.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"},"confirm":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","confirm"]}},
|
|
17
|
-
{"name":"genexus_export_object","description":"Export a GeneXus object part to a text file on disk.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"outputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"overwrite":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","outputPath"]}},
|
|
18
|
-
{"name":"genexus_import_object","description":"Import a text file from disk into a GeneXus object part.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"inputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","inputPath"]}},
|
|
19
|
-
{"name":"genexus_refactor","description":"Run GeneXus refactor: rename, extract procedure, or WWP condition set.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["RenameAttribute","RenameVariable","RenameObject","ExtractProcedure","WWPSetCondition"]},"target":{"type":"string","description":"Primary object or symbol to refactor."},"newName":{"type":"string"},"objectName":{"type":"string"},"code":{"type":"string"},"procedureName":{"type":"string"},"controlAttribute":{"type":"string"},"value":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
20
|
-
{"name":"genexus_add_variable","description":"Add a variable to the Variables part of a GeneXus object.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"varName":{"type":"string"},"typeName":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","varName"]}},
|
|
21
|
-
{"name":"genexus_format","description":"Format a GeneXus code snippet using worker rules.","inputSchema":{"type":"object","properties":{"code":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["code"]}},
|
|
22
|
-
{"name":"genexus_properties","description":"Read or update GeneXus object properties.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set"]},"name":{"type":"string"},"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
|
|
23
|
-
{"name":"genexus_asset","description":"Find, read, or write binary assets inside the active KB.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["find","read","write"]},"path":{"type":"string"},"includeContent":{"type":"boolean"},"maxBytes":{"type":"integer"},"pattern":{"type":"string"},"relativeRoot":{"type":"string"},"limit":{"type":"integer"},"contentBase64":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
24
|
-
{"name":"genexus_history","description":"List, read, save, or restore GeneXus object history snapshots.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get_source","save","restore"]},"name":{"type":"string"},"versionId":{"type":"integer"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
|
|
25
|
-
{"name":"genexus_structure","description":"Read or update visual and logical structure of GeneXus objects.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_visual","update_visual","get_indexes","get_logic"]},"name":{"type":"string"},"payload":{"type":"object","description":"Update payload for update_visual."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
|
|
26
|
-
{"name":"genexus_layout","description":"Native SDK layout/WebForm operations: get_tree, set_property, find_controls, inspect_surface, scan_mutators.","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"]},"name":{"type":"string"},"target":{"type":"string","description":"Object name alias for backward compatibility."},"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"},"query":{"type":"string"},"changes":{"type":"array","items":{"type":"object","required":["control","propertyName","value"]}},"limit":{"type":"integer"},"currentName":{"type":"string"},"newName":{"type":"string"},"printBlockName":{"type":"string"},"height":{"type":"integer"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
27
|
-
{"name":"genexus_doc","description":"Generate structured docs (markdown, sequence diagrams,
|
|
28
|
-
{"name":"genexus_search_source","description":"Regex/semantic search across Procedure/DataProvider/WebPanel/Transaction source.","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"},"scope":{"type":"array","items":{"type":"string"}},"maxResults":{"type":"integer"},"caseSensitive":{"type":"boolean"},"includeComments":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
29
|
-
{"name":"genexus_kb","description":"Manage open KBs: list (with PID/RSS/idle), open (acquire Worker), close (release), set_default (persist alias to config.json).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","open","close","set_default"]},"alias":{"type":"string","description":"KB alias (for open/close). For open, auto-generated from path basename if omitted."},"path":{"type":"string","description":"Absolute path to the KB (required for action=open if alias is not declared in config)."}},"required":["action"]}},
|
|
30
|
-
{"name":"genexus_sql","description":"SQL for a Transaction/Table (DDL) or a procedure For Each navigation.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["ddl","navigation"]},"name":{"type":"string"},"includeSubordinated":{"type":"boolean","description":"action=ddl only."},"levelNumber":{"type":"integer","description":"action=navigation only."},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}}
|
|
31
|
-
]
|
|
1
|
+
[
|
|
2
|
+
{"name":"genexus_whoami","description":"Return current KB context, GeneXus install, and MCP version.","inputSchema":{"type":"object","properties":{}}},
|
|
3
|
+
{"name":"genexus_query","description":"Search objects. Prefixes: usedby:, type:, description:, parent:, parentPath:. Triggers KB index build on first call against a fresh install — _meta.partial=true means more results will appear as indexing continues (watch notifications/progress). Literal-name queries skip the index via direct SDK lookup. genexus_read / genexus_edit / genexus_list_objects / genexus_lifecycle are index-independent and always return immediately.","inputSchema":{"type":"object","properties":{"query":{"type":"string"},"typeFilter":{"type":"string"},"domainFilter":{"type":"string"},"limit":{"type":"integer"},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["query"]}},
|
|
4
|
+
{"name":"genexus_list_objects","description":"List objects with pagination. Feed nextOffset until hasMore=false. Returns minimal shape by default (name, type, path, parent); verbose=true for full shape.","inputSchema":{"type":"object","properties":{"filter":{"type":"string"},"limit":{"type":"integer"},"offset":{"type":"integer"},"parent":{"type":"string"},"parentPath":{"type":"string"},"typeFilter":{"type":"string"},"verbose":{"type":"boolean","description":"When true, returns full item shape (default false)."},"inline_read_top":{"type":"integer","description":"0-3. When >0, response includes inline_reads array with full content of the top N matches. Capped at 3."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
5
|
+
{"name":"genexus_read","description":"Read source/metadata parts. name or targets (mutually exclusive). Paginate with offset/limit. Use parts=[\"Source\",\"Variables\",...] to select specific parts and skip the rest.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"targets":{"type":"array","items":{"type":"string"}},"part":{"type":"string"},"parts":{"type":"array","items":{"type":"string"},"description":"When set, only the listed parts are returned in a combined response. Mutually exclusive with part/offset/limit."},"offset":{"type":"integer"},"limit":{"type":"integer"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
6
|
+
{"name":"genexus_edit","description":"Edit source/metadata. name or targets (mutually exclusive). Use dryRun=true with mode=patch first. Returns post_state.diff (unified diff) by default; verbose=true adds slices (±15 lines); return_post_state=false opts out.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"part":{"type":"string"},"mode":{"type":"string","enum":["full","patch","ops"]},"content":{"type":"string"},"ops":{"type":"array","items":{"type":"object","properties":{"op":{"type":"string"}},"required":["op"]}},"patch":{"description":"RFC 6902 JSON-Patch array, or legacy string replacement text.","anyOf":[{"type":"string"},{"type":"array","items":{"type":"object"}}]},"context":{"type":"string"},"operation":{"type":"string","enum":["Replace","Insert_After","Append"]},"expectedCount":{"type":"integer"},"dryRun":{"type":"boolean"},"verifyRollback":{"type":"boolean"},"targets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"content":{"type":"string"}},"required":["name","content"]}},"type":{"type":"string"},"return_post_state":{"type":"boolean","description":"Set false to omit post_state.diff from the response (default true)."},"verbose":{"type":"boolean","description":"When true, adds slices (±15 lines around each hunk) to post_state (default false)."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
7
|
+
{"name":"genexus_inspect","description":"Snapshot of an object: metadata, variables, structure, signature. Use when you need raw object shape (parts, controls, events repertoire) without source text — for source/code use genexus_read instead.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"include":{"type":"array","items":{"type":"string","enum":["metadata","variables","signature","structure","parts","controls","events_repertoire"]}},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
|
|
8
|
+
{"name":"genexus_analyze","description":"Semantic analysis: impact, dependencies, complexity, naming, summary, explain. Use when you need cross-object reasoning (callers/callees, impact of a change, navigation paths) — don't use for raw source or single-object metadata (see genexus_read / genexus_inspect).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"mode":{"type":"string","enum":["linter","navigation","hierarchy","impact","data_context","ui_context","pattern_metadata","summary","explain"]},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."},"code":{"type":"string","description":"Required when mode=explain."}},"required":["name","mode"]}},
|
|
9
|
+
{"name":"genexus_inject_context","description":"Inject SDT structures and Procedure signatures of called objects.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"recursive":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
|
|
10
|
+
{"name":"genexus_lifecycle","description":"Build/validate/index/poll. action=build is non-blocking when estimated_seconds>=20 — returns {job_id, status:running} and surfaces _meta.background_jobs on the next call. action=status accepts taskId OR job_id via target; wait_seconds>0 long-polls up to 25s.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["build","cancel","rebuild","reorg","validate","validate-kb","sync","index","status","result","snapshots-list","snapshots-restore"]},"target":{"type":"string","description":"Object name(s), taskId, job_id, or op:<operationId>."},"code":{"type":"string"},"limit":{"type":"integer"},"snapshotPath":{"type":"string"},"estimated_seconds":{"type":"integer","description":"Build: <20 forces sync fast-path (one turn); >=20 (default 60) goes async."},"wait_seconds":{"type":"integer","description":"Status: 0-25. >0 long-polls server-side until terminal or timeout."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
11
|
+
{"name":"genexus_forge","description":"Generate new code or structures from templates or translations.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["scaffold","translate","sample"]},"type":{"type":"string"},"name":{"type":"string"},"content":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
12
|
+
{"name":"genexus_test","description":"Execute native GeneXus tests (GXtest).","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name"]}},
|
|
13
|
+
{"name":"genexus_create_object","description":"Create a new GeneXus object in the active KB.","inputSchema":{"type":"object","properties":{"type":{"type":"string","description":"e.g. Procedure, Transaction, WebPanel, SDT."},"name":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["type","name"]}},
|
|
14
|
+
{"name":"genexus_logs","description":"Read worker_debug.log tail for error diagnosis or correlation.","inputSchema":{"type":"object","properties":{"lines":{"type":"integer"},"filterCorrelation":{"type":"string"},"grep":{"type":"string"}}}},
|
|
15
|
+
{"name":"genexus_worker_reload","description":"Hot-reload worker: copy new binaries and respawn.","inputSchema":{"type":"object","properties":{"sourceDir":{"type":"string","description":"Absolute path to freshly built bin/Release."}},"required":["sourceDir"]}},
|
|
16
|
+
{"name":"genexus_delete_object","description":"Delete an object from the KB. Irreversible — confirm=true required.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"},"confirm":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","confirm"]}},
|
|
17
|
+
{"name":"genexus_export_object","description":"Export a GeneXus object part to a text file on disk.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"outputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"overwrite":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","outputPath"]}},
|
|
18
|
+
{"name":"genexus_import_object","description":"Import a text file from disk into a GeneXus object part.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"inputPath":{"type":"string"},"part":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","inputPath"]}},
|
|
19
|
+
{"name":"genexus_refactor","description":"Run GeneXus refactor: rename, extract procedure, or WWP condition set.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["RenameAttribute","RenameVariable","RenameObject","ExtractProcedure","WWPSetCondition"]},"target":{"type":"string","description":"Primary object or symbol to refactor."},"newName":{"type":"string"},"objectName":{"type":"string"},"code":{"type":"string"},"procedureName":{"type":"string"},"controlAttribute":{"type":"string"},"value":{"type":"string"},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
20
|
+
{"name":"genexus_add_variable","description":"Add a variable to the Variables part of a GeneXus object.","inputSchema":{"type":"object","properties":{"name":{"type":"string"},"varName":{"type":"string"},"typeName":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["name","varName"]}},
|
|
21
|
+
{"name":"genexus_format","description":"Format a GeneXus code snippet using worker rules.","inputSchema":{"type":"object","properties":{"code":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["code"]}},
|
|
22
|
+
{"name":"genexus_properties","description":"Read or update GeneXus object properties.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get","set"]},"name":{"type":"string"},"control":{"type":"string"},"propertyName":{"type":"string"},"value":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
|
|
23
|
+
{"name":"genexus_asset","description":"Find, read, or write binary assets inside the active KB.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["find","read","write"]},"path":{"type":"string"},"includeContent":{"type":"boolean"},"maxBytes":{"type":"integer"},"pattern":{"type":"string"},"relativeRoot":{"type":"string"},"limit":{"type":"integer"},"contentBase64":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
24
|
+
{"name":"genexus_history","description":"List, read, save, or restore GeneXus object history snapshots.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","get_source","save","restore"]},"name":{"type":"string"},"versionId":{"type":"integer"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
|
|
25
|
+
{"name":"genexus_structure","description":"Read or update visual and logical structure of GeneXus objects.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["get_visual","update_visual","get_indexes","get_logic"]},"name":{"type":"string"},"payload":{"type":"object","description":"Update payload for update_visual."},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}},
|
|
26
|
+
{"name":"genexus_layout","description":"Native SDK layout/WebForm operations: get_tree, set_property, find_controls, inspect_surface, scan_mutators.","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"]},"name":{"type":"string"},"target":{"type":"string","description":"Object name alias for backward compatibility."},"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":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action"]}},
|
|
27
|
+
{"name":"genexus_doc","description":"Generate structured docs (markdown wiki, sequence diagrams, health reports). Use when producing human-readable artifacts about an object or domain — don't use for programmatic analysis (see genexus_analyze) or source reading (see genexus_read).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["wiki","visualize","health"]},"target":{"type":"string","description":"Object or domain name."}},"required":["action"]}},
|
|
28
|
+
{"name":"genexus_search_source","description":"Regex/semantic search across Procedure/DataProvider/WebPanel/Transaction source.","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"},"scope":{"type":"array","items":{"type":"string"}},"maxResults":{"type":"integer"},"caseSensitive":{"type":"boolean"},"includeComments":{"type":"boolean"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}}}},
|
|
29
|
+
{"name":"genexus_kb","description":"Manage open KBs: list (with PID/RSS/idle), open (acquire Worker), close (release), set_default (persist alias to config.json).","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["list","open","close","set_default"]},"alias":{"type":"string","description":"KB alias (for open/close). For open, auto-generated from path basename if omitted."},"path":{"type":"string","description":"Absolute path to the KB (required for action=open if alias is not declared in config)."}},"required":["action"]}},
|
|
30
|
+
{"name":"genexus_sql","description":"SQL for a Transaction/Table (DDL) or a procedure For Each navigation.","inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["ddl","navigation"]},"name":{"type":"string"},"includeSubordinated":{"type":"boolean","description":"action=ddl only."},"levelNumber":{"type":"integer","description":"action=navigation only."},"type":{"type":"string"},"kb":{"type":"string","description":"Target KB (alias or path). Required when 2+ KBs are open."}},"required":["action","name"]}}
|
|
31
|
+
]
|
|
Binary file
|
|
Binary file
|