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
@@ -1,11 +1,13 @@
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
12
  // Sanitize destination path (remove trailing slash) and normalize UE path
11
13
  let cleanDest = destinationPath.replace(/\/$/, '');
@@ -18,16 +20,18 @@ export class AssetTools {
18
20
  if (sourcePath.includes('test_model.fbx')) {
19
21
  // Create the file outside of Python, before import
20
22
  try {
21
- this.createTestFBX(sourcePath);
22
- } catch (_err) {
23
+ await this.createTestFBX(sourcePath);
24
+ createdTestFile = true;
25
+ } catch (_err) {
23
26
  // If we can't create the file, we'll handle it in Python
24
27
  }
25
28
  }
26
29
 
27
30
  // Use Python API to import asset with file creation fallback
28
- const pythonCode = `
31
+ const pythonCode = `
29
32
  import unreal
30
33
  import os
34
+ import json
31
35
 
32
36
  # Create test FBX if needed
33
37
  source_path = r'${sourcePath}'
@@ -148,52 +152,58 @@ task.options = options
148
152
 
149
153
  # Use AssetTools to import
150
154
  asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
155
+ result = {'success': False, 'error': 'No assets imported', 'source': task.filename}
156
+
151
157
  try:
152
158
  asset_tools.import_asset_tasks([task])
153
159
  if task.imported_object_paths:
154
- print(f"RESULT:{'{'}'success': True, 'imported': {len(task.imported_object_paths)}, 'paths': {task.imported_object_paths}{'}'}")
155
- else:
156
- 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
+ }
157
165
  except Exception as e:
158
- 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))
159
169
  `.trim();
160
170
 
161
171
  const pyResp = await this.bridge.executePython(pythonCode);
162
172
 
163
- // Parse Python output
164
- let outputStr = '';
165
- if (typeof pyResp === 'object' && pyResp !== null) {
166
- if (pyResp.LogOutput && Array.isArray(pyResp.LogOutput)) {
167
- outputStr = pyResp.LogOutput.map((l: any) => l.Output || '').join('');
168
- } else if ('ReturnValue' in pyResp) {
169
- outputStr = String((pyResp as any).ReturnValue);
170
- } else {
171
- outputStr = JSON.stringify(pyResp);
172
- }
173
- } else {
174
- 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
+ };
175
187
  }
176
188
 
177
- const match = outputStr.match(/RESULT:({.*})/);
178
- 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) {
179
200
  try {
180
- const parsed = JSON.parse(match[1].replace(/'/g, '"'));
181
- if (parsed.success) {
182
- const count = parsed.imported?.count ?? 0;
183
- const paths = parsed.imported?.paths ?? [];
184
- return { success: true, message: `Imported ${count} assets to ${cleanDest}`, paths };
185
- } else {
186
- return { error: `Import failed: ${parsed.error || 'Unknown error'} (source: ${parsed.source || sourcePath})` };
187
- }
188
- } catch {
189
- // 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);
190
205
  }
191
206
  }
192
-
193
- // If unable to parse, return generic attempt result
194
- return { error: `Import did not report success for source ${sourcePath}` };
195
- } catch (err) {
196
- return { error: `Failed to import asset: ${err}` };
197
207
  }
198
208
  }
199
209
 
@@ -243,7 +253,7 @@ except Exception as e:
243
253
  }
244
254
  }
245
255
 
246
- private createTestFBX(filePath: string) {
256
+ private async createTestFBX(filePath: string): Promise<void> {
247
257
  // Create a minimal valid FBX ASCII file for testing
248
258
  const fbxContent = `; FBX 7.5.0 project file
249
259
  FBXHeaderExtension: {
@@ -299,11 +309,11 @@ Connections: {
299
309
 
300
310
  // Ensure directory exists
301
311
  const dir = path.dirname(filePath);
302
- if (!fs.existsSync(dir)) {
303
- fs.mkdirSync(dir, { recursive: true });
304
- }
312
+ try {
313
+ await fs.mkdir(dir, { recursive: true });
314
+ } catch {}
305
315
 
306
316
  // Write the FBX file
307
- fs.writeFileSync(filePath, fbxContent, 'utf8');
317
+ await fs.writeFile(filePath, fbxContent, 'utf8');
308
318
  }
309
319
  }