ultimate-unreal-engine-mcp 0.1.22 → 0.1.24
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/README.md +43 -3
- package/dist/index.js +63 -0
- package/dist/tools/editor/index.js +100 -10
- package/dist/tools/viewport/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Ultimate Unreal Engine MCP
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**133 tools. 26 domains. Full read/write access to Unreal Engine 5.7 — including live editor APIs, Blueprint graphs, and viewport screenshots.**
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
[](https://www.unrealengine.com/)
|
|
8
8
|
[](#development)
|
|
9
|
-
[](#tool-catalog)
|
|
10
10
|
|
|
11
11
|
MCP (Model Context Protocol) is a standard that lets AI assistants call tools on external systems — this server exposes Unreal Engine as those tools.
|
|
12
12
|
|
|
@@ -23,6 +23,7 @@ npx ultimate-unreal-engine-mcp setup
|
|
|
23
23
|
- [Architecture](#architecture)
|
|
24
24
|
- [Tool Catalog](#tool-catalog)
|
|
25
25
|
- [Visual Review Loop](#visual-review-loop)
|
|
26
|
+
- [Automatic Self-Verification](#automatic-self-verification)
|
|
26
27
|
- [Headless vs Live](#headless-vs-live)
|
|
27
28
|
- [Installation](#installation)
|
|
28
29
|
- [C++ Plugin Setup](#c-plugin-setup)
|
|
@@ -51,7 +52,7 @@ npx ultimate-unreal-engine-mcp setup --project "C:/MyGame"
|
|
|
51
52
|
|
|
52
53
|
This command writes the correct config for your client. The C++ plugin is optional — file-system tools (C++ parsing, code gen, configs, builds, docs) work without it. Install the plugin for live editor tools (Blueprints, actors, viewport, screenshots).
|
|
53
54
|
|
|
54
|
-
> **Zero optional dependencies.** The C++ plugin compiles on any UE 5.7 install — binary or source. All
|
|
55
|
+
> **Zero optional dependencies.** The C++ plugin compiles on any UE 5.7 install — binary or source. All 133 tools use runtime reflection for optional UE modules (MetaSound, LiveLink, GAS, etc.), so the plugin loads regardless of which plugins your project has enabled. Handlers gracefully report when their target module isn't available.
|
|
55
56
|
|
|
56
57
|
---
|
|
57
58
|
|
|
@@ -543,6 +544,45 @@ ue_cleanup_screenshots(confirm=true)
|
|
|
543
544
|
|
|
544
545
|
---
|
|
545
546
|
|
|
547
|
+
## Automatic Self-Verification
|
|
548
|
+
|
|
549
|
+
Verification isn't a suggestion. It's **server-side enforced**. Every mutating tool automatically queries `actor.componentBounds` after the mutation completes and appends the verification data to the response. The AI sees bounds, positions, and component state in the same tool call result, whether it asked for it or not. If something is wrong, the data is right there and impossible to miss.
|
|
550
|
+
|
|
551
|
+
**Why this matters:** In spatial work (placing doors, aligning meshes, wiring components), a single transform can have cascading effects — rotating a mesh around a pivot shifts its world position, negative scale flips face normals making geometry invisible, collision volumes don't automatically follow mesh offsets. Without verification, the AI reports "done" while the door is floating 2 meters from the wall.
|
|
552
|
+
|
|
553
|
+
**How it works:**
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
ue_spawn_actor("BP_Door", location={x:1040, y:150, z:0})
|
|
557
|
+
→ {actor_label: "BP_Door", _verification: {bounds: {min: {x:806}, max: {x:994}}}}
|
|
558
|
+
← bounds auto-appended by server — in the doorway ✓
|
|
559
|
+
|
|
560
|
+
ue_set_actor_property("BP_Door", "DoorMesh.RelativeRotation", ...)
|
|
561
|
+
→ {success: true, _verification: {bounds: {min: {x:1040}, max: {x:1228}}}}
|
|
562
|
+
← bounds shifted! rotation moved mesh out of gap ✗
|
|
563
|
+
|
|
564
|
+
ue_set_actor_property("BP_Door", "DoorMesh.RelativeLocation", ...)
|
|
565
|
+
→ {success: true, _verification: {bounds: {min: {x:806}, max: {x:994}}}}
|
|
566
|
+
← back in place after offset compensation ✓
|
|
567
|
+
|
|
568
|
+
ue_look_at("BP_Door", angle="front")
|
|
569
|
+
→ [screenshot inline] door visible, handle correct side ✓
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**The Inspect-Reason-Fix cycle:**
|
|
573
|
+
|
|
574
|
+
| Step | What happens | Tool |
|
|
575
|
+
|------|-------------|------|
|
|
576
|
+
| **Act** | Spawn, transform, or set property | `ue_spawn_actor`, `ue_transform_actor`, `ue_set_actor_property` |
|
|
577
|
+
| **Measure** | Get exact numerical positions | `ue_get_component_bounds` |
|
|
578
|
+
| **Reason** | Do bounds match expectations? | (AI reasoning) |
|
|
579
|
+
| **Fix** | Adjust offsets to compensate | Same mutation tools |
|
|
580
|
+
| **Verify** | Confirm fix worked, visually and numerically | `ue_get_component_bounds` + `ue_look_at` |
|
|
581
|
+
|
|
582
|
+
The AI also has access to a `ue://best-practices/self-verification` MCP resource that documents verification patterns, common spatial pitfalls, and the full verification matrix.
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
546
586
|
## Headless vs Live
|
|
547
587
|
|
|
548
588
|
Some tools work without the UE Editor running. Others require the MCPBridge plugin connected to a live editor instance.
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,69 @@ import { registerNetworkingTools } from './tools/networking/index.js';
|
|
|
77
77
|
registerMotionDesignTools(server); // Motion Design tools (MD-01..04)
|
|
78
78
|
registerMovieRenderTools(server); // Movie Render Pipeline tools (MRP-01..04)
|
|
79
79
|
registerNetworkingTools(server); // Networking & Replication tools (NET-01..04)
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Register best-practices resource: self-verification guide
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
server.registerResource('ue_best_practices', 'ue://best-practices/self-verification', {
|
|
84
|
+
title: 'UE MCP Best Practices: Self-Verification',
|
|
85
|
+
description: 'Guidelines for AI assistants using UE MCP tools. Covers mandatory self-verification after mutations, spatial reasoning patterns, and the inspect-reason-fix cycle.',
|
|
86
|
+
mimeType: 'text/markdown',
|
|
87
|
+
}, async (_uri) => ({
|
|
88
|
+
contents: [{
|
|
89
|
+
uri: 'ue://best-practices/self-verification',
|
|
90
|
+
mimeType: 'text/markdown',
|
|
91
|
+
text: `# UE MCP Best Practices: Self-Verification
|
|
92
|
+
|
|
93
|
+
## Core Principle
|
|
94
|
+
|
|
95
|
+
**Always verify your own work before reporting success to the user.**
|
|
96
|
+
|
|
97
|
+
Every mutation (spawn, transform, property set, asset creation) must be followed by a verification step. Never ask the user to "check if that looks right" when you have tools to check it yourself.
|
|
98
|
+
|
|
99
|
+
## Verification Matrix
|
|
100
|
+
|
|
101
|
+
| After this action... | Verify with... |
|
|
102
|
+
|---|---|
|
|
103
|
+
| \`ue_spawn_actor\` | \`ue_get_component_bounds\` to confirm position, then \`ue_look_at\` to visually confirm |
|
|
104
|
+
| \`ue_transform_actor\` | \`ue_get_component_bounds\` — rotations can shift meshes relative to pivots |
|
|
105
|
+
| \`ue_set_actor_property\` (spatial) | \`ue_get_component_bounds\` for position/scale, \`ue_visual_review\` for visual properties |
|
|
106
|
+
| \`ue_set_actor_property\` (material/mesh) | \`ue_look_at\` or \`ue_visual_review\` to confirm visual result |
|
|
107
|
+
| \`ue_delete_actor\` | \`ue_list_actors\` to confirm removal |
|
|
108
|
+
| \`ue_create_data_asset\` / \`ue_create_curve\` | \`ue_query_assets\` to confirm creation |
|
|
109
|
+
| Any visual change | \`ue_visual_review\` or \`ue_look_at\` to see the result |
|
|
110
|
+
|
|
111
|
+
## The Inspect-Reason-Fix Cycle
|
|
112
|
+
|
|
113
|
+
When working with spatial placement (actors, components, doors, furniture):
|
|
114
|
+
|
|
115
|
+
1. **Act** — spawn, transform, or set property
|
|
116
|
+
2. **Measure** — \`ue_get_component_bounds\` to get exact numerical positions
|
|
117
|
+
3. **Reason** — do the bounds match expectations? Are components where they should be?
|
|
118
|
+
4. **Fix** — if not, adjust offsets/rotations to compensate
|
|
119
|
+
5. **Verify** — re-measure to confirm the fix worked
|
|
120
|
+
|
|
121
|
+
Repeat until all components are correctly positioned. Only THEN report success to the user.
|
|
122
|
+
|
|
123
|
+
## Common Spatial Pitfalls
|
|
124
|
+
|
|
125
|
+
- **Negative scale flips normals** — use 180-degree rotation instead of -1 scale to flip meshes
|
|
126
|
+
- **Rotations shift mesh position** — rotating around a pivot moves the mesh; always check bounds after rotation
|
|
127
|
+
- **Component offsets are relative** — a rotated parent changes where child offsets end up in world space
|
|
128
|
+
- **Collision doesn't auto-follow mesh** — if you move/rotate a mesh component, the collision component may need separate adjustment
|
|
129
|
+
|
|
130
|
+
## Visual Verification
|
|
131
|
+
|
|
132
|
+
For any change that affects what the user will SEE in the viewport:
|
|
133
|
+
1. Use \`ue_look_at\` to navigate the camera to the changed actor
|
|
134
|
+
2. Inspect the screenshot — check for: missing meshes, wrong orientation, clipping, z-fighting, scale issues
|
|
135
|
+
3. If something looks wrong, fix it BEFORE telling the user
|
|
136
|
+
|
|
137
|
+
## Cleanup
|
|
138
|
+
|
|
139
|
+
When a visual review session is complete, call \`ue_cleanup_screenshots(confirm=true)\` to free disk space.
|
|
140
|
+
`,
|
|
141
|
+
}],
|
|
142
|
+
}));
|
|
80
143
|
const transport = new StdioServerTransport();
|
|
81
144
|
await server.connect(transport);
|
|
82
145
|
log('Server ready — connected via stdio');
|
|
@@ -43,6 +43,39 @@ async function sendOrDisconnect(bridge, cmd) {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
// ---------------------------------------------------------------------------
|
|
46
|
+
// autoVerifyBounds helper
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
/**
|
|
49
|
+
* Queries actor.componentBounds after a mutation and appends verification
|
|
50
|
+
* data to the original result. If the verification call fails, the original
|
|
51
|
+
* result is returned unmodified (verification is best-effort).
|
|
52
|
+
*/
|
|
53
|
+
async function autoVerifyBounds(bridge, actorLabel, originalResult) {
|
|
54
|
+
// Don't verify if the original command failed
|
|
55
|
+
if (originalResult.isError)
|
|
56
|
+
return originalResult;
|
|
57
|
+
try {
|
|
58
|
+
const verifyResponse = await bridge.sendCommand({
|
|
59
|
+
type: 'actor.componentBounds',
|
|
60
|
+
payload: { actor_label: actorLabel },
|
|
61
|
+
correlationId: '',
|
|
62
|
+
});
|
|
63
|
+
const verifyData = verifyResponse.success
|
|
64
|
+
? { _verification: { actor_label: actorLabel, bounds: verifyResponse.data } }
|
|
65
|
+
: { _verification: { actor_label: actorLabel, error: verifyResponse.error } };
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
...originalResult.content,
|
|
69
|
+
{ type: 'text', text: JSON.stringify(verifyData) },
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Verification is best-effort; don't break the original result
|
|
75
|
+
return originalResult;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
46
79
|
// registerEditorTools
|
|
47
80
|
// ---------------------------------------------------------------------------
|
|
48
81
|
/**
|
|
@@ -85,7 +118,7 @@ export function registerEditorTools(server, bridge) {
|
|
|
85
118
|
// --------------------------------------------------------------------------
|
|
86
119
|
server.registerTool('ue_spawn_actor', {
|
|
87
120
|
title: 'Spawn UE Actor',
|
|
88
|
-
description: '[requires_plugin] Spawn a new actor in the currently open UE Editor level at the specified location.',
|
|
121
|
+
description: '[requires_plugin] Spawn a new actor in the currently open UE Editor level at the specified location. After spawning, ALWAYS verify placement with ue_get_component_bounds or ue_look_at before reporting success to the user.',
|
|
89
122
|
inputSchema: z.object({
|
|
90
123
|
class_name: z.string().describe('The actor class to spawn (e.g., AStaticMeshActor)'),
|
|
91
124
|
location: z.object({
|
|
@@ -104,17 +137,29 @@ export function registerEditorTools(server, bridge) {
|
|
|
104
137
|
if (args.label) {
|
|
105
138
|
payload['label'] = args.label;
|
|
106
139
|
}
|
|
107
|
-
|
|
140
|
+
const result = await sendOrDisconnect(_bridge, {
|
|
108
141
|
type: 'actor.spawn',
|
|
109
142
|
payload,
|
|
110
143
|
});
|
|
144
|
+
// Auto-verify: query bounds of the spawned actor
|
|
145
|
+
const label = args.label ?? (result.content?.[0] && 'text' in result.content[0]
|
|
146
|
+
? (() => { try {
|
|
147
|
+
return JSON.parse(result.content[0].text)?.actor_label;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return undefined;
|
|
151
|
+
} })()
|
|
152
|
+
: undefined);
|
|
153
|
+
if (label)
|
|
154
|
+
return autoVerifyBounds(_bridge, label, result);
|
|
155
|
+
return result;
|
|
111
156
|
}));
|
|
112
157
|
// --------------------------------------------------------------------------
|
|
113
158
|
// ue_set_actor_property
|
|
114
159
|
// --------------------------------------------------------------------------
|
|
115
160
|
server.registerTool('ue_set_actor_property', {
|
|
116
161
|
title: 'Set Actor Property',
|
|
117
|
-
description: '[requires_plugin] Set a UPROPERTY value on an actor by label. Supports bool, int, float, string, name, text, actor (reference by label), and asset (reference by path).',
|
|
162
|
+
description: '[requires_plugin] Set a UPROPERTY value on an actor by label. Supports bool, int, float, string, name, text, actor (reference by label), and asset (reference by path). After setting spatial properties (transforms, offsets, scale), verify the result with ue_get_component_bounds before reporting success.',
|
|
118
163
|
inputSchema: z.object({
|
|
119
164
|
actor_label: z.string().describe('The editor label of the target actor'),
|
|
120
165
|
property_name: z.string().describe('The UPROPERTY name to set (e.g., RoomID, RoomDoor, DoorCurve)'),
|
|
@@ -136,17 +181,19 @@ export function registerEditorTools(server, bridge) {
|
|
|
136
181
|
if (args.component_name) {
|
|
137
182
|
payload['component_name'] = args.component_name;
|
|
138
183
|
}
|
|
139
|
-
|
|
184
|
+
const result = await sendOrDisconnect(_bridge, {
|
|
140
185
|
type: 'actor.setProperty',
|
|
141
186
|
payload,
|
|
142
187
|
});
|
|
188
|
+
// Auto-verify: query bounds after property change
|
|
189
|
+
return autoVerifyBounds(_bridge, args.actor_label, result);
|
|
143
190
|
}));
|
|
144
191
|
// --------------------------------------------------------------------------
|
|
145
192
|
// ue_create_data_asset
|
|
146
193
|
// --------------------------------------------------------------------------
|
|
147
194
|
server.registerTool('ue_create_data_asset', {
|
|
148
195
|
title: 'Create Data Asset',
|
|
149
|
-
description: '[requires_plugin] Create a UPrimaryDataAsset (or subclass) at the given path with reflection-set properties.',
|
|
196
|
+
description: '[requires_plugin] Create a UPrimaryDataAsset (or subclass) at the given path with reflection-set properties. After creation, verify the asset exists with ue_query_assets.',
|
|
150
197
|
inputSchema: z.object({
|
|
151
198
|
asset_path: z.string().describe('UE asset path, e.g. /Game/Data/DA_ValveHandle'),
|
|
152
199
|
class_name: z.string().describe('Asset class name, e.g. ItemDefinition'),
|
|
@@ -171,7 +218,7 @@ export function registerEditorTools(server, bridge) {
|
|
|
171
218
|
// --------------------------------------------------------------------------
|
|
172
219
|
server.registerTool('ue_create_curve', {
|
|
173
220
|
title: 'Create Float Curve',
|
|
174
|
-
description: '[requires_plugin] Create a UCurveFloat asset with keyframes at the given path.',
|
|
221
|
+
description: '[requires_plugin] Create a UCurveFloat asset with keyframes at the given path. After creation, verify the asset exists with ue_query_assets.',
|
|
175
222
|
inputSchema: z.object({
|
|
176
223
|
asset_path: z.string().describe('UE asset path, e.g. /Game/Curves/C_DoorSwing'),
|
|
177
224
|
keys: z.array(z.object({
|
|
@@ -195,7 +242,7 @@ export function registerEditorTools(server, bridge) {
|
|
|
195
242
|
// --------------------------------------------------------------------------
|
|
196
243
|
server.registerTool('ue_transform_actor', {
|
|
197
244
|
title: 'Transform UE Actor',
|
|
198
|
-
description: '[requires_plugin] Move, rotate, and/or scale an existing actor in the currently open UE Editor level.',
|
|
245
|
+
description: '[requires_plugin] Move, rotate, and/or scale an existing actor in the currently open UE Editor level. IMPORTANT: After transforming, always verify the result with ue_get_component_bounds (numerical check) or ue_look_at/ue_visual_review (visual check) before telling the user it worked. Rotations can shift meshes relative to pivots — always confirm final bounds.',
|
|
199
246
|
inputSchema: z.object({
|
|
200
247
|
actor_label: z.string().describe('The editor label of the actor to transform'),
|
|
201
248
|
location: z
|
|
@@ -235,14 +282,16 @@ export function registerEditorTools(server, bridge) {
|
|
|
235
282
|
payload['rotation'] = args.rotation;
|
|
236
283
|
if (args.scale)
|
|
237
284
|
payload['scale'] = args.scale;
|
|
238
|
-
|
|
285
|
+
const result = await sendOrDisconnect(_bridge, { type: 'actor.transform', payload });
|
|
286
|
+
// Auto-verify: query bounds after transform
|
|
287
|
+
return autoVerifyBounds(_bridge, args.actor_label, result);
|
|
239
288
|
}));
|
|
240
289
|
// --------------------------------------------------------------------------
|
|
241
290
|
// ue_delete_actor
|
|
242
291
|
// --------------------------------------------------------------------------
|
|
243
292
|
server.registerTool('ue_delete_actor', {
|
|
244
293
|
title: 'Delete UE Actor',
|
|
245
|
-
description: '[requires_plugin] Delete an actor from the currently open UE Editor level. This operation is destructive.',
|
|
294
|
+
description: '[requires_plugin] Delete an actor from the currently open UE Editor level. This operation is destructive. After deleting, verify with ue_list_actors that the actor is gone.',
|
|
246
295
|
inputSchema: z.object({
|
|
247
296
|
actor_label: z.string().describe('The editor label of the actor to delete'),
|
|
248
297
|
}),
|
|
@@ -251,10 +300,30 @@ export function registerEditorTools(server, bridge) {
|
|
|
251
300
|
destructiveHint: true,
|
|
252
301
|
},
|
|
253
302
|
}, withKnownIssues('ue_delete_actor', async (args) => {
|
|
254
|
-
|
|
303
|
+
const result = await sendOrDisconnect(_bridge, {
|
|
255
304
|
type: 'actor.delete',
|
|
256
305
|
payload: { actor_label: args.actor_label },
|
|
257
306
|
});
|
|
307
|
+
// Auto-verify: confirm actor no longer exists
|
|
308
|
+
if (!result.isError) {
|
|
309
|
+
try {
|
|
310
|
+
const verifyResponse = await _bridge.sendCommand({
|
|
311
|
+
type: 'actor.list',
|
|
312
|
+
correlationId: '',
|
|
313
|
+
});
|
|
314
|
+
if (verifyResponse.success) {
|
|
315
|
+
const actors = verifyResponse.data;
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
...result.content,
|
|
319
|
+
{ type: 'text', text: JSON.stringify({ _verification: { deleted: args.actor_label, remaining_actors: actors } }) },
|
|
320
|
+
],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch { /* verification is best-effort */ }
|
|
325
|
+
}
|
|
326
|
+
return result;
|
|
258
327
|
}));
|
|
259
328
|
// --------------------------------------------------------------------------
|
|
260
329
|
// ue_query_assets
|
|
@@ -325,4 +394,25 @@ export function registerEditorTools(server, bridge) {
|
|
|
325
394
|
}, withKnownIssues('ue_read_level_layout', async (_args) => {
|
|
326
395
|
return sendOrDisconnect(_bridge, { type: 'level.layout' });
|
|
327
396
|
}));
|
|
397
|
+
// --------------------------------------------------------------------------
|
|
398
|
+
// ue_get_component_bounds
|
|
399
|
+
// --------------------------------------------------------------------------
|
|
400
|
+
server.registerTool('ue_get_component_bounds', {
|
|
401
|
+
title: 'Get Actor Component Bounds',
|
|
402
|
+
description: '[requires_plugin] Get the world-space bounding box and transform of an actor and its components. Use this to verify spatial placement after spawning, transforming, or setting properties. Returns min/max bounds, center, and extent for each component.',
|
|
403
|
+
inputSchema: z.object({
|
|
404
|
+
actor_label: z.string().describe('The editor label of the actor to inspect'),
|
|
405
|
+
component_name: z.string().optional().describe('Optional: inspect a specific component only (e.g., DoorMesh, DoorCollision)'),
|
|
406
|
+
}),
|
|
407
|
+
annotations: {
|
|
408
|
+
readOnlyHint: true,
|
|
409
|
+
destructiveHint: false,
|
|
410
|
+
},
|
|
411
|
+
}, withKnownIssues('ue_get_component_bounds', async (args) => {
|
|
412
|
+
const payload = { actor_label: args.actor_label };
|
|
413
|
+
if (args.component_name) {
|
|
414
|
+
payload['component_name'] = args.component_name;
|
|
415
|
+
}
|
|
416
|
+
return sendOrDisconnect(_bridge, { type: 'actor.componentBounds', payload });
|
|
417
|
+
}));
|
|
328
418
|
}
|
|
@@ -428,7 +428,7 @@ export function registerViewportTools(server, bridge) {
|
|
|
428
428
|
// --------------------------------------------------------------------------
|
|
429
429
|
server.registerTool('ue_visual_review', {
|
|
430
430
|
title: 'Visual Review Screenshot',
|
|
431
|
-
description: '[requires_plugin] Take a quick viewport screenshot for visual review. Returns the image inline with camera context. Use this
|
|
431
|
+
description: '[requires_plugin] Take a quick viewport screenshot for visual review. Returns the image inline with camera context. Use this to VERIFY changes after any mutation (spawn, transform, property set). Always screenshot after making visual changes — do not ask the user to check manually.',
|
|
432
432
|
inputSchema: z.object({
|
|
433
433
|
width: z
|
|
434
434
|
.number()
|
|
@@ -662,7 +662,7 @@ export function registerViewportTools(server, bridge) {
|
|
|
662
662
|
// --------------------------------------------------------------------------
|
|
663
663
|
server.registerTool('ue_iterate_scene', {
|
|
664
664
|
title: 'Full Scene Context Snapshot',
|
|
665
|
-
description: '[requires_plugin] Get full visual and semantic context in one call. Takes a screenshot AND returns structured data about nearby actors. Use this to orient yourself — see what you are looking at and
|
|
665
|
+
description: '[requires_plugin] Get full visual and semantic context in one call. Takes a screenshot AND returns structured data about nearby actors. Use this to orient yourself and to verify complex multi-actor changes — see what you are looking at, what is nearby, and whether everything is in the right place.',
|
|
666
666
|
inputSchema: z.object({
|
|
667
667
|
width: z
|
|
668
668
|
.number()
|