blueprint-extractor-mcp 1.6.0 → 1.8.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/dist/index.js +392 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { compactBlueprint } from './compactor.js';
|
|
|
7
7
|
const client = new UEClient();
|
|
8
8
|
const server = new McpServer({
|
|
9
9
|
name: 'blueprint-extractor',
|
|
10
|
-
version: '1.
|
|
10
|
+
version: '1.8.0',
|
|
11
11
|
});
|
|
12
12
|
// Shared scope enum with detailed descriptions
|
|
13
13
|
const scopeEnum = z.enum([
|
|
@@ -18,6 +18,14 @@ const scopeEnum = z.enum([
|
|
|
18
18
|
'Full',
|
|
19
19
|
'FullWithBytecode',
|
|
20
20
|
]);
|
|
21
|
+
const cascadeManifestEntrySchema = z.object({
|
|
22
|
+
assetPath: z.string(),
|
|
23
|
+
assetType: z.string(),
|
|
24
|
+
outputFile: z.string().optional(),
|
|
25
|
+
depth: z.number().int().min(0),
|
|
26
|
+
status: z.string(),
|
|
27
|
+
error: z.string().optional(),
|
|
28
|
+
});
|
|
21
29
|
// Resource: extraction scope reference (static docs — app-controlled read-only context)
|
|
22
30
|
server.resource('extraction-scopes', 'blueprint://scopes', {
|
|
23
31
|
description: 'Reference for Blueprint extraction scopes: what each level includes, typical sizes, and when to use.',
|
|
@@ -210,24 +218,370 @@ RETURNS: JSON object with row struct info, schema, and all rows.`,
|
|
|
210
218
|
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
211
219
|
}
|
|
212
220
|
});
|
|
213
|
-
// Tool 5:
|
|
221
|
+
// Tool 5: extract_behavior_tree
|
|
222
|
+
server.registerTool('extract_behavior_tree', {
|
|
223
|
+
title: 'Extract BehaviorTree',
|
|
224
|
+
description: `Extract a UE5 BehaviorTree asset to structured JSON.
|
|
225
|
+
|
|
226
|
+
USAGE GUIDELINES:
|
|
227
|
+
- Use search_assets first with class_filter "BehaviorTree" if you need to discover the asset path.
|
|
228
|
+
- Returns the full node hierarchy including root decorators, child decorators, decorator logic, services, task/composite nodes, and the linked blackboard asset.
|
|
229
|
+
- Useful for understanding AI decision flow without opening the editor graph.
|
|
230
|
+
|
|
231
|
+
RETURNS: JSON object with the BehaviorTree hierarchy, node properties, and blackboard reference.`,
|
|
232
|
+
inputSchema: {
|
|
233
|
+
asset_path: z.string().describe('UE content path to the BehaviorTree asset (e.g. /Game/AI/BT_MainAI). Use search_assets to find paths.'),
|
|
234
|
+
},
|
|
235
|
+
annotations: {
|
|
236
|
+
title: 'Extract BehaviorTree',
|
|
237
|
+
readOnlyHint: true,
|
|
238
|
+
destructiveHint: false,
|
|
239
|
+
idempotentHint: true,
|
|
240
|
+
openWorldHint: false,
|
|
241
|
+
},
|
|
242
|
+
}, async ({ asset_path }) => {
|
|
243
|
+
try {
|
|
244
|
+
const result = await client.callSubsystem('ExtractBehaviorTree', { AssetPath: asset_path });
|
|
245
|
+
const parsed = JSON.parse(result);
|
|
246
|
+
if (parsed.error) {
|
|
247
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
248
|
+
}
|
|
249
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
// Tool 6: extract_blackboard
|
|
256
|
+
server.registerTool('extract_blackboard', {
|
|
257
|
+
title: 'Extract Blackboard',
|
|
258
|
+
description: `Extract a UE5 Blackboard asset to structured JSON.
|
|
259
|
+
|
|
260
|
+
USAGE GUIDELINES:
|
|
261
|
+
- Use search_assets first with class_filter "Blackboard" or "BlackboardData" to find the asset path.
|
|
262
|
+
- Returns the effective key list, including inherited parent keys and local overrides.
|
|
263
|
+
- Key entries include type information and key-type-specific properties such as base class or enum binding.
|
|
264
|
+
|
|
265
|
+
RETURNS: JSON object with parent blackboard info and effective key definitions.`,
|
|
266
|
+
inputSchema: {
|
|
267
|
+
asset_path: z.string().describe('UE content path to the Blackboard asset (e.g. /Game/AI/BB_MainAI). Use search_assets to find paths.'),
|
|
268
|
+
},
|
|
269
|
+
annotations: {
|
|
270
|
+
title: 'Extract Blackboard',
|
|
271
|
+
readOnlyHint: true,
|
|
272
|
+
destructiveHint: false,
|
|
273
|
+
idempotentHint: true,
|
|
274
|
+
openWorldHint: false,
|
|
275
|
+
},
|
|
276
|
+
}, async ({ asset_path }) => {
|
|
277
|
+
try {
|
|
278
|
+
const result = await client.callSubsystem('ExtractBlackboard', { AssetPath: asset_path });
|
|
279
|
+
const parsed = JSON.parse(result);
|
|
280
|
+
if (parsed.error) {
|
|
281
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
282
|
+
}
|
|
283
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
284
|
+
}
|
|
285
|
+
catch (e) {
|
|
286
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
// Tool 7: extract_user_defined_struct
|
|
290
|
+
server.registerTool('extract_user_defined_struct', {
|
|
291
|
+
title: 'Extract UserDefinedStruct',
|
|
292
|
+
description: `Extract a UE5 UserDefinedStruct asset to structured JSON.
|
|
293
|
+
|
|
294
|
+
USAGE GUIDELINES:
|
|
295
|
+
- Use search_assets first with class_filter "UserDefinedStruct" to find the asset path.
|
|
296
|
+
- Returns field metadata, pin types, struct status, GUID, and typed default values from the struct default instance.
|
|
297
|
+
- Useful when DataTables or Blueprint variables depend on project-defined struct schemas.
|
|
298
|
+
|
|
299
|
+
RETURNS: JSON object with struct metadata and field definitions.`,
|
|
300
|
+
inputSchema: {
|
|
301
|
+
asset_path: z.string().describe('UE content path to the UserDefinedStruct asset (e.g. /Game/Data/S_ItemData). Use search_assets to find paths.'),
|
|
302
|
+
},
|
|
303
|
+
annotations: {
|
|
304
|
+
title: 'Extract UserDefinedStruct',
|
|
305
|
+
readOnlyHint: true,
|
|
306
|
+
destructiveHint: false,
|
|
307
|
+
idempotentHint: true,
|
|
308
|
+
openWorldHint: false,
|
|
309
|
+
},
|
|
310
|
+
}, async ({ asset_path }) => {
|
|
311
|
+
try {
|
|
312
|
+
const result = await client.callSubsystem('ExtractUserDefinedStruct', { AssetPath: asset_path });
|
|
313
|
+
const parsed = JSON.parse(result);
|
|
314
|
+
if (parsed.error) {
|
|
315
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
316
|
+
}
|
|
317
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
// Tool 8: extract_user_defined_enum
|
|
324
|
+
server.registerTool('extract_user_defined_enum', {
|
|
325
|
+
title: 'Extract UserDefinedEnum',
|
|
326
|
+
description: `Extract a UE5 UserDefinedEnum asset to structured JSON.
|
|
327
|
+
|
|
328
|
+
USAGE GUIDELINES:
|
|
329
|
+
- Use search_assets first with class_filter "UserDefinedEnum" to find the asset path.
|
|
330
|
+
- Returns the enum entries, display names, and numeric values, excluding the auto-generated MAX sentinel.
|
|
331
|
+
- Useful when gameplay data, DataAssets, or Blueprint logic refer to project enums.
|
|
332
|
+
|
|
333
|
+
RETURNS: JSON object with enum metadata and entry list.`,
|
|
334
|
+
inputSchema: {
|
|
335
|
+
asset_path: z.string().describe('UE content path to the UserDefinedEnum asset (e.g. /Game/Data/E_ItemRarity). Use search_assets to find paths.'),
|
|
336
|
+
},
|
|
337
|
+
annotations: {
|
|
338
|
+
title: 'Extract UserDefinedEnum',
|
|
339
|
+
readOnlyHint: true,
|
|
340
|
+
destructiveHint: false,
|
|
341
|
+
idempotentHint: true,
|
|
342
|
+
openWorldHint: false,
|
|
343
|
+
},
|
|
344
|
+
}, async ({ asset_path }) => {
|
|
345
|
+
try {
|
|
346
|
+
const result = await client.callSubsystem('ExtractUserDefinedEnum', { AssetPath: asset_path });
|
|
347
|
+
const parsed = JSON.parse(result);
|
|
348
|
+
if (parsed.error) {
|
|
349
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
350
|
+
}
|
|
351
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
// Tool 9: extract_curve
|
|
358
|
+
server.registerTool('extract_curve', {
|
|
359
|
+
title: 'Extract Curve',
|
|
360
|
+
description: `Extract a UE5 curve asset to structured JSON.
|
|
361
|
+
|
|
362
|
+
USAGE GUIDELINES:
|
|
363
|
+
- Use search_assets first with class_filter "Curve" to find curve assets such as CurveFloat, CurveVector, or CurveLinearColor.
|
|
364
|
+
- Returns per-channel keys, tangents, interpolation modes, and default/extrapolation settings.
|
|
365
|
+
- Useful for gameplay tuning curves, UI animation curves, and authored scalar/vector ramps.
|
|
366
|
+
|
|
367
|
+
RETURNS: JSON object with curve type and channel key data.`,
|
|
368
|
+
inputSchema: {
|
|
369
|
+
asset_path: z.string().describe('UE content path to the curve asset (e.g. /Game/Data/C_DamageOverTime). Use search_assets to find paths.'),
|
|
370
|
+
},
|
|
371
|
+
annotations: {
|
|
372
|
+
title: 'Extract Curve',
|
|
373
|
+
readOnlyHint: true,
|
|
374
|
+
destructiveHint: false,
|
|
375
|
+
idempotentHint: true,
|
|
376
|
+
openWorldHint: false,
|
|
377
|
+
},
|
|
378
|
+
}, async ({ asset_path }) => {
|
|
379
|
+
try {
|
|
380
|
+
const result = await client.callSubsystem('ExtractCurve', { AssetPath: asset_path });
|
|
381
|
+
const parsed = JSON.parse(result);
|
|
382
|
+
if (parsed.error) {
|
|
383
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
384
|
+
}
|
|
385
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
386
|
+
}
|
|
387
|
+
catch (e) {
|
|
388
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
// Tool 10: extract_curvetable
|
|
392
|
+
server.registerTool('extract_curvetable', {
|
|
393
|
+
title: 'Extract CurveTable',
|
|
394
|
+
description: `Extract a UE5 CurveTable asset to structured JSON.
|
|
395
|
+
|
|
396
|
+
USAGE GUIDELINES:
|
|
397
|
+
- Use search_assets first with class_filter "CurveTable" to find the asset path.
|
|
398
|
+
- Returns row names plus the per-row curve data for rich or simple curve tables.
|
|
399
|
+
- Useful for difficulty scaling, balance curves, and time/value tables authored in spreadsheet form.
|
|
400
|
+
|
|
401
|
+
RETURNS: JSON object with curve table mode and all rows.`,
|
|
402
|
+
inputSchema: {
|
|
403
|
+
asset_path: z.string().describe('UE content path to the CurveTable asset (e.g. /Game/Data/CT_DifficultyScaling). Use search_assets to find paths.'),
|
|
404
|
+
},
|
|
405
|
+
annotations: {
|
|
406
|
+
title: 'Extract CurveTable',
|
|
407
|
+
readOnlyHint: true,
|
|
408
|
+
destructiveHint: false,
|
|
409
|
+
idempotentHint: true,
|
|
410
|
+
openWorldHint: false,
|
|
411
|
+
},
|
|
412
|
+
}, async ({ asset_path }) => {
|
|
413
|
+
try {
|
|
414
|
+
const result = await client.callSubsystem('ExtractCurveTable', { AssetPath: asset_path });
|
|
415
|
+
const parsed = JSON.parse(result);
|
|
416
|
+
if (parsed.error) {
|
|
417
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
418
|
+
}
|
|
419
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
420
|
+
}
|
|
421
|
+
catch (e) {
|
|
422
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
// Tool 11: extract_material_instance
|
|
426
|
+
server.registerTool('extract_material_instance', {
|
|
427
|
+
title: 'Extract MaterialInstance',
|
|
428
|
+
description: `Extract a UE5 MaterialInstance asset to structured JSON.
|
|
429
|
+
|
|
430
|
+
USAGE GUIDELINES:
|
|
431
|
+
- Use search_assets first with class_filter "MaterialInstance" to find the asset path.
|
|
432
|
+
- Returns the parent material chain, base material, scalar/vector/texture parameters, runtime virtual texture parameters, font parameters, and static switch states.
|
|
433
|
+
- Useful for understanding authored look-dev overrides without opening the material editor.
|
|
434
|
+
|
|
435
|
+
RETURNS: JSON object with effective MaterialInstance parameter values.`,
|
|
436
|
+
inputSchema: {
|
|
437
|
+
asset_path: z.string().describe('UE content path to the MaterialInstance asset (e.g. /Game/Materials/MI_Character_Skin). Use search_assets to find paths.'),
|
|
438
|
+
},
|
|
439
|
+
annotations: {
|
|
440
|
+
title: 'Extract MaterialInstance',
|
|
441
|
+
readOnlyHint: true,
|
|
442
|
+
destructiveHint: false,
|
|
443
|
+
idempotentHint: true,
|
|
444
|
+
openWorldHint: false,
|
|
445
|
+
},
|
|
446
|
+
}, async ({ asset_path }) => {
|
|
447
|
+
try {
|
|
448
|
+
const result = await client.callSubsystem('ExtractMaterialInstance', { AssetPath: asset_path });
|
|
449
|
+
const parsed = JSON.parse(result);
|
|
450
|
+
if (parsed.error) {
|
|
451
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
452
|
+
}
|
|
453
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
454
|
+
}
|
|
455
|
+
catch (e) {
|
|
456
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
// Tool 12: extract_anim_sequence
|
|
460
|
+
server.registerTool('extract_anim_sequence', {
|
|
461
|
+
title: 'Extract AnimSequence',
|
|
462
|
+
description: `Extract a UE5 AnimSequence asset to structured JSON.
|
|
463
|
+
|
|
464
|
+
USAGE GUIDELINES:
|
|
465
|
+
- Use search_assets first with class_filter "AnimSequence" to find the asset path.
|
|
466
|
+
- Returns runtime-stable animation data: length, sample count, sampling rate, additive settings, notifies, authored sync markers, and runtime curve tracks.
|
|
467
|
+
- Useful for inspecting authored animation events and metadata without touching editor-only data models.
|
|
468
|
+
|
|
469
|
+
RETURNS: JSON object with AnimSequence metadata, notifies, sync markers, and curves.`,
|
|
470
|
+
inputSchema: {
|
|
471
|
+
asset_path: z.string().describe('UE content path to the AnimSequence asset (e.g. /Game/Animations/AS_Walk). Use search_assets to find paths.'),
|
|
472
|
+
},
|
|
473
|
+
annotations: {
|
|
474
|
+
title: 'Extract AnimSequence',
|
|
475
|
+
readOnlyHint: true,
|
|
476
|
+
destructiveHint: false,
|
|
477
|
+
idempotentHint: true,
|
|
478
|
+
openWorldHint: false,
|
|
479
|
+
},
|
|
480
|
+
}, async ({ asset_path }) => {
|
|
481
|
+
try {
|
|
482
|
+
const result = await client.callSubsystem('ExtractAnimSequence', { AssetPath: asset_path });
|
|
483
|
+
const parsed = JSON.parse(result);
|
|
484
|
+
if (parsed.error) {
|
|
485
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
486
|
+
}
|
|
487
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
488
|
+
}
|
|
489
|
+
catch (e) {
|
|
490
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
// Tool 13: extract_anim_montage
|
|
494
|
+
server.registerTool('extract_anim_montage', {
|
|
495
|
+
title: 'Extract AnimMontage',
|
|
496
|
+
description: `Extract a UE5 AnimMontage asset to structured JSON.
|
|
497
|
+
|
|
498
|
+
USAGE GUIDELINES:
|
|
499
|
+
- Use search_assets first with class_filter "AnimMontage" to find the asset path.
|
|
500
|
+
- Returns slot tracks, animation segments, montage sections, branching-point notifies, and standard notifies.
|
|
501
|
+
- Useful for understanding combat, traversal, and layered animation sequencing.
|
|
502
|
+
|
|
503
|
+
RETURNS: JSON object with montage structure, slots, sections, and notify data.`,
|
|
504
|
+
inputSchema: {
|
|
505
|
+
asset_path: z.string().describe('UE content path to the AnimMontage asset (e.g. /Game/Animations/AM_Attack). Use search_assets to find paths.'),
|
|
506
|
+
},
|
|
507
|
+
annotations: {
|
|
508
|
+
title: 'Extract AnimMontage',
|
|
509
|
+
readOnlyHint: true,
|
|
510
|
+
destructiveHint: false,
|
|
511
|
+
idempotentHint: true,
|
|
512
|
+
openWorldHint: false,
|
|
513
|
+
},
|
|
514
|
+
}, async ({ asset_path }) => {
|
|
515
|
+
try {
|
|
516
|
+
const result = await client.callSubsystem('ExtractAnimMontage', { AssetPath: asset_path });
|
|
517
|
+
const parsed = JSON.parse(result);
|
|
518
|
+
if (parsed.error) {
|
|
519
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
520
|
+
}
|
|
521
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
522
|
+
}
|
|
523
|
+
catch (e) {
|
|
524
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
// Tool 14: extract_blend_space
|
|
528
|
+
server.registerTool('extract_blend_space', {
|
|
529
|
+
title: 'Extract BlendSpace',
|
|
530
|
+
description: `Extract a UE5 BlendSpace asset to structured JSON.
|
|
531
|
+
|
|
532
|
+
USAGE GUIDELINES:
|
|
533
|
+
- Use search_assets first with class_filter "BlendSpace" to find the asset path.
|
|
534
|
+
- Returns axis definitions, sample count, sample coordinates, and referenced animations for 1D or 2D blend spaces.
|
|
535
|
+
- Useful for locomotion, aim offset, and directional blending analysis.
|
|
536
|
+
|
|
537
|
+
RETURNS: JSON object with BlendSpace axes and sample definitions.`,
|
|
538
|
+
inputSchema: {
|
|
539
|
+
asset_path: z.string().describe('UE content path to the BlendSpace asset (e.g. /Game/Animations/BS_Locomotion). Use search_assets to find paths.'),
|
|
540
|
+
},
|
|
541
|
+
annotations: {
|
|
542
|
+
title: 'Extract BlendSpace',
|
|
543
|
+
readOnlyHint: true,
|
|
544
|
+
destructiveHint: false,
|
|
545
|
+
idempotentHint: true,
|
|
546
|
+
openWorldHint: false,
|
|
547
|
+
},
|
|
548
|
+
}, async ({ asset_path }) => {
|
|
549
|
+
try {
|
|
550
|
+
const result = await client.callSubsystem('ExtractBlendSpace', { AssetPath: asset_path });
|
|
551
|
+
const parsed = JSON.parse(result);
|
|
552
|
+
if (parsed.error) {
|
|
553
|
+
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
554
|
+
}
|
|
555
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
556
|
+
}
|
|
557
|
+
catch (e) {
|
|
558
|
+
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
// Tool 15: extract_cascade
|
|
214
562
|
server.registerTool('extract_cascade', {
|
|
215
563
|
title: 'Extract Cascade',
|
|
216
|
-
description: `Extract multiple assets (Blueprint, AnimBlueprint, StateTree, DataAsset, DataTable) with automatic reference following for
|
|
564
|
+
description: `Extract multiple assets (Blueprint, AnimBlueprint, StateTree, BehaviorTree, Blackboard, DataAsset, DataTable, UserDefinedStruct, UserDefinedEnum, Curve, CurveTable, MaterialInstance, AnimSequence, AnimMontage, BlendSpace) with automatic reference following for supported dependency chains. Follows parent classes, interfaces, component classes, Blueprint references, blackboard links, material instance parents, and animation references up to max_depth levels deep.
|
|
217
565
|
|
|
218
566
|
USAGE GUIDELINES:
|
|
219
567
|
- Use when you need to understand an asset AND its dependencies (parent Blueprints, referenced Blueprints, etc.).
|
|
220
|
-
- Results are written to files on disk (in the project's configured output directory), NOT returned inline — the response
|
|
221
|
-
- For a single asset without dependencies, prefer
|
|
568
|
+
- Results are written to files on disk (in the project's configured output directory), NOT returned inline — the response contains a manifest summary with output filenames.
|
|
569
|
+
- For a single asset without dependencies, prefer the specific extract_* tool for that asset type.
|
|
222
570
|
- Cycle-safe: won't extract the same asset twice.
|
|
223
571
|
|
|
224
|
-
RETURNS: Summary with extracted_count and
|
|
572
|
+
RETURNS: Summary with extracted_count, output_directory path, and a per-asset manifest. Read the output files to inspect the data.`,
|
|
225
573
|
inputSchema: {
|
|
226
574
|
asset_paths: z.array(z.string()).describe('Array of UE content paths to extract (e.g. ["/Game/Blueprints/BP_Character", "/Game/Blueprints/BP_Weapon"])'),
|
|
227
575
|
scope: scopeEnum.default('Full').describe('Extraction depth applied to all assets. Full is the default since cascade is typically used for deep analysis.'),
|
|
228
576
|
max_depth: z.number().int().min(0).max(10).default(3).describe('How many levels deep to follow references (0 = only the listed assets, 3 = default)'),
|
|
229
577
|
graph_filter: z.array(z.string()).optional().describe('Filter to specific graphs by name. Use FunctionsShallow scope first to discover graph names, then pass the names you want here. Empty/omitted = extract all graphs. Example: ["EventGraph", "CalculateDamage"]'),
|
|
230
|
-
|
|
578
|
+
},
|
|
579
|
+
outputSchema: {
|
|
580
|
+
extracted_count: z.number().int().min(0),
|
|
581
|
+
skipped_count: z.number().int().min(0),
|
|
582
|
+
total_count: z.number().int().min(0),
|
|
583
|
+
output_directory: z.string(),
|
|
584
|
+
manifest: z.array(cascadeManifestEntrySchema),
|
|
231
585
|
},
|
|
232
586
|
annotations: {
|
|
233
587
|
title: 'Extract Cascade',
|
|
@@ -236,7 +590,7 @@ RETURNS: Summary with extracted_count and output_directory path. Read the output
|
|
|
236
590
|
idempotentHint: true,
|
|
237
591
|
openWorldHint: false,
|
|
238
592
|
},
|
|
239
|
-
}, async ({ asset_paths, scope, max_depth, graph_filter
|
|
593
|
+
}, async ({ asset_paths, scope, max_depth, graph_filter }) => {
|
|
240
594
|
try {
|
|
241
595
|
const result = await client.callSubsystem('ExtractCascade', {
|
|
242
596
|
AssetPathsJson: JSON.stringify(asset_paths),
|
|
@@ -248,7 +602,29 @@ RETURNS: Summary with extracted_count and output_directory path. Read the output
|
|
|
248
602
|
if (parsed.error) {
|
|
249
603
|
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
250
604
|
}
|
|
251
|
-
|
|
605
|
+
const manifest = Array.isArray(parsed.manifest)
|
|
606
|
+
? parsed.manifest
|
|
607
|
+
: Array.isArray(parsed.assets)
|
|
608
|
+
? parsed.assets
|
|
609
|
+
: [];
|
|
610
|
+
const totalCount = typeof parsed.total_count === 'number' ? parsed.total_count : manifest.length;
|
|
611
|
+
const extractedCount = typeof parsed.extracted_count === 'number'
|
|
612
|
+
? parsed.extracted_count
|
|
613
|
+
: manifest.filter((asset) => asset?.status === 'extracted').length;
|
|
614
|
+
const skippedCount = typeof parsed.skipped_count === 'number'
|
|
615
|
+
? parsed.skipped_count
|
|
616
|
+
: manifest.filter((asset) => asset?.status === 'skipped').length;
|
|
617
|
+
const structuredContent = {
|
|
618
|
+
extracted_count: extractedCount,
|
|
619
|
+
skipped_count: skippedCount,
|
|
620
|
+
total_count: totalCount,
|
|
621
|
+
output_directory: typeof parsed.output_directory === 'string' ? parsed.output_directory : '',
|
|
622
|
+
manifest,
|
|
623
|
+
};
|
|
624
|
+
return {
|
|
625
|
+
content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],
|
|
626
|
+
structuredContent,
|
|
627
|
+
};
|
|
252
628
|
}
|
|
253
629
|
catch (e) {
|
|
254
630
|
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|
|
@@ -257,17 +633,18 @@ RETURNS: Summary with extracted_count and output_directory path. Read the output
|
|
|
257
633
|
// Tool 6: search_assets
|
|
258
634
|
server.registerTool('search_assets', {
|
|
259
635
|
title: 'Search Assets',
|
|
260
|
-
description: `Search for UE5 assets by name. This is a lightweight lookup — use it FIRST to find correct asset paths before calling
|
|
636
|
+
description: `Search for UE5 assets by name. This is a lightweight lookup — use it FIRST to find correct asset paths before calling any extract_* tool.
|
|
261
637
|
|
|
262
638
|
USAGE GUIDELINES:
|
|
263
639
|
- Always call this before any extract_* tool if you don't already have the exact asset path.
|
|
264
640
|
- Searches asset names (not full paths) — partial matches work (e.g. "Character" finds "BP_Character").
|
|
265
|
-
- Filter by class to narrow results: "Blueprint" (default), "AnimBlueprint", "StateTree", "
|
|
641
|
+
- Filter by class to narrow results: "Blueprint" (default), "AnimBlueprint", "WidgetBlueprint", "StateTree", "BehaviorTree", "Blackboard", "DataAsset", "DataTable", "UserDefinedStruct", "UserDefinedEnum", "Curve", "CurveTable", "MaterialInstance", "AnimSequence", "AnimMontage", "BlendSpace", or empty string for all.
|
|
266
642
|
|
|
267
643
|
RETURNS: JSON array of objects with path, name, and class for each matching asset.`,
|
|
268
644
|
inputSchema: {
|
|
269
645
|
query: z.string().describe('Search term to match against asset names. Partial matches work (e.g. "Player" finds "BP_PlayerCharacter").'),
|
|
270
|
-
class_filter: z.string().default('Blueprint').describe('Filter by asset class. Common values: "Blueprint", "AnimBlueprint", "WidgetBlueprint", "StateTree", "
|
|
646
|
+
class_filter: z.string().default('Blueprint').describe('Filter by asset class. Common values: "Blueprint", "AnimBlueprint", "WidgetBlueprint", "StateTree", "BehaviorTree", "Blackboard", "DataAsset", "DataTable", "UserDefinedStruct", "UserDefinedEnum", "Curve", "CurveTable", "MaterialInstance", "AnimSequence", "AnimMontage", "BlendSpace", or "" for all asset types.'),
|
|
647
|
+
max_results: z.number().int().min(1).max(200).default(50).describe('Maximum number of results to return. Lower values keep the response small and the query fast.'),
|
|
271
648
|
},
|
|
272
649
|
annotations: {
|
|
273
650
|
title: 'Search Assets',
|
|
@@ -276,14 +653,14 @@ RETURNS: JSON array of objects with path, name, and class for each matching asse
|
|
|
276
653
|
idempotentHint: true,
|
|
277
654
|
openWorldHint: false,
|
|
278
655
|
},
|
|
279
|
-
}, async ({ query, class_filter }) => {
|
|
656
|
+
}, async ({ query, class_filter, max_results }) => {
|
|
280
657
|
try {
|
|
281
|
-
const result = await client.callSubsystem('SearchAssets', { Query: query, ClassFilter: class_filter });
|
|
658
|
+
const result = await client.callSubsystem('SearchAssets', { Query: query, ClassFilter: class_filter, MaxResults: max_results });
|
|
282
659
|
const parsed = JSON.parse(result);
|
|
283
660
|
if (parsed.error) {
|
|
284
661
|
return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
|
|
285
662
|
}
|
|
286
|
-
return { content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }] };
|
|
663
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed.results ?? [], null, 2) }] };
|
|
287
664
|
}
|
|
288
665
|
catch (e) {
|
|
289
666
|
return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
|