@akiojin/unity-mcp-server 2.43.2 → 2.43.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akiojin/unity-mcp-server",
3
- "version": "2.43.2",
3
+ "version": "2.43.3",
4
4
  "description": "MCP server and Unity Editor bridge — enables AI assistants to control Unity for AI-assisted workflows",
5
5
  "type": "module",
6
6
  "main": "src/core/server.js",
@@ -10,7 +10,7 @@ import { UnityConnection } from './unityConnection.js';
10
10
  import { createHandlers } from '../handlers/index.js';
11
11
  import { config, logger } from './config.js';
12
12
  import { IndexWatcher } from './indexWatcher.js';
13
- import { HybridStdioServerTransport } from './transports/HybridStdioServerTransport.js';
13
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
14
 
15
15
  // Create Unity connection
16
16
  const unityConnection = new UnityConnection();
@@ -189,7 +189,7 @@ export async function startServer(options = {}) {
189
189
  let transport;
190
190
  if (runtimeConfig.stdioEnabled !== false) {
191
191
  console.error(`[unity-mcp-server] MCP transport connecting...`);
192
- transport = new HybridStdioServerTransport();
192
+ transport = new StdioServerTransport();
193
193
  await server.connect(transport);
194
194
  console.error(`[unity-mcp-server] MCP transport connected`);
195
195
 
@@ -261,41 +261,40 @@ export async function startServer(options = {}) {
261
261
  process.on('SIGTERM', stopWatch);
262
262
 
263
263
  // Auto-initialize code index if DB doesn't exist
264
- // DISABLED: Investigating Issue #168 - npx timeout with Claude Code
265
- // (async () => {
266
- // try {
267
- // const { CodeIndex } = await import('./codeIndex.js');
268
- // const index = new CodeIndex(unityConnection);
269
- // const ready = await index.isReady();
270
-
271
- // if (!ready) {
272
- // if (index.disabled) {
273
- // logger.warning(
274
- // `[startup] Code index disabled: ${index.disableReason || 'SQLite native binding missing'}. Skipping auto-build.`
275
- // );
276
- // return;
277
- // }
278
- // logger.info('[startup] Code index DB not ready. Starting auto-build...');
279
- // const { CodeIndexBuildToolHandler } = await import(
280
- // '../handlers/script/CodeIndexBuildToolHandler.js'
281
- // );
282
- // const builder = new CodeIndexBuildToolHandler(unityConnection);
283
- // const result = await builder.execute({});
284
-
285
- // if (result.success) {
286
- // logger.info(
287
- // `[startup] Code index auto-build started: jobId=${result.jobId}. Use code_index_status to check progress.`
288
- // );
289
- // } else {
290
- // logger.warning(`[startup] Code index auto-build failed: ${result.message}`);
291
- // }
292
- // } else {
293
- // logger.info('[startup] Code index DB already exists. Skipping auto-build.');
294
- // }
295
- // } catch (e) {
296
- // logger.warning(`[startup] Code index auto-init failed: ${e.message}`);
297
- // }
298
- // })();
264
+ (async () => {
265
+ try {
266
+ const { CodeIndex } = await import('./codeIndex.js');
267
+ const index = new CodeIndex(unityConnection);
268
+ const ready = await index.isReady();
269
+
270
+ if (!ready) {
271
+ if (index.disabled) {
272
+ logger.warning(
273
+ `[startup] Code index disabled: ${index.disableReason || 'SQLite native binding missing'}. Skipping auto-build.`
274
+ );
275
+ return;
276
+ }
277
+ logger.info('[startup] Code index DB not ready. Starting auto-build...');
278
+ const { CodeIndexBuildToolHandler } = await import(
279
+ '../handlers/script/CodeIndexBuildToolHandler.js'
280
+ );
281
+ const builder = new CodeIndexBuildToolHandler(unityConnection);
282
+ const result = await builder.execute({});
283
+
284
+ if (result.success) {
285
+ logger.info(
286
+ `[startup] Code index auto-build started: jobId=${result.jobId}. Use code_index_status to check progress.`
287
+ );
288
+ } else {
289
+ logger.warning(`[startup] Code index auto-build failed: ${result.message}`);
290
+ }
291
+ } else {
292
+ logger.info('[startup] Code index DB already exists. Skipping auto-build.');
293
+ }
294
+ } catch (e) {
295
+ logger.warning(`[startup] Code index auto-init failed: ${e.message}`);
296
+ }
297
+ })();
299
298
 
300
299
  // Handle shutdown
301
300
  process.on('SIGINT', async () => {
@@ -1,191 +0,0 @@
1
- import process from 'node:process';
2
- import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
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);
7
-
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;
12
- }
13
-
14
- function parseJson(text) {
15
- return JSONRPCMessageSchema.parse(JSON.parse(text));
16
- }
17
-
18
- export class HybridStdioServerTransport {
19
- constructor(stdin = process.stdin, stdout = process.stdout) {
20
- this._stdin = stdin;
21
- this._stdout = stdout;
22
- this._buffer = DEFAULT_BUFFER;
23
- this._started = false;
24
- this._mode = null; // 'content-length' | 'ndjson'
25
-
26
- this._onData = chunk => {
27
- this._buffer = this._buffer.length
28
- ? Buffer.concat([this._buffer, chunk])
29
- : Buffer.from(chunk);
30
- this._processBuffer();
31
- };
32
-
33
- this._onError = error => {
34
- this.onerror?.(error);
35
- };
36
- }
37
-
38
- get framingMode() {
39
- return this._mode;
40
- }
41
-
42
- async start() {
43
- if (this._started) {
44
- throw new Error('HybridStdioServerTransport already started');
45
- }
46
- this._started = true;
47
- this._stdin.on('data', this._onData);
48
- this._stdin.on('error', this._onError);
49
- }
50
-
51
- async close() {
52
- if (!this._started) return;
53
- this._stdin.off('data', this._onData);
54
- this._stdin.off('error', this._onError);
55
- this._buffer = DEFAULT_BUFFER;
56
- this._started = false;
57
- this.onclose?.();
58
- }
59
-
60
- send(message) {
61
- return new Promise(resolve => {
62
- // Always use Content-Length framing for output (MCP protocol standard)
63
- // Input remains hybrid (Content-Length or NDJSON) for client compatibility
64
- const payload = encodeContentLength(message);
65
- if (this._stdout.write(payload)) {
66
- resolve();
67
- } else {
68
- this._stdout.once('drain', resolve);
69
- }
70
- });
71
- }
72
-
73
- _processBuffer() {
74
- while (true) {
75
- const message = this._readMessage();
76
- if (message === null) {
77
- break;
78
- }
79
- this.onmessage?.(message);
80
- }
81
- }
82
-
83
- _readMessage() {
84
- if (!this._buffer || this._buffer.length === 0) {
85
- return null;
86
- }
87
-
88
- if (this._mode === 'content-length') {
89
- return this._readContentLengthMessage();
90
- }
91
- if (this._mode === 'ndjson') {
92
- return this._readNdjsonMessage();
93
- }
94
-
95
- const prefix = this._peekPrefix();
96
- if (!prefix.length) {
97
- return null;
98
- }
99
-
100
- if ('content-length:'.startsWith(prefix.toLowerCase())) {
101
- return null; // Wait for full header keyword before deciding
102
- }
103
-
104
- if (prefix.toLowerCase().startsWith('content-length:')) {
105
- this._mode = 'content-length';
106
- return this._readContentLengthMessage();
107
- }
108
-
109
- const newlineIndex = this._buffer.indexOf(0x0a); // '\n'
110
- if (newlineIndex === -1) {
111
- return null;
112
- }
113
-
114
- this._mode = 'ndjson';
115
- return this._readNdjsonMessage();
116
- }
117
-
118
- _peekPrefix() {
119
- const length = Math.min(this._buffer.length, 32);
120
- return this._buffer.toString('utf8', 0, length).trimStart();
121
- }
122
-
123
- _readContentLengthMessage() {
124
- const { headerEndIndex, separatorLength } = (() => {
125
- const crlfIndex = this._buffer.indexOf('\r\n\r\n');
126
- const lfIndex = this._buffer.indexOf('\n\n');
127
-
128
- if (crlfIndex === -1 && lfIndex === -1) return { headerEndIndex: -1, separatorLength: 0 };
129
- if (crlfIndex === -1) return { headerEndIndex: lfIndex, separatorLength: 2 };
130
- if (lfIndex === -1) return { headerEndIndex: crlfIndex, separatorLength: 4 };
131
-
132
- return crlfIndex < lfIndex
133
- ? { headerEndIndex: crlfIndex, separatorLength: 4 }
134
- : { headerEndIndex: lfIndex, separatorLength: 2 };
135
- })();
136
-
137
- if (headerEndIndex === -1) {
138
- return null;
139
- }
140
-
141
- const header = this._buffer.toString('utf8', 0, headerEndIndex);
142
- const match = header.match(HEADER_RE);
143
- if (!match) {
144
- this._buffer = this._buffer.subarray(headerEndIndex + separatorLength);
145
- this.onerror?.(new Error('Invalid Content-Length header'));
146
- return null;
147
- }
148
-
149
- const length = Number(match[1]);
150
- const totalMessageLength = headerEndIndex + separatorLength + length;
151
- if (this._buffer.length < totalMessageLength) {
152
- return null;
153
- }
154
-
155
- const json = this._buffer.toString(
156
- 'utf8',
157
- headerEndIndex + separatorLength,
158
- totalMessageLength
159
- );
160
- this._buffer = this._buffer.subarray(totalMessageLength);
161
-
162
- try {
163
- return parseJson(json);
164
- } catch (error) {
165
- this.onerror?.(error);
166
- return null;
167
- }
168
- }
169
-
170
- _readNdjsonMessage() {
171
- while (true) {
172
- const newlineIndex = this._buffer.indexOf(0x0a);
173
- if (newlineIndex === -1) {
174
- return null;
175
- }
176
-
177
- let line = this._buffer.toString('utf8', 0, newlineIndex);
178
- this._buffer = this._buffer.subarray(newlineIndex + 1);
179
- line = line.trim();
180
- if (!line) {
181
- continue;
182
- }
183
-
184
- try {
185
- return parseJson(line);
186
- } catch (error) {
187
- this.onerror?.(error);
188
- }
189
- }
190
- }
191
- }