blueprint-extractor-mcp 1.0.0 → 1.2.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 +146 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,12 +6,72 @@ import { UEClient } from './ue-client.js';
|
|
|
6
6
|
const client = new UEClient();
|
|
7
7
|
const server = new McpServer({
|
|
8
8
|
name: 'blueprint-extractor',
|
|
9
|
-
version: '1.
|
|
9
|
+
version: '1.2.0',
|
|
10
10
|
});
|
|
11
|
+
// Shared scope enum with detailed descriptions
|
|
12
|
+
const scopeEnum = z.enum([
|
|
13
|
+
'ClassLevel',
|
|
14
|
+
'Variables',
|
|
15
|
+
'Components',
|
|
16
|
+
'FunctionsShallow',
|
|
17
|
+
'Full',
|
|
18
|
+
'FullWithBytecode',
|
|
19
|
+
]);
|
|
20
|
+
// Resource: extraction scope reference (static docs — app-controlled read-only context)
|
|
21
|
+
server.resource('extraction-scopes', 'blueprint://scopes', {
|
|
22
|
+
description: 'Reference for Blueprint extraction scopes: what each level includes, typical sizes, and when to use.',
|
|
23
|
+
}, async (uri) => ({
|
|
24
|
+
contents: [{
|
|
25
|
+
uri: uri.href,
|
|
26
|
+
mimeType: 'text/plain',
|
|
27
|
+
text: [
|
|
28
|
+
'Blueprint Extraction Scopes',
|
|
29
|
+
'',
|
|
30
|
+
'Each scope includes everything from the previous level:',
|
|
31
|
+
'',
|
|
32
|
+
'| Scope | Adds | Typical Size | Use When |',
|
|
33
|
+
'|-------------------|---------------------------------------------------|---------------|-----------------------------------------------|',
|
|
34
|
+
'| ClassLevel | Parent class, interfaces, class flags, metadata | 1-2 KB | Checking inheritance or interface list |',
|
|
35
|
+
'| Variables | All variables with types, defaults, flags | 2-10 KB | Understanding data model (DEFAULT) |',
|
|
36
|
+
'| Components | SCS component tree with property overrides vs CDO | 5-20 KB | Analyzing component composition |',
|
|
37
|
+
'| FunctionsShallow | Function and event graph names only | 5-25 KB | Listing available functions before deep dive |',
|
|
38
|
+
'| Full | Complete graph nodes, pins, and connections | 20-500+ KB | Understanding graph logic and execution flow |',
|
|
39
|
+
'| FullWithBytecode | Raw bytecode hex dump per function | Largest | Low-level analysis (rarely needed) |',
|
|
40
|
+
'',
|
|
41
|
+
'Start with the narrowest scope that answers your question.',
|
|
42
|
+
'Full scope on complex Blueprints can exceed 200KB and will be truncated.',
|
|
43
|
+
].join('\n'),
|
|
44
|
+
}],
|
|
45
|
+
}));
|
|
11
46
|
// Tool 1: extract_blueprint
|
|
12
|
-
server.
|
|
13
|
-
|
|
14
|
-
|
|
47
|
+
server.registerTool('extract_blueprint', {
|
|
48
|
+
title: 'Extract Blueprint',
|
|
49
|
+
description: `Extract a UE5 Blueprint asset to structured JSON.
|
|
50
|
+
|
|
51
|
+
USAGE GUIDELINES:
|
|
52
|
+
- Use search_assets first to find the correct asset path if you don't already have it.
|
|
53
|
+
- Start with the narrowest scope that answers your question — each level includes everything from the previous:
|
|
54
|
+
* ClassLevel — parent class, interfaces, class flags, metadata (~1-2KB)
|
|
55
|
+
* Variables — + all variables with types, defaults, flags (~2-10KB)
|
|
56
|
+
* Components — + SCS component tree with property overrides (~5-20KB)
|
|
57
|
+
* FunctionsShallow — + function/event graph names only (~5-25KB)
|
|
58
|
+
* Full — + complete graph nodes, pins, and connections (~20-500KB+)
|
|
59
|
+
* FullWithBytecode — + raw bytecode hex dump (largest, rarely needed)
|
|
60
|
+
- Only escalate to Full when you need to understand graph logic (node connections, pin values, execution flow).
|
|
61
|
+
- 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.
|
|
62
|
+
|
|
63
|
+
RETURNS: JSON object with the extracted Blueprint data at the requested scope level.`,
|
|
64
|
+
inputSchema: {
|
|
65
|
+
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.'),
|
|
66
|
+
scope: scopeEnum.default('Variables').describe('Extraction depth. Start with ClassLevel or Variables — only use Full when you need graph/node details.'),
|
|
67
|
+
},
|
|
68
|
+
annotations: {
|
|
69
|
+
title: 'Extract Blueprint',
|
|
70
|
+
readOnlyHint: true,
|
|
71
|
+
destructiveHint: false,
|
|
72
|
+
idempotentHint: true,
|
|
73
|
+
openWorldHint: false,
|
|
74
|
+
},
|
|
15
75
|
}, async ({ asset_path, scope }) => {
|
|
16
76
|
try {
|
|
17
77
|
const result = await client.callSubsystem('ExtractBlueprint', { AssetPath: asset_path, Scope: scope });
|
|
@@ -21,7 +81,7 @@ server.tool('extract_blueprint', 'Extract a UE5 Blueprint asset to JSON. Returns
|
|
|
21
81
|
}
|
|
22
82
|
const text = JSON.stringify(parsed, null, 2);
|
|
23
83
|
if (text.length > 200_000) {
|
|
24
|
-
return { content: [{ type: 'text', text: `Warning: Response is ${(text.length / 1024).toFixed(0)}KB
|
|
84
|
+
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]` }] };
|
|
25
85
|
}
|
|
26
86
|
return { content: [{ type: 'text', text }] };
|
|
27
87
|
}
|
|
@@ -30,8 +90,26 @@ server.tool('extract_blueprint', 'Extract a UE5 Blueprint asset to JSON. Returns
|
|
|
30
90
|
}
|
|
31
91
|
});
|
|
32
92
|
// Tool 2: extract_statetree
|
|
33
|
-
server.
|
|
34
|
-
|
|
93
|
+
server.registerTool('extract_statetree', {
|
|
94
|
+
title: 'Extract StateTree',
|
|
95
|
+
description: `Extract a UE5 StateTree asset to structured JSON.
|
|
96
|
+
|
|
97
|
+
USAGE GUIDELINES:
|
|
98
|
+
- Use search_assets first to find the asset path if needed (filter by class "StateTree").
|
|
99
|
+
- Returns the full state hierarchy: states, tasks, conditions, transitions, evaluators, and linked assets.
|
|
100
|
+
- Response size depends on StateTree complexity — typically 10-100KB.
|
|
101
|
+
|
|
102
|
+
RETURNS: JSON object with schema, state hierarchy, tasks, conditions, transitions, and linked assets.`,
|
|
103
|
+
inputSchema: {
|
|
104
|
+
asset_path: z.string().describe('UE content path to a StateTree asset (e.g. /Game/AI/ST_BotBehavior). Use search_assets with class_filter "StateTree" to find paths.'),
|
|
105
|
+
},
|
|
106
|
+
annotations: {
|
|
107
|
+
title: 'Extract StateTree',
|
|
108
|
+
readOnlyHint: true,
|
|
109
|
+
destructiveHint: false,
|
|
110
|
+
idempotentHint: true,
|
|
111
|
+
openWorldHint: false,
|
|
112
|
+
},
|
|
35
113
|
}, async ({ asset_path }) => {
|
|
36
114
|
try {
|
|
37
115
|
const result = await client.callSubsystem('ExtractStateTree', { AssetPath: asset_path });
|
|
@@ -46,10 +124,29 @@ server.tool('extract_statetree', 'Extract a UE5 StateTree asset to JSON. Returns
|
|
|
46
124
|
}
|
|
47
125
|
});
|
|
48
126
|
// Tool 3: extract_cascade
|
|
49
|
-
server.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
127
|
+
server.registerTool('extract_cascade', {
|
|
128
|
+
title: 'Extract Cascade',
|
|
129
|
+
description: `Extract multiple Blueprint/StateTree assets with automatic reference following. Follows parent classes, interfaces, component classes, and other Blueprint references up to max_depth levels deep.
|
|
130
|
+
|
|
131
|
+
USAGE GUIDELINES:
|
|
132
|
+
- Use when you need to understand an asset AND its dependencies (parent Blueprints, referenced Blueprints, etc.).
|
|
133
|
+
- 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.
|
|
134
|
+
- For a single asset without dependencies, prefer extract_blueprint or extract_statetree instead.
|
|
135
|
+
- Cycle-safe: won't extract the same asset twice.
|
|
136
|
+
|
|
137
|
+
RETURNS: Summary with extracted_count and output_directory path. Read the output files to inspect the data.`,
|
|
138
|
+
inputSchema: {
|
|
139
|
+
asset_paths: z.array(z.string()).describe('Array of UE content paths to extract (e.g. ["/Game/Blueprints/BP_Character", "/Game/Blueprints/BP_Weapon"])'),
|
|
140
|
+
scope: scopeEnum.default('Full').describe('Extraction depth applied to all assets. Full is the default since cascade is typically used for deep analysis.'),
|
|
141
|
+
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)'),
|
|
142
|
+
},
|
|
143
|
+
annotations: {
|
|
144
|
+
title: 'Extract Cascade',
|
|
145
|
+
readOnlyHint: false, // writes files to disk
|
|
146
|
+
destructiveHint: false,
|
|
147
|
+
idempotentHint: true,
|
|
148
|
+
openWorldHint: false,
|
|
149
|
+
},
|
|
53
150
|
}, async ({ asset_paths, scope, max_depth }) => {
|
|
54
151
|
try {
|
|
55
152
|
const result = await client.callSubsystem('ExtractCascade', {
|
|
@@ -68,9 +165,27 @@ server.tool('extract_cascade', 'Extract multiple Blueprint/StateTree assets with
|
|
|
68
165
|
}
|
|
69
166
|
});
|
|
70
167
|
// Tool 4: search_assets
|
|
71
|
-
server.
|
|
72
|
-
|
|
73
|
-
|
|
168
|
+
server.registerTool('search_assets', {
|
|
169
|
+
title: 'Search Assets',
|
|
170
|
+
description: `Search for UE5 assets by name. This is a lightweight lookup — use it FIRST to find correct asset paths before calling extract_blueprint or extract_statetree.
|
|
171
|
+
|
|
172
|
+
USAGE GUIDELINES:
|
|
173
|
+
- Always call this before extract_blueprint/extract_statetree if you don't already have the exact asset path.
|
|
174
|
+
- Searches asset names (not full paths) — partial matches work (e.g. "Character" finds "BP_Character").
|
|
175
|
+
- Filter by class to narrow results: "Blueprint" (default), "StateTree", "WidgetBlueprint", "DataAsset", or empty string for all.
|
|
176
|
+
|
|
177
|
+
RETURNS: JSON array of objects with path, name, and class for each matching asset.`,
|
|
178
|
+
inputSchema: {
|
|
179
|
+
query: z.string().describe('Search term to match against asset names. Partial matches work (e.g. "Player" finds "BP_PlayerCharacter").'),
|
|
180
|
+
class_filter: z.string().default('Blueprint').describe('Filter by asset class. Common values: "Blueprint", "WidgetBlueprint", "StateTree", "DataAsset", or "" for all asset types.'),
|
|
181
|
+
},
|
|
182
|
+
annotations: {
|
|
183
|
+
title: 'Search Assets',
|
|
184
|
+
readOnlyHint: true,
|
|
185
|
+
destructiveHint: false,
|
|
186
|
+
idempotentHint: true,
|
|
187
|
+
openWorldHint: false,
|
|
188
|
+
},
|
|
74
189
|
}, async ({ query, class_filter }) => {
|
|
75
190
|
try {
|
|
76
191
|
const result = await client.callSubsystem('SearchAssets', { Query: query, ClassFilter: class_filter });
|
|
@@ -85,10 +200,23 @@ server.tool('search_assets', 'Search for UE5 assets by name. Returns matching as
|
|
|
85
200
|
}
|
|
86
201
|
});
|
|
87
202
|
// Tool 5: list_assets
|
|
88
|
-
server.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
203
|
+
server.registerTool('list_assets', {
|
|
204
|
+
title: 'List Assets',
|
|
205
|
+
description: `List UE5 assets under a package path. Use this to browse directory contents when you don't know asset names. If you know (part of) the asset name, prefer search_assets instead — it's faster and doesn't require knowing the directory.
|
|
206
|
+
|
|
207
|
+
RETURNS: JSON array of objects with path, name, and class for each asset in the directory.`,
|
|
208
|
+
inputSchema: {
|
|
209
|
+
package_path: z.string().describe('UE package path to list (e.g. /Game/Blueprints, /Game/AI). Use /Game to list from the Content root.'),
|
|
210
|
+
recursive: z.boolean().default(true).describe('Whether to include assets in subdirectories.'),
|
|
211
|
+
class_filter: z.string().default('').describe('Filter by asset class (e.g. "Blueprint", "StateTree"). Empty string returns all asset types.'),
|
|
212
|
+
},
|
|
213
|
+
annotations: {
|
|
214
|
+
title: 'List Assets',
|
|
215
|
+
readOnlyHint: true,
|
|
216
|
+
destructiveHint: false,
|
|
217
|
+
idempotentHint: true,
|
|
218
|
+
openWorldHint: false,
|
|
219
|
+
},
|
|
92
220
|
}, async ({ package_path, recursive, class_filter }) => {
|
|
93
221
|
try {
|
|
94
222
|
const result = await client.callSubsystem('ListAssets', {
|