@akiojin/unity-mcp-server 2.43.1 → 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.1",
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
 
@@ -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
- }