unreal-engine-mcp-server 0.5.0 → 0.5.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/.env.example +1 -1
- package/.github/release-drafter-config.yml +51 -0
- package/.github/workflows/greetings.yml +5 -1
- package/.github/workflows/labeler.yml +2 -1
- package/.github/workflows/publish-mcp.yml +1 -0
- package/.github/workflows/release-drafter.yml +1 -1
- package/.github/workflows/release.yml +3 -3
- package/CHANGELOG.md +71 -0
- package/CONTRIBUTING.md +1 -1
- package/GEMINI.md +115 -0
- package/Public/Plugin_setup_guide.mp4 +0 -0
- package/README.md +166 -200
- package/dist/config.d.ts +0 -1
- package/dist/config.js +0 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/graphql/loaders.d.ts +64 -0
- package/dist/graphql/loaders.js +117 -0
- package/dist/graphql/resolvers.d.ts +3 -3
- package/dist/graphql/resolvers.js +33 -30
- package/dist/graphql/server.js +3 -1
- package/dist/graphql/types.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -2
- package/dist/server-setup.d.ts +0 -1
- package/dist/server-setup.js +0 -40
- package/dist/tools/actors.d.ts +40 -24
- package/dist/tools/actors.js +8 -2
- package/dist/tools/assets.d.ts +19 -71
- package/dist/tools/assets.js +28 -22
- package/dist/tools/base-tool.d.ts +4 -4
- package/dist/tools/base-tool.js +1 -1
- package/dist/tools/blueprint.d.ts +33 -61
- package/dist/tools/consolidated-tool-handlers.js +96 -110
- package/dist/tools/dynamic-handler-registry.d.ts +11 -9
- package/dist/tools/dynamic-handler-registry.js +17 -95
- package/dist/tools/editor.d.ts +19 -193
- package/dist/tools/editor.js +8 -0
- package/dist/tools/environment.d.ts +8 -14
- package/dist/tools/foliage.d.ts +18 -143
- package/dist/tools/foliage.js +4 -2
- package/dist/tools/handlers/actor-handlers.js +0 -5
- package/dist/tools/handlers/asset-handlers.js +454 -454
- package/dist/tools/landscape.d.ts +16 -116
- package/dist/tools/landscape.js +7 -3
- package/dist/tools/level.d.ts +22 -103
- package/dist/tools/level.js +24 -16
- package/dist/tools/lighting.js +5 -1
- package/dist/tools/materials.js +5 -1
- package/dist/tools/niagara.js +37 -2
- package/dist/tools/performance.d.ts +0 -1
- package/dist/tools/performance.js +0 -1
- package/dist/tools/physics.js +5 -1
- package/dist/tools/sequence.d.ts +24 -24
- package/dist/tools/sequence.js +13 -0
- package/dist/tools/ui.d.ts +0 -2
- package/dist/types/automation-responses.d.ts +115 -0
- package/dist/types/automation-responses.js +2 -0
- package/dist/types/responses.d.ts +249 -0
- package/dist/types/responses.js +2 -0
- package/dist/types/tool-interfaces.d.ts +135 -135
- package/dist/utils/command-validator.js +3 -2
- package/dist/utils/path-security.d.ts +2 -0
- package/dist/utils/path-security.js +24 -0
- package/dist/utils/response-factory.d.ts +4 -4
- package/dist/utils/response-factory.js +15 -21
- package/docs/Migration-Guide-v0.5.0.md +1 -9
- package/docs/testing-guide.md +2 -2
- package/package.json +12 -6
- package/scripts/run-all-tests.mjs +25 -20
- package/server.json +3 -2
- package/src/config.ts +1 -1
- package/src/constants.ts +7 -0
- package/src/graphql/loaders.ts +244 -0
- package/src/graphql/resolvers.ts +47 -49
- package/src/graphql/server.ts +3 -1
- package/src/graphql/types.ts +3 -0
- package/src/index.ts +15 -2
- package/src/resources/assets.ts +5 -4
- package/src/server-setup.ts +3 -37
- package/src/tools/actors.ts +36 -28
- package/src/tools/animation.ts +1 -0
- package/src/tools/assets.ts +74 -63
- package/src/tools/base-tool.ts +3 -3
- package/src/tools/blueprint.ts +59 -59
- package/src/tools/consolidated-tool-handlers.ts +129 -150
- package/src/tools/dynamic-handler-registry.ts +22 -140
- package/src/tools/editor.ts +39 -26
- package/src/tools/environment.ts +21 -27
- package/src/tools/foliage.ts +28 -25
- package/src/tools/handlers/actor-handlers.ts +2 -8
- package/src/tools/handlers/asset-handlers.ts +484 -484
- package/src/tools/handlers/sequence-handlers.ts +1 -1
- package/src/tools/landscape.ts +34 -28
- package/src/tools/level.ts +96 -76
- package/src/tools/lighting.ts +6 -1
- package/src/tools/materials.ts +8 -2
- package/src/tools/niagara.ts +44 -2
- package/src/tools/performance.ts +1 -2
- package/src/tools/physics.ts +7 -1
- package/src/tools/sequence.ts +41 -25
- package/src/tools/ui.ts +0 -2
- package/src/types/automation-responses.ts +119 -0
- package/src/types/responses.ts +355 -0
- package/src/types/tool-interfaces.ts +135 -135
- package/src/utils/command-validator.ts +3 -2
- package/src/utils/normalize.test.ts +162 -0
- package/src/utils/path-security.ts +43 -0
- package/src/utils/response-factory.ts +29 -24
- package/src/utils/safe-json.test.ts +90 -0
- package/src/utils/validation.test.ts +184 -0
- package/tests/test-animation.mjs +358 -33
- package/tests/test-asset-graph.mjs +311 -0
- package/tests/test-audio.mjs +314 -116
- package/tests/test-behavior-tree.mjs +327 -144
- package/tests/test-blueprint-graph.mjs +343 -12
- package/tests/test-control-editor.mjs +85 -53
- package/tests/test-graphql.mjs +58 -8
- package/tests/test-input.mjs +349 -0
- package/tests/test-inspect.mjs +291 -61
- package/tests/test-landscape.mjs +304 -48
- package/tests/test-lighting.mjs +428 -0
- package/tests/test-manage-level.mjs +70 -51
- package/tests/test-performance.mjs +539 -0
- package/tests/test-sequence.mjs +82 -46
- package/tests/test-system.mjs +72 -33
- package/tests/test-wasm.mjs +98 -8
- package/vitest.config.ts +35 -0
- package/.github/release-drafter.yml +0 -148
- package/dist/prompts/index.d.ts +0 -21
- package/dist/prompts/index.js +0 -217
- package/dist/tools/blueprint/helpers.d.ts +0 -29
- package/dist/tools/blueprint/helpers.js +0 -182
- package/src/prompts/index.ts +0 -249
- package/src/tools/blueprint/helpers.ts +0 -189
- package/tests/test-blueprint-events.mjs +0 -35
- package/tests/test-extra-tools.mjs +0 -38
- package/tests/test-render.mjs +0 -33
- package/tests/test-search-assets.mjs +0 -66
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import DataLoader from 'dataloader';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
const log = new Logger('GraphQL:Loaders');
|
|
4
|
+
export function createLoaders(automationBridge) {
|
|
5
|
+
return {
|
|
6
|
+
actorLoader: new DataLoader(async (names) => {
|
|
7
|
+
log.debug(`Batching actor fetch for ${names.length} actors`);
|
|
8
|
+
try {
|
|
9
|
+
const result = await automationBridge.sendAutomationRequest('control_actor', {
|
|
10
|
+
action: 'batch_get',
|
|
11
|
+
actorNames: [...names]
|
|
12
|
+
});
|
|
13
|
+
if (result.success && result.actors) {
|
|
14
|
+
return names.map(name => result.actors?.find(a => a.name === name || a.label === name) ?? null);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
log.debug('Batch fetch not supported, falling back to individual fetches');
|
|
19
|
+
}
|
|
20
|
+
const results = await Promise.all(names.map(async (name) => {
|
|
21
|
+
try {
|
|
22
|
+
const result = await automationBridge.sendAutomationRequest('control_actor', {
|
|
23
|
+
action: 'find_by_name',
|
|
24
|
+
actorName: name
|
|
25
|
+
});
|
|
26
|
+
return result.success ? (result.actor ?? null) : null;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
return results;
|
|
33
|
+
}, {
|
|
34
|
+
cache: true,
|
|
35
|
+
maxBatchSize: 50
|
|
36
|
+
}),
|
|
37
|
+
assetLoader: new DataLoader(async (paths) => {
|
|
38
|
+
log.debug(`Batching asset fetch for ${paths.length} assets`);
|
|
39
|
+
try {
|
|
40
|
+
const result = await automationBridge.sendAutomationRequest('manage_asset', {
|
|
41
|
+
action: 'batch_get',
|
|
42
|
+
assetPaths: [...paths]
|
|
43
|
+
});
|
|
44
|
+
if (result.success && result.assets) {
|
|
45
|
+
return paths.map(path => result.assets?.find(a => a.path === path) ?? null);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
log.debug('Batch asset fetch not supported');
|
|
50
|
+
}
|
|
51
|
+
const results = await Promise.all(paths.map(async (path) => {
|
|
52
|
+
try {
|
|
53
|
+
const result = await automationBridge.sendAutomationRequest('manage_asset', {
|
|
54
|
+
action: 'exists',
|
|
55
|
+
assetPath: path
|
|
56
|
+
});
|
|
57
|
+
if (result.success && result.exists) {
|
|
58
|
+
return result.asset ?? { name: path.split('/').pop() || '', path };
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
return results;
|
|
67
|
+
}, {
|
|
68
|
+
cache: true,
|
|
69
|
+
maxBatchSize: 100
|
|
70
|
+
}),
|
|
71
|
+
blueprintLoader: new DataLoader(async (paths) => {
|
|
72
|
+
log.debug(`Batching blueprint fetch for ${paths.length} blueprints`);
|
|
73
|
+
const results = await Promise.all(paths.map(async (path) => {
|
|
74
|
+
try {
|
|
75
|
+
const result = await automationBridge.sendAutomationRequest('manage_blueprint', {
|
|
76
|
+
action: 'get_blueprint',
|
|
77
|
+
blueprintPath: path
|
|
78
|
+
});
|
|
79
|
+
return result.success ? (result.blueprint ?? null) : null;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
return results;
|
|
86
|
+
}, {
|
|
87
|
+
cache: true,
|
|
88
|
+
maxBatchSize: 20
|
|
89
|
+
}),
|
|
90
|
+
actorComponentsLoader: new DataLoader(async (actorNames) => {
|
|
91
|
+
log.debug(`Batching component fetch for ${actorNames.length} actors`);
|
|
92
|
+
const results = await Promise.all(actorNames.map(async (actorName) => {
|
|
93
|
+
try {
|
|
94
|
+
const result = await automationBridge.sendAutomationRequest('control_actor', {
|
|
95
|
+
action: 'get_components',
|
|
96
|
+
actorName
|
|
97
|
+
});
|
|
98
|
+
return result.success ? (result.components ?? null) : null;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}));
|
|
104
|
+
return results;
|
|
105
|
+
}, {
|
|
106
|
+
cache: true,
|
|
107
|
+
maxBatchSize: 30
|
|
108
|
+
})
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function clearLoaders(loaders) {
|
|
112
|
+
loaders.actorLoader.clearAll();
|
|
113
|
+
loaders.assetLoader.clearAll();
|
|
114
|
+
loaders.blueprintLoader.clearAll();
|
|
115
|
+
loaders.actorComponentsLoader.clearAll();
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=loaders.js.map
|
|
@@ -100,7 +100,7 @@ export declare const resolvers: {
|
|
|
100
100
|
}>;
|
|
101
101
|
asset: (_: any, { path }: {
|
|
102
102
|
path: string;
|
|
103
|
-
}, context: GraphQLContext) => Promise<
|
|
103
|
+
}, context: GraphQLContext) => Promise<import("./loaders.js").Asset | null>;
|
|
104
104
|
actors: (_: any, args: any, context: GraphQLContext) => Promise<{
|
|
105
105
|
edges: {
|
|
106
106
|
node: Actor;
|
|
@@ -116,7 +116,7 @@ export declare const resolvers: {
|
|
|
116
116
|
}>;
|
|
117
117
|
actor: (_: any, { name }: {
|
|
118
118
|
name: string;
|
|
119
|
-
}, context: GraphQLContext) => Promise<Actor | null>;
|
|
119
|
+
}, context: GraphQLContext) => Promise<import("./loaders.js").Actor | null>;
|
|
120
120
|
blueprints: (_: any, args: any, context: GraphQLContext) => Promise<{
|
|
121
121
|
edges: {
|
|
122
122
|
node: Blueprint;
|
|
@@ -141,7 +141,7 @@ export declare const resolvers: {
|
|
|
141
141
|
}>;
|
|
142
142
|
blueprint: (_: any, { path }: {
|
|
143
143
|
path: string;
|
|
144
|
-
}, context: GraphQLContext) => Promise<Blueprint | null>;
|
|
144
|
+
}, context: GraphQLContext) => Promise<import("./loaders.js").Blueprint | null>;
|
|
145
145
|
levels: (_: any, __: any, context: GraphQLContext) => Promise<any>;
|
|
146
146
|
currentLevel: (_: any, __: any, context: GraphQLContext) => Promise<{} | null>;
|
|
147
147
|
materials: (_: any, args: any, context: GraphQLContext) => Promise<{
|
|
@@ -87,6 +87,24 @@ export const scalarResolvers = {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
|
+
import { Logger } from '../utils/logger.js';
|
|
91
|
+
const log = new Logger('GraphQL:Resolvers');
|
|
92
|
+
class GraphQLResolverError extends Error {
|
|
93
|
+
extensions;
|
|
94
|
+
constructor(message, code = 'UNREAL_ENGINE_ERROR', originalError) {
|
|
95
|
+
super(message);
|
|
96
|
+
this.name = 'GraphQLResolverError';
|
|
97
|
+
this.extensions = {
|
|
98
|
+
code,
|
|
99
|
+
originalError: originalError?.message
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function createResolverError(operation, error) {
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
log.error(`${operation} failed:`, message);
|
|
106
|
+
return new GraphQLResolverError(`${operation} failed: ${message}`, 'UNREAL_ENGINE_ERROR', error instanceof Error ? error : undefined);
|
|
107
|
+
}
|
|
90
108
|
function logAutomationFailure(source, response) {
|
|
91
109
|
try {
|
|
92
110
|
if (!response || response.success !== false) {
|
|
@@ -96,7 +114,7 @@ function logAutomationFailure(source, response) {
|
|
|
96
114
|
if (errorText.length === 0) {
|
|
97
115
|
return;
|
|
98
116
|
}
|
|
99
|
-
|
|
117
|
+
log.error(`${source} automation failure:`, errorText);
|
|
100
118
|
}
|
|
101
119
|
catch {
|
|
102
120
|
}
|
|
@@ -111,7 +129,7 @@ async function getActorProperties(bridge, actorName) {
|
|
|
111
129
|
return result.success ? result.value || {} : {};
|
|
112
130
|
}
|
|
113
131
|
catch (error) {
|
|
114
|
-
|
|
132
|
+
log.error('Failed to get actor properties:', error);
|
|
115
133
|
return {};
|
|
116
134
|
}
|
|
117
135
|
}
|
|
@@ -129,12 +147,12 @@ async function listAssets(automationBridge, filter, pagination) {
|
|
|
129
147
|
};
|
|
130
148
|
}
|
|
131
149
|
logAutomationFailure('list_assets', response);
|
|
132
|
-
|
|
150
|
+
log.warn('Failed to list assets - returning empty set');
|
|
133
151
|
return { assets: [], totalCount: 0 };
|
|
134
152
|
}
|
|
135
153
|
catch (error) {
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
log.error('Failed to list assets:', error);
|
|
155
|
+
throw createResolverError('listAssets', error);
|
|
138
156
|
}
|
|
139
157
|
}
|
|
140
158
|
async function listActors(automationBridge, filter) {
|
|
@@ -156,22 +174,6 @@ async function listActors(automationBridge, filter) {
|
|
|
156
174
|
return { actors: [] };
|
|
157
175
|
}
|
|
158
176
|
}
|
|
159
|
-
async function getBlueprint(automationBridge, blueprintPath) {
|
|
160
|
-
try {
|
|
161
|
-
const response = await automationBridge.sendAutomationRequest('get_blueprint', {
|
|
162
|
-
blueprintPath
|
|
163
|
-
}, { timeoutMs: 30000 });
|
|
164
|
-
if (response.success && response.result) {
|
|
165
|
-
return response.result;
|
|
166
|
-
}
|
|
167
|
-
logAutomationFailure('get_blueprint', response);
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
console.error('Failed to get blueprint:', error);
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
177
|
export const resolvers = {
|
|
176
178
|
Query: {
|
|
177
179
|
assets: async (_, args, context) => {
|
|
@@ -194,11 +196,10 @@ export const resolvers = {
|
|
|
194
196
|
},
|
|
195
197
|
asset: async (_, { path }, context) => {
|
|
196
198
|
try {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return response.result;
|
|
199
|
+
if (!context.loaders) {
|
|
200
|
+
throw new Error('Loaders not initialized');
|
|
200
201
|
}
|
|
201
|
-
return
|
|
202
|
+
return await context.loaders.assetLoader.load(path);
|
|
202
203
|
}
|
|
203
204
|
catch (error) {
|
|
204
205
|
console.error('Failed to get asset:', error);
|
|
@@ -228,11 +229,10 @@ export const resolvers = {
|
|
|
228
229
|
},
|
|
229
230
|
actor: async (_, { name }, context) => {
|
|
230
231
|
try {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return actors.actors[0];
|
|
232
|
+
if (!context.loaders) {
|
|
233
|
+
throw new Error('Loaders not initialized');
|
|
234
234
|
}
|
|
235
|
-
return
|
|
235
|
+
return await context.loaders.actorLoader.load(name);
|
|
236
236
|
}
|
|
237
237
|
catch (error) {
|
|
238
238
|
console.error('Failed to get actor:', error);
|
|
@@ -279,7 +279,10 @@ export const resolvers = {
|
|
|
279
279
|
}
|
|
280
280
|
},
|
|
281
281
|
blueprint: async (_, { path }, context) => {
|
|
282
|
-
|
|
282
|
+
if (!context.loaders) {
|
|
283
|
+
throw new Error('Loaders not initialized');
|
|
284
|
+
}
|
|
285
|
+
return await context.loaders.blueprintLoader.load(path);
|
|
283
286
|
},
|
|
284
287
|
levels: async (_, __, context) => {
|
|
285
288
|
try {
|
package/dist/graphql/server.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createYoga } from 'graphql-yoga';
|
|
|
2
2
|
import { createServer } from 'http';
|
|
3
3
|
import { Logger } from '../utils/logger.js';
|
|
4
4
|
import { createGraphQLSchema } from './schema.js';
|
|
5
|
+
import { createLoaders } from './loaders.js';
|
|
5
6
|
export class GraphQLServer {
|
|
6
7
|
log = new Logger('GraphQLServer');
|
|
7
8
|
server = null;
|
|
@@ -40,7 +41,8 @@ export class GraphQLServer {
|
|
|
40
41
|
},
|
|
41
42
|
context: () => ({
|
|
42
43
|
bridge: this.bridge,
|
|
43
|
-
automationBridge: this.automationBridge
|
|
44
|
+
automationBridge: this.automationBridge,
|
|
45
|
+
loaders: createLoaders(this.automationBridge)
|
|
44
46
|
}),
|
|
45
47
|
logging: {
|
|
46
48
|
debug: (...args) => this.log.debug('[GraphQL]', ...args),
|
package/dist/graphql/types.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { UnrealBridge } from '../unreal-bridge.js';
|
|
2
2
|
import { AutomationBridge } from '../automation/index.js';
|
|
3
|
+
import type { GraphQLLoaders } from './loaders.js';
|
|
3
4
|
export interface GraphQLContext {
|
|
4
5
|
bridge: UnrealBridge;
|
|
5
6
|
automationBridge: AutomationBridge;
|
|
7
|
+
loaders?: GraphQLLoaders;
|
|
6
8
|
}
|
|
7
9
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
2
2
|
import { UnrealBridge } from './unreal-bridge.js';
|
|
3
3
|
import { AutomationBridge } from './automation/index.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { GraphQLServer } from './graphql/server.js';
|
|
5
6
|
export declare function createServer(): {
|
|
6
7
|
server: Server<{
|
|
7
8
|
method: string;
|
|
@@ -39,6 +40,7 @@ export declare function createServer(): {
|
|
|
39
40
|
}>;
|
|
40
41
|
bridge: UnrealBridge;
|
|
41
42
|
automationBridge: AutomationBridge;
|
|
43
|
+
graphqlServer: GraphQLServer;
|
|
42
44
|
};
|
|
43
45
|
export declare const configSchema: z.ZodObject<{
|
|
44
46
|
logLevel: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { HealthMonitor } from './services/health-monitor.js';
|
|
|
12
12
|
import { ServerSetup } from './server-setup.js';
|
|
13
13
|
import { startMetricsServer } from './services/metrics-server.js';
|
|
14
14
|
import { config } from './config.js';
|
|
15
|
+
import { GraphQLServer } from './graphql/server.js';
|
|
15
16
|
const require = createRequire(import.meta.url);
|
|
16
17
|
const packageInfo = (() => {
|
|
17
18
|
try {
|
|
@@ -81,6 +82,10 @@ export function createServer() {
|
|
|
81
82
|
log.error('Automation bridge error', error);
|
|
82
83
|
});
|
|
83
84
|
startMetricsServer({ healthMonitor, automationBridge, logger: log });
|
|
85
|
+
const graphqlServer = new GraphQLServer(bridge, automationBridge);
|
|
86
|
+
graphqlServer.start().catch((error) => {
|
|
87
|
+
log.warn('GraphQL server failed to start:', error);
|
|
88
|
+
});
|
|
84
89
|
log.debug('Initializing WebAssembly integration...');
|
|
85
90
|
initializeWASM().then(() => {
|
|
86
91
|
log.info('✅ WebAssembly integration initialized (JSON parsing and math operations)');
|
|
@@ -109,7 +114,7 @@ export function createServer() {
|
|
|
109
114
|
});
|
|
110
115
|
const serverSetup = new ServerSetup(server, bridge, automationBridge, log, healthMonitor);
|
|
111
116
|
serverSetup.setup();
|
|
112
|
-
return { server, bridge, automationBridge };
|
|
117
|
+
return { server, bridge, automationBridge, graphqlServer };
|
|
113
118
|
}
|
|
114
119
|
export const configSchema = z.object({
|
|
115
120
|
logLevel: z.enum(['debug', 'info', 'warn', 'error']).optional().default('info').describe('Runtime log level'),
|
|
@@ -131,7 +136,7 @@ export default function createServerDefault({ config } = {}) {
|
|
|
131
136
|
return server;
|
|
132
137
|
}
|
|
133
138
|
export async function startStdioServer() {
|
|
134
|
-
const { server, automationBridge } = createServer();
|
|
139
|
+
const { server, automationBridge, graphqlServer } = createServer();
|
|
135
140
|
const transport = new StdioServerTransport();
|
|
136
141
|
let shuttingDown = false;
|
|
137
142
|
const handleShutdown = async (signal) => {
|
|
@@ -147,6 +152,12 @@ export async function startStdioServer() {
|
|
|
147
152
|
catch (error) {
|
|
148
153
|
log.warn('Failed to stop automation bridge cleanly', error);
|
|
149
154
|
}
|
|
155
|
+
try {
|
|
156
|
+
await graphqlServer.stop();
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
log.warn('Failed to stop GraphQL server cleanly', error);
|
|
160
|
+
}
|
|
150
161
|
try {
|
|
151
162
|
if (typeof server.close === 'function') {
|
|
152
163
|
await server.close();
|
package/dist/server-setup.d.ts
CHANGED
package/dist/server-setup.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import { prompts } from './prompts/index.js';
|
|
3
1
|
import { AssetResources } from './resources/assets.js';
|
|
4
2
|
import { ActorResources } from './resources/actors.js';
|
|
5
3
|
import { LevelResources } from './resources/levels.js';
|
|
@@ -32,7 +30,6 @@ export class ServerSetup {
|
|
|
32
30
|
resourceRegistry.register();
|
|
33
31
|
const toolRegistry = new ToolRegistry(this.server, this.bridge, this.automationBridge, this.logger, this.healthMonitor, this.assetResources, this.actorResources, this.levelResources, ensureConnected);
|
|
34
32
|
toolRegistry.register();
|
|
35
|
-
this.registerPrompts();
|
|
36
33
|
}
|
|
37
34
|
validateEnvironment() {
|
|
38
35
|
const projectPath = process.env.UE_PROJECT_PATH;
|
|
@@ -70,42 +67,5 @@ export class ServerSetup {
|
|
|
70
67
|
}
|
|
71
68
|
return ok;
|
|
72
69
|
}
|
|
73
|
-
registerPrompts() {
|
|
74
|
-
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
75
|
-
return {
|
|
76
|
-
prompts: prompts.map(p => ({
|
|
77
|
-
name: p.name,
|
|
78
|
-
description: p.description,
|
|
79
|
-
arguments: Object.entries(p.arguments || {}).map(([name, schema]) => {
|
|
80
|
-
const meta = {};
|
|
81
|
-
if (schema.type)
|
|
82
|
-
meta.type = schema.type;
|
|
83
|
-
if (schema.enum)
|
|
84
|
-
meta.enum = schema.enum;
|
|
85
|
-
if (schema.default !== undefined)
|
|
86
|
-
meta.default = schema.default;
|
|
87
|
-
return {
|
|
88
|
-
name,
|
|
89
|
-
description: schema.description,
|
|
90
|
-
required: schema.required ?? false,
|
|
91
|
-
...(Object.keys(meta).length ? { _meta: meta } : {})
|
|
92
|
-
};
|
|
93
|
-
})
|
|
94
|
-
}))
|
|
95
|
-
};
|
|
96
|
-
});
|
|
97
|
-
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
98
|
-
const prompt = prompts.find(p => p.name === request.params.name);
|
|
99
|
-
if (!prompt) {
|
|
100
|
-
throw new Error(`Unknown prompt: ${request.params.name}`);
|
|
101
|
-
}
|
|
102
|
-
const args = (request.params.arguments || {});
|
|
103
|
-
const messages = prompt.build(args);
|
|
104
|
-
return {
|
|
105
|
-
description: prompt.description,
|
|
106
|
-
messages
|
|
107
|
-
};
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
70
|
}
|
|
111
71
|
//# sourceMappingURL=server-setup.js.map
|
package/dist/tools/actors.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare class ActorTools extends BaseTool implements IActorTools {
|
|
|
22
22
|
delete(params: {
|
|
23
23
|
actorName?: string;
|
|
24
24
|
actorNames?: string[];
|
|
25
|
-
}): Promise<any
|
|
25
|
+
}): Promise<StandardActionResponse<any>>;
|
|
26
26
|
applyForce(params: {
|
|
27
27
|
actorName: string;
|
|
28
28
|
force: {
|
|
@@ -30,7 +30,7 @@ export declare class ActorTools extends BaseTool implements IActorTools {
|
|
|
30
30
|
y: number;
|
|
31
31
|
z: number;
|
|
32
32
|
};
|
|
33
|
-
}): Promise<any
|
|
33
|
+
}): Promise<StandardActionResponse<any>>;
|
|
34
34
|
private resolveActorClass;
|
|
35
35
|
spawnBlueprint(params: {
|
|
36
36
|
blueprintPath: string;
|
|
@@ -45,7 +45,7 @@ export declare class ActorTools extends BaseTool implements IActorTools {
|
|
|
45
45
|
yaw: number;
|
|
46
46
|
roll: number;
|
|
47
47
|
};
|
|
48
|
-
}): Promise<any
|
|
48
|
+
}): Promise<StandardActionResponse<any>>;
|
|
49
49
|
setTransform(params: {
|
|
50
50
|
actorName: string;
|
|
51
51
|
location?: {
|
|
@@ -63,26 +63,30 @@ export declare class ActorTools extends BaseTool implements IActorTools {
|
|
|
63
63
|
y: number;
|
|
64
64
|
z: number;
|
|
65
65
|
};
|
|
66
|
-
}): Promise<any
|
|
67
|
-
getTransform(actorName: string): Promise<any
|
|
66
|
+
}): Promise<StandardActionResponse<any>>;
|
|
67
|
+
getTransform(actorName: string): Promise<StandardActionResponse<any>>;
|
|
68
68
|
setVisibility(params: {
|
|
69
69
|
actorName: string;
|
|
70
70
|
visible: boolean;
|
|
71
|
-
}): Promise<any
|
|
71
|
+
}): Promise<StandardActionResponse<any>>;
|
|
72
72
|
addComponent(params: {
|
|
73
73
|
actorName: string;
|
|
74
74
|
componentType: string;
|
|
75
75
|
componentName?: string;
|
|
76
76
|
properties?: Record<string, unknown>;
|
|
77
|
-
}): Promise<any
|
|
77
|
+
}): Promise<StandardActionResponse<any>>;
|
|
78
78
|
setComponentProperties(params: {
|
|
79
79
|
actorName: string;
|
|
80
80
|
componentName: string;
|
|
81
81
|
properties: Record<string, unknown>;
|
|
82
|
-
}): Promise<any
|
|
82
|
+
}): Promise<StandardActionResponse<any>>;
|
|
83
83
|
getComponents(actorName: string): Promise<{
|
|
84
84
|
success: boolean;
|
|
85
|
-
error:
|
|
85
|
+
error: string | {
|
|
86
|
+
[key: string]: unknown;
|
|
87
|
+
code?: string;
|
|
88
|
+
message: string;
|
|
89
|
+
};
|
|
86
90
|
message?: undefined;
|
|
87
91
|
components?: undefined;
|
|
88
92
|
count?: undefined;
|
|
@@ -101,45 +105,49 @@ export declare class ActorTools extends BaseTool implements IActorTools {
|
|
|
101
105
|
y: number;
|
|
102
106
|
z: number;
|
|
103
107
|
};
|
|
104
|
-
}): Promise<any
|
|
108
|
+
}): Promise<StandardActionResponse<any>>;
|
|
105
109
|
addTag(params: {
|
|
106
110
|
actorName: string;
|
|
107
111
|
tag: string;
|
|
108
|
-
}): Promise<any
|
|
112
|
+
}): Promise<StandardActionResponse<any>>;
|
|
109
113
|
removeTag(params: {
|
|
110
114
|
actorName: string;
|
|
111
115
|
tag: string;
|
|
112
|
-
}): Promise<any
|
|
116
|
+
}): Promise<StandardActionResponse<any>>;
|
|
113
117
|
findByTag(params: {
|
|
114
118
|
tag: string;
|
|
115
119
|
matchType?: string;
|
|
116
|
-
}): Promise<any
|
|
117
|
-
findByName(name: string): Promise<any
|
|
118
|
-
detach(actorName: string): Promise<any
|
|
120
|
+
}): Promise<StandardActionResponse<any>>;
|
|
121
|
+
findByName(name: string): Promise<StandardActionResponse<any>>;
|
|
122
|
+
detach(actorName: string): Promise<StandardActionResponse<any>>;
|
|
119
123
|
attach(params: {
|
|
120
124
|
childActor: string;
|
|
121
125
|
parentActor: string;
|
|
122
|
-
}): Promise<any
|
|
123
|
-
deleteByTag(tag: string): Promise<any
|
|
126
|
+
}): Promise<StandardActionResponse<any>>;
|
|
127
|
+
deleteByTag(tag: string): Promise<StandardActionResponse<any>>;
|
|
124
128
|
setBlueprintVariables(params: {
|
|
125
129
|
actorName: string;
|
|
126
130
|
variables: Record<string, unknown>;
|
|
127
|
-
}): Promise<any
|
|
131
|
+
}): Promise<StandardActionResponse<any>>;
|
|
128
132
|
createSnapshot(params: {
|
|
129
133
|
actorName: string;
|
|
130
134
|
snapshotName: string;
|
|
131
|
-
}): Promise<any
|
|
135
|
+
}): Promise<StandardActionResponse<any>>;
|
|
132
136
|
restoreSnapshot(params: {
|
|
133
137
|
actorName: string;
|
|
134
138
|
snapshotName: string;
|
|
135
|
-
}): Promise<any
|
|
139
|
+
}): Promise<StandardActionResponse<any>>;
|
|
136
140
|
exportActor(params: {
|
|
137
141
|
actorName: string;
|
|
138
142
|
destinationPath?: string;
|
|
139
|
-
}): Promise<any
|
|
143
|
+
}): Promise<StandardActionResponse<any>>;
|
|
140
144
|
getBoundingBox(actorName: string): Promise<{
|
|
141
145
|
success: boolean;
|
|
142
|
-
error:
|
|
146
|
+
error: string | {
|
|
147
|
+
[key: string]: unknown;
|
|
148
|
+
code?: string;
|
|
149
|
+
message: string;
|
|
150
|
+
};
|
|
143
151
|
message?: undefined;
|
|
144
152
|
boundingBox?: undefined;
|
|
145
153
|
} | {
|
|
@@ -150,7 +158,11 @@ export declare class ActorTools extends BaseTool implements IActorTools {
|
|
|
150
158
|
}>;
|
|
151
159
|
getMetadata(actorName: string): Promise<{
|
|
152
160
|
success: boolean;
|
|
153
|
-
error:
|
|
161
|
+
error: string | {
|
|
162
|
+
[key: string]: unknown;
|
|
163
|
+
code?: string;
|
|
164
|
+
message: string;
|
|
165
|
+
};
|
|
154
166
|
message?: undefined;
|
|
155
167
|
metadata?: undefined;
|
|
156
168
|
} | {
|
|
@@ -163,7 +175,11 @@ export declare class ActorTools extends BaseTool implements IActorTools {
|
|
|
163
175
|
filter?: string;
|
|
164
176
|
}): Promise<{
|
|
165
177
|
success: boolean;
|
|
166
|
-
error:
|
|
178
|
+
error: string | {
|
|
179
|
+
[key: string]: unknown;
|
|
180
|
+
code?: string;
|
|
181
|
+
message: string;
|
|
182
|
+
};
|
|
167
183
|
message?: undefined;
|
|
168
184
|
actors?: undefined;
|
|
169
185
|
count?: undefined;
|
package/dist/tools/actors.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ensureRotation, ensureVector3 } from '../utils/validation.js';
|
|
2
2
|
import { BaseTool } from './base-tool.js';
|
|
3
|
+
import { wasmIntegration } from '../wasm/index.js';
|
|
3
4
|
export class ActorTools extends BaseTool {
|
|
4
5
|
constructor(bridge) {
|
|
5
6
|
super(bridge);
|
|
@@ -38,7 +39,9 @@ export class ActorTools extends BaseTool {
|
|
|
38
39
|
meshPath: params.meshPath
|
|
39
40
|
}, timeoutMs ? { timeoutMs } : undefined);
|
|
40
41
|
if (!response || !response.success) {
|
|
41
|
-
|
|
42
|
+
const error = response?.error;
|
|
43
|
+
const errorMsg = typeof error === 'string' ? error : error?.message || response?.message || 'Failed to spawn actor';
|
|
44
|
+
throw new Error(errorMsg);
|
|
42
45
|
}
|
|
43
46
|
const data = response.data || {};
|
|
44
47
|
const result = {
|
|
@@ -292,7 +295,10 @@ export class ActorTools extends BaseTool {
|
|
|
292
295
|
}
|
|
293
296
|
if (params.offset) {
|
|
294
297
|
const offs = ensureVector3(params.offset, 'duplicate offset');
|
|
295
|
-
|
|
298
|
+
const origin = [0, 0, 0];
|
|
299
|
+
const calculatedOffset = wasmIntegration.vectorAdd(origin, offs);
|
|
300
|
+
console.error('[WASM] Using vectorAdd for duplicate offset calculation');
|
|
301
|
+
payload.offset = { x: calculatedOffset[0], y: calculatedOffset[1], z: calculatedOffset[2] };
|
|
296
302
|
}
|
|
297
303
|
return this.sendRequest('duplicate', payload, 'control_actor');
|
|
298
304
|
}
|