ultimate-unreal-engine-mcp 0.1.0
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 +729 -0
- package/dist/build/error-parser.js +51 -0
- package/dist/build/fix-suggester.js +84 -0
- package/dist/build/ubt-runner.js +146 -0
- package/dist/cli.js +13 -0
- package/dist/config.js +8 -0
- package/dist/docs/data/ue57-api.js +228 -0
- package/dist/docs/doc-index.js +110 -0
- package/dist/docs/types.js +4 -0
- package/dist/generators/class-generator.js +363 -0
- package/dist/generators/file-modifier.js +276 -0
- package/dist/generators/uht-validator.js +177 -0
- package/dist/index.js +89 -0
- package/dist/parsers/cpp-class-index.js +230 -0
- package/dist/parsers/cpp-parser.js +369 -0
- package/dist/parsers/ini-parser.js +216 -0
- package/dist/parsers/uproject-parser.js +130 -0
- package/dist/plugin-bridge/client.js +217 -0
- package/dist/plugin-bridge/protocol.js +6 -0
- package/dist/plugin-bridge/retry.js +23 -0
- package/dist/setup.js +209 -0
- package/dist/tools/ai-systems/index.js +247 -0
- package/dist/tools/ai-systems/types.js +4 -0
- package/dist/tools/animation/index.js +241 -0
- package/dist/tools/animation/types.js +4 -0
- package/dist/tools/audio/index.js +204 -0
- package/dist/tools/audio/types.js +4 -0
- package/dist/tools/blueprint/index.js +495 -0
- package/dist/tools/blueprint/types.js +4 -0
- package/dist/tools/build/index.js +163 -0
- package/dist/tools/chaos/index.js +230 -0
- package/dist/tools/chaos/types.js +4 -0
- package/dist/tools/collision-physics/index.js +211 -0
- package/dist/tools/config/index.js +288 -0
- package/dist/tools/cpp/index.js +305 -0
- package/dist/tools/docs/index.js +251 -0
- package/dist/tools/editor/index.js +242 -0
- package/dist/tools/gas/index.js +222 -0
- package/dist/tools/gas/types.js +5 -0
- package/dist/tools/import-export/index.js +218 -0
- package/dist/tools/input/index.js +146 -0
- package/dist/tools/known-issues/index.js +88 -0
- package/dist/tools/known-issues/middleware.js +55 -0
- package/dist/tools/known-issues/store.js +125 -0
- package/dist/tools/livelink/index.js +203 -0
- package/dist/tools/livelink/types.js +4 -0
- package/dist/tools/material/index.js +190 -0
- package/dist/tools/motion-design/index.js +251 -0
- package/dist/tools/motion-design/types.js +6 -0
- package/dist/tools/movie-render/index.js +220 -0
- package/dist/tools/networking/index.js +149 -0
- package/dist/tools/pcg/index.js +164 -0
- package/dist/tools/selection/index.js +180 -0
- package/dist/tools/sequencer/index.js +218 -0
- package/dist/tools/validation/index.js +183 -0
- package/dist/tools/validation/types.js +4 -0
- package/dist/tools/viewport/index.js +310 -0
- package/dist/tools/worldpartition/index.js +226 -0
- package/dist/tools/worldpartition/types.js +4 -0
- package/dist/utils/execFileNoThrow.js +40 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/path-guard.js +26 -0
- package/package.json +40 -0
- package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPTcpServer.h +59 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// src/tools/pcg/index.ts
|
|
2
|
+
// MCP tool implementations for PCG Framework operations (Phase 24).
|
|
3
|
+
// All tools route commands through PluginBridgeClient to the C++ MCPBridge plugin.
|
|
4
|
+
// Returns structured plugin_not_connected errors when the plugin is absent.
|
|
5
|
+
//
|
|
6
|
+
// Tools registered (Phase 24 — PCG-01 through PCG-04):
|
|
7
|
+
// ue_list_pcg_graphs — List all PCG graph assets in the project
|
|
8
|
+
// ue_inspect_pcg_graph — Inspect nodes, connections, and parameters of a PCG graph
|
|
9
|
+
// ue_execute_pcg_graph — Execute a PCG graph on an actor with optional parameter overrides
|
|
10
|
+
// ue_query_pcg_results — Query PCG execution results for an actor
|
|
11
|
+
//
|
|
12
|
+
// All tools require MCPBridge plugin (Phase 24 — PCG-01 through PCG-04).
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { withKnownIssues } from '../known-issues/middleware.js';
|
|
15
|
+
import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// sendOrDisconnect helper
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Sends a command to the bridge and returns a ToolResult.
|
|
21
|
+
*
|
|
22
|
+
* - On success (response.success true): returns data as JSON text.
|
|
23
|
+
* - On command-level failure (response.success false): returns isError:true with error JSON.
|
|
24
|
+
* - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
|
|
25
|
+
* - On other errors: rethrows (withKnownIssues catches and formats).
|
|
26
|
+
*
|
|
27
|
+
* NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
|
|
28
|
+
* passing an empty string is safe and correct (per STATE.md decision).
|
|
29
|
+
*/
|
|
30
|
+
async function sendOrDisconnect(bridge, cmd) {
|
|
31
|
+
try {
|
|
32
|
+
const response = await bridge.sendCommand({ ...cmd, correlationId: '' });
|
|
33
|
+
if (!response.success) {
|
|
34
|
+
return {
|
|
35
|
+
isError: true,
|
|
36
|
+
content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof PluginNotConnectedError) {
|
|
45
|
+
return {
|
|
46
|
+
isError: true,
|
|
47
|
+
content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
throw err; // withKnownIssues catches unexpected errors
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// registerPCGTools
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
/**
|
|
57
|
+
* Register UE PCG Framework tools on the MCP server.
|
|
58
|
+
*
|
|
59
|
+
* All tools in this domain require the MCPBridge editor plugin.
|
|
60
|
+
* When the plugin is not connected, each handler returns:
|
|
61
|
+
* { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
|
|
62
|
+
*
|
|
63
|
+
* Tools registered (Phase 24):
|
|
64
|
+
* ue_list_pcg_graphs — List all PCG graph assets in the project
|
|
65
|
+
* ue_inspect_pcg_graph — Inspect nodes, connections, and parameters of a PCG graph
|
|
66
|
+
* ue_execute_pcg_graph — Execute a PCG graph on an actor with optional parameter overrides
|
|
67
|
+
* ue_query_pcg_results — Query PCG execution results for an actor
|
|
68
|
+
*
|
|
69
|
+
* @param server The McpServer instance to register tools on.
|
|
70
|
+
* @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
|
|
71
|
+
*/
|
|
72
|
+
export function registerPCGTools(server, bridge) {
|
|
73
|
+
const _bridge = bridge ?? new PluginBridgeClient();
|
|
74
|
+
// --------------------------------------------------------------------------
|
|
75
|
+
// ue_list_pcg_graphs
|
|
76
|
+
// --------------------------------------------------------------------------
|
|
77
|
+
server.registerTool('ue_list_pcg_graphs', {
|
|
78
|
+
title: 'List PCG Graphs',
|
|
79
|
+
description: '[requires_plugin] List all PCG graph assets in the project with node count and component assignment.',
|
|
80
|
+
inputSchema: z.object({}),
|
|
81
|
+
annotations: {
|
|
82
|
+
readOnlyHint: true,
|
|
83
|
+
destructiveHint: false,
|
|
84
|
+
},
|
|
85
|
+
}, withKnownIssues('ue_list_pcg_graphs', async (_args) => {
|
|
86
|
+
return sendOrDisconnect(_bridge, {
|
|
87
|
+
type: 'pcg.list',
|
|
88
|
+
payload: {},
|
|
89
|
+
});
|
|
90
|
+
}));
|
|
91
|
+
// --------------------------------------------------------------------------
|
|
92
|
+
// ue_inspect_pcg_graph
|
|
93
|
+
// --------------------------------------------------------------------------
|
|
94
|
+
server.registerTool('ue_inspect_pcg_graph', {
|
|
95
|
+
title: 'Inspect PCG Graph',
|
|
96
|
+
description: '[requires_plugin] Inspect a PCG graph\'s nodes, connections, parameters, and enabled state.',
|
|
97
|
+
inputSchema: z.object({
|
|
98
|
+
asset_path: z
|
|
99
|
+
.string()
|
|
100
|
+
.min(1)
|
|
101
|
+
.describe('Content browser path to the PCG graph asset, e.g. /Game/PCG/MyGraph'),
|
|
102
|
+
}),
|
|
103
|
+
annotations: {
|
|
104
|
+
readOnlyHint: true,
|
|
105
|
+
destructiveHint: false,
|
|
106
|
+
},
|
|
107
|
+
}, withKnownIssues('ue_inspect_pcg_graph', async (args) => {
|
|
108
|
+
return sendOrDisconnect(_bridge, {
|
|
109
|
+
type: 'pcg.inspect',
|
|
110
|
+
payload: { asset_path: args.asset_path },
|
|
111
|
+
});
|
|
112
|
+
}));
|
|
113
|
+
// --------------------------------------------------------------------------
|
|
114
|
+
// ue_execute_pcg_graph
|
|
115
|
+
// --------------------------------------------------------------------------
|
|
116
|
+
server.registerTool('ue_execute_pcg_graph', {
|
|
117
|
+
title: 'Execute PCG Graph',
|
|
118
|
+
description: '[requires_plugin] Execute a PCG graph on an actor\'s PCG component with optional parameter overrides.',
|
|
119
|
+
inputSchema: z.object({
|
|
120
|
+
actor_label: z
|
|
121
|
+
.string()
|
|
122
|
+
.min(1)
|
|
123
|
+
.describe('Display label of the actor with a PCG component'),
|
|
124
|
+
parameter_overrides: z
|
|
125
|
+
.record(z.union([z.number(), z.string()]))
|
|
126
|
+
.optional()
|
|
127
|
+
.describe('Key-value map of parameter overrides to apply before execution'),
|
|
128
|
+
}),
|
|
129
|
+
annotations: {
|
|
130
|
+
readOnlyHint: false,
|
|
131
|
+
destructiveHint: false,
|
|
132
|
+
},
|
|
133
|
+
}, withKnownIssues('ue_execute_pcg_graph', async (args) => {
|
|
134
|
+
const payload = {
|
|
135
|
+
actor_label: args.actor_label,
|
|
136
|
+
};
|
|
137
|
+
if (args.parameter_overrides !== undefined) {
|
|
138
|
+
payload['parameter_overrides'] = args.parameter_overrides;
|
|
139
|
+
}
|
|
140
|
+
return sendOrDisconnect(_bridge, { type: 'pcg.execute', payload });
|
|
141
|
+
}));
|
|
142
|
+
// --------------------------------------------------------------------------
|
|
143
|
+
// ue_query_pcg_results
|
|
144
|
+
// --------------------------------------------------------------------------
|
|
145
|
+
server.registerTool('ue_query_pcg_results', {
|
|
146
|
+
title: 'Query PCG Results',
|
|
147
|
+
description: '[requires_plugin] Query PCG execution results for an actor — point counts, data entries, and warnings.',
|
|
148
|
+
inputSchema: z.object({
|
|
149
|
+
actor_label: z
|
|
150
|
+
.string()
|
|
151
|
+
.min(1)
|
|
152
|
+
.describe('Display label of the actor with a PCG component'),
|
|
153
|
+
}),
|
|
154
|
+
annotations: {
|
|
155
|
+
readOnlyHint: true,
|
|
156
|
+
destructiveHint: false,
|
|
157
|
+
},
|
|
158
|
+
}, withKnownIssues('ue_query_pcg_results', async (args) => {
|
|
159
|
+
return sendOrDisconnect(_bridge, {
|
|
160
|
+
type: 'pcg.results',
|
|
161
|
+
payload: { actor_label: args.actor_label },
|
|
162
|
+
});
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// src/tools/selection/index.ts
|
|
2
|
+
// MCP tool implementations for Actor Selection and Duplication (Phase 19).
|
|
3
|
+
// All tools route commands through PluginBridgeClient to the C++ MCPBridge handlers.
|
|
4
|
+
// Returns structured plugin_not_connected errors when the plugin is absent.
|
|
5
|
+
//
|
|
6
|
+
// Tools registered (Phase 19 — SEL-01 through SEL-04):
|
|
7
|
+
// ue_select_actors — Select or deselect actors by name, label, or class
|
|
8
|
+
// ue_get_selection — Get current editor selection or change selection mode
|
|
9
|
+
// ue_duplicate_actors — Duplicate selected actors with optional position offset
|
|
10
|
+
// ue_convert_actor — Convert an actor to a different class
|
|
11
|
+
//
|
|
12
|
+
// All tools require MCPBridge plugin (Phase 19 — SEL-01 through SEL-04).
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { withKnownIssues } from '../known-issues/middleware.js';
|
|
15
|
+
import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// sendOrDisconnect helper
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Sends a command to the bridge and returns a ToolResult.
|
|
21
|
+
*
|
|
22
|
+
* - On success (response.success true): returns data as JSON text.
|
|
23
|
+
* - On command-level failure (response.success false): returns isError:true with error JSON.
|
|
24
|
+
* - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
|
|
25
|
+
* - On other errors: rethrows (withKnownIssues catches and formats).
|
|
26
|
+
*
|
|
27
|
+
* NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
|
|
28
|
+
* passing an empty string is safe and correct (per STATE.md decision).
|
|
29
|
+
*/
|
|
30
|
+
async function sendOrDisconnect(bridge, cmd) {
|
|
31
|
+
try {
|
|
32
|
+
const response = await bridge.sendCommand({ ...cmd, correlationId: '' });
|
|
33
|
+
if (!response.success) {
|
|
34
|
+
return {
|
|
35
|
+
isError: true,
|
|
36
|
+
content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof PluginNotConnectedError) {
|
|
45
|
+
return {
|
|
46
|
+
isError: true,
|
|
47
|
+
content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
throw err; // withKnownIssues catches unexpected errors
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// registerSelectionTools
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
/**
|
|
57
|
+
* Register UE actor selection and duplication tools on the MCP server.
|
|
58
|
+
*
|
|
59
|
+
* All tools in this domain require the MCPBridge editor plugin.
|
|
60
|
+
* When the plugin is not connected, each handler returns:
|
|
61
|
+
* { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
|
|
62
|
+
*
|
|
63
|
+
* Tools registered (Phase 19):
|
|
64
|
+
* ue_select_actors — SEL-01: Select or deselect actors by name, label, or class
|
|
65
|
+
* ue_get_selection — SEL-02: Get current editor selection or change selection mode
|
|
66
|
+
* ue_duplicate_actors — SEL-03: Duplicate selected actors with optional offset
|
|
67
|
+
* ue_convert_actor — SEL-04: Convert an actor to a different class
|
|
68
|
+
*
|
|
69
|
+
* @param server The McpServer instance to register tools on.
|
|
70
|
+
* @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
|
|
71
|
+
*/
|
|
72
|
+
export function registerSelectionTools(server, bridge) {
|
|
73
|
+
const _bridge = bridge ?? new PluginBridgeClient();
|
|
74
|
+
// --------------------------------------------------------------------------
|
|
75
|
+
// ue_select_actors (SEL-01)
|
|
76
|
+
// --------------------------------------------------------------------------
|
|
77
|
+
server.registerTool('ue_select_actors', {
|
|
78
|
+
title: 'Select Actors',
|
|
79
|
+
description: "[requires_plugin] Select or deselect actors in the editor by name, label, or class. Supports wildcard patterns (e.g. 'Light*').",
|
|
80
|
+
inputSchema: z.object({
|
|
81
|
+
actors: z
|
|
82
|
+
.array(z.string())
|
|
83
|
+
.optional()
|
|
84
|
+
.describe('Actor labels or names to match. Supports wildcards like Light*'),
|
|
85
|
+
class_filter: z
|
|
86
|
+
.string()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe('Select all actors of this class, e.g. StaticMeshActor'),
|
|
89
|
+
action: z
|
|
90
|
+
.enum(['select', 'deselect'])
|
|
91
|
+
.default('select')
|
|
92
|
+
.describe('Whether to select or deselect matched actors'),
|
|
93
|
+
}),
|
|
94
|
+
annotations: {
|
|
95
|
+
readOnlyHint: false,
|
|
96
|
+
destructiveHint: false,
|
|
97
|
+
},
|
|
98
|
+
}, withKnownIssues('ue_select_actors', async (args) => {
|
|
99
|
+
const payload = {};
|
|
100
|
+
if (args.actors !== undefined)
|
|
101
|
+
payload['actors'] = args.actors;
|
|
102
|
+
if (args.class_filter !== undefined)
|
|
103
|
+
payload['class_filter'] = args.class_filter;
|
|
104
|
+
if (args.action !== undefined)
|
|
105
|
+
payload['action'] = args.action;
|
|
106
|
+
return sendOrDisconnect(_bridge, { type: 'selection.select', payload });
|
|
107
|
+
}));
|
|
108
|
+
// --------------------------------------------------------------------------
|
|
109
|
+
// ue_get_selection (SEL-02)
|
|
110
|
+
// --------------------------------------------------------------------------
|
|
111
|
+
server.registerTool('ue_get_selection', {
|
|
112
|
+
title: 'Get/Set Actor Selection',
|
|
113
|
+
description: "[requires_plugin] Get the current editor actor selection. Optionally change selection mode: 'all' selects everything, 'none' clears selection, 'invert' toggles each actor.",
|
|
114
|
+
inputSchema: z.object({
|
|
115
|
+
mode: z
|
|
116
|
+
.enum(['current', 'all', 'none', 'invert'])
|
|
117
|
+
.default('current')
|
|
118
|
+
.describe('Selection mode: current (read only), all, none, or invert'),
|
|
119
|
+
}),
|
|
120
|
+
annotations: {
|
|
121
|
+
readOnlyHint: false,
|
|
122
|
+
destructiveHint: false,
|
|
123
|
+
},
|
|
124
|
+
}, withKnownIssues('ue_get_selection', async (args) => {
|
|
125
|
+
return sendOrDisconnect(_bridge, {
|
|
126
|
+
type: 'selection.get',
|
|
127
|
+
payload: { mode: args.mode },
|
|
128
|
+
});
|
|
129
|
+
}));
|
|
130
|
+
// --------------------------------------------------------------------------
|
|
131
|
+
// ue_duplicate_actors (SEL-03)
|
|
132
|
+
// --------------------------------------------------------------------------
|
|
133
|
+
server.registerTool('ue_duplicate_actors', {
|
|
134
|
+
title: 'Duplicate Selected Actors',
|
|
135
|
+
description: '[requires_plugin] Duplicate all currently selected actors in the editor with an optional position offset. Defaults to offset (100, 0, 0) if not specified.',
|
|
136
|
+
inputSchema: z.object({
|
|
137
|
+
offset: z
|
|
138
|
+
.object({
|
|
139
|
+
x: z.number().default(100),
|
|
140
|
+
y: z.number().default(0),
|
|
141
|
+
z: z.number().default(0),
|
|
142
|
+
})
|
|
143
|
+
.optional()
|
|
144
|
+
.describe('Position offset for duplicated actors in UE units'),
|
|
145
|
+
}),
|
|
146
|
+
annotations: {
|
|
147
|
+
readOnlyHint: false,
|
|
148
|
+
destructiveHint: false,
|
|
149
|
+
},
|
|
150
|
+
}, withKnownIssues('ue_duplicate_actors', async (args) => {
|
|
151
|
+
return sendOrDisconnect(_bridge, {
|
|
152
|
+
type: 'selection.duplicate',
|
|
153
|
+
payload: { offset: args.offset ?? { x: 100, y: 0, z: 0 } },
|
|
154
|
+
});
|
|
155
|
+
}));
|
|
156
|
+
// --------------------------------------------------------------------------
|
|
157
|
+
// ue_convert_actor (SEL-04)
|
|
158
|
+
// --------------------------------------------------------------------------
|
|
159
|
+
server.registerTool('ue_convert_actor', {
|
|
160
|
+
title: 'Convert Actor Class',
|
|
161
|
+
description: "[requires_plugin] Convert an actor to a different class (e.g. StaticMeshActor to a Blueprint class). The actor is replaced with a new instance of the target class at the same transform.",
|
|
162
|
+
inputSchema: z.object({
|
|
163
|
+
actor_label: z
|
|
164
|
+
.string()
|
|
165
|
+
.describe('Label of the actor to convert'),
|
|
166
|
+
target_class: z
|
|
167
|
+
.string()
|
|
168
|
+
.describe("Target class name (e.g. 'StaticMeshActor') or full path (e.g. '/Game/BP_MyActor.BP_MyActor_C')"),
|
|
169
|
+
}),
|
|
170
|
+
annotations: {
|
|
171
|
+
readOnlyHint: false,
|
|
172
|
+
destructiveHint: true,
|
|
173
|
+
},
|
|
174
|
+
}, withKnownIssues('ue_convert_actor', async (args) => {
|
|
175
|
+
return sendOrDisconnect(_bridge, {
|
|
176
|
+
type: 'selection.convert',
|
|
177
|
+
payload: { actor_label: args.actor_label, target_class: args.target_class },
|
|
178
|
+
});
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// src/tools/sequencer/index.ts
|
|
2
|
+
// MCP tool implementations for Sequencer & Cinematics operations (Phase 13).
|
|
3
|
+
// All tools route commands through PluginBridgeClient to the C++ MCPBridge plugin.
|
|
4
|
+
// Returns structured plugin_not_connected errors when the plugin is absent.
|
|
5
|
+
//
|
|
6
|
+
// Tools registered (Phase 13 — SEQ-01 through SEQ-05):
|
|
7
|
+
// ue_create_sequence — Create a new Level Sequence asset
|
|
8
|
+
// ue_list_sequence_tracks — List all tracks in a Level Sequence
|
|
9
|
+
// ue_add_sequence_track — Add a transform or float track to an actor
|
|
10
|
+
// ue_add_keyframe — Add a keyframe at a given frame on a track
|
|
11
|
+
// ue_sequence_playback — Control Level Sequence playback (play/pause/stop/scrub)
|
|
12
|
+
//
|
|
13
|
+
// All tools require MCPBridge plugin (Phase 13 — SEQ-01 through SEQ-05).
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { withKnownIssues } from '../known-issues/middleware.js';
|
|
16
|
+
import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// sendOrDisconnect helper
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Sends a command to the bridge and returns a ToolResult.
|
|
22
|
+
*
|
|
23
|
+
* - On success (response.success true): returns data as JSON text.
|
|
24
|
+
* - On command-level failure (response.success false): returns isError:true with error JSON.
|
|
25
|
+
* - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
|
|
26
|
+
* - On other errors: rethrows (withKnownIssues catches and formats).
|
|
27
|
+
*
|
|
28
|
+
* NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
|
|
29
|
+
* passing an empty string is safe and correct (per STATE.md decision).
|
|
30
|
+
*/
|
|
31
|
+
async function sendOrDisconnect(bridge, cmd) {
|
|
32
|
+
try {
|
|
33
|
+
const response = await bridge.sendCommand({ ...cmd, correlationId: '' });
|
|
34
|
+
if (!response.success) {
|
|
35
|
+
return {
|
|
36
|
+
isError: true,
|
|
37
|
+
content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof PluginNotConnectedError) {
|
|
46
|
+
return {
|
|
47
|
+
isError: true,
|
|
48
|
+
content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
throw err; // withKnownIssues catches unexpected errors
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// registerSequencerTools
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Register UE Sequencer & Cinematics tools on the MCP server.
|
|
59
|
+
*
|
|
60
|
+
* All tools in this domain require the MCPBridge editor plugin.
|
|
61
|
+
* When the plugin is not connected, each handler returns:
|
|
62
|
+
* { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
|
|
63
|
+
*
|
|
64
|
+
* Tools registered (Phase 13):
|
|
65
|
+
* ue_create_sequence — Create a new Level Sequence asset
|
|
66
|
+
* ue_list_sequence_tracks — List all tracks in a Level Sequence
|
|
67
|
+
* ue_add_sequence_track — Add a property or transform track for an actor
|
|
68
|
+
* ue_add_keyframe — Add a keyframe at a specified frame on a track
|
|
69
|
+
* ue_sequence_playback — Control Level Sequence playback
|
|
70
|
+
*
|
|
71
|
+
* @param server The McpServer instance to register tools on.
|
|
72
|
+
* @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
|
|
73
|
+
*/
|
|
74
|
+
export function registerSequencerTools(server, bridge) {
|
|
75
|
+
const _bridge = bridge ?? new PluginBridgeClient();
|
|
76
|
+
// --------------------------------------------------------------------------
|
|
77
|
+
// ue_create_sequence
|
|
78
|
+
// --------------------------------------------------------------------------
|
|
79
|
+
server.registerTool('ue_create_sequence', {
|
|
80
|
+
title: 'Create Level Sequence',
|
|
81
|
+
description: '[requires_plugin] Create a new Level Sequence asset at the specified content path and open it in the Sequencer editor.',
|
|
82
|
+
inputSchema: z.object({
|
|
83
|
+
asset_path: z
|
|
84
|
+
.string()
|
|
85
|
+
.min(1)
|
|
86
|
+
.describe('Content browser path for the new sequence, e.g. /Game/Cinematics/MySequence'),
|
|
87
|
+
}),
|
|
88
|
+
annotations: {
|
|
89
|
+
readOnlyHint: false,
|
|
90
|
+
destructiveHint: false,
|
|
91
|
+
},
|
|
92
|
+
}, withKnownIssues('ue_create_sequence', async (args) => {
|
|
93
|
+
return sendOrDisconnect(_bridge, {
|
|
94
|
+
type: 'sequencer.create',
|
|
95
|
+
payload: { asset_path: args.asset_path },
|
|
96
|
+
});
|
|
97
|
+
}));
|
|
98
|
+
// --------------------------------------------------------------------------
|
|
99
|
+
// ue_list_sequence_tracks
|
|
100
|
+
// --------------------------------------------------------------------------
|
|
101
|
+
server.registerTool('ue_list_sequence_tracks', {
|
|
102
|
+
title: 'List Sequence Tracks',
|
|
103
|
+
description: '[requires_plugin] List all tracks in a Level Sequence with bound objects, track type, section count, and key count.',
|
|
104
|
+
inputSchema: z.object({
|
|
105
|
+
asset_path: z
|
|
106
|
+
.string()
|
|
107
|
+
.min(1)
|
|
108
|
+
.describe('Content browser path to the Level Sequence asset'),
|
|
109
|
+
}),
|
|
110
|
+
annotations: {
|
|
111
|
+
readOnlyHint: true,
|
|
112
|
+
destructiveHint: false,
|
|
113
|
+
},
|
|
114
|
+
}, withKnownIssues('ue_list_sequence_tracks', async (args) => {
|
|
115
|
+
return sendOrDisconnect(_bridge, {
|
|
116
|
+
type: 'sequencer.tracks',
|
|
117
|
+
payload: { asset_path: args.asset_path },
|
|
118
|
+
});
|
|
119
|
+
}));
|
|
120
|
+
// --------------------------------------------------------------------------
|
|
121
|
+
// ue_add_sequence_track
|
|
122
|
+
// --------------------------------------------------------------------------
|
|
123
|
+
server.registerTool('ue_add_sequence_track', {
|
|
124
|
+
title: 'Add Sequence Track',
|
|
125
|
+
description: '[requires_plugin] Add a property or transform track to a Level Sequence for a given actor.',
|
|
126
|
+
inputSchema: z.object({
|
|
127
|
+
asset_path: z.string().min(1),
|
|
128
|
+
actor_label: z
|
|
129
|
+
.string()
|
|
130
|
+
.min(1)
|
|
131
|
+
.describe('Display label of the actor to bind the track to'),
|
|
132
|
+
track_type: z
|
|
133
|
+
.enum(['transform', 'float'])
|
|
134
|
+
.describe('Track type: transform (3D transform track) or float (generic float property track)'),
|
|
135
|
+
}),
|
|
136
|
+
annotations: {
|
|
137
|
+
readOnlyHint: false,
|
|
138
|
+
destructiveHint: false,
|
|
139
|
+
},
|
|
140
|
+
}, withKnownIssues('ue_add_sequence_track', async (args) => {
|
|
141
|
+
return sendOrDisconnect(_bridge, {
|
|
142
|
+
type: 'sequencer.addTrack',
|
|
143
|
+
payload: {
|
|
144
|
+
asset_path: args.asset_path,
|
|
145
|
+
actor_label: args.actor_label,
|
|
146
|
+
track_type: args.track_type,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}));
|
|
150
|
+
// --------------------------------------------------------------------------
|
|
151
|
+
// ue_add_keyframe
|
|
152
|
+
// --------------------------------------------------------------------------
|
|
153
|
+
server.registerTool('ue_add_keyframe', {
|
|
154
|
+
title: 'Add Keyframe',
|
|
155
|
+
description: '[requires_plugin] Add a keyframe at a specified frame number on an existing track in a Level Sequence.',
|
|
156
|
+
inputSchema: z.object({
|
|
157
|
+
asset_path: z.string().min(1),
|
|
158
|
+
track_type: z
|
|
159
|
+
.enum(['transform', 'float'])
|
|
160
|
+
.describe('Type of track to add keyframe to'),
|
|
161
|
+
frame: z
|
|
162
|
+
.number()
|
|
163
|
+
.int()
|
|
164
|
+
.min(0)
|
|
165
|
+
.describe('Frame number (integer, 0-based at sequence display rate)'),
|
|
166
|
+
value: z
|
|
167
|
+
.number()
|
|
168
|
+
.optional()
|
|
169
|
+
.describe('Keyframe value for float tracks (ignored for transform tracks, defaults 0.0)'),
|
|
170
|
+
}),
|
|
171
|
+
annotations: {
|
|
172
|
+
readOnlyHint: false,
|
|
173
|
+
destructiveHint: false,
|
|
174
|
+
},
|
|
175
|
+
}, withKnownIssues('ue_add_keyframe', async (args) => {
|
|
176
|
+
const payload = {
|
|
177
|
+
asset_path: args.asset_path,
|
|
178
|
+
track_type: args.track_type,
|
|
179
|
+
frame: args.frame,
|
|
180
|
+
};
|
|
181
|
+
if (args.value !== undefined) {
|
|
182
|
+
payload['value'] = args.value;
|
|
183
|
+
}
|
|
184
|
+
return sendOrDisconnect(_bridge, { type: 'sequencer.addKey', payload });
|
|
185
|
+
}));
|
|
186
|
+
// --------------------------------------------------------------------------
|
|
187
|
+
// ue_sequence_playback
|
|
188
|
+
// --------------------------------------------------------------------------
|
|
189
|
+
server.registerTool('ue_sequence_playback', {
|
|
190
|
+
title: 'Sequence Playback Control',
|
|
191
|
+
description: '[requires_plugin] Control Level Sequence playback — play, pause, stop, or scrub to a specific frame.',
|
|
192
|
+
inputSchema: z.object({
|
|
193
|
+
asset_path: z.string().min(1),
|
|
194
|
+
action: z
|
|
195
|
+
.enum(['play', 'pause', 'stop', 'scrub'])
|
|
196
|
+
.describe('Playback action: play, pause, stop, or scrub to a frame'),
|
|
197
|
+
frame: z
|
|
198
|
+
.number()
|
|
199
|
+
.int()
|
|
200
|
+
.min(0)
|
|
201
|
+
.optional()
|
|
202
|
+
.describe('Target frame number for scrub action'),
|
|
203
|
+
}),
|
|
204
|
+
annotations: {
|
|
205
|
+
readOnlyHint: false,
|
|
206
|
+
destructiveHint: false,
|
|
207
|
+
},
|
|
208
|
+
}, withKnownIssues('ue_sequence_playback', async (args) => {
|
|
209
|
+
const payload = {
|
|
210
|
+
asset_path: args.asset_path,
|
|
211
|
+
action: args.action,
|
|
212
|
+
};
|
|
213
|
+
if (args.frame !== undefined) {
|
|
214
|
+
payload['frame'] = args.frame;
|
|
215
|
+
}
|
|
216
|
+
return sendOrDisconnect(_bridge, { type: 'sequencer.playback', payload });
|
|
217
|
+
}));
|
|
218
|
+
}
|