Skip to main content
Glama
test_mcp_cli.py14.9 kB
#!/usr/bin/env python3 """ Tests for MCP CLI management commands. This module tests the functionality of the Claude Code CLI MCP server management system, including adding, removing, listing, and configuring MCP servers. """ import json import pytest from pathlib import Path from unittest.mock import patch, MagicMock, mock_open import tempfile import os import sys # Add the parent directory to the system path for imports sys.path.insert(0, str(Path(__file__).parent.parent)) from src.openrouter_mcp.cli.mcp_manager import ( MCPManager, MCPServerConfig, MCPServerNotFoundError, MCPServerAlreadyExistsError, MCPConfigError ) class TestMCPServerConfig: """Test MCPServerConfig data class.""" def test_server_config_creation(self): """Test creating a server configuration.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"], cwd="/path/to/project", env={"OPENROUTER_API_KEY": "test-key"} ) assert config.name == "openrouter" assert config.command == "python" assert config.args == ["-m", "src.openrouter_mcp.server"] assert config.cwd == "/path/to/project" assert config.env["OPENROUTER_API_KEY"] == "test-key" def test_server_config_to_dict(self): """Test converting server config to dictionary.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"] ) config_dict = config.to_dict() assert "command" in config_dict assert "args" in config_dict assert config_dict["command"] == "python" assert config_dict["args"] == ["-m", "src.openrouter_mcp.server"] def test_server_config_from_dict(self): """Test creating server config from dictionary.""" data = { "command": "node", "args": ["server.js"], "cwd": "/project", "env": {"API_KEY": "secret"} } config = MCPServerConfig.from_dict("test-server", data) assert config.name == "test-server" assert config.command == "node" assert config.args == ["server.js"] assert config.cwd == "/project" assert config.env["API_KEY"] == "secret" class TestMCPManager: """Test MCPManager class.""" @pytest.fixture def temp_config_file(self): """Create a temporary config file for testing.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f: json.dump({"mcpServers": {}}, f) temp_path = f.name yield Path(temp_path) # Cleanup if Path(temp_path).exists(): os.unlink(temp_path) @pytest.fixture def manager(self, temp_config_file): """Create an MCPManager instance with a temporary config file.""" return MCPManager(config_path=temp_config_file) def test_manager_initialization(self, manager, temp_config_file): """Test MCPManager initialization.""" assert manager.config_path == temp_config_file assert manager.config == {"mcpServers": {}} def test_add_mcp_server_success(self, manager): """Test successfully adding an MCP server.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"], cwd=str(Path.cwd()), env={"OPENROUTER_API_KEY": "test-key"} ) manager.add_server(config) assert "openrouter" in manager.config["mcpServers"] assert manager.config["mcpServers"]["openrouter"]["command"] == "python" def test_add_duplicate_server_error(self, manager): """Test adding a duplicate server raises an error.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"] ) manager.add_server(config) with pytest.raises(MCPServerAlreadyExistsError): manager.add_server(config) def test_remove_mcp_server_success(self, manager): """Test successfully removing an MCP server.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"] ) manager.add_server(config) manager.remove_server("openrouter") assert "openrouter" not in manager.config["mcpServers"] def test_remove_nonexistent_server_error(self, manager): """Test removing a non-existent server raises an error.""" with pytest.raises(MCPServerNotFoundError): manager.remove_server("nonexistent") def test_list_installed_servers(self, manager): """Test listing installed MCP servers.""" config1 = MCPServerConfig(name="server1", command="python", args=["s1.py"]) config2 = MCPServerConfig(name="server2", command="node", args=["s2.js"]) manager.add_server(config1) manager.add_server(config2) servers = manager.list_servers() assert len(servers) == 2 assert "server1" in servers assert "server2" in servers def test_update_server_config(self, manager): """Test updating an existing server configuration.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"], env={"OPENROUTER_API_KEY": "old-key"} ) manager.add_server(config) updated_config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"], env={"OPENROUTER_API_KEY": "new-key"} ) manager.update_server(updated_config) assert manager.config["mcpServers"]["openrouter"]["env"]["OPENROUTER_API_KEY"] == "new-key" def test_validate_server_compatibility(self, manager): """Test validating server compatibility.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"] ) # This should not raise an exception assert manager.validate_server(config) is True def test_handle_duplicate_server(self, manager): """Test handling duplicate server with force option.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "old.server"] ) manager.add_server(config) new_config = MCPServerConfig( name="openrouter", command="python", args=["-m", "new.server"] ) # Should replace with force=True manager.add_server(new_config, force=True) assert manager.config["mcpServers"]["openrouter"]["args"] == ["-m", "new.server"] def test_server_not_found_error(self, manager): """Test server not found error handling.""" with pytest.raises(MCPServerNotFoundError) as exc_info: manager.get_server("nonexistent") assert "nonexistent" in str(exc_info.value) def test_get_server_status(self, manager): """Test getting server status.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"] ) manager.add_server(config) status = manager.get_server_status("openrouter") assert status["name"] == "openrouter" assert status["installed"] is True assert "command" in status assert "args" in status def test_backup_config(self, manager, temp_config_file): """Test creating a backup of the configuration.""" config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"] ) manager.add_server(config) backup_path = manager.backup_config() assert backup_path.exists() assert backup_path.suffix == ".backup" # Cleanup if backup_path.exists(): os.unlink(backup_path) def test_restore_config(self, manager, temp_config_file): """Test restoring configuration from backup.""" # Add initial config config = MCPServerConfig( name="openrouter", command="python", args=["-m", "src.openrouter_mcp.server"] ) manager.add_server(config) # Create backup backup_path = manager.backup_config() # Modify config manager.remove_server("openrouter") assert "openrouter" not in manager.config["mcpServers"] # Restore from backup manager.restore_config(backup_path) assert "openrouter" in manager.config["mcpServers"] # Cleanup if backup_path.exists(): os.unlink(backup_path) def test_add_server_from_preset(self, manager): """Test adding a server from a preset configuration.""" preset = manager.add_server_from_preset("openrouter", api_key="test-key") assert "openrouter" in manager.config["mcpServers"] assert manager.config["mcpServers"]["openrouter"]["env"]["OPENROUTER_API_KEY"] == "test-key" def test_save_config_error_handling(self, manager): """Test error handling when saving configuration fails.""" with patch('builtins.open', side_effect=IOError("Permission denied")): with pytest.raises(MCPConfigError) as exc_info: manager.save_config() assert "Failed to save configuration" in str(exc_info.value) def test_load_config_with_invalid_json(self, temp_config_file): """Test loading invalid JSON configuration.""" # Write invalid JSON with open(temp_config_file, 'w') as f: f.write("{ invalid json }") with pytest.raises(MCPConfigError) as exc_info: MCPManager(config_path=temp_config_file) assert "Invalid configuration file" in str(exc_info.value) def test_cross_platform_paths(self, manager): """Test handling cross-platform paths correctly.""" import platform config = MCPServerConfig( name="test", command="python", args=["-m", "module"], cwd="~/project" if platform.system() != "Windows" else "C:\\project" ) manager.add_server(config) saved_config = manager.get_server("test") # Path should be properly expanded assert saved_config.cwd is not None if platform.system() != "Windows": assert not saved_config.cwd.startswith("~") class TestCLIIntegration: """Test CLI command integration.""" @pytest.fixture def cli_runner(self): """Create a CLI test runner.""" from unittest.mock import MagicMock runner = MagicMock() return runner def test_claude_mcp_add_command(self, cli_runner): """Test 'claude mcp add openrouter' command.""" from src.openrouter_mcp.cli.commands import add_mcp_server with patch('src.openrouter_mcp.cli.commands.MCPManager') as MockManager: # Mock the PRESETS class attribute MockManager.PRESETS = {"openrouter": {}} mock_manager = MockManager.return_value mock_manager.add_server_from_preset.return_value = True result = add_mcp_server("openrouter", api_key="test-key") assert result is True mock_manager.add_server_from_preset.assert_called_once_with( "openrouter", api_key="test-key", force=False ) def test_claude_mcp_remove_command(self, cli_runner): """Test 'claude mcp remove openrouter' command.""" from src.openrouter_mcp.cli.commands import remove_mcp_server with patch('src.openrouter_mcp.cli.commands.MCPManager') as MockManager: mock_manager = MockManager.return_value result = remove_mcp_server("openrouter") assert result is True mock_manager.remove_server.assert_called_once_with("openrouter") def test_claude_mcp_list_command(self, cli_runner): """Test 'claude mcp list' command.""" from src.openrouter_mcp.cli.commands import list_mcp_servers with patch('src.openrouter_mcp.cli.commands.MCPManager') as MockManager: mock_manager = MockManager.return_value mock_manager.list_servers.return_value = ["server1", "server2"] servers = list_mcp_servers() assert len(servers) == 2 assert "server1" in servers assert "server2" in servers def test_claude_mcp_status_command(self, cli_runner): """Test 'claude mcp status openrouter' command.""" from src.openrouter_mcp.cli.commands import get_mcp_server_status with patch('src.openrouter_mcp.cli.commands.MCPManager') as MockManager: mock_manager = MockManager.return_value mock_manager.get_server_status.return_value = { "name": "openrouter", "installed": True, "command": "python", "args": [], "cwd": None, "env": {}, "config_path": "/path/to/config" } status = get_mcp_server_status("openrouter") assert status["name"] == "openrouter" assert status["installed"] is True def test_claude_mcp_config_command(self, cli_runner): """Test 'claude mcp config openrouter' command.""" from src.openrouter_mcp.cli.commands import configure_mcp_server with patch('src.openrouter_mcp.cli.commands.MCPManager') as MockManager: mock_manager = MockManager.return_value result = configure_mcp_server( "openrouter", env={"OPENROUTER_API_KEY": "new-key"} ) assert result is True if __name__ == "__main__": pytest.main([__file__, "-v"])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/physics91/openrouter-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server