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.
Files changed (2) hide show
  1. package/dist/index.js +392 -15
  2. 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.6.0',
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: extract_cascade
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 Blueprints and StateTrees. Follows parent classes, interfaces, component classes, and other Blueprint references up to max_depth levels deep.
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 only contains a summary with file paths and count.
221
- - For a single asset without dependencies, prefer extract_blueprint or extract_statetree instead.
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 output_directory path. Read the output files to inspect the data.`,
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
- compact: z.boolean().default(false).describe('When true, strips low-value fields and minifies JSON to reduce size by ~50-70%. Removes: pinId, posX/posY, graphGuid, autogeneratedDefaultValue, nodeComment (when empty), empty connections, empty default_value, empty sub_category. Replaces full exec pin type objects with the string "exec".'),
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, compact }) => {
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
- return { content: [{ type: 'text', text: `Extracted ${parsed.extracted_count} assets to ${parsed.output_directory}` }] };
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 extract_blueprint, extract_statetree, extract_dataasset, or extract_datatable.
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", "DataTable", "WidgetBlueprint", "DataAsset", or empty string for all.
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", "DataTable", "DataAsset", or "" for all asset types.'),
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blueprint-extractor-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "MCP server for UE5 BlueprintExtractor plugin",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",