Skip to content

Commit 876acb9

Browse files
committed
Replace reading of server settings via system env vars with a config file to bolster cross-platformability.
1 parent 2867edf commit 876acb9

File tree

8 files changed

+104
-134
lines changed

8 files changed

+104
-134
lines changed

Editor/UnityBridge/McpUnitySettings.cs

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,6 @@ public void LoadSettings()
7272
string json = File.ReadAllText(SettingsPath);
7373
JsonUtility.FromJsonOverwrite(json, this);
7474
}
75-
76-
// Check for environment variable PORT
77-
string envPort = System.Environment.GetEnvironmentVariable(EnvUnityPort);
78-
if (!string.IsNullOrEmpty(envPort) && int.TryParse(envPort, out int port))
79-
{
80-
Port = port;
81-
}
82-
string envTimeout = System.Environment.GetEnvironmentVariable(EnvUnityRequestTimeout);
83-
if (!string.IsNullOrEmpty(envTimeout) && int.TryParse(envTimeout, out int timeout))
84-
{
85-
RequestTimeoutSeconds = timeout;
86-
}
8775
}
8876
catch (Exception ex)
8977
{
@@ -103,39 +91,15 @@ public void SaveSettings()
10391
string json = JsonUtility.ToJson(this, true);
10492
File.WriteAllText(SettingsPath, json);
10593

106-
// Set environment variable PORT for the Node.js process
107-
// EnvironmentVariableTarget.User and EnvironmentVariableTarget.Machine should be used on .NET implementations running on Windows systems only.
108-
// For non-Windows systems, User and Machine are treated as Process.
109-
// Using Process target for broader compatibility.
110-
// see: https://learn.microsoft.com/en-us/dotnet/api/system.environmentvariabletarget?view=net-8.0#remarks
111-
Environment.SetEnvironmentVariable(EnvUnityPort, Port.ToString(), EnvironmentVariableTarget.Process);
112-
Environment.SetEnvironmentVariable(EnvUnityRequestTimeout, RequestTimeoutSeconds.ToString(), EnvironmentVariableTarget.Process);
113-
114-
// For non w32 systems.
115-
SaveSettingsToNodeServer();
94+
// Now save these same settings to the server to read on start-up.
95+
string propertiesPath = GetServerPath() + "/build/McpUnitySettings.json";
96+
File.WriteAllText(propertiesPath, json);
11697
}
11798
catch (Exception ex)
11899
{
119100
// Can't use LoggerService here as it might create circular dependency
120101
Debug.LogError($"[MCP Unity] Failed to save settings: {ex.Message}");
121102
}
122103
}
123-
124-
125-
public void SaveSettingsToNodeServer() {
126-
try
127-
{
128-
string propertiesPath = GetServerPath() + "/build/server.env";
129-
using (var writer = new StreamWriter(propertiesPath, false))
130-
{
131-
writer.WriteLine($"{EnvUnityPort}={Port}");
132-
writer.WriteLine($"{EnvUnityRequestTimeout}={RequestTimeoutSeconds}");
133-
}
134-
}
135-
catch (Exception ex)
136-
{
137-
Debug.LogError($"[MCP Unity] Failed to write server env properties: {ex.Message}");
138-
}
139-
}
140104
}
141105
}

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,20 @@ This error occurs because the bridge connection is lost when the domain reloads
383383
The workaround is to turn off **Reload Domain** in **Edit > Project Settings > Editor > "Enter Play Mode Settings"**.
384384
</details>
385385

386+
## Node Server
387+
These steps can be useful if you want to build and run the server independently of the Unity plugin.
388+
### Requirements
389+
As well as `node`, you will need `tsc`.
390+
### Steps
391+
1. cd into the `Server~/` directory.
392+
2. run `npm install` to install the project.
393+
3. run `npm run build` to build the project.
394+
4. run `node build/index.js` to run the server! 🚀
395+
396+
### Troubleshooting 🔨
397+
#### Logging
398+
Logging is enabled when the environment variable `LOGGING` is set to true. E.g. `export LOGGING=true`.
399+
386400
## Support & Feedback
387401

388402
If you have any questions or need support, please open an [issue](https://github.com/CoderGamester/mcp-unity/issues) on this repository.

Server~/build/McpUnitySettings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"Port": 8090,
3+
"RequestTimeoutSeconds": 10,
4+
"AutoStartServer": true,
5+
"EnableInfoLogs": true
6+
}

Server~/build/unity/mcpUnity.d.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ export declare class McpUnity {
99
private port;
1010
private ws;
1111
private pendingRequests;
12-
private readonly REQUEST_TIMEOUT;
12+
private requestTimeout;
1313
private retryDelay;
1414
constructor(logger: Logger);
1515
/**
1616
* Start the Unity connection
1717
* @param clientName Optional name of the MCP client connecting to Unity
1818
*/
1919
start(clientName?: string): Promise<void>;
20+
/**
21+
* Reads our configuration file and sets parameters of the server based on them.
22+
*/
23+
private parseAndSetConfig;
2024
/**
2125
* Connect to the Unity WebSocket
2226
* @param clientName Optional name of the MCP client connecting to Unity
@@ -48,14 +52,9 @@ export declare class McpUnity {
4852
*/
4953
get isConnected(): boolean;
5054
/**
51-
* Retrieves the UNITY_PORT value from the Windows registry (HKCU\Environment)
52-
* @returns The port value as a string if found, otherwise an empty string
53-
*/
54-
private getUnityPortFromWindowsRegistry;
55-
/**
56-
* Retrieves the UNITY_PORT value from Unix-like system environment variables
57-
* @returns The port value as a string if found, otherwise an empty string
55+
* Read the McpUnitySettings.json file and return its contents as a JSON object.
56+
* @returns a JSON object with the contents of the McpUnitySettings.json file.
5857
*/
59-
private getUnityPortFromUnixRegistry;
58+
private readConfigFileAsJson;
6059
}
6160
export {};

Server~/build/unity/mcpUnity.js

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
11
import WebSocket from 'ws';
22
import { v4 as uuidv4 } from 'uuid';
33
import { McpUnityError, ErrorType } from '../utils/errors.js';
4-
import { execSync } from 'child_process';
5-
import { default as winreg } from 'winreg';
4+
import { promises as fs } from 'fs';
5+
import path from 'path';
66
export class McpUnity {
77
logger;
8-
port;
8+
port = null;
99
ws = null;
1010
pendingRequests = new Map();
11-
REQUEST_TIMEOUT;
11+
requestTimeout = 10000;
1212
retryDelay = 1000;
1313
constructor(logger) {
1414
this.logger = logger;
15-
// Initialize port from environment variable or use default
16-
const envRegistry = process.platform === 'win32'
17-
? this.getUnityPortFromWindowsRegistry()
18-
: this.getUnityPortFromUnixRegistry();
19-
const envPort = process.env.UNITY_PORT || envRegistry;
20-
this.port = envPort ? parseInt(envPort, 10) : 8090;
21-
this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);
22-
// Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
23-
const envTimeout = process.env.UNITY_REQUEST_TIMEOUT;
24-
this.REQUEST_TIMEOUT = envTimeout ? parseInt(envTimeout, 10) * 1000 : 10000;
25-
this.logger.info(`Using request timeout: ${this.REQUEST_TIMEOUT / 1000} seconds`);
2615
}
2716
/**
2817
* Start the Unity connection
2918
* @param clientName Optional name of the MCP client connecting to Unity
3019
*/
3120
async start(clientName) {
3221
try {
22+
this.logger.info('Attempting to read startup parameters...');
23+
this.parseAndSetConfig();
3324
this.logger.info('Attempting to connect to Unity WebSocket...');
3425
await this.connect(clientName); // Pass client name to connect
3526
this.logger.info('Successfully connected to Unity WebSocket');
@@ -45,6 +36,19 @@ export class McpUnity {
4536
}
4637
return Promise.resolve();
4738
}
39+
/**
40+
* Reads our configuration file and sets parameters of the server based on them.
41+
*/
42+
async parseAndSetConfig() {
43+
const config = await this.readConfigFileAsJson();
44+
const configPort = config.Port;
45+
this.port = configPort ? parseInt(configPort, 10) : 8090;
46+
this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);
47+
// Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
48+
const configTimeout = config.RequestTimeoutSeconds;
49+
this.requestTimeout = configTimeout ? parseInt(configTimeout, 10) * 1000 : 10000;
50+
this.logger.info(`Using request timeout: ${this.requestTimeout / 1000} seconds`);
51+
}
4852
/**
4953
* Connect to the Unity WebSocket
5054
* @param clientName Optional name of the MCP client connecting to Unity
@@ -74,7 +78,7 @@ export class McpUnity {
7478
this.disconnect();
7579
reject(new McpUnityError(ErrorType.CONNECTION, 'Connection timeout'));
7680
}
77-
}, this.REQUEST_TIMEOUT);
81+
}, this.requestTimeout);
7882
this.ws.onopen = () => {
7983
clearTimeout(connectionTimeout);
8084
this.logger.debug('WebSocket connected');
@@ -193,12 +197,12 @@ export class McpUnity {
193197
// Create timeout for the request
194198
const timeout = setTimeout(() => {
195199
if (this.pendingRequests.has(requestId)) {
196-
this.logger.error(`Request ${requestId} timed out after ${this.REQUEST_TIMEOUT}ms`);
200+
this.logger.error(`Request ${requestId} timed out after ${this.requestTimeout}ms`);
197201
this.pendingRequests.delete(requestId);
198202
reject(new McpUnityError(ErrorType.TIMEOUT, 'Request timed out'));
199203
}
200204
this.reconnect();
201-
}, this.REQUEST_TIMEOUT);
205+
}, this.requestTimeout);
202206
// Store pending request
203207
this.pendingRequests.set(requestId, {
204208
resolve,
@@ -225,27 +229,20 @@ export class McpUnity {
225229
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
226230
}
227231
/**
228-
* Retrieves the UNITY_PORT value from the Windows registry (HKCU\Environment)
229-
* @returns The port value as a string if found, otherwise an empty string
230-
*/
231-
getUnityPortFromWindowsRegistry() {
232-
const regKey = new winreg({ hive: winreg.HKCU, key: '\\Environment' });
233-
let result = '';
234-
regKey.get('UNITY_PORT', (err, item) => {
235-
if (err) {
236-
this.logger.error(`Error getting registry value: ${err.message}`);
237-
}
238-
else {
239-
result = item.value;
240-
}
241-
});
242-
return result;
243-
}
244-
/**
245-
* Retrieves the UNITY_PORT value from Unix-like system environment variables
246-
* @returns The port value as a string if found, otherwise an empty string
232+
* Read the McpUnitySettings.json file and return its contents as a JSON object.
233+
* @returns a JSON object with the contents of the McpUnitySettings.json file.
247234
*/
248-
getUnityPortFromUnixRegistry() {
249-
return execSync('printenv UNITY_PORT', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim();
235+
async readConfigFileAsJson() {
236+
const configPath = path.resolve(process.cwd(), 'build/McpUnitySettings.json');
237+
this.logger.debug(`Reading McpUnitySettings.json from ${configPath}`);
238+
try {
239+
const content = await fs.readFile(configPath, 'utf-8');
240+
const json = JSON.parse(content);
241+
return json;
242+
}
243+
catch (err) {
244+
this.logger.debug(`McpUnitySettings.json not found or unreadable: ${err instanceof Error ? err.message : String(err)}`);
245+
return {};
246+
}
250247
}
251248
}

Server~/build/utils/errors.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export var ErrorType;
66
ErrorType["VALIDATION"] = "validation_error";
77
ErrorType["INTERNAL"] = "internal_error";
88
ErrorType["TIMEOUT"] = "timeout_error";
9-
})(ErrorType || (ErrorType = {}));
9+
})(ErrorType = ErrorType || (ErrorType = {}));
1010
export class McpUnityError extends Error {
1111
type;
1212
details;

Server~/build/utils/logger.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export var LogLevel;
55
LogLevel[LogLevel["INFO"] = 1] = "INFO";
66
LogLevel[LogLevel["WARN"] = 2] = "WARN";
77
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
8-
})(LogLevel || (LogLevel = {}));
8+
})(LogLevel = LogLevel || (LogLevel = {}));
99
// Check environment variable for logging
1010
const isLoggingEnabled = process.env.LOGGING === 'true';
1111
// Check environment variable for logging in a file

0 commit comments

Comments
 (0)