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
@@ -2,7 +2,6 @@ import { UnrealBridge } from '../unreal-bridge.js';
2
2
  export declare class DebugVisualizationTools {
3
3
  private bridge;
4
4
  constructor(bridge: UnrealBridge);
5
- private _executeCommand;
6
5
  private pyDraw;
7
6
  drawDebugLine(params: {
8
7
  start: [number, number, number];
@@ -12,10 +11,17 @@ export declare class DebugVisualizationTools {
12
11
  thickness?: number;
13
12
  }): Promise<{
14
13
  success: boolean;
15
- error?: undefined;
14
+ action: string;
15
+ warnings: string[] | undefined;
16
+ details: string[] | undefined;
17
+ rawOutput: string | undefined;
18
+ raw: unknown;
19
+ message?: string;
20
+ error?: string;
16
21
  } | {
17
22
  success: boolean;
18
23
  error: string;
24
+ action: string;
19
25
  }>;
20
26
  drawDebugBox(params: {
21
27
  center: [number, number, number];
@@ -26,10 +32,17 @@ export declare class DebugVisualizationTools {
26
32
  thickness?: number;
27
33
  }): Promise<{
28
34
  success: boolean;
29
- error?: undefined;
35
+ action: string;
36
+ warnings: string[] | undefined;
37
+ details: string[] | undefined;
38
+ rawOutput: string | undefined;
39
+ raw: unknown;
40
+ message?: string;
41
+ error?: string;
30
42
  } | {
31
43
  success: boolean;
32
44
  error: string;
45
+ action: string;
33
46
  }>;
34
47
  drawDebugSphere(params: {
35
48
  center: [number, number, number];
@@ -40,10 +53,17 @@ export declare class DebugVisualizationTools {
40
53
  thickness?: number;
41
54
  }): Promise<{
42
55
  success: boolean;
43
- error?: undefined;
56
+ action: string;
57
+ warnings: string[] | undefined;
58
+ details: string[] | undefined;
59
+ rawOutput: string | undefined;
60
+ raw: unknown;
61
+ message?: string;
62
+ error?: string;
44
63
  } | {
45
64
  success: boolean;
46
65
  error: string;
66
+ action: string;
47
67
  }>;
48
68
  drawDebugCapsule(params: {
49
69
  center: [number, number, number];
@@ -54,10 +74,17 @@ export declare class DebugVisualizationTools {
54
74
  duration?: number;
55
75
  }): Promise<{
56
76
  success: boolean;
57
- error?: undefined;
77
+ action: string;
78
+ warnings: string[] | undefined;
79
+ details: string[] | undefined;
80
+ rawOutput: string | undefined;
81
+ raw: unknown;
82
+ message?: string;
83
+ error?: string;
58
84
  } | {
59
85
  success: boolean;
60
86
  error: string;
87
+ action: string;
61
88
  }>;
62
89
  drawDebugCone(params: {
63
90
  origin: [number, number, number];
@@ -70,10 +97,17 @@ export declare class DebugVisualizationTools {
70
97
  duration?: number;
71
98
  }): Promise<{
72
99
  success: boolean;
73
- error?: undefined;
100
+ action: string;
101
+ warnings: string[] | undefined;
102
+ details: string[] | undefined;
103
+ rawOutput: string | undefined;
104
+ raw: unknown;
105
+ message?: string;
106
+ error?: string;
74
107
  } | {
75
108
  success: boolean;
76
109
  error: string;
110
+ action: string;
77
111
  }>;
78
112
  drawDebugString(params: {
79
113
  location: [number, number, number];
@@ -83,10 +117,17 @@ export declare class DebugVisualizationTools {
83
117
  fontSize?: number;
84
118
  }): Promise<{
85
119
  success: boolean;
86
- error?: undefined;
120
+ action: string;
121
+ warnings: string[] | undefined;
122
+ details: string[] | undefined;
123
+ rawOutput: string | undefined;
124
+ raw: unknown;
125
+ message?: string;
126
+ error?: string;
87
127
  } | {
88
128
  success: boolean;
89
129
  error: string;
130
+ action: string;
90
131
  }>;
91
132
  drawDebugArrow(params: {
92
133
  start: [number, number, number];
@@ -97,10 +138,17 @@ export declare class DebugVisualizationTools {
97
138
  thickness?: number;
98
139
  }): Promise<{
99
140
  success: boolean;
100
- error?: undefined;
141
+ action: string;
142
+ warnings: string[] | undefined;
143
+ details: string[] | undefined;
144
+ rawOutput: string | undefined;
145
+ raw: unknown;
146
+ message?: string;
147
+ error?: string;
101
148
  } | {
102
149
  success: boolean;
103
150
  error: string;
151
+ action: string;
104
152
  }>;
105
153
  drawDebugPoint(params: {
106
154
  location: [number, number, number];
@@ -109,10 +157,17 @@ export declare class DebugVisualizationTools {
109
157
  duration?: number;
110
158
  }): Promise<{
111
159
  success: boolean;
112
- error?: undefined;
160
+ action: string;
161
+ warnings: string[] | undefined;
162
+ details: string[] | undefined;
163
+ rawOutput: string | undefined;
164
+ raw: unknown;
165
+ message?: string;
166
+ error?: string;
113
167
  } | {
114
168
  success: boolean;
115
169
  error: string;
170
+ action: string;
116
171
  }>;
117
172
  drawDebugCoordinateSystem(params: {
118
173
  location: [number, number, number];
@@ -132,10 +187,17 @@ export declare class DebugVisualizationTools {
132
187
  duration?: number;
133
188
  }): Promise<{
134
189
  success: boolean;
135
- error?: undefined;
190
+ action: string;
191
+ warnings: string[] | undefined;
192
+ details: string[] | undefined;
193
+ rawOutput: string | undefined;
194
+ raw: unknown;
195
+ message?: string;
196
+ error?: string;
136
197
  } | {
137
198
  success: boolean;
138
199
  error: string;
200
+ action: string;
139
201
  }>;
140
202
  clearDebugDrawings(): Promise<any>;
141
203
  showCollision(params: {
@@ -1,39 +1,87 @@
1
+ import { bestEffortInterpretedText, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
2
+ import { parseStandardResult } from '../utils/python-output.js';
1
3
  export class DebugVisualizationTools {
2
4
  bridge;
3
5
  constructor(bridge) {
4
6
  this.bridge = bridge;
5
7
  }
6
- // Execute console command (kept for legacy operations)
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
8
  // Helper to draw via Python SystemLibrary with the editor world
20
- async pyDraw(scriptBody) {
9
+ async pyDraw(scriptBody, meta) {
10
+ const action = (meta?.action || 'debug_draw').replace(/\\/g, '\\\\').replace(/'/g, "\\'");
11
+ const payloadObject = meta?.params ?? {};
12
+ const payloadJson = JSON.stringify(payloadObject).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
13
+ const indentedBody = scriptBody
14
+ .split(/\r?\n/)
15
+ .map(line => ` ${line}`)
16
+ .join('\n');
21
17
  const script = `
22
18
  import unreal
23
- # Strict modern API: require UnrealEditorSubsystem (UE 5.1+)
19
+ import json
20
+
21
+ payload = json.loads('${payloadJson}')
24
22
  ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
25
23
  if not ues:
26
24
  raise Exception('UnrealEditorSubsystem not available')
27
25
  world = ues.get_editor_world()
28
- ${scriptBody}
26
+ if not world:
27
+ raise Exception('Editor world unavailable')
28
+ try:
29
+ ${indentedBody}
30
+ print('DEBUG_DRAW:' + json.dumps({'action': '${action}', 'params': payload}))
31
+ print('RESULT:' + json.dumps({'success': True, 'action': '${action}', 'params': payload}))
32
+ except Exception as e:
33
+ print('DEBUG_DRAW_ERROR:' + str(e))
34
+ print('RESULT:' + json.dumps({'success': False, 'action': '${action}', 'error': str(e)}))
29
35
  `.trim()
30
36
  .replace(/\r?\n/g, '\n');
31
37
  try {
32
- await this.bridge.executePython(script);
33
- return { success: true };
38
+ const response = await this.bridge.executePython(script);
39
+ let interpreted = interpretStandardResult(response, {
40
+ successMessage: `${action} executed`,
41
+ failureMessage: `${action} failed`
42
+ });
43
+ const parsed = parseStandardResult(response);
44
+ const parsedPayload = parsed.data ?? {};
45
+ const parsedSuccessValue = parsedPayload.success;
46
+ const normalizedSuccess = typeof parsedSuccessValue === 'string'
47
+ ? ['true', '1', 'yes'].includes(parsedSuccessValue.toLowerCase())
48
+ : parsedSuccessValue === true;
49
+ if (!interpreted.success && normalizedSuccess) {
50
+ interpreted = {
51
+ ...interpreted,
52
+ success: true,
53
+ error: undefined,
54
+ message: interpreted.message || `${action} executed`,
55
+ payload: { ...parsedPayload }
56
+ };
57
+ }
58
+ const finalSuccess = interpreted.success || normalizedSuccess;
59
+ const resolvedAction = coerceString(interpreted.payload.action) ?? action;
60
+ const fallbackOutput = typeof parsed.text === 'string' ? parsed.text : '';
61
+ const rawOutput = bestEffortInterpretedText(interpreted) ?? (fallbackOutput ? fallbackOutput : undefined);
62
+ if (finalSuccess) {
63
+ return {
64
+ ...interpreted.payload,
65
+ success: true,
66
+ action: resolvedAction,
67
+ warnings: interpreted.warnings,
68
+ details: interpreted.details,
69
+ rawOutput,
70
+ raw: parsed.raw ?? interpreted.raw
71
+ };
72
+ }
73
+ return {
74
+ success: false,
75
+ action: resolvedAction,
76
+ error: interpreted.error ?? `${resolvedAction} failed`,
77
+ warnings: interpreted.warnings,
78
+ details: interpreted.details,
79
+ rawOutput,
80
+ raw: parsed.raw ?? interpreted.raw
81
+ };
34
82
  }
35
83
  catch (e) {
36
- return { success: false, error: String(e) };
84
+ return { success: false, error: String(e), action: meta?.action || 'debug_draw' };
37
85
  }
38
86
  }
39
87
  // Draw debug line using Python SystemLibrary
@@ -50,7 +98,16 @@ end = unreal.Vector(${ex}, ${ey}, ${ez})
50
98
  color = unreal.LinearColor(${sr}/255.0, ${sg}/255.0, ${sb}/255.0, ${sa}/255.0)
51
99
  unreal.SystemLibrary.draw_debug_line(world, start, end, color, ${duration}, ${thickness})
52
100
  `;
53
- return this.pyDraw(script);
101
+ return this.pyDraw(script, {
102
+ action: 'debug_line',
103
+ params: {
104
+ start: params.start,
105
+ end: params.end,
106
+ color,
107
+ duration,
108
+ thickness
109
+ }
110
+ });
54
111
  }
55
112
  // Draw debug box using Python SystemLibrary
56
113
  async drawDebugBox(params) {
@@ -69,7 +126,17 @@ rot = unreal.Rotator(${rp}, ${ry}, ${rr})
69
126
  color = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)
70
127
  unreal.SystemLibrary.draw_debug_box(world, center, extent, color, rot, ${duration}, ${thickness})
71
128
  `;
72
- return this.pyDraw(script);
129
+ return this.pyDraw(script, {
130
+ action: 'debug_box',
131
+ params: {
132
+ center: params.center,
133
+ extent: params.extent,
134
+ rotation,
135
+ color,
136
+ duration,
137
+ thickness
138
+ }
139
+ });
73
140
  }
74
141
  // Draw debug sphere using Python SystemLibrary
75
142
  async drawDebugSphere(params) {
@@ -84,7 +151,17 @@ center = unreal.Vector(${cx}, ${cy}, ${cz})
84
151
  color = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)
85
152
  unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segments}, color, ${duration}, ${thickness})
86
153
  `;
87
- return this.pyDraw(script);
154
+ return this.pyDraw(script, {
155
+ action: 'debug_sphere',
156
+ params: {
157
+ center: params.center,
158
+ radius: params.radius,
159
+ segments,
160
+ color,
161
+ duration,
162
+ thickness
163
+ }
164
+ });
88
165
  }
89
166
  // The rest keep console-command fallbacks or editor helpers as before
90
167
  async drawDebugCapsule(params) {
@@ -95,7 +172,17 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
95
172
  const [rp, ry, rr] = rotation;
96
173
  const [cr, cg, cb, ca] = color;
97
174
  const script = `\ncenter = unreal.Vector(${cx}, ${cy}, ${cz})\nrot = unreal.Rotator(${rp}, ${ry}, ${rr})\ncolor = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)\nunreal.SystemLibrary.draw_debug_capsule(world, center, ${params.halfHeight}, ${params.radius}, rot, color, ${duration}, 1.0)\n`;
98
- return this.pyDraw(script);
175
+ return this.pyDraw(script, {
176
+ action: 'debug_capsule',
177
+ params: {
178
+ center: params.center,
179
+ halfHeight: params.halfHeight,
180
+ radius: params.radius,
181
+ rotation,
182
+ color,
183
+ duration
184
+ }
185
+ });
99
186
  }
100
187
  async drawDebugCone(params) {
101
188
  const color = params.color || [255, 0, 255, 255];
@@ -104,7 +191,19 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
104
191
  const [dx, dy, dz] = params.direction;
105
192
  const [cr, cg, cb, ca] = color;
106
193
  const script = `\norigin = unreal.Vector(${ox}, ${oy}, ${oz})\ndir = unreal.Vector(${dx}, ${dy}, ${dz})\ncolor = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)\nunreal.SystemLibrary.draw_debug_cone(world, origin, dir, ${params.length}, ${params.angleWidth}, ${params.angleHeight}, ${params.numSides || 12}, color, ${duration}, 1.0)\n`;
107
- return this.pyDraw(script);
194
+ return this.pyDraw(script, {
195
+ action: 'debug_cone',
196
+ params: {
197
+ origin: params.origin,
198
+ direction: params.direction,
199
+ length: params.length,
200
+ angleWidth: params.angleWidth,
201
+ angleHeight: params.angleHeight,
202
+ numSides: params.numSides || 12,
203
+ color,
204
+ duration
205
+ }
206
+ });
108
207
  }
109
208
  async drawDebugString(params) {
110
209
  const color = params.color || [255, 255, 255, 255];
@@ -112,7 +211,16 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
112
211
  const [x, y, z] = params.location;
113
212
  const [r, g, b, a] = color;
114
213
  const script = `\nloc = unreal.Vector(${x}, ${y}, ${z})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_string(world, loc, "${params.text.replace(/"/g, '\\"')}", None, color, ${duration})\n`;
115
- return this.pyDraw(script);
214
+ return this.pyDraw(script, {
215
+ action: 'debug_string',
216
+ params: {
217
+ location: params.location,
218
+ text: params.text,
219
+ color,
220
+ duration,
221
+ fontSize: params.fontSize
222
+ }
223
+ });
116
224
  }
117
225
  async drawDebugArrow(params) {
118
226
  const color = params.color || [0, 255, 255, 255];
@@ -122,7 +230,17 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
122
230
  const [ex, ey, ez] = params.end;
123
231
  const [r, g, b, a] = color;
124
232
  const script = `\nstart = unreal.Vector(${sx}, ${sy}, ${sz})\nend = unreal.Vector(${ex}, ${ey}, ${ez})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_arrow(world, start, end, ${params.arrowSize || 10.0}, color, ${duration}, ${thickness})\n`;
125
- return this.pyDraw(script);
233
+ return this.pyDraw(script, {
234
+ action: 'debug_arrow',
235
+ params: {
236
+ start: params.start,
237
+ end: params.end,
238
+ arrowSize: params.arrowSize || 10.0,
239
+ color,
240
+ duration,
241
+ thickness
242
+ }
243
+ });
126
244
  }
127
245
  async drawDebugPoint(params) {
128
246
  const size = params.size || 10.0;
@@ -131,7 +249,15 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
131
249
  const [x, y, z] = params.location;
132
250
  const [r, g, b, a] = color;
133
251
  const script = `\nloc = unreal.Vector(${x}, ${y}, ${z})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_point(world, loc, ${size}, color, ${duration})\n`;
134
- return this.pyDraw(script);
252
+ return this.pyDraw(script, {
253
+ action: 'debug_point',
254
+ params: {
255
+ location: params.location,
256
+ size,
257
+ color,
258
+ duration
259
+ }
260
+ });
135
261
  }
136
262
  async drawDebugCoordinateSystem(params) {
137
263
  const rotation = params.rotation || [0, 0, 0];
@@ -151,7 +277,19 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
151
277
  const [rp, ry, rr] = params.rotation;
152
278
  const [r, g, b, a] = color;
153
279
  const script = `\norigin = unreal.Vector(${ox}, ${oy}, ${oz})\nrot = unreal.Rotator(${rp}, ${ry}, ${rr})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_frustum(world, origin, rot, ${params.fov}, ${aspectRatio}, ${nearPlane}, ${farPlane}, color, ${duration})\n`;
154
- return this.pyDraw(script);
280
+ return this.pyDraw(script, {
281
+ action: 'debug_frustum',
282
+ params: {
283
+ origin: params.origin,
284
+ rotation: params.rotation,
285
+ fov: params.fov,
286
+ aspectRatio,
287
+ nearPlane,
288
+ farPlane,
289
+ color,
290
+ duration
291
+ }
292
+ });
155
293
  }
156
294
  async clearDebugDrawings() {
157
295
  return this.bridge.executeConsoleCommand('FlushPersistentDebugLines');
@@ -165,9 +303,7 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
165
303
  else {
166
304
  commands.push('show Collision 0');
167
305
  }
168
- for (const cmd of commands) {
169
- await this.bridge.executeConsoleCommand(cmd);
170
- }
306
+ await this.bridge.executeConsoleCommands(commands);
171
307
  return { success: true, message: `Collision visualization ${params.enabled ? 'enabled' : 'disabled'}` };
172
308
  }
173
309
  async showBounds(params) {
@@ -42,12 +42,19 @@ export declare class EditorTools {
42
42
  }>;
43
43
  buildLighting(): Promise<{
44
44
  success: boolean;
45
- message: any;
45
+ message: string;
46
46
  error?: undefined;
47
+ details?: undefined;
48
+ } | {
49
+ success: boolean;
50
+ error: string;
51
+ details: string | undefined;
52
+ message?: undefined;
47
53
  } | {
48
54
  success: boolean;
49
- error: any;
55
+ error: string;
50
56
  message?: undefined;
57
+ details?: undefined;
51
58
  }>;
52
59
  setViewportCamera(location?: {
53
60
  x: number;
@@ -1,4 +1,5 @@
1
1
  import { toVec3Object, toRotObject } from '../utils/normalize.js';
2
+ import { bestEffortInterpretedText, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
2
3
  export class EditorTools {
3
4
  bridge;
4
5
  constructor(bridge) {
@@ -65,28 +66,13 @@ else:
65
66
  print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
66
67
  `.trim();
67
68
  const resp = await this.bridge.executePython(pythonCmd);
68
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
69
- const m = out.match(/RESULT:({.*})/);
70
- if (m) {
71
- try {
72
- const parsed = JSON.parse(m[1]);
73
- if (parsed.success) {
74
- const method = parsed.method || 'LevelEditorSubsystem';
75
- return { success: true, message: `PIE started (via ${method})` };
76
- }
77
- }
78
- catch {
79
- try {
80
- // Fallback: handle non-JSON python dict-style output
81
- const sanitized = m[1].replace(/'/g, '"').replace(/\bTrue\b/g, 'true').replace(/\bFalse\b/g, 'false');
82
- const parsed = JSON.parse(sanitized);
83
- if (parsed.success) {
84
- const method = parsed.method || 'LevelEditorSubsystem';
85
- return { success: true, message: `PIE started (via ${method})` };
86
- }
87
- }
88
- catch { }
89
- }
69
+ const interpreted = interpretStandardResult(resp, {
70
+ successMessage: 'PIE started',
71
+ failureMessage: 'Failed to start PIE'
72
+ });
73
+ if (interpreted.success) {
74
+ const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
75
+ return { success: true, message: `PIE started (via ${method})` };
90
76
  }
91
77
  // If not verified, fall through to fallback
92
78
  }
@@ -125,20 +111,18 @@ else:
125
111
  print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
126
112
  `.trim();
127
113
  const resp = await this.bridge.executePython(pythonCmd);
128
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
129
- const m = out.match(/RESULT:({.*})/);
130
- if (m) {
131
- try {
132
- const parsed = JSON.parse(m[1].replace(/'/g, '"'));
133
- if (parsed.success) {
134
- const method = parsed.method || 'LevelEditorSubsystem';
135
- return { success: true, message: `PIE stopped via ${method}` };
136
- }
137
- }
138
- catch { }
114
+ const interpreted = interpretStandardResult(resp, {
115
+ successMessage: 'PIE stopped successfully',
116
+ failureMessage: 'Failed to stop PIE'
117
+ });
118
+ if (interpreted.success) {
119
+ const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
120
+ return { success: true, message: `PIE stopped via ${method}` };
139
121
  }
140
- // Default success message if parsing fails
141
- return { success: true, message: 'PIE stopped successfully' };
122
+ if (interpreted.error) {
123
+ return { success: false, error: interpreted.error };
124
+ }
125
+ return { success: false, error: 'Failed to stop PIE' };
142
126
  }
143
127
  catch {
144
128
  // Fallback to console command
@@ -193,16 +177,18 @@ except Exception as e:
193
177
  print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
194
178
  `.trim();
195
179
  const resp = await this.bridge.executePython(py);
196
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
197
- const m = out.match(/RESULT:({.*})/);
198
- if (m) {
199
- try {
200
- const parsed = JSON.parse(m[1].replace(/'/g, '"'));
201
- return parsed.success ? { success: true, message: parsed.message } : { success: false, error: parsed.error };
202
- }
203
- catch { }
180
+ const interpreted = interpretStandardResult(resp, {
181
+ successMessage: 'Lighting build started',
182
+ failureMessage: 'Failed to build lighting'
183
+ });
184
+ if (interpreted.success) {
185
+ return { success: true, message: interpreted.message };
204
186
  }
205
- return { success: true, message: 'Lighting build started' };
187
+ return {
188
+ success: false,
189
+ error: interpreted.error ?? 'Failed to build lighting',
190
+ details: bestEffortInterpretedText(interpreted)
191
+ };
206
192
  }
207
193
  catch (err) {
208
194
  return { success: false, error: `Failed to build lighting: ${err}` };
@@ -14,22 +14,32 @@ export declare class FoliageTools {
14
14
  groundSlope?: number;
15
15
  }): Promise<{
16
16
  success: boolean;
17
- error: any;
17
+ error: string;
18
+ note?: undefined;
19
+ created?: undefined;
20
+ exists?: undefined;
21
+ method?: undefined;
22
+ assetPath?: undefined;
23
+ usedMesh?: undefined;
24
+ message?: undefined;
25
+ } | {
26
+ success: boolean;
27
+ error: string;
28
+ note: string | undefined;
18
29
  created?: undefined;
19
30
  exists?: undefined;
20
31
  method?: undefined;
21
32
  assetPath?: undefined;
22
33
  usedMesh?: undefined;
23
- note?: undefined;
24
34
  message?: undefined;
25
35
  } | {
26
36
  success: boolean;
27
- created: any;
28
- exists: any;
29
- method: any;
30
- assetPath: any;
31
- usedMesh: any;
32
- note: any;
37
+ created: boolean;
38
+ exists: boolean;
39
+ method: string;
40
+ assetPath: string | undefined;
41
+ usedMesh: string | undefined;
42
+ note: string | undefined;
33
43
  message: string;
34
44
  error?: undefined;
35
45
  }>;
@@ -41,20 +51,29 @@ export declare class FoliageTools {
41
51
  eraseMode?: boolean;
42
52
  }): Promise<{
43
53
  success: boolean;
44
- error: any;
54
+ error: string;
55
+ note?: undefined;
56
+ added?: undefined;
57
+ actor?: undefined;
58
+ component?: undefined;
59
+ usedMesh?: undefined;
60
+ message?: undefined;
61
+ } | {
62
+ success: boolean;
63
+ error: string;
64
+ note: string | undefined;
45
65
  added?: undefined;
46
66
  actor?: undefined;
47
67
  component?: undefined;
48
68
  usedMesh?: undefined;
49
- note?: undefined;
50
69
  message?: undefined;
51
70
  } | {
52
71
  success: boolean;
53
- added: any;
54
- actor: any;
55
- component: any;
56
- usedMesh: any;
57
- note: any;
72
+ added: number;
73
+ actor: string | undefined;
74
+ component: string | undefined;
75
+ usedMesh: string | undefined;
76
+ note: string | undefined;
58
77
  message: string;
59
78
  error?: undefined;
60
79
  }>;