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.
Files changed (124) hide show
  1. package/README.md +729 -0
  2. package/dist/build/error-parser.js +51 -0
  3. package/dist/build/fix-suggester.js +84 -0
  4. package/dist/build/ubt-runner.js +146 -0
  5. package/dist/cli.js +13 -0
  6. package/dist/config.js +8 -0
  7. package/dist/docs/data/ue57-api.js +228 -0
  8. package/dist/docs/doc-index.js +110 -0
  9. package/dist/docs/types.js +4 -0
  10. package/dist/generators/class-generator.js +363 -0
  11. package/dist/generators/file-modifier.js +276 -0
  12. package/dist/generators/uht-validator.js +177 -0
  13. package/dist/index.js +89 -0
  14. package/dist/parsers/cpp-class-index.js +230 -0
  15. package/dist/parsers/cpp-parser.js +369 -0
  16. package/dist/parsers/ini-parser.js +216 -0
  17. package/dist/parsers/uproject-parser.js +130 -0
  18. package/dist/plugin-bridge/client.js +217 -0
  19. package/dist/plugin-bridge/protocol.js +6 -0
  20. package/dist/plugin-bridge/retry.js +23 -0
  21. package/dist/setup.js +209 -0
  22. package/dist/tools/ai-systems/index.js +247 -0
  23. package/dist/tools/ai-systems/types.js +4 -0
  24. package/dist/tools/animation/index.js +241 -0
  25. package/dist/tools/animation/types.js +4 -0
  26. package/dist/tools/audio/index.js +204 -0
  27. package/dist/tools/audio/types.js +4 -0
  28. package/dist/tools/blueprint/index.js +495 -0
  29. package/dist/tools/blueprint/types.js +4 -0
  30. package/dist/tools/build/index.js +163 -0
  31. package/dist/tools/chaos/index.js +230 -0
  32. package/dist/tools/chaos/types.js +4 -0
  33. package/dist/tools/collision-physics/index.js +211 -0
  34. package/dist/tools/config/index.js +288 -0
  35. package/dist/tools/cpp/index.js +305 -0
  36. package/dist/tools/docs/index.js +251 -0
  37. package/dist/tools/editor/index.js +242 -0
  38. package/dist/tools/gas/index.js +222 -0
  39. package/dist/tools/gas/types.js +5 -0
  40. package/dist/tools/import-export/index.js +218 -0
  41. package/dist/tools/input/index.js +146 -0
  42. package/dist/tools/known-issues/index.js +88 -0
  43. package/dist/tools/known-issues/middleware.js +55 -0
  44. package/dist/tools/known-issues/store.js +125 -0
  45. package/dist/tools/livelink/index.js +203 -0
  46. package/dist/tools/livelink/types.js +4 -0
  47. package/dist/tools/material/index.js +190 -0
  48. package/dist/tools/motion-design/index.js +251 -0
  49. package/dist/tools/motion-design/types.js +6 -0
  50. package/dist/tools/movie-render/index.js +220 -0
  51. package/dist/tools/networking/index.js +149 -0
  52. package/dist/tools/pcg/index.js +164 -0
  53. package/dist/tools/selection/index.js +180 -0
  54. package/dist/tools/sequencer/index.js +218 -0
  55. package/dist/tools/validation/index.js +183 -0
  56. package/dist/tools/validation/types.js +4 -0
  57. package/dist/tools/viewport/index.js +310 -0
  58. package/dist/tools/worldpartition/index.js +226 -0
  59. package/dist/tools/worldpartition/types.js +4 -0
  60. package/dist/utils/execFileNoThrow.js +40 -0
  61. package/dist/utils/logger.js +27 -0
  62. package/dist/utils/path-guard.js +26 -0
  63. package/package.json +40 -0
  64. package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
  65. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
  66. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
  67. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
  68. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
  69. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
  70. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
  71. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
  72. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
  73. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
  74. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
  75. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
  76. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
  77. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
  78. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
  79. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
  80. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
  81. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
  82. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
  83. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
  84. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
  85. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
  86. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
  87. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
  88. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
  89. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
  90. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
  91. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
  92. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
  93. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
  94. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
  95. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
  96. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
  97. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
  98. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
  99. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
  100. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
  101. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
  102. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
  103. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
  104. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
  105. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
  106. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
  107. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
  108. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
  109. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
  110. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
  111. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
  112. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
  113. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
  114. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
  115. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
  116. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
  117. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
  118. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
  119. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
  120. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
  121. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
  122. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
  123. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
  124. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPTcpServer.h +59 -0
@@ -0,0 +1,222 @@
1
+ // src/tools/gas/index.ts
2
+ // MCP tool implementations for Gameplay Ability System asset inspection (Phase 25).
3
+ // All tools route commands through PluginBridgeClient to the C++ MCPBridge handlers.
4
+ // Returns structured plugin_not_connected errors when the plugin is absent.
5
+ //
6
+ // Tools registered (Phase 25 — GAS-01 through GAS-04):
7
+ // ue_list_abilities — List Gameplay Ability classes with tags, costs, and cooldowns
8
+ // ue_inspect_gameplay_effect — Inspect Gameplay Effect modifiers, duration, and stacking
9
+ // ue_read_attribute_set — Read Attribute Set definitions with base values and clamping
10
+ // ue_query_gameplay_tags — Query Gameplay Tag hierarchy and find tagged assets
11
+ //
12
+ // All tools require MCPBridge plugin (Phase 25 — GAS-01 through GAS-04).
13
+ import { z } from 'zod';
14
+ import { withKnownIssues } from '../known-issues/middleware.js';
15
+ import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
16
+ // Module-level bridge instance — injected in tests via exported handler signatures.
17
+ const bridge = new PluginBridgeClient();
18
+ // ---------------------------------------------------------------------------
19
+ // sendOrDisconnect helper
20
+ // ---------------------------------------------------------------------------
21
+ /**
22
+ * Sends a command to the bridge and returns a ToolResult.
23
+ *
24
+ * - On success (response.success true): returns data as JSON text.
25
+ * - On command-level failure (response.success false): returns isError:true with error JSON.
26
+ * - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
27
+ * - On other errors: rethrows (withKnownIssues catches and formats).
28
+ *
29
+ * NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
30
+ * passing an empty string is safe and correct (per STATE.md decision).
31
+ */
32
+ async function sendOrDisconnect(b, cmd) {
33
+ try {
34
+ const response = await b.sendCommand({ ...cmd, correlationId: '' });
35
+ if (!response.success) {
36
+ return {
37
+ isError: true,
38
+ content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
39
+ };
40
+ }
41
+ return {
42
+ content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
43
+ };
44
+ }
45
+ catch (err) {
46
+ if (err instanceof PluginNotConnectedError) {
47
+ return {
48
+ isError: true,
49
+ content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
50
+ };
51
+ }
52
+ throw err; // withKnownIssues catches unexpected errors
53
+ }
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // Exported handler functions (for direct unit testing via bridge injection)
57
+ // ---------------------------------------------------------------------------
58
+ /**
59
+ * ue_list_abilities handler — satisfies GAS-01.
60
+ * Sends gas.abilities to the plugin; returns all Gameplay Ability classes with
61
+ * tags, cost/cooldown effect references, and instancing policy.
62
+ *
63
+ * @param args Tool arguments including optional class_filter.
64
+ * @param b PluginBridgeClient instance (defaults to module-level singleton).
65
+ */
66
+ export async function handleListAbilities(args, b = bridge) {
67
+ // Response data shape: AbilityListResult
68
+ const payload = {};
69
+ if (args.class_filter !== undefined) {
70
+ payload['class_filter'] = args.class_filter;
71
+ }
72
+ return sendOrDisconnect(b, {
73
+ type: 'gas.abilities',
74
+ payload,
75
+ });
76
+ }
77
+ /**
78
+ * ue_inspect_gameplay_effect handler — satisfies GAS-02.
79
+ * Sends gas.effects to the plugin; returns modifiers, duration policy,
80
+ * stacking configuration, period interval, and Gameplay Cue tags.
81
+ *
82
+ * @param args Tool arguments including asset_path.
83
+ * @param b PluginBridgeClient instance (defaults to module-level singleton).
84
+ */
85
+ export async function handleInspectGameplayEffect(args, b = bridge) {
86
+ // Response data shape: GameplayEffectInspectResult
87
+ return sendOrDisconnect(b, {
88
+ type: 'gas.effects',
89
+ payload: { asset_path: args.asset_path },
90
+ });
91
+ }
92
+ /**
93
+ * ue_read_attribute_set handler — satisfies GAS-03.
94
+ * Sends gas.attributes to the plugin; returns all Attribute Set properties
95
+ * with base values, replication status, and clamping information.
96
+ *
97
+ * @param args Tool arguments including asset_path.
98
+ * @param b PluginBridgeClient instance (defaults to module-level singleton).
99
+ */
100
+ export async function handleReadAttributeSet(args, b = bridge) {
101
+ // Response data shape: AttributeSetReadResult
102
+ return sendOrDisconnect(b, {
103
+ type: 'gas.attributes',
104
+ payload: { asset_path: args.asset_path },
105
+ });
106
+ }
107
+ /**
108
+ * ue_query_gameplay_tags handler — satisfies GAS-04.
109
+ * Sends gas.tags to the plugin; returns matching tag paths from the hierarchy
110
+ * and optionally finds all assets that reference a specific tag.
111
+ *
112
+ * @param args Tool arguments including optional tag_filter and find_assets_with_tag.
113
+ * @param b PluginBridgeClient instance (defaults to module-level singleton).
114
+ */
115
+ export async function handleQueryGameplayTags(args, b = bridge) {
116
+ // Response data shape: GameplayTagQueryResult
117
+ const payload = {
118
+ tag_filter: args.tag_filter ?? '',
119
+ };
120
+ if (args.find_assets_with_tag !== undefined) {
121
+ payload['find_assets_with_tag'] = args.find_assets_with_tag;
122
+ }
123
+ return sendOrDisconnect(b, {
124
+ type: 'gas.tags',
125
+ payload,
126
+ });
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // registerGASTools
130
+ // ---------------------------------------------------------------------------
131
+ /**
132
+ * Register UE Gameplay Ability System inspection tools on the MCP server.
133
+ *
134
+ * All tools in this domain require the MCPBridge editor plugin.
135
+ * When the plugin is not connected, each handler returns:
136
+ * { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
137
+ *
138
+ * Tools registered (Phase 25):
139
+ * ue_list_abilities — GAS-01: List ability classes with tags, costs, and instancing
140
+ * ue_inspect_gameplay_effect — GAS-02: Inspect effect modifiers, duration, stacking
141
+ * ue_read_attribute_set — GAS-03: Read Attribute Set definitions with base values
142
+ * ue_query_gameplay_tags — GAS-04: Query tag hierarchy and find tagged assets
143
+ *
144
+ * @param server The McpServer instance to register tools on.
145
+ * @param _bridge Optional PluginBridgeClient for testing (not used directly — handlers
146
+ * accept bridge injection via their exported function signatures).
147
+ */
148
+ export function registerGASTools(server, _bridge) {
149
+ const b = _bridge ?? new PluginBridgeClient();
150
+ // --------------------------------------------------------------------------
151
+ // ue_list_abilities (GAS-01)
152
+ // --------------------------------------------------------------------------
153
+ server.registerTool('ue_list_abilities', {
154
+ title: 'List Gameplay Abilities',
155
+ description: '[requires_plugin] List all Gameplay Ability classes in the project with their tags, cost/cooldown effect references, and instancing policy.',
156
+ inputSchema: z.object({
157
+ class_filter: z
158
+ .string()
159
+ .optional()
160
+ .describe('Optional substring to filter ability class names (e.g. "Melee" matches GA_MeleeAttack)'),
161
+ }),
162
+ annotations: {
163
+ readOnlyHint: true,
164
+ destructiveHint: false,
165
+ },
166
+ }, withKnownIssues('ue_list_abilities', async (args) => handleListAbilities(args, b)));
167
+ // --------------------------------------------------------------------------
168
+ // ue_inspect_gameplay_effect (GAS-02)
169
+ // --------------------------------------------------------------------------
170
+ server.registerTool('ue_inspect_gameplay_effect', {
171
+ title: 'Inspect Gameplay Effect',
172
+ description: '[requires_plugin] Inspect a Gameplay Effect asset\'s modifiers, duration policy, stacking configuration, and period interval.',
173
+ inputSchema: z.object({
174
+ asset_path: z
175
+ .string()
176
+ .min(1)
177
+ .describe('UE long package path to the Gameplay Effect asset, e.g. /Game/GAS/GE_DealDamage'),
178
+ }),
179
+ annotations: {
180
+ readOnlyHint: true,
181
+ destructiveHint: false,
182
+ },
183
+ }, withKnownIssues('ue_inspect_gameplay_effect', async (args) => handleInspectGameplayEffect(args, b)));
184
+ // --------------------------------------------------------------------------
185
+ // ue_read_attribute_set (GAS-03)
186
+ // --------------------------------------------------------------------------
187
+ server.registerTool('ue_read_attribute_set', {
188
+ title: 'Read Attribute Set',
189
+ description: '[requires_plugin] Read an Attribute Set class\'s attributes with base values, replication status, and clamping information.',
190
+ inputSchema: z.object({
191
+ asset_path: z
192
+ .string()
193
+ .min(1)
194
+ .describe('UE long package path to the Attribute Set asset, e.g. /Game/GAS/AS_Character'),
195
+ }),
196
+ annotations: {
197
+ readOnlyHint: true,
198
+ destructiveHint: false,
199
+ },
200
+ }, withKnownIssues('ue_read_attribute_set', async (args) => handleReadAttributeSet(args, b)));
201
+ // --------------------------------------------------------------------------
202
+ // ue_query_gameplay_tags (GAS-04)
203
+ // --------------------------------------------------------------------------
204
+ server.registerTool('ue_query_gameplay_tags', {
205
+ title: 'Query Gameplay Tags',
206
+ description: '[requires_plugin] Query the Gameplay Tag hierarchy by filter and optionally find all assets that reference a specific tag.',
207
+ inputSchema: z.object({
208
+ tag_filter: z
209
+ .string()
210
+ .optional()
211
+ .describe('Optional tag prefix or substring to filter results (e.g. "Ability.Melee" returns all melee ability tags)'),
212
+ find_assets_with_tag: z
213
+ .string()
214
+ .optional()
215
+ .describe('Optional exact tag path to perform a reverse lookup — returns all assets referencing this tag'),
216
+ }),
217
+ annotations: {
218
+ readOnlyHint: true,
219
+ destructiveHint: false,
220
+ },
221
+ }, withKnownIssues('ue_query_gameplay_tags', async (args) => handleQueryGameplayTags(args, b)));
222
+ }
@@ -0,0 +1,5 @@
1
+ // src/tools/gas/types.ts
2
+ // TypeScript result interfaces for the four Gameplay Ability System MCP tools (Phase 25).
3
+ // These interfaces mirror the JSON shapes returned by the C++ handlers in Plan 25-01.
4
+ // Requirements covered: GAS-01, GAS-02, GAS-03, GAS-04.
5
+ export {};
@@ -0,0 +1,218 @@
1
+ // src/tools/import-export/index.ts
2
+ // MCP tool implementations for asset import/export operations (Phase 21).
3
+ // All tools route commands through PluginBridgeClient to the C++ MCPBridge handlers.
4
+ // Returns structured plugin_not_connected errors when the plugin is absent.
5
+ //
6
+ // Tools registered (Phase 21 — IMP-01 through IMP-04):
7
+ // ue_import_fbx — Import an FBX file into the project at a specified content path
8
+ // ue_import_usd — Import a USD file using the Interchange pipeline
9
+ // ue_export_mesh — Export a StaticMesh or SkeletalMesh asset to FBX format
10
+ // ue_batch_import — Batch import multiple files from a directory with configurable settings
11
+ //
12
+ // Command mappings:
13
+ // ue_import_fbx -> import.fbx
14
+ // ue_import_usd -> import.usd
15
+ // ue_export_mesh -> export.mesh
16
+ // ue_batch_import -> import.batch
17
+ //
18
+ // All tools require MCPBridge plugin (Phase 21 — IMP-01 through IMP-04).
19
+ import { z } from 'zod';
20
+ import { withKnownIssues } from '../known-issues/middleware.js';
21
+ import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
22
+ // ---------------------------------------------------------------------------
23
+ // sendOrDisconnect helper
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Sends a command to the bridge and returns a ToolResult.
27
+ *
28
+ * - On success (response.success true): returns data as JSON text.
29
+ * - On command-level failure (response.success false): returns isError:true with error JSON.
30
+ * - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
31
+ * - On other errors: rethrows (withKnownIssues catches and formats).
32
+ *
33
+ * NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
34
+ * passing an empty string is safe and correct (per STATE.md decision).
35
+ */
36
+ async function sendOrDisconnect(b, cmd) {
37
+ try {
38
+ const response = await b.sendCommand({ ...cmd, correlationId: '' });
39
+ if (!response.success) {
40
+ return {
41
+ isError: true,
42
+ content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
43
+ };
44
+ }
45
+ return {
46
+ content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
47
+ };
48
+ }
49
+ catch (err) {
50
+ if (err instanceof PluginNotConnectedError) {
51
+ return {
52
+ isError: true,
53
+ content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
54
+ };
55
+ }
56
+ throw err; // withKnownIssues catches unexpected errors
57
+ }
58
+ }
59
+ // ---------------------------------------------------------------------------
60
+ // registerImportExportTools
61
+ // ---------------------------------------------------------------------------
62
+ /**
63
+ * Register UE asset import/export tools on the MCP server.
64
+ *
65
+ * All tools in this domain require the MCPBridge editor plugin.
66
+ * When the plugin is not connected, each handler returns:
67
+ * { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
68
+ *
69
+ * Tools registered (Phase 21):
70
+ * ue_import_fbx — IMP-01: Import an FBX file into the project
71
+ * ue_import_usd — IMP-02: Import a USD file via Interchange pipeline
72
+ * ue_export_mesh — IMP-03: Export a static/skeletal mesh to FBX
73
+ * ue_batch_import — IMP-04: Batch import multiple files with settings
74
+ *
75
+ * @param server The McpServer instance to register tools on.
76
+ * @param bridge Optional PluginBridgeClient for testing (injected bridge replaces module singleton).
77
+ */
78
+ export function registerImportExportTools(server, bridge) {
79
+ const _bridge = bridge ?? new PluginBridgeClient();
80
+ // --------------------------------------------------------------------------
81
+ // ue_import_fbx (IMP-01)
82
+ // --------------------------------------------------------------------------
83
+ server.registerTool('ue_import_fbx', {
84
+ title: 'Import FBX File',
85
+ description: '[requires_plugin] Import an FBX file into the project at a specified content path. Returns the list of created asset paths.',
86
+ inputSchema: z.object({
87
+ source_file: z
88
+ .string()
89
+ .min(1)
90
+ .describe('Absolute OS path to the .fbx file to import'),
91
+ dest_path: z
92
+ .string()
93
+ .min(1)
94
+ .describe('Content browser destination path, e.g. /Game/Meshes'),
95
+ import_materials: z
96
+ .boolean()
97
+ .optional()
98
+ .default(true)
99
+ .describe('Import materials from FBX'),
100
+ combine_meshes: z
101
+ .boolean()
102
+ .optional()
103
+ .default(false)
104
+ .describe('Combine all meshes into a single asset'),
105
+ scale_factor: z
106
+ .number()
107
+ .optional()
108
+ .default(1.0)
109
+ .describe('Import scale factor'),
110
+ }),
111
+ annotations: {
112
+ readOnlyHint: false,
113
+ destructiveHint: false,
114
+ },
115
+ }, withKnownIssues('ue_import_fbx', async (args) => sendOrDisconnect(_bridge, {
116
+ type: 'import.fbx',
117
+ payload: { ...args },
118
+ })));
119
+ // --------------------------------------------------------------------------
120
+ // ue_import_usd (IMP-02)
121
+ // --------------------------------------------------------------------------
122
+ server.registerTool('ue_import_usd', {
123
+ title: 'Import USD File',
124
+ description: '[requires_plugin] Import a USD file into the project using the Interchange pipeline. Supports .usd, .usda, and .usdc formats.',
125
+ inputSchema: z.object({
126
+ source_file: z
127
+ .string()
128
+ .min(1)
129
+ .describe('Absolute OS path to the USD file'),
130
+ dest_path: z
131
+ .string()
132
+ .min(1)
133
+ .describe('Content browser destination path'),
134
+ }),
135
+ annotations: {
136
+ readOnlyHint: false,
137
+ destructiveHint: false,
138
+ },
139
+ }, withKnownIssues('ue_import_usd', async (args) => sendOrDisconnect(_bridge, {
140
+ type: 'import.usd',
141
+ payload: { ...args },
142
+ })));
143
+ // --------------------------------------------------------------------------
144
+ // ue_export_mesh (IMP-03)
145
+ // --------------------------------------------------------------------------
146
+ server.registerTool('ue_export_mesh', {
147
+ title: 'Export Mesh to FBX',
148
+ description: '[requires_plugin] Export a StaticMesh or SkeletalMesh asset to FBX format at the specified output path.',
149
+ inputSchema: z.object({
150
+ asset_path: z
151
+ .string()
152
+ .min(1)
153
+ .describe('Content browser path to the mesh asset, e.g. /Game/Meshes/MyMesh'),
154
+ output_file: z
155
+ .string()
156
+ .min(1)
157
+ .describe('Absolute OS path for the output .fbx file'),
158
+ export_collision: z
159
+ .boolean()
160
+ .optional()
161
+ .default(false)
162
+ .describe('Include collision geometry in export'),
163
+ level_of_detail: z
164
+ .number()
165
+ .int()
166
+ .min(0)
167
+ .optional()
168
+ .default(0)
169
+ .describe('LOD level to export (0 = base)'),
170
+ }),
171
+ annotations: {
172
+ readOnlyHint: false,
173
+ destructiveHint: false,
174
+ },
175
+ }, withKnownIssues('ue_export_mesh', async (args) => sendOrDisconnect(_bridge, {
176
+ type: 'export.mesh',
177
+ payload: { ...args },
178
+ })));
179
+ // --------------------------------------------------------------------------
180
+ // ue_batch_import (IMP-04)
181
+ // --------------------------------------------------------------------------
182
+ server.registerTool('ue_batch_import', {
183
+ title: 'Batch Import Files',
184
+ description: '[requires_plugin] Import multiple files from a directory into the project with configurable settings. Supports filtering by file extension.',
185
+ inputSchema: z.object({
186
+ directory: z
187
+ .string()
188
+ .min(1)
189
+ .describe('Absolute OS path to the directory containing files to import'),
190
+ extensions: z
191
+ .array(z.string())
192
+ .optional()
193
+ .default(['fbx'])
194
+ .describe("File extensions to import, e.g. ['fbx', 'obj', 'usd']"),
195
+ dest_path: z
196
+ .string()
197
+ .min(1)
198
+ .describe('Content browser destination path'),
199
+ import_materials: z
200
+ .boolean()
201
+ .optional()
202
+ .default(true)
203
+ .describe('Import materials from source files'),
204
+ scale_factor: z
205
+ .number()
206
+ .optional()
207
+ .default(1.0)
208
+ .describe('Import scale factor for all files'),
209
+ }),
210
+ annotations: {
211
+ readOnlyHint: false,
212
+ destructiveHint: false,
213
+ },
214
+ }, withKnownIssues('ue_batch_import', async (args) => sendOrDisconnect(_bridge, {
215
+ type: 'import.batch',
216
+ payload: { ...args },
217
+ })));
218
+ }
@@ -0,0 +1,146 @@
1
+ // src/tools/input/index.ts
2
+ // MCP tool implementations for Enhanced Input Management (Phase 14).
3
+ // All tools route commands through PluginBridgeClient to the C++ MCPBridge plugin.
4
+ import { z } from 'zod';
5
+ import { withKnownIssues } from '../known-issues/middleware.js';
6
+ import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
7
+ // ---------------------------------------------------------------------------
8
+ // sendOrDisconnect helper
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Sends a command to the bridge and returns a ToolResult.
12
+ *
13
+ * - On success (response.success true): returns data as JSON text.
14
+ * - On command-level failure (response.success false): returns isError:true with error JSON.
15
+ * - On PluginNotConnectedError: returns isError:true with plugin_not_connected JSON.
16
+ * - On other errors: rethrows (withKnownIssues catches and formats).
17
+ *
18
+ * NOTE: sendCommand() overwrites correlationId with crypto.randomUUID() internally;
19
+ * passing an empty string is safe and correct (per STATE.md decision).
20
+ */
21
+ async function sendOrDisconnect(bridge, cmd) {
22
+ try {
23
+ const response = await bridge.sendCommand({ ...cmd, correlationId: '' });
24
+ if (!response.success) {
25
+ return {
26
+ isError: true,
27
+ content: [{ type: 'text', text: JSON.stringify({ error: response.error }) }],
28
+ };
29
+ }
30
+ return {
31
+ content: [{ type: 'text', text: JSON.stringify(response.data ?? {}) }],
32
+ };
33
+ }
34
+ catch (err) {
35
+ if (err instanceof PluginNotConnectedError) {
36
+ return {
37
+ isError: true,
38
+ content: [{ type: 'text', text: JSON.stringify(err.bridgeError) }],
39
+ };
40
+ }
41
+ throw err; // withKnownIssues catches unexpected errors
42
+ }
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // registerInputTools
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * Register UE Enhanced Input Management tools on the MCP server.
49
+ *
50
+ * All tools in this domain require the MCPBridge editor plugin.
51
+ * When the plugin is not connected, each handler returns:
52
+ * { isError: true, content: [{ type: 'text', text: <plugin_not_connected JSON> }] }
53
+ *
54
+ * Tools registered (Phase 14):
55
+ * ue_list_input_actions — List all UInputAction assets with value types
56
+ * ue_create_input_action — Create a new UInputAction asset
57
+ * ue_list_input_contexts — List all UInputMappingContext assets with bindings
58
+ * ue_add_input_binding — Add or update a key binding in a UInputMappingContext
59
+ *
60
+ * @param server The McpServer instance to register tools on.
61
+ * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
62
+ */
63
+ export function registerInputTools(server, bridge) {
64
+ const _bridge = bridge ?? new PluginBridgeClient();
65
+ // --------------------------------------------------------------------------
66
+ // ue_list_input_actions
67
+ // --------------------------------------------------------------------------
68
+ server.registerTool('ue_list_input_actions', {
69
+ title: 'List Input Actions',
70
+ description: '[requires_plugin] List all UInputAction assets in the project with their value types (bool, float, Axis2D, Axis3D).',
71
+ inputSchema: z.object({}),
72
+ annotations: {
73
+ readOnlyHint: true,
74
+ destructiveHint: false,
75
+ },
76
+ }, withKnownIssues('ue_list_input_actions', async (_args) => {
77
+ return sendOrDisconnect(_bridge, { type: 'input.listActions' });
78
+ }));
79
+ // --------------------------------------------------------------------------
80
+ // ue_create_input_action
81
+ // --------------------------------------------------------------------------
82
+ server.registerTool('ue_create_input_action', {
83
+ title: 'Create Input Action',
84
+ description: '[requires_plugin] Create a new UInputAction asset at the given content path with the specified value type.',
85
+ inputSchema: z.object({
86
+ asset_path: z.string().describe('Content-browser package path, e.g. /Game/Input/IA_Jump'),
87
+ value_type: z
88
+ .enum(['bool', 'float', 'Axis2D', 'Axis3D'])
89
+ .describe('Input action value type'),
90
+ }),
91
+ annotations: {
92
+ readOnlyHint: false,
93
+ destructiveHint: false,
94
+ },
95
+ }, withKnownIssues('ue_create_input_action', async (args) => {
96
+ return sendOrDisconnect(_bridge, {
97
+ type: 'input.createAction',
98
+ payload: { asset_path: args.asset_path, value_type: args.value_type },
99
+ });
100
+ }));
101
+ // --------------------------------------------------------------------------
102
+ // ue_list_input_contexts
103
+ // --------------------------------------------------------------------------
104
+ server.registerTool('ue_list_input_contexts', {
105
+ title: 'List Input Mapping Contexts',
106
+ description: '[requires_plugin] List all UInputMappingContext assets in the project with their action-to-key bindings.',
107
+ inputSchema: z.object({}),
108
+ annotations: {
109
+ readOnlyHint: true,
110
+ destructiveHint: false,
111
+ },
112
+ }, withKnownIssues('ue_list_input_contexts', async (_args) => {
113
+ return sendOrDisconnect(_bridge, { type: 'input.listContexts' });
114
+ }));
115
+ // --------------------------------------------------------------------------
116
+ // ue_add_input_binding
117
+ // --------------------------------------------------------------------------
118
+ server.registerTool('ue_add_input_binding', {
119
+ title: 'Add Input Binding',
120
+ description: '[requires_plugin] Add or update a key binding in a UInputMappingContext. Maps an Input Action to a keyboard/gamepad key.',
121
+ inputSchema: z.object({
122
+ asset_path: z
123
+ .string()
124
+ .describe('Package path of the UInputMappingContext, e.g. /Game/Input/IMC_Default'),
125
+ action_path: z
126
+ .string()
127
+ .describe('Package path of the UInputAction to bind, e.g. /Game/Input/IA_Jump'),
128
+ key: z
129
+ .string()
130
+ .describe('Key name as a UE FKey string, e.g. SpaceBar, Gamepad_FaceButton_Bottom'),
131
+ }),
132
+ annotations: {
133
+ readOnlyHint: false,
134
+ destructiveHint: false,
135
+ },
136
+ }, withKnownIssues('ue_add_input_binding', async (args) => {
137
+ return sendOrDisconnect(_bridge, {
138
+ type: 'input.addBinding',
139
+ payload: {
140
+ asset_path: args.asset_path,
141
+ action_path: args.action_path,
142
+ key: args.key,
143
+ },
144
+ });
145
+ }));
146
+ }
@@ -0,0 +1,88 @@
1
+ // src/tools/known-issues/index.ts
2
+ // Registers the ue_list_known_issues and ue_add_known_issue MCP tools.
3
+ // Both tools are wrapped with withKnownIssues middleware per INF-06.
4
+ import { z } from 'zod';
5
+ import { withKnownIssues } from './middleware.js';
6
+ import { readKnownIssues, appendKnownIssue } from './store.js';
7
+ // ---------------------------------------------------------------------------
8
+ // Tool registration
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Register KNOWN_ISSUES management tools on the MCP server.
12
+ *
13
+ * Tools registered:
14
+ * ue_list_known_issues — returns all entries from KNOWN_ISSUES.md (read-only)
15
+ * ue_add_known_issue — appends a new entry to KNOWN_ISSUES.md
16
+ *
17
+ * @param server The McpServer instance to register tools on.
18
+ */
19
+ export function registerKnownIssuesTools(server) {
20
+ // --------------------------------------------------------------------------
21
+ // ue_list_known_issues
22
+ // --------------------------------------------------------------------------
23
+ server.registerTool('ue_list_known_issues', {
24
+ title: 'List Known Issues',
25
+ description: 'Returns all entries from KNOWN_ISSUES.md. Run this before any operation to check for known issues that may affect the result.',
26
+ inputSchema: z.object({}),
27
+ annotations: {
28
+ readOnlyHint: true,
29
+ destructiveHint: false,
30
+ },
31
+ }, withKnownIssues('ue_list_known_issues', async () => {
32
+ const issues = await readKnownIssues();
33
+ if (issues.length === 0) {
34
+ return {
35
+ content: [{ type: 'text', text: 'No known issues recorded.' }],
36
+ };
37
+ }
38
+ const lines = issues.map((i) => `## ${i.id}\n` +
39
+ `**Description:** ${i.description}\n` +
40
+ `**Affected tools:** ${i.affectedTools.join(', ')}\n` +
41
+ `**Root cause:** ${i.rootCause}\n` +
42
+ `**Resolution:** ${i.resolution}\n` +
43
+ `**Date added:** ${i.dateAdded}\n` +
44
+ `**Status:** ${i.status}`);
45
+ return {
46
+ content: [{ type: 'text', text: lines.join('\n\n---\n\n') }],
47
+ };
48
+ }));
49
+ // --------------------------------------------------------------------------
50
+ // ue_add_known_issue
51
+ // --------------------------------------------------------------------------
52
+ server.registerTool('ue_add_known_issue', {
53
+ title: 'Add Known Issue',
54
+ description: 'Appends a new entry to KNOWN_ISSUES.md after user approves a fix.',
55
+ inputSchema: z.object({
56
+ description: z.string(),
57
+ affectedTools: z
58
+ .string()
59
+ .describe('Comma-separated tool names, or * for all tools'),
60
+ rootCause: z.string(),
61
+ resolution: z.string(),
62
+ }),
63
+ annotations: {
64
+ readOnlyHint: false,
65
+ destructiveHint: false,
66
+ },
67
+ }, withKnownIssues('ue_add_known_issue', async ({ description, affectedTools, rootCause, resolution }) => {
68
+ const issue = await appendKnownIssue({
69
+ description,
70
+ affectedTools: affectedTools
71
+ .split(',')
72
+ .map((s) => s.trim())
73
+ .filter(Boolean),
74
+ rootCause,
75
+ resolution,
76
+ dateAdded: new Date().toISOString().split('T')[0] ?? '',
77
+ status: 'active',
78
+ });
79
+ return {
80
+ content: [
81
+ {
82
+ type: 'text',
83
+ text: `Known issue recorded as ${issue.id}. It will be surfaced as a warning before any affected tool runs.`,
84
+ },
85
+ ],
86
+ };
87
+ }));
88
+ }