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
package/dist/setup.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// setup.ts — One-command installer for Ultimate Unreal Engine MCP.
|
|
3
|
+
// Automatically configures Claude Desktop, Claude Code, or Cursor to use this server.
|
|
4
|
+
//
|
|
5
|
+
// Usage:
|
|
6
|
+
// npx ultimate-unreal-engine-mcp setup
|
|
7
|
+
// npx ultimate-unreal-engine-mcp setup --project "C:/MyGame"
|
|
8
|
+
// npx ultimate-unreal-engine-mcp setup --client claude-code
|
|
9
|
+
// npx ultimate-unreal-engine-mcp setup --port 55557
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, cpSync } from 'node:fs';
|
|
11
|
+
import { join, resolve, dirname } from 'node:path';
|
|
12
|
+
import { platform, env } from 'node:process';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Helpers
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
function log(msg) {
|
|
17
|
+
console.error(msg);
|
|
18
|
+
}
|
|
19
|
+
function getConfigPath(client) {
|
|
20
|
+
const home = env['USERPROFILE'] || env['HOME'] || '';
|
|
21
|
+
switch (client) {
|
|
22
|
+
case 'claude-desktop':
|
|
23
|
+
if (platform === 'win32') {
|
|
24
|
+
return join(env['APPDATA'] || join(home, 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
|
|
25
|
+
}
|
|
26
|
+
return join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
27
|
+
case 'claude-code':
|
|
28
|
+
return join(home, '.claude', 'settings.json');
|
|
29
|
+
case 'cursor':
|
|
30
|
+
if (platform === 'win32') {
|
|
31
|
+
return join(env['APPDATA'] || join(home, 'AppData', 'Roaming'), 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'config.json');
|
|
32
|
+
}
|
|
33
|
+
return join(home, '.config', 'cursor', 'mcp.json');
|
|
34
|
+
default:
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function parseArgs(argv) {
|
|
39
|
+
let project = '';
|
|
40
|
+
let client = 'claude-desktop';
|
|
41
|
+
let port = 55557;
|
|
42
|
+
let pluginOnly = false;
|
|
43
|
+
for (let i = 0; i < argv.length; i++) {
|
|
44
|
+
if (argv[i] === '--project' && argv[i + 1]) {
|
|
45
|
+
project = argv[++i];
|
|
46
|
+
}
|
|
47
|
+
else if (argv[i] === '--client' && argv[i + 1]) {
|
|
48
|
+
client = argv[++i];
|
|
49
|
+
}
|
|
50
|
+
else if (argv[i] === '--port' && argv[i + 1]) {
|
|
51
|
+
port = parseInt(argv[++i], 10);
|
|
52
|
+
}
|
|
53
|
+
else if (argv[i] === '--plugin-only') {
|
|
54
|
+
pluginOnly = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { project, client, port, pluginOnly };
|
|
58
|
+
}
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Plugin installer
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
function installPlugin(projectRoot) {
|
|
63
|
+
const pluginSrc = join(dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')), '..', 'unreal-plugin', 'MCPBridge');
|
|
64
|
+
const pluginDest = join(projectRoot, 'Plugins', 'MCPBridge');
|
|
65
|
+
if (!existsSync(pluginSrc)) {
|
|
66
|
+
log(` Plugin source not found at ${pluginSrc}`);
|
|
67
|
+
log(' You may need to copy unreal-plugin/MCPBridge/ manually.');
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (existsSync(pluginDest)) {
|
|
71
|
+
log(` Plugin already exists at ${pluginDest} — skipping copy.`);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
mkdirSync(dirname(pluginDest), { recursive: true });
|
|
76
|
+
cpSync(pluginSrc, pluginDest, { recursive: true });
|
|
77
|
+
log(` Copied MCPBridge plugin to ${pluginDest}`);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
log(` Failed to copy plugin: ${err}`);
|
|
82
|
+
log(` Copy manually: cp -r "${pluginSrc}" "${pluginDest}"`);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Config writer
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
function configureClient(client, port, projectRoot) {
|
|
90
|
+
const configPath = getConfigPath(client);
|
|
91
|
+
if (!configPath) {
|
|
92
|
+
log(` Unknown client: ${client}`);
|
|
93
|
+
log(' Supported: claude-desktop, claude-code, cursor');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// For claude-code, we use the mcpServers format in settings.json
|
|
97
|
+
// For claude-desktop and cursor, we use the standard MCP config format
|
|
98
|
+
let config = {};
|
|
99
|
+
if (existsSync(configPath)) {
|
|
100
|
+
try {
|
|
101
|
+
config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
log(` Warning: Could not parse existing ${configPath}, creating fresh config.`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const serverEntry = {
|
|
108
|
+
command: 'npx',
|
|
109
|
+
args: ['-y', 'ultimate-unreal-engine-mcp'],
|
|
110
|
+
};
|
|
111
|
+
// Only add env if project root is specified
|
|
112
|
+
const envBlock = {};
|
|
113
|
+
if (projectRoot) {
|
|
114
|
+
envBlock['UE_PROJECT_ROOT'] = projectRoot;
|
|
115
|
+
}
|
|
116
|
+
if (port !== 55557) {
|
|
117
|
+
envBlock['UE_PLUGIN_PORT'] = String(port);
|
|
118
|
+
}
|
|
119
|
+
if (Object.keys(envBlock).length > 0) {
|
|
120
|
+
serverEntry['env'] = envBlock;
|
|
121
|
+
}
|
|
122
|
+
if (client === 'claude-code') {
|
|
123
|
+
// Claude Code uses settings.json with mcpServers at top level
|
|
124
|
+
if (!config['mcpServers'] || typeof config['mcpServers'] !== 'object') {
|
|
125
|
+
config['mcpServers'] = {};
|
|
126
|
+
}
|
|
127
|
+
config['mcpServers']['unreal-engine'] = serverEntry;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Claude Desktop and Cursor use the standard format
|
|
131
|
+
if (!config['mcpServers'] || typeof config['mcpServers'] !== 'object') {
|
|
132
|
+
config['mcpServers'] = {};
|
|
133
|
+
}
|
|
134
|
+
config['mcpServers']['unreal-engine'] = serverEntry;
|
|
135
|
+
}
|
|
136
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
137
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
138
|
+
log(` Configured ${client} at ${configPath}`);
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Main
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
function main() {
|
|
144
|
+
const args = parseArgs(process.argv.slice(2));
|
|
145
|
+
// Check if this is a "setup" invocation or just running the server
|
|
146
|
+
const command = process.argv[2];
|
|
147
|
+
if (command !== 'setup') {
|
|
148
|
+
// Not setup — just import and run the server
|
|
149
|
+
// This is handled by the bin entry pointing to dist/index.js
|
|
150
|
+
console.error('Usage: npx ultimate-unreal-engine-mcp setup [--project <path>] [--client <name>] [--port <num>]');
|
|
151
|
+
console.error('');
|
|
152
|
+
console.error('Options:');
|
|
153
|
+
console.error(' --project <path> Path to your UE project (optional, can set later via UE_PROJECT_ROOT)');
|
|
154
|
+
console.error(' --client <name> claude-desktop (default), claude-code, or cursor');
|
|
155
|
+
console.error(' --port <num> Plugin TCP port (default: 55557)');
|
|
156
|
+
console.error(' --plugin-only Only install the UE plugin, skip MCP config');
|
|
157
|
+
console.error('');
|
|
158
|
+
console.error('Examples:');
|
|
159
|
+
console.error(' npx ultimate-unreal-engine-mcp setup');
|
|
160
|
+
console.error(' npx ultimate-unreal-engine-mcp setup --project "C:/MyGame" --client claude-code');
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
log('');
|
|
164
|
+
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
165
|
+
log(' Ultimate Unreal Engine MCP — Setup');
|
|
166
|
+
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
167
|
+
log('');
|
|
168
|
+
// Step 1: Configure MCP client
|
|
169
|
+
if (!args.pluginOnly) {
|
|
170
|
+
log('Step 1: Configuring MCP client...');
|
|
171
|
+
configureClient(args.client, args.port, args.project);
|
|
172
|
+
log('');
|
|
173
|
+
}
|
|
174
|
+
// Step 2: Install UE plugin (if project path provided)
|
|
175
|
+
if (args.project) {
|
|
176
|
+
const projectRoot = resolve(args.project);
|
|
177
|
+
if (!existsSync(projectRoot)) {
|
|
178
|
+
log(`Step 2: Project path not found: ${projectRoot}`);
|
|
179
|
+
log(' Skipping plugin install. You can copy it manually later.');
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
log('Step 2: Installing UE plugin...');
|
|
183
|
+
installPlugin(projectRoot);
|
|
184
|
+
log('');
|
|
185
|
+
log(' After installing, regenerate project files and compile.');
|
|
186
|
+
log(' The plugin starts a TCP server on port ' + args.port + ' when the editor launches.');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
log('Step 2: No --project path specified.');
|
|
191
|
+
log(' To install the UE plugin later, copy unreal-plugin/MCPBridge/');
|
|
192
|
+
log(' into your UE project\'s Plugins/ folder.');
|
|
193
|
+
}
|
|
194
|
+
log('');
|
|
195
|
+
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
196
|
+
log(' Setup complete!');
|
|
197
|
+
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
198
|
+
log('');
|
|
199
|
+
log('Next steps:');
|
|
200
|
+
log(' 1. Restart ' + args.client);
|
|
201
|
+
log(' 2. Ask Claude: "List all Blueprint assets in my project"');
|
|
202
|
+
log('');
|
|
203
|
+
if (!args.project) {
|
|
204
|
+
log('Optional: Set your UE project path:');
|
|
205
|
+
log(' npx ultimate-unreal-engine-mcp setup --project "C:/path/to/MyGame"');
|
|
206
|
+
log('');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
main();
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// src/tools/ai-systems/index.ts
|
|
2
|
+
// MCP tool implementations for AI system asset inspection (Phase 22).
|
|
3
|
+
// All tools route commands through PluginBridgeClient to the C++ MCPBridge handlers.
|
|
4
|
+
// Returns structured plugin_not_connected errors when the plugin is absent.
|
|
5
|
+
//
|
|
6
|
+
// Tools registered (Phase 22 — AI-01 through AI-05):
|
|
7
|
+
// ue_inspect_behavior_tree — Inspect BT node hierarchy, decorators, and services
|
|
8
|
+
// ue_inspect_state_tree — Read State Tree states, transitions, and tasks
|
|
9
|
+
// ue_inspect_blackboard — List Blackboard keys with types and instance-sync status
|
|
10
|
+
// ue_inspect_eqs — Inspect EQS query templates: generators, tests, scoring
|
|
11
|
+
// ue_query_navmesh — Query NavMesh build status, bounds, and point reachability
|
|
12
|
+
//
|
|
13
|
+
// All tools require MCPBridge plugin (Phase 22 — AI-01 through AI-05).
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { withKnownIssues } from '../known-issues/middleware.js';
|
|
16
|
+
import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
|
|
17
|
+
// Module-level bridge instance — injected in tests via exported handler signatures.
|
|
18
|
+
const bridge = new PluginBridgeClient();
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// sendOrDisconnect helper
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
/**
|
|
23
|
+
* Sends a command to the bridge and returns a ToolResult.
|
|
24
|
+
*
|
|
25
|
+
* - On success (response.success true): returns data as JSON text.
|
|
26
|
+
* - On command-level failure (response.success false): returns isError:true with error JSON.
|
|
27
|
+
* - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
|
|
28
|
+
* - On other errors: rethrows (withKnownIssues catches and formats).
|
|
29
|
+
*
|
|
30
|
+
* NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
|
|
31
|
+
* passing an empty string is safe and correct (per STATE.md decision).
|
|
32
|
+
*/
|
|
33
|
+
async function sendOrDisconnect(b, cmd) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await b.sendCommand({ ...cmd, correlationId: '' });
|
|
36
|
+
if (!response.success) {
|
|
37
|
+
return {
|
|
38
|
+
isError: true,
|
|
39
|
+
content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (err instanceof PluginNotConnectedError) {
|
|
48
|
+
return {
|
|
49
|
+
isError: true,
|
|
50
|
+
content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
throw err; // withKnownIssues catches unexpected errors
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Exported handler functions (for direct unit testing via bridge injection)
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/**
|
|
60
|
+
* ue_inspect_behavior_tree handler — satisfies AI-01.
|
|
61
|
+
* Sends ai.behaviorTree to the plugin; returns node hierarchy, decorators, and services.
|
|
62
|
+
*
|
|
63
|
+
* @param args Tool arguments including asset_path.
|
|
64
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
65
|
+
*/
|
|
66
|
+
export async function handleInspectBehaviorTree(args, b = bridge) {
|
|
67
|
+
// Response data shape: BehaviorTreeInspectResult
|
|
68
|
+
return sendOrDisconnect(b, {
|
|
69
|
+
type: 'ai.behaviorTree',
|
|
70
|
+
payload: { asset_path: args.asset_path },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* ue_inspect_state_tree handler — satisfies AI-02.
|
|
75
|
+
* Sends ai.stateTree to the plugin; returns states, transitions, and tasks.
|
|
76
|
+
*
|
|
77
|
+
* @param args Tool arguments including asset_path.
|
|
78
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
79
|
+
*/
|
|
80
|
+
export async function handleInspectStateTree(args, b = bridge) {
|
|
81
|
+
// Response data shape: StateTreeInspectResult
|
|
82
|
+
return sendOrDisconnect(b, {
|
|
83
|
+
type: 'ai.stateTree',
|
|
84
|
+
payload: { asset_path: args.asset_path },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* ue_inspect_blackboard handler — satisfies AI-03.
|
|
89
|
+
* Sends ai.blackboard to the plugin; returns Blackboard keys with types and instance-sync status.
|
|
90
|
+
*
|
|
91
|
+
* @param args Tool arguments including asset_path.
|
|
92
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
93
|
+
*/
|
|
94
|
+
export async function handleInspectBlackboard(args, b = bridge) {
|
|
95
|
+
// Response data shape: BlackboardInspectResult
|
|
96
|
+
return sendOrDisconnect(b, {
|
|
97
|
+
type: 'ai.blackboard',
|
|
98
|
+
payload: { asset_path: args.asset_path },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* ue_inspect_eqs handler — satisfies AI-04.
|
|
103
|
+
* Sends ai.eqs to the plugin; returns EQS generator classes, tests, and scoring configuration.
|
|
104
|
+
*
|
|
105
|
+
* @param args Tool arguments including asset_path.
|
|
106
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
107
|
+
*/
|
|
108
|
+
export async function handleInspectEqs(args, b = bridge) {
|
|
109
|
+
// Response data shape: EQSInspectResult
|
|
110
|
+
return sendOrDisconnect(b, {
|
|
111
|
+
type: 'ai.eqs',
|
|
112
|
+
payload: { asset_path: args.asset_path },
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* ue_query_navmesh handler — satisfies AI-05.
|
|
117
|
+
* Sends ai.navmesh to the plugin; returns NavMesh build status, bounds, and optional
|
|
118
|
+
* point-to-point reachability when both start_point and end_point are provided.
|
|
119
|
+
*
|
|
120
|
+
* @param args Tool arguments including optional start_point and end_point.
|
|
121
|
+
* @param b PluginBridgeClient instance (defaults to module-level singleton).
|
|
122
|
+
*/
|
|
123
|
+
export async function handleQueryNavmesh(args, b = bridge) {
|
|
124
|
+
// Response data shape: NavMeshQueryResult
|
|
125
|
+
const payload = {};
|
|
126
|
+
if (args.start_point !== undefined && args.end_point !== undefined) {
|
|
127
|
+
payload['start_point'] = args.start_point;
|
|
128
|
+
payload['end_point'] = args.end_point;
|
|
129
|
+
}
|
|
130
|
+
return sendOrDisconnect(b, {
|
|
131
|
+
type: 'ai.navmesh',
|
|
132
|
+
payload,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// registerAISystemsTools
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
/**
|
|
139
|
+
* Register UE AI system inspection tools on the MCP server.
|
|
140
|
+
*
|
|
141
|
+
* All tools in this domain require the MCPBridge editor plugin.
|
|
142
|
+
* When the plugin is not connected, each handler returns:
|
|
143
|
+
* { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
|
|
144
|
+
*
|
|
145
|
+
* Tools registered (Phase 22):
|
|
146
|
+
* ue_inspect_behavior_tree — AI-01: Inspect BT node hierarchy with decorators and services
|
|
147
|
+
* ue_inspect_state_tree — AI-02: Read State Tree states, transitions, and tasks
|
|
148
|
+
* ue_inspect_blackboard — AI-03: List Blackboard keys with types and instance-sync status
|
|
149
|
+
* ue_inspect_eqs — AI-04: Inspect EQS query generators, tests, and scoring
|
|
150
|
+
* ue_query_navmesh — AI-05: Query NavMesh status, bounds, and reachability
|
|
151
|
+
*
|
|
152
|
+
* @param server The McpServer instance to register tools on.
|
|
153
|
+
* @param _bridge Optional PluginBridgeClient for testing (not used directly — handlers
|
|
154
|
+
* accept bridge injection via their exported function signatures).
|
|
155
|
+
*/
|
|
156
|
+
export function registerAISystemsTools(server, _bridge) {
|
|
157
|
+
const b = _bridge ?? new PluginBridgeClient();
|
|
158
|
+
// --------------------------------------------------------------------------
|
|
159
|
+
// ue_inspect_behavior_tree (AI-01)
|
|
160
|
+
// --------------------------------------------------------------------------
|
|
161
|
+
server.registerTool('ue_inspect_behavior_tree', {
|
|
162
|
+
title: 'Inspect Behavior Tree',
|
|
163
|
+
description: '[requires_plugin] Inspect a Behavior Tree asset\'s node hierarchy, decorators, and services.',
|
|
164
|
+
inputSchema: z.object({
|
|
165
|
+
asset_path: z
|
|
166
|
+
.string()
|
|
167
|
+
.min(1)
|
|
168
|
+
.describe('UE long package path to the Behavior Tree asset, e.g. /Game/AI/BT_EnemyLogic'),
|
|
169
|
+
}),
|
|
170
|
+
annotations: {
|
|
171
|
+
readOnlyHint: true,
|
|
172
|
+
destructiveHint: false,
|
|
173
|
+
},
|
|
174
|
+
}, withKnownIssues('ue_inspect_behavior_tree', async (args) => handleInspectBehaviorTree(args, b)));
|
|
175
|
+
// --------------------------------------------------------------------------
|
|
176
|
+
// ue_inspect_state_tree (AI-02)
|
|
177
|
+
// --------------------------------------------------------------------------
|
|
178
|
+
server.registerTool('ue_inspect_state_tree', {
|
|
179
|
+
title: 'Inspect State Tree',
|
|
180
|
+
description: '[requires_plugin] Read a State Tree\'s states, transitions, and tasks.',
|
|
181
|
+
inputSchema: z.object({
|
|
182
|
+
asset_path: z
|
|
183
|
+
.string()
|
|
184
|
+
.min(1)
|
|
185
|
+
.describe('UE long package path to the State Tree asset, e.g. /Game/AI/ST_NPCBehavior'),
|
|
186
|
+
}),
|
|
187
|
+
annotations: {
|
|
188
|
+
readOnlyHint: true,
|
|
189
|
+
destructiveHint: false,
|
|
190
|
+
},
|
|
191
|
+
}, withKnownIssues('ue_inspect_state_tree', async (args) => handleInspectStateTree(args, b)));
|
|
192
|
+
// --------------------------------------------------------------------------
|
|
193
|
+
// ue_inspect_blackboard (AI-03)
|
|
194
|
+
// --------------------------------------------------------------------------
|
|
195
|
+
server.registerTool('ue_inspect_blackboard', {
|
|
196
|
+
title: 'Inspect Blackboard',
|
|
197
|
+
description: '[requires_plugin] List all Blackboard keys with their types, default values, and instance-sync status.',
|
|
198
|
+
inputSchema: z.object({
|
|
199
|
+
asset_path: z
|
|
200
|
+
.string()
|
|
201
|
+
.min(1)
|
|
202
|
+
.describe('UE long package path to the Blackboard Data asset, e.g. /Game/AI/BB_EnemyData'),
|
|
203
|
+
}),
|
|
204
|
+
annotations: {
|
|
205
|
+
readOnlyHint: true,
|
|
206
|
+
destructiveHint: false,
|
|
207
|
+
},
|
|
208
|
+
}, withKnownIssues('ue_inspect_blackboard', async (args) => handleInspectBlackboard(args, b)));
|
|
209
|
+
// --------------------------------------------------------------------------
|
|
210
|
+
// ue_inspect_eqs (AI-04)
|
|
211
|
+
// --------------------------------------------------------------------------
|
|
212
|
+
server.registerTool('ue_inspect_eqs', {
|
|
213
|
+
title: 'Inspect EQS Query',
|
|
214
|
+
description: '[requires_plugin] Inspect an Environment Query System template\'s generators, tests, and scoring configuration.',
|
|
215
|
+
inputSchema: z.object({
|
|
216
|
+
asset_path: z
|
|
217
|
+
.string()
|
|
218
|
+
.min(1)
|
|
219
|
+
.describe('UE long package path to the EQS query asset, e.g. /Game/AI/EQS_FindCover'),
|
|
220
|
+
}),
|
|
221
|
+
annotations: {
|
|
222
|
+
readOnlyHint: true,
|
|
223
|
+
destructiveHint: false,
|
|
224
|
+
},
|
|
225
|
+
}, withKnownIssues('ue_inspect_eqs', async (args) => handleInspectEqs(args, b)));
|
|
226
|
+
// --------------------------------------------------------------------------
|
|
227
|
+
// ue_query_navmesh (AI-05)
|
|
228
|
+
// --------------------------------------------------------------------------
|
|
229
|
+
server.registerTool('ue_query_navmesh', {
|
|
230
|
+
title: 'Query NavMesh',
|
|
231
|
+
description: '[requires_plugin] Query NavMesh build status, bounds, and optionally test reachability between two points.',
|
|
232
|
+
inputSchema: z.object({
|
|
233
|
+
start_point: z
|
|
234
|
+
.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
235
|
+
.optional()
|
|
236
|
+
.describe('Optional start point for reachability test'),
|
|
237
|
+
end_point: z
|
|
238
|
+
.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
239
|
+
.optional()
|
|
240
|
+
.describe('Optional end point for reachability test'),
|
|
241
|
+
}),
|
|
242
|
+
annotations: {
|
|
243
|
+
readOnlyHint: true,
|
|
244
|
+
destructiveHint: false,
|
|
245
|
+
},
|
|
246
|
+
}, withKnownIssues('ue_query_navmesh', async (args) => handleQueryNavmesh(args, b)));
|
|
247
|
+
}
|