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,183 @@
|
|
|
1
|
+
// src/tools/validation/index.ts
|
|
2
|
+
// MCP tool implementations for UE data validation and Blueprint compile-check (Phase 16).
|
|
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
|
+
import { z } from 'zod';
|
|
6
|
+
import { withKnownIssues } from '../known-issues/middleware.js';
|
|
7
|
+
import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
|
|
8
|
+
// Module-level bridge instance — injected in tests via exported handler signatures.
|
|
9
|
+
const bridge = new PluginBridgeClient();
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// sendOrDisconnect helper
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Sends a command to the bridge and returns a ToolResult.
|
|
15
|
+
*
|
|
16
|
+
* - On success (response.success true): returns data as JSON text.
|
|
17
|
+
* - On command-level failure (response.success false): returns isError:true with error JSON.
|
|
18
|
+
* - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
|
|
19
|
+
* - On other errors: rethrows (withKnownIssues catches and formats).
|
|
20
|
+
*
|
|
21
|
+
* NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
|
|
22
|
+
* passing an empty string is safe and correct (per STATE.md decision).
|
|
23
|
+
*/
|
|
24
|
+
async function sendOrDisconnect(b, cmd) {
|
|
25
|
+
try {
|
|
26
|
+
const response = await b.sendCommand({ ...cmd, correlationId: '' });
|
|
27
|
+
if (!response.success) {
|
|
28
|
+
return {
|
|
29
|
+
isError: true,
|
|
30
|
+
content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (err instanceof PluginNotConnectedError) {
|
|
39
|
+
return {
|
|
40
|
+
isError: true,
|
|
41
|
+
content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
throw err; // withKnownIssues catches unexpected errors
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Exported handler functions (for direct unit testing via bridge injection)
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
/**
|
|
51
|
+
* ue_validate_asset handler — satisfies VAL-01.
|
|
52
|
+
* Sends validate.asset to the plugin; returns structured pass/fail with error/warning counts.
|
|
53
|
+
*
|
|
54
|
+
* @param args Tool arguments including asset_path.
|
|
55
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
56
|
+
*/
|
|
57
|
+
export async function handleValidateAsset(args, b = bridge) {
|
|
58
|
+
// Response data shape: ValidationAssetResult
|
|
59
|
+
return sendOrDisconnect(b, {
|
|
60
|
+
type: 'validate.asset',
|
|
61
|
+
payload: { asset_path: args.asset_path },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* ue_validate_folder handler — satisfies VAL-02.
|
|
66
|
+
* Sends validate.folder to the plugin; returns per-folder summary with asset counts.
|
|
67
|
+
*
|
|
68
|
+
* @param args Tool arguments including folder_path.
|
|
69
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
70
|
+
*/
|
|
71
|
+
export async function handleValidateFolder(args, b = bridge) {
|
|
72
|
+
// Response data shape: ValidationFolderResult
|
|
73
|
+
return sendOrDisconnect(b, {
|
|
74
|
+
type: 'validate.folder',
|
|
75
|
+
payload: { folder_path: args.folder_path },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* ue_validate_project handler — satisfies VAL-03.
|
|
80
|
+
* Sends validate.project to the plugin; returns categorized project-wide counts.
|
|
81
|
+
*
|
|
82
|
+
* @param _args No input arguments (empty record).
|
|
83
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
84
|
+
*/
|
|
85
|
+
export async function handleValidateProject(_args, b = bridge) {
|
|
86
|
+
return sendOrDisconnect(b, { type: 'validate.project' });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* ue_check_blueprint handler — satisfies VAL-04.
|
|
90
|
+
* Sends validate.blueprint to the plugin; returns compiled:bool with status and errorMessage.
|
|
91
|
+
*
|
|
92
|
+
* @param args Tool arguments including asset_path.
|
|
93
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
94
|
+
*/
|
|
95
|
+
export async function handleCheckBlueprint(args, b = bridge) {
|
|
96
|
+
// Response data shape: BlueprintCompileResult
|
|
97
|
+
return sendOrDisconnect(b, {
|
|
98
|
+
type: 'validate.blueprint',
|
|
99
|
+
payload: { asset_path: args.asset_path },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// registerValidationTools
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
/**
|
|
106
|
+
* Register UE data validation and Blueprint compile-check tools on the MCP server.
|
|
107
|
+
*
|
|
108
|
+
* All tools in this domain require the MCPBridge editor plugin.
|
|
109
|
+
* When the plugin is not connected, each handler returns:
|
|
110
|
+
* { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
|
|
111
|
+
*
|
|
112
|
+
* Tools registered (Phase 16):
|
|
113
|
+
* ue_validate_asset — VAL-01: Run data validation on a single asset
|
|
114
|
+
* ue_validate_folder — VAL-02: Run data validation on all assets in a folder
|
|
115
|
+
* ue_validate_project — VAL-03: Run data validation across all /Game/ assets
|
|
116
|
+
* ue_check_blueprint — VAL-04: Compile-check a Blueprint asset
|
|
117
|
+
*
|
|
118
|
+
* @param server The McpServer instance to register tools on.
|
|
119
|
+
* @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
|
|
120
|
+
*/
|
|
121
|
+
export function registerValidationTools(server, bridge) {
|
|
122
|
+
const _bridge = bridge ?? new PluginBridgeClient();
|
|
123
|
+
// --------------------------------------------------------------------------
|
|
124
|
+
// ue_validate_asset (VAL-01)
|
|
125
|
+
// --------------------------------------------------------------------------
|
|
126
|
+
server.registerTool('ue_validate_asset', {
|
|
127
|
+
title: 'Validate UE Asset',
|
|
128
|
+
description: '[requires_plugin] Run UE data validation rules on a single asset and return structured pass/fail results with error and warning counts.',
|
|
129
|
+
inputSchema: z.object({
|
|
130
|
+
asset_path: z
|
|
131
|
+
.string()
|
|
132
|
+
.describe('UE long package path, e.g. /Game/Blueprints/BP_MyActor'),
|
|
133
|
+
}),
|
|
134
|
+
annotations: {
|
|
135
|
+
readOnlyHint: true,
|
|
136
|
+
destructiveHint: false,
|
|
137
|
+
},
|
|
138
|
+
}, withKnownIssues('ue_validate_asset', async (args) => handleValidateAsset(args, _bridge)));
|
|
139
|
+
// --------------------------------------------------------------------------
|
|
140
|
+
// ue_validate_folder (VAL-02)
|
|
141
|
+
// --------------------------------------------------------------------------
|
|
142
|
+
server.registerTool('ue_validate_folder', {
|
|
143
|
+
title: 'Validate UE Assets in Folder',
|
|
144
|
+
description: '[requires_plugin] Run UE data validation on all assets under a folder path and return a summary report.',
|
|
145
|
+
inputSchema: z.object({
|
|
146
|
+
folder_path: z
|
|
147
|
+
.string()
|
|
148
|
+
.describe('UE package path prefix, e.g. /Game/Blueprints'),
|
|
149
|
+
}),
|
|
150
|
+
annotations: {
|
|
151
|
+
readOnlyHint: true,
|
|
152
|
+
destructiveHint: false,
|
|
153
|
+
},
|
|
154
|
+
}, withKnownIssues('ue_validate_folder', async (args) => handleValidateFolder(args, _bridge)));
|
|
155
|
+
// --------------------------------------------------------------------------
|
|
156
|
+
// ue_validate_project (VAL-03)
|
|
157
|
+
// --------------------------------------------------------------------------
|
|
158
|
+
server.registerTool('ue_validate_project', {
|
|
159
|
+
title: 'Validate All Project Assets',
|
|
160
|
+
description: '[requires_plugin] Run UE data validation across all /Game/ assets and return categorized error, warning, and info counts.',
|
|
161
|
+
inputSchema: z.object({}),
|
|
162
|
+
annotations: {
|
|
163
|
+
readOnlyHint: true,
|
|
164
|
+
destructiveHint: false,
|
|
165
|
+
},
|
|
166
|
+
}, withKnownIssues('ue_validate_project', async (_args) => handleValidateProject({}, _bridge)));
|
|
167
|
+
// --------------------------------------------------------------------------
|
|
168
|
+
// ue_check_blueprint (VAL-04)
|
|
169
|
+
// --------------------------------------------------------------------------
|
|
170
|
+
server.registerTool('ue_check_blueprint', {
|
|
171
|
+
title: 'Check Blueprint Compilation',
|
|
172
|
+
description: '[requires_plugin] Compile-check a Blueprint asset and return structured compiler messages indicating success or failure.',
|
|
173
|
+
inputSchema: z.object({
|
|
174
|
+
asset_path: z
|
|
175
|
+
.string()
|
|
176
|
+
.describe('UE long package path to the Blueprint asset, e.g. /Game/Blueprints/BP_MyActor'),
|
|
177
|
+
}),
|
|
178
|
+
annotations: {
|
|
179
|
+
readOnlyHint: false,
|
|
180
|
+
destructiveHint: false,
|
|
181
|
+
},
|
|
182
|
+
}, withKnownIssues('ue_check_blueprint', async (args) => handleCheckBlueprint(args, _bridge)));
|
|
183
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// src/tools/viewport/index.ts
|
|
2
|
+
// MCP tool implementations for editor state, PIE control, and viewport operations (Phase 12).
|
|
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
|
+
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
|
+
// registerViewportTools
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
/**
|
|
49
|
+
* Register UE Editor viewport, PIE control, and editor state 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 12):
|
|
56
|
+
* ue_editor_state — Query selected actors, open assets, viewport camera
|
|
57
|
+
* ue_pie_start — Start a PIE session
|
|
58
|
+
* ue_pie_stop — Stop the active PIE session
|
|
59
|
+
* ue_pie_logs — Read PIE output log lines
|
|
60
|
+
* ue_pie_game_state — Query runtime game state during PIE
|
|
61
|
+
* ue_viewport_screenshot — Capture viewport as screenshot
|
|
62
|
+
* ue_viewport_camera — Control viewport camera position/rotation/FOV
|
|
63
|
+
* ue_viewport_render_mode — Switch viewport render mode
|
|
64
|
+
* ue_viewport_hires_screenshot — Take a high-resolution screenshot
|
|
65
|
+
*
|
|
66
|
+
* @param server The McpServer instance to register tools on.
|
|
67
|
+
* @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
|
|
68
|
+
*/
|
|
69
|
+
export function registerViewportTools(server, bridge) {
|
|
70
|
+
const _bridge = bridge ?? new PluginBridgeClient();
|
|
71
|
+
// --------------------------------------------------------------------------
|
|
72
|
+
// ue_editor_state
|
|
73
|
+
// --------------------------------------------------------------------------
|
|
74
|
+
server.registerTool('ue_editor_state', {
|
|
75
|
+
title: 'Query UE Editor State',
|
|
76
|
+
description: '[requires_plugin] Query the current UE Editor state — selected actors, open assets, and viewport camera position.',
|
|
77
|
+
inputSchema: z.object({}),
|
|
78
|
+
annotations: {
|
|
79
|
+
readOnlyHint: true,
|
|
80
|
+
destructiveHint: false,
|
|
81
|
+
},
|
|
82
|
+
}, withKnownIssues('ue_editor_state', async (_args) => {
|
|
83
|
+
return sendOrDisconnect(_bridge, { type: 'editor.state' });
|
|
84
|
+
}));
|
|
85
|
+
// --------------------------------------------------------------------------
|
|
86
|
+
// ue_pie_start
|
|
87
|
+
// --------------------------------------------------------------------------
|
|
88
|
+
server.registerTool('ue_pie_start', {
|
|
89
|
+
title: 'Start PIE Session',
|
|
90
|
+
description: '[requires_plugin] Start a Play In Editor (PIE) session.',
|
|
91
|
+
inputSchema: z.object({}),
|
|
92
|
+
annotations: {
|
|
93
|
+
readOnlyHint: false,
|
|
94
|
+
destructiveHint: false,
|
|
95
|
+
},
|
|
96
|
+
}, withKnownIssues('ue_pie_start', async (_args) => {
|
|
97
|
+
return sendOrDisconnect(_bridge, { type: 'pie.start' });
|
|
98
|
+
}));
|
|
99
|
+
// --------------------------------------------------------------------------
|
|
100
|
+
// ue_pie_stop
|
|
101
|
+
// --------------------------------------------------------------------------
|
|
102
|
+
server.registerTool('ue_pie_stop', {
|
|
103
|
+
title: 'Stop PIE Session',
|
|
104
|
+
description: '[requires_plugin] Stop the active Play In Editor (PIE) session.',
|
|
105
|
+
inputSchema: z.object({}),
|
|
106
|
+
annotations: {
|
|
107
|
+
readOnlyHint: false,
|
|
108
|
+
destructiveHint: false,
|
|
109
|
+
},
|
|
110
|
+
}, withKnownIssues('ue_pie_stop', async (_args) => {
|
|
111
|
+
return sendOrDisconnect(_bridge, { type: 'pie.stop' });
|
|
112
|
+
}));
|
|
113
|
+
// --------------------------------------------------------------------------
|
|
114
|
+
// ue_pie_logs
|
|
115
|
+
// --------------------------------------------------------------------------
|
|
116
|
+
server.registerTool('ue_pie_logs', {
|
|
117
|
+
title: 'Read PIE Logs',
|
|
118
|
+
description: '[requires_plugin] Read PIE output log lines captured since the PIE session started.',
|
|
119
|
+
inputSchema: z.object({
|
|
120
|
+
category_filter: z
|
|
121
|
+
.string()
|
|
122
|
+
.optional()
|
|
123
|
+
.describe('Filter log lines containing this string (case-insensitive, empty = all)'),
|
|
124
|
+
max_lines: z
|
|
125
|
+
.number()
|
|
126
|
+
.int()
|
|
127
|
+
.min(1)
|
|
128
|
+
.max(1000)
|
|
129
|
+
.optional()
|
|
130
|
+
.describe('Maximum log lines to return (default 100)'),
|
|
131
|
+
}),
|
|
132
|
+
annotations: {
|
|
133
|
+
readOnlyHint: true,
|
|
134
|
+
destructiveHint: false,
|
|
135
|
+
},
|
|
136
|
+
}, withKnownIssues('ue_pie_logs', async (args) => {
|
|
137
|
+
const payload = {};
|
|
138
|
+
if (args.category_filter)
|
|
139
|
+
payload['category_filter'] = args.category_filter;
|
|
140
|
+
if (args.max_lines !== undefined)
|
|
141
|
+
payload['max_lines'] = args.max_lines;
|
|
142
|
+
return sendOrDisconnect(_bridge, { type: 'pie.logs', payload });
|
|
143
|
+
}));
|
|
144
|
+
// --------------------------------------------------------------------------
|
|
145
|
+
// ue_pie_game_state
|
|
146
|
+
// --------------------------------------------------------------------------
|
|
147
|
+
server.registerTool('ue_pie_game_state', {
|
|
148
|
+
title: 'Query PIE Game State',
|
|
149
|
+
description: '[requires_plugin] Query runtime game state during an active PIE session — actor positions and active GameMode.',
|
|
150
|
+
inputSchema: z.object({}),
|
|
151
|
+
annotations: {
|
|
152
|
+
readOnlyHint: true,
|
|
153
|
+
destructiveHint: false,
|
|
154
|
+
},
|
|
155
|
+
}, withKnownIssues('ue_pie_game_state', async (_args) => {
|
|
156
|
+
return sendOrDisconnect(_bridge, { type: 'pie.gameState' });
|
|
157
|
+
}));
|
|
158
|
+
// --------------------------------------------------------------------------
|
|
159
|
+
// ue_viewport_screenshot
|
|
160
|
+
// --------------------------------------------------------------------------
|
|
161
|
+
server.registerTool('ue_viewport_screenshot', {
|
|
162
|
+
title: 'Take Viewport Screenshot',
|
|
163
|
+
description: '[requires_plugin] Capture the active UE viewport as a screenshot. Returns the screenshot directory path.',
|
|
164
|
+
inputSchema: z.object({
|
|
165
|
+
width: z
|
|
166
|
+
.number()
|
|
167
|
+
.int()
|
|
168
|
+
.min(64)
|
|
169
|
+
.max(7680)
|
|
170
|
+
.optional()
|
|
171
|
+
.describe('Screenshot width in pixels (default 1920)'),
|
|
172
|
+
height: z
|
|
173
|
+
.number()
|
|
174
|
+
.int()
|
|
175
|
+
.min(64)
|
|
176
|
+
.max(4320)
|
|
177
|
+
.optional()
|
|
178
|
+
.describe('Screenshot height in pixels (default 1080)'),
|
|
179
|
+
}),
|
|
180
|
+
annotations: {
|
|
181
|
+
readOnlyHint: true,
|
|
182
|
+
destructiveHint: false,
|
|
183
|
+
},
|
|
184
|
+
}, withKnownIssues('ue_viewport_screenshot', async (args) => {
|
|
185
|
+
const payload = {};
|
|
186
|
+
if (args.width !== undefined)
|
|
187
|
+
payload['width'] = args.width;
|
|
188
|
+
if (args.height !== undefined)
|
|
189
|
+
payload['height'] = args.height;
|
|
190
|
+
return sendOrDisconnect(_bridge, { type: 'viewport.screenshot', payload });
|
|
191
|
+
}));
|
|
192
|
+
// --------------------------------------------------------------------------
|
|
193
|
+
// ue_viewport_camera
|
|
194
|
+
// --------------------------------------------------------------------------
|
|
195
|
+
server.registerTool('ue_viewport_camera', {
|
|
196
|
+
title: 'Control Viewport Camera',
|
|
197
|
+
description: '[requires_plugin] Set the viewport camera position, rotation, FOV, or orbit around a target point.',
|
|
198
|
+
inputSchema: z.object({
|
|
199
|
+
location: z
|
|
200
|
+
.object({
|
|
201
|
+
x: z.number(),
|
|
202
|
+
y: z.number(),
|
|
203
|
+
z: z.number(),
|
|
204
|
+
})
|
|
205
|
+
.optional()
|
|
206
|
+
.describe('Camera world position in Unreal units (cm)'),
|
|
207
|
+
rotation: z
|
|
208
|
+
.object({
|
|
209
|
+
pitch: z.number(),
|
|
210
|
+
yaw: z.number(),
|
|
211
|
+
roll: z.number(),
|
|
212
|
+
})
|
|
213
|
+
.optional()
|
|
214
|
+
.describe('Camera rotation in degrees'),
|
|
215
|
+
fov: z
|
|
216
|
+
.number()
|
|
217
|
+
.min(5)
|
|
218
|
+
.max(170)
|
|
219
|
+
.optional()
|
|
220
|
+
.describe('Horizontal field of view in degrees'),
|
|
221
|
+
look_at: z
|
|
222
|
+
.object({
|
|
223
|
+
x: z.number(),
|
|
224
|
+
y: z.number(),
|
|
225
|
+
z: z.number(),
|
|
226
|
+
})
|
|
227
|
+
.optional()
|
|
228
|
+
.describe('Point to orbit/look at — overrides rotation when location is also provided'),
|
|
229
|
+
}),
|
|
230
|
+
annotations: {
|
|
231
|
+
readOnlyHint: false,
|
|
232
|
+
destructiveHint: false,
|
|
233
|
+
},
|
|
234
|
+
}, withKnownIssues('ue_viewport_camera', async (args) => {
|
|
235
|
+
const payload = {};
|
|
236
|
+
if (args.location !== undefined)
|
|
237
|
+
payload['location'] = args.location;
|
|
238
|
+
if (args.rotation !== undefined)
|
|
239
|
+
payload['rotation'] = args.rotation;
|
|
240
|
+
if (args.fov !== undefined)
|
|
241
|
+
payload['fov'] = args.fov;
|
|
242
|
+
if (args.look_at !== undefined)
|
|
243
|
+
payload['look_at'] = args.look_at;
|
|
244
|
+
return sendOrDisconnect(_bridge, { type: 'viewport.camera', payload });
|
|
245
|
+
}));
|
|
246
|
+
// --------------------------------------------------------------------------
|
|
247
|
+
// ue_viewport_render_mode
|
|
248
|
+
// --------------------------------------------------------------------------
|
|
249
|
+
server.registerTool('ue_viewport_render_mode', {
|
|
250
|
+
title: 'Switch Viewport Render Mode',
|
|
251
|
+
description: '[requires_plugin] Switch the active viewport render mode.',
|
|
252
|
+
inputSchema: z.object({
|
|
253
|
+
mode: z
|
|
254
|
+
.enum(['lit', 'unlit', 'wireframe', 'collision', 'detail_lighting'])
|
|
255
|
+
.describe('Render mode: lit (default), unlit, wireframe, collision, detail_lighting'),
|
|
256
|
+
}),
|
|
257
|
+
annotations: {
|
|
258
|
+
readOnlyHint: false,
|
|
259
|
+
destructiveHint: false,
|
|
260
|
+
},
|
|
261
|
+
}, withKnownIssues('ue_viewport_render_mode', async (args) => {
|
|
262
|
+
return sendOrDisconnect(_bridge, {
|
|
263
|
+
type: 'viewport.renderMode',
|
|
264
|
+
payload: { mode: args.mode },
|
|
265
|
+
});
|
|
266
|
+
}));
|
|
267
|
+
// --------------------------------------------------------------------------
|
|
268
|
+
// ue_viewport_hires_screenshot
|
|
269
|
+
// --------------------------------------------------------------------------
|
|
270
|
+
server.registerTool('ue_viewport_hires_screenshot', {
|
|
271
|
+
title: 'High-Resolution Viewport Screenshot',
|
|
272
|
+
description: '[requires_plugin] Take a high-resolution screenshot with configurable multiplier. Returns the screenshot directory path.',
|
|
273
|
+
inputSchema: z.object({
|
|
274
|
+
resolution_multiplier: z
|
|
275
|
+
.number()
|
|
276
|
+
.int()
|
|
277
|
+
.min(1)
|
|
278
|
+
.max(8)
|
|
279
|
+
.optional()
|
|
280
|
+
.describe('Resolution multiplier (default 2 = 2x base resolution)'),
|
|
281
|
+
width: z
|
|
282
|
+
.number()
|
|
283
|
+
.int()
|
|
284
|
+
.min(64)
|
|
285
|
+
.max(7680)
|
|
286
|
+
.optional()
|
|
287
|
+
.describe('Base width in pixels (default 1920)'),
|
|
288
|
+
height: z
|
|
289
|
+
.number()
|
|
290
|
+
.int()
|
|
291
|
+
.min(64)
|
|
292
|
+
.max(4320)
|
|
293
|
+
.optional()
|
|
294
|
+
.describe('Base height in pixels (default 1080)'),
|
|
295
|
+
}),
|
|
296
|
+
annotations: {
|
|
297
|
+
readOnlyHint: true,
|
|
298
|
+
destructiveHint: false,
|
|
299
|
+
},
|
|
300
|
+
}, withKnownIssues('ue_viewport_hires_screenshot', async (args) => {
|
|
301
|
+
const payload = {};
|
|
302
|
+
if (args.resolution_multiplier !== undefined)
|
|
303
|
+
payload['resolution_multiplier'] = args.resolution_multiplier;
|
|
304
|
+
if (args.width !== undefined)
|
|
305
|
+
payload['width'] = args.width;
|
|
306
|
+
if (args.height !== undefined)
|
|
307
|
+
payload['height'] = args.height;
|
|
308
|
+
return sendOrDisconnect(_bridge, { type: 'viewport.hiresScreenshot', payload });
|
|
309
|
+
}));
|
|
310
|
+
}
|