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,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
|
+
}
|