172 lines
6.2 KiB
Python
172 lines
6.2 KiB
Python
"""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
|