unreal-engine-mcp-server 0.5.1 → 0.5.2
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/.github/workflows/publish-mcp.yml +1 -4
- package/.github/workflows/release-drafter.yml +2 -1
- package/CHANGELOG.md +38 -0
- package/dist/automation/bridge.d.ts +1 -2
- package/dist/automation/bridge.js +24 -23
- package/dist/automation/connection-manager.d.ts +1 -0
- package/dist/automation/connection-manager.js +10 -0
- package/dist/automation/message-handler.js +5 -4
- package/dist/automation/request-tracker.d.ts +4 -0
- package/dist/automation/request-tracker.js +11 -3
- package/dist/tools/actors.d.ts +19 -1
- package/dist/tools/actors.js +15 -5
- package/dist/tools/assets.js +1 -1
- package/dist/tools/blueprint.d.ts +12 -0
- package/dist/tools/blueprint.js +43 -14
- package/dist/tools/consolidated-tool-definitions.js +2 -1
- package/dist/tools/editor.js +3 -2
- package/dist/tools/handlers/actor-handlers.d.ts +1 -1
- package/dist/tools/handlers/actor-handlers.js +14 -8
- package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
- package/dist/tools/handlers/sequence-handlers.js +24 -13
- package/dist/tools/introspection.d.ts +1 -1
- package/dist/tools/introspection.js +1 -1
- package/dist/tools/level.js +3 -3
- package/dist/tools/lighting.d.ts +54 -7
- package/dist/tools/lighting.js +4 -4
- package/dist/tools/materials.d.ts +1 -1
- package/dist/types/tool-types.d.ts +2 -0
- package/dist/unreal-bridge.js +4 -4
- package/dist/utils/command-validator.js +6 -5
- package/dist/utils/error-handler.d.ts +24 -2
- package/dist/utils/error-handler.js +58 -23
- package/dist/utils/normalize.d.ts +7 -4
- package/dist/utils/normalize.js +12 -10
- package/dist/utils/response-validator.js +88 -73
- package/dist/utils/unreal-command-queue.d.ts +2 -0
- package/dist/utils/unreal-command-queue.js +8 -1
- package/docs/handler-mapping.md +4 -2
- package/package.json +1 -1
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
- package/server.json +3 -3
- package/src/automation/bridge.ts +27 -25
- package/src/automation/connection-manager.ts +18 -0
- package/src/automation/message-handler.ts +33 -8
- package/src/automation/request-tracker.ts +39 -7
- package/src/server/tool-registry.ts +3 -3
- package/src/tools/actors.ts +44 -19
- package/src/tools/assets.ts +3 -3
- package/src/tools/blueprint.ts +115 -49
- package/src/tools/consolidated-tool-definitions.ts +2 -1
- package/src/tools/editor.ts +4 -3
- package/src/tools/handlers/actor-handlers.ts +14 -9
- package/src/tools/handlers/sequence-handlers.ts +86 -63
- package/src/tools/introspection.ts +7 -7
- package/src/tools/level.ts +6 -6
- package/src/tools/lighting.ts +19 -19
- package/src/tools/materials.ts +1 -1
- package/src/tools/sequence.ts +1 -1
- package/src/tools/ui.ts +1 -1
- package/src/types/tool-types.ts +4 -0
- package/src/unreal-bridge.ts +71 -26
- package/src/utils/command-validator.ts +46 -5
- package/src/utils/error-handler.ts +128 -45
- package/src/utils/normalize.ts +38 -16
- package/src/utils/response-validator.ts +103 -87
- package/src/utils/unreal-command-queue.ts +13 -1
|
@@ -21,91 +21,106 @@ function buildSummaryText(toolName, payload) {
|
|
|
21
21
|
return `${toolName} responded`;
|
|
22
22
|
}
|
|
23
23
|
const effectivePayload = { ...payload };
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const listKeys = ['actors', 'levels', 'assets', 'folders', 'blueprints', 'components', 'pawnClasses', 'foliageTypes', 'nodes', 'tracks', 'bindings', 'keys'];
|
|
32
|
-
for (const key of listKeys) {
|
|
33
|
-
if (Array.isArray(effectivePayload[key])) {
|
|
34
|
-
const arr = effectivePayload[key];
|
|
35
|
-
const names = arr.map(i => isRecord(i) ? (i.name || i.path || i.id || i.assetName || i.objectPath || i.packageName || i.nodeName || '<?>') : String(i));
|
|
36
|
-
const count = arr.length;
|
|
37
|
-
const preview = names.slice(0, 100).join(', ');
|
|
38
|
-
const suffix = count > 100 ? `, ... (+${count - 100} more)` : '';
|
|
39
|
-
parts.push(`${key}: ${preview}${suffix} (Total: ${count})`);
|
|
24
|
+
const flattenWrappers = (obj, depth = 0) => {
|
|
25
|
+
if (depth > 5)
|
|
26
|
+
return;
|
|
27
|
+
if (isRecord(obj.data)) {
|
|
28
|
+
Object.assign(obj, obj.data);
|
|
29
|
+
delete obj.data;
|
|
30
|
+
flattenWrappers(obj, depth + 1);
|
|
40
31
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
'package', 'dependencies', 'graph', 'tags', 'metadata', 'properties'
|
|
67
|
-
];
|
|
68
|
-
for (const key of usefulKeys) {
|
|
69
|
-
if (effectivePayload[key] !== undefined && effectivePayload[key] !== null) {
|
|
70
|
-
const val = effectivePayload[key];
|
|
71
|
-
if (typeof val === 'object') {
|
|
72
|
-
if (key === 'metadata' || key === 'properties' || key === 'tags') {
|
|
73
|
-
const entries = Object.entries(val);
|
|
74
|
-
const formatted = entries.map(([k, v]) => `${k}=${v}`);
|
|
75
|
-
const limit = 50;
|
|
76
|
-
parts.push(`${key}: { ${formatted.slice(0, limit).join(', ')}${formatted.length > limit ? '...' : ''} }`);
|
|
77
|
-
continue;
|
|
32
|
+
if (isRecord(obj.result)) {
|
|
33
|
+
Object.assign(obj, obj.result);
|
|
34
|
+
delete obj.result;
|
|
35
|
+
flattenWrappers(obj, depth + 1);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
flattenWrappers(effectivePayload);
|
|
39
|
+
const parts = [];
|
|
40
|
+
const addedKeys = new Set();
|
|
41
|
+
const skipKeys = new Set(['requestId', 'type', 'data', 'result', 'warnings']);
|
|
42
|
+
const formatValue = (val) => {
|
|
43
|
+
if (val === null || val === undefined)
|
|
44
|
+
return '';
|
|
45
|
+
if (typeof val === 'string')
|
|
46
|
+
return val.length > 150 ? val.slice(0, 150) + '...' : val;
|
|
47
|
+
if (typeof val === 'number' || typeof val === 'boolean')
|
|
48
|
+
return String(val);
|
|
49
|
+
if (Array.isArray(val)) {
|
|
50
|
+
if (val.length === 0)
|
|
51
|
+
return '[] (0)';
|
|
52
|
+
const items = val.slice(0, 30).map(v => {
|
|
53
|
+
if (isRecord(v)) {
|
|
54
|
+
return v.name || v.path || v.id || v.nodeId || v.nodeName || v.className ||
|
|
55
|
+
v.displayName || v.type || v.assetPath || v.objectPath ||
|
|
56
|
+
JSON.stringify(v).slice(0, 50);
|
|
78
57
|
}
|
|
79
|
-
|
|
58
|
+
return String(v);
|
|
59
|
+
});
|
|
60
|
+
const suffix = val.length > 30 ? `, ... (+${val.length - 30} more)` : '';
|
|
61
|
+
return `[${items.join(', ')}${suffix}] (${val.length})`;
|
|
62
|
+
}
|
|
63
|
+
if (isRecord(val)) {
|
|
64
|
+
const keys = Object.keys(val);
|
|
65
|
+
if (keys.some(k => ['x', 'y', 'z', 'pitch', 'yaw', 'roll'].includes(k))) {
|
|
66
|
+
const x = val.x ?? val.pitch ?? 0;
|
|
67
|
+
const y = val.y ?? val.yaw ?? 0;
|
|
68
|
+
const z = val.z ?? val.roll ?? 0;
|
|
69
|
+
return `[${x}, ${y}, ${z}]`;
|
|
80
70
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
71
|
+
const entries = Object.entries(val).slice(0, 8);
|
|
72
|
+
const formatted = entries.map(([k, v]) => {
|
|
73
|
+
const vStr = typeof v === 'object' ? JSON.stringify(v).slice(0, 40) : String(v);
|
|
74
|
+
return `${k}=${vStr}`;
|
|
75
|
+
});
|
|
76
|
+
return `{ ${formatted.join(', ')}${keys.length > 8 ? ' ...' : ''} }`;
|
|
77
|
+
}
|
|
78
|
+
return String(val);
|
|
79
|
+
};
|
|
80
|
+
for (const key of ['success', 'error']) {
|
|
81
|
+
if (effectivePayload[key] !== undefined && !addedKeys.has(key)) {
|
|
82
|
+
const formatted = formatValue(effectivePayload[key]);
|
|
83
|
+
if (formatted) {
|
|
84
|
+
parts.push(`${key}: ${formatted}`);
|
|
85
|
+
addedKeys.add(key);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
|
|
89
|
+
let hasArrays = false;
|
|
90
|
+
for (const [key, val] of Object.entries(effectivePayload)) {
|
|
91
|
+
if (addedKeys.has(key))
|
|
92
|
+
continue;
|
|
93
|
+
if (skipKeys.has(key))
|
|
94
|
+
continue;
|
|
95
|
+
if (val === undefined || val === null)
|
|
96
|
+
continue;
|
|
97
|
+
if (typeof val === 'string' && val.trim() === '')
|
|
98
|
+
continue;
|
|
99
|
+
if (key === 'message')
|
|
100
|
+
continue;
|
|
101
|
+
if (Array.isArray(val) && val.length > 0)
|
|
102
|
+
hasArrays = true;
|
|
103
|
+
if ((key === 'count' || key === 'totalCount') && hasArrays)
|
|
104
|
+
continue;
|
|
105
|
+
const formatted = formatValue(val);
|
|
106
|
+
if (formatted) {
|
|
107
|
+
parts.push(`${key}: ${formatted}`);
|
|
108
|
+
addedKeys.add(key);
|
|
95
109
|
}
|
|
96
110
|
}
|
|
97
|
-
|
|
98
|
-
|
|
111
|
+
const message = typeof effectivePayload.message === 'string' ? normalizeText(effectivePayload.message) : '';
|
|
112
|
+
if (message && message.toLowerCase() !== 'success') {
|
|
113
|
+
const isDuplicateInfo = /^(found|listed|retrieved|got|loaded|created|deleted|saved|spawned)\s+\d+/i.test(message) ||
|
|
114
|
+
/Folders:\s*\[/.test(message) ||
|
|
115
|
+
/\d+\s+(assets?|folders?|items?|actors?|components?)\s+(and|in|at)/i.test(message);
|
|
116
|
+
const messageInParts = parts.some(p => p.toLowerCase().includes(message.toLowerCase().slice(0, 30)));
|
|
117
|
+
if (!isDuplicateInfo && !messageInParts) {
|
|
99
118
|
parts.push(message);
|
|
100
|
-
if (error)
|
|
101
|
-
parts.push(`Error: ${error}`);
|
|
102
|
-
if (parts.length === 0 && success !== undefined) {
|
|
103
|
-
parts.push(success ? 'Success' : 'Failed');
|
|
104
119
|
}
|
|
105
120
|
}
|
|
106
|
-
const warnings = Array.isArray(
|
|
121
|
+
const warnings = Array.isArray(effectivePayload.warnings) ? effectivePayload.warnings : [];
|
|
107
122
|
if (warnings.length > 0) {
|
|
108
|
-
parts.push(`Warnings: ${warnings.
|
|
123
|
+
parts.push(`Warnings: ${warnings.map((w) => typeof w === 'string' ? w : JSON.stringify(w)).join('; ')}`);
|
|
109
124
|
}
|
|
110
125
|
return parts.length > 0 ? parts.join(' | ') : `${toolName} responded`;
|
|
111
126
|
}
|
|
@@ -11,6 +11,7 @@ export declare class UnrealCommandQueue {
|
|
|
11
11
|
private isProcessing;
|
|
12
12
|
private lastCommandTime;
|
|
13
13
|
private lastStatCommandTime;
|
|
14
|
+
private processorInterval?;
|
|
14
15
|
private readonly MIN_COMMAND_DELAY;
|
|
15
16
|
private readonly MAX_COMMAND_DELAY;
|
|
16
17
|
private readonly STAT_COMMAND_DELAY;
|
|
@@ -19,6 +20,7 @@ export declare class UnrealCommandQueue {
|
|
|
19
20
|
private processQueue;
|
|
20
21
|
private calculateDelay;
|
|
21
22
|
private startProcessor;
|
|
23
|
+
stopProcessor(): void;
|
|
22
24
|
private delay;
|
|
23
25
|
}
|
|
24
26
|
//# sourceMappingURL=unreal-command-queue.d.ts.map
|
|
@@ -5,6 +5,7 @@ export class UnrealCommandQueue {
|
|
|
5
5
|
isProcessing = false;
|
|
6
6
|
lastCommandTime = 0;
|
|
7
7
|
lastStatCommandTime = 0;
|
|
8
|
+
processorInterval;
|
|
8
9
|
MIN_COMMAND_DELAY = 100;
|
|
9
10
|
MAX_COMMAND_DELAY = 500;
|
|
10
11
|
STAT_COMMAND_DELAY = 300;
|
|
@@ -107,12 +108,18 @@ export class UnrealCommandQueue {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
startProcessor() {
|
|
110
|
-
setInterval(() => {
|
|
111
|
+
this.processorInterval = setInterval(() => {
|
|
111
112
|
if (!this.isProcessing && this.queue.length > 0) {
|
|
112
113
|
this.processQueue();
|
|
113
114
|
}
|
|
114
115
|
}, 1000);
|
|
115
116
|
}
|
|
117
|
+
stopProcessor() {
|
|
118
|
+
if (this.processorInterval) {
|
|
119
|
+
clearInterval(this.processorInterval);
|
|
120
|
+
this.processorInterval = undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
116
123
|
delay(ms) {
|
|
117
124
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
118
125
|
}
|
package/docs/handler-mapping.md
CHANGED
|
@@ -235,8 +235,10 @@ This document maps the TypeScript tool definitions to their corresponding C++ ha
|
|
|
235
235
|
|
|
236
236
|
| Action | C++ Handler File | C++ Function | Notes |
|
|
237
237
|
| :--- | :--- | :--- | :--- |
|
|
238
|
-
| `create_node` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | |
|
|
238
|
+
| `create_node` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | Dynamic node class resolution |
|
|
239
239
|
| `delete_node` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | |
|
|
240
240
|
| `connect_pins` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | |
|
|
241
241
|
| `break_pin_links` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | |
|
|
242
|
-
| `set_node_property` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | |
|
|
242
|
+
| `set_node_property` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | |
|
|
243
|
+
| `list_node_types` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | Lists all UK2Node subclasses |
|
|
244
|
+
| `set_pin_default_value` | `McpAutomationBridge_BlueprintGraphHandlers.cpp` | `HandleBlueprintGraphAction` | Sets default value on input pins |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unreal-engine-mcp-server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"mcpName": "io.github.ChiR24/unreal-engine-mcp",
|
|
5
5
|
"description": "A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal Engine via native automation bridge. Built with TypeScript and designed for game development automation.",
|
|
6
6
|
"type": "module",
|