|
1 | 1 | """Tests for the MCP (Model Context Protocol) server implementation."""
|
2 | 2 |
|
| 3 | +import asyncio |
| 4 | +from collections.abc import AsyncIterator |
| 5 | + |
| 6 | +import mcp.types as types |
3 | 7 | import pytest
|
| 8 | +from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream |
4 | 9 | from dirty_equals import IsInstance
|
5 | 10 | from inline_snapshot import snapshot
|
6 | 11 |
|
|
11 | 16 | from .conftest import IsDatetime, try_import
|
12 | 17 |
|
13 | 18 | with try_import() as imports_successful:
|
14 |
| - from mcp.types import CallToolResult, TextContent |
| 19 | + from mcp.types import CallToolResult, JSONRPCMessage, TextContent |
15 | 20 |
|
16 |
| - from pydantic_ai.mcp import MCPServerHTTP, MCPServerStdio |
| 21 | + from pydantic_ai.mcp import MCPServer, MCPServerHTTP, MCPServerStdio |
17 | 22 | from pydantic_ai.models.openai import OpenAIModel
|
18 | 23 | from pydantic_ai.providers.openai import OpenAIProvider
|
19 | 24 |
|
20 |
| - |
21 | 25 | pytestmark = [
|
22 | 26 | pytest.mark.skipif(not imports_successful(), reason='mcp and openai not installed'),
|
23 | 27 | pytest.mark.anyio,
|
@@ -98,6 +102,47 @@ async def test_agent_with_stdio_server(allow_model_requests: None, openai_api_ke
|
98 | 102 | )
|
99 | 103 |
|
100 | 104 |
|
| 105 | +async def test_server_with_duplicate_tool_names(allow_model_requests: None, openai_api_key: str): |
| 106 | + class MockClient: |
| 107 | + async def list_tools(self) -> types.ListToolsResult: |
| 108 | + # Await the sleep coroutine to simulate an asynchronous operation |
| 109 | + await asyncio.sleep(0) |
| 110 | + return types.ListToolsResult( |
| 111 | + tools=[ |
| 112 | + types.Tool(name='x', description='', inputSchema={}), |
| 113 | + types.Tool(name='x', description='', inputSchema={}), |
| 114 | + ] |
| 115 | + ) |
| 116 | + |
| 117 | + class MockMCPServer(MCPServer): |
| 118 | + async def client_streams( |
| 119 | + self, |
| 120 | + ) -> AsyncIterator[ |
| 121 | + tuple[MemoryObjectReceiveStream[JSONRPCMessage | Exception], MemoryObjectSendStream[JSONRPCMessage]] |
| 122 | + ]: |
| 123 | + pass |
| 124 | + |
| 125 | + is_running = True |
| 126 | + |
| 127 | + async def __aenter__(self) -> 'MockMCPServer': |
| 128 | + return self |
| 129 | + |
| 130 | + async def __aexit__(self, *args) -> None: |
| 131 | + pass |
| 132 | + |
| 133 | + _client = MockClient() |
| 134 | + |
| 135 | + server = MockMCPServer() |
| 136 | + model = OpenAIModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key)) |
| 137 | + agent = Agent(model, mcp_servers=[server]) |
| 138 | + async with agent.run_mcp_servers(): |
| 139 | + try: |
| 140 | + await agent.run('What is 0 degrees Celsius in Fahrenheit?') |
| 141 | + assert False |
| 142 | + except UserError as e: |
| 143 | + assert e.message == f'Tool names must be unique: {str(["x", "x"])}' |
| 144 | + |
| 145 | + |
101 | 146 | async def test_agent_with_server_not_running(openai_api_key: str):
|
102 | 147 | server = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
103 | 148 | model = OpenAIModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key))
|
|
0 commit comments