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