unreal-engine-mcp-server 0.2.1
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/.dockerignore +57 -0
- package/.env.production +25 -0
- package/.eslintrc.json +54 -0
- package/.github/workflows/publish-mcp.yml +75 -0
- package/Dockerfile +54 -0
- package/LICENSE +21 -0
- package/Public/icon.png +0 -0
- package/README.md +209 -0
- package/claude_desktop_config_example.json +13 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +7 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +484 -0
- package/dist/prompts/index.d.ts +14 -0
- package/dist/prompts/index.js +38 -0
- package/dist/python-utils.d.ts +29 -0
- package/dist/python-utils.js +54 -0
- package/dist/resources/actors.d.ts +13 -0
- package/dist/resources/actors.js +83 -0
- package/dist/resources/assets.d.ts +23 -0
- package/dist/resources/assets.js +245 -0
- package/dist/resources/levels.d.ts +17 -0
- package/dist/resources/levels.js +94 -0
- package/dist/tools/actors.d.ts +51 -0
- package/dist/tools/actors.js +459 -0
- package/dist/tools/animation.d.ts +196 -0
- package/dist/tools/animation.js +579 -0
- package/dist/tools/assets.d.ts +21 -0
- package/dist/tools/assets.js +304 -0
- package/dist/tools/audio.d.ts +170 -0
- package/dist/tools/audio.js +416 -0
- package/dist/tools/blueprint.d.ts +144 -0
- package/dist/tools/blueprint.js +652 -0
- package/dist/tools/build_environment_advanced.d.ts +66 -0
- package/dist/tools/build_environment_advanced.js +484 -0
- package/dist/tools/consolidated-tool-definitions.d.ts +2598 -0
- package/dist/tools/consolidated-tool-definitions.js +607 -0
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -0
- package/dist/tools/consolidated-tool-handlers.js +1050 -0
- package/dist/tools/debug.d.ts +185 -0
- package/dist/tools/debug.js +265 -0
- package/dist/tools/editor.d.ts +88 -0
- package/dist/tools/editor.js +365 -0
- package/dist/tools/engine.d.ts +30 -0
- package/dist/tools/engine.js +36 -0
- package/dist/tools/foliage.d.ts +155 -0
- package/dist/tools/foliage.js +525 -0
- package/dist/tools/introspection.d.ts +98 -0
- package/dist/tools/introspection.js +683 -0
- package/dist/tools/landscape.d.ts +158 -0
- package/dist/tools/landscape.js +375 -0
- package/dist/tools/level.d.ts +110 -0
- package/dist/tools/level.js +362 -0
- package/dist/tools/lighting.d.ts +159 -0
- package/dist/tools/lighting.js +1179 -0
- package/dist/tools/materials.d.ts +34 -0
- package/dist/tools/materials.js +146 -0
- package/dist/tools/niagara.d.ts +145 -0
- package/dist/tools/niagara.js +289 -0
- package/dist/tools/performance.d.ts +163 -0
- package/dist/tools/performance.js +412 -0
- package/dist/tools/physics.d.ts +189 -0
- package/dist/tools/physics.js +784 -0
- package/dist/tools/rc.d.ts +110 -0
- package/dist/tools/rc.js +363 -0
- package/dist/tools/sequence.d.ts +112 -0
- package/dist/tools/sequence.js +675 -0
- package/dist/tools/tool-definitions.d.ts +4919 -0
- package/dist/tools/tool-definitions.js +891 -0
- package/dist/tools/tool-handlers.d.ts +47 -0
- package/dist/tools/tool-handlers.js +830 -0
- package/dist/tools/ui.d.ts +171 -0
- package/dist/tools/ui.js +337 -0
- package/dist/tools/visual.d.ts +29 -0
- package/dist/tools/visual.js +67 -0
- package/dist/types/env.d.ts +10 -0
- package/dist/types/env.js +18 -0
- package/dist/types/index.d.ts +323 -0
- package/dist/types/index.js +28 -0
- package/dist/types/tool-types.d.ts +274 -0
- package/dist/types/tool-types.js +13 -0
- package/dist/unreal-bridge.d.ts +126 -0
- package/dist/unreal-bridge.js +992 -0
- package/dist/utils/cache-manager.d.ts +64 -0
- package/dist/utils/cache-manager.js +176 -0
- package/dist/utils/error-handler.d.ts +66 -0
- package/dist/utils/error-handler.js +243 -0
- package/dist/utils/errors.d.ts +133 -0
- package/dist/utils/errors.js +256 -0
- package/dist/utils/http.d.ts +26 -0
- package/dist/utils/http.js +135 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.js +32 -0
- package/dist/utils/normalize.d.ts +17 -0
- package/dist/utils/normalize.js +49 -0
- package/dist/utils/response-validator.d.ts +34 -0
- package/dist/utils/response-validator.js +121 -0
- package/dist/utils/safe-json.d.ts +4 -0
- package/dist/utils/safe-json.js +97 -0
- package/dist/utils/stdio-redirect.d.ts +2 -0
- package/dist/utils/stdio-redirect.js +20 -0
- package/dist/utils/validation.d.ts +50 -0
- package/dist/utils/validation.js +173 -0
- package/mcp-config-example.json +14 -0
- package/package.json +63 -0
- package/server.json +60 -0
- package/src/cli.ts +7 -0
- package/src/index.ts +543 -0
- package/src/prompts/index.ts +51 -0
- package/src/python/editor_compat.py +181 -0
- package/src/python-utils.ts +57 -0
- package/src/resources/actors.ts +92 -0
- package/src/resources/assets.ts +251 -0
- package/src/resources/levels.ts +83 -0
- package/src/tools/actors.ts +480 -0
- package/src/tools/animation.ts +713 -0
- package/src/tools/assets.ts +305 -0
- package/src/tools/audio.ts +548 -0
- package/src/tools/blueprint.ts +736 -0
- package/src/tools/build_environment_advanced.ts +526 -0
- package/src/tools/consolidated-tool-definitions.ts +619 -0
- package/src/tools/consolidated-tool-handlers.ts +1093 -0
- package/src/tools/debug.ts +368 -0
- package/src/tools/editor.ts +360 -0
- package/src/tools/engine.ts +32 -0
- package/src/tools/foliage.ts +652 -0
- package/src/tools/introspection.ts +778 -0
- package/src/tools/landscape.ts +523 -0
- package/src/tools/level.ts +410 -0
- package/src/tools/lighting.ts +1316 -0
- package/src/tools/materials.ts +148 -0
- package/src/tools/niagara.ts +312 -0
- package/src/tools/performance.ts +549 -0
- package/src/tools/physics.ts +924 -0
- package/src/tools/rc.ts +437 -0
- package/src/tools/sequence.ts +791 -0
- package/src/tools/tool-definitions.ts +907 -0
- package/src/tools/tool-handlers.ts +941 -0
- package/src/tools/ui.ts +499 -0
- package/src/tools/visual.ts +60 -0
- package/src/types/env.ts +27 -0
- package/src/types/index.ts +414 -0
- package/src/types/tool-types.ts +343 -0
- package/src/unreal-bridge.ts +1118 -0
- package/src/utils/cache-manager.ts +213 -0
- package/src/utils/error-handler.ts +320 -0
- package/src/utils/errors.ts +312 -0
- package/src/utils/http.ts +184 -0
- package/src/utils/logger.ts +30 -0
- package/src/utils/normalize.ts +54 -0
- package/src/utils/response-validator.ts +145 -0
- package/src/utils/safe-json.ts +112 -0
- package/src/utils/stdio-redirect.ts +18 -0
- package/src/utils/validation.ts +212 -0
- package/tsconfig.json +33 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { Logger } from './utils/logger.js';
|
|
5
|
+
import { UnrealBridge } from './unreal-bridge.js';
|
|
6
|
+
import { AssetResources } from './resources/assets.js';
|
|
7
|
+
import { ActorResources } from './resources/actors.js';
|
|
8
|
+
import { LevelResources } from './resources/levels.js';
|
|
9
|
+
import { ActorTools } from './tools/actors.js';
|
|
10
|
+
import { AssetTools } from './tools/assets.js';
|
|
11
|
+
import { EditorTools } from './tools/editor.js';
|
|
12
|
+
import { MaterialTools } from './tools/materials.js';
|
|
13
|
+
import { AnimationTools } from './tools/animation.js';
|
|
14
|
+
import { PhysicsTools } from './tools/physics.js';
|
|
15
|
+
import { NiagaraTools } from './tools/niagara.js';
|
|
16
|
+
import { BlueprintTools } from './tools/blueprint.js';
|
|
17
|
+
import { LevelTools } from './tools/level.js';
|
|
18
|
+
import { LightingTools } from './tools/lighting.js';
|
|
19
|
+
import { LandscapeTools } from './tools/landscape.js';
|
|
20
|
+
import { FoliageTools } from './tools/foliage.js';
|
|
21
|
+
import { DebugVisualizationTools } from './tools/debug.js';
|
|
22
|
+
import { PerformanceTools } from './tools/performance.js';
|
|
23
|
+
import { AudioTools } from './tools/audio.js';
|
|
24
|
+
import { UITools } from './tools/ui.js';
|
|
25
|
+
import { RcTools } from './tools/rc.js';
|
|
26
|
+
import { SequenceTools } from './tools/sequence.js';
|
|
27
|
+
import { IntrospectionTools } from './tools/introspection.js';
|
|
28
|
+
import { VisualTools } from './tools/visual.js';
|
|
29
|
+
import { EngineTools } from './tools/engine.js';
|
|
30
|
+
import { toolDefinitions } from './tools/tool-definitions.js';
|
|
31
|
+
import { handleToolCall } from './tools/tool-handlers.js';
|
|
32
|
+
import { consolidatedToolDefinitions } from './tools/consolidated-tool-definitions.js';
|
|
33
|
+
import { handleConsolidatedToolCall } from './tools/consolidated-tool-handlers.js';
|
|
34
|
+
import { prompts } from './prompts/index.js';
|
|
35
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
36
|
+
import { responseValidator } from './utils/response-validator.js';
|
|
37
|
+
import { ErrorHandler } from './utils/error-handler.js';
|
|
38
|
+
import { routeStdoutLogsToStderr } from './utils/stdio-redirect.js';
|
|
39
|
+
import { cleanObject } from './utils/safe-json.js';
|
|
40
|
+
const log = new Logger('UE-MCP');
|
|
41
|
+
// Ensure stdout remains JSON-only for MCP by routing logs to stderr unless opted out.
|
|
42
|
+
routeStdoutLogsToStderr();
|
|
43
|
+
const metrics = {
|
|
44
|
+
totalRequests: 0,
|
|
45
|
+
successfulRequests: 0,
|
|
46
|
+
failedRequests: 0,
|
|
47
|
+
averageResponseTime: 0,
|
|
48
|
+
responseTimes: [],
|
|
49
|
+
connectionStatus: 'disconnected',
|
|
50
|
+
lastHealthCheck: new Date(),
|
|
51
|
+
uptime: Date.now(),
|
|
52
|
+
recentErrors: []
|
|
53
|
+
};
|
|
54
|
+
// Configuration
|
|
55
|
+
const CONFIG = {
|
|
56
|
+
// Tool mode: true = consolidated (10 tools), false = individual (36+ tools)
|
|
57
|
+
USE_CONSOLIDATED_TOOLS: process.env.USE_CONSOLIDATED_TOOLS !== 'false',
|
|
58
|
+
// Connection retry settings
|
|
59
|
+
MAX_RETRY_ATTEMPTS: 3,
|
|
60
|
+
RETRY_DELAY_MS: 2000,
|
|
61
|
+
// Server info
|
|
62
|
+
SERVER_NAME: 'unreal-engine-mcp',
|
|
63
|
+
SERVER_VERSION: '0.2.1',
|
|
64
|
+
// Monitoring
|
|
65
|
+
HEALTH_CHECK_INTERVAL_MS: 30000 // 30 seconds
|
|
66
|
+
};
|
|
67
|
+
// Helper function to track performance
|
|
68
|
+
function trackPerformance(startTime, success) {
|
|
69
|
+
const responseTime = Date.now() - startTime;
|
|
70
|
+
metrics.totalRequests++;
|
|
71
|
+
if (success) {
|
|
72
|
+
metrics.successfulRequests++;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
metrics.failedRequests++;
|
|
76
|
+
}
|
|
77
|
+
// Keep last 100 response times for average calculation
|
|
78
|
+
metrics.responseTimes.push(responseTime);
|
|
79
|
+
if (metrics.responseTimes.length > 100) {
|
|
80
|
+
metrics.responseTimes.shift();
|
|
81
|
+
}
|
|
82
|
+
// Calculate average
|
|
83
|
+
metrics.averageResponseTime = metrics.responseTimes.reduce((a, b) => a + b, 0) / metrics.responseTimes.length;
|
|
84
|
+
}
|
|
85
|
+
// Health check function
|
|
86
|
+
async function performHealthCheck(bridge) {
|
|
87
|
+
try {
|
|
88
|
+
// Use a safe echo command that doesn't affect any settings
|
|
89
|
+
await bridge.executeConsoleCommand('echo MCP Server Health Check');
|
|
90
|
+
metrics.connectionStatus = 'connected';
|
|
91
|
+
metrics.lastHealthCheck = new Date();
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch (err1) {
|
|
95
|
+
// Fallback: minimal Python ping (if Python plugin is enabled)
|
|
96
|
+
try {
|
|
97
|
+
await bridge.executePython("import sys; sys.stdout.write('OK')");
|
|
98
|
+
metrics.connectionStatus = 'connected';
|
|
99
|
+
metrics.lastHealthCheck = new Date();
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (err2) {
|
|
103
|
+
metrics.connectionStatus = 'error';
|
|
104
|
+
metrics.lastHealthCheck = new Date();
|
|
105
|
+
log.warn('Health check failed (console and python):', err1, err2);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export async function createServer() {
|
|
111
|
+
const bridge = new UnrealBridge();
|
|
112
|
+
// Initialize response validation with schemas
|
|
113
|
+
log.info('Initializing response validation...');
|
|
114
|
+
const toolDefs = CONFIG.USE_CONSOLIDATED_TOOLS ? consolidatedToolDefinitions : toolDefinitions;
|
|
115
|
+
toolDefs.forEach((tool) => {
|
|
116
|
+
if (tool.outputSchema) {
|
|
117
|
+
responseValidator.registerSchema(tool.name, tool.outputSchema);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
log.info(`Registered ${responseValidator.getStats().totalSchemas} output schemas for validation`);
|
|
121
|
+
// Connect to UE5 Remote Control with retries and timeout
|
|
122
|
+
const connected = await bridge.tryConnect(CONFIG.MAX_RETRY_ATTEMPTS, 5000, // 5 second timeout per attempt
|
|
123
|
+
CONFIG.RETRY_DELAY_MS);
|
|
124
|
+
if (connected) {
|
|
125
|
+
metrics.connectionStatus = 'connected';
|
|
126
|
+
log.info('Successfully connected to Unreal Engine');
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
log.warn('Could not connect to Unreal Engine after retries');
|
|
130
|
+
log.info('Server will start anyway - connection will be retried periodically');
|
|
131
|
+
log.info('Make sure Unreal Engine is running with Remote Control enabled');
|
|
132
|
+
metrics.connectionStatus = 'disconnected';
|
|
133
|
+
// Schedule automatic reconnection attempts
|
|
134
|
+
setInterval(async () => {
|
|
135
|
+
if (!bridge.isConnected) {
|
|
136
|
+
log.info('Attempting to reconnect to Unreal Engine...');
|
|
137
|
+
const reconnected = await bridge.tryConnect(1, 5000, 0); // Single attempt
|
|
138
|
+
if (reconnected) {
|
|
139
|
+
log.info('Reconnected to Unreal Engine successfully');
|
|
140
|
+
metrics.connectionStatus = 'connected';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, 10000); // Try every 10 seconds
|
|
144
|
+
}
|
|
145
|
+
// Start periodic health checks
|
|
146
|
+
setInterval(() => {
|
|
147
|
+
performHealthCheck(bridge);
|
|
148
|
+
}, CONFIG.HEALTH_CHECK_INTERVAL_MS);
|
|
149
|
+
// Resources
|
|
150
|
+
const assetResources = new AssetResources(bridge);
|
|
151
|
+
const actorResources = new ActorResources(bridge);
|
|
152
|
+
const levelResources = new LevelResources(bridge);
|
|
153
|
+
// Tools
|
|
154
|
+
const actorTools = new ActorTools(bridge);
|
|
155
|
+
const assetTools = new AssetTools(bridge);
|
|
156
|
+
const editorTools = new EditorTools(bridge);
|
|
157
|
+
const materialTools = new MaterialTools(bridge);
|
|
158
|
+
const animationTools = new AnimationTools(bridge);
|
|
159
|
+
const physicsTools = new PhysicsTools(bridge);
|
|
160
|
+
const niagaraTools = new NiagaraTools(bridge);
|
|
161
|
+
const blueprintTools = new BlueprintTools(bridge);
|
|
162
|
+
const levelTools = new LevelTools(bridge);
|
|
163
|
+
const lightingTools = new LightingTools(bridge);
|
|
164
|
+
const landscapeTools = new LandscapeTools(bridge);
|
|
165
|
+
const foliageTools = new FoliageTools(bridge);
|
|
166
|
+
const debugTools = new DebugVisualizationTools(bridge);
|
|
167
|
+
const performanceTools = new PerformanceTools(bridge);
|
|
168
|
+
const audioTools = new AudioTools(bridge);
|
|
169
|
+
const uiTools = new UITools(bridge);
|
|
170
|
+
const rcTools = new RcTools(bridge);
|
|
171
|
+
const sequenceTools = new SequenceTools(bridge);
|
|
172
|
+
const introspectionTools = new IntrospectionTools(bridge);
|
|
173
|
+
const visualTools = new VisualTools(bridge);
|
|
174
|
+
const engineTools = new EngineTools(bridge);
|
|
175
|
+
const server = new Server({
|
|
176
|
+
name: CONFIG.SERVER_NAME,
|
|
177
|
+
version: CONFIG.SERVER_VERSION
|
|
178
|
+
}, {
|
|
179
|
+
capabilities: {
|
|
180
|
+
resources: {},
|
|
181
|
+
tools: {},
|
|
182
|
+
prompts: {},
|
|
183
|
+
logging: {}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// Handle resource listing
|
|
187
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
188
|
+
return {
|
|
189
|
+
resources: [
|
|
190
|
+
{
|
|
191
|
+
uri: 'ue://assets',
|
|
192
|
+
name: 'Project Assets',
|
|
193
|
+
description: 'List all assets in the project',
|
|
194
|
+
mimeType: 'application/json'
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
uri: 'ue://actors',
|
|
198
|
+
name: 'Level Actors',
|
|
199
|
+
description: 'List all actors in the current level',
|
|
200
|
+
mimeType: 'application/json'
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
uri: 'ue://level',
|
|
204
|
+
name: 'Current Level',
|
|
205
|
+
description: 'Information about the current level',
|
|
206
|
+
mimeType: 'application/json'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
uri: 'ue://exposed',
|
|
210
|
+
name: 'Remote Control Exposed',
|
|
211
|
+
description: 'List all exposed properties via Remote Control',
|
|
212
|
+
mimeType: 'application/json'
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
uri: 'ue://health',
|
|
216
|
+
name: 'Health Status',
|
|
217
|
+
description: 'Server health and performance metrics',
|
|
218
|
+
mimeType: 'application/json'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
uri: 'ue://version',
|
|
222
|
+
name: 'Engine Version',
|
|
223
|
+
description: 'Unreal Engine version and compatibility info',
|
|
224
|
+
mimeType: 'application/json'
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
// Handle resource reading
|
|
230
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
231
|
+
const uri = request.params.uri;
|
|
232
|
+
if (uri === 'ue://assets') {
|
|
233
|
+
const list = await assetResources.list('/Game', true);
|
|
234
|
+
return {
|
|
235
|
+
contents: [{
|
|
236
|
+
uri,
|
|
237
|
+
mimeType: 'application/json',
|
|
238
|
+
text: JSON.stringify(list, null, 2)
|
|
239
|
+
}]
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (uri === 'ue://actors') {
|
|
243
|
+
const list = await actorResources.listActors();
|
|
244
|
+
return {
|
|
245
|
+
contents: [{
|
|
246
|
+
uri,
|
|
247
|
+
mimeType: 'application/json',
|
|
248
|
+
text: JSON.stringify(list, null, 2)
|
|
249
|
+
}]
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (uri === 'ue://level') {
|
|
253
|
+
const level = await levelResources.getCurrentLevel();
|
|
254
|
+
return {
|
|
255
|
+
contents: [{
|
|
256
|
+
uri,
|
|
257
|
+
mimeType: 'application/json',
|
|
258
|
+
text: JSON.stringify(level, null, 2)
|
|
259
|
+
}]
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (uri === 'ue://exposed') {
|
|
263
|
+
try {
|
|
264
|
+
const exposed = await bridge.getExposed();
|
|
265
|
+
return {
|
|
266
|
+
contents: [{
|
|
267
|
+
uri,
|
|
268
|
+
mimeType: 'application/json',
|
|
269
|
+
text: JSON.stringify(exposed, null, 2)
|
|
270
|
+
}]
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return {
|
|
275
|
+
contents: [{
|
|
276
|
+
uri,
|
|
277
|
+
mimeType: 'text/plain',
|
|
278
|
+
text: 'Failed to get exposed properties. Ensure Remote Control is configured.'
|
|
279
|
+
}]
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (uri === 'ue://health') {
|
|
284
|
+
const uptimeMs = Date.now() - metrics.uptime;
|
|
285
|
+
// Query engine version and feature flags (best-effort)
|
|
286
|
+
let versionInfo = {};
|
|
287
|
+
let featureFlags = {};
|
|
288
|
+
try {
|
|
289
|
+
versionInfo = await bridge.getEngineVersion();
|
|
290
|
+
}
|
|
291
|
+
catch { }
|
|
292
|
+
try {
|
|
293
|
+
featureFlags = await bridge.getFeatureFlags();
|
|
294
|
+
}
|
|
295
|
+
catch { }
|
|
296
|
+
const health = {
|
|
297
|
+
status: metrics.connectionStatus,
|
|
298
|
+
uptime: Math.floor(uptimeMs / 1000),
|
|
299
|
+
performance: {
|
|
300
|
+
totalRequests: metrics.totalRequests,
|
|
301
|
+
successfulRequests: metrics.successfulRequests,
|
|
302
|
+
failedRequests: metrics.failedRequests,
|
|
303
|
+
successRate: metrics.totalRequests > 0 ?
|
|
304
|
+
(metrics.successfulRequests / metrics.totalRequests * 100).toFixed(2) + '%' : 'N/A',
|
|
305
|
+
averageResponseTime: Math.round(metrics.averageResponseTime) + 'ms'
|
|
306
|
+
},
|
|
307
|
+
lastHealthCheck: metrics.lastHealthCheck.toISOString(),
|
|
308
|
+
unrealConnection: {
|
|
309
|
+
status: bridge.isConnected ? 'connected' : 'disconnected',
|
|
310
|
+
host: process.env.UE_HOST || 'localhost',
|
|
311
|
+
httpPort: process.env.UE_RC_HTTP_PORT || 30010,
|
|
312
|
+
wsPort: process.env.UE_RC_WS_PORT || 30020,
|
|
313
|
+
engineVersion: versionInfo,
|
|
314
|
+
features: {
|
|
315
|
+
pythonEnabled: featureFlags.pythonEnabled === true,
|
|
316
|
+
subsystems: featureFlags.subsystems || {},
|
|
317
|
+
rcHttpReachable: bridge.isConnected
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
recentErrors: metrics.recentErrors.slice(-5)
|
|
321
|
+
};
|
|
322
|
+
return {
|
|
323
|
+
contents: [{
|
|
324
|
+
uri,
|
|
325
|
+
mimeType: 'application/json',
|
|
326
|
+
text: JSON.stringify(health, null, 2)
|
|
327
|
+
}]
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (uri === 'ue://version') {
|
|
331
|
+
const info = await bridge.getEngineVersion();
|
|
332
|
+
return {
|
|
333
|
+
contents: [{
|
|
334
|
+
uri,
|
|
335
|
+
mimeType: 'application/json',
|
|
336
|
+
text: JSON.stringify(info, null, 2)
|
|
337
|
+
}]
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
341
|
+
});
|
|
342
|
+
// Handle tool listing - switch between consolidated (10) or individual (36) tools
|
|
343
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
344
|
+
log.info(`Serving ${CONFIG.USE_CONSOLIDATED_TOOLS ? 'consolidated' : 'individual'} tools`);
|
|
345
|
+
return {
|
|
346
|
+
tools: CONFIG.USE_CONSOLIDATED_TOOLS ? consolidatedToolDefinitions : toolDefinitions
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
// Handle tool calls - switch between consolidated (10) or individual (36) tools
|
|
350
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
351
|
+
const { name, arguments: args } = request.params;
|
|
352
|
+
const startTime = Date.now();
|
|
353
|
+
// Create tools object for handler
|
|
354
|
+
const tools = {
|
|
355
|
+
actorTools,
|
|
356
|
+
assetTools,
|
|
357
|
+
materialTools,
|
|
358
|
+
editorTools,
|
|
359
|
+
animationTools,
|
|
360
|
+
physicsTools,
|
|
361
|
+
niagaraTools,
|
|
362
|
+
blueprintTools,
|
|
363
|
+
levelTools,
|
|
364
|
+
lightingTools,
|
|
365
|
+
landscapeTools,
|
|
366
|
+
foliageTools,
|
|
367
|
+
debugTools,
|
|
368
|
+
performanceTools,
|
|
369
|
+
audioTools,
|
|
370
|
+
uiTools,
|
|
371
|
+
rcTools,
|
|
372
|
+
sequenceTools,
|
|
373
|
+
introspectionTools,
|
|
374
|
+
visualTools,
|
|
375
|
+
engineTools,
|
|
376
|
+
bridge
|
|
377
|
+
};
|
|
378
|
+
// Use consolidated or individual handler based on configuration
|
|
379
|
+
try {
|
|
380
|
+
log.debug(`Executing tool: ${name}`);
|
|
381
|
+
let result;
|
|
382
|
+
if (CONFIG.USE_CONSOLIDATED_TOOLS) {
|
|
383
|
+
result = await handleConsolidatedToolCall(name, args, tools);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
result = await handleToolCall(name, args, tools);
|
|
387
|
+
}
|
|
388
|
+
log.debug(`Tool ${name} returned result`);
|
|
389
|
+
// Clean the result to remove circular references
|
|
390
|
+
result = cleanObject(result);
|
|
391
|
+
// Validate and enhance response
|
|
392
|
+
result = responseValidator.wrapResponse(name, result);
|
|
393
|
+
trackPerformance(startTime, true);
|
|
394
|
+
log.info(`Tool ${name} completed successfully in ${Date.now() - startTime}ms`);
|
|
395
|
+
// Log that we're returning the response
|
|
396
|
+
const responsePreview = JSON.stringify(result).substring(0, 100);
|
|
397
|
+
log.debug(`Returning response to MCP client: ${responsePreview}...`);
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
trackPerformance(startTime, false);
|
|
402
|
+
// Use consistent error handling
|
|
403
|
+
const errorResponse = ErrorHandler.createErrorResponse(error, name, { ...args, scope: `tool-call/${name}` });
|
|
404
|
+
log.error(`Tool execution failed: ${name}`, errorResponse);
|
|
405
|
+
// Record error for health diagnostics
|
|
406
|
+
try {
|
|
407
|
+
metrics.recentErrors.push({
|
|
408
|
+
time: new Date().toISOString(),
|
|
409
|
+
scope: errorResponse.scope || `tool-call/${name}`,
|
|
410
|
+
type: errorResponse._debug?.errorType || 'UNKNOWN',
|
|
411
|
+
message: errorResponse.error || errorResponse.message || 'Unknown error',
|
|
412
|
+
retriable: Boolean(errorResponse.retriable)
|
|
413
|
+
});
|
|
414
|
+
if (metrics.recentErrors.length > 20)
|
|
415
|
+
metrics.recentErrors.splice(0, metrics.recentErrors.length - 20);
|
|
416
|
+
}
|
|
417
|
+
catch { }
|
|
418
|
+
return {
|
|
419
|
+
content: [{
|
|
420
|
+
type: 'text',
|
|
421
|
+
text: errorResponse.message || `Failed to execute ${name}`
|
|
422
|
+
}],
|
|
423
|
+
...errorResponse
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
// Handle prompt listing
|
|
428
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
429
|
+
return {
|
|
430
|
+
prompts: prompts.map(p => ({
|
|
431
|
+
name: p.name,
|
|
432
|
+
description: p.description,
|
|
433
|
+
arguments: Object.entries(p.arguments || {}).map(([name, schema]) => ({
|
|
434
|
+
name,
|
|
435
|
+
description: schema.description,
|
|
436
|
+
required: schema.required || false
|
|
437
|
+
}))
|
|
438
|
+
}))
|
|
439
|
+
};
|
|
440
|
+
});
|
|
441
|
+
// Handle prompt retrieval
|
|
442
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
443
|
+
const prompt = prompts.find(p => p.name === request.params.name);
|
|
444
|
+
if (!prompt) {
|
|
445
|
+
throw new Error(`Unknown prompt: ${request.params.name}`);
|
|
446
|
+
}
|
|
447
|
+
// Return a template for the lighting setup
|
|
448
|
+
return {
|
|
449
|
+
messages: [
|
|
450
|
+
{
|
|
451
|
+
role: 'user',
|
|
452
|
+
content: {
|
|
453
|
+
type: 'text',
|
|
454
|
+
text: `Set up three-point lighting with ${request.params.arguments?.intensity || 'medium'} intensity`
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
return { server, bridge };
|
|
461
|
+
}
|
|
462
|
+
export async function startStdioServer() {
|
|
463
|
+
const { server } = await createServer();
|
|
464
|
+
const transport = new StdioServerTransport();
|
|
465
|
+
// Add debugging for transport messages
|
|
466
|
+
const originalWrite = process.stdout.write;
|
|
467
|
+
process.stdout.write = function (...args) {
|
|
468
|
+
const message = args[0];
|
|
469
|
+
if (typeof message === 'string' && message.includes('jsonrpc')) {
|
|
470
|
+
log.debug(`Sending to client: ${message.substring(0, 200)}...`);
|
|
471
|
+
}
|
|
472
|
+
return originalWrite.apply(process.stdout, args);
|
|
473
|
+
};
|
|
474
|
+
await server.connect(transport);
|
|
475
|
+
log.info('Unreal Engine MCP Server started on stdio');
|
|
476
|
+
}
|
|
477
|
+
// Start the server when run directly
|
|
478
|
+
if (import.meta.url === `file://${process.argv[1].replace(/\\/g, '/')}`) {
|
|
479
|
+
startStdioServer().catch(error => {
|
|
480
|
+
console.error('Failed to start server:', error);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface PromptArgument {
|
|
2
|
+
type: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
enum?: string[];
|
|
5
|
+
default?: any;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface Prompt {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
arguments?: Record<string, PromptArgument>;
|
|
12
|
+
}
|
|
13
|
+
export declare const prompts: Prompt[];
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const prompts = [
|
|
2
|
+
{
|
|
3
|
+
name: 'setup_three_point_lighting',
|
|
4
|
+
description: 'Set up a basic three-point lighting rig around the current camera focus',
|
|
5
|
+
arguments: {
|
|
6
|
+
intensity: {
|
|
7
|
+
type: 'string',
|
|
8
|
+
enum: ['low', 'medium', 'high'],
|
|
9
|
+
default: 'medium',
|
|
10
|
+
description: 'Light intensity level'
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: 'create_fps_controller',
|
|
16
|
+
description: 'Create a first-person shooter character controller',
|
|
17
|
+
arguments: {
|
|
18
|
+
spawnLocation: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
description: 'Location to spawn the controller',
|
|
21
|
+
required: false
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'setup_post_processing',
|
|
27
|
+
description: 'Configure post-processing volume with cinematic settings',
|
|
28
|
+
arguments: {
|
|
29
|
+
style: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
enum: ['cinematic', 'realistic', 'stylized', 'noir'],
|
|
32
|
+
default: 'cinematic',
|
|
33
|
+
description: 'Visual style preset'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Utilities for Modern Unreal Engine API
|
|
3
|
+
*
|
|
4
|
+
* This module provides Python code snippets that use the modern
|
|
5
|
+
* Unreal Engine Python API instead of deprecated functions.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PythonUtils {
|
|
8
|
+
/**
|
|
9
|
+
* Get all actors in the level using modern API
|
|
10
|
+
* @returns Python code to get all level actors
|
|
11
|
+
*/
|
|
12
|
+
static getAllLevelActors(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Get selected actors using modern API
|
|
15
|
+
* @returns Python code to get selected actors
|
|
16
|
+
*/
|
|
17
|
+
static getSelectedActors(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Spawn actor from class using modern API
|
|
20
|
+
* @returns Python code to spawn actor
|
|
21
|
+
*/
|
|
22
|
+
static spawnActorFromClass(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Get a safe way to access actors
|
|
25
|
+
* @returns Python code with modern API
|
|
26
|
+
*/
|
|
27
|
+
static getSafeActorAccess(): string;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=python-utils.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Utilities for Modern Unreal Engine API
|
|
3
|
+
*
|
|
4
|
+
* This module provides Python code snippets that use the modern
|
|
5
|
+
* Unreal Engine Python API instead of deprecated functions.
|
|
6
|
+
*/
|
|
7
|
+
export class PythonUtils {
|
|
8
|
+
/**
|
|
9
|
+
* Get all actors in the level using modern API
|
|
10
|
+
* @returns Python code to get all level actors
|
|
11
|
+
*/
|
|
12
|
+
static getAllLevelActors() {
|
|
13
|
+
return `
|
|
14
|
+
# Use modern EditorActorSubsystem instead of deprecated EditorLevelLibrary
|
|
15
|
+
editor_actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
16
|
+
actors = editor_actor_subsystem.get_all_level_actors()
|
|
17
|
+
`.trim();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get selected actors using modern API
|
|
21
|
+
* @returns Python code to get selected actors
|
|
22
|
+
*/
|
|
23
|
+
static getSelectedActors() {
|
|
24
|
+
return `
|
|
25
|
+
editor_actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
26
|
+
selected_actors = editor_actor_subsystem.get_selected_level_actors()
|
|
27
|
+
`.trim();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Spawn actor from class using modern API
|
|
31
|
+
* @returns Python code to spawn actor
|
|
32
|
+
*/
|
|
33
|
+
static spawnActorFromClass() {
|
|
34
|
+
return `
|
|
35
|
+
editor_actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
36
|
+
# Use spawn_actor_from_class for spawning
|
|
37
|
+
`.trim();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get a safe way to access actors
|
|
41
|
+
* @returns Python code with modern API
|
|
42
|
+
*/
|
|
43
|
+
static getSafeActorAccess() {
|
|
44
|
+
return `
|
|
45
|
+
# Use modern API
|
|
46
|
+
try:
|
|
47
|
+
editor_actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
48
|
+
actors = editor_actor_subsystem.get_all_level_actors() if editor_actor_subsystem else []
|
|
49
|
+
except:
|
|
50
|
+
actors = []
|
|
51
|
+
`.trim();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=python-utils.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { UnrealBridge } from '../unreal-bridge.js';
|
|
2
|
+
export declare class ActorResources {
|
|
3
|
+
private bridge;
|
|
4
|
+
private cache;
|
|
5
|
+
private readonly CACHE_TTL_MS;
|
|
6
|
+
constructor(bridge: UnrealBridge);
|
|
7
|
+
private getFromCache;
|
|
8
|
+
private setCache;
|
|
9
|
+
listActors(): Promise<any>;
|
|
10
|
+
getActorByName(actorName: string): Promise<any>;
|
|
11
|
+
getActorTransform(actorPath: string): Promise<any>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=actors.d.ts.map
|