@akiojin/unity-mcp-server 2.32.0 → 2.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -5
- package/package.json +9 -4
- package/src/core/config.js +241 -242
- package/src/core/projectInfo.js +15 -0
- package/src/core/transports/HybridStdioServerTransport.js +78 -75
- package/src/handlers/addressables/AddressablesAnalyzeToolHandler.js +45 -47
- package/src/handlers/addressables/AddressablesBuildToolHandler.js +32 -33
- package/src/handlers/addressables/AddressablesManageToolHandler.js +74 -75
- package/src/handlers/component/ComponentFieldSetToolHandler.js +419 -419
- package/src/handlers/index.js +437 -437
- package/src/handlers/input/InputGamepadToolHandler.js +162 -0
- package/src/handlers/input/InputKeyboardToolHandler.js +127 -0
- package/src/handlers/input/InputMouseToolHandler.js +188 -0
- package/src/handlers/input/InputSystemControlToolHandler.js +63 -64
- package/src/handlers/input/InputTouchToolHandler.js +178 -0
- package/src/handlers/playmode/PlaymodePlayToolHandler.js +36 -23
- package/src/handlers/playmode/PlaymodeStopToolHandler.js +32 -21
- package/src/handlers/test/TestGetStatusToolHandler.js +37 -10
- package/src/handlers/test/TestRunToolHandler.js +36 -35
- package/src/lsp/LspProcessManager.js +18 -12
- package/src/utils/editorState.js +42 -0
- package/src/utils/testResultsCache.js +70 -0
- package/src/handlers/input/InputGamepadSimulateToolHandler.js +0 -116
- package/src/handlers/input/InputKeyboardSimulateToolHandler.js +0 -79
- package/src/handlers/input/InputMouseSimulateToolHandler.js +0 -107
- package/src/handlers/input/InputTouchSimulateToolHandler.js +0 -142
|
@@ -1,178 +1,181 @@
|
|
|
1
|
-
import process from 'node:process'
|
|
2
|
-
import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js'
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
|
|
4
|
-
const HEADER_END = '\r\n\r\n'
|
|
5
|
-
const HEADER_RE = /Content-Length:\s*(\d+)/i
|
|
6
|
-
const DEFAULT_BUFFER = Buffer.alloc(0)
|
|
4
|
+
const HEADER_END = '\r\n\r\n';
|
|
5
|
+
const HEADER_RE = /Content-Length:\s*(\d+)/i;
|
|
6
|
+
const DEFAULT_BUFFER = Buffer.alloc(0);
|
|
7
7
|
|
|
8
8
|
function encodeContentLength(message) {
|
|
9
|
-
const json = JSON.stringify(message)
|
|
10
|
-
const header = `Content-Length: ${Buffer.byteLength(json, 'utf8')}${HEADER_END}
|
|
11
|
-
return header + json
|
|
9
|
+
const json = JSON.stringify(message);
|
|
10
|
+
const header = `Content-Length: ${Buffer.byteLength(json, 'utf8')}${HEADER_END}`;
|
|
11
|
+
return header + json;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function encodeNdjson(message) {
|
|
15
|
-
return `${JSON.stringify(message)}\n
|
|
15
|
+
return `${JSON.stringify(message)}\n`;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function parseJson(text) {
|
|
19
|
-
return JSONRPCMessageSchema.parse(JSON.parse(text))
|
|
19
|
+
return JSONRPCMessageSchema.parse(JSON.parse(text));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export class HybridStdioServerTransport {
|
|
23
23
|
constructor(stdin = process.stdin, stdout = process.stdout) {
|
|
24
|
-
this._stdin = stdin
|
|
25
|
-
this._stdout = stdout
|
|
26
|
-
this._buffer = DEFAULT_BUFFER
|
|
27
|
-
this._started = false
|
|
28
|
-
this._mode = null // 'content-length' | 'ndjson'
|
|
24
|
+
this._stdin = stdin;
|
|
25
|
+
this._stdout = stdout;
|
|
26
|
+
this._buffer = DEFAULT_BUFFER;
|
|
27
|
+
this._started = false;
|
|
28
|
+
this._mode = null; // 'content-length' | 'ndjson'
|
|
29
29
|
|
|
30
30
|
this._onData = chunk => {
|
|
31
|
-
this._buffer = this._buffer.length
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
this._buffer = this._buffer.length
|
|
32
|
+
? Buffer.concat([this._buffer, chunk])
|
|
33
|
+
: Buffer.from(chunk);
|
|
34
|
+
this._processBuffer();
|
|
35
|
+
};
|
|
34
36
|
|
|
35
37
|
this._onError = error => {
|
|
36
|
-
this.onerror?.(error)
|
|
37
|
-
}
|
|
38
|
+
this.onerror?.(error);
|
|
39
|
+
};
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
get framingMode() {
|
|
41
|
-
return this._mode
|
|
43
|
+
return this._mode;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
async start() {
|
|
45
47
|
if (this._started) {
|
|
46
|
-
throw new Error('HybridStdioServerTransport already started')
|
|
48
|
+
throw new Error('HybridStdioServerTransport already started');
|
|
47
49
|
}
|
|
48
|
-
this._started = true
|
|
49
|
-
this._stdin.on('data', this._onData)
|
|
50
|
-
this._stdin.on('error', this._onError)
|
|
50
|
+
this._started = true;
|
|
51
|
+
this._stdin.on('data', this._onData);
|
|
52
|
+
this._stdin.on('error', this._onError);
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
async close() {
|
|
54
|
-
if (!this._started) return
|
|
55
|
-
this._stdin.off('data', this._onData)
|
|
56
|
-
this._stdin.off('error', this._onError)
|
|
57
|
-
this._buffer = DEFAULT_BUFFER
|
|
58
|
-
this._started = false
|
|
59
|
-
this.onclose?.()
|
|
56
|
+
if (!this._started) return;
|
|
57
|
+
this._stdin.off('data', this._onData);
|
|
58
|
+
this._stdin.off('error', this._onError);
|
|
59
|
+
this._buffer = DEFAULT_BUFFER;
|
|
60
|
+
this._started = false;
|
|
61
|
+
this.onclose?.();
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
send(message) {
|
|
63
65
|
return new Promise(resolve => {
|
|
64
|
-
const payload =
|
|
66
|
+
const payload =
|
|
67
|
+
this._mode === 'ndjson' ? encodeNdjson(message) : encodeContentLength(message);
|
|
65
68
|
if (this._stdout.write(payload)) {
|
|
66
|
-
resolve()
|
|
69
|
+
resolve();
|
|
67
70
|
} else {
|
|
68
|
-
this._stdout.once('drain', resolve)
|
|
71
|
+
this._stdout.once('drain', resolve);
|
|
69
72
|
}
|
|
70
|
-
})
|
|
73
|
+
});
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
_processBuffer() {
|
|
74
77
|
while (true) {
|
|
75
|
-
const message = this._readMessage()
|
|
78
|
+
const message = this._readMessage();
|
|
76
79
|
if (message === null) {
|
|
77
|
-
break
|
|
80
|
+
break;
|
|
78
81
|
}
|
|
79
|
-
this.onmessage?.(message)
|
|
82
|
+
this.onmessage?.(message);
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
_readMessage() {
|
|
84
87
|
if (!this._buffer || this._buffer.length === 0) {
|
|
85
|
-
return null
|
|
88
|
+
return null;
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
if (this._mode === 'content-length') {
|
|
89
|
-
return this._readContentLengthMessage()
|
|
92
|
+
return this._readContentLengthMessage();
|
|
90
93
|
}
|
|
91
94
|
if (this._mode === 'ndjson') {
|
|
92
|
-
return this._readNdjsonMessage()
|
|
95
|
+
return this._readNdjsonMessage();
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
const prefix = this._peekPrefix()
|
|
98
|
+
const prefix = this._peekPrefix();
|
|
96
99
|
if (!prefix.length) {
|
|
97
|
-
return null
|
|
100
|
+
return null;
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
if ('content-length:'.startsWith(prefix.toLowerCase())) {
|
|
101
|
-
return null // Wait for full header keyword before deciding
|
|
104
|
+
return null; // Wait for full header keyword before deciding
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
if (prefix.toLowerCase().startsWith('content-length:')) {
|
|
105
|
-
this._mode = 'content-length'
|
|
106
|
-
return this._readContentLengthMessage()
|
|
108
|
+
this._mode = 'content-length';
|
|
109
|
+
return this._readContentLengthMessage();
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
const newlineIndex = this._buffer.indexOf(0x0a) // '\n'
|
|
112
|
+
const newlineIndex = this._buffer.indexOf(0x0a); // '\n'
|
|
110
113
|
if (newlineIndex === -1) {
|
|
111
|
-
return null
|
|
114
|
+
return null;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
|
-
this._mode = 'ndjson'
|
|
115
|
-
return this._readNdjsonMessage()
|
|
117
|
+
this._mode = 'ndjson';
|
|
118
|
+
return this._readNdjsonMessage();
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
_peekPrefix() {
|
|
119
|
-
const length = Math.min(this._buffer.length, 32)
|
|
120
|
-
return this._buffer.toString('utf8', 0, length).trimStart()
|
|
122
|
+
const length = Math.min(this._buffer.length, 32);
|
|
123
|
+
return this._buffer.toString('utf8', 0, length).trimStart();
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
_readContentLengthMessage() {
|
|
124
|
-
const headerEndIndex = this._buffer.indexOf(HEADER_END)
|
|
127
|
+
const headerEndIndex = this._buffer.indexOf(HEADER_END);
|
|
125
128
|
if (headerEndIndex === -1) {
|
|
126
|
-
return null
|
|
129
|
+
return null;
|
|
127
130
|
}
|
|
128
131
|
|
|
129
|
-
const header = this._buffer.toString('utf8', 0, headerEndIndex)
|
|
130
|
-
const match = header.match(HEADER_RE)
|
|
132
|
+
const header = this._buffer.toString('utf8', 0, headerEndIndex);
|
|
133
|
+
const match = header.match(HEADER_RE);
|
|
131
134
|
if (!match) {
|
|
132
|
-
this._buffer = this._buffer.subarray(headerEndIndex + HEADER_END.length)
|
|
133
|
-
this.onerror?.(new Error('Invalid Content-Length header'))
|
|
134
|
-
return null
|
|
135
|
+
this._buffer = this._buffer.subarray(headerEndIndex + HEADER_END.length);
|
|
136
|
+
this.onerror?.(new Error('Invalid Content-Length header'));
|
|
137
|
+
return null;
|
|
135
138
|
}
|
|
136
139
|
|
|
137
|
-
const length = Number(match[1])
|
|
138
|
-
const totalMessageLength = headerEndIndex + HEADER_END.length + length
|
|
140
|
+
const length = Number(match[1]);
|
|
141
|
+
const totalMessageLength = headerEndIndex + HEADER_END.length + length;
|
|
139
142
|
if (this._buffer.length < totalMessageLength) {
|
|
140
|
-
return null
|
|
143
|
+
return null;
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
const json = this._buffer.toString(
|
|
144
147
|
'utf8',
|
|
145
148
|
headerEndIndex + HEADER_END.length,
|
|
146
149
|
totalMessageLength
|
|
147
|
-
)
|
|
148
|
-
this._buffer = this._buffer.subarray(totalMessageLength)
|
|
150
|
+
);
|
|
151
|
+
this._buffer = this._buffer.subarray(totalMessageLength);
|
|
149
152
|
|
|
150
153
|
try {
|
|
151
|
-
return parseJson(json)
|
|
154
|
+
return parseJson(json);
|
|
152
155
|
} catch (error) {
|
|
153
|
-
this.onerror?.(error)
|
|
154
|
-
return null
|
|
156
|
+
this.onerror?.(error);
|
|
157
|
+
return null;
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
_readNdjsonMessage() {
|
|
159
162
|
while (true) {
|
|
160
|
-
const newlineIndex = this._buffer.indexOf(0x0a)
|
|
163
|
+
const newlineIndex = this._buffer.indexOf(0x0a);
|
|
161
164
|
if (newlineIndex === -1) {
|
|
162
|
-
return null
|
|
165
|
+
return null;
|
|
163
166
|
}
|
|
164
167
|
|
|
165
|
-
let line = this._buffer.toString('utf8', 0, newlineIndex)
|
|
166
|
-
this._buffer = this._buffer.subarray(newlineIndex + 1)
|
|
167
|
-
line = line.trim()
|
|
168
|
+
let line = this._buffer.toString('utf8', 0, newlineIndex);
|
|
169
|
+
this._buffer = this._buffer.subarray(newlineIndex + 1);
|
|
170
|
+
line = line.trim();
|
|
168
171
|
if (!line) {
|
|
169
|
-
continue
|
|
172
|
+
continue;
|
|
170
173
|
}
|
|
171
174
|
|
|
172
175
|
try {
|
|
173
|
-
return parseJson(line)
|
|
176
|
+
return parseJson(line);
|
|
174
177
|
} catch (error) {
|
|
175
|
-
this.onerror?.(error)
|
|
178
|
+
this.onerror?.(error);
|
|
176
179
|
}
|
|
177
180
|
}
|
|
178
181
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseToolHandler } from '../base/BaseToolHandler.js'
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Addressables Analysis Tool Handler for Unity MCP
|
|
@@ -38,51 +38,51 @@ export default class AddressablesAnalyzeToolHandler extends BaseToolHandler {
|
|
|
38
38
|
},
|
|
39
39
|
required: ['action']
|
|
40
40
|
}
|
|
41
|
-
)
|
|
42
|
-
this.unityConnection = unityConnection
|
|
41
|
+
);
|
|
42
|
+
this.unityConnection = unityConnection;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
validate(params) {
|
|
46
|
-
const { action, assetPath } = params || {}
|
|
46
|
+
const { action, assetPath } = params || {};
|
|
47
47
|
|
|
48
48
|
if (!action) {
|
|
49
|
-
throw new Error('action is required')
|
|
49
|
+
throw new Error('action is required');
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const validActions = ['analyze_duplicates', 'analyze_dependencies', 'analyze_unused']
|
|
52
|
+
const validActions = ['analyze_duplicates', 'analyze_dependencies', 'analyze_unused'];
|
|
53
53
|
if (!validActions.includes(action)) {
|
|
54
|
-
throw new Error(`Invalid action: ${action}. Must be one of: ${validActions.join(', ')}`)
|
|
54
|
+
throw new Error(`Invalid action: ${action}. Must be one of: ${validActions.join(', ')}`);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// Action-specific validation
|
|
58
58
|
if (action === 'analyze_dependencies' && !assetPath) {
|
|
59
|
-
throw new Error('assetPath is required for analyze_dependencies')
|
|
59
|
+
throw new Error('assetPath is required for analyze_dependencies');
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
async execute(params) {
|
|
64
|
-
const { action, ...parameters } = params
|
|
64
|
+
const { action, ...parameters } = params;
|
|
65
65
|
|
|
66
66
|
// Ensure connected
|
|
67
67
|
if (!this.unityConnection.isConnected()) {
|
|
68
|
-
await this.unityConnection.connect()
|
|
68
|
+
await this.unityConnection.connect();
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const result = await this.unityConnection.sendCommand('addressables_analyze', {
|
|
72
72
|
action,
|
|
73
73
|
...parameters
|
|
74
|
-
})
|
|
74
|
+
});
|
|
75
75
|
|
|
76
|
-
return this.formatResponse(action, result)
|
|
76
|
+
return this.formatResponse(action, result);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
formatResponse(action, result) {
|
|
80
80
|
if (result && result.error) {
|
|
81
|
-
throw new Error(result.error.message || result.error)
|
|
81
|
+
throw new Error(result.error.message || result.error);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
if (!result || typeof result !== 'object') {
|
|
85
|
-
throw new Error('Invalid response from Unity')
|
|
85
|
+
throw new Error('Invalid response from Unity');
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// Return formatted response
|
|
@@ -93,88 +93,86 @@ export default class AddressablesAnalyzeToolHandler extends BaseToolHandler {
|
|
|
93
93
|
text: this.formatResultText(action, result)
|
|
94
94
|
}
|
|
95
95
|
]
|
|
96
|
-
}
|
|
96
|
+
};
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
formatResultText(action, result) {
|
|
100
|
-
const lines = []
|
|
100
|
+
const lines = [];
|
|
101
101
|
|
|
102
102
|
switch (action) {
|
|
103
103
|
case 'analyze_duplicates':
|
|
104
|
-
lines.push('🔍 重複アセット分析結果')
|
|
104
|
+
lines.push('🔍 重複アセット分析結果');
|
|
105
105
|
if (result.data && result.data.duplicates) {
|
|
106
106
|
if (result.data.duplicates.length === 0) {
|
|
107
|
-
lines.push(' ✅ 重複アセットは見つかりませんでした')
|
|
107
|
+
lines.push(' ✅ 重複アセットは見つかりませんでした');
|
|
108
108
|
} else {
|
|
109
|
-
lines.push(` ⚠️ 重複アセット: ${result.pagination.total}件`)
|
|
109
|
+
lines.push(` ⚠️ 重複アセット: ${result.pagination.total}件`);
|
|
110
110
|
result.data.duplicates.forEach(dup => {
|
|
111
|
-
lines.push(`\n 📁 ${dup.assetPath}`)
|
|
112
|
-
lines.push(` 使用グループ: ${dup.groups.join(', ')}`)
|
|
113
|
-
})
|
|
111
|
+
lines.push(`\n 📁 ${dup.assetPath}`);
|
|
112
|
+
lines.push(` 使用グループ: ${dup.groups.join(', ')}`);
|
|
113
|
+
});
|
|
114
114
|
if (result.pagination.hasMore) {
|
|
115
115
|
lines.push(
|
|
116
116
|
`\n ... さらに${result.pagination.total - result.pagination.offset - result.pagination.pageSize}件あります`
|
|
117
|
-
)
|
|
117
|
+
);
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
break
|
|
121
|
+
break;
|
|
122
122
|
|
|
123
123
|
case 'analyze_dependencies':
|
|
124
|
-
lines.push('🔍 依存関係分析結果')
|
|
124
|
+
lines.push('🔍 依存関係分析結果');
|
|
125
125
|
if (result.data && result.data.dependencies) {
|
|
126
|
-
const deps = Object.entries(result.data.dependencies)
|
|
126
|
+
const deps = Object.entries(result.data.dependencies);
|
|
127
127
|
if (deps.length === 0) {
|
|
128
|
-
lines.push(' ✅ 依存関係がありません')
|
|
128
|
+
lines.push(' ✅ 依存関係がありません');
|
|
129
129
|
} else {
|
|
130
130
|
deps.forEach(([assetPath, dependencies]) => {
|
|
131
|
-
lines.push(`\n 📁 ${assetPath}`)
|
|
131
|
+
lines.push(`\n 📁 ${assetPath}`);
|
|
132
132
|
if (dependencies.length === 0) {
|
|
133
|
-
lines.push(' 依存なし')
|
|
133
|
+
lines.push(' 依存なし');
|
|
134
134
|
} else {
|
|
135
|
-
lines.push(` 依存数: ${dependencies.length}個`)
|
|
135
|
+
lines.push(` 依存数: ${dependencies.length}個`);
|
|
136
136
|
dependencies.forEach((dep, idx) => {
|
|
137
137
|
if (idx < 10) {
|
|
138
|
-
lines.push(` → ${dep}`)
|
|
138
|
+
lines.push(` → ${dep}`);
|
|
139
139
|
}
|
|
140
|
-
})
|
|
140
|
+
});
|
|
141
141
|
if (dependencies.length > 10) {
|
|
142
|
-
lines.push(` ... 他${dependencies.length - 10}件`)
|
|
142
|
+
lines.push(` ... 他${dependencies.length - 10}件`);
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
-
})
|
|
145
|
+
});
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
-
break
|
|
148
|
+
break;
|
|
149
149
|
|
|
150
150
|
case 'analyze_unused':
|
|
151
|
-
lines.push('🔍 未使用アセット分析結果')
|
|
151
|
+
lines.push('🔍 未使用アセット分析結果');
|
|
152
152
|
if (result.data && result.data.unused) {
|
|
153
153
|
if (result.data.unused.length === 0) {
|
|
154
|
-
lines.push(' ✅ すべてのアセットが使用されています')
|
|
154
|
+
lines.push(' ✅ すべてのアセットが使用されています');
|
|
155
155
|
} else {
|
|
156
|
-
lines.push(` ⚠️ 未使用アセット: ${result.pagination.total}件`)
|
|
156
|
+
lines.push(` ⚠️ 未使用アセット: ${result.pagination.total}件`);
|
|
157
157
|
result.data.unused.forEach(path => {
|
|
158
|
-
lines.push(` 📁 ${path}`)
|
|
159
|
-
})
|
|
158
|
+
lines.push(` 📁 ${path}`);
|
|
159
|
+
});
|
|
160
160
|
if (result.pagination.hasMore) {
|
|
161
161
|
lines.push(
|
|
162
162
|
`\n ... さらに${result.pagination.total - result.pagination.offset - result.pagination.pageSize}件あります`
|
|
163
|
-
)
|
|
163
|
+
);
|
|
164
164
|
}
|
|
165
165
|
lines.push(
|
|
166
166
|
'\n 💡 これらのアセットはAddressableとして登録されておらず、他のAddressableからも参照されていません'
|
|
167
|
-
)
|
|
167
|
+
);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
break
|
|
170
|
+
break;
|
|
171
171
|
|
|
172
172
|
default:
|
|
173
|
-
lines.push(JSON.stringify(result, null, 2))
|
|
173
|
+
lines.push(JSON.stringify(result, null, 2));
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
return lines.join('\n')
|
|
176
|
+
return lines.join('\n');
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
-
|
|
180
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseToolHandler } from '../base/BaseToolHandler.js'
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Addressables Build Tool Handler for Unity MCP
|
|
@@ -29,20 +29,20 @@ export default class AddressablesBuildToolHandler extends BaseToolHandler {
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
required: ['action']
|
|
32
|
-
})
|
|
33
|
-
this.unityConnection = unityConnection
|
|
32
|
+
});
|
|
33
|
+
this.unityConnection = unityConnection;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
validate(params) {
|
|
37
|
-
const { action, buildTarget } = params || {}
|
|
37
|
+
const { action, buildTarget } = params || {};
|
|
38
38
|
|
|
39
39
|
if (!action) {
|
|
40
|
-
throw new Error('action is required')
|
|
40
|
+
throw new Error('action is required');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const validActions = ['build', 'clean_build']
|
|
43
|
+
const validActions = ['build', 'clean_build'];
|
|
44
44
|
if (!validActions.includes(action)) {
|
|
45
|
-
throw new Error(`Invalid action: ${action}. Must be one of: ${validActions.join(', ')}`)
|
|
45
|
+
throw new Error(`Invalid action: ${action}. Must be one of: ${validActions.join(', ')}`);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
if (buildTarget) {
|
|
@@ -54,25 +54,25 @@ export default class AddressablesBuildToolHandler extends BaseToolHandler {
|
|
|
54
54
|
'iOS',
|
|
55
55
|
'Android',
|
|
56
56
|
'WebGL'
|
|
57
|
-
]
|
|
57
|
+
];
|
|
58
58
|
if (!validTargets.includes(buildTarget)) {
|
|
59
59
|
throw new Error(
|
|
60
60
|
`Invalid buildTarget: ${buildTarget}. Must be one of: ${validTargets.join(', ')}`
|
|
61
|
-
)
|
|
61
|
+
);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async execute(params) {
|
|
67
|
-
const { action, ...parameters } = params
|
|
67
|
+
const { action, ...parameters } = params;
|
|
68
68
|
|
|
69
69
|
// Ensure connected
|
|
70
70
|
if (!this.unityConnection.isConnected()) {
|
|
71
|
-
await this.unityConnection.connect()
|
|
71
|
+
await this.unityConnection.connect();
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Build operations can take several minutes
|
|
75
|
-
const timeout = 300000 // 5 minutes
|
|
75
|
+
const timeout = 300000; // 5 minutes
|
|
76
76
|
|
|
77
77
|
const result = await this.unityConnection.sendCommand(
|
|
78
78
|
'addressables_build',
|
|
@@ -81,18 +81,18 @@ export default class AddressablesBuildToolHandler extends BaseToolHandler {
|
|
|
81
81
|
...parameters
|
|
82
82
|
},
|
|
83
83
|
timeout
|
|
84
|
-
)
|
|
84
|
+
);
|
|
85
85
|
|
|
86
|
-
return this.formatResponse(action, result)
|
|
86
|
+
return this.formatResponse(action, result);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
formatResponse(action, result) {
|
|
90
90
|
if (result && result.error) {
|
|
91
|
-
throw new Error(result.error.message || result.error)
|
|
91
|
+
throw new Error(result.error.message || result.error);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
if (!result || typeof result !== 'object') {
|
|
95
|
-
throw new Error('Invalid response from Unity')
|
|
95
|
+
throw new Error('Invalid response from Unity');
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
// Return formatted response
|
|
@@ -103,44 +103,43 @@ export default class AddressablesBuildToolHandler extends BaseToolHandler {
|
|
|
103
103
|
text: this.formatResultText(action, result)
|
|
104
104
|
}
|
|
105
105
|
]
|
|
106
|
-
}
|
|
106
|
+
};
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
formatResultText(action, result) {
|
|
110
|
-
const lines = []
|
|
110
|
+
const lines = [];
|
|
111
111
|
|
|
112
112
|
switch (action) {
|
|
113
113
|
case 'build':
|
|
114
114
|
if (result.data) {
|
|
115
115
|
if (result.data.success) {
|
|
116
|
-
lines.push('✅ Addressablesビルドが成功しました')
|
|
117
|
-
lines.push(` ⏱️ 所要時間: ${result.data.duration.toFixed(2)}秒`)
|
|
116
|
+
lines.push('✅ Addressablesビルドが成功しました');
|
|
117
|
+
lines.push(` ⏱️ 所要時間: ${result.data.duration.toFixed(2)}秒`);
|
|
118
118
|
if (result.data.outputPath) {
|
|
119
|
-
lines.push(` 📁 出力先: ${result.data.outputPath}`)
|
|
119
|
+
lines.push(` 📁 出力先: ${result.data.outputPath}`);
|
|
120
120
|
}
|
|
121
121
|
} else {
|
|
122
|
-
lines.push('❌ Addressablesビルドが失敗しました')
|
|
123
|
-
lines.push(` ⏱️ 所要時間: ${result.data.duration.toFixed(2)}秒`)
|
|
122
|
+
lines.push('❌ Addressablesビルドが失敗しました');
|
|
123
|
+
lines.push(` ⏱️ 所要時間: ${result.data.duration.toFixed(2)}秒`);
|
|
124
124
|
if (result.data.errors && result.data.errors.length > 0) {
|
|
125
|
-
lines.push('\n エラー:')
|
|
125
|
+
lines.push('\n エラー:');
|
|
126
126
|
result.data.errors.forEach(error => {
|
|
127
|
-
lines.push(` ❌ ${error}`)
|
|
128
|
-
})
|
|
127
|
+
lines.push(` ❌ ${error}`);
|
|
128
|
+
});
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
-
break
|
|
132
|
+
break;
|
|
133
133
|
|
|
134
134
|
case 'clean_build':
|
|
135
|
-
lines.push(`✅ ${result.message}`)
|
|
136
|
-
lines.push(' ビルドキャッシュをクリアしました。次回ビルドは完全ビルドになります。')
|
|
137
|
-
break
|
|
135
|
+
lines.push(`✅ ${result.message}`);
|
|
136
|
+
lines.push(' ビルドキャッシュをクリアしました。次回ビルドは完全ビルドになります。');
|
|
137
|
+
break;
|
|
138
138
|
|
|
139
139
|
default:
|
|
140
|
-
lines.push(JSON.stringify(result, null, 2))
|
|
140
|
+
lines.push(JSON.stringify(result, null, 2));
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
return lines.join('\n')
|
|
143
|
+
return lines.join('\n');
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
|