Skip to main content
Glama

Codebase MCP Server

by Ravenight13
test_tool_registration.py9 kB
""" T005: Contract test for tool registration validation. This test validates that all 6 MCP tools are correctly registered with the @mcp.tool() decorator in the FastMCP server implementation. TDD Approach: - Test MUST fail initially (tools not yet decorated in server_fastmcp.py) - After T008-T013 (tool migration), test should pass - Validates both registration and callability of tools Constitutional Compliance: - Principle VII: TDD (tests written first, must fail initially) - Principle VIII: Type Safety (full type annotations) - Principle XI: FastMCP Foundation (validates FastMCP decorator registration) """ from __future__ import annotations from typing import TYPE_CHECKING import pytest if TYPE_CHECKING: from fastmcp import FastMCP # Expected tools for Codebase MCP Server EXPECTED_TOOLS = { "search_code", "index_repository", "create_task", "get_task", "list_tasks", "update_task", } @pytest.fixture def mcp_server() -> FastMCP: """Fixture providing the FastMCP server instance. Returns: FastMCP server instance from server_fastmcp.py """ from src.mcp.server_fastmcp import mcp return mcp @pytest.mark.contract @pytest.mark.asyncio async def test_all_tools_registered(mcp_server: FastMCP) -> None: """Verify all 6 MCP tools are registered with @mcp.tool() decorator. This test validates that the server has registered all required tools for the Codebase MCP Server functionality. Expected to FAIL initially: - server_fastmcp.py has no @mcp.tool() decorators yet - Tools will be migrated in T008-T013 Expected to PASS after: - T008: search_code migration - T009: index_repository migration - T010: create_task migration - T011: get_task migration - T012: list_tasks migration - T013: update_task migration Args: mcp_server: FastMCP server instance from fixture Raises: AssertionError: If registered tools don't match expected tools """ # Get registered tools from FastMCP server registered_tools_dict = await mcp_server.get_tools() registered_tool_names = set(registered_tools_dict.keys()) # Validate: All expected tools are registered missing_tools = EXPECTED_TOOLS - registered_tool_names extra_tools = registered_tool_names - EXPECTED_TOOLS # Assertion 1: Check for missing tools assert not missing_tools, ( f"Missing expected tools: {missing_tools}\n" f"Expected: {sorted(EXPECTED_TOOLS)}\n" f"Registered: {sorted(registered_tool_names)}\n" f"Fix: Add @mcp.tool() decorator to missing tool handlers in T008-T013" ) # Assertion 2: Check for unexpected extra tools assert not extra_tools, ( f"Unexpected tools registered: {extra_tools}\n" f"Expected: {sorted(EXPECTED_TOOLS)}\n" f"Registered: {sorted(registered_tool_names)}\n" f"Fix: Remove unexpected tool registrations from server_fastmcp.py" ) # Assertion 3: Validate exact count assert len(registered_tool_names) == 6, ( f"Expected exactly 6 tools, found {len(registered_tool_names)}\n" f"Registered: {sorted(registered_tool_names)}" ) @pytest.mark.contract @pytest.mark.asyncio async def test_tools_are_callable(mcp_server: FastMCP) -> None: """Verify each registered tool is callable (proper decorator registration). This test validates that all registered tools have been properly decorated with @mcp.tool() and are callable functions. Args: mcp_server: FastMCP server instance from fixture Raises: AssertionError: If any tool is not callable """ # Get registered tools from FastMCP server registered_tools_dict = await mcp_server.get_tools() # Skip test if no tools registered (expected initial state) if not registered_tools_dict: pytest.skip("No tools registered yet - expected for TDD initial state") # Validate: Each tool is callable for tool_name, tool_obj in registered_tools_dict.items(): # Check if tool has __call__ attribute (is callable) assert hasattr(tool_obj, "__call__"), ( f"Tool '{tool_name}' is not callable\n" f"Fix: Ensure tool is properly registered with @mcp.tool() decorator" ) # Validate tool name matches expected set assert tool_name in EXPECTED_TOOLS, ( f"Tool '{tool_name}' is not in expected tool set\n" f"Expected: {sorted(EXPECTED_TOOLS)}\n" f"Fix: Remove unexpected tool registration or update EXPECTED_TOOLS" ) @pytest.mark.contract @pytest.mark.asyncio async def test_tool_registration_count(mcp_server: FastMCP) -> None: """Verify exactly 6 tools are registered (no more, no less). This test provides a focused assertion on the tool count, making it easy to identify when tools are missing or extras are added. Args: mcp_server: FastMCP server instance from fixture Raises: AssertionError: If tool count is not exactly 6 """ registered_tools_dict = await mcp_server.get_tools() tool_count = len(registered_tools_dict) assert tool_count == 6, ( f"Expected exactly 6 tools registered, found {tool_count}\n" f"Registered tools: {sorted(registered_tools_dict.keys()) if tool_count > 0 else 'None'}\n" f"Expected tools: {sorted(EXPECTED_TOOLS)}\n" f"Fix: Complete tool migration in T008-T013" ) @pytest.mark.contract @pytest.mark.asyncio async def test_search_code_tool_registered(mcp_server: FastMCP) -> None: """Verify search_code tool is registered (T008 validation).""" registered_tools_dict = await mcp_server.get_tools() assert "search_code" in registered_tools_dict, ( "search_code tool not registered\n" "Fix: Complete T008 - migrate search_code tool with @mcp.tool() decorator" ) @pytest.mark.contract @pytest.mark.asyncio async def test_index_repository_tool_registered(mcp_server: FastMCP) -> None: """Verify index_repository tool is registered (T009 validation).""" registered_tools_dict = await mcp_server.get_tools() assert "index_repository" in registered_tools_dict, ( "index_repository tool not registered\n" "Fix: Complete T009 - migrate index_repository tool with @mcp.tool() decorator" ) @pytest.mark.contract @pytest.mark.asyncio async def test_create_task_tool_registered(mcp_server: FastMCP) -> None: """Verify create_task tool is registered (T010 validation).""" registered_tools_dict = await mcp_server.get_tools() assert "create_task" in registered_tools_dict, ( "create_task tool not registered\n" "Fix: Complete T010 - migrate create_task tool with @mcp.tool() decorator" ) @pytest.mark.contract @pytest.mark.asyncio async def test_get_task_tool_registered(mcp_server: FastMCP) -> None: """Verify get_task tool is registered (T011 validation).""" registered_tools_dict = await mcp_server.get_tools() assert "get_task" in registered_tools_dict, ( "get_task tool not registered\n" "Fix: Complete T011 - migrate get_task tool with @mcp.tool() decorator" ) @pytest.mark.contract @pytest.mark.asyncio async def test_list_tasks_tool_registered(mcp_server: FastMCP) -> None: """Verify list_tasks tool is registered (T012 validation).""" registered_tools_dict = await mcp_server.get_tools() assert "list_tasks" in registered_tools_dict, ( "list_tasks tool not registered\n" "Fix: Complete T012 - migrate list_tasks tool with @mcp.tool() decorator" ) @pytest.mark.contract @pytest.mark.asyncio async def test_update_task_tool_registered(mcp_server: FastMCP) -> None: """Verify update_task tool is registered (T013 validation).""" registered_tools_dict = await mcp_server.get_tools() assert "update_task" in registered_tools_dict, ( "update_task tool not registered\n" "Fix: Complete T013 - migrate update_task tool with @mcp.tool() decorator" ) @pytest.mark.contract @pytest.mark.asyncio async def test_no_duplicate_tool_registrations(mcp_server: FastMCP) -> None: """Verify no duplicate tool registrations exist. This test ensures that each tool is registered exactly once, preventing registration conflicts or unexpected behavior. Args: mcp_server: FastMCP server instance from fixture Raises: AssertionError: If duplicate tool registrations are detected """ registered_tools_dict = await mcp_server.get_tools() tool_names = list(registered_tools_dict.keys()) # Check for duplicates by comparing list length to set length unique_tool_names = set(tool_names) assert len(tool_names) == len(unique_tool_names), ( f"Duplicate tool registrations detected\n" f"Tool names: {tool_names}\n" f"Unique names: {unique_tool_names}\n" f"Fix: Ensure each tool is registered only once with @mcp.tool()" )

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ravenight13/codebase-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server