@akiojin/unity-mcp-server 5.4.0 → 5.5.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/package.json +2 -2
- package/scripts/ensure-better-sqlite3.mjs +2 -1
- package/src/core/config.js +42 -0
- package/src/core/server.js +50 -3
- package/src/core/toolCategoryFilter.js +309 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akiojin/unity-mcp-server",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
4
4
|
"description": "MCP server and Unity Editor bridge — enables AI assistants to control Unity for AI-assisted workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/core/server.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"lru-cache": "^11.0.2"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
31
|
-
"node": ">=18 <
|
|
31
|
+
"node": ">=18 <25"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
package/src/core/config.js
CHANGED
|
@@ -52,6 +52,15 @@ function parseIntEnv(value) {
|
|
|
52
52
|
return Number.isFinite(n) ? n : undefined;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function parseCsvEnv(value) {
|
|
56
|
+
if (typeof value !== 'string') return undefined;
|
|
57
|
+
const items = value
|
|
58
|
+
.split(',')
|
|
59
|
+
.map(s => s.trim())
|
|
60
|
+
.filter(Boolean);
|
|
61
|
+
return items;
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
function envString(key) {
|
|
56
65
|
const raw = process.env[key];
|
|
57
66
|
if (typeof raw !== 'string') return undefined;
|
|
@@ -143,6 +152,12 @@ const baseConfig = {
|
|
|
143
152
|
fields: []
|
|
144
153
|
},
|
|
145
154
|
|
|
155
|
+
// Tool visibility filter
|
|
156
|
+
tools: {
|
|
157
|
+
includeCategories: [],
|
|
158
|
+
excludeCategories: []
|
|
159
|
+
},
|
|
160
|
+
|
|
146
161
|
// Write queue removed: all edits go through structured Roslyn tools.
|
|
147
162
|
|
|
148
163
|
// Search-related defaults and engine selection
|
|
@@ -190,6 +205,8 @@ function loadEnvConfig() {
|
|
|
190
205
|
const lspRequestTimeoutMs = parseIntEnv(process.env.UNITY_MCP_LSP_REQUEST_TIMEOUT_MS);
|
|
191
206
|
const lspSlowRequestWarnMs = parseIntEnv(process.env.UNITY_MCP_LSP_SLOW_REQUEST_WARN_MS);
|
|
192
207
|
const lspValidationTimeoutMs = parseIntEnv(process.env.UNITY_MCP_LSP_VALIDATION_TIMEOUT_MS);
|
|
208
|
+
const includeCategories = parseCsvEnv(process.env.UNITY_MCP_TOOL_INCLUDE_CATEGORIES);
|
|
209
|
+
const excludeCategories = parseCsvEnv(process.env.UNITY_MCP_TOOL_EXCLUDE_CATEGORIES);
|
|
193
210
|
|
|
194
211
|
const out = {};
|
|
195
212
|
|
|
@@ -227,6 +244,12 @@ function loadEnvConfig() {
|
|
|
227
244
|
out.telemetry = { enabled: telemetryEnabled };
|
|
228
245
|
}
|
|
229
246
|
|
|
247
|
+
if (includeCategories !== undefined || excludeCategories !== undefined) {
|
|
248
|
+
out.tools = {};
|
|
249
|
+
if (includeCategories !== undefined) out.tools.includeCategories = includeCategories;
|
|
250
|
+
if (excludeCategories !== undefined) out.tools.excludeCategories = excludeCategories;
|
|
251
|
+
}
|
|
252
|
+
|
|
230
253
|
if (lspRequestTimeoutMs !== undefined) {
|
|
231
254
|
out.lsp = { requestTimeoutMs: lspRequestTimeoutMs };
|
|
232
255
|
}
|
|
@@ -306,6 +329,25 @@ function validateAndNormalizeConfig(cfg) {
|
|
|
306
329
|
cfg.project.codeIndexRoot = null;
|
|
307
330
|
}
|
|
308
331
|
|
|
332
|
+
if (!cfg.tools || typeof cfg.tools !== 'object') {
|
|
333
|
+
cfg.tools = {};
|
|
334
|
+
}
|
|
335
|
+
if (!Array.isArray(cfg.tools.includeCategories)) {
|
|
336
|
+
cfg.tools.includeCategories = [];
|
|
337
|
+
}
|
|
338
|
+
if (!Array.isArray(cfg.tools.excludeCategories)) {
|
|
339
|
+
cfg.tools.excludeCategories = [];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
cfg.tools.includeCategories = cfg.tools.includeCategories
|
|
343
|
+
.filter(v => typeof v === 'string')
|
|
344
|
+
.map(v => v.trim())
|
|
345
|
+
.filter(Boolean);
|
|
346
|
+
cfg.tools.excludeCategories = cfg.tools.excludeCategories
|
|
347
|
+
.filter(v => typeof v === 'string')
|
|
348
|
+
.map(v => v.trim())
|
|
349
|
+
.filter(Boolean);
|
|
350
|
+
|
|
309
351
|
// lsp timeout sanity
|
|
310
352
|
if (cfg.lsp?.requestTimeoutMs !== undefined) {
|
|
311
353
|
const t = Number(cfg.lsp.requestTimeoutMs);
|
package/src/core/server.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import fs from 'node:fs';
|
|
17
17
|
import { StdioRpcServer } from './stdioRpcServer.js';
|
|
18
18
|
import { createProjectRootGuard } from './projectRootGuard.js';
|
|
19
|
+
import { createToolCategoryPolicy, filterToolsByCategory } from './toolCategoryFilter.js';
|
|
19
20
|
|
|
20
21
|
// Deferred state - will be initialized after transport connection
|
|
21
22
|
let unityConnection = null;
|
|
@@ -126,8 +127,41 @@ export async function startServer(options = {}) {
|
|
|
126
127
|
...config,
|
|
127
128
|
http: { ...config.http, ...(options.http || {}) },
|
|
128
129
|
telemetry: { ...config.telemetry, ...(options.telemetry || {}) },
|
|
130
|
+
tools: {
|
|
131
|
+
includeCategories: config.tools?.includeCategories || [],
|
|
132
|
+
excludeCategories: config.tools?.excludeCategories || []
|
|
133
|
+
},
|
|
129
134
|
stdioEnabled: options.stdioEnabled !== undefined ? options.stdioEnabled : true
|
|
130
135
|
};
|
|
136
|
+
const toolCategoryPolicy = createToolCategoryPolicy(runtimeConfig.tools, logger);
|
|
137
|
+
let publicToolNames = null;
|
|
138
|
+
let toolFilterLogged = false;
|
|
139
|
+
|
|
140
|
+
const applyCategoryFilter = tools => {
|
|
141
|
+
if (!toolCategoryPolicy.isActive) {
|
|
142
|
+
return tools;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const filtered = filterToolsByCategory(tools, toolCategoryPolicy);
|
|
146
|
+
publicToolNames = filtered.publicToolNames;
|
|
147
|
+
|
|
148
|
+
if (!toolFilterLogged) {
|
|
149
|
+
const include =
|
|
150
|
+
toolCategoryPolicy.includeList.length > 0
|
|
151
|
+
? toolCategoryPolicy.includeList.join(', ')
|
|
152
|
+
: '(all)';
|
|
153
|
+
const exclude =
|
|
154
|
+
toolCategoryPolicy.excludeList.length > 0
|
|
155
|
+
? toolCategoryPolicy.excludeList.join(', ')
|
|
156
|
+
: '(none)';
|
|
157
|
+
logger.info(
|
|
158
|
+
`[MCP] Tool category filter enabled. include=${include}, exclude=${exclude}`
|
|
159
|
+
);
|
|
160
|
+
toolFilterLogged = true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return filtered.tools;
|
|
164
|
+
};
|
|
131
165
|
|
|
132
166
|
const projectInfoProvider =
|
|
133
167
|
deps.projectInfoProvider ||
|
|
@@ -331,14 +365,15 @@ export async function startServer(options = {}) {
|
|
|
331
365
|
server?.setRequestHandler('tools/list', async () => {
|
|
332
366
|
const manifestTools = readToolManifest();
|
|
333
367
|
if (manifestTools) {
|
|
334
|
-
|
|
368
|
+
const visibleTools = applyCategoryFilter(manifestTools);
|
|
369
|
+
logger.info(`[MCP] Returning ${visibleTools.length} tool definitions`);
|
|
335
370
|
requestPostInit();
|
|
336
|
-
return { tools:
|
|
371
|
+
return { tools: visibleTools };
|
|
337
372
|
}
|
|
338
373
|
|
|
339
374
|
await ensureInitialized(deps);
|
|
340
375
|
|
|
341
|
-
const
|
|
376
|
+
const allTools = Array.from(handlers.values())
|
|
342
377
|
.map((handler, index) => {
|
|
343
378
|
try {
|
|
344
379
|
const definition = handler.getDefinition();
|
|
@@ -356,6 +391,7 @@ export async function startServer(options = {}) {
|
|
|
356
391
|
})
|
|
357
392
|
.filter(tool => tool !== null);
|
|
358
393
|
|
|
394
|
+
const tools = applyCategoryFilter(allTools);
|
|
359
395
|
logger.info(`[MCP] Returning ${tools.length} tool definitions`);
|
|
360
396
|
requestPostInit();
|
|
361
397
|
return { tools };
|
|
@@ -373,6 +409,17 @@ export async function startServer(options = {}) {
|
|
|
373
409
|
{ args }
|
|
374
410
|
);
|
|
375
411
|
|
|
412
|
+
if (toolCategoryPolicy.isActive) {
|
|
413
|
+
if (!publicToolNames) {
|
|
414
|
+
const handlerTools = Array.from(handlers.values()).map(handler => ({ name: handler.name }));
|
|
415
|
+
publicToolNames = filterToolsByCategory(handlerTools, toolCategoryPolicy).publicToolNames;
|
|
416
|
+
}
|
|
417
|
+
if (!publicToolNames.has(name)) {
|
|
418
|
+
logger.error(`[MCP] Tool not found (filtered): ${name}`);
|
|
419
|
+
throw new Error(`Tool not found: ${name}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
376
423
|
const guardError = await projectRootGuard(args || {});
|
|
377
424
|
if (guardError) {
|
|
378
425
|
logger.error(`[MCP] projectRoot guard failed: ${guardError}`);
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool category filtering for tools/list and tools/call visibility control.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const CATEGORY_NAMES = Object.freeze([
|
|
6
|
+
'system',
|
|
7
|
+
'gameobject',
|
|
8
|
+
'scene',
|
|
9
|
+
'analysis',
|
|
10
|
+
'playmode',
|
|
11
|
+
'ui',
|
|
12
|
+
'input',
|
|
13
|
+
'asset',
|
|
14
|
+
'prefab',
|
|
15
|
+
'material',
|
|
16
|
+
'addressables',
|
|
17
|
+
'menu',
|
|
18
|
+
'console',
|
|
19
|
+
'screenshot',
|
|
20
|
+
'video',
|
|
21
|
+
'component',
|
|
22
|
+
'compilation',
|
|
23
|
+
'test',
|
|
24
|
+
'editor',
|
|
25
|
+
'settings',
|
|
26
|
+
'package',
|
|
27
|
+
'script',
|
|
28
|
+
'profiler',
|
|
29
|
+
'general'
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
function toCategoryKey(value) {
|
|
33
|
+
if (typeof value !== 'string') return '';
|
|
34
|
+
return value
|
|
35
|
+
.trim()
|
|
36
|
+
.toLowerCase()
|
|
37
|
+
.replace(/[\s_-]+/g, '');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const CATEGORY_KEY_TO_NAME = new Map(CATEGORY_NAMES.map(name => [toCategoryKey(name), name]));
|
|
41
|
+
|
|
42
|
+
// User-friendly aliases
|
|
43
|
+
CATEGORY_KEY_TO_NAME.set('addressable', 'addressables');
|
|
44
|
+
CATEGORY_KEY_TO_NAME.set('gameobj', 'gameobject');
|
|
45
|
+
CATEGORY_KEY_TO_NAME.set('play', 'playmode');
|
|
46
|
+
CATEGORY_KEY_TO_NAME.set('playmode', 'playmode');
|
|
47
|
+
CATEGORY_KEY_TO_NAME.set('uitoolkit', 'ui');
|
|
48
|
+
CATEGORY_KEY_TO_NAME.set('ugui', 'ui');
|
|
49
|
+
CATEGORY_KEY_TO_NAME.set('imgui', 'ui');
|
|
50
|
+
|
|
51
|
+
const SYSTEM_TOOLS = new Set([
|
|
52
|
+
'ping',
|
|
53
|
+
'refresh_assets',
|
|
54
|
+
'get_command_stats',
|
|
55
|
+
'get_server_info',
|
|
56
|
+
'search_tools'
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
const GAMEOBJECT_TOOLS = new Set([
|
|
60
|
+
'create_gameobject',
|
|
61
|
+
'find_gameobject',
|
|
62
|
+
'modify_gameobject',
|
|
63
|
+
'delete_gameobject',
|
|
64
|
+
'get_hierarchy'
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
const SCENE_TOOLS = new Set(['create_scene', 'load_scene', 'save_scene', 'list_scenes', 'get_scene_info']);
|
|
68
|
+
|
|
69
|
+
const ANALYSIS_TOOLS = new Set([
|
|
70
|
+
'get_gameobject_details',
|
|
71
|
+
'analyze_scene_contents',
|
|
72
|
+
'get_component_values',
|
|
73
|
+
'find_by_component',
|
|
74
|
+
'get_object_references',
|
|
75
|
+
'get_animator_state',
|
|
76
|
+
'get_animator_runtime_info',
|
|
77
|
+
'get_input_actions_state',
|
|
78
|
+
'analyze_input_actions_asset'
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
const PLAYMODE_TOOLS = new Set(['play_game', 'pause_game', 'stop_game', 'playmode_wait_for_state']);
|
|
82
|
+
|
|
83
|
+
const UI_TOOLS = new Set([
|
|
84
|
+
'find_ui_elements',
|
|
85
|
+
'click_ui_element',
|
|
86
|
+
'get_ui_element_state',
|
|
87
|
+
'set_ui_element_value',
|
|
88
|
+
'simulate_ui_input'
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
const INPUT_TOOLS = new Set([
|
|
92
|
+
'input_system_control',
|
|
93
|
+
'input_keyboard',
|
|
94
|
+
'input_mouse',
|
|
95
|
+
'input_gamepad',
|
|
96
|
+
'input_touch',
|
|
97
|
+
'create_action_map',
|
|
98
|
+
'remove_action_map',
|
|
99
|
+
'add_input_action',
|
|
100
|
+
'remove_input_action',
|
|
101
|
+
'add_input_binding',
|
|
102
|
+
'remove_input_binding',
|
|
103
|
+
'remove_all_bindings',
|
|
104
|
+
'create_composite_binding',
|
|
105
|
+
'manage_control_schemes'
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
const ASSET_TOOLS = new Set([
|
|
109
|
+
'manage_asset_database',
|
|
110
|
+
'manage_asset_import_settings',
|
|
111
|
+
'analyze_asset_dependencies'
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
const PREFAB_TOOLS = new Set([
|
|
115
|
+
'create_prefab',
|
|
116
|
+
'modify_prefab',
|
|
117
|
+
'instantiate_prefab',
|
|
118
|
+
'open_prefab',
|
|
119
|
+
'exit_prefab_mode',
|
|
120
|
+
'save_prefab'
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
const MATERIAL_TOOLS = new Set(['create_material', 'modify_material']);
|
|
124
|
+
|
|
125
|
+
const MENU_TOOLS = new Set(['execute_menu_item']);
|
|
126
|
+
const CONSOLE_TOOLS = new Set(['clear_console', 'read_console']);
|
|
127
|
+
const SCREENSHOT_TOOLS = new Set(['capture_screenshot', 'analyze_screenshot']);
|
|
128
|
+
const VIDEO_TOOLS = new Set([
|
|
129
|
+
'capture_video_start',
|
|
130
|
+
'capture_video_stop',
|
|
131
|
+
'capture_video_status',
|
|
132
|
+
'video_capture_for'
|
|
133
|
+
]);
|
|
134
|
+
const COMPONENT_TOOLS = new Set([
|
|
135
|
+
'add_component',
|
|
136
|
+
'remove_component',
|
|
137
|
+
'modify_component',
|
|
138
|
+
'list_components',
|
|
139
|
+
'get_component_types',
|
|
140
|
+
'set_component_field'
|
|
141
|
+
]);
|
|
142
|
+
const COMPILATION_TOOLS = new Set(['get_compilation_state']);
|
|
143
|
+
const TEST_TOOLS = new Set(['run_tests', 'get_test_status']);
|
|
144
|
+
const EDITOR_TOOLS = new Set([
|
|
145
|
+
'manage_tags',
|
|
146
|
+
'manage_layers',
|
|
147
|
+
'manage_selection',
|
|
148
|
+
'manage_windows',
|
|
149
|
+
'manage_tools',
|
|
150
|
+
'get_editor_state',
|
|
151
|
+
'quit_editor'
|
|
152
|
+
]);
|
|
153
|
+
const SETTINGS_TOOLS = new Set(['get_project_settings', 'update_project_settings']);
|
|
154
|
+
const PACKAGE_TOOLS = new Set(['package_manager', 'registry_config', 'list_packages']);
|
|
155
|
+
const SCRIPT_TOOLS = new Set([
|
|
156
|
+
'read',
|
|
157
|
+
'search',
|
|
158
|
+
'edit_structured',
|
|
159
|
+
'edit_snippet',
|
|
160
|
+
'get_symbols',
|
|
161
|
+
'find_symbol',
|
|
162
|
+
'find_refs',
|
|
163
|
+
'build_index',
|
|
164
|
+
'update_index',
|
|
165
|
+
'get_index_status',
|
|
166
|
+
'rename_symbol',
|
|
167
|
+
'create_class',
|
|
168
|
+
'remove_symbol'
|
|
169
|
+
]);
|
|
170
|
+
|
|
171
|
+
const TOOL_SETS = [
|
|
172
|
+
['system', SYSTEM_TOOLS],
|
|
173
|
+
['gameobject', GAMEOBJECT_TOOLS],
|
|
174
|
+
['scene', SCENE_TOOLS],
|
|
175
|
+
['analysis', ANALYSIS_TOOLS],
|
|
176
|
+
['playmode', PLAYMODE_TOOLS],
|
|
177
|
+
['ui', UI_TOOLS],
|
|
178
|
+
['input', INPUT_TOOLS],
|
|
179
|
+
['asset', ASSET_TOOLS],
|
|
180
|
+
['prefab', PREFAB_TOOLS],
|
|
181
|
+
['material', MATERIAL_TOOLS],
|
|
182
|
+
['menu', MENU_TOOLS],
|
|
183
|
+
['console', CONSOLE_TOOLS],
|
|
184
|
+
['screenshot', SCREENSHOT_TOOLS],
|
|
185
|
+
['video', VIDEO_TOOLS],
|
|
186
|
+
['component', COMPONENT_TOOLS],
|
|
187
|
+
['compilation', COMPILATION_TOOLS],
|
|
188
|
+
['test', TEST_TOOLS],
|
|
189
|
+
['editor', EDITOR_TOOLS],
|
|
190
|
+
['settings', SETTINGS_TOOLS],
|
|
191
|
+
['package', PACKAGE_TOOLS],
|
|
192
|
+
['script', SCRIPT_TOOLS]
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
function logWarning(logger, message) {
|
|
196
|
+
if (!logger) return;
|
|
197
|
+
if (typeof logger.warning === 'function') {
|
|
198
|
+
logger.warning(message);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (typeof logger.warn === 'function') {
|
|
202
|
+
logger.warn(message);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (typeof logger.info === 'function') {
|
|
206
|
+
logger.info(message);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function normalizeCategoryList(rawValues = []) {
|
|
211
|
+
const values = Array.isArray(rawValues) ? rawValues : [];
|
|
212
|
+
const normalized = [];
|
|
213
|
+
const unknown = [];
|
|
214
|
+
const seen = new Set();
|
|
215
|
+
|
|
216
|
+
for (const value of values) {
|
|
217
|
+
const key = toCategoryKey(value);
|
|
218
|
+
if (!key) continue;
|
|
219
|
+
const category = CATEGORY_KEY_TO_NAME.get(key);
|
|
220
|
+
if (!category) {
|
|
221
|
+
unknown.push(String(value).trim());
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (seen.has(category)) continue;
|
|
225
|
+
seen.add(category);
|
|
226
|
+
normalized.push(category);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return { normalized, unknown };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function getToolCategory(toolName) {
|
|
233
|
+
const name = typeof toolName === 'string' ? toolName : '';
|
|
234
|
+
if (!name) return 'general';
|
|
235
|
+
if (name.startsWith('addressables_')) return 'addressables';
|
|
236
|
+
if (name.startsWith('profiler_')) return 'profiler';
|
|
237
|
+
|
|
238
|
+
for (const [category, set] of TOOL_SETS) {
|
|
239
|
+
if (set.has(name)) return category;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return 'general';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function createToolCategoryPolicy(config = {}, logger) {
|
|
246
|
+
const includeResult = normalizeCategoryList(config.includeCategories);
|
|
247
|
+
const excludeResult = normalizeCategoryList(config.excludeCategories);
|
|
248
|
+
|
|
249
|
+
if (includeResult.unknown.length > 0) {
|
|
250
|
+
logWarning(
|
|
251
|
+
logger,
|
|
252
|
+
`[tool-filter] Ignoring unknown include categories: ${includeResult.unknown.join(', ')}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
if (excludeResult.unknown.length > 0) {
|
|
256
|
+
logWarning(
|
|
257
|
+
logger,
|
|
258
|
+
`[tool-filter] Ignoring unknown exclude categories: ${excludeResult.unknown.join(', ')}`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
includeCategories: new Set(includeResult.normalized),
|
|
264
|
+
excludeCategories: new Set(excludeResult.normalized),
|
|
265
|
+
includeList: includeResult.normalized,
|
|
266
|
+
excludeList: excludeResult.normalized,
|
|
267
|
+
isActive: includeResult.normalized.length > 0 || excludeResult.normalized.length > 0
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function filterToolsByCategory(tools, policy) {
|
|
272
|
+
const list = Array.isArray(tools) ? tools : [];
|
|
273
|
+
const publicTools = [];
|
|
274
|
+
const hiddenToolNames = new Set();
|
|
275
|
+
const publicToolNames = new Set();
|
|
276
|
+
const includeCategories = policy?.includeCategories ?? new Set();
|
|
277
|
+
const excludeCategories = policy?.excludeCategories ?? new Set();
|
|
278
|
+
const includeEnabled = includeCategories.size > 0;
|
|
279
|
+
const excludeEnabled = excludeCategories.size > 0;
|
|
280
|
+
|
|
281
|
+
for (const tool of list) {
|
|
282
|
+
const name = tool?.name;
|
|
283
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const category = getToolCategory(name);
|
|
288
|
+
const includeMatch = !includeEnabled || includeCategories.has(category);
|
|
289
|
+
const excludeMatch = excludeEnabled && excludeCategories.has(category);
|
|
290
|
+
const visible = includeMatch && !excludeMatch;
|
|
291
|
+
|
|
292
|
+
if (visible) {
|
|
293
|
+
publicTools.push(tool);
|
|
294
|
+
publicToolNames.add(name);
|
|
295
|
+
} else {
|
|
296
|
+
hiddenToolNames.add(name);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
tools: publicTools,
|
|
302
|
+
publicToolNames,
|
|
303
|
+
hiddenToolNames
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function getKnownToolCategories() {
|
|
308
|
+
return [...CATEGORY_NAMES];
|
|
309
|
+
}
|