"""
MCP Protocol handler tests.
Tests for initialize, tools/list, prompts/list, resources/list protocol methods.
Requirements: 2.1
"""
import pytest
from mcp.protocol import ProtocolHandler
class TestProtocolHandlerInitialize:
"""Tests for initialize protocol method."""
@pytest.fixture
def handler(self):
"""Create a ProtocolHandler instance."""
return ProtocolHandler()
def test_initialize_returns_protocol_version(self, handler):
"""Initialize should return the protocol version."""
request = {"params": {"clientInfo": {"name": "test-client"}}}
response = handler.handle_initialize(request, request_id=1)
assert "result" in response
assert "protocolVersion" in response["result"]
assert response["result"]["protocolVersion"] == "2024-11-05"
def test_initialize_returns_server_info(self, handler):
"""Initialize should return server info."""
request = {"params": {}}
response = handler.handle_initialize(request, request_id=1)
assert "serverInfo" in response["result"]
assert "name" in response["result"]["serverInfo"]
assert "version" in response["result"]["serverInfo"]
def test_initialize_returns_capabilities(self, handler):
"""Initialize should return server capabilities."""
request = {"params": {}}
response = handler.handle_initialize(request, request_id=1)
assert "capabilities" in response["result"]
capabilities = response["result"]["capabilities"]
assert "tools" in capabilities
assert "prompts" in capabilities
assert "resources" in capabilities
def test_initialize_includes_request_id(self, handler):
"""Initialize response should include the request ID."""
request = {"params": {}}
response = handler.handle_initialize(request, request_id=42)
assert response["id"] == 42
class TestProtocolHandlerToolsList:
"""Tests for tools/list protocol method."""
@pytest.fixture
def handler(self):
"""Create a ProtocolHandler instance."""
return ProtocolHandler()
def test_tools_list_returns_tools_array(self, handler):
"""tools/list should return an array of tools."""
tool_definitions = [{"name": "test_tool", "description": "A test tool"}]
response = handler.handle_tools_list(request_id=1, tool_definitions=tool_definitions)
assert "result" in response
assert "tools" in response["result"]
assert isinstance(response["result"]["tools"], list)
def test_tools_list_includes_provided_definitions(self, handler):
"""tools/list should include the provided tool definitions."""
tool_definitions = [
{"name": "tool1", "description": "Tool 1"},
{"name": "tool2", "description": "Tool 2"},
]
response = handler.handle_tools_list(request_id=1, tool_definitions=tool_definitions)
assert len(response["result"]["tools"]) == 2
assert response["result"]["tools"][0]["name"] == "tool1"
assert response["result"]["tools"][1]["name"] == "tool2"
def test_tools_list_includes_request_id(self, handler):
"""tools/list response should include the request ID."""
response = handler.handle_tools_list(request_id=99, tool_definitions=[])
assert response["id"] == 99
class TestProtocolHandlerPromptsList:
"""Tests for prompts/list protocol method."""
@pytest.fixture
def handler(self):
"""Create a ProtocolHandler instance."""
return ProtocolHandler()
def test_prompts_list_returns_prompts_array(self, handler):
"""prompts/list should return an array of prompts."""
response = handler.handle_prompts_list(request_id=1)
assert "result" in response
assert "prompts" in response["result"]
assert isinstance(response["result"]["prompts"], list)
def test_prompts_list_includes_request_id(self, handler):
"""prompts/list response should include the request ID."""
response = handler.handle_prompts_list(request_id=77)
assert response["id"] == 77
class TestProtocolHandlerResourcesList:
"""Tests for resources/list protocol method."""
@pytest.fixture
def handler(self):
"""Create a ProtocolHandler instance."""
return ProtocolHandler()
def test_resources_list_returns_resources_array(self, handler):
"""resources/list should return an array of resources."""
response = handler.handle_resources_list(request_id=1)
assert "result" in response
assert "resources" in response["result"]
assert isinstance(response["result"]["resources"], list)
def test_resources_list_contains_expected_resources(self, handler):
"""resources/list should contain the expected resource URIs."""
response = handler.handle_resources_list(request_id=1)
resources = response["result"]["resources"]
resource_uris = [r["uri"] for r in resources]
# Check for some expected resources
assert "mingli://configuration" in resource_uris
assert "mingli://heavenly-stems" in resource_uris
assert "mingli://five-elements" in resource_uris
def test_resources_list_includes_request_id(self, handler):
"""resources/list response should include the request ID."""
response = handler.handle_resources_list(request_id=55)
assert response["id"] == 55
def test_resources_have_required_fields(self, handler):
"""Each resource should have uri, name, and description."""
response = handler.handle_resources_list(request_id=1)
for resource in response["result"]["resources"]:
assert "uri" in resource
assert "name" in resource
assert "description" in resource
class TestProtocolHandlerPromptsGet:
"""Tests for prompts/get protocol method."""
@pytest.fixture
def handler(self):
"""Create a ProtocolHandler instance."""
return ProtocolHandler()
def test_prompts_get_requires_name(self, handler):
"""prompts/get should return error when name is missing."""
request = {"params": {}}
response = handler.handle_prompts_get(request, request_id=1)
assert "error" in response
assert "name is required" in response["error"]["message"]
def test_prompts_get_rejects_path_traversal(self, handler):
"""prompts/get should reject path traversal attempts."""
request = {"params": {"name": "../etc/passwd"}}
response = handler.handle_prompts_get(request, request_id=1)
assert "error" in response
assert "Invalid prompt" in response["error"]["message"]
def test_prompts_get_rejects_dotted_names(self, handler):
"""prompts/get should reject names starting with dot."""
request = {"params": {"name": ".hidden"}}
response = handler.handle_prompts_get(request, request_id=1)
assert "error" in response
class TestProtocolHandlerResourcesGet:
"""Tests for resources/get protocol method."""
@pytest.fixture
def handler(self):
"""Create a ProtocolHandler instance."""
return ProtocolHandler()
def test_resources_get_requires_uri(self, handler):
"""resources/get should return error when URI is missing."""
request = {"params": {}}
response = handler.handle_resources_get(request, request_id=1)
assert "error" in response
assert "URI is required" in response["error"]["message"]
def test_resources_get_returns_error_for_unknown_uri(self, handler):
"""resources/get should return error for unknown URIs."""
request = {"params": {"uri": "mingli://unknown-resource"}}
response = handler.handle_resources_get(request, request_id=1)
assert "error" in response
assert "not found" in response["error"]["message"]
def test_resources_get_returns_content_for_valid_uri(self, handler):
"""resources/get should return content for valid URIs."""
request = {"params": {"uri": "mingli://configuration"}}
response = handler.handle_resources_get(request, request_id=1)
assert "result" in response
assert "contents" in response["result"]