diff --git a/libs/python/mcp-server/QUICK_TEST_COMMANDS.sh b/libs/python/mcp-server/QUICK_TEST_COMMANDS.sh new file mode 100755 index 00000000..3242c610 --- /dev/null +++ b/libs/python/mcp-server/QUICK_TEST_COMMANDS.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Quick Test Commands for MCP Server Local Desktop Option +# Run these commands to test the implementation + +set -e # Exit on error + +echo "======================================================================" +echo "Testing MCP Server Local Desktop Option" +echo "======================================================================" +echo "" + +# Change to repo root +cd "$(dirname "$0")/.." + +# Test 1: Quick Logic Test (No setup required) +echo "Test 1: Quick Logic Test (No setup required)" +echo "----------------------------------------------------------------------" +python tests/quick_test_local_option.py +echo "" + +# Test 2: Automated Tests (Requires pytest and packages) +echo "Test 2: Automated Tests (Requires pytest and packages installed)" +echo "----------------------------------------------------------------------" +if command -v pytest &> /dev/null; then + echo "Running pytest..." + pytest tests/test_mcp_server_local_option.py -v || echo "Note: Some tests may require full setup" +else + echo "⚠️ pytest not found. Install with: pip install pytest" +fi +echo "" + +# Test 3: Existing MCP server tests +echo "Test 3: Existing MCP Server Tests" +echo "----------------------------------------------------------------------" +if command -v pytest &> /dev/null; then + echo "Running existing session management tests..." + pytest tests/test_mcp_server_session_management.py -v || echo "Note: Some tests may fail if dependencies are missing" +else + echo "⚠️ pytest not found. Install with: pip install pytest" +fi +echo "" + +# Summary +echo "======================================================================" +echo "Test Summary" +echo "======================================================================" +echo "✅ Quick logic test completed" +echo "" +echo "Next steps for comprehensive testing:" +echo "1. Install dependencies:" +echo " pip install -e libs/python/core" +echo " pip install -e libs/python/computer" +echo " pip install -e libs/python/agent" +echo " pip install -e libs/python/mcp-server" +echo " pip install -e libs/python/computer-server" +echo "" +echo "2. For manual end-to-end testing, see:" +echo " tests/MANUAL_TEST_LOCAL_OPTION.md" +echo "" +echo "3. For detailed testing info, see:" +echo " tests/TESTING_SUMMARY.md" +echo "" + diff --git a/libs/python/mcp-server/quick_test_local_option.py b/libs/python/mcp-server/quick_test_local_option.py new file mode 100755 index 00000000..e997f6a9 --- /dev/null +++ b/libs/python/mcp-server/quick_test_local_option.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +""" +Quick test to verify the local desktop option logic without full setup. + +This script tests the environment variable parsing and logic flow +without requiring VMs, computer-server, or MCP clients to be running. +""" + +import os +import sys + + +def test_env_var_parsing(): + """Test that environment variable is parsed correctly.""" + print("Testing CUA_USE_HOST_COMPUTER_SERVER environment variable parsing...") + print("-" * 60) + + test_cases = [ + # (env_value, expected_result, description) + ("true", True, "lowercase 'true'"), + ("True", True, "capitalized 'True'"), + ("TRUE", True, "uppercase 'TRUE'"), + ("1", True, "numeric '1'"), + ("yes", True, "lowercase 'yes'"), + ("Yes", True, "capitalized 'Yes'"), + ("false", False, "lowercase 'false'"), + ("False", False, "capitalized 'False'"), + ("FALSE", False, "uppercase 'FALSE'"), + ("0", False, "numeric '0'"), + ("no", False, "lowercase 'no'"), + ("", False, "empty string"), + ("random", False, "random value"), + (None, False, "not set (None)"), + ] + + passed = 0 + failed = 0 + + for env_value, expected, description in test_cases: + # Simulate the logic from session_manager.py line 59 + if env_value is None: + actual = os.getenv("CUA_USE_HOST_COMPUTER_SERVER", "false").lower() in ( + "true", + "1", + "yes", + ) + else: + os.environ["CUA_USE_HOST_COMPUTER_SERVER"] = env_value + actual = os.getenv("CUA_USE_HOST_COMPUTER_SERVER", "false").lower() in ( + "true", + "1", + "yes", + ) + + status = "✓ PASS" if actual == expected else "✗ FAIL" + if actual == expected: + passed += 1 + else: + failed += 1 + + print( + f"{status} | Value: {env_value!r:15} | Expected: {expected!s:5} | Got: {actual!s:5} | {description}" + ) + + # Clean up + os.environ.pop("CUA_USE_HOST_COMPUTER_SERVER", None) + + print("-" * 60) + print(f"Results: {passed} passed, {failed} failed") + return failed == 0 + + +def test_session_manager_logic(): + """Test the logic flow in session_manager.py without actual Computer creation.""" + print("\nTesting session_manager.py logic flow...") + print("-" * 60) + + # Read the actual session_manager.py to verify the logic + import pathlib + + session_manager_path = ( + pathlib.Path(__file__).parent.parent + / "libs" + / "python" + / "mcp-server" + / "mcp_server" + / "session_manager.py" + ) + + if not session_manager_path.exists(): + print(f"✗ FAIL | session_manager.py not found at {session_manager_path}") + return False + + content = session_manager_path.read_text() + + # Check for the key logic + checks = [ + ('os.getenv("CUA_USE_HOST_COMPUTER_SERVER"', "Environment variable check present"), + ("use_host_computer_server=use_host", "use_host_computer_server parameter passed"), + ("Computer(", "Computer instantiation present"), + ] + + all_checks_passed = True + for check_str, description in checks: + if check_str in content: + print(f"✓ PASS | {description}") + else: + print(f"✗ FAIL | {description} - not found") + all_checks_passed = False + + print("-" * 60) + return all_checks_passed + + +def test_documentation_consistency(): + """Verify documentation mentions the new feature.""" + print("\nTesting documentation consistency...") + print("-" * 60) + + import pathlib + + docs_to_check = [ + ("configuration.mdx", "CUA_USE_HOST_COMPUTER_SERVER"), + ("usage.mdx", "Targeting Your Local Desktop"), + ] + + docs_path = ( + pathlib.Path(__file__).parent.parent + / "docs" + / "content" + / "docs" + / "libraries" + / "mcp-server" + ) + + all_docs_ok = True + for doc_file, expected_content in docs_to_check: + doc_path = docs_path / doc_file + if not doc_path.exists(): + print(f"✗ FAIL | {doc_file} not found") + all_docs_ok = False + continue + + content = doc_path.read_text() + if expected_content in content: + print(f"✓ PASS | {doc_file} contains '{expected_content}'") + else: + print(f"✗ FAIL | {doc_file} missing '{expected_content}'") + all_docs_ok = False + + print("-" * 60) + return all_docs_ok + + +def print_usage_examples(): + """Print usage examples for both modes.""" + print("\n" + "=" * 60) + print("USAGE EXAMPLES") + print("=" * 60) + + print("\n1. DEFAULT MODE (VM):") + print("-" * 60) + print( + """ +{ + "mcpServers": { + "cua-agent": { + "command": "/bin/bash", + "args": ["~/.cua/start_mcp_server.sh"], + "env": { + "CUA_MODEL_NAME": "anthropic/claude-3-5-sonnet-20241022" + } + } + } +} + +Note: CUA_USE_HOST_COMPUTER_SERVER is not set, so VM mode is used (safe). +""" + ) + + print("\n2. LOCAL DESKTOP MODE:") + print("-" * 60) + print( + """ +Step 1: Start computer-server locally: + python -m computer_server + +Step 2: Configure MCP client: +{ + "mcpServers": { + "cua-agent": { + "command": "/bin/bash", + "args": ["~/.cua/start_mcp_server.sh"], + "env": { + "CUA_MODEL_NAME": "anthropic/claude-3-5-sonnet-20241022", + "CUA_USE_HOST_COMPUTER_SERVER": "true" + } + } + } +} + +⚠️ WARNING: AI will have direct access to your desktop! +""" + ) + + +def main(): + """Run all quick tests.""" + print("=" * 60) + print("QUICK TEST: MCP Server Local Desktop Option") + print("=" * 60) + print() + + results = [] + + # Run tests + results.append(("Environment Variable Parsing", test_env_var_parsing())) + results.append(("Session Manager Logic", test_session_manager_logic())) + results.append(("Documentation Consistency", test_documentation_consistency())) + + # Print summary + print("\n" + "=" * 60) + print("SUMMARY") + print("=" * 60) + for test_name, passed in results: + status = "✓ PASSED" if passed else "✗ FAILED" + print(f"{status} | {test_name}") + + all_passed = all(result for _, result in results) + + if all_passed: + print("\n🎉 All quick tests passed!") + print_usage_examples() + print("\nNext steps:") + print("1. Run full automated tests: pytest tests/test_mcp_server_local_option.py") + print("2. Follow manual testing guide: tests/MANUAL_TEST_LOCAL_OPTION.md") + return 0 + else: + print("\n❌ Some tests failed. Please review the output above.") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/libs/python/mcp-server/test_mcp_server_local_option.py b/libs/python/mcp-server/test_mcp_server_local_option.py new file mode 100644 index 00000000..b1540726 --- /dev/null +++ b/libs/python/mcp-server/test_mcp_server_local_option.py @@ -0,0 +1,138 @@ +""" +Test script to verify MCP Server local desktop option works correctly. + +This test verifies: +1. Default behavior: Computer uses VM +2. New behavior: Computer uses host when CUA_USE_HOST_COMPUTER_SERVER=true +""" + +import asyncio +import os +import sys +from pathlib import Path + +# Add the mcp-server module to path +mcp_server_path = Path(__file__).parent.parent / "libs" / "python" / "mcp-server" +sys.path.insert(0, str(mcp_server_path.parent.parent.parent / "libs" / "python")) + +import pytest + + +@pytest.mark.asyncio +async def test_default_vm_mode(): + """Test that the default mode uses VM (not host computer server).""" + # Ensure environment variable is not set or is false + os.environ.pop("CUA_USE_HOST_COMPUTER_SERVER", None) + + from mcp_server.session_manager import ComputerPool + + pool = ComputerPool(max_size=1) + + try: + computer = await pool.acquire() + + # Verify the computer was initialized + assert computer is not None + + # Check that use_host_computer_server was set to False (default) + # This should start a VM + print("✓ Default mode: Computer initialized (VM mode expected)") + + await pool.release(computer) + + finally: + await pool.shutdown() + + +@pytest.mark.asyncio +async def test_local_desktop_mode(): + """Test that setting CUA_USE_HOST_COMPUTER_SERVER=true uses host.""" + # Set environment variable to true + os.environ["CUA_USE_HOST_COMPUTER_SERVER"] = "true" + + # Need to reload module to pick up new env var + import importlib + + import mcp_server.session_manager + from mcp_server.session_manager import ComputerPool + + importlib.reload(mcp_server.session_manager) + + pool = mcp_server.session_manager.ComputerPool(max_size=1) + + try: + computer = await pool.acquire() + + # Verify the computer was initialized + assert computer is not None + + # Check that use_host_computer_server was set to True + print("✓ Local mode: Computer initialized (host mode expected)") + + await pool.release(computer) + + finally: + await pool.shutdown() + # Clean up env var + os.environ.pop("CUA_USE_HOST_COMPUTER_SERVER", None) + + +@pytest.mark.asyncio +async def test_env_var_parsing(): + """Test that various values of CUA_USE_HOST_COMPUTER_SERVER are parsed correctly.""" + test_cases = [ + ("true", True), + ("True", True), + ("TRUE", True), + ("1", True), + ("yes", True), + ("false", False), + ("False", False), + ("FALSE", False), + ("0", False), + ("no", False), + ("", False), + ("random", False), + ] + + for value, expected in test_cases: + os.environ["CUA_USE_HOST_COMPUTER_SERVER"] = value + + # Check parsing logic + use_host = os.getenv("CUA_USE_HOST_COMPUTER_SERVER", "false").lower() in ( + "true", + "1", + "yes", + ) + + assert ( + use_host == expected + ), f"Failed for value '{value}': expected {expected}, got {use_host}" + print(f"✓ Env var '{value}' correctly parsed as {expected}") + + os.environ.pop("CUA_USE_HOST_COMPUTER_SERVER", None) + + +if __name__ == "__main__": + print("Testing MCP Server Local Desktop Option") + print("=" * 60) + + print("\n1. Testing environment variable parsing...") + asyncio.run(test_env_var_parsing()) + + print("\n2. Testing default VM mode...") + try: + asyncio.run(test_default_vm_mode()) + except Exception as e: + print(f"✗ Default VM mode test failed: {e}") + print("Note: This may require lume/VM setup to fully test") + + print("\n3. Testing local desktop mode...") + try: + asyncio.run(test_local_desktop_mode()) + except Exception as e: + print(f"✗ Local desktop mode test failed: {e}") + print("Note: This may require computer-server to be running locally") + + print("\n" + "=" * 60) + print("Tests completed!")