unreal-engine-mcp-server 0.4.0 → 0.4.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/.env.production +1 -1
- package/.github/copilot-instructions.md +45 -0
- package/.github/workflows/publish-mcp.yml +3 -2
- package/README.md +21 -5
- package/dist/index.js +124 -31
- package/dist/prompts/index.d.ts +10 -3
- package/dist/prompts/index.js +186 -7
- package/dist/resources/actors.d.ts +19 -1
- package/dist/resources/actors.js +55 -64
- package/dist/resources/assets.js +46 -62
- package/dist/resources/levels.d.ts +21 -3
- package/dist/resources/levels.js +29 -54
- package/dist/tools/actors.d.ts +3 -14
- package/dist/tools/actors.js +246 -302
- package/dist/tools/animation.d.ts +57 -102
- package/dist/tools/animation.js +429 -450
- package/dist/tools/assets.d.ts +13 -2
- package/dist/tools/assets.js +52 -44
- package/dist/tools/audio.d.ts +22 -13
- package/dist/tools/audio.js +467 -121
- package/dist/tools/blueprint.d.ts +32 -13
- package/dist/tools/blueprint.js +699 -448
- package/dist/tools/build_environment_advanced.d.ts +0 -1
- package/dist/tools/build_environment_advanced.js +190 -45
- package/dist/tools/consolidated-tool-definitions.js +78 -252
- package/dist/tools/consolidated-tool-handlers.js +506 -133
- package/dist/tools/debug.d.ts +72 -10
- package/dist/tools/debug.js +167 -31
- package/dist/tools/editor.d.ts +9 -2
- package/dist/tools/editor.js +30 -44
- package/dist/tools/foliage.d.ts +34 -15
- package/dist/tools/foliage.js +97 -107
- package/dist/tools/introspection.js +19 -21
- package/dist/tools/landscape.d.ts +1 -2
- package/dist/tools/landscape.js +311 -168
- package/dist/tools/level.d.ts +3 -28
- package/dist/tools/level.js +642 -192
- package/dist/tools/lighting.d.ts +14 -3
- package/dist/tools/lighting.js +236 -123
- package/dist/tools/materials.d.ts +25 -7
- package/dist/tools/materials.js +102 -79
- package/dist/tools/niagara.d.ts +10 -12
- package/dist/tools/niagara.js +74 -94
- package/dist/tools/performance.d.ts +12 -4
- package/dist/tools/performance.js +38 -79
- package/dist/tools/physics.d.ts +34 -10
- package/dist/tools/physics.js +364 -292
- package/dist/tools/rc.js +97 -23
- package/dist/tools/sequence.d.ts +1 -0
- package/dist/tools/sequence.js +125 -22
- package/dist/tools/ui.d.ts +31 -4
- package/dist/tools/ui.js +83 -66
- package/dist/tools/visual.d.ts +11 -0
- package/dist/tools/visual.js +245 -30
- package/dist/types/tool-types.d.ts +0 -6
- package/dist/types/tool-types.js +1 -8
- package/dist/unreal-bridge.d.ts +32 -2
- package/dist/unreal-bridge.js +621 -127
- package/dist/utils/elicitation.d.ts +57 -0
- package/dist/utils/elicitation.js +104 -0
- package/dist/utils/error-handler.d.ts +0 -33
- package/dist/utils/error-handler.js +4 -111
- package/dist/utils/http.d.ts +2 -22
- package/dist/utils/http.js +12 -75
- package/dist/utils/normalize.d.ts +4 -4
- package/dist/utils/normalize.js +15 -7
- package/dist/utils/python-output.d.ts +18 -0
- package/dist/utils/python-output.js +290 -0
- package/dist/utils/python.d.ts +2 -0
- package/dist/utils/python.js +4 -0
- package/dist/utils/response-validator.js +28 -2
- package/dist/utils/result-helpers.d.ts +27 -0
- package/dist/utils/result-helpers.js +147 -0
- package/dist/utils/safe-json.d.ts +0 -2
- package/dist/utils/safe-json.js +0 -43
- package/dist/utils/validation.d.ts +16 -0
- package/dist/utils/validation.js +70 -7
- package/mcp-config-example.json +2 -2
- package/package.json +10 -9
- package/server.json +37 -14
- package/src/index.ts +130 -33
- package/src/prompts/index.ts +211 -13
- package/src/resources/actors.ts +59 -44
- package/src/resources/assets.ts +48 -51
- package/src/resources/levels.ts +35 -45
- package/src/tools/actors.ts +269 -313
- package/src/tools/animation.ts +556 -539
- package/src/tools/assets.ts +53 -43
- package/src/tools/audio.ts +507 -113
- package/src/tools/blueprint.ts +778 -462
- package/src/tools/build_environment_advanced.ts +266 -64
- package/src/tools/consolidated-tool-definitions.ts +90 -264
- package/src/tools/consolidated-tool-handlers.ts +630 -121
- package/src/tools/debug.ts +176 -33
- package/src/tools/editor.ts +35 -37
- package/src/tools/foliage.ts +110 -104
- package/src/tools/introspection.ts +24 -22
- package/src/tools/landscape.ts +334 -181
- package/src/tools/level.ts +683 -182
- package/src/tools/lighting.ts +244 -123
- package/src/tools/materials.ts +114 -83
- package/src/tools/niagara.ts +87 -81
- package/src/tools/performance.ts +49 -88
- package/src/tools/physics.ts +393 -299
- package/src/tools/rc.ts +102 -24
- package/src/tools/sequence.ts +136 -28
- package/src/tools/ui.ts +101 -70
- package/src/tools/visual.ts +250 -29
- package/src/types/tool-types.ts +0 -9
- package/src/unreal-bridge.ts +658 -140
- package/src/utils/elicitation.ts +129 -0
- package/src/utils/error-handler.ts +4 -159
- package/src/utils/http.ts +16 -115
- package/src/utils/normalize.ts +20 -10
- package/src/utils/python-output.ts +351 -0
- package/src/utils/python.ts +3 -0
- package/src/utils/response-validator.ts +25 -2
- package/src/utils/result-helpers.ts +193 -0
- package/src/utils/safe-json.ts +0 -50
- package/src/utils/validation.ts +94 -7
- package/tests/run-unreal-tool-tests.mjs +720 -0
- package/tsconfig.json +2 -2
- package/dist/python-utils.d.ts +0 -29
- package/dist/python-utils.js +0 -54
- package/dist/types/index.d.ts +0 -323
- package/dist/types/index.js +0 -28
- package/dist/utils/cache-manager.d.ts +0 -64
- package/dist/utils/cache-manager.js +0 -176
- package/dist/utils/errors.d.ts +0 -133
- package/dist/utils/errors.js +0 -256
- package/src/python/editor_compat.py +0 -181
- package/src/python-utils.ts +0 -57
- package/src/types/index.ts +0 -414
- package/src/utils/cache-manager.ts +0 -213
- package/src/utils/errors.ts +0 -312
package/src/tools/performance.ts
CHANGED
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
// Performance tools for Unreal Engine
|
|
2
2
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
3
|
+
import { coerceBoolean, coerceNumber, interpretStandardResult } from '../utils/result-helpers.js';
|
|
3
4
|
|
|
4
5
|
export class PerformanceTools {
|
|
5
6
|
constructor(private bridge: UnrealBridge) {}
|
|
6
7
|
|
|
7
|
-
// Execute console command
|
|
8
|
-
private async _executeCommand(command: string) {
|
|
9
|
-
return this.bridge.httpCall('/remote/object/call', 'PUT', {
|
|
10
|
-
objectPath: '/Script/Engine.Default__KismetSystemLibrary',
|
|
11
|
-
functionName: 'ExecuteConsoleCommand',
|
|
12
|
-
parameters: {
|
|
13
|
-
WorldContextObject: null,
|
|
14
|
-
Command: command,
|
|
15
|
-
SpecificPlayer: null
|
|
16
|
-
},
|
|
17
|
-
generateTransaction: false
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
8
|
// Start profiling
|
|
22
9
|
async startProfiling(params: {
|
|
23
10
|
type: 'CPU' | 'GPU' | 'Memory' | 'RenderThread' | 'GameThread' | 'All';
|
|
24
11
|
duration?: number;
|
|
25
12
|
}) {
|
|
26
|
-
|
|
13
|
+
const commands: string[] = [];
|
|
27
14
|
|
|
28
15
|
switch (params.type) {
|
|
29
16
|
case 'CPU':
|
|
@@ -52,9 +39,7 @@ export class PerformanceTools {
|
|
|
52
39
|
commands.push(`stat stopfile ${params.duration}`);
|
|
53
40
|
}
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
57
|
-
}
|
|
42
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
58
43
|
|
|
59
44
|
return { success: true, message: `${params.type} profiling started` };
|
|
60
45
|
}
|
|
@@ -66,9 +51,7 @@ export class PerformanceTools {
|
|
|
66
51
|
'stat none'
|
|
67
52
|
];
|
|
68
53
|
|
|
69
|
-
|
|
70
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
71
|
-
}
|
|
54
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
72
55
|
|
|
73
56
|
return { success: true, message: 'Profiling stopped' };
|
|
74
57
|
}
|
|
@@ -138,12 +121,19 @@ export class PerformanceTools {
|
|
|
138
121
|
Foliage: 'Foliage',
|
|
139
122
|
Shading: 'Shading',
|
|
140
123
|
};
|
|
124
|
+
const requestedLevel = Number(params.level);
|
|
125
|
+
if (!Number.isInteger(requestedLevel) || requestedLevel < 0 || requestedLevel > 4) {
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
error: 'Invalid scalability level. Expected integer between 0 and 4.'
|
|
129
|
+
};
|
|
130
|
+
}
|
|
141
131
|
|
|
142
132
|
const base = categoryBaseMap[params.category] || params.category;
|
|
143
133
|
|
|
144
134
|
// Use direct console command to set with highest priority (SetByConsole)
|
|
145
135
|
// This avoids conflicts with the scalability system
|
|
146
|
-
const setCommand = `sg.${base}Quality ${
|
|
136
|
+
const setCommand = `sg.${base}Quality ${requestedLevel}`;
|
|
147
137
|
|
|
148
138
|
// Apply the console command directly
|
|
149
139
|
await this.bridge.executeConsoleCommand(setCommand);
|
|
@@ -153,7 +143,7 @@ export class PerformanceTools {
|
|
|
153
143
|
/* eslint-disable no-useless-escape */
|
|
154
144
|
const py = `
|
|
155
145
|
import unreal, json
|
|
156
|
-
result = {'success': True, 'category': '${base}', 'requested': ${
|
|
146
|
+
result = {'success': True, 'category': '${base}', 'requested': ${requestedLevel}, 'actual': ${requestedLevel}, 'method': 'ConsoleOnly'}
|
|
157
147
|
|
|
158
148
|
# Simply verify the console variable was set correctly
|
|
159
149
|
try:
|
|
@@ -198,30 +188,21 @@ print('RESULT:' + json.dumps(result))
|
|
|
198
188
|
// Always try to apply through Python for consistency
|
|
199
189
|
try {
|
|
200
190
|
const pyResp = await this.bridge.executePython(py);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
success: true,
|
|
217
|
-
message: `${params.category} quality set to level ${params.level}`,
|
|
218
|
-
verified,
|
|
219
|
-
readback: parsed.actual,
|
|
220
|
-
method: parsed.method || 'Unknown'
|
|
221
|
-
};
|
|
222
|
-
} catch {
|
|
223
|
-
// Fall through to simple success
|
|
224
|
-
}
|
|
191
|
+
const interpreted = interpretStandardResult(pyResp, {
|
|
192
|
+
successMessage: `${params.category} quality set to level ${requestedLevel}`,
|
|
193
|
+
failureMessage: `Failed to set ${params.category} quality`
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (interpreted.success) {
|
|
197
|
+
const actual = coerceNumber(interpreted.payload.actual) ?? requestedLevel;
|
|
198
|
+
const verified = coerceBoolean(interpreted.payload.success, true) === true && actual === requestedLevel;
|
|
199
|
+
return {
|
|
200
|
+
success: true,
|
|
201
|
+
message: interpreted.message,
|
|
202
|
+
verified,
|
|
203
|
+
readback: actual,
|
|
204
|
+
method: (interpreted.payload.method as string) || 'ConsoleOnly'
|
|
205
|
+
};
|
|
225
206
|
}
|
|
226
207
|
} catch {
|
|
227
208
|
// Ignore Python errors and fall through
|
|
@@ -230,7 +211,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
230
211
|
// If Python fails, the console command was still applied
|
|
231
212
|
return {
|
|
232
213
|
success: true,
|
|
233
|
-
message: `${params.category} quality set to level ${
|
|
214
|
+
message: `${params.category} quality set to level ${requestedLevel}`,
|
|
234
215
|
method: 'CVarOnly'
|
|
235
216
|
};
|
|
236
217
|
}
|
|
@@ -295,7 +276,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
295
276
|
detailed?: boolean;
|
|
296
277
|
outputPath?: string;
|
|
297
278
|
}) {
|
|
298
|
-
|
|
279
|
+
const commands: string[] = [];
|
|
299
280
|
|
|
300
281
|
if (params.detailed) {
|
|
301
282
|
commands.push('memreport -full');
|
|
@@ -307,9 +288,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
307
288
|
commands.push(`obj savepackage ${params.outputPath}`);
|
|
308
289
|
}
|
|
309
290
|
|
|
310
|
-
|
|
311
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
312
|
-
}
|
|
291
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
313
292
|
|
|
314
293
|
return { success: true, message: 'Memory report generated' };
|
|
315
294
|
}
|
|
@@ -320,7 +299,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
320
299
|
poolSize?: number; // MB
|
|
321
300
|
boostPlayerLocation?: boolean;
|
|
322
301
|
}) {
|
|
323
|
-
|
|
302
|
+
const commands: string[] = [];
|
|
324
303
|
|
|
325
304
|
commands.push(`r.TextureStreaming ${params.enabled ? 1 : 0}`);
|
|
326
305
|
|
|
@@ -332,9 +311,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
332
311
|
commands.push(`r.Streaming.UseFixedPoolSize ${params.boostPlayerLocation ? 1 : 0}`);
|
|
333
312
|
}
|
|
334
313
|
|
|
335
|
-
|
|
336
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
337
|
-
}
|
|
314
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
338
315
|
|
|
339
316
|
return { success: true, message: 'Texture streaming configured' };
|
|
340
317
|
}
|
|
@@ -345,7 +322,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
345
322
|
lodBias?: number; // skeletal LOD bias (int)
|
|
346
323
|
distanceScale?: number; // distance scale (float) applied to both static and skeletal
|
|
347
324
|
}) {
|
|
348
|
-
|
|
325
|
+
const commands: string[] = [];
|
|
349
326
|
|
|
350
327
|
if (params.forceLOD !== undefined) {
|
|
351
328
|
commands.push(`r.ForceLOD ${params.forceLOD}`);
|
|
@@ -362,9 +339,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
362
339
|
commands.push(`r.SkeletalMeshLODDistanceScale ${params.distanceScale}`);
|
|
363
340
|
}
|
|
364
341
|
|
|
365
|
-
|
|
366
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
367
|
-
}
|
|
342
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
368
343
|
|
|
369
344
|
return { success: true, message: 'LOD settings configured' };
|
|
370
345
|
}
|
|
@@ -394,9 +369,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
394
369
|
`t.MaxFPS ${p.maxFPS}`,
|
|
395
370
|
];
|
|
396
371
|
|
|
397
|
-
|
|
398
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
399
|
-
}
|
|
372
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
400
373
|
|
|
401
374
|
return { success: true, message: 'Baseline performance settings applied', params: p };
|
|
402
375
|
}
|
|
@@ -407,7 +380,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
407
380
|
enableBatching?: boolean; // no-op (deprecated internal toggle)
|
|
408
381
|
mergeActors?: boolean;
|
|
409
382
|
}) {
|
|
410
|
-
|
|
383
|
+
const commands: string[] = [];
|
|
411
384
|
|
|
412
385
|
if (params.enableInstancing !== undefined) {
|
|
413
386
|
commands.push(`r.MeshDrawCommands.DynamicInstancing ${params.enableInstancing ? 1 : 0}`);
|
|
@@ -419,9 +392,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
419
392
|
commands.push('MergeActors');
|
|
420
393
|
}
|
|
421
394
|
|
|
422
|
-
|
|
423
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
424
|
-
}
|
|
395
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
425
396
|
|
|
426
397
|
return { success: true, message: 'Draw call optimization configured' };
|
|
427
398
|
}
|
|
@@ -432,7 +403,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
432
403
|
method?: 'Hardware' | 'Software' | 'Hierarchical';
|
|
433
404
|
freezeRendering?: boolean;
|
|
434
405
|
}) {
|
|
435
|
-
|
|
406
|
+
const commands: string[] = [];
|
|
436
407
|
|
|
437
408
|
// Enable/disable HZB occlusion (boolean)
|
|
438
409
|
commands.push(`r.HZBOcclusion ${params.enabled ? 1 : 0}`);
|
|
@@ -442,9 +413,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
442
413
|
commands.push(`FreezeRendering ${params.freezeRendering ? 1 : 0}`);
|
|
443
414
|
}
|
|
444
415
|
|
|
445
|
-
|
|
446
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
447
|
-
}
|
|
416
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
448
417
|
|
|
449
418
|
return { success: true, message: 'Occlusion culling configured' };
|
|
450
419
|
}
|
|
@@ -455,7 +424,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
455
424
|
cacheShaders?: boolean;
|
|
456
425
|
reducePermutations?: boolean;
|
|
457
426
|
}) {
|
|
458
|
-
|
|
427
|
+
const commands: string[] = [];
|
|
459
428
|
|
|
460
429
|
if (params.compileOnDemand !== undefined) {
|
|
461
430
|
commands.push(`r.ShaderDevelopmentMode ${params.compileOnDemand ? 1 : 0}`);
|
|
@@ -469,9 +438,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
469
438
|
commands.push('RecompileShaders changed');
|
|
470
439
|
}
|
|
471
440
|
|
|
472
|
-
|
|
473
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
474
|
-
}
|
|
441
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
475
442
|
|
|
476
443
|
return { success: true, message: 'Shader optimization configured' };
|
|
477
444
|
}
|
|
@@ -482,7 +449,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
482
449
|
maxPixelsPerEdge?: number;
|
|
483
450
|
streamingPoolSize?: number;
|
|
484
451
|
}) {
|
|
485
|
-
|
|
452
|
+
const commands: string[] = [];
|
|
486
453
|
|
|
487
454
|
commands.push(`r.Nanite ${params.enabled ? 1 : 0}`);
|
|
488
455
|
|
|
@@ -494,9 +461,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
494
461
|
commands.push(`r.Nanite.StreamingPoolSize ${params.streamingPoolSize}`);
|
|
495
462
|
}
|
|
496
463
|
|
|
497
|
-
|
|
498
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
499
|
-
}
|
|
464
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
500
465
|
|
|
501
466
|
return { success: true, message: 'Nanite configured' };
|
|
502
467
|
}
|
|
@@ -507,7 +472,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
507
472
|
streamingDistance?: number;
|
|
508
473
|
cellSize?: number;
|
|
509
474
|
}) {
|
|
510
|
-
|
|
475
|
+
const commands: string[] = [];
|
|
511
476
|
|
|
512
477
|
commands.push(`wp.Runtime.EnableStreaming ${params.enabled ? 1 : 0}`);
|
|
513
478
|
|
|
@@ -519,9 +484,7 @@ print('RESULT:' + json.dumps(result))
|
|
|
519
484
|
commands.push(`wp.Runtime.CellSize ${params.cellSize}`);
|
|
520
485
|
}
|
|
521
486
|
|
|
522
|
-
|
|
523
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
524
|
-
}
|
|
487
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
525
488
|
|
|
526
489
|
return { success: true, message: 'World Partition configured' };
|
|
527
490
|
}
|
|
@@ -533,16 +496,14 @@ print('RESULT:' + json.dumps(result))
|
|
|
533
496
|
}) {
|
|
534
497
|
const duration = params.duration || 60;
|
|
535
498
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
await this.bridge.executeConsoleCommand('profilegpu');
|
|
499
|
+
// Start recording and GPU profiling
|
|
500
|
+
await this.bridge.executeConsoleCommands(['stat startfile', 'profilegpu']);
|
|
539
501
|
|
|
540
502
|
// Wait for the requested duration
|
|
541
503
|
await new Promise(resolve => setTimeout(resolve, duration * 1000));
|
|
542
504
|
|
|
543
505
|
// Stop recording and clear stats
|
|
544
|
-
await this.bridge.
|
|
545
|
-
await this.bridge.executeConsoleCommand('stat none');
|
|
506
|
+
await this.bridge.executeConsoleCommands(['stat stopfile', 'stat none']);
|
|
546
507
|
|
|
547
508
|
return { success: true, message: `Benchmark completed for ${duration} seconds` };
|
|
548
509
|
}
|