Skip to main content
Glama
fastmcp-dependency-injection-test-collection.md9.23 kB
--- title: FastMCP Dependency Injection Causing Test Collection Failures slug: fastmcp-dependency-injection-test-collection category: test-failures tags: - fastmcp - mcp-protocol - dependency-injection - pytest - testing - pydantic - migration component: - server.py - tests/test_mcp_tools.py severity: high symptoms: - "PydanticSchemaGenerationError: Unable to generate pydantic-core schema for Progress" - "TypeError: Context.__init__() missing 1 required positional argument: 'fastmcp'" - "AttributeError: 'FastMCP' object has no attribute 'create_initialization_options'" - Tests fail during collection, not execution - All test files fail to load root_cause: | FastMCP uses a different dependency injection pattern than MCP SDK. Context and Progress are special dependency markers that FastMCP injects at runtime, not regular defaults. Using Context() or Progress=None causes Pydantic schema generation failures during test collection. Additionally, MCP SDK's testing utilities are incompatible with FastMCP. related_files: - plans/feat-fastmcp-background-tasks.md - CLAUDE.md - README.md --- # FastMCP Dependency Injection Causing Test Collection Failures ## Problem Summary After migrating from MCP SDK's FastMCP to jlowin's `fastmcp` package, tests fail during pytest collection with Pydantic schema generation errors. The server module cannot be imported because FastMCP's dependency injection markers are not properly configured. ## Symptoms ### Error 1: Pydantic Schema Generation ``` PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'fastmcp.server.dependencies.Progress'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it. ``` ### Error 2: Context Missing Argument ``` TypeError: Context.__init__() missing 1 required positional argument: 'fastmcp' ``` ### Error 3: Incompatible Testing Utilities ``` AttributeError: 'FastMCP' object has no attribute 'create_initialization_options' ``` ## Root Cause Analysis ### 1. Context Dependency Injection FastMCP's `Context` is not meant to be instantiated directly. It requires `CurrentContext()` as a sentinel marker that tells FastMCP to inject the actual context at runtime. **Wrong:** ```python async def my_tool(task: str, ctx: Context = None) -> str: # Won't work async def my_tool(task: str, ctx: Context = Context()) -> str: # Fails - needs 'fastmcp' arg ``` **Correct:** ```python from fastmcp.dependencies import CurrentContext from fastmcp.server.context import Context async def my_tool(task: str, ctx: Context = CurrentContext()) -> str: # Works! ``` ### 2. Progress Dependency Injection Similarly, `Progress` needs `Progress()` as a default (which creates a sentinel), not `None`. **Wrong:** ```python async def my_tool(task: str, progress: Progress = None) -> str: # Pydantic error ``` **Correct:** ```python from fastmcp.dependencies import Progress async def my_tool(task: str, progress: Progress = Progress()) -> str: # Works! ``` ### 3. Testing Framework Incompatibility MCP SDK's `create_connected_server_and_client_session` is incompatible with FastMCP. FastMCP servers must be tested using FastMCP's own `Client` class. **Wrong:** ```python from mcp.shared.memory import create_connected_server_and_client_session async with create_connected_server_and_client_session(app) as session: # Fails: FastMCP doesn't have create_initialization_options ``` **Correct:** ```python from fastmcp import Client async with Client(app) as client: result = await client.call_tool("my_tool", {"param": "value"}) ``` ## Solution ### Step 1: Update Imports in server.py ```python # Before (MCP SDK) from mcp.server.fastmcp import Context, FastMCP # After (FastMCP package) from fastmcp import FastMCP, TaskConfig from fastmcp.dependencies import CurrentContext, Progress from fastmcp.server.context import Context ``` ### Step 2: Fix Tool Signatures ```python @server.tool(task=TaskConfig(mode="optional")) async def run_browser_agent( task: str, max_steps: Optional[int] = None, ctx: Context = CurrentContext(), # noqa: B008 progress: Progress = Progress(), # noqa: B008 ) -> str: """Execute a browser automation task.""" # Implementation... ``` The `# noqa: B008` comments suppress ruff's warning about mutable defaults - these are intentional sentinel values. ### Step 3: Update Tests to Use FastMCP Client ```python """Tests for MCP server tools using FastMCP in-memory testing.""" from collections.abc import AsyncGenerator from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastmcp import Client @pytest.fixture def anyio_backend(): return "asyncio" @pytest.fixture async def client(monkeypatch) -> AsyncGenerator[Client, None]: """Create an in-memory FastMCP client for testing.""" # Set environment variables BEFORE importing server monkeypatch.setenv("MCP_LLM_PROVIDER", "openai") monkeypatch.setenv("MCP_LLM_MODEL_NAME", "gpt-4") monkeypatch.setenv("OPENAI_API_KEY", "test-key") monkeypatch.setenv("MCP_BROWSER_HEADLESS", "true") # Import server after setting env vars from mcp_server_browser_use.server import serve app = serve() async with Client(app) as client: yield client class TestListTools: @pytest.mark.anyio async def test_list_tools(self, client: Client): """Should list all available tools.""" tools = await client.list_tools() tool_names = [tool.name for tool in tools] assert "run_browser_agent" in tool_names assert "run_deep_research" in tool_names class TestRunBrowserAgent: @pytest.mark.anyio async def test_run_browser_agent_success(self, client: Client): """Should successfully run browser agent with mocked dependencies.""" mock_agent = MagicMock() mock_result = MagicMock() mock_result.final_result.return_value = "Task completed" mock_agent.run = AsyncMock(return_value=mock_result) with ( patch("mcp_server_browser_use.server.get_llm", return_value=MagicMock()), patch("mcp_server_browser_use.server.Agent", return_value=mock_agent), ): result = await client.call_tool( "run_browser_agent", {"task": "Go to example.com"} ) # FastMCP returns CallToolResult with content list assert result.content is not None assert "Task completed" in result.content[0].text ``` ### Step 4: Verify Progress Handling in Tools When using Progress, always check if it's available before calling methods: ```python async def run_browser_agent( task: str, progress: Progress = Progress(), ) -> str: # Progress is always a valid object, but check before using if progress: await progress.set_total(steps) await progress.set_message("Starting...") # ... do work ... if progress: await progress.increment() ``` ## Prevention Strategies ### 1. Use Correct Import Pattern Always import from the correct FastMCP modules: ```python # Correct imports for FastMCP from fastmcp import FastMCP, TaskConfig, Client from fastmcp.dependencies import CurrentContext, Progress from fastmcp.server.context import Context ``` ### 2. Use CurrentContext() Not Context() `CurrentContext()` is a sentinel that tells FastMCP to inject the actual context. `Context()` tries to instantiate the class directly and fails. ### 3. Set Environment Variables Before Import In tests, always set environment variables **before** importing the server module: ```python @pytest.fixture async def client(monkeypatch): # 1. Set env vars FIRST monkeypatch.setenv("MCP_LLM_PROVIDER", "openai") # 2. THEN import server from mcp_server_browser_use.server import serve # 3. Create client async with Client(serve()) as client: yield client ``` ### 4. Consult Context7 for FastMCP Documentation When encountering FastMCP issues, use Context7 to fetch current documentation: ```python # Search for FastMCP patterns mcp__plugin_compound-engineering_context7__resolve-library-id(libraryName="fastmcp") mcp__plugin_compound-engineering_context7__get-library-docs( context7CompatibleLibraryID="/jlowin/fastmcp", topic="context progress dependency injection" ) ``` ## Validation Checklist - [ ] All tools use `ctx: Context = CurrentContext()` - [ ] All tools use `progress: Progress = Progress()` (not `None`) - [ ] Tests import `Client` from `fastmcp`, not MCP SDK utilities - [ ] Tests set env vars before importing server module - [ ] `pytest` collection succeeds without errors - [ ] All tests pass with `uv run pytest -v` ## Related Documentation - [FastMCP Background Tasks Plan](../../plans/feat-fastmcp-background-tasks.md) - [FastMCP Official Docs](https://gofastmcp.com/) - [Context7 FastMCP Reference](/jlowin/fastmcp) ## Commands ```bash # Run all tests uv run pytest -v # Run specific test file uv run pytest tests/test_mcp_tools.py -v # Run with short traceback for debugging uv run pytest -v --tb=short ```

Latest Blog Posts

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/Saik0s/mcp-browser-use'

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