From 2b51b2f8fa976d556ebf1672d5532db7b287ff7c Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Tue, 11 Aug 2026 21:36:37 -0700 Subject: [PATCH] Resolve FastMCP settings before lifecycle tests --- pyproject.toml | 5 +++++ tests/conftest.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index aa379d4e..54d0f420 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,6 +108,11 @@ markers = [ "e2e_recovery: opt-in end-to-end SSE recovery harness (real server + real SSE consumers, scripted provider — NOT live, no LLM backend needed); tens of seconds each. CI lanes run ``-m 'not live and not e2e_recovery'``; select with ``-m e2e_recovery``.", ] filterwarnings = [ + # The MCP v1 FastMCP integration fixture rebuilds its incomplete generic + # Settings model before construction. Keep the new pydantic-settings 2.15 + # diagnostic fatal so removing that compatibility step cannot regress + # into a warning hidden in the full-suite summary. + "error:Field 'lifespan' has an incomplete definition", # mcp v1 deprecates streamablehttp_client for an entry point whose call # shape only settles in v2 — adoption rides the deliberate v2 migration # (pin capped <2); silence exactly this message until then. diff --git a/tests/conftest.py b/tests/conftest.py index e6493602..53258715 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,6 +15,20 @@ from unittest.mock import MagicMock import pytest +def pytest_sessionstart(session: pytest.Session) -> None: + """Resolve MCP v1's generic FastMCP settings model for test servers. + + MCP 1.29.0 defines ``Settings`` before ``FastMCP``, so its ``lifespan`` + annotation remains an unresolved forward reference after import. Rebuild + once, after the module is fully loaded, before any integration fixture + constructs a FastMCP server. Pydantic's public hook is a no-op once the SDK + ships a complete model. + """ + from mcp.server.fastmcp.server import Settings as FastMCPSettings + + FastMCPSettings.model_rebuild() + + def stop_loop_thread(loop: asyncio.AbstractEventLoop, thread: threading.Thread) -> None: """Fully tear down a ``loop.run_forever``-in-a-thread test loop.