diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
index dfa62692..ac43b21f 100644
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ b/Editor/UnityBridge/McpUnitySettings.cs
@@ -19,6 +19,9 @@ public class McpUnitySettings
private const string EnvUnityPort = "UNITY_PORT";
private const string EnvUnityRequestTimeout = "UNITY_REQUEST_TIMEOUT";
+ ///
+ /// This file path is also read by the MCP server. Changes here will require updates to it. See mcpUnity.ts
+ ///
private const string SettingsPath = "ProjectSettings/McpUnitySettings.json";
private static McpUnitySettings _instance;
@@ -72,18 +75,6 @@ public void LoadSettings()
string json = File.ReadAllText(SettingsPath);
JsonUtility.FromJsonOverwrite(json, this);
}
-
- // Check for environment variable PORT
- string envPort = System.Environment.GetEnvironmentVariable(EnvUnityPort);
- if (!string.IsNullOrEmpty(envPort) && int.TryParse(envPort, out int port))
- {
- Port = port;
- }
- string envTimeout = System.Environment.GetEnvironmentVariable(EnvUnityRequestTimeout);
- if (!string.IsNullOrEmpty(envTimeout) && int.TryParse(envTimeout, out int timeout))
- {
- RequestTimeoutSeconds = timeout;
- }
}
catch (Exception ex)
{
@@ -95,6 +86,9 @@ public void LoadSettings()
///
/// Save settings to disk
///
+ ///
+ /// WARNING: This file is also read by the MCP server. Changes here will require updates to it. See mcpUnity.ts
+ ///
public void SaveSettings()
{
try
@@ -102,14 +96,6 @@ public void SaveSettings()
// Save settings to McpUnitySettings.json
string json = JsonUtility.ToJson(this, true);
File.WriteAllText(SettingsPath, json);
-
- // Set environment variable PORT for the Node.js process
- // EnvironmentVariableTarget.User and EnvironmentVariableTarget.Machine should be used on .NET implementations running on Windows systems only.
- // For non-Windows systems, User and Machine are treated as Process.
- // Using Process target for broader compatibility.
- // see: https://learn.microsoft.com/en-us/dotnet/api/system.environmentvariabletarget?view=net-8.0#remarks
- Environment.SetEnvironmentVariable(EnvUnityPort, Port.ToString(), EnvironmentVariableTarget.Process);
- Environment.SetEnvironmentVariable(EnvUnityRequestTimeout, RequestTimeoutSeconds.ToString(), EnvironmentVariableTarget.Process);
}
catch (Exception ex)
{
diff --git a/Editor/Utils/McpConfigUtils.cs b/Editor/Utils/McpConfigUtils.cs
index 64c0dc65..510df7e3 100644
--- a/Editor/Utils/McpConfigUtils.cs
+++ b/Editor/Utils/McpConfigUtils.cs
@@ -60,7 +60,7 @@ public static string GenerateMcpConfigJson(bool useTabsIndentation)
}
///
- /// Gets the absolute path to the Server directory containing package.json
+ /// Gets the absolute path to the Server directory containing package.json (root server dir).
/// Works whether MCP Unity is installed via Package Manager or directly in the Assets folder
///
public static string GetServerPath()
diff --git a/Server~/build/unity/mcpUnity.d.ts b/Server~/build/unity/mcpUnity.d.ts
index 98a30487..d46b3912 100644
--- a/Server~/build/unity/mcpUnity.d.ts
+++ b/Server~/build/unity/mcpUnity.d.ts
@@ -9,7 +9,7 @@ export declare class McpUnity {
private port;
private ws;
private pendingRequests;
- private readonly REQUEST_TIMEOUT;
+ private requestTimeout;
private retryDelay;
constructor(logger: Logger);
/**
@@ -17,6 +17,10 @@ export declare class McpUnity {
* @param clientName Optional name of the MCP client connecting to Unity
*/
start(clientName?: string): Promise;
+ /**
+ * Reads our configuration file and sets parameters of the server based on them.
+ */
+ private parseAndSetConfig;
/**
* Connect to the Unity WebSocket
* @param clientName Optional name of the MCP client connecting to Unity
@@ -48,14 +52,9 @@ export declare class McpUnity {
*/
get isConnected(): boolean;
/**
- * Retrieves the UNITY_PORT value from the Windows registry (HKCU\Environment)
- * @returns The port value as a string if found, otherwise an empty string
- */
- private getUnityPortFromWindowsRegistry;
- /**
- * Retrieves the UNITY_PORT value from Unix-like system environment variables
- * @returns The port value as a string if found, otherwise an empty string
+ * Read the McpUnitySettings.json file and return its contents as a JSON object.
+ * @returns a JSON object with the contents of the McpUnitySettings.json file.
*/
- private getUnityPortFromUnixRegistry;
+ private readConfigFileAsJson;
}
export {};
diff --git a/Server~/build/unity/mcpUnity.js b/Server~/build/unity/mcpUnity.js
index 7013a8f8..81366f6c 100644
--- a/Server~/build/unity/mcpUnity.js
+++ b/Server~/build/unity/mcpUnity.js
@@ -1,28 +1,17 @@
import WebSocket from 'ws';
import { v4 as uuidv4 } from 'uuid';
import { McpUnityError, ErrorType } from '../utils/errors.js';
-import { execSync } from 'child_process';
-import { default as winreg } from 'winreg';
+import { promises as fs } from 'fs';
+import path from 'path';
export class McpUnity {
logger;
- port;
+ port = null;
ws = null;
pendingRequests = new Map();
- REQUEST_TIMEOUT;
+ requestTimeout = 10000;
retryDelay = 1000;
constructor(logger) {
this.logger = logger;
- // Initialize port from environment variable or use default
- const envRegistry = process.platform === 'win32'
- ? this.getUnityPortFromWindowsRegistry()
- : this.getUnityPortFromUnixRegistry();
- const envPort = process.env.UNITY_PORT || envRegistry;
- this.port = envPort ? parseInt(envPort, 10) : 8090;
- this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);
- // Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
- const envTimeout = process.env.UNITY_REQUEST_TIMEOUT;
- this.REQUEST_TIMEOUT = envTimeout ? parseInt(envTimeout, 10) * 1000 : 10000;
- this.logger.info(`Using request timeout: ${this.REQUEST_TIMEOUT / 1000} seconds`);
}
/**
* Start the Unity connection
@@ -30,6 +19,8 @@ export class McpUnity {
*/
async start(clientName) {
try {
+ this.logger.info('Attempting to read startup parameters...');
+ await this.parseAndSetConfig();
this.logger.info('Attempting to connect to Unity WebSocket...');
await this.connect(clientName); // Pass client name to connect
this.logger.info('Successfully connected to Unity WebSocket');
@@ -45,6 +36,19 @@ export class McpUnity {
}
return Promise.resolve();
}
+ /**
+ * Reads our configuration file and sets parameters of the server based on them.
+ */
+ async parseAndSetConfig() {
+ const config = await this.readConfigFileAsJson();
+ const configPort = config.Port;
+ this.port = configPort ? parseInt(configPort, 10) : 8090;
+ this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);
+ // Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
+ const configTimeout = config.RequestTimeoutSeconds;
+ this.requestTimeout = configTimeout ? parseInt(configTimeout, 10) * 1000 : 10000;
+ this.logger.info(`Using request timeout: ${this.requestTimeout / 1000} seconds`);
+ }
/**
* Connect to the Unity WebSocket
* @param clientName Optional name of the MCP client connecting to Unity
@@ -74,7 +78,7 @@ export class McpUnity {
this.disconnect();
reject(new McpUnityError(ErrorType.CONNECTION, 'Connection timeout'));
}
- }, this.REQUEST_TIMEOUT);
+ }, this.requestTimeout);
this.ws.onopen = () => {
clearTimeout(connectionTimeout);
this.logger.debug('WebSocket connected');
@@ -193,12 +197,12 @@ export class McpUnity {
// Create timeout for the request
const timeout = setTimeout(() => {
if (this.pendingRequests.has(requestId)) {
- this.logger.error(`Request ${requestId} timed out after ${this.REQUEST_TIMEOUT}ms`);
+ this.logger.error(`Request ${requestId} timed out after ${this.requestTimeout}ms`);
this.pendingRequests.delete(requestId);
reject(new McpUnityError(ErrorType.TIMEOUT, 'Request timed out'));
}
this.reconnect();
- }, this.REQUEST_TIMEOUT);
+ }, this.requestTimeout);
// Store pending request
this.pendingRequests.set(requestId, {
resolve,
@@ -225,27 +229,20 @@ export class McpUnity {
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
}
/**
- * Retrieves the UNITY_PORT value from the Windows registry (HKCU\Environment)
- * @returns The port value as a string if found, otherwise an empty string
- */
- getUnityPortFromWindowsRegistry() {
- const regKey = new winreg({ hive: winreg.HKCU, key: '\\Environment' });
- let result = '';
- regKey.get('UNITY_PORT', (err, item) => {
- if (err) {
- this.logger.error(`Error getting registry value: ${err.message}`);
- }
- else {
- result = item.value;
- }
- });
- return result;
- }
- /**
- * Retrieves the UNITY_PORT value from Unix-like system environment variables
- * @returns The port value as a string if found, otherwise an empty string
+ * Read the McpUnitySettings.json file and return its contents as a JSON object.
+ * @returns a JSON object with the contents of the McpUnitySettings.json file.
*/
- getUnityPortFromUnixRegistry() {
- return execSync('printenv UNITY_PORT', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim();
+ async readConfigFileAsJson() {
+ const configPath = path.resolve(process.cwd(), '../ProjectSettings/McpUnitySettings.json');
+ this.logger.debug(`Reading McpUnitySettings.json from ${configPath}`);
+ try {
+ const content = await fs.readFile(configPath, 'utf-8');
+ const json = JSON.parse(content);
+ return json;
+ }
+ catch (err) {
+ this.logger.debug(`McpUnitySettings.json not found or unreadable: ${err instanceof Error ? err.message : String(err)}`);
+ return {};
+ }
}
}
diff --git a/Server~/build/utils/errors.js b/Server~/build/utils/errors.js
index 7382c65f..429a4d72 100644
--- a/Server~/build/utils/errors.js
+++ b/Server~/build/utils/errors.js
@@ -6,7 +6,7 @@ export var ErrorType;
ErrorType["VALIDATION"] = "validation_error";
ErrorType["INTERNAL"] = "internal_error";
ErrorType["TIMEOUT"] = "timeout_error";
-})(ErrorType || (ErrorType = {}));
+})(ErrorType = ErrorType || (ErrorType = {}));
export class McpUnityError extends Error {
type;
details;
diff --git a/Server~/build/utils/logger.js b/Server~/build/utils/logger.js
index ec6b4c13..212c2829 100644
--- a/Server~/build/utils/logger.js
+++ b/Server~/build/utils/logger.js
@@ -5,7 +5,7 @@ export var LogLevel;
LogLevel[LogLevel["INFO"] = 1] = "INFO";
LogLevel[LogLevel["WARN"] = 2] = "WARN";
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
-})(LogLevel || (LogLevel = {}));
+})(LogLevel = LogLevel || (LogLevel = {}));
// Check environment variable for logging
const isLoggingEnabled = process.env.LOGGING === 'true';
// Check environment variable for logging in a file
diff --git a/Server~/src/unity/mcpUnity.ts b/Server~/src/unity/mcpUnity.ts
index 83838c00..5b54e5f7 100644
--- a/Server~/src/unity/mcpUnity.ts
+++ b/Server~/src/unity/mcpUnity.ts
@@ -2,8 +2,8 @@ import WebSocket from 'ws';
import { v4 as uuidv4 } from 'uuid';
import { Logger } from '../utils/logger.js';
import { McpUnityError, ErrorType } from '../utils/errors.js';
-import { execSync } from 'child_process';
-import { default as winreg } from 'winreg';
+import { promises as fs } from 'fs';
+import path from 'path';
interface PendingRequest {
resolve: (value: any) => void;
@@ -30,28 +30,14 @@ interface UnityResponse {
export class McpUnity {
private logger: Logger;
- private port: number;
+ private port: number | null = null;
private ws: WebSocket | null = null;
private pendingRequests: Map = new Map();
- private readonly REQUEST_TIMEOUT: number;
+ private requestTimeout = 10000;
private retryDelay = 1000;
constructor(logger: Logger) {
this.logger = logger;
-
- // Initialize port from environment variable or use default
- const envRegistry = process.platform === 'win32'
- ? this.getUnityPortFromWindowsRegistry()
- : this.getUnityPortFromUnixRegistry();
-
- const envPort = process.env.UNITY_PORT || envRegistry;
- this.port = envPort ? parseInt(envPort, 10) : 8090;
- this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);
-
- // Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
- const envTimeout = process.env.UNITY_REQUEST_TIMEOUT;
- this.REQUEST_TIMEOUT = envTimeout ? parseInt(envTimeout, 10) * 1000 : 10000;
- this.logger.info(`Using request timeout: ${this.REQUEST_TIMEOUT / 1000} seconds`);
}
/**
@@ -60,6 +46,9 @@ export class McpUnity {
*/
public async start(clientName?: string): Promise {
try {
+ this.logger.info('Attempting to read startup parameters...');
+ await this.parseAndSetConfig();
+
this.logger.info('Attempting to connect to Unity WebSocket...');
await this.connect(clientName); // Pass client name to connect
this.logger.info('Successfully connected to Unity WebSocket');
@@ -77,6 +66,22 @@ export class McpUnity {
return Promise.resolve();
}
+
+ /**
+ * Reads our configuration file and sets parameters of the server based on them.
+ */
+ private async parseAndSetConfig() {
+ const config = await this.readConfigFileAsJson();
+
+ const configPort = config.Port;
+ this.port = configPort ? parseInt(configPort, 10) : 8090;
+ this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);
+
+ // Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
+ const configTimeout = config.RequestTimeoutSeconds;
+ this.requestTimeout = configTimeout ? parseInt(configTimeout, 10) * 1000 : 10000;
+ this.logger.info(`Using request timeout: ${this.requestTimeout / 1000} seconds`);
+ }
/**
* Connect to the Unity WebSocket
@@ -112,7 +117,7 @@ export class McpUnity {
this.disconnect();
reject(new McpUnityError(ErrorType.CONNECTION, 'Connection timeout'));
}
- }, this.REQUEST_TIMEOUT);
+ }, this.requestTimeout);
this.ws.onopen = () => {
clearTimeout(connectionTimeout);
@@ -249,12 +254,12 @@ export class McpUnity {
// Create timeout for the request
const timeout = setTimeout(() => {
if (this.pendingRequests.has(requestId)) {
- this.logger.error(`Request ${requestId} timed out after ${this.REQUEST_TIMEOUT}ms`);
+ this.logger.error(`Request ${requestId} timed out after ${this.requestTimeout}ms`);
this.pendingRequests.delete(requestId);
reject(new McpUnityError(ErrorType.TIMEOUT, 'Request timed out'));
}
this.reconnect();
- }, this.REQUEST_TIMEOUT);
+ }, this.requestTimeout);
// Store pending request
this.pendingRequests.set(requestId, {
@@ -282,29 +287,21 @@ export class McpUnity {
// Basic WebSocket connection check
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
}
-
- /**
- * Retrieves the UNITY_PORT value from the Windows registry (HKCU\Environment)
- * @returns The port value as a string if found, otherwise an empty string
- */
- private getUnityPortFromWindowsRegistry(): string {
- const regKey = new winreg({hive: winreg.HKCU, key: '\\Environment'});
- let result = '';
- regKey.get('UNITY_PORT', (err: Error | null, item: any) => {
- if (err) {
- this.logger.error(`Error getting registry value: ${err.message}`);
- } else {
- result = item.value;
- }
- });
- return result;
- }
/**
- * Retrieves the UNITY_PORT value from Unix-like system environment variables
- * @returns The port value as a string if found, otherwise an empty string
+ * Read the McpUnitySettings.json file and return its contents as a JSON object.
+ * @returns a JSON object with the contents of the McpUnitySettings.json file.
*/
- private getUnityPortFromUnixRegistry(): string {
- return execSync('printenv UNITY_PORT', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim();
+ private async readConfigFileAsJson(): Promise {
+ const configPath = path.resolve(process.cwd(), '../ProjectSettings/McpUnitySettings.json');
+ this.logger.debug(`Reading McpUnitySettings.json from ${configPath}`);
+ try {
+ const content = await fs.readFile(configPath, 'utf-8');
+ const json = JSON.parse(content);
+ return json;
+ } catch (err) {
+ this.logger.debug(`McpUnitySettings.json not found or unreadable: ${err instanceof Error ? err.message : String(err)}`);
+ return {};
+ }
}
}