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,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
|
+
}
|