Skip to main content
Glama
test_mcp_server.py12 kB
""" Unit tests for the TLS MCP Server These tests verify that our MCP server functions work correctly: 1. Tools are listed properly 2. Certificate fetching works 3. Certificate analysis works 4. Certificate linting works 5. Error handling works properly """ import pytest import asyncio import ssl import socket from unittest.mock import patch, mock_open, MagicMock from pathlib import Path # Import our MCP server functions from tls_mcp_server.main import ( list_tools, call_tool, fetch_certificate, server ) # Sample certificate for testing (Google's cert) SAMPLE_CERT_PEM = """-----BEGIN CERTIFICATE----- MIIEXzCCA0egAwIBAgIRALdEK2HlE3F/7XfVF6WTN58wDQYJKoZIhvcNAQELBQAw RzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkdlb1RydXN0IEluYy4xHzAdBgNVBAMT Fkdlb1RydXN0IFJhcGlkU1NMIENBMB4XDTI0MDEyNDA4MjUyNVoXDTI1MDEyNDA4 MjUyNFowFjEUMBIGA1UEAwwLKi5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZI zj0DAQcDQgAEI8YCDQf6jAD9fIcF7n/8C3hQGk+Ym5h3VpVPUjK7dNbLsGWQJF3s zGCQOCh9Lb0rJm0XfNkEHMKp8BYHlQw6/KOCAuwwggLoMB0GA1UdJQQWMBQGCCsG AQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQN4B3y0L2K 2v1eP7VjJ3xNPfzWVjAfBgNVHSMEGDAWgBSxE/8p8WOHXqBOG2b3GRYd+3XNQTCB 2AYIKwYBBQUHAQEEgcswgcgwJgYIKwYBBQUHMAGGGmh0dHA6Ly9vY3NwLmdlb3Ry dXN0LmNvbS8wgZ0GCCsGAQUFBzACgZCGgY1odHRwOi8vY2VydHMuZ2VvdHJ1c3Qu Y29tL2NlcnRzL2dlb3RydXN0cmFwaWRzc2xjYS5jcnQwgbYGA1UdEQSBrjCBq4IL Ki5nb29nbGUuY29tggxhcGkuZ29vZ2xlLmNvbYIOYXBpcy5nb29nbGUuY29tggwq Lmdvb2dsZS5jb22CDSoueW91dHViZS5jb22CCnlvdXR1YmUuY29tMAwGA1UdEwEB /wQCMAAwDQYJKoZIhvcNAQELBQADggEBAE5m3Qp9FdNf+gPf6yCbCHsxP8jNUJOG k1H3aeFHzTi2H8c7tQKlCpKJ7KRw5o7vKn9B1vZJ7k4J3+hRYl8hP7kRn/ELdCCl 9+5Q3nG9W3uOtB7DJb2k9wCjBZzP4E8KUJ7rZlxEMqK6m8k7l9m3xkJGT5qGhVxf qP8wGlxF4H5Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G 4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y 4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4H8Y4p8Y4qPt5G4 -----END CERTIFICATE-----""" class TestMCPServer: """Test class for MCP Server functionality""" @pytest.mark.asyncio async def test_list_tools(self): """Test that list_tools returns the expected tools""" tools = await list_tools() # Should have exactly 1 tool now (fetch_certificate with options) assert len(tools) == 1 # Check tool name tool = tools[0] assert tool.name == "fetch_certificate" # Check that tool has required fields assert hasattr(tool, 'name') assert hasattr(tool, 'description') assert hasattr(tool, 'inputSchema') assert tool.description is not None assert len(tool.description) > 0 # Check input schema has the new options schema = tool.inputSchema properties = schema["properties"] assert "hostname" in properties assert "port" in properties assert "include_pem" in properties assert "analyze" in properties assert "lint" in properties assert "use_openssl" in properties assert "analyze_ciphers" in properties assert "cipher_scan_type" in properties @pytest.mark.asyncio async def test_call_tool_unknown(self): """Test calling an unknown tool returns error""" result = await call_tool("unknown_tool", {}) assert len(result) == 1 assert result[0].type == "text" assert "Unknown tool" in result[0].text @pytest.mark.asyncio async def test_fetch_certificate_quick_analysis(self): """Test certificate fetching with quick analysis (default)""" with patch('tls_mcp_server.main._fetch_raw_certificate') as mock_fetch, \ patch('tls_mcp_server.main._is_openssl_available') as mock_openssl_check, \ patch('tls_mcp_server.main._analyze_with_cryptography') as mock_analyze: # Setup mocks mock_fetch.return_value = SAMPLE_CERT_PEM mock_openssl_check.return_value = False # Force use of cryptography mock_analyze.return_value = "Mock analysis results" # Test the function with default options (quick analysis) result = await fetch_certificate({"hostname": "example.com"}) # Verify results assert len(result) == 1 assert result[0].type == "text" assert "Certificate Analysis for example.com:443" in result[0].text assert "Mock analysis results" in result[0].text # Verify mocks were called correctly mock_fetch.assert_called_once_with("example.com", 443) mock_analyze.assert_called_once_with(SAMPLE_CERT_PEM, "quick") @pytest.mark.asyncio async def test_fetch_certificate_with_detailed_analysis(self): """Test certificate fetching with detailed analysis""" with patch('tls_mcp_server.main._fetch_raw_certificate') as mock_fetch, \ patch('tls_mcp_server.main._is_openssl_available') as mock_openssl_check, \ patch('tls_mcp_server.main._analyze_with_openssl') as mock_openssl_analyze: # Setup mocks mock_fetch.return_value = SAMPLE_CERT_PEM mock_openssl_check.return_value = True # OpenSSL available mock_openssl_analyze.return_value = "Detailed OpenSSL analysis" # Test with detailed analysis result = await fetch_certificate({ "hostname": "example.com", "analyze": "detailed", "use_openssl": True }) # Verify results assert len(result) == 1 assert result[0].type == "text" assert "Certificate Analysis for example.com:443" in result[0].text assert "Detailed OpenSSL analysis" in result[0].text # Verify OpenSSL was used mock_openssl_analyze.assert_called_once_with(SAMPLE_CERT_PEM, "detailed") @pytest.mark.asyncio async def test_fetch_certificate_with_lint(self): """Test certificate fetching with zlint""" with patch('tls_mcp_server.main._fetch_raw_certificate') as mock_fetch, \ patch('tls_mcp_server.main._analyze_with_cryptography') as mock_analyze, \ patch('tls_mcp_server.main._run_zlint') as mock_zlint: # Setup mocks mock_fetch.return_value = SAMPLE_CERT_PEM mock_analyze.return_value = "Quick analysis" mock_zlint.return_value = "Zlint results" # Test with linting enabled result = await fetch_certificate({ "hostname": "example.com", "lint": True }) # Verify results assert len(result) == 1 assert result[0].type == "text" assert "Certificate Analysis for example.com:443" in result[0].text assert "Quick analysis" in result[0].text assert "Zlint results" in result[0].text # Verify zlint was called mock_zlint.assert_called_once_with(SAMPLE_CERT_PEM) @pytest.mark.asyncio async def test_fetch_certificate_with_pem_output(self): """Test certificate fetching with PEM output included""" with patch('tls_mcp_server.main._fetch_raw_certificate') as mock_fetch, \ patch('tls_mcp_server.main._analyze_with_cryptography') as mock_analyze: # Setup mocks mock_fetch.return_value = SAMPLE_CERT_PEM mock_analyze.return_value = "Analysis results" # Test with PEM output included result = await fetch_certificate({ "hostname": "example.com", "include_pem": True }) # Verify results assert len(result) == 1 assert result[0].type == "text" response_text = result[0].text assert "Certificate Analysis for example.com:443" in response_text assert "Analysis results" in response_text assert "Raw PEM Certificate:" in response_text assert SAMPLE_CERT_PEM in response_text @pytest.mark.asyncio async def test_fetch_certificate_no_analysis(self): """Test certificate fetching with no analysis""" with patch('tls_mcp_server.main._fetch_raw_certificate') as mock_fetch: # Setup mocks mock_fetch.return_value = SAMPLE_CERT_PEM # Test with no analysis result = await fetch_certificate({ "hostname": "example.com", "analyze": "none", "include_pem": True }) # Verify results assert len(result) == 1 assert result[0].type == "text" response_text = result[0].text assert "Certificate Analysis for example.com:443" in response_text assert "Raw PEM Certificate:" in response_text assert SAMPLE_CERT_PEM in response_text @pytest.mark.asyncio async def test_fetch_certificate_connection_error(self): """Test certificate fetching with connection error""" with patch('tls_mcp_server.main._fetch_raw_certificate') as mock_fetch: mock_fetch.side_effect = ConnectionError("Connection refused") result = await fetch_certificate({"hostname": "nonexistent.example", "port": 443}) assert len(result) == 1 assert result[0].type == "text" assert "Failed to fetch certificate" in result[0].text assert "Connection refused" in result[0].text @pytest.mark.asyncio async def test_fetch_certificate_with_cipher_analysis(self): """Test certificate fetching with cipher analysis""" with patch('tls_mcp_server.main._fetch_raw_certificate') as mock_fetch, \ patch('tls_mcp_server.main._analyze_with_cryptography') as mock_analyze, \ patch('tls_mcp_server.main._analyze_cipher_suites') as mock_ciphers: # Setup mocks mock_fetch.return_value = SAMPLE_CERT_PEM mock_analyze.return_value = "Certificate analysis" mock_ciphers.return_value = "Cipher analysis results" # Test with cipher analysis enabled result = await fetch_certificate({ "hostname": "example.com", "analyze_ciphers": True, "cipher_scan_type": "quick" }) # Verify results assert len(result) == 1 assert result[0].type == "text" response_text = result[0].text assert "Certificate Analysis for example.com:443" in response_text assert "Certificate analysis" in response_text assert "Cipher analysis results" in response_text # Verify cipher analysis was called mock_ciphers.assert_called_once_with("example.com", 443, "quick") @pytest.mark.asyncio async def test_call_tool_routing(self): """Test that call_tool correctly routes to fetch_certificate function""" # Test fetch_certificate routing with patch('tls_mcp_server.main.fetch_certificate') as mock_fetch: mock_fetch.return_value = [MagicMock(type="text", text="mocked")] result = await call_tool("fetch_certificate", {"hostname": "test.com"}) mock_fetch.assert_called_once_with({"hostname": "test.com"}) def test_server_instance(self): """Test that server instance is created correctly""" # Verify the server is created with correct name assert server.name == "tls-mcp-server" # Verify server is a proper MCP server instance from mcp.server import Server assert isinstance(server, Server) if __name__ == "__main__": pytest.main([__file__])

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/malaya-zemlya/tls-mcp'

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