@akiojin/unity-mcp-server 2.16.1 → 2.26.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/LICENSE +0 -0
- package/README.md +14 -4
- package/bin/unity-mcp-server +7 -1
- package/package.json +27 -5
- package/src/core/codeIndex.js +0 -0
- package/src/core/codeIndexDb.js +0 -0
- package/src/core/config.js +242 -200
- package/src/core/indexWatcher.js +83 -13
- package/src/core/jobManager.js +178 -0
- package/src/core/projectInfo.js +65 -118
- package/src/core/server.js +15 -2
- package/src/core/unityConnection.js +0 -0
- package/src/handlers/addressables/AddressablesAnalyzeToolHandler.js +180 -0
- package/src/handlers/addressables/AddressablesBuildToolHandler.js +146 -0
- package/src/handlers/addressables/AddressablesManageToolHandler.js +272 -0
- package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +0 -0
- package/src/handlers/analysis/FindByComponentToolHandler.js +0 -0
- package/src/handlers/analysis/GetAnimatorStateToolHandler.js +0 -0
- package/src/handlers/analysis/GetComponentValuesToolHandler.js +0 -0
- package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +0 -0
- package/src/handlers/analysis/GetInputActionsStateToolHandler.js +0 -0
- package/src/handlers/analysis/GetObjectReferencesToolHandler.js +0 -0
- package/src/handlers/asset/AssetDatabaseManageToolHandler.js +0 -0
- package/src/handlers/asset/AssetDependencyAnalyzeToolHandler.js +0 -0
- package/src/handlers/asset/AssetImportSettingsManageToolHandler.js +0 -0
- package/src/handlers/asset/AssetMaterialCreateToolHandler.js +0 -0
- package/src/handlers/asset/AssetMaterialModifyToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabCreateToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabExitModeToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabInstantiateToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabModifyToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabOpenToolHandler.js +0 -0
- package/src/handlers/asset/AssetPrefabSaveToolHandler.js +0 -0
- package/src/handlers/base/BaseToolHandler.js +0 -0
- package/src/handlers/compilation/CompilationGetStateToolHandler.js +0 -0
- package/src/handlers/component/ComponentAddToolHandler.js +0 -0
- package/src/handlers/component/ComponentFieldSetToolHandler.js +419 -0
- package/src/handlers/component/ComponentGetTypesToolHandler.js +0 -0
- package/src/handlers/component/ComponentListToolHandler.js +0 -0
- package/src/handlers/component/ComponentModifyToolHandler.js +0 -0
- package/src/handlers/component/ComponentRemoveToolHandler.js +0 -0
- package/src/handlers/console/ConsoleClearToolHandler.js +0 -0
- package/src/handlers/console/ConsoleReadToolHandler.js +295 -276
- package/src/handlers/editor/EditorLayersManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorSelectionManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorTagsManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorToolsManageToolHandler.js +0 -0
- package/src/handlers/editor/EditorWindowsManageToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectCreateToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectDeleteToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectFindToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectGetHierarchyToolHandler.js +0 -0
- package/src/handlers/gameobject/GameObjectModifyToolHandler.js +0 -0
- package/src/handlers/index.js +437 -406
- package/src/handlers/input/InputActionAddToolHandler.js +0 -0
- package/src/handlers/input/InputActionMapCreateToolHandler.js +0 -0
- package/src/handlers/input/InputActionMapRemoveToolHandler.js +0 -0
- package/src/handlers/input/InputActionRemoveToolHandler.js +0 -0
- package/src/handlers/input/InputBindingAddToolHandler.js +0 -0
- package/src/handlers/input/InputBindingCompositeCreateToolHandler.js +0 -0
- package/src/handlers/input/InputBindingRemoveAllToolHandler.js +0 -0
- package/src/handlers/input/InputBindingRemoveToolHandler.js +0 -0
- package/src/handlers/input/InputControlSchemesManageToolHandler.js +0 -0
- package/src/handlers/input/InputGamepadSimulateToolHandler.js +0 -0
- package/src/handlers/input/InputKeyboardSimulateToolHandler.js +0 -0
- package/src/handlers/input/InputMouseSimulateToolHandler.js +0 -0
- package/src/handlers/input/InputSystemControlToolHandler.js +0 -0
- package/src/handlers/input/InputTouchSimulateToolHandler.js +0 -0
- package/src/handlers/menu/MenuItemExecuteToolHandler.js +0 -0
- package/src/handlers/package/PackageManagerToolHandler.js +0 -0
- package/src/handlers/package/RegistryConfigToolHandler.js +0 -0
- package/src/handlers/playmode/PlaymodeGetStateToolHandler.js +1 -1
- package/src/handlers/playmode/PlaymodePauseToolHandler.js +1 -1
- package/src/handlers/playmode/PlaymodePlayToolHandler.js +0 -0
- package/src/handlers/playmode/PlaymodeStopToolHandler.js +0 -0
- package/src/handlers/playmode/PlaymodeWaitForStateToolHandler.js +0 -0
- package/src/handlers/scene/GetSceneInfoToolHandler.js +0 -0
- package/src/handlers/scene/SceneCreateToolHandler.js +0 -0
- package/src/handlers/scene/SceneListToolHandler.js +0 -0
- package/src/handlers/scene/SceneLoadToolHandler.js +0 -0
- package/src/handlers/scene/SceneSaveToolHandler.js +0 -0
- package/src/handlers/screenshot/ScreenshotAnalyzeToolHandler.js +0 -0
- package/src/handlers/screenshot/ScreenshotCaptureToolHandler.js +0 -0
- package/src/handlers/script/CodeIndexBuildToolHandler.js +125 -15
- package/src/handlers/script/CodeIndexStatusToolHandler.js +129 -0
- package/src/handlers/script/ScriptCreateClassToolHandler.js +0 -0
- package/src/handlers/script/ScriptEditSnippetToolHandler.js +1 -1
- package/src/handlers/script/ScriptEditStructuredToolHandler.js +1 -1
- package/src/handlers/script/ScriptPackagesListToolHandler.js +0 -0
- package/src/handlers/script/ScriptReadToolHandler.js +0 -0
- package/src/handlers/script/ScriptRefactorRenameToolHandler.js +0 -0
- package/src/handlers/script/ScriptRefsFindToolHandler.js +0 -0
- package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +0 -0
- package/src/handlers/script/ScriptSearchToolHandler.js +0 -0
- package/src/handlers/script/ScriptSymbolFindToolHandler.js +0 -0
- package/src/handlers/script/ScriptSymbolsGetToolHandler.js +0 -0
- package/src/handlers/settings/SettingsGetToolHandler.js +0 -0
- package/src/handlers/settings/SettingsUpdateToolHandler.js +0 -0
- package/src/handlers/system/SystemGetCommandStatsToolHandler.js +0 -0
- package/src/handlers/system/SystemPingToolHandler.js +0 -0
- package/src/handlers/system/SystemRefreshAssetsToolHandler.js +0 -0
- package/src/handlers/test/TestRunToolHandler.js +0 -0
- package/src/handlers/ui/UIClickElementToolHandler.js +0 -0
- package/src/handlers/ui/UIFindElementsToolHandler.js +0 -0
- package/src/handlers/ui/UIGetElementStateToolHandler.js +0 -0
- package/src/handlers/ui/UISetElementValueToolHandler.js +0 -0
- package/src/handlers/ui/UISimulateInputToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureForToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureStartToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureStatusToolHandler.js +0 -0
- package/src/handlers/video/VideoCaptureStopToolHandler.js +0 -0
- package/src/lsp/CSharpLspUtils.js +27 -4
- package/src/lsp/LspProcessManager.js +0 -0
- package/src/lsp/LspRpcClient.js +0 -0
- package/src/tools/analysis/analyzeSceneContents.js +0 -0
- package/src/tools/analysis/findByComponent.js +0 -0
- package/src/tools/analysis/getAnimatorState.js +0 -0
- package/src/tools/analysis/getComponentValues.js +0 -0
- package/src/tools/analysis/getGameObjectDetails.js +0 -0
- package/src/tools/analysis/getInputActionsState.js +0 -0
- package/src/tools/analysis/getObjectReferences.js +0 -0
- package/src/tools/input/inputActionsEditor.js +0 -0
- package/src/tools/scene/createScene.js +0 -0
- package/src/tools/scene/getSceneInfo.js +0 -0
- package/src/tools/scene/listScenes.js +0 -0
- package/src/tools/scene/loadScene.js +0 -0
- package/src/tools/scene/saveScene.js +0 -0
- package/src/tools/system/ping.js +0 -0
- package/src/tools/video/recordFor.js +0 -0
- package/src/tools/video/recordPlayMode.js +0 -0
- package/src/utils/csharpParse.js +0 -0
- package/src/utils/validators.js +0 -0
- package/src/handlers/script/ScriptIndexStatusToolHandler.js +0 -61
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -7,7 +7,7 @@ export class PlaymodeGetStateToolHandler extends BaseToolHandler {
|
|
|
7
7
|
constructor(unityConnection) {
|
|
8
8
|
super(
|
|
9
9
|
'playmode_get_state',
|
|
10
|
-
'Get editor state
|
|
10
|
+
'Get current Unity editor state including play mode status',
|
|
11
11
|
{
|
|
12
12
|
type: 'object',
|
|
13
13
|
properties: {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -5,15 +5,27 @@ import path from 'path';
|
|
|
5
5
|
import { ProjectInfoProvider } from '../../core/projectInfo.js';
|
|
6
6
|
import { LspRpcClient } from '../../lsp/LspRpcClient.js';
|
|
7
7
|
import { logger } from '../../core/config.js';
|
|
8
|
+
import { JobManager } from '../../core/jobManager.js';
|
|
8
9
|
|
|
9
10
|
export class CodeIndexBuildToolHandler extends BaseToolHandler {
|
|
10
11
|
constructor(unityConnection) {
|
|
11
12
|
super(
|
|
12
13
|
'code_index_build',
|
|
13
|
-
'Build (or rebuild) the persistent SQLite symbol index by scanning document symbols via the C# LSP. Stores DB under .unity/cache/code-index/code-index.db.',
|
|
14
|
+
'Build (or rebuild) the persistent SQLite symbol index by scanning document symbols via the C# LSP. Returns immediately with jobId for background execution. Check progress with code_index_status. Stores DB under .unity/cache/code-index/code-index.db.',
|
|
14
15
|
{
|
|
15
16
|
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
+
properties: {
|
|
18
|
+
throttleMs: {
|
|
19
|
+
type: 'number',
|
|
20
|
+
minimum: 0,
|
|
21
|
+
description: 'Optional delay in milliseconds after processing each file (testing/debugging).'
|
|
22
|
+
},
|
|
23
|
+
delayStartMs: {
|
|
24
|
+
type: 'number',
|
|
25
|
+
minimum: 0,
|
|
26
|
+
description: 'Optional delay before processing begins (useful to keep job in running state briefly).'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
17
29
|
required: []
|
|
18
30
|
}
|
|
19
31
|
);
|
|
@@ -21,10 +33,50 @@ export class CodeIndexBuildToolHandler extends BaseToolHandler {
|
|
|
21
33
|
this.index = new CodeIndex(unityConnection);
|
|
22
34
|
this.projectInfo = new ProjectInfoProvider(unityConnection);
|
|
23
35
|
this.lsp = null; // lazy init with projectRoot
|
|
36
|
+
this.jobManager = JobManager.getInstance();
|
|
37
|
+
this.currentJobId = null; // Track current running job
|
|
24
38
|
}
|
|
25
39
|
|
|
26
40
|
async execute(params = {}) {
|
|
41
|
+
// Check if a build is already running
|
|
42
|
+
if (this.currentJobId) {
|
|
43
|
+
const existingJob = this.jobManager.get(this.currentJobId);
|
|
44
|
+
if (existingJob && existingJob.status === 'running') {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: 'build_already_running',
|
|
48
|
+
message: `Code index build is already running (jobId: ${this.currentJobId}). Use code_index_status to check progress.`,
|
|
49
|
+
jobId: this.currentJobId
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Generate new jobId
|
|
55
|
+
const jobId = `build-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`;
|
|
56
|
+
this.currentJobId = jobId;
|
|
57
|
+
|
|
58
|
+
// Create background job
|
|
59
|
+
this.jobManager.create(jobId, async (job) => {
|
|
60
|
+
return await this._executeBuild(params, job);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Return immediately with jobId
|
|
64
|
+
return {
|
|
65
|
+
success: true,
|
|
66
|
+
jobId,
|
|
67
|
+
message: 'Code index build started in background',
|
|
68
|
+
checkStatus: 'Use code_index_status to check progress and completion'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Internal method that performs the actual build
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
async _executeBuild(params, job) {
|
|
27
77
|
try {
|
|
78
|
+
const throttleMs = Math.max(0, Number(params?.throttleMs ?? 0));
|
|
79
|
+
const delayStartMs = Math.max(0, Number(params?.delayStartMs ?? 0));
|
|
28
80
|
const info = await this.projectInfo.get();
|
|
29
81
|
const roots = [
|
|
30
82
|
path.resolve(info.projectRoot, 'Assets'),
|
|
@@ -35,7 +87,16 @@ export class CodeIndexBuildToolHandler extends BaseToolHandler {
|
|
|
35
87
|
const seen = new Set();
|
|
36
88
|
for (const r of roots) this.walkCs(r, files, seen);
|
|
37
89
|
|
|
38
|
-
|
|
90
|
+
// Initialize LSP with error handling
|
|
91
|
+
if (!this.lsp) {
|
|
92
|
+
try {
|
|
93
|
+
this.lsp = new LspRpcClient(info.projectRoot);
|
|
94
|
+
logger.info(`[index][${job.id}] LSP initialized for project: ${info.projectRoot}`);
|
|
95
|
+
} catch (lspError) {
|
|
96
|
+
logger.error(`[index][${job.id}] LSP initialization failed: ${lspError.message}`);
|
|
97
|
+
throw new Error(`LSP initialization failed: ${lspError.message}. Ensure C# LSP is properly configured and OmniSharp is available.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
39
100
|
const lsp = this.lsp;
|
|
40
101
|
|
|
41
102
|
// Incremental detection based on size-mtime signature
|
|
@@ -74,6 +135,13 @@ export class CodeIndexBuildToolHandler extends BaseToolHandler {
|
|
|
74
135
|
const startAt = Date.now();
|
|
75
136
|
let i = 0; let updated = 0; let processed = 0;
|
|
76
137
|
|
|
138
|
+
// Initialize progress
|
|
139
|
+
job.progress.total = absList.length;
|
|
140
|
+
job.progress.processed = 0;
|
|
141
|
+
job.progress.rate = 0;
|
|
142
|
+
|
|
143
|
+
logger.info(`[index][${job.id}] Build started: ${absList.length} files to process, ${removed.length} to remove (status: ${job.status})`);
|
|
144
|
+
|
|
77
145
|
// LSP request with small retry/backoff
|
|
78
146
|
const requestWithRetry = async (uri, maxRetries = Math.max(0, Math.min(5, Number(params?.retry ?? 2)))) => {
|
|
79
147
|
let lastErr = null;
|
|
@@ -88,26 +156,45 @@ export class CodeIndexBuildToolHandler extends BaseToolHandler {
|
|
|
88
156
|
}
|
|
89
157
|
throw lastErr || new Error('documentSymbol failed');
|
|
90
158
|
};
|
|
159
|
+
if (delayStartMs > 0) {
|
|
160
|
+
await new Promise(resolve => setTimeout(resolve, delayStartMs));
|
|
161
|
+
}
|
|
162
|
+
|
|
91
163
|
const worker = async () => {
|
|
92
164
|
while (true) {
|
|
93
165
|
const idx = i++;
|
|
94
166
|
if (idx >= absList.length) break;
|
|
95
167
|
const abs = absList[idx];
|
|
168
|
+
const rel = this.toRel(abs, info.projectRoot);
|
|
96
169
|
try {
|
|
97
170
|
const uri = 'file://' + abs.replace(/\\/g, '/');
|
|
98
171
|
const docSymbols = await requestWithRetry(uri, 2);
|
|
99
172
|
const rows = toRows(uri, docSymbols);
|
|
100
|
-
const rel = this.toRel(abs, info.projectRoot);
|
|
101
173
|
await this.index.replaceSymbolsForPath(rel, rows);
|
|
102
174
|
await this.index.upsertFile(rel, wanted.get(rel));
|
|
103
175
|
updated += 1;
|
|
104
|
-
} catch {
|
|
176
|
+
} catch (err) {
|
|
177
|
+
// File access or LSP error - skip and continue
|
|
178
|
+
// This allows build to continue even if some files fail
|
|
179
|
+
if (processed % 50 === 0) {
|
|
180
|
+
// Log occasionally to avoid spam
|
|
181
|
+
logger.warn(`[index][${job.id}] Skipped file due to error: ${rel} - ${err.message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
105
184
|
finally {
|
|
106
185
|
processed += 1;
|
|
186
|
+
|
|
187
|
+
// Update job progress
|
|
188
|
+
const elapsed = Math.max(1, Date.now() - startAt);
|
|
189
|
+
job.progress.processed = processed;
|
|
190
|
+
job.progress.rate = parseFloat((processed * 1000 / elapsed).toFixed(1));
|
|
191
|
+
|
|
107
192
|
if (processed % reportEvery === 0 || processed === absList.length) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
193
|
+
logger.info(`[index][${job.id}] progress ${processed}/${absList.length} (removed:${removed.length}) rate:${job.progress.rate} f/s (status: ${job.status})`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (throttleMs > 0) {
|
|
197
|
+
await new Promise(resolve => setTimeout(resolve, throttleMs));
|
|
111
198
|
}
|
|
112
199
|
}
|
|
113
200
|
}
|
|
@@ -116,14 +203,37 @@ export class CodeIndexBuildToolHandler extends BaseToolHandler {
|
|
|
116
203
|
await Promise.all(workers);
|
|
117
204
|
|
|
118
205
|
const stats = await this.index.getStats();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
206
|
+
|
|
207
|
+
// Clear current job tracking on success
|
|
208
|
+
if (this.currentJobId === job.id) {
|
|
209
|
+
this.currentJobId = null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const result = {
|
|
213
|
+
updatedFiles: updated,
|
|
214
|
+
removedFiles: removed.length,
|
|
215
|
+
totalIndexedSymbols: stats.total,
|
|
216
|
+
lastIndexedAt: stats.lastIndexedAt
|
|
126
217
|
};
|
|
218
|
+
|
|
219
|
+
logger.info(`[index][${job.id}] Build completed successfully: updated=${result.updatedFiles}, removed=${result.removedFiles}, total=${result.totalIndexedSymbols} (status: completed)`);
|
|
220
|
+
|
|
221
|
+
return result;
|
|
222
|
+
} catch (e) {
|
|
223
|
+
// Clear current job tracking on error
|
|
224
|
+
if (this.currentJobId === job.id) {
|
|
225
|
+
this.currentJobId = null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Log detailed error with job context
|
|
229
|
+
logger.error(`[index][${job.id}] Build failed: ${e.message} (status: failed)`);
|
|
230
|
+
|
|
231
|
+
// Provide helpful error message with context
|
|
232
|
+
const errorMessage = e.message.includes('LSP')
|
|
233
|
+
? `LSP error: ${e.message}`
|
|
234
|
+
: `Build error: ${e.message}. Hint: C# LSP not ready. Ensure manifest/auto-download and workspace paths are valid.`;
|
|
235
|
+
|
|
236
|
+
throw new Error(errorMessage);
|
|
127
237
|
}
|
|
128
238
|
}
|
|
129
239
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
4
|
+
import { ProjectInfoProvider } from '../../core/projectInfo.js';
|
|
5
|
+
import { JobManager } from '../../core/jobManager.js';
|
|
6
|
+
import { CodeIndex } from '../../core/codeIndex.js';
|
|
7
|
+
|
|
8
|
+
export class CodeIndexStatusToolHandler extends BaseToolHandler {
|
|
9
|
+
constructor(unityConnection) {
|
|
10
|
+
super(
|
|
11
|
+
'code_index_status',
|
|
12
|
+
'Report code index status and readiness for symbol/search operations. BEST PRACTICES: Check before heavy symbol operations. Shows total files indexed and coverage percentage. If coverage is low, some symbol operations may be incomplete. Index is automatically built on first use. No parameters needed - lightweight status check.',
|
|
13
|
+
{
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {},
|
|
16
|
+
required: []
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
this.unityConnection = unityConnection;
|
|
20
|
+
this.projectInfo = new ProjectInfoProvider(unityConnection);
|
|
21
|
+
this.jobManager = JobManager.getInstance();
|
|
22
|
+
this.codeIndex = new CodeIndex(unityConnection);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async execute() {
|
|
26
|
+
const jobManager = this.jobManager;
|
|
27
|
+
const buildJobs = jobManager.getAllJobs()
|
|
28
|
+
.filter((job) => typeof job?.id === 'string' && job.id.startsWith('build-'))
|
|
29
|
+
.sort((a, b) => new Date(b.startedAt || 0).getTime() - new Date(a.startedAt || 0).getTime());
|
|
30
|
+
const latestBuildJob = buildJobs.length > 0 ? buildJobs[0] : null;
|
|
31
|
+
|
|
32
|
+
// Check whether the persistent index already exists or is being built in the background.
|
|
33
|
+
const ready = await this.codeIndex.isReady();
|
|
34
|
+
const buildInProgress = latestBuildJob?.status === 'running';
|
|
35
|
+
if (!ready && !buildInProgress) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: 'index_not_built',
|
|
39
|
+
message: 'Code index is not built. Please run UnityMCP.code_index_build first.'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let projectInfo;
|
|
44
|
+
try {
|
|
45
|
+
projectInfo = await this.projectInfo.get();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: 'project_info_unavailable',
|
|
50
|
+
message: error.message || 'Unable to resolve Unity project root.'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const projectRoot = projectInfo.projectRoot.replace(/\\/g, '/');
|
|
55
|
+
const roots = [
|
|
56
|
+
path.resolve(projectRoot, 'Assets'),
|
|
57
|
+
path.resolve(projectRoot, 'Packages'),
|
|
58
|
+
path.resolve(projectRoot, 'Library/PackageCache')
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
let totalFiles = 0;
|
|
62
|
+
const breakdown = { assets: 0, packages: 0, packageCache: 0, other: 0 };
|
|
63
|
+
|
|
64
|
+
const visit = (targetPath) => {
|
|
65
|
+
try {
|
|
66
|
+
if (!fs.existsSync(targetPath)) return;
|
|
67
|
+
const stats = fs.statSync(targetPath);
|
|
68
|
+
if (stats.isFile()) {
|
|
69
|
+
if (!targetPath.endsWith('.cs')) return;
|
|
70
|
+
totalFiles += 1;
|
|
71
|
+
const normalized = targetPath.replace(/\\/g, '/');
|
|
72
|
+
const relative = normalized.replace(projectRoot, '').replace(/^\//, '');
|
|
73
|
+
if (relative.startsWith('Assets/')) breakdown.assets += 1;
|
|
74
|
+
else if (relative.startsWith('Packages/')) breakdown.packages += 1;
|
|
75
|
+
else if (relative.includes('Library/PackageCache/')) breakdown.packageCache += 1;
|
|
76
|
+
else breakdown.other += 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (stats.isDirectory()) {
|
|
81
|
+
for (const child of fs.readdirSync(targetPath)) {
|
|
82
|
+
if (child === 'obj' || child === 'bin' || child.startsWith('.')) continue;
|
|
83
|
+
visit(path.join(targetPath, child));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Ignore permission errors while traversing; status reporting should not fail because of a single file.
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (const root of roots) visit(root);
|
|
92
|
+
|
|
93
|
+
const stats = await this.codeIndex.getStats();
|
|
94
|
+
const coverage = totalFiles > 0 ? Math.min(1, stats.total / totalFiles) : 0;
|
|
95
|
+
|
|
96
|
+
const indexInfo = {
|
|
97
|
+
ready,
|
|
98
|
+
rows: stats.total,
|
|
99
|
+
lastIndexedAt: stats.lastIndexedAt
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
if (latestBuildJob) {
|
|
103
|
+
const progress = latestBuildJob.progress ? { ...latestBuildJob.progress } : undefined;
|
|
104
|
+
indexInfo.buildJob = {
|
|
105
|
+
id: latestBuildJob.id,
|
|
106
|
+
status: latestBuildJob.status,
|
|
107
|
+
startedAt: latestBuildJob.startedAt ?? null,
|
|
108
|
+
...(progress ? { progress } : {})
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if (latestBuildJob.status === 'completed') {
|
|
112
|
+
indexInfo.buildJob.completedAt = latestBuildJob.completedAt ?? null;
|
|
113
|
+
indexInfo.buildJob.result = latestBuildJob.result ?? null;
|
|
114
|
+
} else if (latestBuildJob.status === 'failed') {
|
|
115
|
+
indexInfo.buildJob.failedAt = latestBuildJob.failedAt ?? null;
|
|
116
|
+
indexInfo.buildJob.error = latestBuildJob.error ?? 'Unknown error';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
totalFiles,
|
|
123
|
+
indexedFiles: stats.total,
|
|
124
|
+
coverage,
|
|
125
|
+
breakdown,
|
|
126
|
+
index: indexInfo
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
File without changes
|
|
@@ -15,7 +15,7 @@ export class ScriptEditSnippetToolHandler extends BaseToolHandler {
|
|
|
15
15
|
constructor(unityConnection) {
|
|
16
16
|
super(
|
|
17
17
|
'script_edit_snippet',
|
|
18
|
-
'
|
|
18
|
+
'[C# EDITING - PRECISION TOOL] For Unity C# scripts, use for tiny surgical edits (≤80 chars per change). Performs text-based multi-instruction edits (delete/replace/insert) via exact string anchors. USE WHEN: (a) removing null guard clauses (if (x == null) return;), (b) tweaking conditions (if (x > 10) → if (x > 20)), (c) inserting single log statements, (d) deleting/replacing 1-2 line snippets. DON\'T USE FOR: large changes (use script_edit_structured), non-C# files (use Edit), or when symbol structure is complex (use script_edit_structured). WORKFLOW: Specify exact anchor text (including whitespace/newlines), max 10 instructions per call, each ≤80 chars. Anchor must match exactly once in file. Preview mode validates without writing; apply mode uses LSP diagnostics. Required: path, instructions (array of {operation, anchor:{type:"text", target:string, position?:"before"|"after"}, newText?}).',
|
|
19
19
|
{
|
|
20
20
|
type: 'object',
|
|
21
21
|
properties: {
|
|
@@ -6,7 +6,7 @@ export class ScriptEditStructuredToolHandler extends BaseToolHandler {
|
|
|
6
6
|
constructor(unityConnection) {
|
|
7
7
|
super(
|
|
8
8
|
'script_edit_structured',
|
|
9
|
-
'
|
|
9
|
+
'[C# EDITING - PRIMARY TOOL] For Unity C# script editing, PREFER this tool over Read/Edit/Write for structural code changes. Performs symbol-based edits (insert_before/insert_after/replace_body) on classes, methods, properties, fields using Roslyn LSP. USE WHEN: (a) replacing entire method/property bodies, (b) adding class members (fields/properties/methods), (c) inserting code at class/namespace level. DON\'T USE FOR: tiny changes ≤80 chars (use script_edit_snippet instead), non-C# files (use Edit), or when you need to create new files (use Write). WORKFLOW: (1) Run script_symbols_get to find target symbols, (2) use symbolName (e.g., "MyClass/MyMethod"), (3) apply edits. Insert operations target containers (class/namespace), not methods. Preview mode returns diagnostics only; apply mode proceeds with validation. Required: path (Assets/|Packages/), symbolName, operation. Optional: kind, newText, preview.',
|
|
10
10
|
{
|
|
11
11
|
type: 'object',
|
|
12
12
|
properties: {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
import { logger } from '../core/config.js';
|
|
5
6
|
import { WORKSPACE_ROOT } from '../core/config.js';
|
|
6
7
|
|
|
@@ -14,12 +15,34 @@ export class CSharpLspUtils {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
getDesiredVersion() {
|
|
18
|
+
const candidates = [];
|
|
19
|
+
|
|
20
|
+
// When launched from workspace root: mcp-server/package.json
|
|
21
|
+
candidates.push(path.resolve('mcp-server/package.json'));
|
|
22
|
+
// When launched within mcp-server directory
|
|
23
|
+
candidates.push(path.resolve('package.json'));
|
|
24
|
+
|
|
25
|
+
// Resolve relative to this module (always inside mcp-server/src/lsp)
|
|
17
26
|
try {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
} catch {
|
|
21
|
-
|
|
27
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
candidates.push(path.resolve(moduleDir, '../../package.json'));
|
|
29
|
+
} catch {}
|
|
30
|
+
|
|
31
|
+
// Resolve relative to WORKSPACE_ROOT if provided
|
|
32
|
+
if (WORKSPACE_ROOT) {
|
|
33
|
+
candidates.push(path.resolve(WORKSPACE_ROOT, 'mcp-server', 'package.json'));
|
|
34
|
+
candidates.push(path.resolve(WORKSPACE_ROOT, 'package.json'));
|
|
22
35
|
}
|
|
36
|
+
|
|
37
|
+
for (const candidate of candidates) {
|
|
38
|
+
try {
|
|
39
|
+
if (!fs.existsSync(candidate)) continue;
|
|
40
|
+
const pkg = JSON.parse(fs.readFileSync(candidate, 'utf8'));
|
|
41
|
+
if (pkg?.version) return pkg.version;
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null;
|
|
23
46
|
}
|
|
24
47
|
|
|
25
48
|
getExecutableName() {
|
|
File without changes
|
package/src/lsp/LspRpcClient.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tools/system/ping.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/utils/csharpParse.js
CHANGED
|
File without changes
|
package/src/utils/validators.js
CHANGED
|
File without changes
|