From 82c23d79c7af2a9a3fe51303fa9d4033439cced1 Mon Sep 17 00:00:00 2001 From: conradomateu Date: Mon, 16 Mar 2026 15:25:07 +0100 Subject: [PATCH] feat: add auto-start option for RPC server on FreeCAD launch Adds an opt-in setting to automatically start the RPC server when FreeCAD opens, removing the need to manually click "Start RPC Server" each session. Changes: - New `auto_start_rpc` setting in freecad_mcp_settings.json (default: false) - New "Auto-Start Server" checkable menu item under FreeCAD MCP menu - InitGui.py uses QTimer.singleShot(0) to start the server after FreeCAD finishes initialization, only when the setting is enabled - Consolidated _sync_remote_toggle_state into _sync_toggle_states to handle both Remote Connections and Auto-Start checkboxes Tested on macOS with FreeCAD 1.0. Cross-platform: uses only PySide QtCore.QTimer and FreeCAD.getUserAppDataDir() (no OS-specific code). Co-Authored-By: Claude Opus 4.6 --- addon/FreeCADMCP/InitGui.py | 20 +++++++++ addon/FreeCADMCP/rpc_server/rpc_server.py | 50 +++++++++++++++++++---- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/addon/FreeCADMCP/InitGui.py b/addon/FreeCADMCP/InitGui.py index 6a3037f..8ae42bf 100644 --- a/addon/FreeCADMCP/InitGui.py +++ b/addon/FreeCADMCP/InitGui.py @@ -8,6 +8,7 @@ class FreeCADMCPAddonWorkbench(Workbench): commands = [ "Start_RPC_Server", "Stop_RPC_Server", + "Toggle_Auto_Start", "Toggle_Remote_Connections", "Configure_Allowed_IPs", ] @@ -28,3 +29,22 @@ class FreeCADMCPAddonWorkbench(Workbench): Gui.addWorkbench(FreeCADMCPAddonWorkbench()) + + +def _auto_start_mcp(): + try: + from rpc_server import rpc_server + + settings = rpc_server.load_settings() + if not settings.get("auto_start_rpc", False): + return + + msg = rpc_server.start_rpc_server() + FreeCAD.Console.PrintMessage(f"[MCP] Auto-start: {msg}\n") + except Exception as e: + FreeCAD.Console.PrintWarning(f"[MCP] Auto-start failed: {e}\n") + + +from PySide import QtCore + +QtCore.QTimer.singleShot(0, _auto_start_mcp) diff --git a/addon/FreeCADMCP/rpc_server/rpc_server.py b/addon/FreeCADMCP/rpc_server/rpc_server.py index 304c7c3..ba6d333 100644 --- a/addon/FreeCADMCP/rpc_server/rpc_server.py +++ b/addon/FreeCADMCP/rpc_server/rpc_server.py @@ -32,6 +32,7 @@ _SETTINGS_FILENAME = "freecad_mcp_settings.json" _DEFAULT_SETTINGS = { "remote_enabled": False, "allowed_ips": "127.0.0.1", + "auto_start_rpc": False, } @@ -709,26 +710,59 @@ class ConfigureAllowedIPsCommand: return True +class ToggleAutoStartCommand: + def GetResources(self): + return { + "MenuText": "Auto-Start Server", + "ToolTip": "Automatically start the RPC server when FreeCAD launches.", + "Checkable": True, + } + + def Activated(self, checked=0): + settings = load_settings() + settings["auto_start_rpc"] = bool(checked) + save_settings(settings) + + if settings["auto_start_rpc"]: + FreeCAD.Console.PrintMessage( + "MCP RPC server will start automatically on next FreeCAD launch.\n" + ) + else: + FreeCAD.Console.PrintMessage( + "MCP RPC server auto-start disabled.\n" + ) + + def IsActive(self): + return True + + FreeCADGui.addCommand("Start_RPC_Server", StartRPCServerCommand()) FreeCADGui.addCommand("Stop_RPC_Server", StopRPCServerCommand()) +FreeCADGui.addCommand("Toggle_Auto_Start", ToggleAutoStartCommand()) FreeCADGui.addCommand("Toggle_Remote_Connections", ToggleRemoteConnectionsCommand()) FreeCADGui.addCommand("Configure_Allowed_IPs", ConfigureAllowedIPsCommand()) -def _sync_remote_toggle_state(): - """Sync the Remote Connections checkbox with saved settings on startup.""" +def _sync_toggle_states(): + """Sync checkable menu items with saved settings on startup.""" try: settings = load_settings() - enabled = settings.get("remote_enabled", False) main_window = FreeCADGui.getMainWindow() + toggle_map = { + "Remote Connections": settings.get("remote_enabled", False), + "Auto-Start Server": settings.get("auto_start_rpc", False), + } + found = 0 for action in main_window.findChildren(QtWidgets.QAction): - if action.text() == "Remote Connections": - action.setChecked(enabled) - return + if action.text() in toggle_map: + action.setChecked(toggle_map[action.text()]) + found += 1 + if found == len(toggle_map): + return except Exception: pass # Retry if menu not ready yet - QtCore.QTimer.singleShot(2000, _sync_remote_toggle_state) + QtCore.QTimer.singleShot(2000, _sync_toggle_states) -QtCore.QTimer.singleShot(2000, _sync_remote_toggle_state) \ No newline at end of file +QtCore.QTimer.singleShot(2000, _sync_toggle_states) \ No newline at end of file