blueprint-extractor-mcp 1.3.0 → 1.4.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.
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Compacts Blueprint extraction JSON by stripping low-value fields and minifying.
3
+ * Reduces size by ~50-70% for LLM consumption.
4
+ */
5
+ /**
6
+ * Compacts a parsed Blueprint extraction result.
7
+ * - Removes positional data (posX, posY), GUIDs, empty fields
8
+ * - Replaces nodeGuid with sequential short IDs (n0, n1, ...)
9
+ * - Rewrites connection references to use short IDs
10
+ * - Replaces exec pin type objects with the string "exec"
11
+ */
12
+ export declare function compactBlueprint(data: unknown): unknown;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Compacts Blueprint extraction JSON by stripping low-value fields and minifying.
3
+ * Reduces size by ~50-70% for LLM consumption.
4
+ */
5
+ /**
6
+ * Compacts a parsed Blueprint extraction result.
7
+ * - Removes positional data (posX, posY), GUIDs, empty fields
8
+ * - Replaces nodeGuid with sequential short IDs (n0, n1, ...)
9
+ * - Rewrites connection references to use short IDs
10
+ * - Replaces exec pin type objects with the string "exec"
11
+ */
12
+ export function compactBlueprint(data) {
13
+ if (!data || typeof data !== 'object')
14
+ return data;
15
+ const root = data;
16
+ const bp = root.blueprint;
17
+ if (!bp)
18
+ return data;
19
+ const functions = bp.functions;
20
+ if (!Array.isArray(functions))
21
+ return data;
22
+ for (const graph of functions) {
23
+ compactGraph(graph);
24
+ }
25
+ return data;
26
+ }
27
+ function compactGraph(graph) {
28
+ // Remove graphGuid at graph level
29
+ delete graph.graphGuid;
30
+ const nodes = graph.nodes;
31
+ if (!Array.isArray(nodes))
32
+ return;
33
+ // Build guidToShortId map
34
+ const guidToShortId = new Map();
35
+ for (let i = 0; i < nodes.length; i++) {
36
+ const guid = nodes[i].nodeGuid;
37
+ if (guid) {
38
+ guidToShortId.set(guid, `n${i}`);
39
+ }
40
+ }
41
+ // Process each node
42
+ for (let i = 0; i < nodes.length; i++) {
43
+ const node = nodes[i];
44
+ // Replace nodeGuid with short ID
45
+ delete node.nodeGuid;
46
+ node.id = `n${i}`;
47
+ // Remove positional data
48
+ delete node.posX;
49
+ delete node.posY;
50
+ // Remove empty nodeComment
51
+ if (node.nodeComment === '') {
52
+ delete node.nodeComment;
53
+ }
54
+ // Process pins
55
+ const pins = node.pins;
56
+ if (Array.isArray(pins)) {
57
+ for (const pin of pins) {
58
+ compactPin(pin, guidToShortId);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ function compactPin(pin, guidToShortId) {
64
+ // Remove pinId
65
+ delete pin.pinId;
66
+ // Remove autogeneratedDefaultValue
67
+ delete pin.autogeneratedDefaultValue;
68
+ // Remove empty defaultValue
69
+ if (pin.defaultValue === '') {
70
+ delete pin.defaultValue;
71
+ }
72
+ // Process type object
73
+ const type = pin.type;
74
+ if (type && typeof type === 'object') {
75
+ // Remove empty sub_category
76
+ if (type.sub_category === '') {
77
+ delete type.sub_category;
78
+ }
79
+ // Replace exec pin type with string "exec"
80
+ if (type.category === 'exec') {
81
+ pin.type = 'exec';
82
+ }
83
+ }
84
+ // Rewrite connection nodeGuid references to short IDs
85
+ const connections = pin.connections;
86
+ if (Array.isArray(connections)) {
87
+ if (connections.length === 0) {
88
+ // Remove empty connections arrays
89
+ delete pin.connections;
90
+ }
91
+ else {
92
+ for (const conn of connections) {
93
+ const connGuid = conn.nodeGuid;
94
+ if (connGuid && guidToShortId.has(connGuid)) {
95
+ conn.nodeGuid = guidToShortId.get(connGuid);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
package/dist/index.js CHANGED
@@ -3,10 +3,11 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { z } from 'zod';
5
5
  import { UEClient } from './ue-client.js';
6
+ import { compactBlueprint } from './compactor.js';
6
7
  const client = new UEClient();
7
8
  const server = new McpServer({
8
9
  name: 'blueprint-extractor',
9
- version: '1.3.0',
10
+ version: '1.4.0',
10
11
  });
11
12
  // Shared scope enum with detailed descriptions
12
13
  const scopeEnum = z.enum([
@@ -62,11 +63,15 @@ USAGE GUIDELINES:
62
63
  * FullWithBytecode — + raw bytecode hex dump (largest, rarely needed)
63
64
  - Only escalate to Full when you need to understand graph logic (node connections, pin values, execution flow).
64
65
  - Full scope on complex Blueprints can exceed 200KB and will be truncated. If truncated, use a narrower scope or inspect specific functions via the graph names from FunctionsShallow.
66
+ - Use FunctionsShallow scope first to get graph names, then request specific graphs with graph_filter to reduce output size.
67
+ - Use compact=true to reduce JSON size by ~50-70% for LLM consumption.
65
68
 
66
69
  RETURNS: JSON object with the extracted Blueprint data at the requested scope level.`,
67
70
  inputSchema: {
68
71
  asset_path: z.string().describe('UE content path to the Blueprint asset. Must start with /Game/ (e.g. /Game/Blueprints/BP_Character). Use search_assets to find paths.'),
69
72
  scope: scopeEnum.default('Variables').describe('Extraction depth. Start with ClassLevel or Variables — only use Full when you need graph/node details.'),
73
+ 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"]'),
74
+ 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".'),
70
75
  },
71
76
  annotations: {
72
77
  title: 'Extract Blueprint',
@@ -75,14 +80,21 @@ RETURNS: JSON object with the extracted Blueprint data at the requested scope le
75
80
  idempotentHint: true,
76
81
  openWorldHint: false,
77
82
  },
78
- }, async ({ asset_path, scope }) => {
83
+ }, async ({ asset_path, scope, graph_filter, compact }) => {
79
84
  try {
80
- const result = await client.callSubsystem('ExtractBlueprint', { AssetPath: asset_path, Scope: scope });
81
- const parsed = JSON.parse(result);
85
+ const result = await client.callSubsystem('ExtractBlueprint', {
86
+ AssetPath: asset_path,
87
+ Scope: scope,
88
+ GraphFilter: graph_filter ? graph_filter.join(',') : '',
89
+ });
90
+ let parsed = JSON.parse(result);
82
91
  if (parsed.error) {
83
92
  return { content: [{ type: 'text', text: `Error: ${parsed.error}` }], isError: true };
84
93
  }
85
- const text = JSON.stringify(parsed, null, 2);
94
+ if (compact) {
95
+ parsed = compactBlueprint(parsed);
96
+ }
97
+ const text = compact ? JSON.stringify(parsed) : JSON.stringify(parsed, null, 2);
86
98
  if (text.length > 200_000) {
87
99
  return { content: [{ type: 'text', text: `Warning: Response is ${(text.length / 1024).toFixed(0)}KB — consider using a narrower scope (ClassLevel, Variables, or FunctionsShallow).\n\n${text.substring(0, 200_000)}...\n[TRUNCATED]` }] };
88
100
  }
@@ -214,6 +226,8 @@ RETURNS: Summary with extracted_count and output_directory path. Read the output
214
226
  asset_paths: z.array(z.string()).describe('Array of UE content paths to extract (e.g. ["/Game/Blueprints/BP_Character", "/Game/Blueprints/BP_Weapon"])'),
215
227
  scope: scopeEnum.default('Full').describe('Extraction depth applied to all assets. Full is the default since cascade is typically used for deep analysis.'),
216
228
  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
+ 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".'),
217
231
  },
218
232
  annotations: {
219
233
  title: 'Extract Cascade',
@@ -222,12 +236,13 @@ RETURNS: Summary with extracted_count and output_directory path. Read the output
222
236
  idempotentHint: true,
223
237
  openWorldHint: false,
224
238
  },
225
- }, async ({ asset_paths, scope, max_depth }) => {
239
+ }, async ({ asset_paths, scope, max_depth, graph_filter, compact }) => {
226
240
  try {
227
241
  const result = await client.callSubsystem('ExtractCascade', {
228
242
  AssetPathsJson: JSON.stringify(asset_paths),
229
243
  Scope: scope,
230
244
  MaxDepth: max_depth,
245
+ GraphFilter: graph_filter ? graph_filter.join(',') : '',
231
246
  });
232
247
  const parsed = JSON.parse(result);
233
248
  if (parsed.error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blueprint-extractor-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "MCP server for UE5 BlueprintExtractor plugin",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",