FastAPI-MCP

  • tests
import pytest from fastapi import FastAPI from pydantic import BaseModel from typing import List, Optional from fastapi_mcp import add_mcp_server class Item(BaseModel): id: int name: str description: Optional[str] = None price: float tags: List[str] = [] @pytest.fixture def sample_app(): """Create a sample FastAPI app for testing.""" app = FastAPI( title="Test API", description="A test API for unit testing", version="0.1.0", ) @app.get("/items/", response_model=List[Item], tags=["items"]) async def list_items(skip: int = 0, limit: int = 10): """ List all items. Returns a list of items, with pagination support. """ return [] @app.get("/items/{item_id}", response_model=Item, tags=["items"]) async def read_item(item_id: int): """ Get a specific item by ID. Returns the item with the specified ID. """ return {"id": item_id, "name": "Test Item", "price": 0.0} @app.post("/items/", response_model=Item, tags=["items"]) async def create_item(item: Item): """ Create a new item. Returns the created item. """ return item return app def test_tool_generation_basic(sample_app): """Test that MCP tools are properly generated with default settings.""" # Create MCP server and register tools mcp_server = add_mcp_server(sample_app, serve_tools=True, base_url="http://localhost:8000") # Extract tools for inspection tools = mcp_server._tool_manager.list_tools() # Tool count may include the MCP endpoint itself, so check for at least the API endpoints assert len(tools) >= 3, f"Expected at least 3 tools, got {len(tools)}" # Check each tool has required properties for tool in tools: assert hasattr(tool, "name"), "Tool missing 'name' property" assert hasattr(tool, "description"), "Tool missing 'description' property" assert hasattr(tool, "parameters"), "Tool missing 'parameters' property" assert hasattr(tool, "fn_metadata"), "Tool missing 'fn_metadata' property" # Skip MCP's internal tool that doesn't follow the same patterns if tool.name == "handle_mcp_connection_mcp_get": continue # With describe_all_responses=False by default, description should only include success response code assert "200" in tool.description, f"Expected success response code in description for {tool.name}" assert "422" not in tool.description, f"Expected not to see 422 response in tool description for {tool.name}" # With describe_full_response_schema=False by default, description should not include the full output schema, only an example assert "Example Response" in tool.description, f"Expected example response in description for {tool.name}" assert "Output Schema" not in tool.description, ( f"Expected not to see output schema in description for {tool.name}" ) # Verify specific parameters are present in the appropriate tools list_items_tool = next((t for t in tools if t.name == "list_items_items__get"), None) assert list_items_tool is not None, "list_items tool not found" assert "skip" in list_items_tool.parameters["properties"], "Expected 'skip' parameter" assert "limit" in list_items_tool.parameters["properties"], "Expected 'limit' parameter" def test_tool_generation_with_full_schema(sample_app): """Test that MCP tools include full response schema when requested.""" # Create MCP server with full schema for all operations mcp_server = add_mcp_server( sample_app, serve_tools=True, base_url="http://localhost:8000", describe_full_response_schema=True ) # Extract tools for inspection tools = mcp_server._tool_manager.list_tools() # Check all tools have the appropriate schema information for tool in tools: # Skip MCP's internal tool that doesn't follow the same patterns if tool.name == "handle_mcp_connection_mcp_get": continue description = tool.description # Check that the tool includes information about the Item schema assert "Item" in description, f"Item schema should be included in the description for {tool.name}" assert "price" in description, f"Item properties should be included in the description for {tool.name}" def test_tool_generation_with_all_responses(sample_app): """Test that MCP tools include all possible responses when requested.""" # Create MCP server with all response status codes mcp_server = add_mcp_server( sample_app, serve_tools=True, base_url="http://localhost:8000", describe_all_responses=True ) # Extract tools for inspection tools = mcp_server._tool_manager.list_tools() # Check all API tools include all response status codes for tool in tools: # Skip MCP's internal tool that doesn't follow the same patterns if tool.name == "handle_mcp_connection_mcp_get": continue assert "200" in tool.description, f"Expected success response code in description for {tool.name}" assert "422" in tool.description, f"Expected 422 response code in description for {tool.name}" def test_tool_generation_with_all_responses_and_full_schema(sample_app): """Test that MCP tools include all possible responses and full schema when requested.""" # Create MCP server with all response status codes and full schema mcp_server = add_mcp_server( sample_app, serve_tools=True, base_url="http://localhost:8000", describe_all_responses=True, describe_full_response_schema=True, ) # Extract tools for inspection tools = mcp_server._tool_manager.list_tools() # Check all tools include all response status codes and the full output schema for tool in tools: # Skip MCP's internal tool that doesn't follow the same patterns if tool.name == "handle_mcp_connection_mcp_get": continue assert "200" in tool.description, f"Expected success response code in description for {tool.name}" assert "422" in tool.description, f"Expected 422 response code in description for {tool.name}" assert "Output Schema" in tool.description, f"Expected output schema in description for {tool.name}" def test_custom_tool_addition(sample_app): """Test that custom tools can be added alongside API tools.""" # Create MCP server with API tools mcp_server = add_mcp_server(sample_app, serve_tools=True, base_url="http://localhost:8000") # Get initial tool count initial_tool_count = len(mcp_server._tool_manager.list_tools()) # Add a custom tool @mcp_server.tool() async def custom_tool() -> str: """A custom tool for testing.""" return "Test result" # Extract tools for inspection tools = mcp_server._tool_manager.list_tools() # Verify we have one more tool than before assert len(tools) == initial_tool_count + 1, f"Expected {initial_tool_count + 1} tools, got {len(tools)}" # Find both API tools and custom tools list_items_tool = next((t for t in tools if t.name == "list_items_items__get"), None) assert list_items_tool is not None, "API tool (list_items) not found" custom_tool_def = next((t for t in tools if t.name == "custom_tool"), None) assert custom_tool_def is not None, "Custom tool not found" assert custom_tool_def.description == "A custom tool for testing.", "Custom tool description not preserved"