@akiojin/unity-mcp-server 2.14.14
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/LICENSE +21 -0
- package/README.md +206 -0
- package/bin/unity-mcp-server +2 -0
- package/package.json +73 -0
- package/src/core/codeIndex.js +163 -0
- package/src/core/codeIndexDb.js +96 -0
- package/src/core/config.js +165 -0
- package/src/core/indexWatcher.js +52 -0
- package/src/core/projectInfo.js +111 -0
- package/src/core/server.js +294 -0
- package/src/core/unityConnection.js +426 -0
- package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +35 -0
- package/src/handlers/analysis/FindByComponentToolHandler.js +20 -0
- package/src/handlers/analysis/GetAnimatorStateToolHandler.js +37 -0
- package/src/handlers/analysis/GetComponentValuesToolHandler.js +20 -0
- package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +35 -0
- package/src/handlers/analysis/GetInputActionsStateToolHandler.js +37 -0
- package/src/handlers/analysis/GetObjectReferencesToolHandler.js +20 -0
- package/src/handlers/asset/AssetDatabaseToolHandler.js +221 -0
- package/src/handlers/asset/AssetDependencyToolHandler.js +201 -0
- package/src/handlers/asset/AssetImportSettingsToolHandler.js +170 -0
- package/src/handlers/asset/CreateMaterialToolHandler.js +96 -0
- package/src/handlers/asset/CreatePrefabToolHandler.js +78 -0
- package/src/handlers/asset/ExitPrefabModeToolHandler.js +83 -0
- package/src/handlers/asset/InstantiatePrefabToolHandler.js +133 -0
- package/src/handlers/asset/ModifyMaterialToolHandler.js +76 -0
- package/src/handlers/asset/ModifyPrefabToolHandler.js +72 -0
- package/src/handlers/asset/OpenPrefabToolHandler.js +121 -0
- package/src/handlers/asset/SavePrefabToolHandler.js +106 -0
- package/src/handlers/base/BaseToolHandler.js +133 -0
- package/src/handlers/compilation/GetCompilationStateToolHandler.js +90 -0
- package/src/handlers/component/AddComponentToolHandler.js +126 -0
- package/src/handlers/component/GetComponentTypesToolHandler.js +100 -0
- package/src/handlers/component/ListComponentsToolHandler.js +85 -0
- package/src/handlers/component/ModifyComponentToolHandler.js +143 -0
- package/src/handlers/component/RemoveComponentToolHandler.js +108 -0
- package/src/handlers/console/ClearConsoleToolHandler.js +160 -0
- package/src/handlers/console/ReadConsoleToolHandler.js +276 -0
- package/src/handlers/editor/LayerManagementToolHandler.js +160 -0
- package/src/handlers/editor/SelectionToolHandler.js +141 -0
- package/src/handlers/editor/TagManagementToolHandler.js +129 -0
- package/src/handlers/editor/ToolManagementToolHandler.js +135 -0
- package/src/handlers/editor/WindowManagementToolHandler.js +125 -0
- package/src/handlers/gameobject/CreateGameObjectToolHandler.js +131 -0
- package/src/handlers/gameobject/DeleteGameObjectToolHandler.js +101 -0
- package/src/handlers/gameobject/FindGameObjectToolHandler.js +119 -0
- package/src/handlers/gameobject/GetHierarchyToolHandler.js +132 -0
- package/src/handlers/gameobject/ModifyGameObjectToolHandler.js +128 -0
- package/src/handlers/index.js +389 -0
- package/src/handlers/input/AddInputActionToolHandler.js +20 -0
- package/src/handlers/input/AddInputBindingToolHandler.js +20 -0
- package/src/handlers/input/CreateActionMapToolHandler.js +20 -0
- package/src/handlers/input/CreateCompositeBindingToolHandler.js +20 -0
- package/src/handlers/input/GamepadSimulationHandler.js +116 -0
- package/src/handlers/input/InputSystemHandler.js +80 -0
- package/src/handlers/input/KeyboardSimulationHandler.js +79 -0
- package/src/handlers/input/ManageControlSchemesToolHandler.js +20 -0
- package/src/handlers/input/MouseSimulationHandler.js +107 -0
- package/src/handlers/input/RemoveActionMapToolHandler.js +20 -0
- package/src/handlers/input/RemoveAllBindingsToolHandler.js +20 -0
- package/src/handlers/input/RemoveInputActionToolHandler.js +20 -0
- package/src/handlers/input/RemoveInputBindingToolHandler.js +20 -0
- package/src/handlers/input/TouchSimulationHandler.js +142 -0
- package/src/handlers/menu/ExecuteMenuItemToolHandler.js +304 -0
- package/src/handlers/package/PackageManagerToolHandler.js +248 -0
- package/src/handlers/package/RegistryConfigToolHandler.js +198 -0
- package/src/handlers/playmode/GetEditorStateToolHandler.js +81 -0
- package/src/handlers/playmode/PauseToolHandler.js +44 -0
- package/src/handlers/playmode/PlayToolHandler.js +91 -0
- package/src/handlers/playmode/StopToolHandler.js +77 -0
- package/src/handlers/playmode/WaitForEditorStateToolHandler.js +45 -0
- package/src/handlers/scene/CreateSceneToolHandler.js +91 -0
- package/src/handlers/scene/GetSceneInfoToolHandler.js +20 -0
- package/src/handlers/scene/ListScenesToolHandler.js +58 -0
- package/src/handlers/scene/LoadSceneToolHandler.js +92 -0
- package/src/handlers/scene/SaveSceneToolHandler.js +76 -0
- package/src/handlers/screenshot/AnalyzeScreenshotToolHandler.js +238 -0
- package/src/handlers/screenshot/CaptureScreenshotToolHandler.js +692 -0
- package/src/handlers/script/BuildCodeIndexToolHandler.js +163 -0
- package/src/handlers/script/ScriptCreateClassFileToolHandler.js +60 -0
- package/src/handlers/script/ScriptEditStructuredToolHandler.js +173 -0
- package/src/handlers/script/ScriptIndexStatusToolHandler.js +61 -0
- package/src/handlers/script/ScriptPackagesListToolHandler.js +103 -0
- package/src/handlers/script/ScriptReadToolHandler.js +106 -0
- package/src/handlers/script/ScriptRefactorRenameToolHandler.js +83 -0
- package/src/handlers/script/ScriptRefsFindToolHandler.js +144 -0
- package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +79 -0
- package/src/handlers/script/ScriptSearchToolHandler.js +320 -0
- package/src/handlers/script/ScriptSymbolFindToolHandler.js +117 -0
- package/src/handlers/script/ScriptSymbolsGetToolHandler.js +96 -0
- package/src/handlers/settings/GetProjectSettingsToolHandler.js +161 -0
- package/src/handlers/settings/UpdateProjectSettingsToolHandler.js +272 -0
- package/src/handlers/system/GetCommandStatsToolHandler.js +25 -0
- package/src/handlers/system/PingToolHandler.js +53 -0
- package/src/handlers/system/RefreshAssetsToolHandler.js +45 -0
- package/src/handlers/ui/ClickUIElementToolHandler.js +110 -0
- package/src/handlers/ui/FindUIElementsToolHandler.js +63 -0
- package/src/handlers/ui/GetUIElementStateToolHandler.js +50 -0
- package/src/handlers/ui/SetUIElementValueToolHandler.js +49 -0
- package/src/handlers/ui/SimulateUIInputToolHandler.js +156 -0
- package/src/handlers/video/CaptureVideoForToolHandler.js +96 -0
- package/src/handlers/video/CaptureVideoStartToolHandler.js +38 -0
- package/src/handlers/video/CaptureVideoStatusToolHandler.js +30 -0
- package/src/handlers/video/CaptureVideoStopToolHandler.js +32 -0
- package/src/lsp/CSharpLspUtils.js +134 -0
- package/src/lsp/LspProcessManager.js +60 -0
- package/src/lsp/LspRpcClient.js +133 -0
- package/src/tools/analysis/analyzeSceneContents.js +100 -0
- package/src/tools/analysis/findByComponent.js +87 -0
- package/src/tools/analysis/getAnimatorState.js +326 -0
- package/src/tools/analysis/getComponentValues.js +182 -0
- package/src/tools/analysis/getGameObjectDetails.js +159 -0
- package/src/tools/analysis/getInputActionsState.js +329 -0
- package/src/tools/analysis/getObjectReferences.js +86 -0
- package/src/tools/input/inputActionsEditor.js +556 -0
- package/src/tools/scene/createScene.js +112 -0
- package/src/tools/scene/getSceneInfo.js +95 -0
- package/src/tools/scene/listScenes.js +82 -0
- package/src/tools/scene/loadScene.js +122 -0
- package/src/tools/scene/saveScene.js +91 -0
- package/src/tools/system/ping.js +72 -0
- package/src/tools/video/recordFor.js +31 -0
- package/src/tools/video/recordPlayMode.js +61 -0
- package/src/utils/csharpParse.js +88 -0
- package/src/utils/validators.js +90 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for removing components from GameObjects in Unity
|
|
5
|
+
*/
|
|
6
|
+
export class RemoveComponentToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'remove_component',
|
|
10
|
+
'Remove a component from a GameObject (scene or prefab mode) by type/index.',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
gameObjectPath: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'Path to the GameObject (e.g., "/Player" or "/Canvas/Button")'
|
|
17
|
+
},
|
|
18
|
+
componentType: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Type of component to remove (e.g., "Rigidbody", "BoxCollider")'
|
|
21
|
+
},
|
|
22
|
+
componentIndex: {
|
|
23
|
+
type: 'number',
|
|
24
|
+
description: 'Index of component if multiple of same type exist (default: 0)'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
required: ['gameObjectPath', 'componentType']
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
this.unityConnection = unityConnection;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validates the input parameters
|
|
36
|
+
* @param {Object} params - The input parameters
|
|
37
|
+
* @throws {Error} If validation fails
|
|
38
|
+
*/
|
|
39
|
+
validate(params) {
|
|
40
|
+
super.validate(params); // Check required fields
|
|
41
|
+
|
|
42
|
+
const { componentIndex } = params;
|
|
43
|
+
|
|
44
|
+
// Validate component index if provided
|
|
45
|
+
if (componentIndex !== undefined && componentIndex < 0) {
|
|
46
|
+
throw new Error('componentIndex must be non-negative');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Executes the remove component operation
|
|
52
|
+
* @param {Object} params - The validated input parameters
|
|
53
|
+
* @returns {Promise<Object>} The result of removing the component
|
|
54
|
+
*/
|
|
55
|
+
async execute(params) {
|
|
56
|
+
// Ensure connection to Unity
|
|
57
|
+
if (!this.unityConnection.isConnected()) {
|
|
58
|
+
await this.unityConnection.connect();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Send command to Unity
|
|
62
|
+
const response = await this.unityConnection.sendCommand('remove_component', params);
|
|
63
|
+
|
|
64
|
+
// Handle Unity response
|
|
65
|
+
if (response.error) {
|
|
66
|
+
throw new Error(response.error);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Return result
|
|
70
|
+
return {
|
|
71
|
+
removed: response.removed,
|
|
72
|
+
componentType: response.componentType,
|
|
73
|
+
message: response.message || 'Component removal completed',
|
|
74
|
+
...(response.componentIndex !== undefined && { componentIndex: response.componentIndex })
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Gets example usage for this tool
|
|
80
|
+
* @returns {Object} Example usage scenarios
|
|
81
|
+
*/
|
|
82
|
+
getExamples() {
|
|
83
|
+
return {
|
|
84
|
+
removeRigidbody: {
|
|
85
|
+
description: 'Remove Rigidbody component',
|
|
86
|
+
params: {
|
|
87
|
+
gameObjectPath: '/Player',
|
|
88
|
+
componentType: 'Rigidbody'
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
removeSpecificCollider: {
|
|
92
|
+
description: 'Remove specific collider when multiple exist',
|
|
93
|
+
params: {
|
|
94
|
+
gameObjectPath: '/ComplexObject',
|
|
95
|
+
componentType: 'BoxCollider',
|
|
96
|
+
componentIndex: 1
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
removeLight: {
|
|
100
|
+
description: 'Remove Light component',
|
|
101
|
+
params: {
|
|
102
|
+
gameObjectPath: '/Lighting/OldLight',
|
|
103
|
+
componentType: 'Light'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for clearing Unity Editor console logs
|
|
5
|
+
*/
|
|
6
|
+
export class ClearConsoleToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'clear_console',
|
|
10
|
+
'Clear Console logs (optionally set auto-clear and preserve levels).',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
clearOnPlay: {
|
|
15
|
+
type: 'boolean',
|
|
16
|
+
description: 'Clear console when entering play mode. Unity default: false. Set to true for cleaner testing sessions'
|
|
17
|
+
},
|
|
18
|
+
clearOnRecompile: {
|
|
19
|
+
type: 'boolean',
|
|
20
|
+
description: 'Clear console on script recompilation. Unity default: false. Set to true to focus on current compilation errors'
|
|
21
|
+
},
|
|
22
|
+
clearOnBuild: {
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
description: 'Clear console when building. Unity default: false. Set to true for clean build logs'
|
|
25
|
+
},
|
|
26
|
+
preserveWarnings: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
description: 'Preserve warning messages when clearing (default: false)'
|
|
29
|
+
},
|
|
30
|
+
preserveErrors: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
description: 'Preserve error messages when clearing (default: false)'
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
required: []
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
this.unityConnection = unityConnection;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validates the input parameters
|
|
44
|
+
* @param {Object} params - The input parameters
|
|
45
|
+
* @throws {Error} If validation fails
|
|
46
|
+
*/
|
|
47
|
+
validate(params) {
|
|
48
|
+
const {
|
|
49
|
+
clearOnPlay,
|
|
50
|
+
clearOnRecompile,
|
|
51
|
+
clearOnBuild,
|
|
52
|
+
preserveWarnings,
|
|
53
|
+
preserveErrors
|
|
54
|
+
} = params;
|
|
55
|
+
|
|
56
|
+
// Validate boolean parameters
|
|
57
|
+
if (clearOnPlay !== undefined && typeof clearOnPlay !== 'boolean') {
|
|
58
|
+
throw new Error('clearOnPlay must be a boolean');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (clearOnRecompile !== undefined && typeof clearOnRecompile !== 'boolean') {
|
|
62
|
+
throw new Error('clearOnRecompile must be a boolean');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (clearOnBuild !== undefined && typeof clearOnBuild !== 'boolean') {
|
|
66
|
+
throw new Error('clearOnBuild must be a boolean');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (preserveWarnings !== undefined && typeof preserveWarnings !== 'boolean') {
|
|
70
|
+
throw new Error('preserveWarnings must be a boolean');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (preserveErrors !== undefined && typeof preserveErrors !== 'boolean') {
|
|
74
|
+
throw new Error('preserveErrors must be a boolean');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Validate logical consistency
|
|
78
|
+
const isClearing = clearOnPlay !== false || clearOnRecompile !== false || clearOnBuild !== false;
|
|
79
|
+
const isPreserving = preserveWarnings === true || preserveErrors === true;
|
|
80
|
+
|
|
81
|
+
if (isPreserving && !isClearing) {
|
|
82
|
+
throw new Error('Cannot preserve specific log types when not clearing console');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Executes the console clear operation
|
|
88
|
+
* @param {Object} params - The input parameters
|
|
89
|
+
* @returns {Promise<Object>} The result of the clear operation
|
|
90
|
+
*/
|
|
91
|
+
async execute(params) {
|
|
92
|
+
const {
|
|
93
|
+
clearOnPlay,
|
|
94
|
+
clearOnRecompile,
|
|
95
|
+
clearOnBuild,
|
|
96
|
+
preserveWarnings,
|
|
97
|
+
preserveErrors
|
|
98
|
+
} = params;
|
|
99
|
+
|
|
100
|
+
// Ensure connection to Unity
|
|
101
|
+
if (!this.unityConnection.isConnected()) {
|
|
102
|
+
await this.unityConnection.connect();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Prepare command parameters
|
|
106
|
+
const commandParams = {
|
|
107
|
+
clearOnPlay,
|
|
108
|
+
clearOnRecompile,
|
|
109
|
+
clearOnBuild,
|
|
110
|
+
preserveWarnings,
|
|
111
|
+
preserveErrors
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Send command to Unity
|
|
115
|
+
const response = await this.unityConnection.sendCommand('clear_console', commandParams);
|
|
116
|
+
|
|
117
|
+
// Handle Unity response
|
|
118
|
+
if (response.success === false) {
|
|
119
|
+
throw new Error(response.error || 'Failed to clear console');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Build result object
|
|
123
|
+
const result = {
|
|
124
|
+
message: response.message || 'Console cleared successfully',
|
|
125
|
+
timestamp: response.timestamp || new Date().toISOString()
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Include optional statistics if available
|
|
129
|
+
if (response.clearedCount !== undefined) {
|
|
130
|
+
result.clearedCount = response.clearedCount;
|
|
131
|
+
}
|
|
132
|
+
if (response.remainingCount !== undefined) {
|
|
133
|
+
result.remainingCount = response.remainingCount;
|
|
134
|
+
}
|
|
135
|
+
if (response.preservedWarnings !== undefined) {
|
|
136
|
+
result.preservedWarnings = response.preservedWarnings;
|
|
137
|
+
}
|
|
138
|
+
if (response.preservedErrors !== undefined) {
|
|
139
|
+
result.preservedErrors = response.preservedErrors;
|
|
140
|
+
}
|
|
141
|
+
if (response.settingsUpdated !== undefined) {
|
|
142
|
+
result.settingsUpdated = response.settingsUpdated;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Include settings if they were updated
|
|
146
|
+
if (response.settingsUpdated) {
|
|
147
|
+
if (response.clearOnPlay !== undefined) {
|
|
148
|
+
result.clearOnPlay = response.clearOnPlay;
|
|
149
|
+
}
|
|
150
|
+
if (response.clearOnRecompile !== undefined) {
|
|
151
|
+
result.clearOnRecompile = response.clearOnRecompile;
|
|
152
|
+
}
|
|
153
|
+
if (response.clearOnBuild !== undefined) {
|
|
154
|
+
result.clearOnBuild = response.clearOnBuild;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for reading Unity Editor console logs with advanced filtering
|
|
5
|
+
*/
|
|
6
|
+
export class ReadConsoleToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'read_console',
|
|
10
|
+
'Read Console logs with filters (type/text/time), formatting, sort, and grouping.',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
count: {
|
|
15
|
+
type: 'number',
|
|
16
|
+
description: 'Number of logs to retrieve (1-1000, default: 100)',
|
|
17
|
+
minimum: 1,
|
|
18
|
+
maximum: 1000
|
|
19
|
+
},
|
|
20
|
+
logTypes: {
|
|
21
|
+
type: 'array',
|
|
22
|
+
description: 'Filter by log types (default: ["All"])',
|
|
23
|
+
items: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
enum: ['Info', 'Warning', 'Error', 'All']
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
filterText: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Filter logs containing this text (case-insensitive)'
|
|
31
|
+
},
|
|
32
|
+
includeStackTrace: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
description: 'Include stack traces in results. Unity default: false (set to true for debugging)'
|
|
35
|
+
},
|
|
36
|
+
format: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Output format for logs. Unity default: compact. RECOMMENDED: compact for general use, detailed for debugging',
|
|
39
|
+
enum: ['detailed', 'compact', 'json', 'plain']
|
|
40
|
+
},
|
|
41
|
+
sinceTimestamp: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Only return logs after this timestamp (ISO 8601)'
|
|
44
|
+
},
|
|
45
|
+
untilTimestamp: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Only return logs before this timestamp (ISO 8601)'
|
|
48
|
+
},
|
|
49
|
+
sortOrder: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Sort order for logs (default: newest)',
|
|
52
|
+
enum: ['newest', 'oldest']
|
|
53
|
+
},
|
|
54
|
+
groupBy: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Group logs by criteria (default: none)',
|
|
57
|
+
enum: ['none', 'type', 'file', 'time']
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
required: []
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
this.unityConnection = unityConnection;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Validates the input parameters
|
|
69
|
+
* @param {Object} params - The input parameters
|
|
70
|
+
* @throws {Error} If validation fails
|
|
71
|
+
*/
|
|
72
|
+
validate(params) {
|
|
73
|
+
const {
|
|
74
|
+
count,
|
|
75
|
+
logTypes,
|
|
76
|
+
filterText,
|
|
77
|
+
includeStackTrace,
|
|
78
|
+
format,
|
|
79
|
+
sinceTimestamp,
|
|
80
|
+
untilTimestamp,
|
|
81
|
+
sortOrder,
|
|
82
|
+
groupBy
|
|
83
|
+
} = params;
|
|
84
|
+
|
|
85
|
+
// Validate count
|
|
86
|
+
if (count !== undefined) {
|
|
87
|
+
if (typeof count !== 'number' || count < 1 || count > 1000) {
|
|
88
|
+
throw new Error('count must be between 1 and 1000');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Validate log types
|
|
93
|
+
if (logTypes !== undefined) {
|
|
94
|
+
if (!Array.isArray(logTypes)) {
|
|
95
|
+
throw new Error('logTypes must be an array');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const validTypes = ['Info', 'Warning', 'Error', 'All'];
|
|
99
|
+
for (const type of logTypes) {
|
|
100
|
+
if (!validTypes.includes(type)) {
|
|
101
|
+
// Invalid types are treated as 'All' later, so just log a warning
|
|
102
|
+
console.warn(`Invalid log type: ${type}. Will be treated as 'All'.`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Validate timestamps
|
|
108
|
+
if (sinceTimestamp !== undefined) {
|
|
109
|
+
if (!this.isValidISO8601(sinceTimestamp)) {
|
|
110
|
+
throw new Error('sinceTimestamp must be a valid ISO 8601 timestamp');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (untilTimestamp !== undefined) {
|
|
115
|
+
if (!this.isValidISO8601(untilTimestamp)) {
|
|
116
|
+
throw new Error('untilTimestamp must be a valid ISO 8601 timestamp');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Validate timestamp order
|
|
121
|
+
if (sinceTimestamp && untilTimestamp) {
|
|
122
|
+
const since = new Date(sinceTimestamp);
|
|
123
|
+
const until = new Date(untilTimestamp);
|
|
124
|
+
if (until <= since) {
|
|
125
|
+
throw new Error('untilTimestamp must be after sinceTimestamp');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Validate format
|
|
130
|
+
if (format !== undefined) {
|
|
131
|
+
const validFormats = ['detailed', 'compact', 'json', 'plain'];
|
|
132
|
+
if (!validFormats.includes(format)) {
|
|
133
|
+
throw new Error(`format must be one of: ${validFormats.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Validate sort order
|
|
138
|
+
if (sortOrder !== undefined) {
|
|
139
|
+
const validOrders = ['newest', 'oldest'];
|
|
140
|
+
if (!validOrders.includes(sortOrder)) {
|
|
141
|
+
throw new Error(`sortOrder must be one of: ${validOrders.join(', ')}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Validate groupBy
|
|
146
|
+
if (groupBy !== undefined) {
|
|
147
|
+
const validGroups = ['none', 'type', 'file', 'time'];
|
|
148
|
+
if (!validGroups.includes(groupBy)) {
|
|
149
|
+
throw new Error(`groupBy must be one of: ${validGroups.join(', ')}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Checks if a string is a valid ISO 8601 timestamp
|
|
156
|
+
* @param {string} timestamp - The timestamp to validate
|
|
157
|
+
* @returns {boolean} True if valid
|
|
158
|
+
*/
|
|
159
|
+
isValidISO8601(timestamp) {
|
|
160
|
+
const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
|
|
161
|
+
if (!regex.test(timestamp)) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const date = new Date(timestamp);
|
|
166
|
+
return !isNaN(date.getTime());
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Executes the enhanced log reading operation
|
|
171
|
+
* @param {Object} params - The input parameters
|
|
172
|
+
* @returns {Promise<Object>} The result of the log reading
|
|
173
|
+
*/
|
|
174
|
+
async execute(params) {
|
|
175
|
+
const {
|
|
176
|
+
count,
|
|
177
|
+
logTypes,
|
|
178
|
+
filterText,
|
|
179
|
+
includeStackTrace,
|
|
180
|
+
format,
|
|
181
|
+
sinceTimestamp,
|
|
182
|
+
untilTimestamp,
|
|
183
|
+
sortOrder,
|
|
184
|
+
groupBy
|
|
185
|
+
} = params;
|
|
186
|
+
|
|
187
|
+
// Convert simplified log types to Unity log types
|
|
188
|
+
let expandedLogTypes = [];
|
|
189
|
+
|
|
190
|
+
if (!logTypes || logTypes.length === 0 || logTypes.includes('All')) {
|
|
191
|
+
// Default to all types
|
|
192
|
+
expandedLogTypes = ['Log', 'Warning', 'Error', 'Exception', 'Assert'];
|
|
193
|
+
} else {
|
|
194
|
+
// Expand each simplified type to Unity types
|
|
195
|
+
logTypes.forEach(type => {
|
|
196
|
+
switch(type) {
|
|
197
|
+
case 'Info':
|
|
198
|
+
expandedLogTypes.push('Log');
|
|
199
|
+
break;
|
|
200
|
+
case 'Warning':
|
|
201
|
+
expandedLogTypes.push('Warning');
|
|
202
|
+
break;
|
|
203
|
+
case 'Error':
|
|
204
|
+
// Error includes all error-related types
|
|
205
|
+
expandedLogTypes.push('Error', 'Exception', 'Assert');
|
|
206
|
+
break;
|
|
207
|
+
case 'All': expandedLogTypes.push('Log', 'Warning', 'Error', 'Exception', 'Assert');
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Remove duplicates
|
|
213
|
+
expandedLogTypes = [...new Set(expandedLogTypes)];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Ensure connection to Unity
|
|
217
|
+
if (!this.unityConnection.isConnected()) {
|
|
218
|
+
await this.unityConnection.connect();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Prepare command parameters
|
|
222
|
+
const commandParams = {
|
|
223
|
+
count,
|
|
224
|
+
logTypes: expandedLogTypes,
|
|
225
|
+
includeStackTrace,
|
|
226
|
+
format,
|
|
227
|
+
sortOrder,
|
|
228
|
+
groupBy
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Add optional parameters
|
|
232
|
+
if (filterText !== undefined) {
|
|
233
|
+
commandParams.filterText = filterText;
|
|
234
|
+
}
|
|
235
|
+
if (sinceTimestamp !== undefined) {
|
|
236
|
+
commandParams.sinceTimestamp = sinceTimestamp;
|
|
237
|
+
}
|
|
238
|
+
if (untilTimestamp !== undefined) {
|
|
239
|
+
commandParams.untilTimestamp = untilTimestamp;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Send command to Unity
|
|
243
|
+
const response = await this.unityConnection.sendCommand('read_console', commandParams);
|
|
244
|
+
|
|
245
|
+
// Handle Unity response
|
|
246
|
+
if (response.success === false) {
|
|
247
|
+
throw new Error(response.error || 'Failed to read logs');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Build result object
|
|
251
|
+
const result = {
|
|
252
|
+
logs: response.logs || [],
|
|
253
|
+
count: response.count || 0,
|
|
254
|
+
totalCaptured: response.totalCaptured || 0
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// Include optional fields if available
|
|
258
|
+
if (response.filteredCount !== undefined) {
|
|
259
|
+
result.filteredCount = response.filteredCount;
|
|
260
|
+
}
|
|
261
|
+
if (response.statistics !== undefined) {
|
|
262
|
+
result.statistics = response.statistics;
|
|
263
|
+
}
|
|
264
|
+
if (response.groupedLogs !== undefined) {
|
|
265
|
+
result.groupedLogs = response.groupedLogs;
|
|
266
|
+
}
|
|
267
|
+
if (response.format !== undefined) {
|
|
268
|
+
result.format = response.format;
|
|
269
|
+
}
|
|
270
|
+
if (response.groupBy !== undefined) {
|
|
271
|
+
result.groupBy = response.groupBy;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles Unity layer management operations
|
|
5
|
+
*/
|
|
6
|
+
export class LayerManagementToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'manage_layers',
|
|
10
|
+
'Manage project layers: add/remove/list and convert (by name/index).',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
action: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
enum: ['add', 'remove', 'get', 'get_by_name', 'get_by_index'],
|
|
17
|
+
description: 'Operation: add, remove, get, get_by_name, or get_by_index.'
|
|
18
|
+
},
|
|
19
|
+
layerName: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Layer name (required for add/remove/get_by_name). Letters/numbers/space/_ only.'
|
|
22
|
+
},
|
|
23
|
+
layerIndex: {
|
|
24
|
+
type: 'number',
|
|
25
|
+
minimum: 0,
|
|
26
|
+
maximum: 31,
|
|
27
|
+
description: 'Layer index (0-31). Required for get_by_index.'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ['action']
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
this.unityConnection = unityConnection;
|
|
34
|
+
this.RESERVED_LAYERS = ['Default', 'TransparentFX', 'Ignore Raycast', 'Water', 'UI'];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Validate the parameters for the layer management operation
|
|
39
|
+
*/
|
|
40
|
+
validate(params) {
|
|
41
|
+
const { action, layerName, layerIndex } = params;
|
|
42
|
+
|
|
43
|
+
// Check action is provided
|
|
44
|
+
if (!action) {
|
|
45
|
+
throw new Error('action is required');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Validate action is one of the allowed values
|
|
49
|
+
const allowedActions = ['add', 'remove', 'get', 'get_by_name', 'get_by_index'];
|
|
50
|
+
if (!allowedActions.includes(action)) {
|
|
51
|
+
throw new Error(`action must be one of: ${allowedActions.join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Validate based on action
|
|
55
|
+
switch (action) {
|
|
56
|
+
case 'add':
|
|
57
|
+
case 'remove':
|
|
58
|
+
if (layerName === undefined || layerName === null) {
|
|
59
|
+
throw new Error(`layerName is required for ${action} action`);
|
|
60
|
+
}
|
|
61
|
+
if (layerName === '') {
|
|
62
|
+
throw new Error('layerName cannot be empty');
|
|
63
|
+
}
|
|
64
|
+
if (!this.isValidLayerName(layerName)) {
|
|
65
|
+
throw new Error('layerName contains invalid characters');
|
|
66
|
+
}
|
|
67
|
+
if (action === 'add' && this.RESERVED_LAYERS.includes(layerName)) {
|
|
68
|
+
throw new Error('layerName is reserved');
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case 'get_by_name':
|
|
73
|
+
if (layerName === undefined || layerName === null) {
|
|
74
|
+
throw new Error('layerName is required for get_by_name action');
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'get_by_index':
|
|
79
|
+
if (layerIndex === undefined || layerIndex === null) {
|
|
80
|
+
throw new Error('layerIndex is required for get_by_index action');
|
|
81
|
+
}
|
|
82
|
+
if (layerIndex < 0 || layerIndex > 31) {
|
|
83
|
+
throw new Error('layerIndex must be between 0 and 31');
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Call parent validation last
|
|
89
|
+
super.validate(params);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if layer name contains only valid characters
|
|
94
|
+
*/
|
|
95
|
+
isValidLayerName(layerName) {
|
|
96
|
+
// Layer names should only contain letters, numbers, spaces, and underscores
|
|
97
|
+
const validPattern = /^[a-zA-Z0-9\s_]+$/;
|
|
98
|
+
return validPattern.test(layerName);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Execute the layer management command
|
|
103
|
+
*/
|
|
104
|
+
async execute(params) {
|
|
105
|
+
// Ensure connected
|
|
106
|
+
if (!this.unityConnection.isConnected()) {
|
|
107
|
+
await this.unityConnection.connect();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const result = await this.unityConnection.sendCommand('manage_layers', params);
|
|
111
|
+
|
|
112
|
+
if (result.error) {
|
|
113
|
+
throw new Error(result.error);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get examples of how to use this tool
|
|
121
|
+
*/
|
|
122
|
+
getExamples() {
|
|
123
|
+
return {
|
|
124
|
+
getLayers: {
|
|
125
|
+
description: 'Get all layers with indices',
|
|
126
|
+
params: {
|
|
127
|
+
action: 'get'
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
addLayer: {
|
|
131
|
+
description: 'Add a new layer',
|
|
132
|
+
params: {
|
|
133
|
+
action: 'add',
|
|
134
|
+
layerName: 'Enemy'
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
removeLayer: {
|
|
138
|
+
description: 'Remove an existing layer',
|
|
139
|
+
params: {
|
|
140
|
+
action: 'remove',
|
|
141
|
+
layerName: 'Enemy'
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
getLayerByName: {
|
|
145
|
+
description: 'Get layer index by name',
|
|
146
|
+
params: {
|
|
147
|
+
action: 'get_by_name',
|
|
148
|
+
layerName: 'Player'
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
getLayerByIndex: {
|
|
152
|
+
description: 'Get layer name by index',
|
|
153
|
+
params: {
|
|
154
|
+
action: 'get_by_index',
|
|
155
|
+
layerIndex: 8
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|