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,251 @@
1
+ // src/tools/docs/index.ts
2
+ // Registers UE documentation tools backed by DocIndex and the bundled UE 5.7 API data.
3
+ // All handlers are wrapped with withKnownIssues() per INF-06.
4
+ import { z } from 'zod';
5
+ import { withKnownIssues } from '../known-issues/middleware.js';
6
+ import { DocIndex } from '../../docs/doc-index.js';
7
+ import { UE57_API_RECORDS } from '../../docs/data/ue57-api.js';
8
+ // ---------------------------------------------------------------------------
9
+ // Module-level singleton — loaded once at import time.
10
+ // Avoids rebuilding the MiniSearch index on every tool call.
11
+ // ---------------------------------------------------------------------------
12
+ const docIndex = new DocIndex();
13
+ docIndex.load(UE57_API_RECORDS);
14
+ // ---------------------------------------------------------------------------
15
+ // Pure handler functions (exported for testing without MCP server)
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * ue_search_api handler logic — exported for direct testing.
19
+ * @param args Tool input args ({ query: string })
20
+ * @param index DocIndex to query (defaults to the module singleton)
21
+ */
22
+ export async function handleSearchApi(args, index = docIndex) {
23
+ const results = index.search(args.query, 20);
24
+ if (results.length === 0) {
25
+ return {
26
+ content: [
27
+ { type: 'text', text: `No results found for query: "${args.query}"` },
28
+ ],
29
+ };
30
+ }
31
+ const lines = [
32
+ `Search results for "${args.query}" (${results.length} result${results.length === 1 ? '' : 's'}):`,
33
+ ];
34
+ results.forEach((result, idx) => {
35
+ const r = result.record;
36
+ const scoreStr = result.score.toFixed(2);
37
+ lines.push('');
38
+ lines.push(`${idx + 1}. [${r.type}] ${r.fullName} (score: ${scoreStr})`);
39
+ if (r.module) {
40
+ if (r.type === 'class' || r.type === 'enum') {
41
+ lines.push(` Module: ${r.module} | Include: ${r.includePath}`);
42
+ }
43
+ else {
44
+ if (r.signature) {
45
+ lines.push(` Signature: ${r.signature}`);
46
+ }
47
+ lines.push(` Include: ${r.includePath}`);
48
+ }
49
+ }
50
+ if (r.description) {
51
+ lines.push(` ${r.description}`);
52
+ }
53
+ });
54
+ return {
55
+ content: [{ type: 'text', text: lines.join('\n') }],
56
+ };
57
+ }
58
+ /**
59
+ * ue_lookup_class handler logic — exported for direct testing.
60
+ * @param args Tool input args ({ class_name: string })
61
+ * @param index DocIndex to query (defaults to the module singleton)
62
+ */
63
+ export async function handleLookupClass(args, index = docIndex) {
64
+ const records = index.lookupClass(args.class_name);
65
+ if (records.length === 0) {
66
+ return {
67
+ content: [
68
+ {
69
+ type: 'text',
70
+ text: `Class not found: "${args.class_name}". Check spelling and case (e.g., UStaticMeshComponent).`,
71
+ },
72
+ ],
73
+ isError: true,
74
+ };
75
+ }
76
+ const classRecord = records.find((r) => r.type === 'class') ?? records[0];
77
+ const members = records.filter((r) => r.type !== 'class');
78
+ const lines = [
79
+ `Class: ${classRecord.className}`,
80
+ `Module: ${classRecord.module}`,
81
+ `Include: #include "${classRecord.includePath}"`,
82
+ `Description: ${classRecord.description}`,
83
+ ];
84
+ if (classRecord.deprecated) {
85
+ lines.push('');
86
+ lines.push(`[DEPRECATED] ${classRecord.deprecatedMessage}`);
87
+ if (classRecord.replacementAPI) {
88
+ lines.push(`Replacement: ${classRecord.replacementAPI}`);
89
+ }
90
+ }
91
+ lines.push('');
92
+ lines.push(`Members (${members.length}):`);
93
+ if (members.length === 0) {
94
+ lines.push(' (no indexed members)');
95
+ }
96
+ else {
97
+ for (const m of members) {
98
+ if (m.type === 'function') {
99
+ lines.push(` [function] ${m.name} — ${m.signature}`);
100
+ }
101
+ else if (m.type === 'property') {
102
+ lines.push(` [property] ${m.name}`);
103
+ }
104
+ else {
105
+ lines.push(` [${m.type}] ${m.name}`);
106
+ }
107
+ }
108
+ }
109
+ return {
110
+ content: [{ type: 'text', text: lines.join('\n') }],
111
+ };
112
+ }
113
+ /**
114
+ * ue_get_include_path handler logic — exported for direct testing.
115
+ * @param args Tool input args ({ class_name: string })
116
+ * @param index DocIndex to query (defaults to the module singleton)
117
+ */
118
+ export async function handleGetIncludePath(args, index = docIndex) {
119
+ const includePath = index.getIncludePath(args.class_name);
120
+ if (includePath === null) {
121
+ return {
122
+ content: [
123
+ {
124
+ type: 'text',
125
+ text: `No include path found for "${args.class_name}". Class not in UE 5.7 index.`,
126
+ },
127
+ ],
128
+ isError: true,
129
+ };
130
+ }
131
+ return {
132
+ content: [{ type: 'text', text: `#include "${includePath}"` }],
133
+ };
134
+ }
135
+ /**
136
+ * ue_check_deprecation handler logic — exported for direct testing.
137
+ * @param args Tool input args ({ symbol_name: string })
138
+ * @param index DocIndex to query (defaults to the module singleton)
139
+ */
140
+ export async function handleCheckDeprecation(args, index = docIndex) {
141
+ const result = index.checkDeprecation(args.symbol_name);
142
+ if (result === null) {
143
+ return {
144
+ content: [
145
+ {
146
+ type: 'text',
147
+ text: `Symbol not found: "${args.symbol_name}". Cannot check deprecation status.`,
148
+ },
149
+ ],
150
+ isError: true,
151
+ };
152
+ }
153
+ if (!result.deprecated) {
154
+ return {
155
+ content: [
156
+ {
157
+ type: 'text',
158
+ text: `"${args.symbol_name}" is NOT deprecated in UE 5.7. Safe to use.`,
159
+ },
160
+ ],
161
+ };
162
+ }
163
+ return {
164
+ content: [
165
+ {
166
+ type: 'text',
167
+ text: `"${args.symbol_name}" IS DEPRECATED in UE 5.7.\nReason: ${result.message}\nRecommended replacement: ${result.replacement}`,
168
+ },
169
+ ],
170
+ };
171
+ }
172
+ // ---------------------------------------------------------------------------
173
+ // Tool registration
174
+ // ---------------------------------------------------------------------------
175
+ /**
176
+ * Register UE documentation tools on the MCP server.
177
+ *
178
+ * Tools registered:
179
+ * ue_search_api — Full-text search across the UE API reference
180
+ * ue_lookup_class — Look up a specific UE class in the API docs
181
+ * ue_get_include_path — Get the correct #include path for a UE class
182
+ * ue_check_deprecation — Check if a UE symbol is deprecated in UE 5.7
183
+ *
184
+ * @param server The McpServer instance to register tools on.
185
+ */
186
+ export function registerDocsTools(server) {
187
+ // --------------------------------------------------------------------------
188
+ // ue_search_api
189
+ // --------------------------------------------------------------------------
190
+ server.registerTool('ue_search_api', {
191
+ title: 'Search UE API',
192
+ description: 'Search the Unreal Engine 5.7 API reference documentation for classes, functions, and concepts.',
193
+ inputSchema: z.object({
194
+ query: z.string().describe('Search query (e.g., "UStaticMeshComponent collision")'),
195
+ }),
196
+ annotations: {
197
+ readOnlyHint: true,
198
+ destructiveHint: false,
199
+ },
200
+ }, withKnownIssues('ue_search_api', async (args) => {
201
+ return handleSearchApi(args);
202
+ }));
203
+ // --------------------------------------------------------------------------
204
+ // ue_lookup_class
205
+ // --------------------------------------------------------------------------
206
+ server.registerTool('ue_lookup_class', {
207
+ title: 'Lookup UE Class',
208
+ description: 'Look up a specific Unreal Engine class in the API docs, returning its description, hierarchy, and key methods.',
209
+ inputSchema: z.object({
210
+ class_name: z.string().describe('The UE class name (e.g., UStaticMeshComponent)'),
211
+ }),
212
+ annotations: {
213
+ readOnlyHint: true,
214
+ destructiveHint: false,
215
+ },
216
+ }, withKnownIssues('ue_lookup_class', async (args) => {
217
+ return handleLookupClass(args);
218
+ }));
219
+ // --------------------------------------------------------------------------
220
+ // ue_get_include_path
221
+ // --------------------------------------------------------------------------
222
+ server.registerTool('ue_get_include_path', {
223
+ title: 'Get UE Include Path',
224
+ description: 'Get the correct #include path for a Unreal Engine class (e.g., #include "Components/StaticMeshComponent.h").',
225
+ inputSchema: z.object({
226
+ class_name: z.string().describe('The UE class name to find the include path for'),
227
+ }),
228
+ annotations: {
229
+ readOnlyHint: true,
230
+ destructiveHint: false,
231
+ },
232
+ }, withKnownIssues('ue_get_include_path', async (args) => {
233
+ return handleGetIncludePath(args);
234
+ }));
235
+ // --------------------------------------------------------------------------
236
+ // ue_check_deprecation
237
+ // --------------------------------------------------------------------------
238
+ server.registerTool('ue_check_deprecation', {
239
+ title: 'Check UE Symbol Deprecation',
240
+ description: 'Check whether a UE 5.7 symbol (class, function, or macro) is deprecated and what the recommended replacement is.',
241
+ inputSchema: z.object({
242
+ symbol_name: z.string().describe('The UE symbol name to check for deprecation'),
243
+ }),
244
+ annotations: {
245
+ readOnlyHint: true,
246
+ destructiveHint: false,
247
+ },
248
+ }, withKnownIssues('ue_check_deprecation', async (args) => {
249
+ return handleCheckDeprecation(args);
250
+ }));
251
+ }
@@ -0,0 +1,242 @@
1
+ // src/tools/editor/index.ts
2
+ // Real tool implementations for UE Editor actor/asset operations (Phase 9).
3
+ // All tools route commands through PluginBridgeClient to the C++ MCPBridge plugin.
4
+ // Returns structured plugin_not_connected errors (not crashes) when plugin is absent.
5
+ import { z } from 'zod';
6
+ import { withKnownIssues } from '../known-issues/middleware.js';
7
+ import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
8
+ // ---------------------------------------------------------------------------
9
+ // sendOrDisconnect helper
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Sends a command to the bridge and returns a ToolResult.
13
+ *
14
+ * - On success (response.success true): returns data as JSON text.
15
+ * - On command-level failure (response.success false): returns isError:true with error JSON.
16
+ * - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
17
+ * - On other errors: rethrows (withKnownIssues catches and formats).
18
+ *
19
+ * NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
20
+ * passing an empty string is safe and correct (per STATE.md decision).
21
+ */
22
+ async function sendOrDisconnect(bridge, cmd) {
23
+ try {
24
+ const response = await bridge.sendCommand({ ...cmd, correlationId: '' });
25
+ if (!response.success) {
26
+ return {
27
+ isError: true,
28
+ content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
29
+ };
30
+ }
31
+ return {
32
+ content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
33
+ };
34
+ }
35
+ catch (err) {
36
+ if (err instanceof PluginNotConnectedError) {
37
+ return {
38
+ isError: true,
39
+ content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
40
+ };
41
+ }
42
+ throw err; // withKnownIssues catches unexpected errors
43
+ }
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // registerEditorTools
47
+ // ---------------------------------------------------------------------------
48
+ /**
49
+ * Register UE Editor actor/asset tools on the MCP server.
50
+ *
51
+ * All tools in this domain require the MCPBridge editor plugin.
52
+ * When the plugin is not connected, each handler returns:
53
+ * { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
54
+ *
55
+ * Tools registered (Phase 9 — real implementations):
56
+ * ue_list_actors — List actors in the current level
57
+ * ue_spawn_actor — Spawn a new actor in the level
58
+ * ue_transform_actor — Move, rotate, and/or scale an existing actor
59
+ * ue_delete_actor — Delete an actor from the level
60
+ * ue_query_assets — Query the UE asset registry
61
+ * ue_trace_references — Trace asset reference graph
62
+ * ue_read_level_layout — Read the open level structure
63
+ *
64
+ * @param server The McpServer instance to register tools on.
65
+ * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
66
+ */
67
+ export function registerEditorTools(server, bridge) {
68
+ const _bridge = bridge ?? new PluginBridgeClient();
69
+ // --------------------------------------------------------------------------
70
+ // ue_list_actors
71
+ // --------------------------------------------------------------------------
72
+ server.registerTool('ue_list_actors', {
73
+ title: 'List UE Actors',
74
+ description: '[requires_plugin] List all actors in the currently open UE Editor level.',
75
+ inputSchema: z.object({}),
76
+ annotations: {
77
+ readOnlyHint: true,
78
+ destructiveHint: false,
79
+ },
80
+ }, withKnownIssues('ue_list_actors', async (_args) => {
81
+ return sendOrDisconnect(_bridge, { type: 'actor.list' });
82
+ }));
83
+ // --------------------------------------------------------------------------
84
+ // ue_spawn_actor
85
+ // --------------------------------------------------------------------------
86
+ server.registerTool('ue_spawn_actor', {
87
+ title: 'Spawn UE Actor',
88
+ description: '[requires_plugin] Spawn a new actor in the currently open UE Editor level at the specified location.',
89
+ inputSchema: z.object({
90
+ class_name: z.string().describe('The actor class to spawn (e.g., AStaticMeshActor)'),
91
+ location: z.object({
92
+ x: z.number().describe('X coordinate in Unreal units (cm)'),
93
+ y: z.number().describe('Y coordinate in Unreal units (cm)'),
94
+ z: z.number().describe('Z coordinate in Unreal units (cm)'),
95
+ }).describe('World-space location to spawn the actor at'),
96
+ }),
97
+ annotations: {
98
+ readOnlyHint: false,
99
+ destructiveHint: false,
100
+ },
101
+ }, withKnownIssues('ue_spawn_actor', async (args) => {
102
+ return sendOrDisconnect(_bridge, {
103
+ type: 'actor.spawn',
104
+ payload: { class_name: args.class_name, location: args.location },
105
+ });
106
+ }));
107
+ // --------------------------------------------------------------------------
108
+ // ue_transform_actor (Phase 9 — adds scale field; renamed from Phase 1 stub)
109
+ // --------------------------------------------------------------------------
110
+ server.registerTool('ue_transform_actor', {
111
+ title: 'Transform UE Actor',
112
+ description: '[requires_plugin] Move, rotate, and/or scale an existing actor in the currently open UE Editor level.',
113
+ inputSchema: z.object({
114
+ actor_label: z.string().describe('The editor label of the actor to transform'),
115
+ location: z
116
+ .object({
117
+ x: z.number().describe('X coordinate in Unreal units (cm)'),
118
+ y: z.number().describe('Y coordinate in Unreal units (cm)'),
119
+ z: z.number().describe('Z coordinate in Unreal units (cm)'),
120
+ })
121
+ .optional()
122
+ .describe('New world-space location (optional — omit to keep current location)'),
123
+ rotation: z
124
+ .object({
125
+ pitch: z.number().describe('Pitch angle in degrees'),
126
+ yaw: z.number().describe('Yaw angle in degrees'),
127
+ roll: z.number().describe('Roll angle in degrees'),
128
+ })
129
+ .optional()
130
+ .describe('New rotation in degrees (optional — omit to keep current rotation)'),
131
+ scale: z
132
+ .object({
133
+ x: z.number().describe('X scale factor'),
134
+ y: z.number().describe('Y scale factor'),
135
+ z: z.number().describe('Z scale factor'),
136
+ })
137
+ .optional()
138
+ .describe('New 3D scale (optional, default 1.0 per axis)'),
139
+ }),
140
+ annotations: {
141
+ readOnlyHint: false,
142
+ destructiveHint: false,
143
+ },
144
+ }, withKnownIssues('ue_transform_actor', async (args) => {
145
+ const payload = { actor_label: args.actor_label };
146
+ if (args.location)
147
+ payload['location'] = args.location;
148
+ if (args.rotation)
149
+ payload['rotation'] = args.rotation;
150
+ if (args.scale)
151
+ payload['scale'] = args.scale;
152
+ return sendOrDisconnect(_bridge, { type: 'actor.transform', payload });
153
+ }));
154
+ // --------------------------------------------------------------------------
155
+ // ue_delete_actor
156
+ // --------------------------------------------------------------------------
157
+ server.registerTool('ue_delete_actor', {
158
+ title: 'Delete UE Actor',
159
+ description: '[requires_plugin] Delete an actor from the currently open UE Editor level. This operation is destructive.',
160
+ inputSchema: z.object({
161
+ actor_label: z.string().describe('The editor label of the actor to delete'),
162
+ }),
163
+ annotations: {
164
+ readOnlyHint: false,
165
+ destructiveHint: true,
166
+ },
167
+ }, withKnownIssues('ue_delete_actor', async (args) => {
168
+ return sendOrDisconnect(_bridge, {
169
+ type: 'actor.delete',
170
+ payload: { actor_label: args.actor_label },
171
+ });
172
+ }));
173
+ // --------------------------------------------------------------------------
174
+ // ue_query_assets
175
+ // --------------------------------------------------------------------------
176
+ server.registerTool('ue_query_assets', {
177
+ title: 'Query UE Assets',
178
+ description: '[requires_plugin] Query the UE asset registry for assets matching optional filters (type, path prefix, tag).',
179
+ inputSchema: z.object({
180
+ asset_type: z
181
+ .string()
182
+ .optional()
183
+ .describe('Asset class name to filter by (e.g., StaticMesh, Blueprint)'),
184
+ path_prefix: z
185
+ .string()
186
+ .optional()
187
+ .describe('Asset path prefix to filter by (e.g., /Game/Meshes/)'),
188
+ tag: z
189
+ .string()
190
+ .optional()
191
+ .describe('Asset tag key to filter by'),
192
+ }),
193
+ annotations: {
194
+ readOnlyHint: true,
195
+ destructiveHint: false,
196
+ },
197
+ }, withKnownIssues('ue_query_assets', async (args) => {
198
+ // Map TS-friendly names to the C++ handler's expected field names.
199
+ // C++ expects 'asset_class' (not 'asset_type') and 'tag_key' (not 'tag').
200
+ const payload = {};
201
+ if (args.asset_type)
202
+ payload['asset_class'] = args.asset_type;
203
+ if (args.path_prefix)
204
+ payload['path_prefix'] = args.path_prefix;
205
+ if (args.tag)
206
+ payload['tag_key'] = args.tag;
207
+ return sendOrDisconnect(_bridge, { type: 'asset.query', payload });
208
+ }));
209
+ // --------------------------------------------------------------------------
210
+ // ue_trace_references (new tool — EDT-06)
211
+ // --------------------------------------------------------------------------
212
+ server.registerTool('ue_trace_references', {
213
+ title: 'Trace UE Asset References',
214
+ description: '[requires_plugin] Trace the reference graph for a given asset — what it depends on and what depends on it.',
215
+ inputSchema: z.object({
216
+ package_path: z.string().describe('Asset package path (e.g. /Game/Meshes/SM_Rock)'),
217
+ }),
218
+ annotations: {
219
+ readOnlyHint: true,
220
+ destructiveHint: false,
221
+ },
222
+ }, withKnownIssues('ue_trace_references', async (args) => {
223
+ return sendOrDisconnect(_bridge, {
224
+ type: 'asset.references',
225
+ payload: { package_path: args.package_path },
226
+ });
227
+ }));
228
+ // --------------------------------------------------------------------------
229
+ // ue_read_level_layout (new tool — EDT-07)
230
+ // --------------------------------------------------------------------------
231
+ server.registerTool('ue_read_level_layout', {
232
+ title: 'Read UE Level Layout',
233
+ description: '[requires_plugin] Read the open level structure — placed actors, streaming sublevels, and world partition status.',
234
+ inputSchema: z.object({}),
235
+ annotations: {
236
+ readOnlyHint: true,
237
+ destructiveHint: false,
238
+ },
239
+ }, withKnownIssues('ue_read_level_layout', async (_args) => {
240
+ return sendOrDisconnect(_bridge, { type: 'level.layout' });
241
+ }));
242
+ }