unreal-engine-mcp-server 0.5.0 → 0.5.1

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 (139) hide show
  1. package/.env.example +1 -1
  2. package/.github/release-drafter-config.yml +51 -0
  3. package/.github/workflows/greetings.yml +5 -1
  4. package/.github/workflows/labeler.yml +2 -1
  5. package/.github/workflows/publish-mcp.yml +1 -0
  6. package/.github/workflows/release-drafter.yml +1 -1
  7. package/.github/workflows/release.yml +3 -3
  8. package/CHANGELOG.md +71 -0
  9. package/CONTRIBUTING.md +1 -1
  10. package/GEMINI.md +115 -0
  11. package/Public/Plugin_setup_guide.mp4 +0 -0
  12. package/README.md +166 -200
  13. package/dist/config.d.ts +0 -1
  14. package/dist/config.js +0 -1
  15. package/dist/constants.d.ts +4 -0
  16. package/dist/constants.js +4 -0
  17. package/dist/graphql/loaders.d.ts +64 -0
  18. package/dist/graphql/loaders.js +117 -0
  19. package/dist/graphql/resolvers.d.ts +3 -3
  20. package/dist/graphql/resolvers.js +33 -30
  21. package/dist/graphql/server.js +3 -1
  22. package/dist/graphql/types.d.ts +2 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +13 -2
  25. package/dist/server-setup.d.ts +0 -1
  26. package/dist/server-setup.js +0 -40
  27. package/dist/tools/actors.d.ts +40 -24
  28. package/dist/tools/actors.js +8 -2
  29. package/dist/tools/assets.d.ts +19 -71
  30. package/dist/tools/assets.js +28 -22
  31. package/dist/tools/base-tool.d.ts +4 -4
  32. package/dist/tools/base-tool.js +1 -1
  33. package/dist/tools/blueprint.d.ts +33 -61
  34. package/dist/tools/consolidated-tool-handlers.js +96 -110
  35. package/dist/tools/dynamic-handler-registry.d.ts +11 -9
  36. package/dist/tools/dynamic-handler-registry.js +17 -95
  37. package/dist/tools/editor.d.ts +19 -193
  38. package/dist/tools/editor.js +8 -0
  39. package/dist/tools/environment.d.ts +8 -14
  40. package/dist/tools/foliage.d.ts +18 -143
  41. package/dist/tools/foliage.js +4 -2
  42. package/dist/tools/handlers/actor-handlers.js +0 -5
  43. package/dist/tools/handlers/asset-handlers.js +454 -454
  44. package/dist/tools/landscape.d.ts +16 -116
  45. package/dist/tools/landscape.js +7 -3
  46. package/dist/tools/level.d.ts +22 -103
  47. package/dist/tools/level.js +24 -16
  48. package/dist/tools/lighting.js +5 -1
  49. package/dist/tools/materials.js +5 -1
  50. package/dist/tools/niagara.js +37 -2
  51. package/dist/tools/performance.d.ts +0 -1
  52. package/dist/tools/performance.js +0 -1
  53. package/dist/tools/physics.js +5 -1
  54. package/dist/tools/sequence.d.ts +24 -24
  55. package/dist/tools/sequence.js +13 -0
  56. package/dist/tools/ui.d.ts +0 -2
  57. package/dist/types/automation-responses.d.ts +115 -0
  58. package/dist/types/automation-responses.js +2 -0
  59. package/dist/types/responses.d.ts +249 -0
  60. package/dist/types/responses.js +2 -0
  61. package/dist/types/tool-interfaces.d.ts +135 -135
  62. package/dist/utils/command-validator.js +3 -2
  63. package/dist/utils/path-security.d.ts +2 -0
  64. package/dist/utils/path-security.js +24 -0
  65. package/dist/utils/response-factory.d.ts +4 -4
  66. package/dist/utils/response-factory.js +15 -21
  67. package/docs/Migration-Guide-v0.5.0.md +1 -9
  68. package/docs/testing-guide.md +2 -2
  69. package/package.json +12 -6
  70. package/scripts/run-all-tests.mjs +25 -20
  71. package/server.json +3 -2
  72. package/src/config.ts +1 -1
  73. package/src/constants.ts +7 -0
  74. package/src/graphql/loaders.ts +244 -0
  75. package/src/graphql/resolvers.ts +47 -49
  76. package/src/graphql/server.ts +3 -1
  77. package/src/graphql/types.ts +3 -0
  78. package/src/index.ts +15 -2
  79. package/src/resources/assets.ts +5 -4
  80. package/src/server-setup.ts +3 -37
  81. package/src/tools/actors.ts +36 -28
  82. package/src/tools/animation.ts +1 -0
  83. package/src/tools/assets.ts +74 -63
  84. package/src/tools/base-tool.ts +3 -3
  85. package/src/tools/blueprint.ts +59 -59
  86. package/src/tools/consolidated-tool-handlers.ts +129 -150
  87. package/src/tools/dynamic-handler-registry.ts +22 -140
  88. package/src/tools/editor.ts +39 -26
  89. package/src/tools/environment.ts +21 -27
  90. package/src/tools/foliage.ts +28 -25
  91. package/src/tools/handlers/actor-handlers.ts +2 -8
  92. package/src/tools/handlers/asset-handlers.ts +484 -484
  93. package/src/tools/handlers/sequence-handlers.ts +1 -1
  94. package/src/tools/landscape.ts +34 -28
  95. package/src/tools/level.ts +96 -76
  96. package/src/tools/lighting.ts +6 -1
  97. package/src/tools/materials.ts +8 -2
  98. package/src/tools/niagara.ts +44 -2
  99. package/src/tools/performance.ts +1 -2
  100. package/src/tools/physics.ts +7 -1
  101. package/src/tools/sequence.ts +41 -25
  102. package/src/tools/ui.ts +0 -2
  103. package/src/types/automation-responses.ts +119 -0
  104. package/src/types/responses.ts +355 -0
  105. package/src/types/tool-interfaces.ts +135 -135
  106. package/src/utils/command-validator.ts +3 -2
  107. package/src/utils/normalize.test.ts +162 -0
  108. package/src/utils/path-security.ts +43 -0
  109. package/src/utils/response-factory.ts +29 -24
  110. package/src/utils/safe-json.test.ts +90 -0
  111. package/src/utils/validation.test.ts +184 -0
  112. package/tests/test-animation.mjs +358 -33
  113. package/tests/test-asset-graph.mjs +311 -0
  114. package/tests/test-audio.mjs +314 -116
  115. package/tests/test-behavior-tree.mjs +327 -144
  116. package/tests/test-blueprint-graph.mjs +343 -12
  117. package/tests/test-control-editor.mjs +85 -53
  118. package/tests/test-graphql.mjs +58 -8
  119. package/tests/test-input.mjs +349 -0
  120. package/tests/test-inspect.mjs +291 -61
  121. package/tests/test-landscape.mjs +304 -48
  122. package/tests/test-lighting.mjs +428 -0
  123. package/tests/test-manage-level.mjs +70 -51
  124. package/tests/test-performance.mjs +539 -0
  125. package/tests/test-sequence.mjs +82 -46
  126. package/tests/test-system.mjs +72 -33
  127. package/tests/test-wasm.mjs +98 -8
  128. package/vitest.config.ts +35 -0
  129. package/.github/release-drafter.yml +0 -148
  130. package/dist/prompts/index.d.ts +0 -21
  131. package/dist/prompts/index.js +0 -217
  132. package/dist/tools/blueprint/helpers.d.ts +0 -29
  133. package/dist/tools/blueprint/helpers.js +0 -182
  134. package/src/prompts/index.ts +0 -249
  135. package/src/tools/blueprint/helpers.ts +0 -189
  136. package/tests/test-blueprint-events.mjs +0 -35
  137. package/tests/test-extra-tools.mjs +0 -38
  138. package/tests/test-render.mjs +0 -33
  139. package/tests/test-search-assets.mjs +0 -66
@@ -1,6 +1,7 @@
1
1
  import { UnrealBridge } from '../unreal-bridge.js';
2
2
  import { AutomationBridge } from '../automation/index.js';
3
- export declare class LandscapeTools {
3
+ import { ILandscapeTools, StandardActionResponse } from '../types/tool-interfaces.js';
4
+ export declare class LandscapeTools implements ILandscapeTools {
4
5
  private bridge;
5
6
  private automationBridge?;
6
7
  constructor(bridge: UnrealBridge, automationBridge?: AutomationBridge | undefined);
@@ -18,7 +19,7 @@ export declare class LandscapeTools {
18
19
  runtimeGrid?: string;
19
20
  isSpatiallyLoaded?: boolean;
20
21
  dataLayers?: string[];
21
- }): Promise<Record<string, unknown>>;
22
+ }): Promise<StandardActionResponse>;
22
23
  sculptLandscape(params: {
23
24
  landscapeName: string;
24
25
  tool: string;
@@ -27,17 +28,7 @@ export declare class LandscapeTools {
27
28
  strength?: number;
28
29
  location?: [number, number, number];
29
30
  radius?: number;
30
- }): Promise<{
31
- success: boolean;
32
- error: string;
33
- message?: undefined;
34
- details?: undefined;
35
- } | {
36
- success: boolean;
37
- message: string;
38
- details: import("../automation/types.js").AutomationBridgeResponseMessage;
39
- error?: undefined;
40
- }>;
31
+ }): Promise<StandardActionResponse>;
41
32
  paintLandscape(params: {
42
33
  landscapeName: string;
43
34
  layerName: string;
@@ -47,17 +38,7 @@ export declare class LandscapeTools {
47
38
  targetValue?: number;
48
39
  radius?: number;
49
40
  density?: number;
50
- }): Promise<{
51
- success: boolean;
52
- error: string;
53
- message?: undefined;
54
- details?: undefined;
55
- } | {
56
- success: boolean;
57
- message: string;
58
- details: import("../automation/types.js").AutomationBridgeResponseMessage;
59
- error?: undefined;
60
- }>;
41
+ }): Promise<StandardActionResponse>;
61
42
  createProceduralTerrain(params: {
62
43
  name: string;
63
44
  location?: [number, number, number];
@@ -67,37 +48,7 @@ export declare class LandscapeTools {
67
48
  heightFunction?: string;
68
49
  material?: string;
69
50
  settings?: Record<string, unknown>;
70
- }): Promise<{
71
- success: boolean;
72
- error: string;
73
- message: string;
74
- actorName?: undefined;
75
- vertices?: undefined;
76
- triangles?: undefined;
77
- size?: undefined;
78
- subdivisions?: undefined;
79
- details?: undefined;
80
- } | {
81
- success: boolean;
82
- message: string;
83
- actorName: any;
84
- vertices: any;
85
- triangles: any;
86
- size: any;
87
- subdivisions: any;
88
- details: any;
89
- error?: undefined;
90
- } | {
91
- success: boolean;
92
- error: string;
93
- message?: undefined;
94
- actorName?: undefined;
95
- vertices?: undefined;
96
- triangles?: undefined;
97
- size?: undefined;
98
- subdivisions?: undefined;
99
- details?: undefined;
100
- }>;
51
+ }): Promise<StandardActionResponse>;
101
52
  createLandscapeGrassType(params: {
102
53
  name: string;
103
54
  meshPath: string;
@@ -106,11 +57,11 @@ export declare class LandscapeTools {
106
57
  maxScale?: number;
107
58
  path?: string;
108
59
  staticMesh?: string;
109
- }): Promise<any>;
60
+ }): Promise<StandardActionResponse>;
110
61
  setLandscapeMaterial(params: {
111
62
  landscapeName: string;
112
63
  materialPath: string;
113
- }): Promise<any>;
64
+ }): Promise<StandardActionResponse>;
114
65
  createLandscapeGrass(params: {
115
66
  landscapeName: string;
116
67
  grassType: string;
@@ -118,85 +69,42 @@ export declare class LandscapeTools {
118
69
  minScale?: number;
119
70
  maxScale?: number;
120
71
  randomRotation?: boolean;
121
- }): Promise<{
122
- success: boolean;
123
- message: string;
124
- }>;
72
+ }): Promise<StandardActionResponse>;
125
73
  updateLandscapeCollision(params: {
126
74
  landscapeName: string;
127
75
  collisionMipLevel?: number;
128
76
  simpleCollision?: boolean;
129
- }): Promise<{
130
- success: boolean;
131
- message: string;
132
- }>;
77
+ }): Promise<StandardActionResponse>;
133
78
  retopologizeLandscape(params: {
134
79
  landscapeName: string;
135
80
  targetTriangleCount?: number;
136
81
  preserveDetails?: boolean;
137
- }): Promise<{
138
- success: boolean;
139
- message: string;
140
- }>;
82
+ }): Promise<StandardActionResponse>;
141
83
  createWaterBody(params: {
142
84
  type: 'Ocean' | 'Lake' | 'River' | 'Stream';
143
85
  name: string;
144
86
  location?: [number, number, number];
145
87
  size?: [number, number];
146
88
  depth?: number;
147
- }): Promise<any>;
89
+ }): Promise<StandardActionResponse>;
148
90
  configureWorldPartition(params: {
149
91
  landscapeName: string;
150
92
  enableSpatialLoading?: boolean;
151
93
  runtimeGrid?: string;
152
94
  dataLayers?: string[];
153
95
  streamingDistance?: number;
154
- }): Promise<{
155
- success: boolean;
156
- error: string;
157
- message?: undefined;
158
- changes?: undefined;
159
- } | {
160
- success: boolean;
161
- message: string;
162
- changes: unknown;
163
- error?: undefined;
164
- }>;
96
+ }): Promise<StandardActionResponse>;
165
97
  setDataLayers(params: {
166
98
  landscapeName: string;
167
99
  dataLayerNames: string[];
168
100
  operation: 'add' | 'remove' | 'set';
169
- }): Promise<{
170
- success: boolean;
171
- message: string;
172
- layers: string[];
173
- error?: undefined;
174
- } | {
175
- success: boolean;
176
- error: string;
177
- message?: undefined;
178
- layers?: undefined;
179
- }>;
101
+ }): Promise<StandardActionResponse>;
180
102
  configureStreamingCells(params: {
181
103
  landscapeName: string;
182
104
  cellSize?: number;
183
105
  loadingRange?: number;
184
106
  enableHLOD?: boolean;
185
- }): Promise<{
186
- success: boolean;
187
- message: string;
188
- settings: {
189
- cellSize: number | undefined;
190
- loadingRange: number | undefined;
191
- hlod: boolean | undefined;
192
- };
193
- error?: undefined;
194
- } | {
195
- success: boolean;
196
- error: string;
197
- message?: undefined;
198
- settings?: undefined;
199
- }>;
107
+ }): Promise<StandardActionResponse>;
200
108
  modifyHeightmap(params: {
201
109
  landscapeName: string;
202
110
  heightData: number[];
@@ -205,14 +113,6 @@ export declare class LandscapeTools {
205
113
  maxX: number;
206
114
  maxY: number;
207
115
  updateNormals?: boolean;
208
- }): Promise<{
209
- success: boolean;
210
- error: string;
211
- message?: undefined;
212
- } | {
213
- success: boolean;
214
- message: string;
215
- error?: undefined;
216
- }>;
116
+ }): Promise<StandardActionResponse>;
217
117
  }
218
118
  //# sourceMappingURL=landscape.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import { ensureVector3 } from '../utils/validation.js';
2
+ import { wasmIntegration } from '../wasm/index.js';
2
3
  export class LandscapeTools {
3
4
  bridge;
4
5
  automationBridge;
@@ -28,6 +29,9 @@ export class LandscapeTools {
28
29
  throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
29
30
  }
30
31
  const [locX, locY, locZ] = ensureVector3(params.location ?? [0, 0, 0], 'landscape location');
32
+ const zeroVector = [0, 0, 0];
33
+ const processedLocation = wasmIntegration.vectorAdd(zeroVector, [locX, locY, locZ]);
34
+ console.error('[WASM] Using vectorAdd for landscape positioning');
31
35
  const sectionsPerComponent = Math.max(1, Math.floor(params.sectionsPerComponent ?? 1));
32
36
  const quadsPerSection = Math.max(1, Math.floor(params.quadsPerSection ?? 63));
33
37
  try {
@@ -36,9 +40,9 @@ export class LandscapeTools {
36
40
  const quadsPerComponent = quadsPerSection;
37
41
  const payload = {
38
42
  name,
39
- x: locX,
40
- y: locY,
41
- z: locZ,
43
+ x: processedLocation[0],
44
+ y: processedLocation[1],
45
+ z: processedLocation[2],
42
46
  componentsX,
43
47
  componentsY,
44
48
  quadsPerComponent,
@@ -1,5 +1,5 @@
1
1
  import { BaseTool } from './base-tool.js';
2
- import { ILevelTools } from '../types/tool-interfaces.js';
2
+ import { ILevelTools, StandardActionResponse } from '../types/tool-interfaces.js';
3
3
  export declare class LevelTools extends BaseTool implements ILevelTools {
4
4
  private managedLevels;
5
5
  private listCache?;
@@ -15,13 +15,8 @@ export declare class LevelTools extends BaseTool implements ILevelTools {
15
15
  private listManagedLevels;
16
16
  private summarizeLevel;
17
17
  private setCurrentLevel;
18
- listLevels(): Promise<Record<string, unknown> | {
19
- success: true;
20
- message: string;
21
- count: number;
22
- levels: Array<Record<string, unknown>>;
23
- }>;
24
- getLevelSummary(levelPath?: string): Promise<Record<string, unknown>>;
18
+ listLevels(): Promise<StandardActionResponse>;
19
+ getLevelSummary(levelPath?: string): Promise<StandardActionResponse>;
25
20
  registerLight(levelPath: string | undefined, info: {
26
21
  name: string;
27
22
  type: string;
@@ -32,122 +27,52 @@ export declare class LevelTools extends BaseTool implements ILevelTools {
32
27
  exportPath: string;
33
28
  note?: string;
34
29
  timeoutMs?: number;
35
- }): Promise<{
36
- success: boolean;
37
- error: string;
38
- levelPath?: undefined;
39
- exportPath?: undefined;
40
- details?: undefined;
41
- message?: undefined;
42
- } | {
43
- success: boolean;
44
- error: any;
45
- levelPath: string;
46
- exportPath: string;
47
- details: any;
48
- message?: undefined;
49
- } | {
50
- success: boolean;
51
- message: string;
52
- levelPath: string;
53
- exportPath: string;
54
- details: any;
55
- error?: undefined;
56
- }>;
30
+ }): Promise<StandardActionResponse>;
57
31
  importLevel(params: {
58
32
  packagePath: string;
59
33
  destinationPath?: string;
60
34
  streaming?: boolean;
61
35
  timeoutMs?: number;
62
- }): Promise<{
63
- success: boolean;
64
- error: any;
65
- levelPath: string;
66
- details: any;
67
- message?: undefined;
68
- partitioned?: undefined;
69
- streaming?: undefined;
70
- } | {
71
- success: boolean;
72
- message: string;
73
- levelPath: string;
74
- partitioned: boolean;
75
- streaming: boolean;
76
- details: any;
77
- error?: undefined;
78
- } | {
79
- success: boolean;
80
- error: string;
81
- levelPath?: undefined;
82
- details?: undefined;
83
- message?: undefined;
84
- partitioned?: undefined;
85
- streaming?: undefined;
86
- }>;
36
+ }): Promise<StandardActionResponse>;
87
37
  saveLevelAs(params: {
88
38
  sourcePath?: string;
89
39
  targetPath: string;
90
- }): Promise<{
91
- success: boolean;
92
- error: any;
93
- message?: undefined;
94
- levelPath?: undefined;
95
- } | {
96
- success: boolean;
97
- message: any;
98
- levelPath: string;
99
- error?: undefined;
100
- }>;
40
+ }): Promise<StandardActionResponse>;
101
41
  deleteLevels(params: {
102
42
  levelPaths: string[];
103
- }): Promise<{
104
- success: boolean;
105
- message: string;
106
- removed: string[];
107
- }>;
43
+ }): Promise<StandardActionResponse>;
108
44
  loadLevel(params: {
109
45
  levelPath: string;
110
46
  streaming?: boolean;
111
47
  position?: [number, number, number];
112
- }): Promise<any>;
48
+ }): Promise<StandardActionResponse>;
113
49
  saveLevel(params: {
114
50
  levelName?: string;
115
51
  savePath?: string;
116
- }): Promise<Record<string, unknown> | {
117
- success: boolean;
118
- error: any;
119
- }>;
52
+ }): Promise<StandardActionResponse>;
120
53
  createLevel(params: {
121
54
  levelName: string;
122
55
  template?: 'Empty' | 'Default' | 'VR' | 'TimeOfDay';
123
56
  savePath?: string;
124
- }): Promise<Record<string, unknown> | {
125
- success: boolean;
126
- error: any;
127
- path: string;
128
- partitioned: boolean;
129
- }>;
57
+ }): Promise<StandardActionResponse>;
130
58
  addSubLevel(params: {
131
59
  parentLevel?: string;
132
60
  subLevelPath: string;
133
61
  streamingMethod?: 'Blueprint' | 'AlwaysLoaded';
134
- }): Promise<any>;
62
+ }): Promise<StandardActionResponse>;
135
63
  streamLevel(params: {
136
64
  levelPath?: string;
137
65
  levelName?: string;
138
66
  shouldBeLoaded: boolean;
139
67
  shouldBeVisible?: boolean;
140
68
  position?: [number, number, number];
141
- }): Promise<any>;
69
+ }): Promise<StandardActionResponse>;
142
70
  setupWorldComposition(params: {
143
71
  enableComposition: boolean;
144
72
  tileSize?: number;
145
73
  distanceStreaming?: boolean;
146
74
  streamingDistance?: number;
147
- }): Promise<{
148
- success: boolean;
149
- message: string;
150
- }>;
75
+ }): Promise<StandardActionResponse>;
151
76
  editLevelBlueprint(params: {
152
77
  eventType: 'BeginPlay' | 'EndPlay' | 'Tick' | 'Custom';
153
78
  customEventName?: string;
@@ -156,50 +81,44 @@ export declare class LevelTools extends BaseTool implements ILevelTools {
156
81
  position: [number, number];
157
82
  connections?: string[];
158
83
  }>;
159
- }): Promise<any>;
84
+ }): Promise<StandardActionResponse>;
160
85
  createSubLevel(params: {
161
86
  name: string;
162
87
  type: 'Persistent' | 'Streaming' | 'Lighting' | 'Gameplay';
163
88
  parent?: string;
164
- }): Promise<any>;
89
+ }): Promise<StandardActionResponse>;
165
90
  setWorldSettings(params: {
166
91
  gravity?: number;
167
92
  worldScale?: number;
168
93
  gameMode?: string;
169
94
  defaultPawn?: string;
170
95
  killZ?: number;
171
- }): Promise<{
172
- success: boolean;
173
- message: string;
174
- }>;
96
+ }): Promise<StandardActionResponse>;
175
97
  setLevelBounds(params: {
176
98
  min: [number, number, number];
177
99
  max: [number, number, number];
178
- }): Promise<any>;
100
+ }): Promise<StandardActionResponse>;
179
101
  buildNavMesh(params: {
180
102
  rebuildAll?: boolean;
181
103
  selectedOnly?: boolean;
182
- }): Promise<Record<string, unknown> | {
183
- success: boolean;
184
- error: any;
185
- }>;
104
+ }): Promise<StandardActionResponse>;
186
105
  setLevelVisibility(params: {
187
106
  levelName: string;
188
107
  visible: boolean;
189
- }): Promise<any>;
108
+ }): Promise<StandardActionResponse>;
190
109
  setWorldOrigin(params: {
191
110
  location: [number, number, number];
192
- }): Promise<any>;
111
+ }): Promise<StandardActionResponse>;
193
112
  createStreamingVolume(params: {
194
113
  levelName: string;
195
114
  position: [number, number, number];
196
115
  size: [number, number, number];
197
116
  streamingDistance?: number;
198
- }): Promise<any>;
117
+ }): Promise<StandardActionResponse>;
199
118
  setLevelLOD(params: {
200
119
  levelName: string;
201
120
  lodLevel: number;
202
121
  distance: number;
203
- }): Promise<any>;
122
+ }): Promise<StandardActionResponse>;
204
123
  }
205
124
  //# sourceMappingURL=level.d.ts.map
@@ -1,4 +1,6 @@
1
1
  import { BaseTool } from './base-tool.js';
2
+ import { sanitizePath } from '../utils/path-security.js';
3
+ import { DEFAULT_OPERATION_TIMEOUT_MS, DEFAULT_ASSET_OP_TIMEOUT_MS, LONG_RUNNING_OP_TIMEOUT_MS } from '../constants.js';
2
4
  export class LevelTools extends BaseTool {
3
5
  managedLevels = new Map();
4
6
  listCache;
@@ -18,6 +20,12 @@ export class LevelTools extends BaseTool {
18
20
  if (!formatted.startsWith('/Game/')) {
19
21
  formatted = `/Game/${formatted.replace(/^\/+/, '')}`;
20
22
  }
23
+ try {
24
+ formatted = sanitizePath(formatted);
25
+ }
26
+ catch (e) {
27
+ throw new Error(`Security validation failed for level path: ${e.message}`);
28
+ }
21
29
  formatted = formatted.replace(/\.umap$/i, '');
22
30
  if (formatted.endsWith('/')) {
23
31
  formatted = formatted.slice(0, -1);
@@ -180,6 +188,7 @@ export class LevelTools extends BaseTool {
180
188
  const managedOnly = managed.levels.filter(m => !ueLevels.some(u => u.path === m.path));
181
189
  const finalLevels = [...ueLevels, ...managedOnly];
182
190
  const result = {
191
+ ...response,
183
192
  success: true,
184
193
  message: 'Levels listed from Unreal Engine',
185
194
  levels: finalLevels,
@@ -190,7 +199,6 @@ export class LevelTools extends BaseTool {
190
199
  levels: finalLevels,
191
200
  count: finalLevels.length
192
201
  },
193
- ...response,
194
202
  managedLevels: managed.levels,
195
203
  managedLevelCount: managed.count
196
204
  };
@@ -234,7 +242,7 @@ export class LevelTools extends BaseTool {
234
242
  action: 'export_level',
235
243
  levelPath: resolved,
236
244
  exportPath: params.exportPath
237
- }, { timeoutMs: params.timeoutMs ?? 300000 });
245
+ }, { timeoutMs: params.timeoutMs ?? LONG_RUNNING_OP_TIMEOUT_MS });
238
246
  if (res?.success === false) {
239
247
  return {
240
248
  success: false,
@@ -265,7 +273,7 @@ export class LevelTools extends BaseTool {
265
273
  action: 'import_level',
266
274
  packagePath: params.packagePath,
267
275
  destinationPath: destination.path
268
- }, { timeoutMs: params.timeoutMs ?? 300000 });
276
+ }, { timeoutMs: params.timeoutMs ?? LONG_RUNNING_OP_TIMEOUT_MS });
269
277
  if (res?.success === false) {
270
278
  return {
271
279
  success: false,
@@ -295,7 +303,7 @@ export class LevelTools extends BaseTool {
295
303
  action: 'save_level_as',
296
304
  savePath: target.path
297
305
  }, {
298
- timeoutMs: 60000
306
+ timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
299
307
  });
300
308
  if (response.success === false) {
301
309
  return { success: false, error: response.error || response.message || 'Failed to save level as' };
@@ -382,7 +390,7 @@ export class LevelTools extends BaseTool {
382
390
  const response = await this.sendAutomationRequest('manage_level', {
383
391
  action: 'load',
384
392
  levelPath: params.levelPath
385
- }, { timeoutMs: 30000 });
393
+ }, { timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS });
386
394
  if (response.success) {
387
395
  this.setCurrentLevel(normalizedPath);
388
396
  this.mutateRecord(normalizedPath, {
@@ -391,11 +399,11 @@ export class LevelTools extends BaseTool {
391
399
  visible: true
392
400
  });
393
401
  return {
402
+ ...response,
394
403
  success: true,
395
404
  message: `Level loaded: ${params.levelPath}`,
396
405
  level: normalizedPath,
397
- streaming: false,
398
- ...response
406
+ streaming: false
399
407
  };
400
408
  }
401
409
  }
@@ -461,15 +469,15 @@ export class LevelTools extends BaseTool {
461
469
  payload.savePath = params.savePath;
462
470
  }
463
471
  const response = await this.sendAutomationRequest('manage_level', payload, {
464
- timeoutMs: 60000
472
+ timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
465
473
  });
466
474
  if (response.success === false) {
467
475
  return { success: false, error: response.error || response.message || 'Failed to save level' };
468
476
  }
469
477
  const result = {
478
+ ...response,
470
479
  success: true,
471
- message: response.message || 'Level saved',
472
- ...response
480
+ message: response.message || 'Level saved'
473
481
  };
474
482
  if (response.skipped) {
475
483
  result.skipped = response.skipped;
@@ -498,7 +506,7 @@ export class LevelTools extends BaseTool {
498
506
  levelPath: fullPath,
499
507
  useWorldPartition: isPartitioned
500
508
  }, {
501
- timeoutMs: 60000
509
+ timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
502
510
  });
503
511
  if (response.success === false) {
504
512
  return {
@@ -509,13 +517,13 @@ export class LevelTools extends BaseTool {
509
517
  };
510
518
  }
511
519
  const result = {
520
+ ...response,
512
521
  success: true,
513
522
  message: response.message || 'Level created',
514
523
  path: response.levelPath || fullPath,
515
524
  packagePath: response.packagePath ?? fullPath,
516
525
  objectPath: response.objectPath,
517
- partitioned: isPartitioned,
518
- ...response
526
+ partitioned: isPartitioned
519
527
  };
520
528
  if (response.warnings) {
521
529
  result.warnings = response.warnings;
@@ -551,7 +559,7 @@ export class LevelTools extends BaseTool {
551
559
  subLevelPath: sub,
552
560
  parentPath: parent,
553
561
  streamingMethod: params.streamingMethod
554
- }, { timeoutMs: 30000 });
562
+ }, { timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS });
555
563
  if (response && (response.error === 'PACKAGE_NOT_FOUND' || response.error === 'ADD_FAILED') && !sub.endsWith('.umap')) {
556
564
  const subWithExt = sub + '.umap';
557
565
  response = await this.sendAutomationRequest('manage_level', {
@@ -560,7 +568,7 @@ export class LevelTools extends BaseTool {
560
568
  subLevelPath: subWithExt,
561
569
  parentPath: parent,
562
570
  streamingMethod: params.streamingMethod
563
- }, { timeoutMs: 30000 });
571
+ }, { timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS });
564
572
  }
565
573
  if (response.success) {
566
574
  this.ensureRecord(sub, { loaded: true, visible: true, streaming: true });
@@ -608,7 +616,7 @@ export class LevelTools extends BaseTool {
608
616
  shouldBeLoaded: params.shouldBeLoaded,
609
617
  shouldBeVisible
610
618
  }, {
611
- timeoutMs: 60000
619
+ timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
612
620
  });
613
621
  if (response.success === false) {
614
622
  const errorCode = typeof response.error === 'string' ? response.error : '';
@@ -1,4 +1,5 @@
1
1
  import { ensureVector3 } from '../utils/validation.js';
2
+ import { wasmIntegration } from '../wasm/index.js';
2
3
  export class LightingTools {
3
4
  bridge;
4
5
  automationBridge;
@@ -32,7 +33,10 @@ export class LightingTools {
32
33
  name: params.name,
33
34
  };
34
35
  if (params.location) {
35
- payload.location = { x: params.location[0], y: params.location[1], z: params.location[2] };
36
+ const zeroVector = [0, 0, 0];
37
+ const processedLocation = wasmIntegration.vectorAdd(zeroVector, params.location);
38
+ console.error('[WASM] Using vectorAdd for light positioning');
39
+ payload.location = { x: processedLocation[0], y: processedLocation[1], z: processedLocation[2] };
36
40
  }
37
41
  if (params.rotation) {
38
42
  if (Array.isArray(params.rotation)) {
@@ -129,7 +129,11 @@ export class MaterialTools {
129
129
  return { success: false, error: resp?.error ?? resp?.message ?? 'CREATE_MATERIAL_INSTANCE_FAILED' };
130
130
  }
131
131
  }
132
- catch (_e) {
132
+ catch (e) {
133
+ const msg = e instanceof Error ? e.message : String(e);
134
+ if (!msg.includes('unknown') && !msg.includes('UNKNOWN_PLUGIN_ACTION')) {
135
+ console.warn(`[MaterialTools] Plugin create_material_instance failed with specific error (falling back to python): ${msg}`);
136
+ }
133
137
  }
134
138
  }
135
139
  const createParams = {