ultimate-unreal-engine-mcp 0.1.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/README.md +729 -0
- package/dist/build/error-parser.js +51 -0
- package/dist/build/fix-suggester.js +84 -0
- package/dist/build/ubt-runner.js +146 -0
- package/dist/cli.js +13 -0
- package/dist/config.js +8 -0
- package/dist/docs/data/ue57-api.js +228 -0
- package/dist/docs/doc-index.js +110 -0
- package/dist/docs/types.js +4 -0
- package/dist/generators/class-generator.js +363 -0
- package/dist/generators/file-modifier.js +276 -0
- package/dist/generators/uht-validator.js +177 -0
- package/dist/index.js +89 -0
- package/dist/parsers/cpp-class-index.js +230 -0
- package/dist/parsers/cpp-parser.js +369 -0
- package/dist/parsers/ini-parser.js +216 -0
- package/dist/parsers/uproject-parser.js +130 -0
- package/dist/plugin-bridge/client.js +217 -0
- package/dist/plugin-bridge/protocol.js +6 -0
- package/dist/plugin-bridge/retry.js +23 -0
- package/dist/setup.js +209 -0
- package/dist/tools/ai-systems/index.js +247 -0
- package/dist/tools/ai-systems/types.js +4 -0
- package/dist/tools/animation/index.js +241 -0
- package/dist/tools/animation/types.js +4 -0
- package/dist/tools/audio/index.js +204 -0
- package/dist/tools/audio/types.js +4 -0
- package/dist/tools/blueprint/index.js +495 -0
- package/dist/tools/blueprint/types.js +4 -0
- package/dist/tools/build/index.js +163 -0
- package/dist/tools/chaos/index.js +230 -0
- package/dist/tools/chaos/types.js +4 -0
- package/dist/tools/collision-physics/index.js +211 -0
- package/dist/tools/config/index.js +288 -0
- package/dist/tools/cpp/index.js +305 -0
- package/dist/tools/docs/index.js +251 -0
- package/dist/tools/editor/index.js +242 -0
- package/dist/tools/gas/index.js +222 -0
- package/dist/tools/gas/types.js +5 -0
- package/dist/tools/import-export/index.js +218 -0
- package/dist/tools/input/index.js +146 -0
- package/dist/tools/known-issues/index.js +88 -0
- package/dist/tools/known-issues/middleware.js +55 -0
- package/dist/tools/known-issues/store.js +125 -0
- package/dist/tools/livelink/index.js +203 -0
- package/dist/tools/livelink/types.js +4 -0
- package/dist/tools/material/index.js +190 -0
- package/dist/tools/motion-design/index.js +251 -0
- package/dist/tools/motion-design/types.js +6 -0
- package/dist/tools/movie-render/index.js +220 -0
- package/dist/tools/networking/index.js +149 -0
- package/dist/tools/pcg/index.js +164 -0
- package/dist/tools/selection/index.js +180 -0
- package/dist/tools/sequencer/index.js +218 -0
- package/dist/tools/validation/index.js +183 -0
- package/dist/tools/validation/types.js +4 -0
- package/dist/tools/viewport/index.js +310 -0
- package/dist/tools/worldpartition/index.js +226 -0
- package/dist/tools/worldpartition/types.js +4 -0
- package/dist/utils/execFileNoThrow.js +40 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/path-guard.js +26 -0
- package/package.json +40 -0
- package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPTcpServer.h +59 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// src/tools/config/index.ts
|
|
2
|
+
// Registers UE config file tools on the MCP server.
|
|
3
|
+
// All handlers validated with validatePath() and wrapped with withKnownIssues().
|
|
4
|
+
// No console.log — all logging uses stderr via console.error (see logger.ts).
|
|
5
|
+
// Backup (.bak) is created before any write operation.
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { withKnownIssues } from '../known-issues/middleware.js';
|
|
10
|
+
import { parseIni, setIniValue } from '../../parsers/ini-parser.js';
|
|
11
|
+
import { parseUproject, parseUplugin } from '../../parsers/uproject-parser.js';
|
|
12
|
+
import { validatePath } from '../../utils/path-guard.js';
|
|
13
|
+
import { PROJECT_ROOT } from '../../config.js';
|
|
14
|
+
/**
|
|
15
|
+
* Register UE config file tools on the MCP server.
|
|
16
|
+
*
|
|
17
|
+
* Tools registered:
|
|
18
|
+
* ue_read_config — Read a section/key from a UE .ini config file
|
|
19
|
+
* ue_write_config — Write a key to a UE .ini config file (with .bak backup)
|
|
20
|
+
* ue_read_uproject — Parse a .uproject file
|
|
21
|
+
* ue_list_plugins — List plugins enabled in a .uproject file
|
|
22
|
+
*
|
|
23
|
+
* @param server The McpServer instance to register tools on.
|
|
24
|
+
*/
|
|
25
|
+
export function registerConfigTools(server) {
|
|
26
|
+
// --------------------------------------------------------------------------
|
|
27
|
+
// ue_read_config
|
|
28
|
+
// --------------------------------------------------------------------------
|
|
29
|
+
server.registerTool('ue_read_config', {
|
|
30
|
+
title: 'Read UE Config',
|
|
31
|
+
description: 'Read a value or section from a UE .ini configuration file (DefaultEngine.ini, DefaultGame.ini, etc.).',
|
|
32
|
+
inputSchema: z.object({
|
|
33
|
+
config_file: z.string().describe('Absolute path to the .ini config file'),
|
|
34
|
+
section: z.string().optional().describe('The [Section] to read (optional — omit to list all sections)'),
|
|
35
|
+
key: z.string().optional().describe('The key to read within the section (optional — omit to list all keys in section)'),
|
|
36
|
+
}),
|
|
37
|
+
annotations: {
|
|
38
|
+
readOnlyHint: true,
|
|
39
|
+
destructiveHint: false,
|
|
40
|
+
},
|
|
41
|
+
}, withKnownIssues('ue_read_config', async (args) => {
|
|
42
|
+
// 1. Validate path — throws on traversal; withKnownIssues catches → isError
|
|
43
|
+
const resolvedPath = validatePath(args.config_file, PROJECT_ROOT);
|
|
44
|
+
// 2. Read file — ENOENT returns isError (not a throw caught by middleware)
|
|
45
|
+
let content;
|
|
46
|
+
try {
|
|
47
|
+
content = await fs.readFile(resolvedPath, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const e = err;
|
|
51
|
+
if (e.code === 'ENOENT') {
|
|
52
|
+
return {
|
|
53
|
+
isError: true,
|
|
54
|
+
content: [{ type: 'text', text: `File not found: ${resolvedPath}` }],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
throw err; // unexpected — let withKnownIssues catch it
|
|
58
|
+
}
|
|
59
|
+
// 3. Parse
|
|
60
|
+
const parsed = parseIni(content);
|
|
61
|
+
const fileName = path.basename(resolvedPath);
|
|
62
|
+
// 4. No section: list all section names
|
|
63
|
+
if (!args.section) {
|
|
64
|
+
const sections = Object.keys(parsed);
|
|
65
|
+
const text = sections.length > 0
|
|
66
|
+
? `Sections in ${fileName}:\n${sections.join('\n')}`
|
|
67
|
+
: `No sections found in ${fileName}`;
|
|
68
|
+
return { content: [{ type: 'text', text }] };
|
|
69
|
+
}
|
|
70
|
+
// 5. Section but no key: return all keys and values in section
|
|
71
|
+
if (!args.key) {
|
|
72
|
+
const section = parsed[args.section];
|
|
73
|
+
if (!section) {
|
|
74
|
+
return {
|
|
75
|
+
isError: true,
|
|
76
|
+
content: [{ type: 'text', text: `Section '[${args.section}]' not found in ${fileName}` }],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const lines = Object.entries(section).map(([k, vals]) => `${k}=${vals.join(', ')}`);
|
|
80
|
+
const text = lines.length > 0
|
|
81
|
+
? `[${args.section}] in ${fileName}:\n${lines.join('\n')}`
|
|
82
|
+
: `[${args.section}] exists but has no keys in ${fileName}`;
|
|
83
|
+
return { content: [{ type: 'text', text }] };
|
|
84
|
+
}
|
|
85
|
+
// 6. Section and key: return value(s)
|
|
86
|
+
const section = parsed[args.section];
|
|
87
|
+
if (!section) {
|
|
88
|
+
return {
|
|
89
|
+
isError: true,
|
|
90
|
+
content: [{ type: 'text', text: `Section '[${args.section}]' not found in ${fileName}` }],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const values = section[args.key];
|
|
94
|
+
if (!values || values.length === 0) {
|
|
95
|
+
return {
|
|
96
|
+
isError: true,
|
|
97
|
+
content: [{ type: 'text', text: `Key '${args.key}' not found in section '[${args.section}]' in ${fileName}` }],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const text = values.length === 1
|
|
101
|
+
? values[0]
|
|
102
|
+
: values.join('\n');
|
|
103
|
+
return { content: [{ type: 'text', text }] };
|
|
104
|
+
}));
|
|
105
|
+
// --------------------------------------------------------------------------
|
|
106
|
+
// ue_write_config
|
|
107
|
+
// --------------------------------------------------------------------------
|
|
108
|
+
server.registerTool('ue_write_config', {
|
|
109
|
+
title: 'Write UE Config',
|
|
110
|
+
description: 'Write a key=value pair to a UE .ini configuration file. Creates the section and key if they do not exist. ' +
|
|
111
|
+
'Do not write to Saved/Config/ — those files are managed by the editor and will be overwritten at next launch.',
|
|
112
|
+
inputSchema: z.object({
|
|
113
|
+
config_file: z.string().describe('Absolute path to the .ini config file'),
|
|
114
|
+
section: z.string().describe('The [Section] to write to'),
|
|
115
|
+
key: z.string().describe('The key to set'),
|
|
116
|
+
value: z.string().describe('The value to write'),
|
|
117
|
+
}),
|
|
118
|
+
annotations: {
|
|
119
|
+
readOnlyHint: false,
|
|
120
|
+
destructiveHint: false,
|
|
121
|
+
},
|
|
122
|
+
}, withKnownIssues('ue_write_config', async (args) => {
|
|
123
|
+
// 1. Validate path — throws on traversal
|
|
124
|
+
const resolvedPath = validatePath(args.config_file, PROJECT_ROOT);
|
|
125
|
+
// 2. Read existing content — ENOENT → empty string (new file)
|
|
126
|
+
let currentContent = '';
|
|
127
|
+
try {
|
|
128
|
+
currentContent = await fs.readFile(resolvedPath, 'utf8');
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
const e = err;
|
|
132
|
+
if (e.code !== 'ENOENT')
|
|
133
|
+
throw err;
|
|
134
|
+
// ENOENT: new file — proceed with empty content
|
|
135
|
+
}
|
|
136
|
+
// 3. Backup before modifying — skip if new file (ENOENT on copy)
|
|
137
|
+
try {
|
|
138
|
+
await fs.copyFile(resolvedPath, resolvedPath + '.bak');
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
const e = err;
|
|
142
|
+
if (e.code !== 'ENOENT')
|
|
143
|
+
throw err;
|
|
144
|
+
// ENOENT: new file, nothing to back up — continue
|
|
145
|
+
}
|
|
146
|
+
// 4. Ensure parent directory exists for new files
|
|
147
|
+
await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
|
|
148
|
+
// 5. Compute new content and write
|
|
149
|
+
const newContent = setIniValue(currentContent, args.section, args.key, args.value);
|
|
150
|
+
await fs.writeFile(resolvedPath, newContent, 'utf8');
|
|
151
|
+
return {
|
|
152
|
+
content: [{ type: 'text', text: `Written: [${args.section}] ${args.key}=${args.value}` }],
|
|
153
|
+
};
|
|
154
|
+
}));
|
|
155
|
+
// --------------------------------------------------------------------------
|
|
156
|
+
// ue_read_uproject
|
|
157
|
+
// --------------------------------------------------------------------------
|
|
158
|
+
server.registerTool('ue_read_uproject', {
|
|
159
|
+
title: 'Read UE Project File',
|
|
160
|
+
description: 'Parse a .uproject JSON file and return its contents (engine version, plugins, modules, etc.).',
|
|
161
|
+
inputSchema: z.object({
|
|
162
|
+
uproject_path: z.string().describe('Absolute path to the .uproject file'),
|
|
163
|
+
}),
|
|
164
|
+
annotations: {
|
|
165
|
+
readOnlyHint: true,
|
|
166
|
+
destructiveHint: false,
|
|
167
|
+
},
|
|
168
|
+
}, withKnownIssues('ue_read_uproject', async (args) => {
|
|
169
|
+
// 1. Validate path
|
|
170
|
+
const resolvedPath = validatePath(args.uproject_path, PROJECT_ROOT);
|
|
171
|
+
// 2. Read file
|
|
172
|
+
let content;
|
|
173
|
+
try {
|
|
174
|
+
content = await fs.readFile(resolvedPath, 'utf8');
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
const e = err;
|
|
178
|
+
if (e.code === 'ENOENT') {
|
|
179
|
+
return {
|
|
180
|
+
isError: true,
|
|
181
|
+
content: [{ type: 'text', text: `File not found: ${resolvedPath}` }],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
// 3. Parse with Zod-validated parser
|
|
187
|
+
const result = parseUproject(content);
|
|
188
|
+
if (!result.success) {
|
|
189
|
+
return {
|
|
190
|
+
isError: true,
|
|
191
|
+
content: [{ type: 'text', text: result.error }],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// 4. Format response
|
|
195
|
+
const data = result.data;
|
|
196
|
+
const engineAssoc = data.EngineAssociation ?? 'not specified';
|
|
197
|
+
const modules = data.Modules ?? [];
|
|
198
|
+
const plugins = data.Plugins ?? [];
|
|
199
|
+
const moduleList = modules.length > 0
|
|
200
|
+
? modules.map((m) => `${m.Name} [${m.Type}]`).join(', ')
|
|
201
|
+
: 'none';
|
|
202
|
+
const pluginList = plugins.length > 0
|
|
203
|
+
? plugins.map((p) => `${p.Name} [${p.Enabled ? 'enabled' : 'disabled'}]`).join(', ')
|
|
204
|
+
: 'none';
|
|
205
|
+
const text = [
|
|
206
|
+
`Engine Association: ${engineAssoc}`,
|
|
207
|
+
`Modules (${modules.length}): ${moduleList}`,
|
|
208
|
+
`Plugins (${plugins.length}): ${pluginList}`,
|
|
209
|
+
].join('\n');
|
|
210
|
+
return { content: [{ type: 'text', text }] };
|
|
211
|
+
}));
|
|
212
|
+
// --------------------------------------------------------------------------
|
|
213
|
+
// ue_list_plugins
|
|
214
|
+
// --------------------------------------------------------------------------
|
|
215
|
+
server.registerTool('ue_list_plugins', {
|
|
216
|
+
title: 'List UE Plugins',
|
|
217
|
+
description: 'List all plugins enabled in a .uproject file, including engine plugins and project-local plugins.',
|
|
218
|
+
inputSchema: z.object({
|
|
219
|
+
uproject_path: z.string().describe('Absolute path to the .uproject file'),
|
|
220
|
+
}),
|
|
221
|
+
annotations: {
|
|
222
|
+
readOnlyHint: true,
|
|
223
|
+
destructiveHint: false,
|
|
224
|
+
},
|
|
225
|
+
}, withKnownIssues('ue_list_plugins', async (args) => {
|
|
226
|
+
// 1. Validate path
|
|
227
|
+
const resolvedPath = validatePath(args.uproject_path, PROJECT_ROOT);
|
|
228
|
+
// 2. Read file
|
|
229
|
+
let content;
|
|
230
|
+
try {
|
|
231
|
+
content = await fs.readFile(resolvedPath, 'utf8');
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
const e = err;
|
|
235
|
+
if (e.code === 'ENOENT') {
|
|
236
|
+
return {
|
|
237
|
+
isError: true,
|
|
238
|
+
content: [{ type: 'text', text: `File not found: ${resolvedPath}` }],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
throw err;
|
|
242
|
+
}
|
|
243
|
+
// 3. Parse .uproject
|
|
244
|
+
const result = parseUproject(content);
|
|
245
|
+
if (!result.success) {
|
|
246
|
+
return {
|
|
247
|
+
isError: true,
|
|
248
|
+
content: [{ type: 'text', text: result.error }],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
// 4. For each plugin, try to resolve its .uplugin file
|
|
252
|
+
const plugins = result.data.Plugins ?? [];
|
|
253
|
+
const uprojectDir = path.dirname(resolvedPath);
|
|
254
|
+
const lines = [];
|
|
255
|
+
for (const plugin of plugins) {
|
|
256
|
+
const status = plugin.Enabled ? 'enabled' : 'disabled';
|
|
257
|
+
const upluginPath = path.join(uprojectDir, 'Plugins', plugin.Name, `${plugin.Name}.uplugin`);
|
|
258
|
+
let depInfo;
|
|
259
|
+
try {
|
|
260
|
+
const upluginContent = await fs.readFile(upluginPath, 'utf8');
|
|
261
|
+
const upluginResult = parseUplugin(upluginContent);
|
|
262
|
+
if (upluginResult.success) {
|
|
263
|
+
const deps = upluginResult.data.Plugins ?? [];
|
|
264
|
+
depInfo = deps.length > 0
|
|
265
|
+
? `dependencies: [${deps.map((d) => d.Name).join(', ')}]`
|
|
266
|
+
: 'dependencies: none';
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
depInfo = `dependencies: unknown (parse error: ${upluginResult.error})`;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
const e = err;
|
|
274
|
+
if (e.code === 'ENOENT') {
|
|
275
|
+
depInfo = 'Engine plugin — .uplugin not accessible without engine root';
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
lines.push(`[${status}] ${plugin.Name} — ${depInfo}`);
|
|
282
|
+
}
|
|
283
|
+
const text = lines.length > 0
|
|
284
|
+
? lines.join('\n')
|
|
285
|
+
: 'No plugins found in .uproject file';
|
|
286
|
+
return { content: [{ type: 'text', text }] };
|
|
287
|
+
}));
|
|
288
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
// src/tools/cpp/index.ts
|
|
2
|
+
// Registers real C++ parsing and analysis tools on the MCP server.
|
|
3
|
+
// Implements CPP-01 through CPP-04 using cpp-parser.ts and cpp-class-index.ts.
|
|
4
|
+
// Implements GEN-01 through GEN-08 using class-generator.ts and file-modifier.ts.
|
|
5
|
+
//
|
|
6
|
+
// All handlers:
|
|
7
|
+
// - Wrapped with withKnownIssues() for known-issue pre-check and exception catching
|
|
8
|
+
// - File path args validated via validatePath() before any fs operation (T-04-06, T-05-09, T-05-10)
|
|
9
|
+
// - Use console.error only (never console.log — stdout safety)
|
|
10
|
+
import * as fs from 'fs/promises';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { withKnownIssues } from '../known-issues/middleware.js';
|
|
14
|
+
import { parseCppFile } from '../../parsers/cpp-parser.js';
|
|
15
|
+
import { buildIndex } from '../../parsers/cpp-class-index.js';
|
|
16
|
+
import { validatePath } from '../../utils/path-guard.js';
|
|
17
|
+
import { PROJECT_ROOT } from '../../config.js';
|
|
18
|
+
import { generateClass } from '../../generators/class-generator.js';
|
|
19
|
+
import { addProperty, addFunction, addInclude } from '../../generators/file-modifier.js';
|
|
20
|
+
import { validateUhtRules } from '../../generators/uht-validator.js';
|
|
21
|
+
/**
|
|
22
|
+
* ue_read_cpp_class handler — reads and parses a UE C++ header file.
|
|
23
|
+
* CPP-01: Extracts UCLASS, UPROPERTY, UFUNCTION declarations with specifiers and meta.
|
|
24
|
+
*/
|
|
25
|
+
export async function handleReadCppClass(args) {
|
|
26
|
+
const safePath = validatePath(args.file_path, PROJECT_ROOT);
|
|
27
|
+
const content = await fs.readFile(safePath, 'utf8');
|
|
28
|
+
const result = parseCppFile(content);
|
|
29
|
+
if (!result.success) {
|
|
30
|
+
return { isError: true, content: [{ type: 'text', text: `Parse error: ${result.error}` }] };
|
|
31
|
+
}
|
|
32
|
+
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* ue_get_class_hierarchy handler — returns the inheritance chain for a UE class.
|
|
36
|
+
* CPP-02: Walks parentClass links in the project class index.
|
|
37
|
+
*/
|
|
38
|
+
export async function handleGetClassHierarchy(args) {
|
|
39
|
+
const indexResult = await buildIndex(PROJECT_ROOT);
|
|
40
|
+
if (!indexResult.success) {
|
|
41
|
+
return { isError: true, content: [{ type: 'text', text: `Index error: ${indexResult.error}` }] };
|
|
42
|
+
}
|
|
43
|
+
const chain = indexResult.index.getClassHierarchy(args.class_name);
|
|
44
|
+
return { content: [{ type: 'text', text: JSON.stringify({ className: args.class_name, chain }, null, 2) }] };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* ue_trace_includes handler — returns the direct #include list from a header file.
|
|
48
|
+
* CPP-03: Reads the file directly and returns its parsed include list.
|
|
49
|
+
*/
|
|
50
|
+
export async function handleTraceIncludes(args) {
|
|
51
|
+
const safePath = validatePath(args.file_path, PROJECT_ROOT);
|
|
52
|
+
const content = await fs.readFile(safePath, 'utf8');
|
|
53
|
+
const parseResult = parseCppFile(content);
|
|
54
|
+
if (!parseResult.success) {
|
|
55
|
+
return { isError: true, content: [{ type: 'text', text: `Parse error: ${parseResult.error}` }] };
|
|
56
|
+
}
|
|
57
|
+
return { content: [{ type: 'text', text: JSON.stringify({ filePath: safePath, includes: parseResult.data.includes }, null, 2) }] };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* ue_find_class_file handler — locates header and source files for a UE class.
|
|
61
|
+
* CPP-04: Searches the project class index for the given class name.
|
|
62
|
+
*/
|
|
63
|
+
export async function handleFindClassFile(args) {
|
|
64
|
+
const indexResult = await buildIndex(PROJECT_ROOT);
|
|
65
|
+
if (!indexResult.success) {
|
|
66
|
+
return { isError: true, content: [{ type: 'text', text: `Index error: ${indexResult.error}` }] };
|
|
67
|
+
}
|
|
68
|
+
const found = indexResult.index.findClassFile(args.class_name);
|
|
69
|
+
if (!found) {
|
|
70
|
+
return { content: [{ type: 'text', text: `Class "${args.class_name}" not found in Source/ directory` }] };
|
|
71
|
+
}
|
|
72
|
+
return { content: [{ type: 'text', text: JSON.stringify(found, null, 2) }] };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* ue_generate_class handler — generates a new UE C++ class file pair (.h + .cpp).
|
|
76
|
+
* GEN-01 through GEN-04, GEN-08: Creates class files for Actor, Component, GameMode,
|
|
77
|
+
* Interface, etc. with correct UHT macros. Validates with UHT rules before writing.
|
|
78
|
+
* T-05-09: Validates output_dir and final file paths against PROJECT_ROOT.
|
|
79
|
+
*/
|
|
80
|
+
export async function handleGenerateClass(args) {
|
|
81
|
+
const safeDir = validatePath(args.output_dir, PROJECT_ROOT);
|
|
82
|
+
// Validate class_type is a known ClassType
|
|
83
|
+
const VALID_TYPES = ['Actor', 'ActorComponent', 'SceneComponent', 'GameMode', 'GameState', 'PlayerState', 'PlayerController', 'Interface'];
|
|
84
|
+
if (!VALID_TYPES.includes(args.class_type)) {
|
|
85
|
+
return { isError: true, content: [{ type: 'text', text: `Unknown class_type: "${args.class_type}". Valid: ${VALID_TYPES.join(', ')}` }] };
|
|
86
|
+
}
|
|
87
|
+
const generated = generateClass({
|
|
88
|
+
classType: args.class_type,
|
|
89
|
+
className: args.class_name,
|
|
90
|
+
moduleName: args.module_name,
|
|
91
|
+
parentClass: args.parent_class,
|
|
92
|
+
});
|
|
93
|
+
// UHT pre-validation (GEN-08): validate header before writing to disk
|
|
94
|
+
const validation = validateUhtRules(generated.header);
|
|
95
|
+
if (!validation.valid) {
|
|
96
|
+
return { isError: true, content: [{ type: 'text', text: `UHT validation failed:\n${validation.errors.join('\n')}` }] };
|
|
97
|
+
}
|
|
98
|
+
const headerPath = path.join(safeDir, generated.headerFileName);
|
|
99
|
+
const cppPath = path.join(safeDir, generated.cppFileName);
|
|
100
|
+
// Re-validate final paths are within project root (T-05-09)
|
|
101
|
+
validatePath(headerPath, PROJECT_ROOT);
|
|
102
|
+
validatePath(cppPath, PROJECT_ROOT);
|
|
103
|
+
await fs.writeFile(headerPath, generated.header, 'utf8');
|
|
104
|
+
await fs.writeFile(cppPath, generated.cpp, 'utf8');
|
|
105
|
+
const HOT_RELOAD_WARNING = '\n\nNote: Restart the Unreal Editor (or recompile) before creating Blueprint subclasses of the new class. Hot Reload does not register new C++ classes.';
|
|
106
|
+
return {
|
|
107
|
+
content: [{
|
|
108
|
+
type: 'text',
|
|
109
|
+
text: `Generated ${args.class_type} class "${generated.headerFileName.replace('.h', '')}"\nHeader: ${headerPath}\nCpp: ${cppPath}${HOT_RELOAD_WARNING}`,
|
|
110
|
+
}],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* ue_add_property handler — adds a UPROPERTY declaration to an existing .h file.
|
|
115
|
+
* GEN-05: Reads file, calls addProperty (which calls validateUhtRules internally), writes result.
|
|
116
|
+
* T-05-10: Validates file_path against PROJECT_ROOT before reading/writing.
|
|
117
|
+
*/
|
|
118
|
+
export async function handleAddProperty(args) {
|
|
119
|
+
const safePath = validatePath(args.file_path, PROJECT_ROOT);
|
|
120
|
+
const content = await fs.readFile(safePath, 'utf8');
|
|
121
|
+
const result = addProperty(content, args.class_name, {
|
|
122
|
+
specifiers: args.specifiers,
|
|
123
|
+
type: args.type,
|
|
124
|
+
name: args.name,
|
|
125
|
+
defaultValue: args.default_value,
|
|
126
|
+
});
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
return { isError: true, content: [{ type: 'text', text: result.error }] };
|
|
129
|
+
}
|
|
130
|
+
await fs.writeFile(safePath, result.content, 'utf8');
|
|
131
|
+
return { content: [{ type: 'text', text: `Added UPROPERTY ${args.name} to class ${args.class_name} in ${safePath}` }] };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* ue_add_function handler — adds a UFUNCTION declaration to an existing .h file.
|
|
135
|
+
* GEN-06: Reads file, calls addFunction (which calls validateUhtRules internally), writes result.
|
|
136
|
+
* T-05-10: Validates file_path against PROJECT_ROOT before reading/writing.
|
|
137
|
+
*/
|
|
138
|
+
export async function handleAddFunction(args) {
|
|
139
|
+
const safePath = validatePath(args.file_path, PROJECT_ROOT);
|
|
140
|
+
const content = await fs.readFile(safePath, 'utf8');
|
|
141
|
+
const result = addFunction(content, args.class_name, {
|
|
142
|
+
specifiers: args.specifiers,
|
|
143
|
+
returnType: args.return_type,
|
|
144
|
+
name: args.name,
|
|
145
|
+
params: args.params,
|
|
146
|
+
isConst: args.is_const,
|
|
147
|
+
});
|
|
148
|
+
if (!result.success) {
|
|
149
|
+
return { isError: true, content: [{ type: 'text', text: result.error }] };
|
|
150
|
+
}
|
|
151
|
+
await fs.writeFile(safePath, result.content, 'utf8');
|
|
152
|
+
return { content: [{ type: 'text', text: `Added UFUNCTION ${args.name} to class ${args.class_name} in ${safePath}` }] };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* ue_add_include handler — adds an #include directive to an existing .h or .cpp file.
|
|
156
|
+
* GEN-07: Reads file, calls addInclude (idempotent, which calls validateUhtRules), writes result.
|
|
157
|
+
* T-05-10: Validates file_path against PROJECT_ROOT before reading/writing.
|
|
158
|
+
*/
|
|
159
|
+
export async function handleAddInclude(args) {
|
|
160
|
+
const safePath = validatePath(args.file_path, PROJECT_ROOT);
|
|
161
|
+
const content = await fs.readFile(safePath, 'utf8');
|
|
162
|
+
const result = addInclude(content, args.include_path);
|
|
163
|
+
if (!result.success) {
|
|
164
|
+
return { isError: true, content: [{ type: 'text', text: result.error }] };
|
|
165
|
+
}
|
|
166
|
+
await fs.writeFile(safePath, result.content, 'utf8');
|
|
167
|
+
return { content: [{ type: 'text', text: `Added include "${args.include_path}" to ${safePath}` }] };
|
|
168
|
+
}
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Tool registration
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
/**
|
|
173
|
+
* Register C++ analysis and generation tools on the MCP server.
|
|
174
|
+
*
|
|
175
|
+
* Tools registered (real implementations — CPP-01 through CPP-04, GEN-01 through GEN-08):
|
|
176
|
+
* ue_read_cpp_class — Read and parse a UE C++ header file
|
|
177
|
+
* ue_get_class_hierarchy — Get inheritance hierarchy for a UE class
|
|
178
|
+
* ue_trace_includes — Trace include chain for a header file
|
|
179
|
+
* ue_find_class_file — Locate the header and source files for a UE class name
|
|
180
|
+
* ue_generate_class — Generate a new UE C++ class file pair (.h + .cpp)
|
|
181
|
+
* ue_add_property — Add a UPROPERTY declaration to an existing .h file
|
|
182
|
+
* ue_add_function — Add a UFUNCTION declaration to an existing .h file
|
|
183
|
+
* ue_add_include — Add an #include directive to an existing .h or .cpp file
|
|
184
|
+
*
|
|
185
|
+
* @param server The McpServer instance to register tools on.
|
|
186
|
+
*/
|
|
187
|
+
export function registerCppTools(server) {
|
|
188
|
+
// --------------------------------------------------------------------------
|
|
189
|
+
// ue_read_cpp_class
|
|
190
|
+
// --------------------------------------------------------------------------
|
|
191
|
+
server.registerTool('ue_read_cpp_class', {
|
|
192
|
+
title: 'Read UE C++ Class',
|
|
193
|
+
description: 'Read and parse a UE C++ header file, extracting UCLASS, UPROPERTY, and UFUNCTION declarations.',
|
|
194
|
+
inputSchema: z.object({
|
|
195
|
+
file_path: z.string().describe('Absolute path to the .h file'),
|
|
196
|
+
}),
|
|
197
|
+
annotations: {
|
|
198
|
+
readOnlyHint: true,
|
|
199
|
+
destructiveHint: false,
|
|
200
|
+
},
|
|
201
|
+
}, withKnownIssues('ue_read_cpp_class', handleReadCppClass));
|
|
202
|
+
// --------------------------------------------------------------------------
|
|
203
|
+
// ue_get_class_hierarchy
|
|
204
|
+
// --------------------------------------------------------------------------
|
|
205
|
+
server.registerTool('ue_get_class_hierarchy', {
|
|
206
|
+
title: 'Get UE Class Hierarchy',
|
|
207
|
+
description: 'Get the full inheritance hierarchy for a Unreal Engine C++ class.',
|
|
208
|
+
inputSchema: z.object({
|
|
209
|
+
class_name: z.string().describe('The UE class name (e.g., AActor, UObject)'),
|
|
210
|
+
}),
|
|
211
|
+
annotations: {
|
|
212
|
+
readOnlyHint: true,
|
|
213
|
+
destructiveHint: false,
|
|
214
|
+
},
|
|
215
|
+
}, withKnownIssues('ue_get_class_hierarchy', handleGetClassHierarchy));
|
|
216
|
+
// --------------------------------------------------------------------------
|
|
217
|
+
// ue_trace_includes
|
|
218
|
+
// --------------------------------------------------------------------------
|
|
219
|
+
server.registerTool('ue_trace_includes', {
|
|
220
|
+
title: 'Trace UE Include Chain',
|
|
221
|
+
description: 'Trace the #include chain for a UE C++ header file to identify circular or missing includes.',
|
|
222
|
+
inputSchema: z.object({
|
|
223
|
+
file_path: z.string().describe('Absolute path to the .h file to trace'),
|
|
224
|
+
}),
|
|
225
|
+
annotations: {
|
|
226
|
+
readOnlyHint: true,
|
|
227
|
+
destructiveHint: false,
|
|
228
|
+
},
|
|
229
|
+
}, withKnownIssues('ue_trace_includes', handleTraceIncludes));
|
|
230
|
+
// --------------------------------------------------------------------------
|
|
231
|
+
// ue_find_class_file
|
|
232
|
+
// --------------------------------------------------------------------------
|
|
233
|
+
server.registerTool('ue_find_class_file', {
|
|
234
|
+
title: 'Find UE Class File',
|
|
235
|
+
description: 'Locate the header file (.h) that declares a given UE C++ class name.',
|
|
236
|
+
inputSchema: z.object({
|
|
237
|
+
class_name: z.string().describe('The UE class name to find (e.g., APlayerController)'),
|
|
238
|
+
}),
|
|
239
|
+
annotations: {
|
|
240
|
+
readOnlyHint: true,
|
|
241
|
+
destructiveHint: false,
|
|
242
|
+
},
|
|
243
|
+
}, withKnownIssues('ue_find_class_file', handleFindClassFile));
|
|
244
|
+
// --------------------------------------------------------------------------
|
|
245
|
+
// ue_generate_class
|
|
246
|
+
// --------------------------------------------------------------------------
|
|
247
|
+
server.registerTool('ue_generate_class', {
|
|
248
|
+
title: 'Generate UE C++ Class',
|
|
249
|
+
description: 'Generate a new UE C++ class (Actor, Component, GameMode, Interface, etc.) with correct UHT macros and boilerplate. Writes .h and .cpp files to the specified directory.',
|
|
250
|
+
inputSchema: z.object({
|
|
251
|
+
class_type: z.enum(['Actor', 'ActorComponent', 'SceneComponent', 'GameMode', 'GameState', 'PlayerState', 'PlayerController', 'Interface'])
|
|
252
|
+
.describe('The type of UE class to generate'),
|
|
253
|
+
class_name: z.string().describe('Class name without prefix (e.g., "MyActor" → "AMyActor")'),
|
|
254
|
+
module_name: z.string().describe('The module name (e.g., "MyGame") — used for the API macro'),
|
|
255
|
+
output_dir: z.string().describe('Absolute directory path where .h and .cpp will be written'),
|
|
256
|
+
parent_class: z.string().optional().describe('Optional parent class override (defaults by class_type)'),
|
|
257
|
+
}),
|
|
258
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
259
|
+
}, withKnownIssues('ue_generate_class', handleGenerateClass));
|
|
260
|
+
// --------------------------------------------------------------------------
|
|
261
|
+
// ue_add_property
|
|
262
|
+
// --------------------------------------------------------------------------
|
|
263
|
+
server.registerTool('ue_add_property', {
|
|
264
|
+
title: 'Add UPROPERTY to UE Class',
|
|
265
|
+
description: 'Add a UPROPERTY declaration to an existing UE C++ header file. The property is inserted after the last existing UPROPERTY in the target class.',
|
|
266
|
+
inputSchema: z.object({
|
|
267
|
+
file_path: z.string().describe('Absolute path to the .h file'),
|
|
268
|
+
class_name: z.string().describe('The class to add the property to'),
|
|
269
|
+
specifiers: z.array(z.string()).describe('UPROPERTY specifiers (e.g., ["EditAnywhere", "BlueprintReadWrite"])'),
|
|
270
|
+
type: z.string().describe('Property type (e.g., "float", "AActor*", "TArray<FHitResult>")'),
|
|
271
|
+
name: z.string().describe('Property name (e.g., "Health")'),
|
|
272
|
+
default_value: z.string().optional().describe('Optional default value (e.g., "100.0f")'),
|
|
273
|
+
}),
|
|
274
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
275
|
+
}, withKnownIssues('ue_add_property', handleAddProperty));
|
|
276
|
+
// --------------------------------------------------------------------------
|
|
277
|
+
// ue_add_function
|
|
278
|
+
// --------------------------------------------------------------------------
|
|
279
|
+
server.registerTool('ue_add_function', {
|
|
280
|
+
title: 'Add UFUNCTION to UE Class',
|
|
281
|
+
description: 'Add a UFUNCTION declaration to an existing UE C++ header file. The function signature is inserted after the last existing UFUNCTION in the target class.',
|
|
282
|
+
inputSchema: z.object({
|
|
283
|
+
file_path: z.string().describe('Absolute path to the .h file'),
|
|
284
|
+
class_name: z.string().describe('The class to add the function to'),
|
|
285
|
+
specifiers: z.array(z.string()).describe('UFUNCTION specifiers (e.g., ["BlueprintCallable", "Category=\\"Combat\\""])'),
|
|
286
|
+
return_type: z.string().describe('Return type (e.g., "void", "float", "FVector")'),
|
|
287
|
+
name: z.string().describe('Function name (e.g., "Attack")'),
|
|
288
|
+
params: z.string().optional().describe('Parameter list as raw string (e.g., "float Damage, AActor* Target")'),
|
|
289
|
+
is_const: z.boolean().optional().describe('Whether the function is const'),
|
|
290
|
+
}),
|
|
291
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
292
|
+
}, withKnownIssues('ue_add_function', handleAddFunction));
|
|
293
|
+
// --------------------------------------------------------------------------
|
|
294
|
+
// ue_add_include
|
|
295
|
+
// --------------------------------------------------------------------------
|
|
296
|
+
server.registerTool('ue_add_include', {
|
|
297
|
+
title: 'Add #include to UE C++ File',
|
|
298
|
+
description: 'Add an #include directive to an existing UE C++ header file. Inserts before the .generated.h include (UHT requirement). Idempotent — safe to call even if the include already exists.',
|
|
299
|
+
inputSchema: z.object({
|
|
300
|
+
file_path: z.string().describe('Absolute path to the .h or .cpp file'),
|
|
301
|
+
include_path: z.string().describe('Include path with quotes or angle brackets (e.g., \'"Engine/Actor.h"\' or \'<algorithm>\')'),
|
|
302
|
+
}),
|
|
303
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
304
|
+
}, withKnownIssues('ue_add_include', handleAddInclude));
|
|
305
|
+
}
|