blueprint-extractor-mcp 6.0.3 → 6.0.5
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/README.md +17 -7
- package/dist/catalogs/example-catalog.js +108 -11
- package/dist/helpers/package-metadata.d.ts +2 -0
- package/dist/helpers/package-metadata.js +5 -0
- package/dist/helpers/project-resolution.d.ts +1 -0
- package/dist/helpers/project-resolution.js +2 -1
- package/dist/helpers/subsystem.js +2 -0
- package/dist/helpers/tool-help.js +31 -3
- package/dist/helpers/verification.js +58 -8
- package/dist/resources/example-and-capture-resources.js +4 -3
- package/dist/resources/static-doc-resources.js +12 -11
- package/dist/schemas/tool-inputs.d.ts +8 -8
- package/dist/server-config.js +9 -2
- package/dist/server-factory.js +2 -1
- package/dist/tool-context.d.ts +1 -0
- package/dist/tool-surface-manager.js +2 -1
- package/dist/tools/extraction.js +4 -1
- package/dist/tools/import-jobs.js +4 -1
- package/dist/tools/material-authoring.js +9 -3
- package/dist/tools/project-control.js +68 -0
- package/dist/tools/widget-verification.js +106 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Blueprint Extractor MCP is a [Model Context Protocol](https://modelcontextprotoc
|
|
|
23
23
|
AI Assistant stdio MCP Server HTTP :30010 Unreal Editor
|
|
24
24
|
───────────── ◄────────────► ───────────────── ◄──────────────────► ─────────────────
|
|
25
25
|
Claude Code Node.js process Remote Control API
|
|
26
|
-
Codex
|
|
26
|
+
Codex 95 tools BlueprintExtractor
|
|
27
27
|
... 4 resource templates plugin
|
|
28
28
|
8 prompts
|
|
29
29
|
```
|
|
@@ -35,9 +35,10 @@ Blueprint Extractor MCP is a [Model Context Protocol](https://modelcontextprotoc
|
|
|
35
35
|
| **Extract** | Read Blueprints, widgets, materials, animations, data assets, state trees, and more |
|
|
36
36
|
| **Author** | Create and modify widgets, materials, Blueprints, input actions, AI assets, data tables |
|
|
37
37
|
| **Build** | Compile project code, trigger Live Coding, restart the editor, sync changes |
|
|
38
|
+
| **PIE** | Start, stop, and relaunch Play-In-Editor sessions from the active editor |
|
|
38
39
|
| **Import** | Bring in textures, meshes, and generic assets with async job polling |
|
|
39
40
|
| **Test** | Run UE automation tests, collect results and artifacts |
|
|
40
|
-
| **Verify** | Capture widget screenshots, compare against references, inspect motion checkpoints |
|
|
41
|
+
| **Verify** | Capture widget previews, editor screenshots, runtime screenshots, compare against references, inspect motion checkpoints |
|
|
41
42
|
|
|
42
43
|
<br>
|
|
43
44
|
|
|
@@ -68,7 +69,7 @@ Connects to the editor at `127.0.0.1:30010` by default.
|
|
|
68
69
|
```bash
|
|
69
70
|
claude mcp add -s user -t stdio blueprint-extractor \
|
|
70
71
|
-e UE_REMOTE_CONTROL_PORT=30010 \
|
|
71
|
-
-- npx -y blueprint-extractor-mcp@
|
|
72
|
+
-- npx -y blueprint-extractor-mcp@6.0.5
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
</td></tr>
|
|
@@ -77,7 +78,7 @@ claude mcp add -s user -t stdio blueprint-extractor \
|
|
|
77
78
|
|
|
78
79
|
```bash
|
|
79
80
|
codex mcp add --env UE_REMOTE_CONTROL_PORT=30010 \
|
|
80
|
-
blueprint-extractor -- npx -y blueprint-extractor-mcp@
|
|
81
|
+
blueprint-extractor -- npx -y blueprint-extractor-mcp@6.0.5
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
</td></tr>
|
|
@@ -101,8 +102,8 @@ Only **~13 core tools** are visible by default to keep the context window lean.
|
|
|
101
102
|
| `animation_authoring` | 7 | Anim sequences, montages, blend spaces, widget motion |
|
|
102
103
|
| `data_tables` | 7 | Data assets, data tables, curves, Enhanced Input actions & mappings |
|
|
103
104
|
| `import` | 3 | `import_assets` with texture/mesh options, job polling |
|
|
104
|
-
| `automation_testing` |
|
|
105
|
-
| `verification` |
|
|
105
|
+
| `automation_testing` | 7 | `run_automation_tests`, run inspection, project automation context, PIE lifecycle control |
|
|
106
|
+
| `verification` | 9 | Widget captures, editor/runtime screenshots, motion checkpoint bundles, reference comparisons |
|
|
106
107
|
|
|
107
108
|
### Contract Design
|
|
108
109
|
|
|
@@ -115,6 +116,8 @@ The tool contract is optimized for model reliability:
|
|
|
115
116
|
- **Explicit-save semantics** — nothing persists until `save_assets` is called
|
|
116
117
|
- **Next-step hints** guiding the assistant toward the logical follow-up action
|
|
117
118
|
|
|
119
|
+
See [../docs/CURRENT_STATUS.md](../docs/CURRENT_STATUS.md) for the current validation snapshot, normative docs, and the one-shot stabilization ledger.
|
|
120
|
+
|
|
118
121
|
<br>
|
|
119
122
|
|
|
120
123
|
## Configuration
|
|
@@ -130,6 +133,8 @@ The tool contract is optimized for model reliability:
|
|
|
130
133
|
| `UE_BUILD_PLATFORM` | — | e.g. `Win64` |
|
|
131
134
|
| `UE_BUILD_CONFIGURATION` | — | e.g. `Development` |
|
|
132
135
|
|
|
136
|
+
`get_project_automation_context` surfaces the editor-derived `engineRoot`, `projectFilePath`, `editorTarget`, and `isPlayingInEditor` state that project-control and verification flows use for fallback or guard logic.
|
|
137
|
+
|
|
133
138
|
<br>
|
|
134
139
|
|
|
135
140
|
## Resources & Prompts
|
|
@@ -175,7 +180,12 @@ npm test # unit + stdio integration
|
|
|
175
180
|
| `npm run test:publish-gate` | Version consistency and publish readiness |
|
|
176
181
|
| `BLUEPRINT_EXTRACTOR_LIVE_E2E=1 npm run test:live` | Full end-to-end against a running editor |
|
|
177
182
|
|
|
178
|
-
The live suite exercises texture/mesh import via HTTP fixtures, material authoring workflows, Enhanced Input round-trips, and
|
|
183
|
+
The live suite exercises texture/mesh import via HTTP fixtures, material authoring workflows, Enhanced Input round-trips, widget authoring, and project-control round-trips.
|
|
184
|
+
|
|
185
|
+
The UE runner keeps two explicit lanes:
|
|
186
|
+
|
|
187
|
+
- headless/default: `BlueprintExtractor` with `-NullRHI`
|
|
188
|
+
- rendered verification: targeted filters with `-NoNullRHI` and optional `-AllowSoftwareRendering`
|
|
179
189
|
|
|
180
190
|
<br>
|
|
181
191
|
|
|
@@ -4,18 +4,18 @@ export const exampleCatalog = {
|
|
|
4
4
|
summary: 'Inspect the current widget, apply the smallest structural change that solves the layout problem, compile, visually confirm the rendered result, then save.',
|
|
5
5
|
recommended_flow: [
|
|
6
6
|
'extract_widget_blueprint',
|
|
7
|
-
'
|
|
8
|
-
'
|
|
7
|
+
'patch_widget',
|
|
8
|
+
'batch_widget_operations',
|
|
9
|
+
'compile_widget',
|
|
9
10
|
'capture_widget_preview',
|
|
10
11
|
'save_assets',
|
|
11
12
|
],
|
|
12
13
|
examples: [
|
|
13
14
|
{
|
|
14
15
|
title: 'patch_title_text',
|
|
15
|
-
tool: '
|
|
16
|
+
tool: 'patch_widget',
|
|
16
17
|
arguments: {
|
|
17
18
|
asset_path: '/Game/UI/WBP_Window',
|
|
18
|
-
operation: 'patch_widget',
|
|
19
19
|
widget_path: 'WindowRoot/TitleBar/TitleText',
|
|
20
20
|
properties: { Text: 'Window' },
|
|
21
21
|
compile_after: true,
|
|
@@ -23,10 +23,9 @@ export const exampleCatalog = {
|
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
title: 'insert_body_text',
|
|
26
|
-
tool: '
|
|
26
|
+
tool: 'batch_widget_operations',
|
|
27
27
|
arguments: {
|
|
28
28
|
asset_path: '/Game/UI/WBP_Window',
|
|
29
|
-
operation: 'batch',
|
|
30
29
|
operations: [
|
|
31
30
|
{
|
|
32
31
|
operation: 'insert_child',
|
|
@@ -49,10 +48,11 @@ export const exampleCatalog = {
|
|
|
49
48
|
'normalize_ui_design_input',
|
|
50
49
|
'design_menu_from_design_spec',
|
|
51
50
|
'extract_widget_blueprint',
|
|
52
|
-
'
|
|
51
|
+
'import_assets',
|
|
53
52
|
'create_material_instance',
|
|
54
|
-
'
|
|
55
|
-
'
|
|
53
|
+
'patch_widget',
|
|
54
|
+
'batch_widget_operations',
|
|
55
|
+
'compile_widget',
|
|
56
56
|
'capture_widget_preview',
|
|
57
57
|
'compare_capture_to_reference',
|
|
58
58
|
'save_assets',
|
|
@@ -60,10 +60,9 @@ export const exampleCatalog = {
|
|
|
60
60
|
examples: [
|
|
61
61
|
{
|
|
62
62
|
title: 'text_image_menu_screen',
|
|
63
|
-
tool: '
|
|
63
|
+
tool: 'patch_widget',
|
|
64
64
|
arguments: {
|
|
65
65
|
asset_path: '/Game/UI/Screens/WBP_MainMenu',
|
|
66
|
-
operation: 'patch_widget',
|
|
67
66
|
widget_path: 'WindowRoot/TitleBar/TitleText',
|
|
68
67
|
properties: { Text: 'Campaign' },
|
|
69
68
|
compile_after: true,
|
|
@@ -432,6 +431,64 @@ export const exampleCatalog = {
|
|
|
432
431
|
},
|
|
433
432
|
],
|
|
434
433
|
},
|
|
434
|
+
data_asset_instanced_graph: {
|
|
435
|
+
summary: 'Use generic DataAsset reflection for asset-owned config data, including inline instanced UObject graphs for UPROPERTY(Instanced) and EditInlineNew values.',
|
|
436
|
+
recommended_flow: [
|
|
437
|
+
'create_data_asset',
|
|
438
|
+
'modify_data_asset',
|
|
439
|
+
'extract_asset',
|
|
440
|
+
'save_assets',
|
|
441
|
+
],
|
|
442
|
+
examples: [
|
|
443
|
+
{
|
|
444
|
+
title: 'create_inline_object_graph',
|
|
445
|
+
tool: 'create_data_asset',
|
|
446
|
+
arguments: {
|
|
447
|
+
asset_class_path: '/Script/BlueprintExtractorFixture.BlueprintExtractorFixtureDataAsset',
|
|
448
|
+
asset_path: '/Game/Data/DA_MenuConfig',
|
|
449
|
+
properties: {
|
|
450
|
+
Count: 7,
|
|
451
|
+
InlineObject: {
|
|
452
|
+
classPath: '/Script/BlueprintExtractorFixture.BlueprintExtractorFixtureInlineObject',
|
|
453
|
+
properties: {
|
|
454
|
+
Label: 'Root',
|
|
455
|
+
Count: 11,
|
|
456
|
+
Child: {
|
|
457
|
+
classPath: '/Script/BlueprintExtractorFixture.BlueprintExtractorFixtureInlineObject',
|
|
458
|
+
properties: {
|
|
459
|
+
Label: 'Leaf',
|
|
460
|
+
Count: 12,
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
title: 'patch_inline_object_graph',
|
|
470
|
+
tool: 'modify_data_asset',
|
|
471
|
+
arguments: {
|
|
472
|
+
asset_path: '/Game/Data/DA_MenuConfig.DA_MenuConfig',
|
|
473
|
+
properties: {
|
|
474
|
+
Count: 8,
|
|
475
|
+
InlineObject: {
|
|
476
|
+
properties: {
|
|
477
|
+
Label: 'RootModified',
|
|
478
|
+
Count: 13,
|
|
479
|
+
Child: {
|
|
480
|
+
properties: {
|
|
481
|
+
Label: 'LeafModified',
|
|
482
|
+
Count: 14,
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
],
|
|
491
|
+
},
|
|
435
492
|
window_ui_polish: {
|
|
436
493
|
summary: 'Use the thin sequencing helper when a screen change touches variable flags, class defaults, compile, and optional code sync in one flow, then gate persistence on visual confirmation.',
|
|
437
494
|
recommended_flow: [
|
|
@@ -461,6 +518,46 @@ export const exampleCatalog = {
|
|
|
461
518
|
},
|
|
462
519
|
],
|
|
463
520
|
},
|
|
521
|
+
pie_and_screenshots: {
|
|
522
|
+
summary: 'Use explicit PIE lifecycle controls for live editor sessions, and use the editor/runtime screenshot tools when rendered evidence is needed in the shared verification-artifact format.',
|
|
523
|
+
recommended_flow: [
|
|
524
|
+
'get_project_automation_context',
|
|
525
|
+
'start_pie',
|
|
526
|
+
'capture_editor_screenshot',
|
|
527
|
+
'capture_runtime_screenshot',
|
|
528
|
+
'stop_pie',
|
|
529
|
+
],
|
|
530
|
+
examples: [
|
|
531
|
+
{
|
|
532
|
+
title: 'start_play_session',
|
|
533
|
+
tool: 'start_pie',
|
|
534
|
+
arguments: {
|
|
535
|
+
simulate: false,
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
title: 'capture_editor_viewport',
|
|
540
|
+
tool: 'capture_editor_screenshot',
|
|
541
|
+
arguments: {},
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
title: 'capture_runtime_frame_from_automation',
|
|
545
|
+
tool: 'capture_runtime_screenshot',
|
|
546
|
+
arguments: {
|
|
547
|
+
automation_filter: 'BlueprintExtractor.ProjectControl.PIEAndScreenshots',
|
|
548
|
+
engine_root: 'C:/Program Files/Epic Games/UE_5.7',
|
|
549
|
+
project_path: 'C:/Projects/MyGame/MyGame.uproject',
|
|
550
|
+
target: 'MyGameEditor',
|
|
551
|
+
null_rhi: false,
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
title: 'stop_play_session',
|
|
556
|
+
tool: 'stop_pie',
|
|
557
|
+
arguments: {},
|
|
558
|
+
},
|
|
559
|
+
],
|
|
560
|
+
},
|
|
464
561
|
project_code: {
|
|
465
562
|
summary: 'Use explicit changed_paths so build-vs-live-coding decisions stay deterministic.',
|
|
466
563
|
recommended_flow: [
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
const packageJson = require('../../package.json');
|
|
4
|
+
export const packageName = packageJson.name ?? 'blueprint-extractor-mcp';
|
|
5
|
+
export const packageVersion = packageJson.version ?? '0.0.0';
|
|
@@ -18,5 +18,6 @@ type ResolveProjectInputsDeps = {
|
|
|
18
18
|
};
|
|
19
19
|
export declare function rememberExternalBuild(result: CompileProjectCodeResult): Record<string, unknown>;
|
|
20
20
|
export declare function getProjectAutomationContext(deps: GetProjectAutomationContextDeps): Promise<ProjectAutomationContext>;
|
|
21
|
+
export declare const HEURISTIC_ENGINE_CANDIDATES: string[];
|
|
21
22
|
export declare function resolveProjectInputs(request: ProjectInputsRequest, deps: ResolveProjectInputsDeps): Promise<ResolvedProjectInputs>;
|
|
22
23
|
export {};
|
|
@@ -31,7 +31,8 @@ export async function getProjectAutomationContext(deps) {
|
|
|
31
31
|
return nextContext;
|
|
32
32
|
}
|
|
33
33
|
let cachedHeuristicEngineRoot;
|
|
34
|
-
const HEURISTIC_ENGINE_CANDIDATES = [
|
|
34
|
+
export const HEURISTIC_ENGINE_CANDIDATES = [
|
|
35
|
+
'C:/Program Files/Epic Games/UE_5.7',
|
|
35
36
|
'C:/Program Files/Epic Games/UE_5.6',
|
|
36
37
|
'C:/Program Files/Epic Games/UE_5.5',
|
|
37
38
|
'C:/Program Files/Epic Games/UE_5.4',
|
|
@@ -62,6 +62,8 @@ function extractFailureMessage(parsed) {
|
|
|
62
62
|
return parsed.errorMessage;
|
|
63
63
|
if (typeof parsed.error === 'string' && parsed.error.length > 0)
|
|
64
64
|
return parsed.error;
|
|
65
|
+
if (typeof parsed.errorSummary === 'string' && parsed.errorSummary.length > 0)
|
|
66
|
+
return parsed.errorSummary;
|
|
65
67
|
if (Array.isArray(parsed.diagnostics) && parsed.diagnostics.length > 0) {
|
|
66
68
|
const messages = parsed.diagnostics
|
|
67
69
|
.filter((d) => typeof d === 'object' && d !== null && typeof d.message === 'string')
|
|
@@ -101,12 +101,29 @@ export function summarizeOutputSchema(schema) {
|
|
|
101
101
|
return summary;
|
|
102
102
|
}
|
|
103
103
|
export function collectToolExampleFamilies(exampleCatalog, toolName) {
|
|
104
|
+
const equivalentToolNames = new Set([toolName]);
|
|
105
|
+
if (toolName === 'modify_widget_blueprint') {
|
|
106
|
+
[
|
|
107
|
+
'patch_widget',
|
|
108
|
+
'patch_widget_class_defaults',
|
|
109
|
+
'insert_widget_child',
|
|
110
|
+
'remove_widget',
|
|
111
|
+
'move_widget',
|
|
112
|
+
'wrap_widget',
|
|
113
|
+
'replace_widget_class',
|
|
114
|
+
'replace_widget_tree',
|
|
115
|
+
'batch_widget_operations',
|
|
116
|
+
].forEach((name) => equivalentToolNames.add(name));
|
|
117
|
+
}
|
|
118
|
+
if (toolName === 'compile_widget_blueprint') {
|
|
119
|
+
equivalentToolNames.add('compile_widget');
|
|
120
|
+
}
|
|
104
121
|
return Object.entries(exampleCatalog)
|
|
105
122
|
.flatMap(([family, entry]) => {
|
|
106
123
|
const exampleTitles = entry.examples
|
|
107
|
-
.filter((example) => example.tool
|
|
124
|
+
.filter((example) => equivalentToolNames.has(example.tool))
|
|
108
125
|
.map((example) => example.title);
|
|
109
|
-
const usedInRecommendedFlow = entry.recommended_flow.
|
|
126
|
+
const usedInRecommendedFlow = entry.recommended_flow.some((name) => equivalentToolNames.has(name));
|
|
110
127
|
if (!usedInRecommendedFlow && exampleTitles.length === 0) {
|
|
111
128
|
return [];
|
|
112
129
|
}
|
|
@@ -136,6 +153,14 @@ export function collectRelatedResources(toolName) {
|
|
|
136
153
|
resources.add('blueprint://widget-best-practices');
|
|
137
154
|
resources.add('blueprint://verification-workflows');
|
|
138
155
|
}
|
|
156
|
+
if (toolName === 'capture_editor_screenshot'
|
|
157
|
+
|| toolName === 'capture_runtime_screenshot'
|
|
158
|
+
|| toolName === 'start_pie'
|
|
159
|
+
|| toolName === 'stop_pie'
|
|
160
|
+
|| toolName === 'relaunch_pie') {
|
|
161
|
+
resources.add('blueprint://verification-workflows');
|
|
162
|
+
resources.add('blueprint://project-automation');
|
|
163
|
+
}
|
|
139
164
|
if (toolName.includes('material')) {
|
|
140
165
|
resources.add('blueprint://material-graph-guidance');
|
|
141
166
|
}
|
|
@@ -147,7 +172,10 @@ export function collectRelatedResources(toolName) {
|
|
|
147
172
|
|| toolName === 'trigger_live_coding'
|
|
148
173
|
|| toolName === 'restart_editor'
|
|
149
174
|
|| toolName === 'wait_for_editor'
|
|
150
|
-
|| toolName === 'sync_project_code'
|
|
175
|
+
|| toolName === 'sync_project_code'
|
|
176
|
+
|| toolName === 'start_pie'
|
|
177
|
+
|| toolName === 'stop_pie'
|
|
178
|
+
|| toolName === 'relaunch_pie') {
|
|
151
179
|
resources.add('blueprint://project-automation');
|
|
152
180
|
}
|
|
153
181
|
if (toolName.includes('animation')
|
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
import { buildCaptureResourceUri } from './capture.js';
|
|
2
2
|
import { firstDefinedString, isPlainObject, } from './formatting.js';
|
|
3
|
-
function
|
|
3
|
+
function normalizeVerificationSurface(value) {
|
|
4
|
+
if (value === 'editor_viewport') {
|
|
5
|
+
return 'editor_tool_viewport';
|
|
6
|
+
}
|
|
7
|
+
if (value === 'runtime_viewport') {
|
|
8
|
+
return 'pie_runtime';
|
|
9
|
+
}
|
|
4
10
|
return value === 'editor_offscreen'
|
|
5
11
|
|| value === 'pie_runtime'
|
|
6
12
|
|| value === 'editor_tool_viewport'
|
|
7
13
|
|| value === 'external_packaged'
|
|
8
|
-
|| value === 'widget_motion_checkpoint'
|
|
14
|
+
|| value === 'widget_motion_checkpoint'
|
|
15
|
+
? value
|
|
16
|
+
: undefined;
|
|
17
|
+
}
|
|
18
|
+
function isVerificationSurface(value) {
|
|
19
|
+
return normalizeVerificationSurface(value) !== undefined;
|
|
9
20
|
}
|
|
10
21
|
function inferVerificationSurface(captureType) {
|
|
11
|
-
|
|
12
|
-
|
|
22
|
+
const normalizedSurface = normalizeVerificationSurface(captureType);
|
|
23
|
+
if (normalizedSurface) {
|
|
24
|
+
return normalizedSurface;
|
|
13
25
|
}
|
|
14
26
|
switch (captureType) {
|
|
27
|
+
case 'editor_screenshot':
|
|
28
|
+
return 'editor_tool_viewport';
|
|
29
|
+
case 'runtime_screenshot':
|
|
30
|
+
case 'automation_screenshot':
|
|
31
|
+
case 'automation_image_artifact':
|
|
32
|
+
case 'automation_diff':
|
|
33
|
+
return 'pie_runtime';
|
|
15
34
|
case 'widget_motion_checkpoint':
|
|
16
35
|
return 'widget_motion_checkpoint';
|
|
17
36
|
case 'widget_preview':
|
|
@@ -54,6 +73,28 @@ function buildDefaultWorldContext(payload, surface) {
|
|
|
54
73
|
}
|
|
55
74
|
return context;
|
|
56
75
|
}
|
|
76
|
+
if (surface === 'editor_tool_viewport') {
|
|
77
|
+
return {
|
|
78
|
+
contextType: 'editor_viewport',
|
|
79
|
+
renderLane: 'viewport',
|
|
80
|
+
...(typeof payload.assetPath === 'string' && payload.assetPath.length > 0
|
|
81
|
+
? { assetPath: payload.assetPath }
|
|
82
|
+
: {}),
|
|
83
|
+
...(typeof payload.isPlayingInEditor === 'boolean'
|
|
84
|
+
? { isPlayingInEditor: payload.isPlayingInEditor }
|
|
85
|
+
: { isPlayingInEditor: false }),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (surface === 'pie_runtime') {
|
|
89
|
+
return {
|
|
90
|
+
contextType: 'runtime_viewport',
|
|
91
|
+
renderLane: 'viewport',
|
|
92
|
+
...(typeof payload.assetPath === 'string' && payload.assetPath.length > 0
|
|
93
|
+
? { assetPath: payload.assetPath }
|
|
94
|
+
: {}),
|
|
95
|
+
isPlayingInEditor: true,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
57
98
|
if (surface === 'widget_motion_checkpoint') {
|
|
58
99
|
const context = {
|
|
59
100
|
contextType: 'widget_motion',
|
|
@@ -91,6 +132,13 @@ function buildDefaultCameraContext(payload, surface) {
|
|
|
91
132
|
...(typeof height === 'number' ? { height } : {}),
|
|
92
133
|
};
|
|
93
134
|
}
|
|
135
|
+
if (surface === 'editor_tool_viewport' || surface === 'pie_runtime') {
|
|
136
|
+
return {
|
|
137
|
+
contextType: surface === 'editor_tool_viewport' ? 'editor_viewport' : 'runtime_viewport',
|
|
138
|
+
...(typeof width === 'number' ? { width } : {}),
|
|
139
|
+
...(typeof height === 'number' ? { height } : {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
94
142
|
if (surface === 'widget_motion_checkpoint') {
|
|
95
143
|
return {
|
|
96
144
|
contextType: 'motion_checkpoint',
|
|
@@ -127,7 +175,7 @@ function normalizeAutomationVerificationArtifacts(payload) {
|
|
|
127
175
|
return existing.map((artifact) => {
|
|
128
176
|
const normalized = normalizeVerificationArtifact({
|
|
129
177
|
...artifact,
|
|
130
|
-
surface:
|
|
178
|
+
surface: normalizeVerificationSurface(artifact.surface) ?? 'pie_runtime',
|
|
131
179
|
captureType: typeof artifact.captureType === 'string' && artifact.captureType.length > 0
|
|
132
180
|
? artifact.captureType
|
|
133
181
|
: 'automation_image_artifact',
|
|
@@ -198,9 +246,11 @@ export function normalizeVerificationArtifact(payload) {
|
|
|
198
246
|
: assetPath
|
|
199
247
|
? [assetPath]
|
|
200
248
|
: [];
|
|
201
|
-
const surface =
|
|
202
|
-
|
|
203
|
-
|
|
249
|
+
const surface = normalizeVerificationSurface(basePayload.surface)
|
|
250
|
+
?? (isVerificationSurface(basePayload.surface)
|
|
251
|
+
? basePayload.surface
|
|
252
|
+
: undefined)
|
|
253
|
+
?? inferVerificationSurface(basePayload.captureType);
|
|
204
254
|
const worldContext = buildDefaultWorldContext(basePayload, surface);
|
|
205
255
|
const cameraContext = buildDefaultCameraContext(basePayload, surface);
|
|
206
256
|
return {
|
|
@@ -260,7 +260,8 @@ export function registerExampleAndCaptureResources({ server, automationControlle
|
|
|
260
260
|
'Blueprint Extractor Unsupported Surfaces',
|
|
261
261
|
'',
|
|
262
262
|
'- Generic create_data_asset and modify_data_asset reject Enhanced Input asset classes. Use the dedicated InputAction/InputMappingContext tools instead.',
|
|
263
|
-
'-
|
|
263
|
+
'- Generic DataAsset reflection does support inline instanced UObject graphs for asset-owned config, but it does not support literal embedded AActor instances.',
|
|
264
|
+
'- modify_material remains available as an advanced escape hatch, but the canonical authoring path is create_material plus material_graph_operation for settings, node creation, wiring, and root-property binding.',
|
|
264
265
|
'- There is still no first-class Substrate graph DSL.',
|
|
265
266
|
'- CommonUI wrapper widgets are not a backdoor into internal Slate/UButton background or style fields. For CommonButtonBase-family widgets, treat raw UButton background/style properties as unsupported and use extract_commonui_button_style, create_commonui_button_style, modify_commonui_button_style, or apply_commonui_button_style.',
|
|
266
267
|
'- Dedicated widget animation authoring is supported only for the constrained supported track subset. Unsupported track families and broader arbitrary MovieScene synthesis remain outside the public contract.',
|
|
@@ -281,9 +282,9 @@ export function registerExampleAndCaptureResources({ server, automationControlle
|
|
|
281
282
|
'2. search_assets and extract the current HUD, transition widgets, and target screen widgets.',
|
|
282
283
|
'3. Inspect class defaults, BindWidget names, and current activatable-window flow before replacing any widget tree.',
|
|
283
284
|
'4. Choose a preset layout pattern such as centered_overlay, common_menu_shell, activatable_window, or list_detail.',
|
|
284
|
-
'5. Apply the smallest
|
|
285
|
+
'5. Apply the smallest widget operation possible: patch_widget, insert_widget_child, remove_widget, move_widget, wrap_widget, replace_widget_class, or batch_widget_operations. Only use replace_widget_tree when broad structure must change.',
|
|
285
286
|
'6. If the redesign includes authored motion on the supported track subset, use create_widget_animation or modify_widget_animation instead of trying to encode that work through generic widget patches.',
|
|
286
|
-
'7. Compile immediately after structural or animation changes. If compile fails, inspect compile diagnostics and rerun the smallest recovery patch first.',
|
|
287
|
+
'7. Compile immediately after structural or animation changes with compile_widget. If compile fails, inspect compile diagnostics and rerun the smallest recovery patch first.',
|
|
287
288
|
'8. Run capture_widget_preview or capture_widget_motion_checkpoints after the compile result is clean so the rendered result is visually confirmed for each required checkpoint.',
|
|
288
289
|
'9. If reference images or checkpoint frames exist, run compare_capture_to_reference or compare_motion_capture_bundle for key states before save_assets.',
|
|
289
290
|
'10. Save after capture or compare succeeds, or report lower-confidence / partial verification explicitly when the visual checkpoint is blocked.',
|
|
@@ -38,13 +38,13 @@ export function registerStaticDocResources(server) {
|
|
|
38
38
|
'- Write responses include: success, operation, assetPath, assetClass, changedObjects, dirtyPackages, diagnostics, and optional validation/compile summaries.',
|
|
39
39
|
'',
|
|
40
40
|
'Current write-capable families:',
|
|
41
|
-
'- WidgetBlueprint: create_widget_blueprint,
|
|
41
|
+
'- WidgetBlueprint: create_widget_blueprint, replace_widget_tree, patch_widget, insert_widget_child, remove_widget, move_widget, wrap_widget, replace_widget_class, batch_widget_operations, compile_widget. Deprecated compatibility aliases remain available for modify_widget_blueprint, build_widget_tree, and compile_widget_blueprint.',
|
|
42
42
|
'- CommonUI button style Blueprints: extract_commonui_button_style, create_commonui_button_style, modify_commonui_button_style, apply_commonui_button_style',
|
|
43
|
-
'- DataAsset: create_data_asset, modify_data_asset',
|
|
43
|
+
'- DataAsset: create_data_asset, modify_data_asset. Inline instanced UObject graphs are supported for UPROPERTY(Instanced) / EditInlineNew values.',
|
|
44
44
|
'- DataTable: create_data_table, modify_data_table',
|
|
45
45
|
'- Curve: create_curve, modify_curve',
|
|
46
46
|
'- CurveTable: create_curve_table, modify_curve_table',
|
|
47
|
-
'- Material graph assets: extract_material, create_material, modify_material,
|
|
47
|
+
'- Material graph assets: extract_material, create_material, modify_material, compile_material_asset. Use asset_kind="function", "layer", or "layer_blend" for material-family variants.',
|
|
48
48
|
'- MaterialInstance: create_material_instance, modify_material_instance',
|
|
49
49
|
'- UserDefinedStruct: create_user_defined_struct, modify_user_defined_struct',
|
|
50
50
|
'- UserDefinedEnum: create_user_defined_enum, modify_user_defined_enum',
|
|
@@ -73,11 +73,11 @@ export function registerStaticDocResources(server) {
|
|
|
73
73
|
'- Blueprint graphs: operation upsert_function_graphs preserves unrelated graphs; append_function_call_to_sequence patches an existing sequence-style initializer without replacing the whole graph.',
|
|
74
74
|
'',
|
|
75
75
|
'WidgetBlueprint guidance:',
|
|
76
|
-
'-
|
|
76
|
+
'- replace_widget_tree is the destructive bootstrap path for whole-tree replacement.',
|
|
77
77
|
'- extract_widget_blueprint returns a compact authoring snapshot with widgetPath annotations and additive packagePath/objectPath fields.',
|
|
78
78
|
'- modify_widget supports direct widget_name or widget_path patches for one widget.',
|
|
79
|
-
'-
|
|
80
|
-
'-
|
|
79
|
+
'- Prefer operation-specific widget tools such as patch_widget, patch_widget_class_defaults, insert_widget_child, remove_widget, move_widget, wrap_widget, replace_widget_class, and batch_widget_operations.',
|
|
80
|
+
'- compile_widget validates the asset but still does not save it.',
|
|
81
81
|
'- apply_window_ui_changes is a thin MCP helper that sequences variable-flag updates, class defaults, optional font work, compile, optional save, and optional code sync.',
|
|
82
82
|
'',
|
|
83
83
|
'Explicit deferrals:',
|
|
@@ -130,7 +130,7 @@ export function registerStaticDocResources(server) {
|
|
|
130
130
|
'- Keep payloads small by sending only changed fields, not full extracted objects, unless the tool explicitly expects a full replacement payload.',
|
|
131
131
|
'- Re-extract after mutation when you need confirmation; do not assume UE normalized fields exactly as sent.',
|
|
132
132
|
'- For fidelity-sensitive menu work, normalize text, image, Figma-export, or HTML/CSS inputs into design_spec_json before authoring assets.',
|
|
133
|
-
'- For multi-step widget work, prefer extract_widget_blueprint ->
|
|
133
|
+
'- For multi-step widget work, prefer extract_widget_blueprint -> patch_widget or batch_widget_operations -> compile_widget -> capture_widget_preview -> save_assets.',
|
|
134
134
|
'- For authored widget motion, prefer extract_widget_animation -> create_widget_animation or modify_widget_animation -> capture_widget_motion_checkpoints -> compare_motion_capture_bundle before save_assets.',
|
|
135
135
|
'- When reference frames exist, extend the widget flow to compare_capture_to_reference before save_assets. Without reference frames, capture and report lower-confidence or partial verification explicitly.',
|
|
136
136
|
'- If widget preview capture is blocked, report partial verification explicitly instead of treating compile/save as visual proof.',
|
|
@@ -218,12 +218,13 @@ export function registerStaticDocResources(server) {
|
|
|
218
218
|
registerStaticTextResource(server, 'project-automation-guidance', 'blueprint://project-automation', 'Host/editor project automation guidance for build, Live Coding, restart, and reconnect flows.', [
|
|
219
219
|
'Blueprint Extractor Project Automation',
|
|
220
220
|
'',
|
|
221
|
-
'- get_project_automation_context returns the editor-derived engine root, project file path,
|
|
221
|
+
'- get_project_automation_context returns the editor-derived engine root, project file path, editor target, and isPlayingInEditor state that project-control tools use as their first fallback.',
|
|
222
222
|
'- compile_project_code runs an external UBT build from the MCP host.',
|
|
223
223
|
'- compile_project_code and sync_project_code resolve engine_root, project_path, and target in this order: explicit args -> editor context -> environment.',
|
|
224
224
|
'- trigger_live_coding requests an editor-side Live Coding compile and is only supported on Windows-focused setups. changed_paths remains an accepted compatibility input but the current editor-side trigger ignores it. When Live Coding reports NoChanges or another fallback state, the result includes fallbackRecommended, reason, and the last external build context when available.',
|
|
225
225
|
'- restart_editor requests an editor restart, then waits for Remote Control to disconnect and reconnect. When save_dirty_assets is true, all dirty packages are saved before the restart to prevent modal save dialogs.',
|
|
226
226
|
'- wait_for_editor polls Remote Control once per second and returns a normalized readiness result that callers can use after restart windows or transient disconnects.',
|
|
227
|
+
'- start_pie, stop_pie, and relaunch_pie are the explicit live-editor PIE lifecycle controls.',
|
|
227
228
|
'- sync_project_code requires explicit changed_paths and chooses Live Coding vs build_and_restart deterministically.',
|
|
228
229
|
'- sync_project_code.restart_first now means shutdown-first: save/checkpoint if requested, ask the editor to close without relaunching, build with the DLL unlocked, then launch the editor from the MCP host and wait for reconnect.',
|
|
229
230
|
'- apply_window_ui_changes can checkpoint after each mutation step without changing the low-level explicit-save contract. Use that when debugging editor ensures or breakpoint-heavy UI iterations.',
|
|
@@ -239,8 +240,8 @@ export function registerStaticDocResources(server) {
|
|
|
239
240
|
'Blueprint Extractor Verification Workflows',
|
|
240
241
|
'',
|
|
241
242
|
'Three verification lanes work together:',
|
|
242
|
-
'- Semantic verification: use extract_blueprint, extract_widget_blueprint, extract_asset,
|
|
243
|
-
'- Visual verification: use capture_widget_preview and compare_capture_to_reference when structure can compile but the rendered result may still be wrong.',
|
|
243
|
+
'- Semantic verification: use extract_blueprint, extract_widget_blueprint, extract_asset, compile_widget, compile_material_asset, and compile/save diagnostics to verify authored data, graph wiring, widget hierarchy, class defaults, and graph layout.',
|
|
244
|
+
'- Visual verification: use capture_widget_preview, capture_editor_screenshot, capture_runtime_screenshot, and compare_capture_to_reference when structure can compile but the rendered result may still be wrong.',
|
|
244
245
|
'- Gameplay/runtime verification: use run_automation_tests for mechanics, behavior, interactions, and scenario validation through Automation Specs or Functional Tests.',
|
|
245
246
|
'',
|
|
246
247
|
'Recommended mapping:',
|
|
@@ -287,7 +288,7 @@ export function registerStaticDocResources(server) {
|
|
|
287
288
|
'1. Normalize raw text, image, PNG/Figma, or HTML/CSS inputs into design_spec_json.',
|
|
288
289
|
'2. Inspect the current widget, HUD, transition widgets, and class defaults before rewriting structure.',
|
|
289
290
|
'3. Author reusable foundation assets first under /Game/UI/Foundation/Materials, /Game/UI/Foundation/MaterialInstances, /Game/UI/Foundation/Styles, and /Game/UI/Foundation/Widgets.',
|
|
290
|
-
'4. Assemble the menu screen under /Game/UI/Screens with
|
|
291
|
+
'4. Assemble the menu screen under /Game/UI/Screens with patch_widget, batch_widget_operations, and related widget tools.',
|
|
291
292
|
'5. If motion is part of the spec, author or patch widget timelines with create_widget_animation or modify_widget_animation using the supported track subset.',
|
|
292
293
|
'6. Compile, then capture key visual checkpoints such as open, focused, or pressed.',
|
|
293
294
|
'7. If reference frames exist, compare captures with compare_capture_to_reference or compare_motion_capture_bundle before save_assets.',
|
|
@@ -847,17 +847,17 @@ export declare const MeshImportOptionsSchema: z.ZodObject<{
|
|
|
847
847
|
generate_collision: z.ZodOptional<z.ZodBoolean>;
|
|
848
848
|
skeleton_path: z.ZodOptional<z.ZodString>;
|
|
849
849
|
}, "strip", z.ZodTypeAny, {
|
|
850
|
-
import_textures?: boolean | undefined;
|
|
851
850
|
mesh_type?: string | undefined;
|
|
852
851
|
import_materials?: boolean | undefined;
|
|
852
|
+
import_textures?: boolean | undefined;
|
|
853
853
|
import_animations?: boolean | undefined;
|
|
854
854
|
combine_meshes?: boolean | undefined;
|
|
855
855
|
generate_collision?: boolean | undefined;
|
|
856
856
|
skeleton_path?: string | undefined;
|
|
857
857
|
}, {
|
|
858
|
-
import_textures?: boolean | undefined;
|
|
859
858
|
mesh_type?: string | undefined;
|
|
860
859
|
import_materials?: boolean | undefined;
|
|
860
|
+
import_textures?: boolean | undefined;
|
|
861
861
|
import_animations?: boolean | undefined;
|
|
862
862
|
combine_meshes?: boolean | undefined;
|
|
863
863
|
generate_collision?: boolean | undefined;
|
|
@@ -1053,17 +1053,17 @@ export declare const MeshImportPayloadSchema: z.ZodObject<{
|
|
|
1053
1053
|
generate_collision: z.ZodOptional<z.ZodBoolean>;
|
|
1054
1054
|
skeleton_path: z.ZodOptional<z.ZodString>;
|
|
1055
1055
|
}, "strip", z.ZodTypeAny, {
|
|
1056
|
-
import_textures?: boolean | undefined;
|
|
1057
1056
|
mesh_type?: string | undefined;
|
|
1058
1057
|
import_materials?: boolean | undefined;
|
|
1058
|
+
import_textures?: boolean | undefined;
|
|
1059
1059
|
import_animations?: boolean | undefined;
|
|
1060
1060
|
combine_meshes?: boolean | undefined;
|
|
1061
1061
|
generate_collision?: boolean | undefined;
|
|
1062
1062
|
skeleton_path?: string | undefined;
|
|
1063
1063
|
}, {
|
|
1064
|
-
import_textures?: boolean | undefined;
|
|
1065
1064
|
mesh_type?: string | undefined;
|
|
1066
1065
|
import_materials?: boolean | undefined;
|
|
1066
|
+
import_textures?: boolean | undefined;
|
|
1067
1067
|
import_animations?: boolean | undefined;
|
|
1068
1068
|
combine_meshes?: boolean | undefined;
|
|
1069
1069
|
generate_collision?: boolean | undefined;
|
|
@@ -1072,9 +1072,9 @@ export declare const MeshImportPayloadSchema: z.ZodObject<{
|
|
|
1072
1072
|
}, "strip", z.ZodTypeAny, {
|
|
1073
1073
|
url?: string | undefined;
|
|
1074
1074
|
options?: {
|
|
1075
|
-
import_textures?: boolean | undefined;
|
|
1076
1075
|
mesh_type?: string | undefined;
|
|
1077
1076
|
import_materials?: boolean | undefined;
|
|
1077
|
+
import_textures?: boolean | undefined;
|
|
1078
1078
|
import_animations?: boolean | undefined;
|
|
1079
1079
|
combine_meshes?: boolean | undefined;
|
|
1080
1080
|
generate_collision?: boolean | undefined;
|
|
@@ -1091,9 +1091,9 @@ export declare const MeshImportPayloadSchema: z.ZodObject<{
|
|
|
1091
1091
|
}, {
|
|
1092
1092
|
url?: string | undefined;
|
|
1093
1093
|
options?: {
|
|
1094
|
-
import_textures?: boolean | undefined;
|
|
1095
1094
|
mesh_type?: string | undefined;
|
|
1096
1095
|
import_materials?: boolean | undefined;
|
|
1096
|
+
import_textures?: boolean | undefined;
|
|
1097
1097
|
import_animations?: boolean | undefined;
|
|
1098
1098
|
combine_meshes?: boolean | undefined;
|
|
1099
1099
|
generate_collision?: boolean | undefined;
|
|
@@ -1112,9 +1112,9 @@ export declare const MeshImportPayloadSchema: z.ZodObject<{
|
|
|
1112
1112
|
items: {
|
|
1113
1113
|
url?: string | undefined;
|
|
1114
1114
|
options?: {
|
|
1115
|
-
import_textures?: boolean | undefined;
|
|
1116
1115
|
mesh_type?: string | undefined;
|
|
1117
1116
|
import_materials?: boolean | undefined;
|
|
1117
|
+
import_textures?: boolean | undefined;
|
|
1118
1118
|
import_animations?: boolean | undefined;
|
|
1119
1119
|
combine_meshes?: boolean | undefined;
|
|
1120
1120
|
generate_collision?: boolean | undefined;
|
|
@@ -1133,9 +1133,9 @@ export declare const MeshImportPayloadSchema: z.ZodObject<{
|
|
|
1133
1133
|
items: {
|
|
1134
1134
|
url?: string | undefined;
|
|
1135
1135
|
options?: {
|
|
1136
|
-
import_textures?: boolean | undefined;
|
|
1137
1136
|
mesh_type?: string | undefined;
|
|
1138
1137
|
import_materials?: boolean | undefined;
|
|
1138
|
+
import_textures?: boolean | undefined;
|
|
1139
1139
|
import_animations?: boolean | undefined;
|
|
1140
1140
|
combine_meshes?: boolean | undefined;
|
|
1141
1141
|
generate_collision?: boolean | undefined;
|
package/dist/server-config.js
CHANGED
|
@@ -2,13 +2,13 @@ const EDITOR_UNAVAILABLE_MESSAGE_FRAGMENT = 'UE Editor not running or Remote Con
|
|
|
2
2
|
const SUBSYSTEM_UNAVAILABLE_MESSAGE_FRAGMENT = 'BlueprintExtractor subsystem not found';
|
|
3
3
|
export const EDITOR_POLL_INTERVAL_MS = 1_000;
|
|
4
4
|
export const serverInstructions = [
|
|
5
|
-
'Blueprint Extractor MCP
|
|
5
|
+
'Blueprint Extractor MCP uses a v2 public contract with workflow-scoped tool surfaces, snake_case arguments, prompt workflows, and structured JSON results.',
|
|
6
6
|
// Tool discovery
|
|
7
7
|
'Only ~13 core tools are visible by default. Use activate_workflow_scope to load specialized tool families: widget_authoring (or sub-scopes: widget_authoring_structure, widget_authoring_visual, widget_verification), material_authoring, blueprint_authoring, schema_ai_authoring, animation_authoring, data_tables, import, automation_testing, verification.',
|
|
8
8
|
'Use find_and_extract for search+extract in one call (activate any authoring scope to access it). Use search_assets when you only need to locate assets.',
|
|
9
9
|
'Call get_tool_help before the first use of a complex or polymorphic tool when you need operation-specific payload guidance. This may also auto-activate the relevant workflow scope.',
|
|
10
10
|
// Deferred tool directory (tools available via activate_workflow_scope)
|
|
11
|
-
'Deferred tool families — widget_authoring_structure: create/replace/patch/insert/remove/move/wrap widgets. widget_authoring_visual: CommonUI styles, widget animations, compile_widget, extraction. widget_verification: captures and comparisons. widget_authoring: activates all three widget sub-scopes. material_authoring: create/modify material, material_graph_operation, material instances. blueprint_authoring: create/modify blueprint members and graphs, trigger_live_coding. schema_ai_authoring: structs, enums, blackboards, behavior trees, state trees. animation_authoring: anim sequences, montages, blend spaces, widget animations. data_tables: data assets, input actions, tables, curves. import: import_assets, job tracking. automation_testing: run/get/list automation tests, project automation context. verification: widget captures and comparisons.',
|
|
11
|
+
'Deferred tool families — widget_authoring_structure: create/replace/patch/insert/remove/move/wrap widgets. widget_authoring_visual: CommonUI styles, widget animations, compile_widget, extraction. widget_verification: captures and comparisons. widget_authoring: activates all three widget sub-scopes. material_authoring: create/modify material, material_graph_operation, material instances. blueprint_authoring: create/modify blueprint members and graphs, trigger_live_coding. schema_ai_authoring: structs, enums, blackboards, behavior trees, state trees. animation_authoring: anim sequences, montages, blend spaces, widget animations. data_tables: data assets, input actions, tables, curves. import: import_assets, job tracking. automation_testing: run/get/list automation tests, project automation context, and PIE lifecycle control. verification: widget captures, editor screenshots, runtime screenshots, and comparisons.',
|
|
12
12
|
// Extraction
|
|
13
13
|
'All extract_* tools default to compact: true. Pass compact: false for verbose output.',
|
|
14
14
|
// Search
|
|
@@ -34,6 +34,7 @@ export const serverInstructions = [
|
|
|
34
34
|
'Motion support includes dedicated widget animation authoring for render_opacity, render_transform translation/scale/angle, and color_and_opacity tracks. Treat broader arbitrary MovieScene track synthesis as deferred_to_future or unsupported when it exceeds that subset.',
|
|
35
35
|
// Testing
|
|
36
36
|
'Use run_automation_tests for gameplay or runtime verification. If no Automation Spec or Functional Test exists for a mechanic, report verification as partial instead of inferring success from structure alone.',
|
|
37
|
+
'Use capture_runtime_screenshot when a runtime verification lane already exports screenshot artifacts through automation. Use capture_editor_screenshot for the active editor viewport when a rendered editor reference is needed.',
|
|
37
38
|
// Results format
|
|
38
39
|
'Successful tool results use structuredContent as the canonical JSON payload. Recoverable execution failures return isError=true with code, message, recoverable, and next_steps.',
|
|
39
40
|
].join('\n');
|
|
@@ -46,6 +47,7 @@ export const taskAwareTools = new Set([
|
|
|
46
47
|
'get_import_job',
|
|
47
48
|
'list_import_jobs',
|
|
48
49
|
'run_automation_tests',
|
|
50
|
+
'capture_runtime_screenshot',
|
|
49
51
|
'get_automation_test_run',
|
|
50
52
|
'list_automation_test_runs',
|
|
51
53
|
]);
|
|
@@ -142,12 +144,17 @@ export const TOOL_MODE_ANNOTATIONS = new Map([
|
|
|
142
144
|
['trigger_live_coding', 'editor_only'],
|
|
143
145
|
['sync_project_code', 'editor_only'],
|
|
144
146
|
['wait_for_editor', 'editor_only'],
|
|
147
|
+
['start_pie', 'editor_only'],
|
|
148
|
+
['stop_pie', 'editor_only'],
|
|
149
|
+
['relaunch_pie', 'editor_only'],
|
|
145
150
|
// ── Import (editor_only) ──
|
|
146
151
|
['import_assets', 'editor_only'],
|
|
147
152
|
// ── Automation tests (editor_only) ──
|
|
148
153
|
['run_automation_tests', 'editor_only'],
|
|
149
154
|
// ── Verification / captures (editor_only) ──
|
|
150
155
|
['capture_widget_preview', 'editor_only'],
|
|
156
|
+
['capture_editor_screenshot', 'editor_only'],
|
|
157
|
+
['capture_runtime_screenshot', 'editor_only'],
|
|
151
158
|
['capture_widget_motion_checkpoints', 'editor_only'],
|
|
152
159
|
['compare_capture_to_reference', 'editor_only'],
|
|
153
160
|
['compare_motion_capture_bundle', 'editor_only'],
|
package/dist/server-factory.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { UEClient } from './ue-client.js';
|
|
4
|
+
import { packageVersion } from './helpers/package-metadata.js';
|
|
4
5
|
import { ProjectController, } from './project-controller.js';
|
|
5
6
|
import { AutomationController, } from './automation-controller.js';
|
|
6
7
|
import { firstDefinedString } from './helpers/formatting.js';
|
|
@@ -23,7 +24,7 @@ export function createBlueprintExtractorServer(client = new UEClient(), projectC
|
|
|
23
24
|
const registeredToolMap = new Map();
|
|
24
25
|
const server = new McpServer({
|
|
25
26
|
name: 'blueprint-extractor',
|
|
26
|
-
version:
|
|
27
|
+
version: packageVersion,
|
|
27
28
|
}, {
|
|
28
29
|
instructions: serverInstructions,
|
|
29
30
|
});
|
package/dist/tool-context.d.ts
CHANGED
|
@@ -153,7 +153,7 @@ const SCOPE_DEFINITIONS = {
|
|
|
153
153
|
prompts: [],
|
|
154
154
|
tools: [
|
|
155
155
|
'run_automation_tests', 'get_automation_test_run', 'list_automation_test_runs',
|
|
156
|
-
'get_project_automation_context',
|
|
156
|
+
'get_project_automation_context', 'start_pie', 'stop_pie', 'relaunch_pie',
|
|
157
157
|
],
|
|
158
158
|
},
|
|
159
159
|
verification: {
|
|
@@ -161,6 +161,7 @@ const SCOPE_DEFINITIONS = {
|
|
|
161
161
|
description: 'Widget verification and capture tools',
|
|
162
162
|
prompts: [],
|
|
163
163
|
tools: [
|
|
164
|
+
'capture_editor_screenshot', 'capture_runtime_screenshot',
|
|
164
165
|
'capture_widget_preview', 'capture_widget_motion_checkpoints',
|
|
165
166
|
'compare_capture_to_reference', 'list_captures', 'cleanup_captures',
|
|
166
167
|
'compare_motion_capture_bundle',
|
package/dist/tools/extraction.js
CHANGED
|
@@ -143,7 +143,10 @@ export function registerExtractionTools({ server, callSubsystemJson, scopeEnum,
|
|
|
143
143
|
if (compact) {
|
|
144
144
|
parsed = compactMaterial(parsed);
|
|
145
145
|
}
|
|
146
|
-
return jsonToolSuccess(
|
|
146
|
+
return jsonToolSuccess({
|
|
147
|
+
...parsed,
|
|
148
|
+
operation: 'extract_material',
|
|
149
|
+
});
|
|
147
150
|
}
|
|
148
151
|
catch (error) {
|
|
149
152
|
return jsonToolError(error);
|
|
@@ -66,7 +66,10 @@ export function registerImportJobTools({ server, callSubsystemJson, importJobSch
|
|
|
66
66
|
PayloadJson: JSON.stringify(transformedPayload),
|
|
67
67
|
bValidateOnly: validate_only,
|
|
68
68
|
});
|
|
69
|
-
return jsonToolSuccess(
|
|
69
|
+
return jsonToolSuccess({
|
|
70
|
+
...parsed,
|
|
71
|
+
operation: 'import_assets',
|
|
72
|
+
});
|
|
70
73
|
}
|
|
71
74
|
catch (error) {
|
|
72
75
|
return jsonToolError(error);
|
|
@@ -15,6 +15,12 @@ function structuredToolError(message, options = {}) {
|
|
|
15
15
|
isError: true,
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
+
function normalizeMaterialToolOperation(parsed, operation) {
|
|
19
|
+
return {
|
|
20
|
+
...parsed,
|
|
21
|
+
operation,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
18
24
|
export function registerMaterialAuthoringTools({ server, callSubsystemJson, jsonObjectSchema, materialNodePositionSchema, materialConnectionSelectorFieldsSchema, materialGraphOperationKindSchema, materialGraphOperationSchema, }) {
|
|
19
25
|
server.registerTool('create_material', {
|
|
20
26
|
title: 'Create Material',
|
|
@@ -42,7 +48,7 @@ export function registerMaterialAuthoringTools({ server, callSubsystemJson, json
|
|
|
42
48
|
SettingsJson: JSON.stringify(settings ?? {}),
|
|
43
49
|
bValidateOnly: validate_only,
|
|
44
50
|
});
|
|
45
|
-
return jsonToolSuccess(parsed);
|
|
51
|
+
return jsonToolSuccess(normalizeMaterialToolOperation(parsed, 'create_material'));
|
|
46
52
|
}
|
|
47
53
|
const parsed = await callSubsystemJson('CreateMaterialFunction', {
|
|
48
54
|
AssetPath: asset_path,
|
|
@@ -50,7 +56,7 @@ export function registerMaterialAuthoringTools({ server, callSubsystemJson, json
|
|
|
50
56
|
SettingsJson: JSON.stringify(settings ?? {}),
|
|
51
57
|
bValidateOnly: validate_only,
|
|
52
58
|
});
|
|
53
|
-
return jsonToolSuccess(parsed);
|
|
59
|
+
return jsonToolSuccess(normalizeMaterialToolOperation(parsed, 'create_material'));
|
|
54
60
|
}
|
|
55
61
|
catch (error) {
|
|
56
62
|
return jsonToolError(error);
|
|
@@ -171,7 +177,7 @@ export function registerMaterialAuthoringTools({ server, callSubsystemJson, json
|
|
|
171
177
|
PayloadJson: JSON.stringify(payload),
|
|
172
178
|
bValidateOnly: validate_only,
|
|
173
179
|
});
|
|
174
|
-
return jsonToolSuccess(parsed);
|
|
180
|
+
return jsonToolSuccess(normalizeMaterialToolOperation(parsed, 'modify_material'));
|
|
175
181
|
}
|
|
176
182
|
catch (error) {
|
|
177
183
|
return jsonToolError(error);
|
|
@@ -43,6 +43,74 @@ export function registerProjectControlTools({ server, client, projectController,
|
|
|
43
43
|
return jsonToolError(error);
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
|
+
server.registerTool('start_pie', {
|
|
47
|
+
title: 'Start PIE',
|
|
48
|
+
description: 'Request a Play-In-Editor session from the active editor.',
|
|
49
|
+
inputSchema: {
|
|
50
|
+
simulate: z.boolean().default(false).describe('When true, start Simulate-In-Editor instead of Play-In-Editor.'),
|
|
51
|
+
},
|
|
52
|
+
annotations: {
|
|
53
|
+
title: 'Start PIE',
|
|
54
|
+
readOnlyHint: false,
|
|
55
|
+
destructiveHint: false,
|
|
56
|
+
idempotentHint: false,
|
|
57
|
+
openWorldHint: false,
|
|
58
|
+
},
|
|
59
|
+
}, async ({ simulate }) => {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = await callSubsystemJson('StartPIE', {
|
|
62
|
+
bSimulateInEditor: simulate,
|
|
63
|
+
});
|
|
64
|
+
return jsonToolSuccess(parsed);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return jsonToolError(error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
server.registerTool('stop_pie', {
|
|
71
|
+
title: 'Stop PIE',
|
|
72
|
+
description: 'Stop the current Play-In-Editor session if one is active.',
|
|
73
|
+
inputSchema: {},
|
|
74
|
+
annotations: {
|
|
75
|
+
title: 'Stop PIE',
|
|
76
|
+
readOnlyHint: false,
|
|
77
|
+
destructiveHint: false,
|
|
78
|
+
idempotentHint: false,
|
|
79
|
+
openWorldHint: false,
|
|
80
|
+
},
|
|
81
|
+
}, async () => {
|
|
82
|
+
try {
|
|
83
|
+
const parsed = await callSubsystemJson('StopPIE', {});
|
|
84
|
+
return jsonToolSuccess(parsed);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return jsonToolError(error);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
server.registerTool('relaunch_pie', {
|
|
91
|
+
title: 'Relaunch PIE',
|
|
92
|
+
description: 'Restart the current Play-In-Editor session by stopping it and scheduling a fresh launch.',
|
|
93
|
+
inputSchema: {
|
|
94
|
+
simulate: z.boolean().default(false).describe('When true, relaunch into Simulate-In-Editor instead of Play-In-Editor.'),
|
|
95
|
+
},
|
|
96
|
+
annotations: {
|
|
97
|
+
title: 'Relaunch PIE',
|
|
98
|
+
readOnlyHint: false,
|
|
99
|
+
destructiveHint: false,
|
|
100
|
+
idempotentHint: false,
|
|
101
|
+
openWorldHint: false,
|
|
102
|
+
},
|
|
103
|
+
}, async ({ simulate }) => {
|
|
104
|
+
try {
|
|
105
|
+
const parsed = await callSubsystemJson('RelaunchPIE', {
|
|
106
|
+
bSimulateInEditor: simulate,
|
|
107
|
+
});
|
|
108
|
+
return jsonToolSuccess(parsed);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return jsonToolError(error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
46
114
|
server.registerTool('wait_for_editor', {
|
|
47
115
|
title: 'Wait For Editor',
|
|
48
116
|
description: 'Poll the editor connection once per second until Remote Control responds again or the timeout elapses.',
|
|
@@ -3,6 +3,23 @@ import { buildCaptureResourceUri, buildResourceLinkContent, maybeBuildInlineImag
|
|
|
3
3
|
import { explainProjectResolutionFailure } from '../helpers/project-utils.js';
|
|
4
4
|
import { jsonToolError, jsonToolSuccess } from '../helpers/subsystem.js';
|
|
5
5
|
import { normalizeAutomationRunResult, normalizeVerificationArtifact, normalizeVerificationArtifactReference, normalizeVerificationComparison, } from '../helpers/verification.js';
|
|
6
|
+
async function buildCaptureExtraContent(artifact, label, description) {
|
|
7
|
+
const extraContent = [];
|
|
8
|
+
const resourceUri = typeof artifact.resourceUri === 'string'
|
|
9
|
+
? artifact.resourceUri
|
|
10
|
+
: '';
|
|
11
|
+
const captureId = typeof artifact.captureId === 'string'
|
|
12
|
+
? artifact.captureId
|
|
13
|
+
: '';
|
|
14
|
+
if (resourceUri) {
|
|
15
|
+
extraContent.push(buildResourceLinkContent(resourceUri, label || `Capture ${captureId || 'capture'}`, 'image/png', description));
|
|
16
|
+
}
|
|
17
|
+
const inlineImage = await maybeBuildInlineImageContent(typeof artifact.artifactPath === 'string' ? artifact.artifactPath : undefined);
|
|
18
|
+
if (inlineImage) {
|
|
19
|
+
extraContent.push(inlineImage);
|
|
20
|
+
}
|
|
21
|
+
return extraContent;
|
|
22
|
+
}
|
|
6
23
|
export function registerWidgetVerificationTools({ server, callSubsystemJson, automationController, resolveProjectInputs, captureResultSchema, widgetAnimationCheckpointSchema, motionCaptureModeSchema, motionCaptureBundleResultSchema, compareCaptureResultSchema, listCapturesResultSchema, cleanupCapturesResultSchema, compareMotionCaptureBundleResultSchema, }) {
|
|
7
24
|
server.registerTool('capture_widget_preview', {
|
|
8
25
|
title: 'Capture Widget Preview',
|
|
@@ -47,6 +64,95 @@ export function registerWidgetVerificationTools({ server, callSubsystemJson, aut
|
|
|
47
64
|
return jsonToolError(error);
|
|
48
65
|
}
|
|
49
66
|
});
|
|
67
|
+
server.registerTool('capture_editor_screenshot', {
|
|
68
|
+
title: 'Capture Editor Screenshot',
|
|
69
|
+
description: 'Capture the active editor viewport and return screenshot artifacts.',
|
|
70
|
+
inputSchema: {},
|
|
71
|
+
outputSchema: captureResultSchema,
|
|
72
|
+
annotations: {
|
|
73
|
+
title: 'Capture Editor Screenshot',
|
|
74
|
+
readOnlyHint: true,
|
|
75
|
+
destructiveHint: false,
|
|
76
|
+
idempotentHint: false,
|
|
77
|
+
openWorldHint: false,
|
|
78
|
+
},
|
|
79
|
+
}, async () => {
|
|
80
|
+
try {
|
|
81
|
+
const parsed = await callSubsystemJson('CaptureEditorScreenshot', {});
|
|
82
|
+
const artifact = normalizeVerificationArtifact(parsed);
|
|
83
|
+
const captureId = typeof artifact.captureId === 'string' ? artifact.captureId : '';
|
|
84
|
+
const resourceUri = buildCaptureResourceUri(captureId);
|
|
85
|
+
const normalizedArtifact = {
|
|
86
|
+
...artifact,
|
|
87
|
+
resourceUri,
|
|
88
|
+
};
|
|
89
|
+
const extraContent = await buildCaptureExtraContent(normalizedArtifact, captureId ? `Editor ${captureId}` : 'Editor screenshot', 'Captured editor viewport screenshot.');
|
|
90
|
+
return jsonToolSuccess(normalizedArtifact, { extraContent });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return jsonToolError(error);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
server.registerTool('capture_runtime_screenshot', {
|
|
97
|
+
title: 'Capture Runtime Screenshot',
|
|
98
|
+
description: 'Run an automation scenario and return the first normalized runtime screenshot artifact.',
|
|
99
|
+
inputSchema: {
|
|
100
|
+
automation_filter: z.string().regex(/^[A-Za-z0-9_.+* -]+$/u, 'automation_filter must contain only alphanumeric, dots, underscores, plus, asterisk, hyphen, and spaces').describe('Automation test filter passed to run_automation_tests.'),
|
|
101
|
+
engine_root: z.string().optional().describe('Optional Unreal Engine root. Falls back to editor context or UE_ENGINE_ROOT.'),
|
|
102
|
+
project_path: z.string().optional().describe('Optional .uproject path. Falls back to editor context or UE_PROJECT_PATH.'),
|
|
103
|
+
target: z.string().optional().describe('Optional editor target name to keep in the run metadata.'),
|
|
104
|
+
report_output_dir: z.string().optional().describe('Optional host filesystem directory for this run.'),
|
|
105
|
+
timeout_seconds: z.number().int().positive().default(3600).describe('Maximum wall-clock time for the automation process before the host terminates it.'),
|
|
106
|
+
null_rhi: z.boolean().default(false).describe('Leave false for screenshot-backed runtime verification. Set true only when the automation path exports images without live rendering.'),
|
|
107
|
+
},
|
|
108
|
+
outputSchema: captureResultSchema,
|
|
109
|
+
annotations: {
|
|
110
|
+
title: 'Capture Runtime Screenshot',
|
|
111
|
+
readOnlyHint: true,
|
|
112
|
+
destructiveHint: false,
|
|
113
|
+
idempotentHint: false,
|
|
114
|
+
openWorldHint: false,
|
|
115
|
+
},
|
|
116
|
+
}, async ({ automation_filter, engine_root, project_path, target, report_output_dir, timeout_seconds, null_rhi }) => {
|
|
117
|
+
try {
|
|
118
|
+
const resolved = await resolveProjectInputs({ engine_root, project_path, target });
|
|
119
|
+
if (!resolved.engineRoot || !resolved.projectPath) {
|
|
120
|
+
throw explainProjectResolutionFailure('capture_runtime_screenshot requires engine_root and project_path', resolved);
|
|
121
|
+
}
|
|
122
|
+
const run = normalizeAutomationRunResult(await automationController.runAutomationTests({
|
|
123
|
+
automationFilter: automation_filter,
|
|
124
|
+
engineRoot: resolved.engineRoot,
|
|
125
|
+
projectPath: resolved.projectPath,
|
|
126
|
+
target: resolved.target,
|
|
127
|
+
reportOutputDir: report_output_dir,
|
|
128
|
+
timeoutMs: timeout_seconds * 1000,
|
|
129
|
+
nullRhi: null_rhi,
|
|
130
|
+
}));
|
|
131
|
+
const runtimeArtifact = (Array.isArray(run.verificationArtifacts) ? run.verificationArtifacts : [])
|
|
132
|
+
.map((artifact) => normalizeVerificationArtifactReference(artifact))
|
|
133
|
+
.find((artifact) => artifact.surface === 'pie_runtime');
|
|
134
|
+
if (!runtimeArtifact) {
|
|
135
|
+
return jsonToolError(new Error('capture_runtime_screenshot did not find a normalized pie_runtime artifact. Ensure the automation scenario exports a screenshot-backed artifact.'));
|
|
136
|
+
}
|
|
137
|
+
const extraContent = await buildCaptureExtraContent(runtimeArtifact, typeof runtimeArtifact.captureId === 'string' && runtimeArtifact.captureId.length > 0
|
|
138
|
+
? `Runtime ${runtimeArtifact.captureId}`
|
|
139
|
+
: 'Runtime screenshot', 'Automation-exported runtime screenshot.');
|
|
140
|
+
return jsonToolSuccess({
|
|
141
|
+
...runtimeArtifact,
|
|
142
|
+
operation: 'capture_runtime_screenshot',
|
|
143
|
+
automationRun: run,
|
|
144
|
+
inputResolution: {
|
|
145
|
+
engineRoot: resolved.sources.engineRoot,
|
|
146
|
+
projectPath: resolved.sources.projectPath,
|
|
147
|
+
target: resolved.sources.target,
|
|
148
|
+
contextError: resolved.contextError,
|
|
149
|
+
},
|
|
150
|
+
}, { extraContent });
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return jsonToolError(error);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
50
156
|
server.registerTool('capture_widget_motion_checkpoints', {
|
|
51
157
|
title: 'Capture Widget Motion Checkpoints',
|
|
52
158
|
description: 'Play a widget animation or automation scenario and capture named motion checkpoints.\n\n'
|