unreal-engine-mcp-server 0.4.0 → 0.4.3
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 +1 -1
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
2
|
+
import { interpretStandardResult, coerceBoolean, coerceNumber, coerceString, coerceStringArray } from '../utils/result-helpers.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Advanced Build Environment Tools
|
|
@@ -137,19 +138,85 @@ except Exception as e:
|
|
|
137
138
|
print(f"RESULT:{json.dumps(result)}")
|
|
138
139
|
`.trim();
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
141
|
+
const response = await this.bridge.executePython(pythonScript);
|
|
142
|
+
const interpreted = interpretStandardResult(response, {
|
|
143
|
+
successMessage: `Created procedural terrain '${params.name}'`,
|
|
144
|
+
failureMessage: `Failed to create procedural terrain '${params.name}'`
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!interpreted.success) {
|
|
148
|
+
const failure: {
|
|
149
|
+
success: false;
|
|
150
|
+
error: string;
|
|
151
|
+
message: string;
|
|
152
|
+
warnings?: string[];
|
|
153
|
+
details?: string[];
|
|
154
|
+
payload?: Record<string, unknown>;
|
|
155
|
+
} = {
|
|
156
|
+
success: false,
|
|
157
|
+
error: interpreted.error ?? interpreted.message,
|
|
158
|
+
message: interpreted.message
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if (interpreted.warnings) {
|
|
162
|
+
failure.warnings = interpreted.warnings;
|
|
163
|
+
}
|
|
164
|
+
if (interpreted.details) {
|
|
165
|
+
failure.details = interpreted.details;
|
|
166
|
+
}
|
|
167
|
+
if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
|
|
168
|
+
failure.payload = interpreted.payload;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return failure;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const payload = { ...interpreted.payload } as Record<string, unknown>;
|
|
175
|
+
const actorName = coerceString(payload.actor_name) ?? coerceString(payload.actorName);
|
|
176
|
+
const vertices = coerceNumber(payload.vertices);
|
|
177
|
+
const triangles = coerceNumber(payload.triangles);
|
|
178
|
+
const subdivisions = coerceNumber(payload.subdivisions);
|
|
179
|
+
const sizeArray = Array.isArray(payload.size)
|
|
180
|
+
? (payload.size as unknown[]).map(entry => {
|
|
181
|
+
if (typeof entry === 'number' && Number.isFinite(entry)) {
|
|
182
|
+
return entry;
|
|
183
|
+
}
|
|
184
|
+
if (typeof entry === 'string') {
|
|
185
|
+
const parsed = Number(entry);
|
|
186
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
}).filter((entry): entry is number => typeof entry === 'number')
|
|
190
|
+
: undefined;
|
|
191
|
+
|
|
192
|
+
payload.success = true;
|
|
193
|
+
payload.message = interpreted.message;
|
|
194
|
+
|
|
195
|
+
if (actorName) {
|
|
196
|
+
payload.actor_name = actorName;
|
|
197
|
+
payload.actorName = actorName;
|
|
198
|
+
}
|
|
199
|
+
if (typeof vertices === 'number') {
|
|
200
|
+
payload.vertices = vertices;
|
|
201
|
+
}
|
|
202
|
+
if (typeof triangles === 'number') {
|
|
203
|
+
payload.triangles = triangles;
|
|
204
|
+
}
|
|
205
|
+
if (typeof subdivisions === 'number') {
|
|
206
|
+
payload.subdivisions = subdivisions;
|
|
207
|
+
}
|
|
208
|
+
if (sizeArray && sizeArray.length === 2) {
|
|
209
|
+
payload.size = sizeArray;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (interpreted.warnings) {
|
|
213
|
+
payload.warnings = interpreted.warnings;
|
|
214
|
+
}
|
|
215
|
+
if (interpreted.details) {
|
|
216
|
+
payload.details = interpreted.details;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return payload as any;
|
|
153
220
|
}
|
|
154
221
|
|
|
155
222
|
/**
|
|
@@ -305,19 +372,80 @@ except Exception as e:
|
|
|
305
372
|
print(f"RESULT:{json.dumps(result)}")
|
|
306
373
|
`.trim();
|
|
307
374
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
375
|
+
const response = await this.bridge.executePython(pythonScript);
|
|
376
|
+
const interpreted = interpretStandardResult(response, {
|
|
377
|
+
successMessage: `Created procedural foliage volume '${params.name}'`,
|
|
378
|
+
failureMessage: `Failed to create procedural foliage volume '${params.name}'`
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
if (!interpreted.success) {
|
|
382
|
+
const failure: {
|
|
383
|
+
success: false;
|
|
384
|
+
error: string;
|
|
385
|
+
message: string;
|
|
386
|
+
warnings?: string[];
|
|
387
|
+
details?: string[];
|
|
388
|
+
payload?: Record<string, unknown>;
|
|
389
|
+
} = {
|
|
390
|
+
success: false,
|
|
391
|
+
error: interpreted.error ?? interpreted.message,
|
|
392
|
+
message: interpreted.message
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
if (interpreted.warnings) {
|
|
396
|
+
failure.warnings = interpreted.warnings;
|
|
397
|
+
}
|
|
398
|
+
if (interpreted.details) {
|
|
399
|
+
failure.details = interpreted.details;
|
|
400
|
+
}
|
|
401
|
+
if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
|
|
402
|
+
failure.payload = interpreted.payload;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return failure;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const payload = { ...interpreted.payload } as Record<string, unknown>;
|
|
409
|
+
const volumeActor = coerceString(payload.volume_actor) ?? coerceString(payload.volumeActor);
|
|
410
|
+
const spawnerPath = coerceString(payload.spawner_path) ?? coerceString(payload.spawnerPath);
|
|
411
|
+
const foliageCount = coerceNumber(payload.foliage_types_count) ?? coerceNumber(payload.foliageTypesCount);
|
|
412
|
+
const resimulated = coerceBoolean(payload.resimulated);
|
|
413
|
+
const note = coerceString(payload.note);
|
|
414
|
+
const messages = coerceStringArray(payload.messages);
|
|
415
|
+
|
|
416
|
+
payload.success = true;
|
|
417
|
+
payload.message = interpreted.message;
|
|
418
|
+
|
|
419
|
+
if (volumeActor) {
|
|
420
|
+
payload.volume_actor = volumeActor;
|
|
421
|
+
payload.volumeActor = volumeActor;
|
|
422
|
+
}
|
|
423
|
+
if (spawnerPath) {
|
|
424
|
+
payload.spawner_path = spawnerPath;
|
|
425
|
+
payload.spawnerPath = spawnerPath;
|
|
426
|
+
}
|
|
427
|
+
if (typeof foliageCount === 'number') {
|
|
428
|
+
payload.foliage_types_count = foliageCount;
|
|
429
|
+
payload.foliageTypesCount = foliageCount;
|
|
430
|
+
}
|
|
431
|
+
if (typeof resimulated === 'boolean') {
|
|
432
|
+
payload.resimulated = resimulated;
|
|
433
|
+
}
|
|
434
|
+
if (note) {
|
|
435
|
+
payload.note = note;
|
|
436
|
+
}
|
|
437
|
+
if (messages && messages.length > 0) {
|
|
438
|
+
payload.messages = messages;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (interpreted.warnings) {
|
|
442
|
+
payload.warnings = interpreted.warnings;
|
|
443
|
+
}
|
|
444
|
+
if (interpreted.details) {
|
|
445
|
+
payload.details = interpreted.details;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return payload as any;
|
|
321
449
|
}
|
|
322
450
|
|
|
323
451
|
/**
|
|
@@ -389,19 +517,59 @@ except Exception as e:
|
|
|
389
517
|
print(f"RESULT:{json.dumps(result)}")
|
|
390
518
|
`.trim();
|
|
391
519
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
520
|
+
const response = await this.bridge.executePython(pythonScript);
|
|
521
|
+
const interpreted = interpretStandardResult(response, {
|
|
522
|
+
successMessage: 'Foliage instances added',
|
|
523
|
+
failureMessage: 'Failed to add foliage instances'
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
if (!interpreted.success) {
|
|
527
|
+
const failure: {
|
|
528
|
+
success: false;
|
|
529
|
+
error: string;
|
|
530
|
+
message: string;
|
|
531
|
+
warnings?: string[];
|
|
532
|
+
details?: string[];
|
|
533
|
+
payload?: Record<string, unknown>;
|
|
534
|
+
} = {
|
|
535
|
+
success: false,
|
|
536
|
+
error: interpreted.error ?? interpreted.message,
|
|
537
|
+
message: interpreted.message
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
if (interpreted.warnings) {
|
|
541
|
+
failure.warnings = interpreted.warnings;
|
|
542
|
+
}
|
|
543
|
+
if (interpreted.details) {
|
|
544
|
+
failure.details = interpreted.details;
|
|
545
|
+
}
|
|
546
|
+
if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
|
|
547
|
+
failure.payload = interpreted.payload;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return failure;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const payload = { ...interpreted.payload } as Record<string, unknown>;
|
|
554
|
+
const count = coerceNumber(payload.instances_count) ?? coerceNumber(payload.instancesCount);
|
|
555
|
+
const message = coerceString(payload.message) ?? interpreted.message;
|
|
556
|
+
|
|
557
|
+
payload.success = true;
|
|
558
|
+
payload.message = message;
|
|
559
|
+
|
|
560
|
+
if (typeof count === 'number') {
|
|
561
|
+
payload.instances_count = count;
|
|
562
|
+
payload.instancesCount = count;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (interpreted.warnings) {
|
|
566
|
+
payload.warnings = interpreted.warnings;
|
|
567
|
+
}
|
|
568
|
+
if (interpreted.details) {
|
|
569
|
+
payload.details = interpreted.details;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return payload as any;
|
|
405
573
|
}
|
|
406
574
|
|
|
407
575
|
/**
|
|
@@ -500,31 +668,65 @@ except Exception as e:
|
|
|
500
668
|
print(f"RESULT:{json.dumps(result)}")
|
|
501
669
|
`.trim();
|
|
502
670
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
671
|
+
const response = await this.bridge.executePython(pythonScript);
|
|
672
|
+
const interpreted = interpretStandardResult(response, {
|
|
673
|
+
successMessage: `Created landscape grass type '${params.name}'`,
|
|
674
|
+
failureMessage: `Failed to create landscape grass type '${params.name}'`
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
if (!interpreted.success) {
|
|
678
|
+
const failure: {
|
|
679
|
+
success: false;
|
|
680
|
+
error: string;
|
|
681
|
+
message: string;
|
|
682
|
+
warnings?: string[];
|
|
683
|
+
details?: string[];
|
|
684
|
+
payload?: Record<string, unknown>;
|
|
685
|
+
} = {
|
|
686
|
+
success: false,
|
|
687
|
+
error: interpreted.error ?? interpreted.message,
|
|
688
|
+
message: interpreted.message
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
if (interpreted.warnings) {
|
|
692
|
+
failure.warnings = interpreted.warnings;
|
|
693
|
+
}
|
|
694
|
+
if (interpreted.details) {
|
|
695
|
+
failure.details = interpreted.details;
|
|
696
|
+
}
|
|
697
|
+
if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
|
|
698
|
+
failure.payload = interpreted.payload;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return failure;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const payload = { ...interpreted.payload } as Record<string, unknown>;
|
|
705
|
+
const assetPath = coerceString(payload.asset_path) ?? coerceString(payload.assetPath);
|
|
706
|
+
const note = coerceString(payload.note);
|
|
707
|
+
const messages = coerceStringArray(payload.messages);
|
|
708
|
+
|
|
709
|
+
payload.success = true;
|
|
710
|
+
payload.message = interpreted.message;
|
|
711
|
+
|
|
712
|
+
if (assetPath) {
|
|
713
|
+
payload.asset_path = assetPath;
|
|
714
|
+
payload.assetPath = assetPath;
|
|
715
|
+
}
|
|
716
|
+
if (note) {
|
|
717
|
+
payload.note = note;
|
|
718
|
+
}
|
|
719
|
+
if (messages && messages.length > 0) {
|
|
720
|
+
payload.messages = messages;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (interpreted.warnings) {
|
|
724
|
+
payload.warnings = interpreted.warnings;
|
|
725
|
+
}
|
|
726
|
+
if (interpreted.details) {
|
|
727
|
+
payload.details = interpreted.details;
|
|
728
|
+
}
|
|
517
729
|
|
|
518
|
-
|
|
519
|
-
if (response && typeof response === 'object') {
|
|
520
|
-
if (response.LogOutput && Array.isArray(response.LogOutput)) {
|
|
521
|
-
return response.LogOutput.map((log: any) => log.Output || '').join('');
|
|
522
|
-
} else if (response.CommandResult) {
|
|
523
|
-
return response.CommandResult;
|
|
524
|
-
} else if (response.ReturnValue) {
|
|
525
|
-
return JSON.stringify(response);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return typeof response === 'string' ? response : JSON.stringify(response);
|
|
730
|
+
return payload as any;
|
|
529
731
|
}
|
|
530
732
|
}
|