import json from typing import Any import mcp.types import pytest from dirty_equals import Contains from mcp import McpError from fastmcp import FastMCP from fastmcp.client import Client from fastmcp.client.transports import FastMCPTransport from fastmcp.exceptions import ToolError from fastmcp.server.proxy import FastMCPProxy USERS = [ {"id": "1", "name": "Alice", "active": True}, {"id": "2", "name": "Bob", "active": True}, {"id": "3", "name": "Charlie", "active": False}, ] @pytest.fixture def fastmcp_server(): server = FastMCP("TestServer") # --- Tools --- @server.tool() def greet(name: str) -> str: """Greet someone by name.""" return f"Hello, {name}!" @server.tool() def tool_without_description() -> str: return "Hello?" @server.tool() def add(a: int, b: int) -> int: """Add two numbers together.""" return a + b @server.tool() def error_tool(): """This tool always raises an error.""" raise ValueError("This is a test error") # --- Resources --- @server.resource(uri="resource://wave") def wave() -> str: return "👋" @server.resource(uri="data://users") async def get_users() -> list[dict[str, Any]]: return USERS @server.resource(uri="data://user/{user_id}") async def get_user(user_id: str) -> dict[str, Any] | None: return next((user for user in USERS if user["id"] == user_id), None) # --- Prompts --- @server.prompt() def welcome(name: str) -> str: return f"Welcome to FastMCP, {name}!" return server @pytest.fixture async def proxy_server(fastmcp_server): """Fixture that creates a FastMCP proxy server.""" return FastMCP.as_proxy(Client(transport=FastMCPTransport(fastmcp_server))) async def test_create_proxy(fastmcp_server): """Test that the proxy server properly forwards requests to the original server.""" # Create a client client = Client(transport=FastMCPTransport(fastmcp_server)) server = FastMCPProxy.as_proxy(client) assert isinstance(server, FastMCPProxy) assert isinstance(server, FastMCP) assert server.name == "FastMCP" async def test_as_proxy_with_server(fastmcp_server): """FastMCP.as_proxy should accept a FastMCP instance.""" proxy = FastMCP.as_proxy(fastmcp_server) result = await proxy._mcp_call_tool("greet", {"name": "Test"}) assert isinstance(result[0], mcp.types.TextContent) assert result[0].text == "Hello, Test!" async def test_as_proxy_with_transport(fastmcp_server): """FastMCP.as_proxy should accept a ClientTransport.""" proxy = FastMCP.as_proxy(FastMCPTransport(fastmcp_server)) result = await proxy._mcp_call_tool("greet", {"name": "Test"}) assert isinstance(result[0], mcp.types.TextContent) assert result[0].text == "Hello, Test!" def test_as_proxy_with_url(): """FastMCP.as_proxy should accept a URL without connecting.""" proxy = FastMCP.as_proxy("http://example.com/mcp") assert isinstance(proxy, FastMCPProxy) assert repr(proxy.client.transport).startswith("