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
@@ -1,29 +1,37 @@
1
1
  import { UnrealBridge } from '../unreal-bridge.js';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { bestEffortInterpretedText, coerceNumber, coerceStringArray, interpretStandardResult } from '../utils/result-helpers.js';
4
5
 
5
6
  export class AssetTools {
6
7
  constructor(private bridge: UnrealBridge) {}
7
8
 
8
9
  async importAsset(sourcePath: string, destinationPath: string) {
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
 
13
19
  // Create test FBX file if it's a test file
14
20
  if (sourcePath.includes('test_model.fbx')) {
15
21
  // Create the file outside of Python, before import
16
22
  try {
17
- this.createTestFBX(sourcePath);
18
- } catch (_err) {
23
+ await this.createTestFBX(sourcePath);
24
+ createdTestFile = true;
25
+ } catch (_err) {
19
26
  // If we can't create the file, we'll handle it in Python
20
27
  }
21
28
  }
22
29
 
23
30
  // Use Python API to import asset with file creation fallback
24
- const pythonCode = `
31
+ const pythonCode = `
25
32
  import unreal
26
33
  import os
34
+ import json
27
35
 
28
36
  # Create test FBX if needed
29
37
  source_path = r'${sourcePath}'
@@ -144,52 +152,58 @@ task.options = options
144
152
 
145
153
  # Use AssetTools to import
146
154
  asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
155
+ result = {'success': False, 'error': 'No assets imported', 'source': task.filename}
156
+
147
157
  try:
148
158
  asset_tools.import_asset_tasks([task])
149
159
  if task.imported_object_paths:
150
- print(f"RESULT:{'{'}'success': True, 'imported': {len(task.imported_object_paths)}, 'paths': {task.imported_object_paths}{'}'}")
151
- else:
152
- print(f"RESULT:{'{'}'success': False, 'error': 'No assets imported', 'source': task.filename{'}'}")
160
+ result = {
161
+ 'success': True,
162
+ 'imported': len(task.imported_object_paths),
163
+ 'paths': list(task.imported_object_paths)
164
+ }
153
165
  except Exception as e:
154
- print(f"RESULT:{'{'}'success': False, 'error': str(e), 'source': task.filename{'}'}")
166
+ result = {'success': False, 'error': str(e), 'source': task.filename}
167
+
168
+ print('RESULT:' + json.dumps(result))
155
169
  `.trim();
156
170
 
157
171
  const pyResp = await this.bridge.executePython(pythonCode);
158
172
 
159
- // Parse Python output
160
- let outputStr = '';
161
- if (typeof pyResp === 'object' && pyResp !== null) {
162
- if (pyResp.LogOutput && Array.isArray(pyResp.LogOutput)) {
163
- outputStr = pyResp.LogOutput.map((l: any) => l.Output || '').join('');
164
- } else if ('ReturnValue' in pyResp) {
165
- outputStr = String((pyResp as any).ReturnValue);
166
- } else {
167
- outputStr = JSON.stringify(pyResp);
168
- }
169
- } else {
170
- outputStr = String(pyResp || '');
173
+ const interpreted = interpretStandardResult(pyResp, {
174
+ successMessage: `Imported assets to ${cleanDest}`,
175
+ failureMessage: 'Import failed'
176
+ });
177
+
178
+ if (interpreted.success) {
179
+ const count = coerceNumber(interpreted.payload.imported) ?? 0;
180
+ const paths = coerceStringArray(interpreted.payload.paths) ?? [];
181
+ return {
182
+ success: true,
183
+ message: `Imported ${count} assets to ${cleanDest}`,
184
+ imported: count,
185
+ paths
186
+ };
171
187
  }
172
188
 
173
- const match = outputStr.match(/RESULT:({.*})/);
174
- if (match) {
189
+ const errorMessage = `Import failed: ${interpreted.error ?? 'Unknown error'} (source: ${interpreted.payload.source ?? sourcePath})`;
190
+ return {
191
+ success: false,
192
+ message: errorMessage,
193
+ error: errorMessage,
194
+ details: bestEffortInterpretedText(interpreted)
195
+ };
196
+ } catch (err) {
197
+ return { success: false, error: `Failed to import asset: ${err}` };
198
+ } finally {
199
+ if (createdTestFile) {
175
200
  try {
176
- const parsed = JSON.parse(match[1].replace(/'/g, '"'));
177
- if (parsed.success) {
178
- const count = parsed.imported?.count ?? 0;
179
- const paths = parsed.imported?.paths ?? [];
180
- return { success: true, message: `Imported ${count} assets to ${cleanDest}`, paths };
181
- } else {
182
- return { error: `Import failed: ${parsed.error || 'Unknown error'} (source: ${parsed.source || sourcePath})` };
183
- }
184
- } catch {
185
- // Fall through
201
+ await fs.rm(sourcePath, { force: true });
202
+ } catch (cleanupError) {
203
+ // Swallow cleanup error but log for debug visibility
204
+ console.warn(`Failed to clean up temporary FBX ${sourcePath}:`, cleanupError);
186
205
  }
187
206
  }
188
-
189
- // If unable to parse, return generic attempt result
190
- return { error: `Import did not report success for source ${sourcePath}` };
191
- } catch (err) {
192
- return { error: `Failed to import asset: ${err}` };
193
207
  }
194
208
  }
195
209
 
@@ -239,7 +253,7 @@ except Exception as e:
239
253
  }
240
254
  }
241
255
 
242
- private createTestFBX(filePath: string) {
256
+ private async createTestFBX(filePath: string): Promise<void> {
243
257
  // Create a minimal valid FBX ASCII file for testing
244
258
  const fbxContent = `; FBX 7.5.0 project file
245
259
  FBXHeaderExtension: {
@@ -295,11 +309,11 @@ Connections: {
295
309
 
296
310
  // Ensure directory exists
297
311
  const dir = path.dirname(filePath);
298
- if (!fs.existsSync(dir)) {
299
- fs.mkdirSync(dir, { recursive: true });
300
- }
312
+ try {
313
+ await fs.mkdir(dir, { recursive: true });
314
+ } catch {}
301
315
 
302
316
  // Write the FBX file
303
- fs.writeFileSync(filePath, fbxContent, 'utf8');
317
+ await fs.writeFile(filePath, fbxContent, 'utf8');
304
318
  }
305
319
  }