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.
- package/.env.production +1 -1
- package/.github/copilot-instructions.md +45 -0
- package/.github/workflows/publish-mcp.yml +3 -2
- package/README.md +21 -5
- package/dist/index.js +124 -31
- package/dist/prompts/index.d.ts +10 -3
- package/dist/prompts/index.js +186 -7
- package/dist/resources/actors.d.ts +19 -1
- package/dist/resources/actors.js +55 -64
- package/dist/resources/assets.js +46 -62
- package/dist/resources/levels.d.ts +21 -3
- package/dist/resources/levels.js +29 -54
- package/dist/tools/actors.d.ts +3 -14
- package/dist/tools/actors.js +246 -302
- package/dist/tools/animation.d.ts +57 -102
- package/dist/tools/animation.js +429 -450
- package/dist/tools/assets.d.ts +13 -2
- package/dist/tools/assets.js +52 -44
- package/dist/tools/audio.d.ts +22 -13
- package/dist/tools/audio.js +467 -121
- package/dist/tools/blueprint.d.ts +32 -13
- package/dist/tools/blueprint.js +699 -448
- package/dist/tools/build_environment_advanced.d.ts +0 -1
- package/dist/tools/build_environment_advanced.js +190 -45
- package/dist/tools/consolidated-tool-definitions.js +78 -252
- package/dist/tools/consolidated-tool-handlers.js +506 -133
- package/dist/tools/debug.d.ts +72 -10
- package/dist/tools/debug.js +167 -31
- package/dist/tools/editor.d.ts +9 -2
- package/dist/tools/editor.js +30 -44
- package/dist/tools/foliage.d.ts +34 -15
- package/dist/tools/foliage.js +97 -107
- package/dist/tools/introspection.js +19 -21
- package/dist/tools/landscape.d.ts +1 -2
- package/dist/tools/landscape.js +311 -168
- package/dist/tools/level.d.ts +3 -28
- package/dist/tools/level.js +642 -192
- package/dist/tools/lighting.d.ts +14 -3
- package/dist/tools/lighting.js +236 -123
- package/dist/tools/materials.d.ts +25 -7
- package/dist/tools/materials.js +102 -79
- package/dist/tools/niagara.d.ts +10 -12
- package/dist/tools/niagara.js +74 -94
- package/dist/tools/performance.d.ts +12 -4
- package/dist/tools/performance.js +38 -79
- package/dist/tools/physics.d.ts +34 -10
- package/dist/tools/physics.js +364 -292
- package/dist/tools/rc.js +97 -23
- package/dist/tools/sequence.d.ts +1 -0
- package/dist/tools/sequence.js +125 -22
- package/dist/tools/ui.d.ts +31 -4
- package/dist/tools/ui.js +83 -66
- package/dist/tools/visual.d.ts +11 -0
- package/dist/tools/visual.js +245 -30
- package/dist/types/tool-types.d.ts +0 -6
- package/dist/types/tool-types.js +1 -8
- package/dist/unreal-bridge.d.ts +32 -2
- package/dist/unreal-bridge.js +621 -127
- package/dist/utils/elicitation.d.ts +57 -0
- package/dist/utils/elicitation.js +104 -0
- package/dist/utils/error-handler.d.ts +0 -33
- package/dist/utils/error-handler.js +4 -111
- package/dist/utils/http.d.ts +2 -22
- package/dist/utils/http.js +12 -75
- package/dist/utils/normalize.d.ts +4 -4
- package/dist/utils/normalize.js +15 -7
- package/dist/utils/python-output.d.ts +18 -0
- package/dist/utils/python-output.js +290 -0
- package/dist/utils/python.d.ts +2 -0
- package/dist/utils/python.js +4 -0
- package/dist/utils/response-validator.js +28 -2
- package/dist/utils/result-helpers.d.ts +27 -0
- package/dist/utils/result-helpers.js +147 -0
- package/dist/utils/safe-json.d.ts +0 -2
- package/dist/utils/safe-json.js +0 -43
- package/dist/utils/validation.d.ts +16 -0
- package/dist/utils/validation.js +70 -7
- package/mcp-config-example.json +2 -2
- package/package.json +10 -9
- package/server.json +37 -14
- package/src/index.ts +130 -33
- package/src/prompts/index.ts +211 -13
- package/src/resources/actors.ts +59 -44
- package/src/resources/assets.ts +48 -51
- package/src/resources/levels.ts +35 -45
- package/src/tools/actors.ts +269 -313
- package/src/tools/animation.ts +556 -539
- package/src/tools/assets.ts +53 -43
- package/src/tools/audio.ts +507 -113
- package/src/tools/blueprint.ts +778 -462
- package/src/tools/build_environment_advanced.ts +266 -64
- package/src/tools/consolidated-tool-definitions.ts +90 -264
- package/src/tools/consolidated-tool-handlers.ts +630 -121
- package/src/tools/debug.ts +176 -33
- package/src/tools/editor.ts +35 -37
- package/src/tools/foliage.ts +110 -104
- package/src/tools/introspection.ts +24 -22
- package/src/tools/landscape.ts +334 -181
- package/src/tools/level.ts +683 -182
- package/src/tools/lighting.ts +244 -123
- package/src/tools/materials.ts +114 -83
- package/src/tools/niagara.ts +87 -81
- package/src/tools/performance.ts +49 -88
- package/src/tools/physics.ts +393 -299
- package/src/tools/rc.ts +102 -24
- package/src/tools/sequence.ts +136 -28
- package/src/tools/ui.ts +101 -70
- package/src/tools/visual.ts +250 -29
- package/src/types/tool-types.ts +0 -9
- package/src/unreal-bridge.ts +658 -140
- package/src/utils/elicitation.ts +129 -0
- package/src/utils/error-handler.ts +4 -159
- package/src/utils/http.ts +16 -115
- package/src/utils/normalize.ts +20 -10
- package/src/utils/python-output.ts +351 -0
- package/src/utils/python.ts +3 -0
- package/src/utils/response-validator.ts +25 -2
- package/src/utils/result-helpers.ts +193 -0
- package/src/utils/safe-json.ts +0 -50
- package/src/utils/validation.ts +94 -7
- package/tests/run-unreal-tool-tests.mjs +720 -0
- package/tsconfig.json +2 -2
- package/dist/python-utils.d.ts +0 -29
- package/dist/python-utils.js +0 -54
- package/dist/types/index.d.ts +0 -323
- package/dist/types/index.js +0 -28
- package/dist/utils/cache-manager.d.ts +0 -64
- package/dist/utils/cache-manager.js +0 -176
- package/dist/utils/errors.d.ts +0 -133
- package/dist/utils/errors.js +0 -256
- package/src/python/editor_compat.py +0 -181
- package/src/python-utils.ts +0 -57
- package/src/types/index.ts +0 -414
- package/src/utils/cache-manager.ts +0 -213
- package/src/utils/errors.ts +0 -312
|
@@ -2,27 +2,112 @@
|
|
|
2
2
|
import { cleanObject } from '../utils/safe-json.js';
|
|
3
3
|
import { Logger } from '../utils/logger.js';
|
|
4
4
|
const log = new Logger('ConsolidatedToolHandler');
|
|
5
|
+
const ACTION_REQUIRED_ERROR = 'Missing required parameter: action';
|
|
6
|
+
function ensureArgsPresent(args) {
|
|
7
|
+
if (args === null || args === undefined) {
|
|
8
|
+
throw new Error('Invalid arguments: null or undefined');
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function requireAction(args) {
|
|
12
|
+
ensureArgsPresent(args);
|
|
13
|
+
const action = args.action;
|
|
14
|
+
if (typeof action !== 'string' || action.trim() === '') {
|
|
15
|
+
throw new Error(ACTION_REQUIRED_ERROR);
|
|
16
|
+
}
|
|
17
|
+
return action;
|
|
18
|
+
}
|
|
19
|
+
function requireNonEmptyString(value, field, message) {
|
|
20
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
21
|
+
throw new Error(message ?? `Invalid ${field}: must be a non-empty string`);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function requirePositiveNumber(value, field, message) {
|
|
26
|
+
if (typeof value !== 'number' || !isFinite(value) || value <= 0) {
|
|
27
|
+
throw new Error(message ?? `Invalid ${field}: must be a positive number`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function requireVector3Components(vector, message) {
|
|
32
|
+
if (!vector ||
|
|
33
|
+
typeof vector.x !== 'number' ||
|
|
34
|
+
typeof vector.y !== 'number' ||
|
|
35
|
+
typeof vector.z !== 'number') {
|
|
36
|
+
throw new Error(message);
|
|
37
|
+
}
|
|
38
|
+
return [vector.x, vector.y, vector.z];
|
|
39
|
+
}
|
|
40
|
+
function getElicitationTimeoutMs(tools) {
|
|
41
|
+
if (!tools)
|
|
42
|
+
return undefined;
|
|
43
|
+
const direct = tools.elicitationTimeoutMs;
|
|
44
|
+
if (typeof direct === 'number' && Number.isFinite(direct)) {
|
|
45
|
+
return direct;
|
|
46
|
+
}
|
|
47
|
+
if (typeof tools.getElicitationTimeoutMs === 'function') {
|
|
48
|
+
const value = tools.getElicitationTimeoutMs();
|
|
49
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
async function elicitMissingPrimitiveArgs(tools, args, prompt, fieldSchemas) {
|
|
56
|
+
if (!tools ||
|
|
57
|
+
typeof tools.supportsElicitation !== 'function' ||
|
|
58
|
+
!tools.supportsElicitation() ||
|
|
59
|
+
typeof tools.elicit !== 'function') {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const properties = {};
|
|
63
|
+
const required = [];
|
|
64
|
+
for (const [key, schema] of Object.entries(fieldSchemas)) {
|
|
65
|
+
const value = args?.[key];
|
|
66
|
+
const missing = value === undefined ||
|
|
67
|
+
value === null ||
|
|
68
|
+
(typeof value === 'string' && value.trim() === '');
|
|
69
|
+
if (missing) {
|
|
70
|
+
properties[key] = schema;
|
|
71
|
+
required.push(key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (required.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
const timeoutMs = getElicitationTimeoutMs(tools);
|
|
77
|
+
const options = {
|
|
78
|
+
fallback: async () => ({ ok: false, error: 'missing-params' })
|
|
79
|
+
};
|
|
80
|
+
if (typeof timeoutMs === 'number') {
|
|
81
|
+
options.timeoutMs = timeoutMs;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const elicited = await tools.elicit(prompt, { type: 'object', properties, required }, options);
|
|
85
|
+
if (elicited?.ok && elicited.value) {
|
|
86
|
+
for (const key of required) {
|
|
87
|
+
const value = elicited.value[key];
|
|
88
|
+
if (value === undefined || value === null)
|
|
89
|
+
continue;
|
|
90
|
+
args[key] = typeof value === 'string' ? value.trim() : value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
log.debug('Special elicitation fallback skipped', {
|
|
96
|
+
prompt,
|
|
97
|
+
err: err?.message || String(err)
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
5
101
|
export async function handleConsolidatedToolCall(name, args, tools) {
|
|
6
102
|
const startTime = Date.now();
|
|
7
103
|
// Use scoped logger (stderr) to avoid polluting stdout JSON
|
|
8
104
|
log.debug(`Starting execution of ${name} at ${new Date().toISOString()}`);
|
|
9
105
|
try {
|
|
10
|
-
|
|
11
|
-
if (args === null || args === undefined) {
|
|
12
|
-
throw new Error('Invalid arguments: null or undefined');
|
|
13
|
-
}
|
|
106
|
+
ensureArgsPresent(args);
|
|
14
107
|
switch (name) {
|
|
15
108
|
// 1. ASSET MANAGER
|
|
16
109
|
case 'manage_asset':
|
|
17
|
-
|
|
18
|
-
if (args === null || args === undefined) {
|
|
19
|
-
throw new Error('Invalid arguments: null or undefined');
|
|
20
|
-
}
|
|
21
|
-
// Validate action exists
|
|
22
|
-
if (!args.action) {
|
|
23
|
-
throw new Error('Missing required parameter: action');
|
|
24
|
-
}
|
|
25
|
-
switch (args.action) {
|
|
110
|
+
switch (requireAction(args)) {
|
|
26
111
|
case 'list': {
|
|
27
112
|
if (args.directory !== undefined && args.directory !== null && typeof args.directory !== 'string') {
|
|
28
113
|
throw new Error('Invalid directory: must be a string');
|
|
@@ -31,20 +116,66 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
31
116
|
return cleanObject({ success: true, ...res });
|
|
32
117
|
}
|
|
33
118
|
case 'import': {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
119
|
+
let sourcePath = typeof args.sourcePath === 'string' ? args.sourcePath.trim() : '';
|
|
120
|
+
let destinationPath = typeof args.destinationPath === 'string' ? args.destinationPath.trim() : '';
|
|
121
|
+
if ((!sourcePath || !destinationPath) && typeof tools.supportsElicitation === 'function' && tools.supportsElicitation() && typeof tools.elicit === 'function') {
|
|
122
|
+
const schemaProps = {};
|
|
123
|
+
const required = [];
|
|
124
|
+
if (!sourcePath) {
|
|
125
|
+
schemaProps.sourcePath = {
|
|
126
|
+
type: 'string',
|
|
127
|
+
title: 'Source File Path',
|
|
128
|
+
description: 'Full path to the asset file on disk to import'
|
|
129
|
+
};
|
|
130
|
+
required.push('sourcePath');
|
|
131
|
+
}
|
|
132
|
+
if (!destinationPath) {
|
|
133
|
+
schemaProps.destinationPath = {
|
|
134
|
+
type: 'string',
|
|
135
|
+
title: 'Destination Path',
|
|
136
|
+
description: 'Unreal content path where the asset should be imported (e.g., /Game/MCP/Assets)'
|
|
137
|
+
};
|
|
138
|
+
required.push('destinationPath');
|
|
139
|
+
}
|
|
140
|
+
if (required.length > 0) {
|
|
141
|
+
const timeoutMs = getElicitationTimeoutMs(tools);
|
|
142
|
+
const options = { fallback: async () => ({ ok: false, error: 'missing-import-params' }) };
|
|
143
|
+
if (typeof timeoutMs === 'number') {
|
|
144
|
+
options.timeoutMs = timeoutMs;
|
|
145
|
+
}
|
|
146
|
+
const elicited = await tools.elicit('Provide the missing import parameters for manage_asset.import', { type: 'object', properties: schemaProps, required }, options);
|
|
147
|
+
if (elicited?.ok && elicited.value) {
|
|
148
|
+
if (typeof elicited.value.sourcePath === 'string') {
|
|
149
|
+
sourcePath = elicited.value.sourcePath.trim();
|
|
150
|
+
}
|
|
151
|
+
if (typeof elicited.value.destinationPath === 'string') {
|
|
152
|
+
destinationPath = elicited.value.destinationPath.trim();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
39
156
|
}
|
|
40
|
-
const
|
|
157
|
+
const sourcePathValidated = requireNonEmptyString(sourcePath || args.sourcePath, 'sourcePath', 'Invalid sourcePath');
|
|
158
|
+
const destinationPathValidated = requireNonEmptyString(destinationPath || args.destinationPath, 'destinationPath', 'Invalid destinationPath');
|
|
159
|
+
const res = await tools.assetTools.importAsset(sourcePathValidated, destinationPathValidated);
|
|
41
160
|
return cleanObject(res);
|
|
42
161
|
}
|
|
43
162
|
case 'create_material': {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
163
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the material details for manage_asset.create_material', {
|
|
164
|
+
name: {
|
|
165
|
+
type: 'string',
|
|
166
|
+
title: 'Material Name',
|
|
167
|
+
description: 'Name for the new material asset'
|
|
168
|
+
},
|
|
169
|
+
path: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
title: 'Save Path',
|
|
172
|
+
description: 'Optional Unreal content path where the material should be saved'
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
const sanitizedName = typeof args.name === 'string' ? args.name.trim() : args.name;
|
|
176
|
+
const sanitizedPath = typeof args.path === 'string' ? args.path.trim() : args.path;
|
|
177
|
+
const name = requireNonEmptyString(sanitizedName, 'name', 'Invalid name: must be a non-empty string');
|
|
178
|
+
const res = await tools.materialTools.createMaterial(name, sanitizedPath || '/Game/Materials');
|
|
48
179
|
return cleanObject(res);
|
|
49
180
|
}
|
|
50
181
|
default:
|
|
@@ -52,40 +183,57 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
52
183
|
}
|
|
53
184
|
// 2. ACTOR CONTROL
|
|
54
185
|
case 'control_actor':
|
|
55
|
-
|
|
56
|
-
if (!args.action) {
|
|
57
|
-
throw new Error('Missing required parameter: action');
|
|
58
|
-
}
|
|
59
|
-
switch (args.action) {
|
|
186
|
+
switch (requireAction(args)) {
|
|
60
187
|
case 'spawn': {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
188
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the spawn parameters for control_actor.spawn', {
|
|
189
|
+
classPath: {
|
|
190
|
+
type: 'string',
|
|
191
|
+
title: 'Actor Class or Asset Path',
|
|
192
|
+
description: 'Class name (e.g., StaticMeshActor) or asset path (e.g., /Engine/BasicShapes/Cube) to spawn'
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
const classPathInput = typeof args.classPath === 'string' ? args.classPath.trim() : args.classPath;
|
|
196
|
+
const classPath = requireNonEmptyString(classPathInput, 'classPath', 'Invalid classPath: must be a non-empty string');
|
|
197
|
+
const actorNameInput = typeof args.actorName === 'string' && args.actorName.trim() !== ''
|
|
198
|
+
? args.actorName
|
|
199
|
+
: (typeof args.name === 'string' ? args.name : undefined);
|
|
64
200
|
const res = await tools.actorTools.spawn({
|
|
65
|
-
classPath
|
|
201
|
+
classPath,
|
|
66
202
|
location: args.location,
|
|
67
|
-
rotation: args.rotation
|
|
203
|
+
rotation: args.rotation,
|
|
204
|
+
actorName: actorNameInput
|
|
68
205
|
});
|
|
69
206
|
return cleanObject(res);
|
|
70
207
|
}
|
|
71
208
|
case 'delete': {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
209
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Which actor should control_actor.delete remove?', {
|
|
210
|
+
actorName: {
|
|
211
|
+
type: 'string',
|
|
212
|
+
title: 'Actor Name',
|
|
213
|
+
description: 'Exact label of the actor to delete'
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
const actorNameArg = typeof args.actorName === 'string' && args.actorName.trim() !== ''
|
|
217
|
+
? args.actorName
|
|
218
|
+
: (typeof args.name === 'string' ? args.name : undefined);
|
|
219
|
+
const actorName = requireNonEmptyString(actorNameArg, 'actorName', 'Invalid actorName');
|
|
220
|
+
const res = await tools.bridge.executeEditorFunction('DELETE_ACTOR', { actor_name: actorName });
|
|
76
221
|
return cleanObject(res);
|
|
77
222
|
}
|
|
78
223
|
case 'apply_force': {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
224
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the target actor for control_actor.apply_force', {
|
|
225
|
+
actorName: {
|
|
226
|
+
type: 'string',
|
|
227
|
+
title: 'Actor Name',
|
|
228
|
+
description: 'Physics-enabled actor that should receive the force'
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
const actorName = requireNonEmptyString(args.actorName, 'actorName', 'Invalid actorName');
|
|
232
|
+
const vector = requireVector3Components(args.force, 'Invalid force: must have numeric x,y,z');
|
|
85
233
|
const res = await tools.physicsTools.applyForce({
|
|
86
|
-
actorName
|
|
234
|
+
actorName,
|
|
87
235
|
forceType: 'Force',
|
|
88
|
-
vector
|
|
236
|
+
vector
|
|
89
237
|
});
|
|
90
238
|
return cleanObject(res);
|
|
91
239
|
}
|
|
@@ -94,11 +242,7 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
94
242
|
}
|
|
95
243
|
// 3. EDITOR CONTROL
|
|
96
244
|
case 'control_editor':
|
|
97
|
-
|
|
98
|
-
if (!args.action) {
|
|
99
|
-
throw new Error('Missing required parameter: action');
|
|
100
|
-
}
|
|
101
|
-
switch (args.action) {
|
|
245
|
+
switch (requireAction(args)) {
|
|
102
246
|
case 'play': {
|
|
103
247
|
const res = await tools.editorTools.playInEditor();
|
|
104
248
|
return cleanObject(res);
|
|
@@ -112,11 +256,9 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
112
256
|
return cleanObject(res);
|
|
113
257
|
}
|
|
114
258
|
case 'set_game_speed': {
|
|
115
|
-
|
|
116
|
-
throw new Error('Invalid speed: must be a positive number');
|
|
117
|
-
}
|
|
259
|
+
const speed = requirePositiveNumber(args.speed, 'speed', 'Invalid speed: must be a positive number');
|
|
118
260
|
// Use console command via bridge
|
|
119
|
-
const res = await tools.bridge.executeConsoleCommand(`slomo ${
|
|
261
|
+
const res = await tools.bridge.executeConsoleCommand(`slomo ${speed}`);
|
|
120
262
|
return cleanObject(res);
|
|
121
263
|
}
|
|
122
264
|
case 'eject': {
|
|
@@ -132,9 +274,15 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
132
274
|
return cleanObject(res);
|
|
133
275
|
}
|
|
134
276
|
case 'set_view_mode': {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
277
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the view mode for control_editor.set_view_mode', {
|
|
278
|
+
viewMode: {
|
|
279
|
+
type: 'string',
|
|
280
|
+
title: 'View Mode',
|
|
281
|
+
description: 'Viewport view mode (e.g., Lit, Unlit, Wireframe)'
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
const viewMode = requireNonEmptyString(args.viewMode, 'viewMode', 'Missing required parameter: viewMode');
|
|
285
|
+
const res = await tools.bridge.setSafeViewMode(viewMode);
|
|
138
286
|
return cleanObject(res);
|
|
139
287
|
}
|
|
140
288
|
default:
|
|
@@ -142,13 +290,17 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
142
290
|
}
|
|
143
291
|
// 4. LEVEL MANAGER
|
|
144
292
|
case 'manage_level':
|
|
145
|
-
|
|
146
|
-
throw new Error('Missing required parameter: action');
|
|
147
|
-
switch (args.action) {
|
|
293
|
+
switch (requireAction(args)) {
|
|
148
294
|
case 'load': {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
295
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Select the level to load for manage_level.load', {
|
|
296
|
+
levelPath: {
|
|
297
|
+
type: 'string',
|
|
298
|
+
title: 'Level Path',
|
|
299
|
+
description: 'Content path of the level asset to load (e.g., /Game/Maps/MyLevel)'
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
const levelPath = requireNonEmptyString(args.levelPath, 'levelPath', 'Missing required parameter: levelPath');
|
|
303
|
+
const res = await tools.levelTools.loadLevel({ levelPath, streaming: !!args.streaming });
|
|
152
304
|
return cleanObject(res);
|
|
153
305
|
}
|
|
154
306
|
case 'save': {
|
|
@@ -156,26 +308,124 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
156
308
|
return cleanObject(res);
|
|
157
309
|
}
|
|
158
310
|
case 'stream': {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
311
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the streaming level name for manage_level.stream', {
|
|
312
|
+
levelName: {
|
|
313
|
+
type: 'string',
|
|
314
|
+
title: 'Level Name',
|
|
315
|
+
description: 'Streaming level name to toggle'
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
const levelName = requireNonEmptyString(args.levelName, 'levelName', 'Missing required parameter: levelName');
|
|
319
|
+
const res = await tools.levelTools.streamLevel({ levelName, shouldBeLoaded: !!args.shouldBeLoaded, shouldBeVisible: !!args.shouldBeVisible });
|
|
162
320
|
return cleanObject(res);
|
|
163
321
|
}
|
|
164
322
|
case 'create_light': {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
323
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the light details for manage_level.create_light', {
|
|
324
|
+
lightType: {
|
|
325
|
+
type: 'string',
|
|
326
|
+
title: 'Light Type',
|
|
327
|
+
description: 'Directional, Point, Spot, Rect, or Sky'
|
|
328
|
+
},
|
|
329
|
+
name: {
|
|
330
|
+
type: 'string',
|
|
331
|
+
title: 'Light Name',
|
|
332
|
+
description: 'Name for the new light actor'
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
const lightType = requireNonEmptyString(args.lightType, 'lightType', 'Missing required parameter: lightType');
|
|
336
|
+
const name = requireNonEmptyString(args.name, 'name', 'Invalid name');
|
|
337
|
+
const typeKey = lightType.toLowerCase();
|
|
338
|
+
const toVector = (value, fallback) => {
|
|
339
|
+
if (Array.isArray(value) && value.length === 3) {
|
|
340
|
+
return [Number(value[0]) || 0, Number(value[1]) || 0, Number(value[2]) || 0];
|
|
341
|
+
}
|
|
342
|
+
if (value && typeof value === 'object') {
|
|
343
|
+
return [Number(value.x) || 0, Number(value.y) || 0, Number(value.z) || 0];
|
|
344
|
+
}
|
|
345
|
+
return fallback;
|
|
346
|
+
};
|
|
347
|
+
const toRotator = (value, fallback) => {
|
|
348
|
+
if (Array.isArray(value) && value.length === 3) {
|
|
349
|
+
return [Number(value[0]) || 0, Number(value[1]) || 0, Number(value[2]) || 0];
|
|
350
|
+
}
|
|
351
|
+
if (value && typeof value === 'object') {
|
|
352
|
+
return [Number(value.pitch) || 0, Number(value.yaw) || 0, Number(value.roll) || 0];
|
|
353
|
+
}
|
|
354
|
+
return fallback;
|
|
355
|
+
};
|
|
356
|
+
const toColor = (value) => {
|
|
357
|
+
if (Array.isArray(value) && value.length === 3) {
|
|
358
|
+
return [Number(value[0]) || 0, Number(value[1]) || 0, Number(value[2]) || 0];
|
|
359
|
+
}
|
|
360
|
+
if (value && typeof value === 'object') {
|
|
361
|
+
return [Number(value.r) || 0, Number(value.g) || 0, Number(value.b) || 0];
|
|
362
|
+
}
|
|
363
|
+
return undefined;
|
|
364
|
+
};
|
|
365
|
+
const location = toVector(args.location, [0, 0, typeKey === 'directional' ? 500 : 0]);
|
|
366
|
+
const rotation = toRotator(args.rotation, [0, 0, 0]);
|
|
367
|
+
const color = toColor(args.color);
|
|
368
|
+
const castShadows = typeof args.castShadows === 'boolean' ? args.castShadows : undefined;
|
|
369
|
+
if (typeKey === 'directional') {
|
|
370
|
+
return cleanObject(await tools.lightingTools.createDirectionalLight({
|
|
371
|
+
name,
|
|
372
|
+
intensity: args.intensity,
|
|
373
|
+
color,
|
|
374
|
+
rotation,
|
|
375
|
+
castShadows,
|
|
376
|
+
temperature: args.temperature
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
if (typeKey === 'point') {
|
|
380
|
+
return cleanObject(await tools.lightingTools.createPointLight({
|
|
381
|
+
name,
|
|
382
|
+
location,
|
|
383
|
+
intensity: args.intensity,
|
|
384
|
+
radius: args.radius,
|
|
385
|
+
color,
|
|
386
|
+
falloffExponent: args.falloffExponent,
|
|
387
|
+
castShadows
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
if (typeKey === 'spot') {
|
|
391
|
+
const innerCone = typeof args.innerCone === 'number' ? args.innerCone : undefined;
|
|
392
|
+
const outerCone = typeof args.outerCone === 'number' ? args.outerCone : undefined;
|
|
393
|
+
if (innerCone !== undefined && outerCone !== undefined && innerCone >= outerCone) {
|
|
394
|
+
throw new Error('innerCone must be less than outerCone');
|
|
395
|
+
}
|
|
396
|
+
return cleanObject(await tools.lightingTools.createSpotLight({
|
|
397
|
+
name,
|
|
398
|
+
location,
|
|
399
|
+
rotation,
|
|
400
|
+
intensity: args.intensity,
|
|
401
|
+
innerCone: args.innerCone,
|
|
402
|
+
outerCone: args.outerCone,
|
|
403
|
+
radius: args.radius,
|
|
404
|
+
color,
|
|
405
|
+
castShadows
|
|
406
|
+
}));
|
|
407
|
+
}
|
|
408
|
+
if (typeKey === 'rect') {
|
|
409
|
+
return cleanObject(await tools.lightingTools.createRectLight({
|
|
410
|
+
name,
|
|
411
|
+
location,
|
|
412
|
+
rotation,
|
|
413
|
+
intensity: args.intensity,
|
|
414
|
+
width: args.width,
|
|
415
|
+
height: args.height,
|
|
416
|
+
color
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
if (typeKey === 'sky' || typeKey === 'skylight') {
|
|
420
|
+
return cleanObject(await tools.lightingTools.createSkyLight({
|
|
421
|
+
name,
|
|
422
|
+
sourceType: args.sourceType,
|
|
423
|
+
cubemapPath: args.cubemapPath,
|
|
424
|
+
intensity: args.intensity,
|
|
425
|
+
recapture: args.recapture
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
throw new Error(`Unknown light type: ${lightType}`);
|
|
179
429
|
}
|
|
180
430
|
case 'build_lighting': {
|
|
181
431
|
const res = await tools.lightingTools.buildLighting({ quality: args.quality || 'High', buildReflectionCaptures: true });
|
|
@@ -186,34 +436,60 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
186
436
|
}
|
|
187
437
|
// 5. ANIMATION & PHYSICS
|
|
188
438
|
case 'animation_physics':
|
|
189
|
-
|
|
190
|
-
if (!args.action) {
|
|
191
|
-
throw new Error('Missing required parameter: action');
|
|
192
|
-
}
|
|
193
|
-
switch (args.action) {
|
|
439
|
+
switch (requireAction(args)) {
|
|
194
440
|
case 'create_animation_bp': {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
441
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide details for animation_physics.create_animation_bp', {
|
|
442
|
+
name: {
|
|
443
|
+
type: 'string',
|
|
444
|
+
title: 'Blueprint Name',
|
|
445
|
+
description: 'Name of the Animation Blueprint to create'
|
|
446
|
+
},
|
|
447
|
+
skeletonPath: {
|
|
448
|
+
type: 'string',
|
|
449
|
+
title: 'Skeleton Path',
|
|
450
|
+
description: 'Content path of the skeleton asset to bind'
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
const name = requireNonEmptyString(args.name, 'name', 'Invalid name');
|
|
454
|
+
const skeletonPath = requireNonEmptyString(args.skeletonPath, 'skeletonPath', 'Invalid skeletonPath');
|
|
455
|
+
const res = await tools.animationTools.createAnimationBlueprint({ name, skeletonPath, savePath: args.savePath });
|
|
200
456
|
return cleanObject(res);
|
|
201
457
|
}
|
|
202
458
|
case 'play_montage': {
|
|
203
|
-
|
|
204
|
-
|
|
459
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide playback details for animation_physics.play_montage', {
|
|
460
|
+
actorName: {
|
|
461
|
+
type: 'string',
|
|
462
|
+
title: 'Actor Name',
|
|
463
|
+
description: 'Actor that should play the montage'
|
|
464
|
+
},
|
|
465
|
+
montagePath: {
|
|
466
|
+
type: 'string',
|
|
467
|
+
title: 'Montage Path',
|
|
468
|
+
description: 'Montage or animation asset path to play'
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
const actorName = requireNonEmptyString(args.actorName, 'actorName', 'Invalid actorName');
|
|
205
472
|
const montagePath = args.montagePath || args.animationPath;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const res = await tools.animationTools.playAnimation({ actorName: args.actorName, animationType: 'Montage', animationPath: montagePath, playRate: args.playRate });
|
|
473
|
+
const validatedMontage = requireNonEmptyString(montagePath, 'montagePath', 'Invalid montagePath');
|
|
474
|
+
const res = await tools.animationTools.playAnimation({ actorName, animationType: 'Montage', animationPath: validatedMontage, playRate: args.playRate });
|
|
209
475
|
return cleanObject(res);
|
|
210
476
|
}
|
|
211
477
|
case 'setup_ragdoll': {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
478
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide setup details for animation_physics.setup_ragdoll', {
|
|
479
|
+
skeletonPath: {
|
|
480
|
+
type: 'string',
|
|
481
|
+
title: 'Skeleton Path',
|
|
482
|
+
description: 'Content path for the skeleton asset'
|
|
483
|
+
},
|
|
484
|
+
physicsAssetName: {
|
|
485
|
+
type: 'string',
|
|
486
|
+
title: 'Physics Asset Name',
|
|
487
|
+
description: 'Name of the physics asset to apply'
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
const skeletonPath = requireNonEmptyString(args.skeletonPath, 'skeletonPath', 'Invalid skeletonPath');
|
|
491
|
+
const physicsAssetName = requireNonEmptyString(args.physicsAssetName, 'physicsAssetName', 'Invalid physicsAssetName');
|
|
492
|
+
const res = await tools.physicsTools.setupRagdoll({ skeletonPath, physicsAssetName, blendWeight: args.blendWeight, savePath: args.savePath });
|
|
217
493
|
return cleanObject(res);
|
|
218
494
|
}
|
|
219
495
|
default:
|
|
@@ -221,21 +497,51 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
221
497
|
}
|
|
222
498
|
// 6. EFFECTS SYSTEM
|
|
223
499
|
case 'create_effect':
|
|
224
|
-
switch (args
|
|
500
|
+
switch (requireAction(args)) {
|
|
225
501
|
case 'particle': {
|
|
502
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the particle effect details for create_effect.particle', {
|
|
503
|
+
effectType: {
|
|
504
|
+
type: 'string',
|
|
505
|
+
title: 'Effect Type',
|
|
506
|
+
description: 'Preset effect type to spawn (e.g., Fire, Smoke)'
|
|
507
|
+
}
|
|
508
|
+
});
|
|
226
509
|
const res = await tools.niagaraTools.createEffect({ effectType: args.effectType, name: args.name, location: args.location, scale: args.scale, customParameters: args.customParameters });
|
|
227
510
|
return cleanObject(res);
|
|
228
511
|
}
|
|
229
512
|
case 'niagara': {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
513
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the Niagara system path for create_effect.niagara', {
|
|
514
|
+
systemPath: {
|
|
515
|
+
type: 'string',
|
|
516
|
+
title: 'Niagara System Path',
|
|
517
|
+
description: 'Asset path of the Niagara system to spawn'
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
const systemPath = requireNonEmptyString(args.systemPath, 'systemPath', 'Invalid systemPath');
|
|
521
|
+
const verifyResult = await tools.bridge.executePythonWithResult(`
|
|
522
|
+
import unreal, json
|
|
523
|
+
path = r"${systemPath}"
|
|
524
|
+
exists = unreal.EditorAssetLibrary.does_asset_exist(path)
|
|
525
|
+
print('RESULT:' + json.dumps({'success': exists, 'exists': exists, 'path': path}))
|
|
526
|
+
`.trim());
|
|
527
|
+
if (!verifyResult?.exists) {
|
|
528
|
+
return cleanObject({ success: false, error: `Niagara system not found at ${systemPath}` });
|
|
529
|
+
}
|
|
530
|
+
const loc = Array.isArray(args.location)
|
|
531
|
+
? { x: args.location[0], y: args.location[1], z: args.location[2] }
|
|
532
|
+
: args.location || { x: 0, y: 0, z: 0 };
|
|
533
|
+
const res = await tools.niagaraTools.spawnEffect({
|
|
534
|
+
systemPath,
|
|
535
|
+
location: [loc.x ?? 0, loc.y ?? 0, loc.z ?? 0],
|
|
536
|
+
rotation: Array.isArray(args.rotation) ? args.rotation : undefined,
|
|
537
|
+
scale: args.scale
|
|
538
|
+
});
|
|
235
539
|
return cleanObject(res);
|
|
236
540
|
}
|
|
237
541
|
case 'debug_shape': {
|
|
238
|
-
const
|
|
542
|
+
const shapeInput = args.shape ?? 'Sphere';
|
|
543
|
+
const shape = String(shapeInput).trim().toLowerCase();
|
|
544
|
+
const originalShapeLabel = String(shapeInput).trim() || 'shape';
|
|
239
545
|
const loc = args.location || { x: 0, y: 0, z: 0 };
|
|
240
546
|
const size = args.size || 100;
|
|
241
547
|
const color = args.color || [255, 0, 0, 255];
|
|
@@ -269,19 +575,53 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
269
575
|
return cleanObject(await tools.debugTools.drawDebugString({ location: [loc.x, loc.y, loc.z], text, color, duration }));
|
|
270
576
|
}
|
|
271
577
|
// Default fallback
|
|
272
|
-
return cleanObject(
|
|
578
|
+
return cleanObject({ success: false, error: `Unsupported debug shape: ${originalShapeLabel}` });
|
|
273
579
|
}
|
|
274
580
|
default:
|
|
275
581
|
throw new Error(`Unknown effect action: ${args.action}`);
|
|
276
582
|
}
|
|
277
583
|
// 7. BLUEPRINT MANAGER
|
|
278
584
|
case 'manage_blueprint':
|
|
279
|
-
switch (args
|
|
585
|
+
switch (requireAction(args)) {
|
|
280
586
|
case 'create': {
|
|
281
|
-
|
|
587
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide details for manage_blueprint.create', {
|
|
588
|
+
name: {
|
|
589
|
+
type: 'string',
|
|
590
|
+
title: 'Blueprint Name',
|
|
591
|
+
description: 'Name for the new Blueprint asset'
|
|
592
|
+
},
|
|
593
|
+
blueprintType: {
|
|
594
|
+
type: 'string',
|
|
595
|
+
title: 'Blueprint Type',
|
|
596
|
+
description: 'Base type such as Actor, Pawn, Character, etc.'
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
const res = await tools.blueprintTools.createBlueprint({
|
|
600
|
+
name: args.name,
|
|
601
|
+
blueprintType: args.blueprintType || 'Actor',
|
|
602
|
+
savePath: args.savePath,
|
|
603
|
+
parentClass: args.parentClass
|
|
604
|
+
});
|
|
282
605
|
return cleanObject(res);
|
|
283
606
|
}
|
|
284
607
|
case 'add_component': {
|
|
608
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide details for manage_blueprint.add_component', {
|
|
609
|
+
name: {
|
|
610
|
+
type: 'string',
|
|
611
|
+
title: 'Blueprint Name',
|
|
612
|
+
description: 'Blueprint asset to modify'
|
|
613
|
+
},
|
|
614
|
+
componentType: {
|
|
615
|
+
type: 'string',
|
|
616
|
+
title: 'Component Type',
|
|
617
|
+
description: 'Component class to add (e.g., StaticMeshComponent)'
|
|
618
|
+
},
|
|
619
|
+
componentName: {
|
|
620
|
+
type: 'string',
|
|
621
|
+
title: 'Component Name',
|
|
622
|
+
description: 'Name for the new component'
|
|
623
|
+
}
|
|
624
|
+
});
|
|
285
625
|
const res = await tools.blueprintTools.addComponent({ blueprintName: args.name, componentType: args.componentType, componentName: args.componentName });
|
|
286
626
|
return cleanObject(res);
|
|
287
627
|
}
|
|
@@ -290,7 +630,7 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
290
630
|
}
|
|
291
631
|
// 8. ENVIRONMENT BUILDER
|
|
292
632
|
case 'build_environment':
|
|
293
|
-
switch (args
|
|
633
|
+
switch (requireAction(args)) {
|
|
294
634
|
case 'create_landscape': {
|
|
295
635
|
const res = await tools.landscapeTools.createLandscape({ name: args.name, sizeX: args.sizeX, sizeY: args.sizeY, materialPath: args.materialPath });
|
|
296
636
|
return cleanObject(res);
|
|
@@ -364,9 +704,7 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
364
704
|
}
|
|
365
705
|
// 9. SYSTEM CONTROL
|
|
366
706
|
case 'system_control':
|
|
367
|
-
|
|
368
|
-
throw new Error('Missing required parameter: action');
|
|
369
|
-
switch (args.action) {
|
|
707
|
+
switch (requireAction(args)) {
|
|
370
708
|
case 'profile': {
|
|
371
709
|
const res = await tools.performanceTools.startProfiling({ type: args.profileType, duration: args.duration });
|
|
372
710
|
return cleanObject(res);
|
|
@@ -380,16 +718,38 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
380
718
|
return cleanObject(res);
|
|
381
719
|
}
|
|
382
720
|
case 'play_sound': {
|
|
721
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide the audio asset for system_control.play_sound', {
|
|
722
|
+
soundPath: {
|
|
723
|
+
type: 'string',
|
|
724
|
+
title: 'Sound Asset Path',
|
|
725
|
+
description: 'Asset path of the sound to play'
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
const soundPath = requireNonEmptyString(args.soundPath, 'soundPath', 'Missing required parameter: soundPath');
|
|
383
729
|
if (args.location && typeof args.location === 'object') {
|
|
384
730
|
const loc = [args.location.x || 0, args.location.y || 0, args.location.z || 0];
|
|
385
|
-
const res = await tools.audioTools.playSoundAtLocation({ soundPath
|
|
731
|
+
const res = await tools.audioTools.playSoundAtLocation({ soundPath, location: loc, volume: args.volume, pitch: args.pitch, startTime: args.startTime });
|
|
386
732
|
return cleanObject(res);
|
|
387
733
|
}
|
|
388
|
-
const res = await tools.audioTools.playSound2D({ soundPath
|
|
734
|
+
const res = await tools.audioTools.playSound2D({ soundPath, volume: args.volume, pitch: args.pitch, startTime: args.startTime });
|
|
389
735
|
return cleanObject(res);
|
|
390
736
|
}
|
|
391
737
|
case 'create_widget': {
|
|
392
|
-
|
|
738
|
+
await elicitMissingPrimitiveArgs(tools, args, 'Provide details for system_control.create_widget', {
|
|
739
|
+
widgetName: {
|
|
740
|
+
type: 'string',
|
|
741
|
+
title: 'Widget Name',
|
|
742
|
+
description: 'Name for the new UI widget asset'
|
|
743
|
+
},
|
|
744
|
+
widgetType: {
|
|
745
|
+
type: 'string',
|
|
746
|
+
title: 'Widget Type',
|
|
747
|
+
description: 'Widget type such as HUD, Menu, Overlay, etc.'
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
const widgetName = requireNonEmptyString(args.widgetName ?? args.name, 'widgetName', 'Missing required parameter: widgetName');
|
|
751
|
+
const widgetType = requireNonEmptyString(args.widgetType, 'widgetType', 'Missing required parameter: widgetType');
|
|
752
|
+
const res = await tools.uiTools.createWidget({ name: widgetName, type: widgetType, savePath: args.savePath });
|
|
393
753
|
return cleanObject(res);
|
|
394
754
|
}
|
|
395
755
|
case 'show_widget': {
|
|
@@ -423,19 +783,32 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
423
783
|
return { success: false, error: 'Command blocked for safety' };
|
|
424
784
|
}
|
|
425
785
|
try {
|
|
426
|
-
const
|
|
427
|
-
|
|
786
|
+
const raw = await tools.bridge.executeConsoleCommand(cmd);
|
|
787
|
+
const summary = tools.bridge.summarizeConsoleCommand(cmd, raw);
|
|
788
|
+
const output = summary.output || '';
|
|
789
|
+
const looksInvalid = /unknown|invalid/i.test(output);
|
|
790
|
+
return cleanObject({
|
|
791
|
+
success: summary.returnValue !== false && !looksInvalid,
|
|
792
|
+
command: summary.command,
|
|
793
|
+
output: output || undefined,
|
|
794
|
+
logLines: summary.logLines?.length ? summary.logLines : undefined,
|
|
795
|
+
returnValue: summary.returnValue,
|
|
796
|
+
message: !looksInvalid
|
|
797
|
+
? (output || 'Command executed')
|
|
798
|
+
: undefined,
|
|
799
|
+
error: looksInvalid ? output : undefined,
|
|
800
|
+
raw: summary.raw
|
|
801
|
+
});
|
|
428
802
|
}
|
|
429
803
|
catch (e) {
|
|
430
804
|
return cleanObject({ success: false, command: cmd, error: e?.message || String(e) });
|
|
431
805
|
}
|
|
432
806
|
// 11. REMOTE CONTROL PRESETS - Direct implementation
|
|
433
807
|
case 'manage_rc':
|
|
434
|
-
if (!args.action)
|
|
435
|
-
throw new Error('Missing required parameter: action');
|
|
436
808
|
// Handle RC operations directly through RcTools
|
|
437
809
|
let rcResult;
|
|
438
|
-
|
|
810
|
+
const rcAction = requireAction(args);
|
|
811
|
+
switch (rcAction) {
|
|
439
812
|
// Support both 'create_preset' and 'create' for compatibility
|
|
440
813
|
case 'create_preset':
|
|
441
814
|
case 'create':
|
|
@@ -461,9 +834,11 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
461
834
|
rcResult = await tools.rcTools.listPresets();
|
|
462
835
|
break;
|
|
463
836
|
case 'delete':
|
|
464
|
-
|
|
837
|
+
case 'delete_preset':
|
|
838
|
+
const presetIdentifier = args.presetId || args.presetPath;
|
|
839
|
+
if (!presetIdentifier)
|
|
465
840
|
throw new Error('Missing required parameter: presetId');
|
|
466
|
-
rcResult = await tools.rcTools.deletePreset(
|
|
841
|
+
rcResult = await tools.rcTools.deletePreset(presetIdentifier);
|
|
467
842
|
if (rcResult.success) {
|
|
468
843
|
rcResult.message = 'Preset deleted successfully';
|
|
469
844
|
}
|
|
@@ -558,21 +933,20 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
558
933
|
};
|
|
559
934
|
break;
|
|
560
935
|
default:
|
|
561
|
-
throw new Error(`Unknown RC action: ${
|
|
936
|
+
throw new Error(`Unknown RC action: ${rcAction}. Valid actions are: create_preset, expose_actor, expose_property, list_fields, set_property, get_property, or their simplified versions: create, list, delete, expose, get_exposed, set_value, get_value, call_function`);
|
|
562
937
|
}
|
|
563
938
|
// Return result directly - MCP formatting will be handled by response validator
|
|
564
939
|
// Clean to prevent circular references
|
|
565
940
|
return cleanObject(rcResult);
|
|
566
941
|
// 12. SEQUENCER / CINEMATICS
|
|
567
942
|
case 'manage_sequence':
|
|
568
|
-
if (!args.action)
|
|
569
|
-
throw new Error('Missing required parameter: action');
|
|
570
943
|
// Direct handling for sequence operations
|
|
571
944
|
const seqResult = await (async () => {
|
|
572
945
|
const sequenceTools = tools.sequenceTools;
|
|
573
946
|
if (!sequenceTools)
|
|
574
947
|
throw new Error('Sequence tools not available');
|
|
575
|
-
|
|
948
|
+
const action = requireAction(args);
|
|
949
|
+
switch (action) {
|
|
576
950
|
case 'create':
|
|
577
951
|
return await sequenceTools.create({ name: args.name, path: args.path });
|
|
578
952
|
case 'open':
|
|
@@ -616,7 +990,7 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
616
990
|
throw new Error('Missing required parameter: speed');
|
|
617
991
|
return await sequenceTools.setPlaybackSpeed({ speed: args.speed });
|
|
618
992
|
default:
|
|
619
|
-
throw new Error(`Unknown sequence action: ${
|
|
993
|
+
throw new Error(`Unknown sequence action: ${action}`);
|
|
620
994
|
}
|
|
621
995
|
})();
|
|
622
996
|
// Return result directly - MCP formatting will be handled by response validator
|
|
@@ -624,9 +998,8 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
624
998
|
return cleanObject(seqResult);
|
|
625
999
|
// 13. INTROSPECTION
|
|
626
1000
|
case 'inspect':
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
switch (args.action) {
|
|
1001
|
+
const inspectAction = requireAction(args);
|
|
1002
|
+
switch (inspectAction) {
|
|
630
1003
|
case 'inspect_object': {
|
|
631
1004
|
const res = await tools.introspectionTools.inspectObject({ objectPath: args.objectPath, detailed: args.detailed });
|
|
632
1005
|
return cleanObject(res);
|
|
@@ -636,7 +1009,7 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
636
1009
|
return cleanObject(res);
|
|
637
1010
|
}
|
|
638
1011
|
default:
|
|
639
|
-
throw new Error(`Unknown inspect action: ${
|
|
1012
|
+
throw new Error(`Unknown inspect action: ${inspectAction}`);
|
|
640
1013
|
}
|
|
641
1014
|
default:
|
|
642
1015
|
throw new Error(`Unknown consolidated tool: ${name}`);
|