"""Tests for MCPServerConfig and ServerConfig MCP section parsing""" import pytest from agentkit.server.config import MCPServerConfig, ServerConfig class TestMCPServerConfig: """Tests for MCPServerConfig dataclass""" def test_from_dict_stdio(self): data = { "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], "env": {"NODE_ENV": "production"}, "timeout": 60.0, } config = MCPServerConfig.from_dict(data) assert config.transport == "stdio" assert config.command == "npx" assert config.args == ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] assert config.env == {"NODE_ENV": "production"} assert config.timeout == 60.0 assert config.url is None assert config.headers is None def test_from_dict_streamable_http(self): data = { "transport": "streamable_http", "url": "http://localhost:3001/mcp", "headers": {"Authorization": "Bearer test-token"}, "timeout": 45.0, } config = MCPServerConfig.from_dict(data) assert config.transport == "streamable_http" assert config.url == "http://localhost:3001/mcp" assert config.headers == {"Authorization": "Bearer test-token"} assert config.timeout == 45.0 assert config.command is None assert config.args is None def test_from_dict_sse(self): data = { "transport": "sse", "url": "http://localhost:3002/sse", } config = MCPServerConfig.from_dict(data) assert config.transport == "sse" assert config.url == "http://localhost:3002/sse" assert config.command is None def test_from_dict_defaults(self): data = {} config = MCPServerConfig.from_dict(data) assert config.transport == "stdio" assert config.command is None assert config.args is None assert config.env is None assert config.url is None assert config.headers is None assert config.timeout == 30.0 def test_validate_stdio_valid(self): config = MCPServerConfig(transport="stdio", command="python") config.validate() # Should not raise def test_validate_stdio_missing_command(self): config = MCPServerConfig(transport="stdio", command=None) with pytest.raises(ValueError, match="stdio transport requires 'command'"): config.validate() def test_validate_streamable_http_missing_url(self): config = MCPServerConfig(transport="streamable_http", url=None) with pytest.raises(ValueError, match="streamable_http transport requires 'url'"): config.validate() def test_validate_sse_missing_url(self): config = MCPServerConfig(transport="sse", url=None) with pytest.raises(ValueError, match="sse transport requires 'url'"): config.validate() def test_validate_invalid_transport(self): config = MCPServerConfig(transport="websocket") with pytest.raises(ValueError, match="Invalid transport: websocket"): config.validate() def test_validate_http_with_url(self): config = MCPServerConfig(transport="streamable_http", url="http://localhost:3001") config.validate() # Should not raise def test_validate_sse_with_url(self): config = MCPServerConfig(transport="sse", url="http://localhost:3002") config.validate() # Should not raise class TestServerConfigMCPSection: """Tests for ServerConfig parsing with mcp section""" def test_from_dict_with_mcp_servers(self): data = { "mcp": { "servers": { "filesystem": { "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], }, "remote": { "transport": "streamable_http", "url": "http://localhost:3001/mcp", }, } } } config = ServerConfig.from_dict(data) assert len(config.mcp_servers) == 2 assert "filesystem" in config.mcp_servers assert "remote" in config.mcp_servers assert config.mcp_servers["filesystem"].transport == "stdio" assert config.mcp_servers["filesystem"].command == "npx" assert config.mcp_servers["remote"].transport == "streamable_http" assert config.mcp_servers["remote"].url == "http://localhost:3001/mcp" def test_from_dict_without_mcp_section(self): data = {} config = ServerConfig.from_dict(data) assert config.mcp_servers == {} def test_from_dict_with_empty_mcp_servers(self): data = {"mcp": {"servers": {}}} config = ServerConfig.from_dict(data) assert config.mcp_servers == {} def test_from_dict_mcp_servers_with_sse(self): data = { "mcp": { "servers": { "sse-server": { "transport": "sse", "url": "http://localhost:3002/sse", "headers": {"X-API-Key": "secret"}, "timeout": 60.0, } } } } config = ServerConfig.from_dict(data) assert len(config.mcp_servers) == 1 sse_conf = config.mcp_servers["sse-server"] assert sse_conf.transport == "sse" assert sse_conf.url == "http://localhost:3002/sse" assert sse_conf.headers == {"X-API-Key": "secret"} assert sse_conf.timeout == 60.0 def test_from_dict_mcp_ignores_non_dict_entries(self): data = { "mcp": { "servers": { "valid": { "transport": "stdio", "command": "python", }, "invalid": "not-a-dict", } } } config = ServerConfig.from_dict(data) assert len(config.mcp_servers) == 1 assert "valid" in config.mcp_servers assert "invalid" not in config.mcp_servers