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.
Files changed (124) hide show
  1. package/README.md +729 -0
  2. package/dist/build/error-parser.js +51 -0
  3. package/dist/build/fix-suggester.js +84 -0
  4. package/dist/build/ubt-runner.js +146 -0
  5. package/dist/cli.js +13 -0
  6. package/dist/config.js +8 -0
  7. package/dist/docs/data/ue57-api.js +228 -0
  8. package/dist/docs/doc-index.js +110 -0
  9. package/dist/docs/types.js +4 -0
  10. package/dist/generators/class-generator.js +363 -0
  11. package/dist/generators/file-modifier.js +276 -0
  12. package/dist/generators/uht-validator.js +177 -0
  13. package/dist/index.js +89 -0
  14. package/dist/parsers/cpp-class-index.js +230 -0
  15. package/dist/parsers/cpp-parser.js +369 -0
  16. package/dist/parsers/ini-parser.js +216 -0
  17. package/dist/parsers/uproject-parser.js +130 -0
  18. package/dist/plugin-bridge/client.js +217 -0
  19. package/dist/plugin-bridge/protocol.js +6 -0
  20. package/dist/plugin-bridge/retry.js +23 -0
  21. package/dist/setup.js +209 -0
  22. package/dist/tools/ai-systems/index.js +247 -0
  23. package/dist/tools/ai-systems/types.js +4 -0
  24. package/dist/tools/animation/index.js +241 -0
  25. package/dist/tools/animation/types.js +4 -0
  26. package/dist/tools/audio/index.js +204 -0
  27. package/dist/tools/audio/types.js +4 -0
  28. package/dist/tools/blueprint/index.js +495 -0
  29. package/dist/tools/blueprint/types.js +4 -0
  30. package/dist/tools/build/index.js +163 -0
  31. package/dist/tools/chaos/index.js +230 -0
  32. package/dist/tools/chaos/types.js +4 -0
  33. package/dist/tools/collision-physics/index.js +211 -0
  34. package/dist/tools/config/index.js +288 -0
  35. package/dist/tools/cpp/index.js +305 -0
  36. package/dist/tools/docs/index.js +251 -0
  37. package/dist/tools/editor/index.js +242 -0
  38. package/dist/tools/gas/index.js +222 -0
  39. package/dist/tools/gas/types.js +5 -0
  40. package/dist/tools/import-export/index.js +218 -0
  41. package/dist/tools/input/index.js +146 -0
  42. package/dist/tools/known-issues/index.js +88 -0
  43. package/dist/tools/known-issues/middleware.js +55 -0
  44. package/dist/tools/known-issues/store.js +125 -0
  45. package/dist/tools/livelink/index.js +203 -0
  46. package/dist/tools/livelink/types.js +4 -0
  47. package/dist/tools/material/index.js +190 -0
  48. package/dist/tools/motion-design/index.js +251 -0
  49. package/dist/tools/motion-design/types.js +6 -0
  50. package/dist/tools/movie-render/index.js +220 -0
  51. package/dist/tools/networking/index.js +149 -0
  52. package/dist/tools/pcg/index.js +164 -0
  53. package/dist/tools/selection/index.js +180 -0
  54. package/dist/tools/sequencer/index.js +218 -0
  55. package/dist/tools/validation/index.js +183 -0
  56. package/dist/tools/validation/types.js +4 -0
  57. package/dist/tools/viewport/index.js +310 -0
  58. package/dist/tools/worldpartition/index.js +226 -0
  59. package/dist/tools/worldpartition/types.js +4 -0
  60. package/dist/utils/execFileNoThrow.js +40 -0
  61. package/dist/utils/logger.js +27 -0
  62. package/dist/utils/path-guard.js +26 -0
  63. package/package.json +40 -0
  64. package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
  65. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
  66. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
  67. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
  68. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
  69. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
  70. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
  71. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
  72. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
  73. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
  74. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
  75. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
  76. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
  77. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
  78. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
  79. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
  80. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
  81. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
  82. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
  83. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
  84. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
  85. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
  86. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
  87. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
  88. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
  89. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
  90. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
  91. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
  92. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
  93. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
  94. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
  95. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
  96. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
  97. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
  98. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
  99. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
  100. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
  101. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
  102. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
  103. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
  104. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
  105. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
  106. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
  107. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
  108. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
  109. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
  110. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
  111. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
  112. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
  113. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
  114. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
  115. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
  116. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
  117. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
  118. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
  119. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
  120. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
  121. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
  122. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
  123. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
  124. 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
+ }