@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.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/bin/unity-mcp-server +2 -0
  4. package/package.json +73 -0
  5. package/src/core/codeIndex.js +163 -0
  6. package/src/core/codeIndexDb.js +96 -0
  7. package/src/core/config.js +165 -0
  8. package/src/core/indexWatcher.js +52 -0
  9. package/src/core/projectInfo.js +111 -0
  10. package/src/core/server.js +294 -0
  11. package/src/core/unityConnection.js +426 -0
  12. package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +35 -0
  13. package/src/handlers/analysis/FindByComponentToolHandler.js +20 -0
  14. package/src/handlers/analysis/GetAnimatorStateToolHandler.js +37 -0
  15. package/src/handlers/analysis/GetComponentValuesToolHandler.js +20 -0
  16. package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +35 -0
  17. package/src/handlers/analysis/GetInputActionsStateToolHandler.js +37 -0
  18. package/src/handlers/analysis/GetObjectReferencesToolHandler.js +20 -0
  19. package/src/handlers/asset/AssetDatabaseToolHandler.js +221 -0
  20. package/src/handlers/asset/AssetDependencyToolHandler.js +201 -0
  21. package/src/handlers/asset/AssetImportSettingsToolHandler.js +170 -0
  22. package/src/handlers/asset/CreateMaterialToolHandler.js +96 -0
  23. package/src/handlers/asset/CreatePrefabToolHandler.js +78 -0
  24. package/src/handlers/asset/ExitPrefabModeToolHandler.js +83 -0
  25. package/src/handlers/asset/InstantiatePrefabToolHandler.js +133 -0
  26. package/src/handlers/asset/ModifyMaterialToolHandler.js +76 -0
  27. package/src/handlers/asset/ModifyPrefabToolHandler.js +72 -0
  28. package/src/handlers/asset/OpenPrefabToolHandler.js +121 -0
  29. package/src/handlers/asset/SavePrefabToolHandler.js +106 -0
  30. package/src/handlers/base/BaseToolHandler.js +133 -0
  31. package/src/handlers/compilation/GetCompilationStateToolHandler.js +90 -0
  32. package/src/handlers/component/AddComponentToolHandler.js +126 -0
  33. package/src/handlers/component/GetComponentTypesToolHandler.js +100 -0
  34. package/src/handlers/component/ListComponentsToolHandler.js +85 -0
  35. package/src/handlers/component/ModifyComponentToolHandler.js +143 -0
  36. package/src/handlers/component/RemoveComponentToolHandler.js +108 -0
  37. package/src/handlers/console/ClearConsoleToolHandler.js +160 -0
  38. package/src/handlers/console/ReadConsoleToolHandler.js +276 -0
  39. package/src/handlers/editor/LayerManagementToolHandler.js +160 -0
  40. package/src/handlers/editor/SelectionToolHandler.js +141 -0
  41. package/src/handlers/editor/TagManagementToolHandler.js +129 -0
  42. package/src/handlers/editor/ToolManagementToolHandler.js +135 -0
  43. package/src/handlers/editor/WindowManagementToolHandler.js +125 -0
  44. package/src/handlers/gameobject/CreateGameObjectToolHandler.js +131 -0
  45. package/src/handlers/gameobject/DeleteGameObjectToolHandler.js +101 -0
  46. package/src/handlers/gameobject/FindGameObjectToolHandler.js +119 -0
  47. package/src/handlers/gameobject/GetHierarchyToolHandler.js +132 -0
  48. package/src/handlers/gameobject/ModifyGameObjectToolHandler.js +128 -0
  49. package/src/handlers/index.js +389 -0
  50. package/src/handlers/input/AddInputActionToolHandler.js +20 -0
  51. package/src/handlers/input/AddInputBindingToolHandler.js +20 -0
  52. package/src/handlers/input/CreateActionMapToolHandler.js +20 -0
  53. package/src/handlers/input/CreateCompositeBindingToolHandler.js +20 -0
  54. package/src/handlers/input/GamepadSimulationHandler.js +116 -0
  55. package/src/handlers/input/InputSystemHandler.js +80 -0
  56. package/src/handlers/input/KeyboardSimulationHandler.js +79 -0
  57. package/src/handlers/input/ManageControlSchemesToolHandler.js +20 -0
  58. package/src/handlers/input/MouseSimulationHandler.js +107 -0
  59. package/src/handlers/input/RemoveActionMapToolHandler.js +20 -0
  60. package/src/handlers/input/RemoveAllBindingsToolHandler.js +20 -0
  61. package/src/handlers/input/RemoveInputActionToolHandler.js +20 -0
  62. package/src/handlers/input/RemoveInputBindingToolHandler.js +20 -0
  63. package/src/handlers/input/TouchSimulationHandler.js +142 -0
  64. package/src/handlers/menu/ExecuteMenuItemToolHandler.js +304 -0
  65. package/src/handlers/package/PackageManagerToolHandler.js +248 -0
  66. package/src/handlers/package/RegistryConfigToolHandler.js +198 -0
  67. package/src/handlers/playmode/GetEditorStateToolHandler.js +81 -0
  68. package/src/handlers/playmode/PauseToolHandler.js +44 -0
  69. package/src/handlers/playmode/PlayToolHandler.js +91 -0
  70. package/src/handlers/playmode/StopToolHandler.js +77 -0
  71. package/src/handlers/playmode/WaitForEditorStateToolHandler.js +45 -0
  72. package/src/handlers/scene/CreateSceneToolHandler.js +91 -0
  73. package/src/handlers/scene/GetSceneInfoToolHandler.js +20 -0
  74. package/src/handlers/scene/ListScenesToolHandler.js +58 -0
  75. package/src/handlers/scene/LoadSceneToolHandler.js +92 -0
  76. package/src/handlers/scene/SaveSceneToolHandler.js +76 -0
  77. package/src/handlers/screenshot/AnalyzeScreenshotToolHandler.js +238 -0
  78. package/src/handlers/screenshot/CaptureScreenshotToolHandler.js +692 -0
  79. package/src/handlers/script/BuildCodeIndexToolHandler.js +163 -0
  80. package/src/handlers/script/ScriptCreateClassFileToolHandler.js +60 -0
  81. package/src/handlers/script/ScriptEditStructuredToolHandler.js +173 -0
  82. package/src/handlers/script/ScriptIndexStatusToolHandler.js +61 -0
  83. package/src/handlers/script/ScriptPackagesListToolHandler.js +103 -0
  84. package/src/handlers/script/ScriptReadToolHandler.js +106 -0
  85. package/src/handlers/script/ScriptRefactorRenameToolHandler.js +83 -0
  86. package/src/handlers/script/ScriptRefsFindToolHandler.js +144 -0
  87. package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +79 -0
  88. package/src/handlers/script/ScriptSearchToolHandler.js +320 -0
  89. package/src/handlers/script/ScriptSymbolFindToolHandler.js +117 -0
  90. package/src/handlers/script/ScriptSymbolsGetToolHandler.js +96 -0
  91. package/src/handlers/settings/GetProjectSettingsToolHandler.js +161 -0
  92. package/src/handlers/settings/UpdateProjectSettingsToolHandler.js +272 -0
  93. package/src/handlers/system/GetCommandStatsToolHandler.js +25 -0
  94. package/src/handlers/system/PingToolHandler.js +53 -0
  95. package/src/handlers/system/RefreshAssetsToolHandler.js +45 -0
  96. package/src/handlers/ui/ClickUIElementToolHandler.js +110 -0
  97. package/src/handlers/ui/FindUIElementsToolHandler.js +63 -0
  98. package/src/handlers/ui/GetUIElementStateToolHandler.js +50 -0
  99. package/src/handlers/ui/SetUIElementValueToolHandler.js +49 -0
  100. package/src/handlers/ui/SimulateUIInputToolHandler.js +156 -0
  101. package/src/handlers/video/CaptureVideoForToolHandler.js +96 -0
  102. package/src/handlers/video/CaptureVideoStartToolHandler.js +38 -0
  103. package/src/handlers/video/CaptureVideoStatusToolHandler.js +30 -0
  104. package/src/handlers/video/CaptureVideoStopToolHandler.js +32 -0
  105. package/src/lsp/CSharpLspUtils.js +134 -0
  106. package/src/lsp/LspProcessManager.js +60 -0
  107. package/src/lsp/LspRpcClient.js +133 -0
  108. package/src/tools/analysis/analyzeSceneContents.js +100 -0
  109. package/src/tools/analysis/findByComponent.js +87 -0
  110. package/src/tools/analysis/getAnimatorState.js +326 -0
  111. package/src/tools/analysis/getComponentValues.js +182 -0
  112. package/src/tools/analysis/getGameObjectDetails.js +159 -0
  113. package/src/tools/analysis/getInputActionsState.js +329 -0
  114. package/src/tools/analysis/getObjectReferences.js +86 -0
  115. package/src/tools/input/inputActionsEditor.js +556 -0
  116. package/src/tools/scene/createScene.js +112 -0
  117. package/src/tools/scene/getSceneInfo.js +95 -0
  118. package/src/tools/scene/listScenes.js +82 -0
  119. package/src/tools/scene/loadScene.js +122 -0
  120. package/src/tools/scene/saveScene.js +91 -0
  121. package/src/tools/system/ping.js +72 -0
  122. package/src/tools/video/recordFor.js +31 -0
  123. package/src/tools/video/recordPlayMode.js +61 -0
  124. package/src/utils/csharpParse.js +88 -0
  125. package/src/utils/validators.js +90 -0
@@ -0,0 +1,117 @@
1
+ import path from 'path';
2
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
3
+ import { CodeIndex } from '../../core/codeIndex.js';
4
+ import { LspRpcClient } from '../../lsp/LspRpcClient.js';
5
+ import { ProjectInfoProvider } from '../../core/projectInfo.js';
6
+
7
+ export class ScriptSymbolFindToolHandler extends BaseToolHandler {
8
+ constructor(unityConnection) {
9
+ super(
10
+ 'script_symbol_find',
11
+ 'Find symbol definitions by name (class/method/field/property) using the bundled C# LSP. Guidance: prefer narrowing by kind and set exact=true when possible; use scope=assets|packages to avoid large outputs. Use results (container, namespace) to construct container namePath like Outer/Nested/Member for subsequent edit tools.',
12
+ {
13
+ type: 'object',
14
+ properties: {
15
+ name: {
16
+ type: 'string',
17
+ description: 'Symbol name to search (exact or fuzzy based on "exact").'
18
+ },
19
+ kind: {
20
+ type: 'string',
21
+ description: 'Optional: narrow by symbol kind (class, method, field, property).'
22
+ },
23
+ scope: {
24
+ type: 'string',
25
+ enum: ['assets', 'packages', 'embedded', 'all'],
26
+ description: 'Search scope: assets (Assets/), packages (Packages/), embedded, or all (default: all).'
27
+ },
28
+ exact: {
29
+ type: 'boolean',
30
+ description: 'If true, match name exactly; otherwise allows partial matches (default: false).'
31
+ }
32
+ },
33
+ required: ['name']
34
+ }
35
+ );
36
+ this.unityConnection = unityConnection;
37
+ this.index = new CodeIndex(unityConnection);
38
+ this.projectInfo = new ProjectInfoProvider(unityConnection);
39
+ this.lsp = null;
40
+ }
41
+
42
+ validate(params) {
43
+ super.validate(params);
44
+
45
+ const { name } = params;
46
+
47
+ if (!name || name.trim() === '') {
48
+ throw new Error('name cannot be empty');
49
+ }
50
+ }
51
+
52
+ async execute(params) {
53
+ const { name, kind, scope = 'assets', exact = false } = params;
54
+ // Prefer persistent index if available
55
+ let results = [];
56
+ if (await this.index.isReady()) {
57
+ const rows = await this.index.querySymbols({ name, kind, scope, exact });
58
+ results = rows.map(r => ({
59
+ path: r.path,
60
+ symbol: {
61
+ name: r.name,
62
+ kind: r.kind,
63
+ namespace: r.ns,
64
+ container: r.container,
65
+ startLine: r.line,
66
+ startColumn: r.column,
67
+ endLine: r.line,
68
+ endColumn: r.column
69
+ }
70
+ }));
71
+ } else {
72
+ const info = await this.projectInfo.get();
73
+ if (!this.lsp) this.lsp = new LspRpcClient(info.projectRoot);
74
+ const resp = await this.lsp.request('workspace/symbol', { query: String(name) });
75
+ const arr = resp?.result || [];
76
+ results = arr.map(s => ({
77
+ path: (s.location?.uri || '').replace('file://',''),
78
+ symbol: {
79
+ name: s.name,
80
+ kind: this.mapKind(s.kind),
81
+ namespace: null,
82
+ container: null,
83
+ startLine: (s.location?.range?.start?.line ?? 0) + 1,
84
+ startColumn: (s.location?.range?.start?.character ?? 0) + 1,
85
+ endLine: (s.location?.range?.end?.line ?? 0) + 1,
86
+ endColumn: (s.location?.range?.end?.character ?? 0) + 1,
87
+ }
88
+ }));
89
+ }
90
+ // Optional post-filtering: scope and exact name
91
+ if (scope && scope !== 'all') {
92
+ results = results.filter(x => {
93
+ const p = (x.path || '').replace(/\\\\/g, '/');
94
+ switch (scope) {
95
+ case 'assets': return p.startsWith('Assets/');
96
+ case 'packages': return p.startsWith('Packages/') || p.startsWith('Library/PackageCache/');
97
+ case 'embedded': return p.startsWith('Packages/'); }
98
+ });
99
+ }
100
+ if (exact) {
101
+ const target = String(name);
102
+ results = results.filter(x => x.symbol && x.symbol.name === target);
103
+ }
104
+ return { success: true, results, total: results.length };
105
+ }
106
+
107
+ mapKind(k) {
108
+ switch(k){
109
+ case 5: return 'class';
110
+ case 23: return 'struct';
111
+ case 11: return 'interface';
112
+ case 10: return 'enum';
113
+ case 6: return 'method';
114
+ case 7: return 'property';
115
+ case 8: return 'field'; }
116
+ }
117
+ }
@@ -0,0 +1,96 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
4
+ import { ProjectInfoProvider } from '../../core/projectInfo.js';
5
+
6
+ export class ScriptSymbolsGetToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'script_symbols_get',
10
+ 'FIRST STEP: Identify symbols (classes, methods, fields, properties) with spans before any edit. Path must start with Assets/ or Packages/. Use this to scope changes to a single symbol and avoid line-based edits. Returns line/column positions and container names (helpful to build container namePath like Outer/Nested/Member).',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ path: {
15
+ type: 'string',
16
+ description: 'Project-relative .cs path under Assets/ or Packages/ (e.g., Packages/unity-mcp-server/Editor/Foo.cs). Do NOT prefix repository folders (e.g., UnityMCPServer/…).'
17
+ }
18
+ },
19
+ required: ['path']
20
+ }
21
+ );
22
+ this.unityConnection = unityConnection;
23
+ this.projectInfo = new ProjectInfoProvider(unityConnection);
24
+ }
25
+
26
+ validate(params) {
27
+ super.validate(params);
28
+
29
+ const { path } = params;
30
+
31
+ if (!path || path.trim() === '') {
32
+ throw new Error('path cannot be empty');
33
+ }
34
+
35
+ // Check for .cs extension
36
+ if (!path.endsWith('.cs')) {
37
+ throw new Error('Only .cs files are supported');
38
+ }
39
+ }
40
+
41
+ async execute(params) {
42
+ // Normalize to project-relative path (strip repo-root prefixes if provided)
43
+ const rawPath = String(params.path || '').replace(/\\/g, '/');
44
+ const ai = rawPath.indexOf('Assets/');
45
+ const pi = rawPath.indexOf('Packages/');
46
+ const cut = (ai >= 0 && pi >= 0) ? Math.min(ai, pi) : (ai >= 0 ? ai : pi);
47
+ const relPath = cut >= 0 ? rawPath.substring(cut) : rawPath;
48
+
49
+ try {
50
+ const info = await this.projectInfo.get();
51
+ if (!relPath.startsWith('Assets/') && !relPath.startsWith('Packages/')) {
52
+ return { error: 'Path must be under Assets/ or Packages/' };
53
+ }
54
+ const abs = path.join(info.projectRoot, relPath);
55
+ const st = await fs.stat(abs).catch(() => null);
56
+ if (!st || !st.isFile()) return { error: 'File not found', path: relPath };
57
+ const { LspRpcClient } = await import('../../lsp/LspRpcClient.js');
58
+ const lsp = new LspRpcClient(info.projectRoot);
59
+ const uri = 'file://' + abs.replace(/\\\\/g, '/');
60
+ const res = await lsp.request('textDocument/documentSymbol', { textDocument: { uri } });
61
+ const docSymbols = res?.result ?? res ?? [];
62
+ const list = [];
63
+ const visit = (s, container) => {
64
+ const start = s.range?.start || s.selectionRange?.start || {};
65
+ list.push({
66
+ name: s.name || '',
67
+ kind: this.mapKind(s.kind),
68
+ container: container || null,
69
+ namespace: null,
70
+ startLine: (start.line ?? 0) + 1,
71
+ startColumn: (start.character ?? 0) + 1,
72
+ endLine: (start.line ?? 0) + 1,
73
+ endColumn: (start.character ?? 0) + 1
74
+ });
75
+ if (Array.isArray(s.children)) for (const c of s.children) visit(c, s.name || container);
76
+ };
77
+ if (Array.isArray(docSymbols)) for (const s of docSymbols) visit(s, null);
78
+ return { success: true, path: relPath, symbols: list };
79
+ } catch (e) {
80
+ return { error: e.message || 'Failed to get symbols' };
81
+ }
82
+ }
83
+
84
+ mapKind(k) {
85
+ switch (k) {
86
+ case 5: return 'class';
87
+ case 23: return 'struct';
88
+ case 11: return 'interface';
89
+ case 10: return 'enum';
90
+ case 6: return 'method';
91
+ case 7: return 'property';
92
+ case 8: return 'field';
93
+ default: return undefined;
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,161 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for retrieving Unity project settings
5
+ */
6
+ export class GetProjectSettingsToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'get_project_settings',
10
+ 'Get project settings by category via include flags (player/graphics/quality/physics/etc.).',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ includePlayer: {
15
+ type: 'boolean',
16
+ description: 'Include player settings (company/product name, version, etc.) (default: true).'
17
+ },
18
+ includeGraphics: {
19
+ type: 'boolean',
20
+ description: 'Include graphics settings (color space, render pipeline, etc.) (default: false).'
21
+ },
22
+ includeQuality: {
23
+ type: 'boolean',
24
+ description: 'Include quality settings (levels, anti-aliasing, shadows, etc.) (default: false).'
25
+ },
26
+ includePhysics: {
27
+ type: 'boolean',
28
+ description: 'Include 3D physics settings (gravity, solver iterations, etc.) (default: false).'
29
+ },
30
+ includePhysics2D: {
31
+ type: 'boolean',
32
+ description: 'Include 2D physics settings (default: false).'
33
+ },
34
+ includeAudio: {
35
+ type: 'boolean',
36
+ description: 'Include audio settings (speaker mode, DSP buffer, volume, etc.) (default: false).'
37
+ },
38
+ includeTime: {
39
+ type: 'boolean',
40
+ description: 'Include time settings (fixed timestep, time scale, etc.) (default: false).'
41
+ },
42
+ includeInputManager: {
43
+ type: 'boolean',
44
+ description: 'Include input manager settings (legacy input system) (default: false).'
45
+ },
46
+ includeEditor: {
47
+ type: 'boolean',
48
+ description: 'Include editor settings (Unity Remote, serialization mode, etc.) (default: false).'
49
+ },
50
+ includeBuild: {
51
+ type: 'boolean',
52
+ description: 'Include build settings (scenes in build, build target, etc.) (default: false).'
53
+ },
54
+ includeTags: {
55
+ type: 'boolean',
56
+ description: 'Include tags, layers, and sorting layers (default: false).'
57
+ }
58
+ },
59
+ required: []
60
+ }
61
+ );
62
+
63
+ this.unityConnection = unityConnection;
64
+ }
65
+
66
+ /**
67
+ * Validates the input parameters
68
+ * @param {Object} params - The input parameters
69
+ * @throws {Error} If validation fails
70
+ */
71
+ validate(params) {
72
+ // All parameters are optional booleans with defaults, no special validation needed
73
+ const booleanParams = [
74
+ 'includePlayer', 'includeGraphics', 'includeQuality',
75
+ 'includePhysics', 'includePhysics2D', 'includeAudio',
76
+ 'includeTime', 'includeInputManager', 'includeEditor',
77
+ 'includeBuild', 'includeTags'
78
+ ];
79
+
80
+ for (const param of booleanParams) {
81
+ if (params[param] !== undefined && typeof params[param] !== 'boolean') {
82
+ throw new Error(`${param} must be a boolean value`);
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Executes the get project settings operation
89
+ * @param {Object} params - The validated input parameters
90
+ * @returns {Promise<Object>} The project settings
91
+ */
92
+ async execute(params) {
93
+ // Ensure connection to Unity
94
+ if (!this.unityConnection.isConnected()) {
95
+ await this.unityConnection.connect();
96
+ }
97
+
98
+ // Send command to Unity
99
+ const response = await this.unityConnection.sendCommand('get_project_settings', params);
100
+
101
+ // Handle Unity response
102
+ if (response.error) {
103
+ throw new Error(response.error);
104
+ }
105
+
106
+ // Return the settings object
107
+ return response;
108
+ }
109
+
110
+ /**
111
+ * Gets example usage for this tool
112
+ * @returns {Object} Example usage scenarios
113
+ */
114
+ getExamples() {
115
+ return {
116
+ getBasicSettings: {
117
+ description: 'Get basic player settings only (default)',
118
+ params: {}
119
+ },
120
+ getGraphicsAndQuality: {
121
+ description: 'Get graphics and quality settings',
122
+ params: {
123
+ includeGraphics: true,
124
+ includeQuality: true
125
+ }
126
+ },
127
+ getPhysicsSettings: {
128
+ description: 'Get all physics-related settings',
129
+ params: {
130
+ includePhysics: true,
131
+ includePhysics2D: true,
132
+ includeTime: true
133
+ }
134
+ },
135
+ getEditorSettings: {
136
+ description: 'Get editor and build configuration',
137
+ params: {
138
+ includeEditor: true,
139
+ includeBuild: true,
140
+ includeTags: true
141
+ }
142
+ },
143
+ getAllSettings: {
144
+ description: 'Get all available settings',
145
+ params: {
146
+ includePlayer: true,
147
+ includeGraphics: true,
148
+ includeQuality: true,
149
+ includePhysics: true,
150
+ includePhysics2D: true,
151
+ includeAudio: true,
152
+ includeTime: true,
153
+ includeInputManager: true,
154
+ includeEditor: true,
155
+ includeBuild: true,
156
+ includeTags: true
157
+ }
158
+ }
159
+ };
160
+ }
161
+ }
@@ -0,0 +1,272 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ /**
4
+ * Handler for updating Unity project settings with safety confirmation
5
+ */
6
+ export class UpdateProjectSettingsToolHandler extends BaseToolHandler {
7
+ constructor(unityConnection) {
8
+ super(
9
+ 'update_project_settings',
10
+ 'Update project settings by category with a confirmation safety flag.',
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ confirmChanges: {
15
+ type: 'boolean',
16
+ description: 'Safety flag: must be true to apply changes (prevents accidental edits).'
17
+ },
18
+ player: {
19
+ type: 'object',
20
+ description: 'Player settings (company/product name, bundle version, windowing).',
21
+ properties: {
22
+ companyName: { type: 'string', description: 'Company name' },
23
+ productName: { type: 'string', description: 'Product name' },
24
+ version: { type: 'string', description: 'Bundle version' },
25
+ defaultScreenWidth: { type: 'number', description: 'Default screen width' },
26
+ defaultScreenHeight: { type: 'number', description: 'Default screen height' },
27
+ runInBackground: { type: 'boolean', description: 'Allow running in background' }
28
+ }
29
+ },
30
+ graphics: {
31
+ type: 'object',
32
+ description: 'Graphics settings (color space, render pipeline, etc.).',
33
+ properties: {
34
+ colorSpace: {
35
+ type: 'string',
36
+ enum: ['Gamma', 'Linear'],
37
+ description: 'Color space (requires Unity restart)'
38
+ }
39
+ }
40
+ },
41
+ physics: {
42
+ type: 'object',
43
+ description: '3D physics settings (gravity, solver iterations, thresholds).',
44
+ properties: {
45
+ gravity: {
46
+ type: 'object',
47
+ properties: {
48
+ x: { type: 'number' },
49
+ y: { type: 'number' },
50
+ z: { type: 'number' }
51
+ }
52
+ },
53
+ defaultSolverIterations: { type: 'number' },
54
+ defaultSolverVelocityIterations: { type: 'number' },
55
+ bounceThreshold: { type: 'number' },
56
+ sleepThreshold: { type: 'number' },
57
+ defaultContactOffset: { type: 'number' }
58
+ }
59
+ },
60
+ physics2D: {
61
+ type: 'object',
62
+ description: '2D physics settings (gravity, iterations, thresholds).',
63
+ properties: {
64
+ gravity: {
65
+ type: 'object',
66
+ properties: {
67
+ x: { type: 'number' },
68
+ y: { type: 'number' }
69
+ }
70
+ },
71
+ velocityIterations: { type: 'number' },
72
+ positionIterations: { type: 'number' },
73
+ velocityThreshold: { type: 'number' }
74
+ }
75
+ },
76
+ audio: {
77
+ type: 'object',
78
+ description: 'Audio settings (global volume).',
79
+ properties: {
80
+ globalVolume: {
81
+ type: 'number',
82
+ minimum: 0,
83
+ maximum: 1,
84
+ description: 'Global volume (0-1)'
85
+ }
86
+ }
87
+ },
88
+ time: {
89
+ type: 'object',
90
+ description: 'Time settings (fixed timestep, time scale).',
91
+ properties: {
92
+ fixedDeltaTime: { type: 'number', description: 'Fixed timestep' },
93
+ timeScale: { type: 'number', minimum: 0, description: 'Time scale (0 = paused)' },
94
+ maximumDeltaTime: { type: 'number', description: 'Maximum allowed timestep' }
95
+ }
96
+ },
97
+ quality: {
98
+ type: 'object',
99
+ description: 'Quality settings (levels, vSync, AA, shadows).',
100
+ properties: {
101
+ currentLevel: { type: 'string', description: 'Quality level name' },
102
+ vSyncCount: {
103
+ type: 'number',
104
+ minimum: 0,
105
+ maximum: 4,
106
+ description: 'VSync count (0=off, 1=every VBlank, 2=every second VBlank)'
107
+ },
108
+ antiAliasing: {
109
+ type: 'number',
110
+ enum: [0, 2, 4, 8],
111
+ description: 'Anti-aliasing samples'
112
+ },
113
+ shadowDistance: { type: 'number', minimum: 0, description: 'Shadow rendering distance' }
114
+ }
115
+ }
116
+ },
117
+ required: ['confirmChanges']
118
+ }
119
+ );
120
+
121
+ this.unityConnection = unityConnection;
122
+ }
123
+
124
+ /**
125
+ * Validates the input parameters
126
+ * @param {Object} params - The input parameters
127
+ * @throws {Error} If validation fails
128
+ */
129
+ validate(params) {
130
+ // Require explicit confirmation
131
+ if (!params.confirmChanges) {
132
+ throw new Error('confirmChanges must be set to true to update settings. This is a safety measure to prevent accidental changes.');
133
+ }
134
+
135
+ // Check that at least one settings category is provided
136
+ const settingsCategories = ['player', 'graphics', 'physics', 'physics2D', 'audio', 'time', 'quality'];
137
+ const hasSettings = settingsCategories.some(cat => params[cat] !== undefined);
138
+
139
+ if (!hasSettings) {
140
+ throw new Error('At least one settings category must be provided to update');
141
+ }
142
+
143
+ // Validate specific settings
144
+ if (params.audio?.globalVolume !== undefined) {
145
+ const volume = params.audio.globalVolume;
146
+ if (typeof volume !== 'number' || volume < 0 || volume > 1) {
147
+ throw new Error('globalVolume must be a number between 0 and 1');
148
+ }
149
+ }
150
+
151
+ if (params.quality?.vSyncCount !== undefined) {
152
+ const vSync = params.quality.vSyncCount;
153
+ if (!Number.isInteger(vSync) || vSync < 0 || vSync > 4) {
154
+ throw new Error('vSyncCount must be an integer between 0 and 4');
155
+ }
156
+ }
157
+
158
+ if (params.quality?.antiAliasing !== undefined) {
159
+ const aa = params.quality.antiAliasing;
160
+ if (![0, 2, 4, 8].includes(aa)) {
161
+ throw new Error('antiAliasing must be 0, 2, 4, or 8');
162
+ }
163
+ }
164
+
165
+ if (params.graphics?.colorSpace !== undefined) {
166
+ const colorSpace = params.graphics.colorSpace;
167
+ if (!['Gamma', 'Linear'].includes(colorSpace)) {
168
+ throw new Error('colorSpace must be either "Gamma" or "Linear"');
169
+ }
170
+ }
171
+
172
+ if (params.time?.timeScale !== undefined) {
173
+ const timeScale = params.time.timeScale;
174
+ if (typeof timeScale !== 'number' || timeScale < 0) {
175
+ throw new Error('timeScale must be a non-negative number');
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Executes the update project settings operation
182
+ * @param {Object} params - The validated input parameters
183
+ * @returns {Promise<Object>} The update results
184
+ */
185
+ async execute(params) {
186
+ // Ensure connection to Unity
187
+ if (!this.unityConnection.isConnected()) {
188
+ await this.unityConnection.connect();
189
+ }
190
+
191
+ // Send command to Unity
192
+ const response = await this.unityConnection.sendCommand('update_project_settings', params);
193
+
194
+ // Handle Unity response
195
+ if (response.error) {
196
+ // Check for specific error codes
197
+ if (response.code === 'CONFIRMATION_REQUIRED') {
198
+ throw new Error('Settings update requires confirmation. Set confirmChanges to true.');
199
+ }
200
+ throw new Error(response.error);
201
+ }
202
+
203
+ // Return the update results
204
+ return response;
205
+ }
206
+
207
+ /**
208
+ * Gets example usage for this tool
209
+ * @returns {Object} Example usage scenarios
210
+ */
211
+ getExamples() {
212
+ return {
213
+ updatePlayerSettings: {
214
+ description: 'Update player settings like company and product name',
215
+ params: {
216
+ confirmChanges: true,
217
+ player: {
218
+ companyName: 'My Company',
219
+ productName: 'My Game',
220
+ version: '1.0.0'
221
+ }
222
+ }
223
+ },
224
+ updatePhysicsSettings: {
225
+ description: 'Update physics settings like gravity',
226
+ params: {
227
+ confirmChanges: true,
228
+ physics: {
229
+ gravity: { x: 0, y: -9.81, z: 0 },
230
+ defaultSolverIterations: 10
231
+ }
232
+ }
233
+ },
234
+ updateQualitySettings: {
235
+ description: 'Update quality settings',
236
+ params: {
237
+ confirmChanges: true,
238
+ quality: {
239
+ vSyncCount: 1,
240
+ antiAliasing: 4,
241
+ shadowDistance: 150
242
+ }
243
+ }
244
+ },
245
+ updateMultipleCategories: {
246
+ description: 'Update multiple settings categories at once',
247
+ params: {
248
+ confirmChanges: true,
249
+ player: {
250
+ version: '2.0.0'
251
+ },
252
+ audio: {
253
+ globalVolume: 0.8
254
+ },
255
+ time: {
256
+ timeScale: 1.0,
257
+ fixedDeltaTime: 0.02
258
+ }
259
+ }
260
+ },
261
+ safetyCheckExample: {
262
+ description: 'Example showing safety check (will fail)',
263
+ params: {
264
+ confirmChanges: false, // This will trigger safety check
265
+ player: {
266
+ productName: 'Accidental Change'
267
+ }
268
+ }
269
+ }
270
+ };
271
+ }
272
+ }
@@ -0,0 +1,25 @@
1
+ import { BaseToolHandler } from '../base/BaseToolHandler.js';
2
+
3
+ export class GetCommandStatsToolHandler extends BaseToolHandler {
4
+ constructor(unityConnection) {
5
+ super(
6
+ 'get_command_stats',
7
+ 'Retrieve aggregated counts and recent Unity command types to audit traffic.',
8
+ {
9
+ type: 'object',
10
+ properties: {},
11
+ required: []
12
+ }
13
+ );
14
+ this.unityConnection = unityConnection;
15
+ }
16
+
17
+ async execute(params) {
18
+ if (!this.unityConnection.isConnected()) {
19
+ await this.unityConnection.connect();
20
+ }
21
+ const result = await this.unityConnection.sendCommand('get_command_stats', {});
22
+ return result;
23
+ }
24
+ }
25
+