diff --git a/addon/FreecadRobustMCPBridge/freecad_mcp_bridge/server.py b/addon/FreecadRobustMCPBridge/freecad_mcp_bridge/server.py
index d4fc242..c26cb0f 100644
--- a/addon/FreecadRobustMCPBridge/freecad_mcp_bridge/server.py
+++ b/addon/FreecadRobustMCPBridge/freecad_mcp_bridge/server.py
@@ -22,6 +22,7 @@ import contextlib
import errno
import io
import json
+import os
import queue
import sys
import threading
@@ -236,13 +237,14 @@ class FreecadMCPPlugin:
self._running = True
- # Print instance ID to stderr for test automation to capture.
- # Stdout may be reserved for JSON-RPC when running in stdio mode.
- print(
- f"FREECAD_MCP_BRIDGE_INSTANCE_ID={self._instance_id}",
- file=sys.stderr,
- flush=True,
- )
+ # Print instance ID to stderr only for test automation (when env var is set).
+ # This avoids red error text in FreeCAD's console during normal use.
+ if os.environ.get("FREECAD_MCP_TESTING"):
+ print(
+ f"FREECAD_MCP_BRIDGE_INSTANCE_ID={self._instance_id}",
+ file=sys.stderr,
+ flush=True,
+ )
# Start the queue processing timer on the main thread
self._start_queue_processor()
diff --git a/addon/FreecadRobustMCPBridge/icons/mcp_start.svg b/addon/FreecadRobustMCPBridge/icons/mcp_start.svg
index 9b4293b..8862453 100644
--- a/addon/FreecadRobustMCPBridge/icons/mcp_start.svg
+++ b/addon/FreecadRobustMCPBridge/icons/mcp_start.svg
@@ -1,54 +1,54 @@
diff --git a/addon/FreecadRobustMCPBridge/icons/mcp_status.svg b/addon/FreecadRobustMCPBridge/icons/mcp_status.svg
index 563621e..6e4e76f 100644
--- a/addon/FreecadRobustMCPBridge/icons/mcp_status.svg
+++ b/addon/FreecadRobustMCPBridge/icons/mcp_status.svg
@@ -1,57 +1,57 @@
diff --git a/addon/FreecadRobustMCPBridge/icons/mcp_stop.svg b/addon/FreecadRobustMCPBridge/icons/mcp_stop.svg
index 11071d4..d92b80b 100644
--- a/addon/FreecadRobustMCPBridge/icons/mcp_stop.svg
+++ b/addon/FreecadRobustMCPBridge/icons/mcp_stop.svg
@@ -1,43 +1,46 @@
diff --git a/addon/FreecadRobustMCPBridge/icons/preferences-robust_mcp_bridge.svg b/addon/FreecadRobustMCPBridge/icons/preferences-robust_mcp_bridge.svg
index 450a068..d99322e 100644
--- a/addon/FreecadRobustMCPBridge/icons/preferences-robust_mcp_bridge.svg
+++ b/addon/FreecadRobustMCPBridge/icons/preferences-robust_mcp_bridge.svg
@@ -1,84 +1,84 @@
diff --git a/src/freecad_mcp/server.py b/src/freecad_mcp/server.py
index ac4ed19..5ce8ef3 100644
--- a/src/freecad_mcp/server.py
+++ b/src/freecad_mcp/server.py
@@ -33,6 +33,7 @@ Example:
import argparse
import logging
+import os
import sys
import uuid
from collections.abc import AsyncIterator
@@ -411,9 +412,11 @@ def main() -> None:
# Set up logging
logging.getLogger().setLevel(config.log_level)
- # Print instance ID to stderr for test automation to capture
- # Must use stderr because stdout is reserved for JSON-RPC in stdio mode
- print(f"FREECAD_MCP_INSTANCE_ID={INSTANCE_ID}", file=sys.stderr, flush=True)
+ # Print instance ID to stderr only for test automation (when env var is set).
+ # This avoids unwanted output during normal use.
+ # Must use stderr because stdout is reserved for JSON-RPC in stdio mode.
+ if os.environ.get("FREECAD_MCP_TESTING"):
+ print(f"FREECAD_MCP_INSTANCE_ID={INSTANCE_ID}", file=sys.stderr, flush=True)
logger.info("Starting FreeCAD Robust MCP Server")
logger.info("Instance ID: %s", INSTANCE_ID)
diff --git a/tests/unit/test_server.py b/tests/unit/test_server.py
index f52db4b..63bbf08 100644
--- a/tests/unit/test_server.py
+++ b/tests/unit/test_server.py
@@ -1,5 +1,6 @@
"""Tests for the main server module."""
+import os
import sys
from unittest.mock import AsyncMock, MagicMock, patch
@@ -226,7 +227,7 @@ class TestMain:
"""Tests for main function."""
def test_main_prints_instance_id(self):
- """Main should print instance ID on startup."""
+ """Main should print instance ID on startup when FREECAD_MCP_TESTING is set."""
import freecad_mcp.server as server_module
from freecad_mcp.config import TransportType
@@ -240,6 +241,7 @@ class TestMain:
patch.object(server_module, "get_config", return_value=mock_config),
patch.object(server_module.mcp, "run") as mock_run,
patch("builtins.print") as mock_print,
+ patch.dict(os.environ, {"FREECAD_MCP_TESTING": "1"}),
):
# Mock run to exit immediately
mock_run.return_value = None
@@ -258,6 +260,37 @@ class TestMain:
)
assert instance_id_call.kwargs.get("file") == sys.stderr
+ def test_main_no_instance_id_without_testing_env(self):
+ """Main should NOT print instance ID when FREECAD_MCP_TESTING is unset."""
+ import freecad_mcp.server as server_module
+ from freecad_mcp.config import TransportType
+
+ mock_config = MagicMock()
+ mock_config.log_level = "INFO"
+ mock_config.mode = FreecadMode.EMBEDDED
+ mock_config.transport = TransportType.STDIO
+
+ # Ensure FREECAD_MCP_TESTING is not set
+ env_without_testing = {
+ k: v for k, v in os.environ.items() if k != "FREECAD_MCP_TESTING"
+ }
+
+ with (
+ patch.object(sys, "argv", DEFAULT_ARGV),
+ patch.object(server_module, "get_config", return_value=mock_config),
+ patch.object(server_module.mcp, "run") as mock_run,
+ patch("builtins.print") as mock_print,
+ patch.dict(os.environ, env_without_testing, clear=True),
+ ):
+ # Mock run to exit immediately
+ mock_run.return_value = None
+
+ server_module.main()
+
+ # Check that instance ID was NOT printed
+ print_calls = [str(call) for call in mock_print.call_args_list]
+ assert not any("FREECAD_MCP_INSTANCE_ID=" in call for call in print_calls)
+
def test_main_http_transport(self):
"""Main should start HTTP transport when configured."""
import freecad_mcp.server as server_module
@@ -438,6 +471,7 @@ class TestStdioProtocolCleanliness:
"FREECAD_MODE": "xmlrpc",
"FREECAD_XMLRPC_PORT": "59999",
"FREECAD_SOCKET_HOST": "localhost",
+ "FREECAD_MCP_TESTING": "1", # Enable stderr instance ID output
},
)