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.
Files changed (135) hide show
  1. package/.env.production +1 -1
  2. package/.github/copilot-instructions.md +45 -0
  3. package/.github/workflows/publish-mcp.yml +1 -1
  4. package/README.md +21 -5
  5. package/dist/index.js +124 -31
  6. package/dist/prompts/index.d.ts +10 -3
  7. package/dist/prompts/index.js +186 -7
  8. package/dist/resources/actors.d.ts +19 -1
  9. package/dist/resources/actors.js +55 -64
  10. package/dist/resources/assets.js +46 -62
  11. package/dist/resources/levels.d.ts +21 -3
  12. package/dist/resources/levels.js +29 -54
  13. package/dist/tools/actors.d.ts +3 -14
  14. package/dist/tools/actors.js +246 -302
  15. package/dist/tools/animation.d.ts +57 -102
  16. package/dist/tools/animation.js +429 -450
  17. package/dist/tools/assets.d.ts +13 -2
  18. package/dist/tools/assets.js +52 -44
  19. package/dist/tools/audio.d.ts +22 -13
  20. package/dist/tools/audio.js +467 -121
  21. package/dist/tools/blueprint.d.ts +32 -13
  22. package/dist/tools/blueprint.js +699 -448
  23. package/dist/tools/build_environment_advanced.d.ts +0 -1
  24. package/dist/tools/build_environment_advanced.js +190 -45
  25. package/dist/tools/consolidated-tool-definitions.js +78 -252
  26. package/dist/tools/consolidated-tool-handlers.js +506 -133
  27. package/dist/tools/debug.d.ts +72 -10
  28. package/dist/tools/debug.js +167 -31
  29. package/dist/tools/editor.d.ts +9 -2
  30. package/dist/tools/editor.js +30 -44
  31. package/dist/tools/foliage.d.ts +34 -15
  32. package/dist/tools/foliage.js +97 -107
  33. package/dist/tools/introspection.js +19 -21
  34. package/dist/tools/landscape.d.ts +1 -2
  35. package/dist/tools/landscape.js +311 -168
  36. package/dist/tools/level.d.ts +3 -28
  37. package/dist/tools/level.js +642 -192
  38. package/dist/tools/lighting.d.ts +14 -3
  39. package/dist/tools/lighting.js +236 -123
  40. package/dist/tools/materials.d.ts +25 -7
  41. package/dist/tools/materials.js +102 -79
  42. package/dist/tools/niagara.d.ts +10 -12
  43. package/dist/tools/niagara.js +74 -94
  44. package/dist/tools/performance.d.ts +12 -4
  45. package/dist/tools/performance.js +38 -79
  46. package/dist/tools/physics.d.ts +34 -10
  47. package/dist/tools/physics.js +364 -292
  48. package/dist/tools/rc.js +97 -23
  49. package/dist/tools/sequence.d.ts +1 -0
  50. package/dist/tools/sequence.js +125 -22
  51. package/dist/tools/ui.d.ts +31 -4
  52. package/dist/tools/ui.js +83 -66
  53. package/dist/tools/visual.d.ts +11 -0
  54. package/dist/tools/visual.js +245 -30
  55. package/dist/types/tool-types.d.ts +0 -6
  56. package/dist/types/tool-types.js +1 -8
  57. package/dist/unreal-bridge.d.ts +32 -2
  58. package/dist/unreal-bridge.js +621 -127
  59. package/dist/utils/elicitation.d.ts +57 -0
  60. package/dist/utils/elicitation.js +104 -0
  61. package/dist/utils/error-handler.d.ts +0 -33
  62. package/dist/utils/error-handler.js +4 -111
  63. package/dist/utils/http.d.ts +2 -22
  64. package/dist/utils/http.js +12 -75
  65. package/dist/utils/normalize.d.ts +4 -4
  66. package/dist/utils/normalize.js +15 -7
  67. package/dist/utils/python-output.d.ts +18 -0
  68. package/dist/utils/python-output.js +290 -0
  69. package/dist/utils/python.d.ts +2 -0
  70. package/dist/utils/python.js +4 -0
  71. package/dist/utils/response-validator.js +28 -2
  72. package/dist/utils/result-helpers.d.ts +27 -0
  73. package/dist/utils/result-helpers.js +147 -0
  74. package/dist/utils/safe-json.d.ts +0 -2
  75. package/dist/utils/safe-json.js +0 -43
  76. package/dist/utils/validation.d.ts +16 -0
  77. package/dist/utils/validation.js +70 -7
  78. package/mcp-config-example.json +2 -2
  79. package/package.json +10 -9
  80. package/server.json +37 -14
  81. package/src/index.ts +130 -33
  82. package/src/prompts/index.ts +211 -13
  83. package/src/resources/actors.ts +59 -44
  84. package/src/resources/assets.ts +48 -51
  85. package/src/resources/levels.ts +35 -45
  86. package/src/tools/actors.ts +269 -313
  87. package/src/tools/animation.ts +556 -539
  88. package/src/tools/assets.ts +53 -43
  89. package/src/tools/audio.ts +507 -113
  90. package/src/tools/blueprint.ts +778 -462
  91. package/src/tools/build_environment_advanced.ts +266 -64
  92. package/src/tools/consolidated-tool-definitions.ts +90 -264
  93. package/src/tools/consolidated-tool-handlers.ts +630 -121
  94. package/src/tools/debug.ts +176 -33
  95. package/src/tools/editor.ts +35 -37
  96. package/src/tools/foliage.ts +110 -104
  97. package/src/tools/introspection.ts +24 -22
  98. package/src/tools/landscape.ts +334 -181
  99. package/src/tools/level.ts +683 -182
  100. package/src/tools/lighting.ts +244 -123
  101. package/src/tools/materials.ts +114 -83
  102. package/src/tools/niagara.ts +87 -81
  103. package/src/tools/performance.ts +49 -88
  104. package/src/tools/physics.ts +393 -299
  105. package/src/tools/rc.ts +102 -24
  106. package/src/tools/sequence.ts +136 -28
  107. package/src/tools/ui.ts +101 -70
  108. package/src/tools/visual.ts +250 -29
  109. package/src/types/tool-types.ts +0 -9
  110. package/src/unreal-bridge.ts +658 -140
  111. package/src/utils/elicitation.ts +129 -0
  112. package/src/utils/error-handler.ts +4 -159
  113. package/src/utils/http.ts +16 -115
  114. package/src/utils/normalize.ts +20 -10
  115. package/src/utils/python-output.ts +351 -0
  116. package/src/utils/python.ts +3 -0
  117. package/src/utils/response-validator.ts +25 -2
  118. package/src/utils/result-helpers.ts +193 -0
  119. package/src/utils/safe-json.ts +0 -50
  120. package/src/utils/validation.ts +94 -7
  121. package/tests/run-unreal-tool-tests.mjs +720 -0
  122. package/tsconfig.json +2 -2
  123. package/dist/python-utils.d.ts +0 -29
  124. package/dist/python-utils.js +0 -54
  125. package/dist/types/index.d.ts +0 -323
  126. package/dist/types/index.js +0 -28
  127. package/dist/utils/cache-manager.d.ts +0 -64
  128. package/dist/utils/cache-manager.js +0 -176
  129. package/dist/utils/errors.d.ts +0 -133
  130. package/dist/utils/errors.js +0 -256
  131. package/src/python/editor_compat.py +0 -181
  132. package/src/python-utils.ts +0 -57
  133. package/src/types/index.ts +0 -414
  134. package/src/utils/cache-manager.ts +0 -213
  135. package/src/utils/errors.ts +0 -312
@@ -5,13 +5,24 @@ export declare class AssetTools {
5
5
  importAsset(sourcePath: string, destinationPath: string): Promise<{
6
6
  success: boolean;
7
7
  message: string;
8
- paths: any;
8
+ imported: number;
9
+ paths: string[];
9
10
  error?: undefined;
11
+ details?: undefined;
10
12
  } | {
13
+ success: boolean;
14
+ message: string;
15
+ error: string;
16
+ details: string | undefined;
17
+ imported?: undefined;
18
+ paths?: undefined;
19
+ } | {
20
+ success: boolean;
11
21
  error: string;
12
- success?: undefined;
13
22
  message?: undefined;
23
+ imported?: undefined;
14
24
  paths?: undefined;
25
+ details?: undefined;
15
26
  }>;
16
27
  duplicateAsset(sourcePath: string, destinationPath: string): Promise<any>;
17
28
  deleteAsset(assetPath: string): Promise<any>;
@@ -1,11 +1,13 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { bestEffortInterpretedText, coerceNumber, coerceStringArray, interpretStandardResult } from '../utils/result-helpers.js';
3
4
  export class AssetTools {
4
5
  bridge;
5
6
  constructor(bridge) {
6
7
  this.bridge = bridge;
7
8
  }
8
9
  async importAsset(sourcePath, destinationPath) {
10
+ let createdTestFile = false;
9
11
  try {
10
12
  // Sanitize destination path (remove trailing slash) and normalize UE path
11
13
  let cleanDest = destinationPath.replace(/\/$/, '');
@@ -17,7 +19,8 @@ export class AssetTools {
17
19
  if (sourcePath.includes('test_model.fbx')) {
18
20
  // Create the file outside of Python, before import
19
21
  try {
20
- this.createTestFBX(sourcePath);
22
+ await this.createTestFBX(sourcePath);
23
+ createdTestFile = true;
21
24
  }
22
25
  catch (_err) {
23
26
  // If we can't create the file, we'll handle it in Python
@@ -27,6 +30,7 @@ export class AssetTools {
27
30
  const pythonCode = `
28
31
  import unreal
29
32
  import os
33
+ import json
30
34
 
31
35
  # Create test FBX if needed
32
36
  source_path = r'${sourcePath}'
@@ -147,54 +151,57 @@ task.options = options
147
151
 
148
152
  # Use AssetTools to import
149
153
  asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
154
+ result = {'success': False, 'error': 'No assets imported', 'source': task.filename}
155
+
150
156
  try:
151
157
  asset_tools.import_asset_tasks([task])
152
158
  if task.imported_object_paths:
153
- print(f"RESULT:{'{'}'success': True, 'imported': {len(task.imported_object_paths)}, 'paths': {task.imported_object_paths}{'}'}")
154
- else:
155
- print(f"RESULT:{'{'}'success': False, 'error': 'No assets imported', 'source': task.filename{'}'}")
159
+ result = {
160
+ 'success': True,
161
+ 'imported': len(task.imported_object_paths),
162
+ 'paths': list(task.imported_object_paths)
163
+ }
156
164
  except Exception as e:
157
- print(f"RESULT:{'{'}'success': False, 'error': str(e), 'source': task.filename{'}'}")
165
+ result = {'success': False, 'error': str(e), 'source': task.filename}
166
+
167
+ print('RESULT:' + json.dumps(result))
158
168
  `.trim();
159
169
  const pyResp = await this.bridge.executePython(pythonCode);
160
- // Parse Python output
161
- let outputStr = '';
162
- if (typeof pyResp === 'object' && pyResp !== null) {
163
- if (pyResp.LogOutput && Array.isArray(pyResp.LogOutput)) {
164
- outputStr = pyResp.LogOutput.map((l) => l.Output || '').join('');
165
- }
166
- else if ('ReturnValue' in pyResp) {
167
- outputStr = String(pyResp.ReturnValue);
168
- }
169
- else {
170
- outputStr = JSON.stringify(pyResp);
171
- }
172
- }
173
- else {
174
- outputStr = String(pyResp || '');
170
+ const interpreted = interpretStandardResult(pyResp, {
171
+ successMessage: `Imported assets to ${cleanDest}`,
172
+ failureMessage: 'Import failed'
173
+ });
174
+ if (interpreted.success) {
175
+ const count = coerceNumber(interpreted.payload.imported) ?? 0;
176
+ const paths = coerceStringArray(interpreted.payload.paths) ?? [];
177
+ return {
178
+ success: true,
179
+ message: `Imported ${count} assets to ${cleanDest}`,
180
+ imported: count,
181
+ paths
182
+ };
175
183
  }
176
- const match = outputStr.match(/RESULT:({.*})/);
177
- if (match) {
184
+ const errorMessage = `Import failed: ${interpreted.error ?? 'Unknown error'} (source: ${interpreted.payload.source ?? sourcePath})`;
185
+ return {
186
+ success: false,
187
+ message: errorMessage,
188
+ error: errorMessage,
189
+ details: bestEffortInterpretedText(interpreted)
190
+ };
191
+ }
192
+ catch (err) {
193
+ return { success: false, error: `Failed to import asset: ${err}` };
194
+ }
195
+ finally {
196
+ if (createdTestFile) {
178
197
  try {
179
- const parsed = JSON.parse(match[1].replace(/'/g, '"'));
180
- if (parsed.success) {
181
- const count = parsed.imported?.count ?? 0;
182
- const paths = parsed.imported?.paths ?? [];
183
- return { success: true, message: `Imported ${count} assets to ${cleanDest}`, paths };
184
- }
185
- else {
186
- return { error: `Import failed: ${parsed.error || 'Unknown error'} (source: ${parsed.source || sourcePath})` };
187
- }
198
+ await fs.rm(sourcePath, { force: true });
188
199
  }
189
- catch {
190
- // Fall through
200
+ catch (cleanupError) {
201
+ // Swallow cleanup error but log for debug visibility
202
+ console.warn(`Failed to clean up temporary FBX ${sourcePath}:`, cleanupError);
191
203
  }
192
204
  }
193
- // If unable to parse, return generic attempt result
194
- return { error: `Import did not report success for source ${sourcePath}` };
195
- }
196
- catch (err) {
197
- return { error: `Failed to import asset: ${err}` };
198
205
  }
199
206
  }
200
207
  async duplicateAsset(sourcePath, destinationPath) {
@@ -243,7 +250,7 @@ except Exception as e:
243
250
  return { error: `Failed to save asset: ${err}` };
244
251
  }
245
252
  }
246
- createTestFBX(filePath) {
253
+ async createTestFBX(filePath) {
247
254
  // Create a minimal valid FBX ASCII file for testing
248
255
  const fbxContent = `; FBX 7.5.0 project file
249
256
  FBXHeaderExtension: {
@@ -298,11 +305,12 @@ Connections: {
298
305
  `;
299
306
  // Ensure directory exists
300
307
  const dir = path.dirname(filePath);
301
- if (!fs.existsSync(dir)) {
302
- fs.mkdirSync(dir, { recursive: true });
308
+ try {
309
+ await fs.mkdir(dir, { recursive: true });
303
310
  }
311
+ catch { }
304
312
  // Write the FBX file
305
- fs.writeFileSync(filePath, fbxContent, 'utf8');
313
+ await fs.writeFile(filePath, fbxContent, 'utf8');
306
314
  }
307
315
  }
308
316
  //# sourceMappingURL=assets.js.map
@@ -2,7 +2,7 @@ import { UnrealBridge } from '../unreal-bridge.js';
2
2
  export declare class AudioTools {
3
3
  private bridge;
4
4
  constructor(bridge: UnrealBridge);
5
- private _executeCommand;
5
+ private interpretResult;
6
6
  createSoundCue(params: {
7
7
  name: string;
8
8
  wavePath?: string;
@@ -14,13 +14,16 @@ export declare class AudioTools {
14
14
  attenuationSettings?: string;
15
15
  };
16
16
  }): Promise<{
17
- success: boolean;
17
+ success: true;
18
18
  message: string;
19
- error?: undefined;
19
+ details?: string;
20
+ } | {
21
+ success: false;
22
+ error: string;
23
+ details?: string;
20
24
  } | {
21
25
  success: boolean;
22
- error: any;
23
- message?: undefined;
26
+ error: string;
24
27
  }>;
25
28
  playSoundAtLocation(params: {
26
29
  soundPath: string;
@@ -29,13 +32,16 @@ export declare class AudioTools {
29
32
  pitch?: number;
30
33
  startTime?: number;
31
34
  }): Promise<{
32
- success: boolean;
35
+ success: true;
33
36
  message: string;
34
- error?: undefined;
37
+ details?: string;
38
+ } | {
39
+ success: false;
40
+ error: string;
41
+ details?: string;
35
42
  } | {
36
43
  success: boolean;
37
- error: any;
38
- message?: undefined;
44
+ error: string;
39
45
  }>;
40
46
  playSound2D(params: {
41
47
  soundPath: string;
@@ -43,13 +49,16 @@ export declare class AudioTools {
43
49
  pitch?: number;
44
50
  startTime?: number;
45
51
  }): Promise<{
46
- success: boolean;
52
+ success: true;
47
53
  message: string;
48
- error?: undefined;
54
+ details?: string;
55
+ } | {
56
+ success: false;
57
+ error: string;
58
+ details?: string;
49
59
  } | {
50
60
  success: boolean;
51
- error: any;
52
- message?: undefined;
61
+ error: string;
53
62
  }>;
54
63
  createAudioComponent(params: {
55
64
  actorName: string;