File size: 6,389 Bytes
cc899d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
---
title: Testing Your Server
sidebarTitle: Testing
description: Unit test your MCP servers with the FastMCP Client's deterministic testing capabilities
icon: vial
---

The [FastMCP Client](/clients/client) is a deterministic testing tool that gives you complete programmatic control over MCP server interactions. You call specific tools with exact arguments, verify responses, and test edge cases - making it ideal for unit testing your MCP servers.

## In-Memory Testing

The FastMCP Client's standout feature is in-memory testing. Instead of deploying your server or managing network connections, you pass your server instance directly to the client. This creates a zero-overhead connection that runs entirely in memory.

What makes this approach so powerful is that everything runs in the same Python process. You can set breakpoints anywhere - in your test code or inside your server handlers - and step through with your debugger. There's no server startup scripts, no port management, no cleanup between tests. Tests execute instantly without network overhead.

```python
from fastmcp import FastMCP, Client

# Create your server
server = FastMCP("WeatherServer")

@server.tool
def get_temperature(city: str) -> dict:
    """Get current temperature for a city"""
    temps = {"NYC": 72, "LA": 85, "Chicago": 68}
    return {"city": city, "temp": temps.get(city, 70)}

@server.resource("weather://forecast")
def get_forecast() -> dict:
    """Get 5-day forecast"""
    return {"days": 5, "conditions": "sunny"}

async def test_weather_operations():
    # Pass server directly - no deployment needed
    async with Client(server) as client:
        # Test tool execution
        result = await client.call_tool("get_temperature", {"city": "NYC"})
        assert result.data == {"city": "NYC", "temp": 72}
        
        # Test resource retrieval
        forecast = await client.read_resource("weather://forecast")
        assert forecast.contents[0].data == {"days": 5, "conditions": "sunny"}
```

The in-memory approach transforms MCP testing from a deployment challenge into standard unit testing. You focus on testing your server's behavior, not wrestling with infrastructure.

## Testing with Frameworks

The FastMCP Client works seamlessly with any Python testing framework. Whether you prefer pytest, unittest, or another framework, the pattern remains consistent: create a server, pass it to the client, and verify behavior.

```python
import pytest
from fastmcp import FastMCP, Client

@pytest.fixture
def weather_server():
    server = FastMCP("WeatherServer")
    
    @server.tool
    def get_temperature(city: str) -> dict:
        temps = {"NYC": 72, "LA": 85, "Chicago": 68}
        return {"city": city, "temp": temps.get(city, 70)}
    
    return server

@pytest.mark.asyncio
async def test_temperature_tool(weather_server):
    async with Client(weather_server) as client:
        result = await client.call_tool("get_temperature", {"city": "LA"})
        assert result.data == {"city": "LA", "temp": 85}

@pytest.mark.asyncio
async def test_unknown_city(weather_server):
    async with Client(weather_server) as client:
        result = await client.call_tool("get_temperature", {"city": "Paris"})
        assert result.data["temp"] == 70  # Default temperature
```

## Mocking External Dependencies

FastMCP servers are standard Python objects, so you can mock external dependencies using your preferred mocking approach. Replace databases, APIs, or any external service with test doubles to keep your tests fast and deterministic.

```python
from unittest.mock import AsyncMock

async def test_database_tool():
    server = FastMCP("DataServer")
    
    # Mock the database
    mock_db = AsyncMock()
    mock_db.fetch_users.return_value = [
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"}
    ]
    
    @server.tool
    async def list_users() -> list:
        return await mock_db.fetch_users()
    
    async with Client(server) as client:
        result = await client.call_tool("list_users", {})
        assert len(result.data) == 2
        assert result.data[0]["name"] == "Alice"
        mock_db.fetch_users.assert_called_once()
```

## Testing Deployed Servers

While in-memory testing covers most unit testing needs, you'll occasionally need to test against a deployed server - to verify authentication, test network behavior, or validate deployments.

### HTTP Transport Testing

When you need to test actual network behavior or verify a deployment, connect to your running server using its URL:

```python
from fastmcp import Client

async def test_deployed_server():
    # Connect to a running server
    async with Client("http://localhost:8000/mcp/") as client:
        await client.ping()
        
        # Test with real network transport
        tools = await client.list_tools()
        assert len(tools) > 0
        
        result = await client.call_tool("greet", {"name": "World"})
        assert "Hello" in result.data
```

### Testing Authentication

The FastMCP Client handles authentication transparently, making it easy to test secured servers:

```python
async def test_authenticated_server():
    # Bearer token authentication
    async with Client(
        "https://api.example.com/mcp",
        headers={"Authorization": "Bearer test-token"}
    ) as client:
        await client.ping()
        tools = await client.list_tools()
        
    # OAuth flow (opens browser for authorization)
    async with Client("https://api.example.com/mcp", auth="oauth") as client:
        result = await client.call_tool("protected_tool", {})
        assert result.data is not None
```

## Best Practices

1. **Default to in-memory testing** - It's faster, more reliable, and easier to debug
2. **Test behavior, not implementation** - Call tools and verify responses rather than testing internals
3. **Use framework fixtures** - Create reusable server configurations for your test suite
4. **Mock external dependencies** - Keep tests fast and deterministic by mocking databases, APIs, etc.
5. **Test error cases** - Verify your server handles invalid inputs and edge cases properly

The FastMCP Client transforms MCP server testing from a deployment challenge into a straightforward unit testing task. With in-memory connections and deterministic control, you can build comprehensive test suites that run in milliseconds.