fastmcp/tests/client/test_slim_package_boundaries.py
2026-05-22 18:41:19 -04:00

92 lines
3 KiB
Python

from __future__ import annotations
import builtins
import contextlib
import types
from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any, cast
import pytest
PROJECT_ROOT = Path(__file__).resolve().parents[2]
@contextlib.contextmanager
def block_server_imports():
original_import = builtins.__import__
def blocked_import(
name: str,
globals: Mapping[str, object] | None = None,
locals: Mapping[str, object] | None = None,
fromlist: Sequence[str] | None = (),
level: int = 0,
) -> types.ModuleType:
if level == 0 and (
name == "fastmcp.server" or name.startswith("fastmcp.server.")
):
raise ImportError(f"blocked server import: {name}")
return original_import(name, globals, locals, fromlist, level)
cast(Any, builtins).__import__ = blocked_import
try:
yield
finally:
cast(Any, builtins).__import__ = original_import
def test_client_http_headers_do_not_require_server() -> None:
from fastmcp.client.dependencies import get_http_headers
with block_server_imports():
assert get_http_headers(include_all=True) == {}
async def test_multiserver_config_requires_server_for_now() -> None:
from fastmcp.client.transports import MCPConfigTransport
with block_server_imports():
transport = MCPConfigTransport(
{
"mcpServers": {
"one": {"command": "uvx", "args": ["one"]},
"two": {"command": "uvx", "args": ["two"]},
}
}
)
with pytest.raises(
ImportError, match="multiple servers require the full `fastmcp`"
):
async with transport.connect_session():
pass
def test_fastmcp_metapackage_delegates_to_slim() -> None:
pyproject = (PROJECT_ROOT / "pyproject.toml").read_text()
assert 'dynamic = ["version", "dependencies", "optional-dependencies"]' in pyproject
assert "[project.scripts]" in pyproject
assert 'fastmcp = "fastmcp.cli:app"' in pyproject
assert "[tool.hatch.build.targets.wheel]" in pyproject
assert "bypass-selection = true" in pyproject
assert "only-include = []" in pyproject
assert 'exclude = ["/*"]' in pyproject
assert '"fastmcp-slim[client,server]=={{ version }}"' in pyproject
def test_fastmcp_slim_installs_private_import_root() -> None:
pyproject = (PROJECT_ROOT / "fastmcp_slim" / "pyproject.toml").read_text()
assert "[project.scripts]" not in pyproject
assert 'fastmcp = "fastmcp.cli:app"' not in pyproject
assert "[tool.hatch.build.targets.wheel]" in pyproject
assert "bypass-selection = true" in pyproject
assert "only-include = []" in pyproject
assert "[tool.hatch.build.targets.wheel.force-include]" in pyproject
assert 'fastmcp = "fastmcp_slim/fastmcp"' in pyproject
assert '"fastmcp_slim.pth" = "fastmcp_slim.pth"' in pyproject
pth_file = PROJECT_ROOT / "fastmcp_slim" / "fastmcp_slim.pth"
assert pth_file.read_text().strip() == "fastmcp_slim"