Production MCP Systems
Testing and Best Practices
5 min read
Comprehensive testing ensures your MCP server works correctly in all scenarios.
Test Categories
| Type | Purpose | Tools |
|---|---|---|
| Unit | Test individual functions | pytest |
| Integration | Test components together | pytest-asyncio |
| E2E | Full system tests | MCP Inspector |
Unit Testing Tools
import pytest
from unittest.mock import AsyncMock, patch
from server import WeatherTool
class TestWeatherTool:
def test_get_definition(self):
tool = WeatherTool()
definition = tool.get_tool_definition()
assert definition["name"] == "get_weather"
assert "city" in definition["inputSchema"]["properties"]
def test_validate_valid_input(self):
tool = WeatherTool()
assert tool.validate({"city": "London"}) == True
def test_validate_empty_city(self):
tool = WeatherTool()
with pytest.raises(ValueError, match="City is required"):
tool.validate({"city": ""})
@pytest.mark.asyncio
async def test_execute(self):
tool = WeatherTool()
result = await tool.execute({"city": "London"})
assert "temperature" in result
assert "condition" in result
Integration Testing
import pytest
from httpx import AsyncClient
from server import app
@pytest.fixture
async def client():
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
@pytest.mark.asyncio
async def test_mcp_endpoint(client):
response = await client.post("/mcp", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
})
assert response.status_code == 200
data = response.json()
assert "tools" in data
@pytest.mark.asyncio
async def test_tool_call(client):
response = await client.post("/mcp", json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {"city": "Tokyo"}
}
})
assert response.status_code == 200
assert response.json()["result"]
Mocking External Services
@pytest.mark.asyncio
async def test_weather_api_failure():
with patch("server.fetch_weather", new_callable=AsyncMock) as mock:
mock.side_effect = Exception("API unavailable")
tool = WeatherTool()
with pytest.raises(McpError) as exc:
await tool.execute({"city": "London"})
assert "unavailable" in str(exc.value)
Best Practices Summary
| Practice | Benefit |
|---|---|
| Use environment variables | Security, flexibility |
| Implement health checks | Reliability monitoring |
| Add request timeouts | Prevent hanging |
| Log structured data | Easy debugging |
| Version your API | Backwards compatibility |
| Document tools clearly | Better AI usage |
| Test edge cases | Robust handling |
Production Checklist
- All secrets in environment variables
- Health check endpoint implemented
- Prometheus metrics exposed
- Structured logging configured
- Rate limiting enabled
- CORS properly configured
- SSL/TLS enabled
- Error handling comprehensive
- Tests passing in CI/CD
Congratulations! You've completed the MCP Mastery course. :::