cursor_mcp_bridge.py•3.17 kB
#!/usr/bin/env python3
"""
Cursor MCP Bridge for FreeCAD
This script launches the FreeCAD MCP server in stdio mode for Cursor integration.
All debugging/info messages are redirected to stderr, keeping stdout clean for JSON-only communication.
"""
import atexit
import logging
import os
import subprocess
import sys
# Configure logging to stderr only (keep stdout clean for JSON)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
stream=sys.stderr, # Send all logging to stderr, not stdout
)
logger = logging.getLogger("cursor_mcp_bridge")
# Get the directory where this script is located
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
MCP_SERVER_SCRIPT = os.path.join(
SCRIPT_DIR, "src", "mcp_freecad", "server", "freecad_mcp_server.py"
)
# Track processes for cleanup
processes = []
def cleanup():
"""Clean up processes on exit"""
for proc in processes:
if proc.poll() is None: # Still running
try:
logger.info(f"Terminating process {proc.pid}")
proc.terminate()
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
logger.warning(f"Process {proc.pid} did not terminate, killing")
proc.kill()
except Exception as e:
logger.error(f"Error terminating process {proc.pid}: {e}")
# Register cleanup function
atexit.register(cleanup)
def start_mcp_server():
"""Start the MCP server in stdio mode, properly connecting stdin/stdout"""
logger.info("Starting MCP server in stdio mode...")
# CORRECTION: We must connect our stdin/stdout to the MCP server process
# for proper communication, but send stderr to a log file
with open(
os.path.join(SCRIPT_DIR, "logs", "mcp_server_stderr.log"), "w"
) as stderr_log:
proc = subprocess.Popen(
[sys.executable, MCP_SERVER_SCRIPT],
stdin=sys.stdin, # Connect our stdin to the MCP server
stdout=sys.stdout, # Connect MCP server's output directly to our stdout
stderr=stderr_log, # Redirect stderr to a log file
bufsize=0, # Unbuffered communication
)
processes.append(proc)
logger.info(f"MCP server started with PID: {proc.pid}")
return proc
def main():
"""Start the servers and handle graceful shutdown"""
try:
# Start MCP server - this connects stdin/stdout directly
mcp_proc = start_mcp_server()
# Now simply wait for the MCP server to exit
# Since we've connected stdin/stdout directly to the MCP server,
# it will read from Cursor and write to Cursor without our interference
exit_code = mcp_proc.wait()
logger.info(f"MCP server exited with code: {exit_code}")
return exit_code
except KeyboardInterrupt:
logger.info("Interrupted, shutting down servers...")
except Exception as e:
logger.error(f"Error: {e}")
return 1
finally:
# Clean up all processes
cleanup()
return 0
if __name__ == "__main__":
sys.exit(main())