unreal-engine-mcp-server 0.4.0 → 0.4.4

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 +3 -2
  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,21 +1,9 @@
1
+ import { coerceBoolean, coerceNumber, interpretStandardResult } from '../utils/result-helpers.js';
1
2
  export class PerformanceTools {
2
3
  bridge;
3
4
  constructor(bridge) {
4
5
  this.bridge = bridge;
5
6
  }
6
- // Execute console command
7
- async _executeCommand(command) {
8
- return this.bridge.httpCall('/remote/object/call', 'PUT', {
9
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
10
- functionName: 'ExecuteConsoleCommand',
11
- parameters: {
12
- WorldContextObject: null,
13
- Command: command,
14
- SpecificPlayer: null
15
- },
16
- generateTransaction: false
17
- });
18
- }
19
7
  // Start profiling
20
8
  async startProfiling(params) {
21
9
  const commands = [];
@@ -44,9 +32,7 @@ export class PerformanceTools {
44
32
  if (params.duration) {
45
33
  commands.push(`stat stopfile ${params.duration}`);
46
34
  }
47
- for (const cmd of commands) {
48
- await this.bridge.executeConsoleCommand(cmd);
49
- }
35
+ await this.bridge.executeConsoleCommands(commands);
50
36
  return { success: true, message: `${params.type} profiling started` };
51
37
  }
52
38
  // Stop profiling
@@ -55,9 +41,7 @@ export class PerformanceTools {
55
41
  'stat stopfile',
56
42
  'stat none'
57
43
  ];
58
- for (const cmd of commands) {
59
- await this.bridge.executeConsoleCommand(cmd);
60
- }
44
+ await this.bridge.executeConsoleCommands(commands);
61
45
  return { success: true, message: 'Profiling stopped' };
62
46
  }
63
47
  // Show FPS
@@ -112,10 +96,17 @@ export class PerformanceTools {
112
96
  Foliage: 'Foliage',
113
97
  Shading: 'Shading',
114
98
  };
99
+ const requestedLevel = Number(params.level);
100
+ if (!Number.isInteger(requestedLevel) || requestedLevel < 0 || requestedLevel > 4) {
101
+ return {
102
+ success: false,
103
+ error: 'Invalid scalability level. Expected integer between 0 and 4.'
104
+ };
105
+ }
115
106
  const base = categoryBaseMap[params.category] || params.category;
116
107
  // Use direct console command to set with highest priority (SetByConsole)
117
108
  // This avoids conflicts with the scalability system
118
- const setCommand = `sg.${base}Quality ${params.level}`;
109
+ const setCommand = `sg.${base}Quality ${requestedLevel}`;
119
110
  // Apply the console command directly
120
111
  await this.bridge.executeConsoleCommand(setCommand);
121
112
  // Skip GameUserSettings entirely to avoid any scalability triggers
@@ -123,7 +114,7 @@ export class PerformanceTools {
123
114
  /* eslint-disable no-useless-escape */
124
115
  const py = `
125
116
  import unreal, json
126
- result = {'success': True, 'category': '${base}', 'requested': ${params.level}, 'actual': ${params.level}, 'method': 'ConsoleOnly'}
117
+ result = {'success': True, 'category': '${base}', 'requested': ${requestedLevel}, 'actual': ${requestedLevel}, 'method': 'ConsoleOnly'}
127
118
 
128
119
  # Simply verify the console variable was set correctly
129
120
  try:
@@ -167,32 +158,20 @@ print('RESULT:' + json.dumps(result))
167
158
  // Always try to apply through Python for consistency
168
159
  try {
169
160
  const pyResp = await this.bridge.executePython(py);
170
- let out = '';
171
- if (pyResp?.LogOutput && Array.isArray(pyResp.LogOutput)) {
172
- out = pyResp.LogOutput.map((l) => l.Output || '').join('');
173
- }
174
- else if (typeof pyResp === 'string') {
175
- out = pyResp;
176
- }
177
- else {
178
- out = JSON.stringify(pyResp);
179
- }
180
- const m = out.match(/RESULT:({.*})/);
181
- if (m) {
182
- try {
183
- const parsed = JSON.parse(m[1]);
184
- const verified = parsed.success && (parsed.actual === params.level);
185
- return {
186
- success: true,
187
- message: `${params.category} quality set to level ${params.level}`,
188
- verified,
189
- readback: parsed.actual,
190
- method: parsed.method || 'Unknown'
191
- };
192
- }
193
- catch {
194
- // Fall through to simple success
195
- }
161
+ const interpreted = interpretStandardResult(pyResp, {
162
+ successMessage: `${params.category} quality set to level ${requestedLevel}`,
163
+ failureMessage: `Failed to set ${params.category} quality`
164
+ });
165
+ if (interpreted.success) {
166
+ const actual = coerceNumber(interpreted.payload.actual) ?? requestedLevel;
167
+ const verified = coerceBoolean(interpreted.payload.success, true) === true && actual === requestedLevel;
168
+ return {
169
+ success: true,
170
+ message: interpreted.message,
171
+ verified,
172
+ readback: actual,
173
+ method: interpreted.payload.method || 'ConsoleOnly'
174
+ };
196
175
  }
197
176
  }
198
177
  catch {
@@ -201,7 +180,7 @@ print('RESULT:' + json.dumps(result))
201
180
  // If Python fails, the console command was still applied
202
181
  return {
203
182
  success: true,
204
- message: `${params.category} quality set to level ${params.level}`,
183
+ message: `${params.category} quality set to level ${requestedLevel}`,
205
184
  method: 'CVarOnly'
206
185
  };
207
186
  }
@@ -257,9 +236,7 @@ print('RESULT:' + json.dumps(result))
257
236
  if (params.outputPath) {
258
237
  commands.push(`obj savepackage ${params.outputPath}`);
259
238
  }
260
- for (const cmd of commands) {
261
- await this.bridge.executeConsoleCommand(cmd);
262
- }
239
+ await this.bridge.executeConsoleCommands(commands);
263
240
  return { success: true, message: 'Memory report generated' };
264
241
  }
265
242
  // Texture streaming
@@ -272,9 +249,7 @@ print('RESULT:' + json.dumps(result))
272
249
  if (params.boostPlayerLocation !== undefined) {
273
250
  commands.push(`r.Streaming.UseFixedPoolSize ${params.boostPlayerLocation ? 1 : 0}`);
274
251
  }
275
- for (const cmd of commands) {
276
- await this.bridge.executeConsoleCommand(cmd);
277
- }
252
+ await this.bridge.executeConsoleCommands(commands);
278
253
  return { success: true, message: 'Texture streaming configured' };
279
254
  }
280
255
  // LOD settings
@@ -292,9 +267,7 @@ print('RESULT:' + json.dumps(result))
292
267
  commands.push(`r.StaticMeshLODDistanceScale ${params.distanceScale}`);
293
268
  commands.push(`r.SkeletalMeshLODDistanceScale ${params.distanceScale}`);
294
269
  }
295
- for (const cmd of commands) {
296
- await this.bridge.executeConsoleCommand(cmd);
297
- }
270
+ await this.bridge.executeConsoleCommands(commands);
298
271
  return { success: true, message: 'LOD settings configured' };
299
272
  }
300
273
  // Apply a baseline performance profile (explicit CVar enforcement)
@@ -314,9 +287,7 @@ print('RESULT:' + json.dumps(result))
314
287
  `r.VSync ${p.vsync ? 1 : 0}`,
315
288
  `t.MaxFPS ${p.maxFPS}`,
316
289
  ];
317
- for (const cmd of commands) {
318
- await this.bridge.executeConsoleCommand(cmd);
319
- }
290
+ await this.bridge.executeConsoleCommands(commands);
320
291
  return { success: true, message: 'Baseline performance settings applied', params: p };
321
292
  }
322
293
  // Draw call optimization
@@ -329,9 +300,7 @@ print('RESULT:' + json.dumps(result))
329
300
  if (params.mergeActors) {
330
301
  commands.push('MergeActors');
331
302
  }
332
- for (const cmd of commands) {
333
- await this.bridge.executeConsoleCommand(cmd);
334
- }
303
+ await this.bridge.executeConsoleCommands(commands);
335
304
  return { success: true, message: 'Draw call optimization configured' };
336
305
  }
337
306
  // Occlusion culling
@@ -343,9 +312,7 @@ print('RESULT:' + json.dumps(result))
343
312
  if (params.freezeRendering !== undefined) {
344
313
  commands.push(`FreezeRendering ${params.freezeRendering ? 1 : 0}`);
345
314
  }
346
- for (const cmd of commands) {
347
- await this.bridge.executeConsoleCommand(cmd);
348
- }
315
+ await this.bridge.executeConsoleCommands(commands);
349
316
  return { success: true, message: 'Occlusion culling configured' };
350
317
  }
351
318
  // Shader compilation
@@ -360,9 +327,7 @@ print('RESULT:' + json.dumps(result))
360
327
  if (params.reducePermutations) {
361
328
  commands.push('RecompileShaders changed');
362
329
  }
363
- for (const cmd of commands) {
364
- await this.bridge.executeConsoleCommand(cmd);
365
- }
330
+ await this.bridge.executeConsoleCommands(commands);
366
331
  return { success: true, message: 'Shader optimization configured' };
367
332
  }
368
333
  // Nanite settings
@@ -375,9 +340,7 @@ print('RESULT:' + json.dumps(result))
375
340
  if (params.streamingPoolSize !== undefined) {
376
341
  commands.push(`r.Nanite.StreamingPoolSize ${params.streamingPoolSize}`);
377
342
  }
378
- for (const cmd of commands) {
379
- await this.bridge.executeConsoleCommand(cmd);
380
- }
343
+ await this.bridge.executeConsoleCommands(commands);
381
344
  return { success: true, message: 'Nanite configured' };
382
345
  }
383
346
  // World Partition streaming
@@ -390,22 +353,18 @@ print('RESULT:' + json.dumps(result))
390
353
  if (params.cellSize !== undefined) {
391
354
  commands.push(`wp.Runtime.CellSize ${params.cellSize}`);
392
355
  }
393
- for (const cmd of commands) {
394
- await this.bridge.executeConsoleCommand(cmd);
395
- }
356
+ await this.bridge.executeConsoleCommands(commands);
396
357
  return { success: true, message: 'World Partition configured' };
397
358
  }
398
359
  // Benchmark
399
360
  async runBenchmark(params) {
400
361
  const duration = params.duration || 60;
401
362
  // Start recording and GPU profiling
402
- await this.bridge.executeConsoleCommand('stat startfile');
403
- await this.bridge.executeConsoleCommand('profilegpu');
363
+ await this.bridge.executeConsoleCommands(['stat startfile', 'profilegpu']);
404
364
  // Wait for the requested duration
405
365
  await new Promise(resolve => setTimeout(resolve, duration * 1000));
406
366
  // Stop recording and clear stats
407
- await this.bridge.executeConsoleCommand('stat stopfile');
408
- await this.bridge.executeConsoleCommand('stat none');
367
+ await this.bridge.executeConsoleCommands(['stat stopfile', 'stat none']);
409
368
  return { success: true, message: `Benchmark completed for ${duration} seconds` };
410
369
  }
411
370
  }
@@ -28,20 +28,30 @@ export declare class PhysicsTools {
28
28
  };
29
29
  }>;
30
30
  }): Promise<{
31
+ success: true;
32
+ message: string;
33
+ path: string;
34
+ existingAsset?: boolean;
35
+ warnings?: string[];
36
+ details?: string[];
37
+ } | {
31
38
  success: boolean;
32
39
  message: string;
33
40
  error: string | undefined;
34
- path?: undefined;
41
+ warnings?: undefined;
42
+ details?: undefined;
35
43
  } | {
36
- success: boolean;
44
+ success: false;
37
45
  message: string;
38
- path: string;
39
- error?: undefined;
46
+ error: string;
47
+ warnings: string[] | undefined;
48
+ details: string[] | undefined;
40
49
  } | {
41
50
  success: boolean;
42
51
  error: string;
43
52
  message?: undefined;
44
- path?: undefined;
53
+ warnings?: undefined;
54
+ details?: undefined;
45
55
  }>;
46
56
  /**
47
57
  * Create Physics Constraint
@@ -133,7 +143,25 @@ export declare class PhysicsTools {
133
143
  vector: [number, number, number];
134
144
  boneName?: string;
135
145
  isLocal?: boolean;
136
- }): Promise<any>;
146
+ }): Promise<{
147
+ success: boolean;
148
+ message: string;
149
+ availableActors: string[] | undefined;
150
+ details: string[] | undefined;
151
+ error?: undefined;
152
+ } | {
153
+ success: boolean;
154
+ error: string;
155
+ availableActors: string[] | undefined;
156
+ details: string[] | undefined;
157
+ message?: undefined;
158
+ } | {
159
+ success: boolean;
160
+ error: string;
161
+ message?: undefined;
162
+ availableActors?: undefined;
163
+ details?: undefined;
164
+ }>;
137
165
  /**
138
166
  * Configure Cloth Simulation
139
167
  */
@@ -181,9 +209,5 @@ export declare class PhysicsTools {
181
209
  error: string;
182
210
  message?: undefined;
183
211
  }>;
184
- /**
185
- * Helper function to execute console commands
186
- */
187
- private _executeCommand;
188
212
  }
189
213
  //# sourceMappingURL=physics.d.ts.map