diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a3a144677..574838e73 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -145,7 +145,7 @@ jobs: import fastmcp import fastmcp.settings - assert any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts")) + assert not any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts")) try: from fastmcp.cli import app @@ -174,7 +174,7 @@ jobs: from fastmcp.client.transports import StdioTransport, StreamableHttpTransport from fastmcp.mcp_config import MCPConfig - assert any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts")) + assert not any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts")) try: from fastmcp.cli import app @@ -207,10 +207,7 @@ jobs: from fastmcp import FastMCP from fastmcp.cli import app - assert any( - ep.name == "fastmcp" and ep.value == "fastmcp.cli:app" - for ep in entry_points(group="console_scripts") - ) + assert not any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts")) mcp = FastMCP("smoke") assert app is not None @@ -235,7 +232,9 @@ jobs: assert not any("fastmcp-slim[full" in req for req in fastmcp_reqs) assert any( - ep.name == "fastmcp" and ep.value == "fastmcp.cli:app" + ep.name == "fastmcp" + and ep.value == "fastmcp.cli:app" + and ep.dist.name == "fastmcp" for ep in entry_points(group="console_scripts") ) diff --git a/docs/getting-started/installation.mdx b/docs/getting-started/installation.mdx index 20443337b..7ac0d7283 100644 --- a/docs/getting-started/installation.mdx +++ b/docs/getting-started/installation.mdx @@ -62,6 +62,23 @@ Alternatively, wait for the stable v5 release. See [this issue](https://github.c ## Upgrading +### From FastMCP 3.3.0 or 3.3.1 + +FastMCP 3.3.2 repairs a package split issue that could affect environments upgraded from FastMCP 3.2.x with pip. Upgrade to the latest release to restore both the `fastmcp` import package and the `fastmcp` CLI: + +```bash +python -m pip install --upgrade fastmcp +uv pip install --upgrade fastmcp +uv sync --upgrade-package fastmcp +``` + +For an environment that still has missing package files after upgrading, reinstall the paired packages: + +```bash +python -m pip install --upgrade --force-reinstall fastmcp fastmcp-slim +uv pip install --upgrade --reinstall-package fastmcp --reinstall-package fastmcp-slim fastmcp +``` + ### From FastMCP 2.0 See the [Upgrade Guide](/getting-started/upgrading/from-fastmcp-2) for a complete list of breaking changes and migration steps. diff --git a/fastmcp_slim/fastmcp_slim.pth b/fastmcp_slim/fastmcp_slim.pth new file mode 100644 index 000000000..a0e6d7a00 --- /dev/null +++ b/fastmcp_slim/fastmcp_slim.pth @@ -0,0 +1 @@ +fastmcp_slim diff --git a/fastmcp_slim/pyproject.toml b/fastmcp_slim/pyproject.toml index 61b9e163c..d9d20a4cb 100644 --- a/fastmcp_slim/pyproject.toml +++ b/fastmcp_slim/pyproject.toml @@ -41,9 +41,6 @@ Homepage = "https://gofastmcp.com" Repository = "https://github.com/PrefectHQ/fastmcp" Documentation = "https://gofastmcp.com" -[project.scripts] -fastmcp = "fastmcp.cli:app" - [build-system] requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"] build-backend = "hatchling.build" @@ -55,7 +52,12 @@ source = "uv-dynamic-versioning" allow-direct-references = true [tool.hatch.build.targets.wheel] -packages = ["fastmcp"] +bypass-selection = true +only-include = [] + +[tool.hatch.build.targets.wheel.force-include] +fastmcp = "fastmcp_slim/fastmcp" +"fastmcp_slim.pth" = "fastmcp_slim.pth" [tool.uv-dynamic-versioning] vcs = "git" diff --git a/pyproject.toml b/pyproject.toml index 1813bf0e7..7814ae266 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,9 @@ Homepage = "https://gofastmcp.com" Repository = "https://github.com/PrefectHQ/fastmcp" Documentation = "https://gofastmcp.com" +[project.scripts] +fastmcp = "fastmcp.cli:app" + [build-system] requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"] build-backend = "hatchling.build" @@ -138,6 +141,7 @@ exclude = ["**/node_modules", "**/__pycache__", ".venv", ".git", "dist"] [tool.ty.environment] python-version = "3.10" +extra-paths = ["fastmcp_slim"] [tool.ty.rules] division-by-zero = "warn" diff --git a/tests/client/test_slim_package_boundaries.py b/tests/client/test_slim_package_boundaries.py index 5c6a4b073..cdf77d961 100644 --- a/tests/client/test_slim_package_boundaries.py +++ b/tests/client/test_slim_package_boundaries.py @@ -4,10 +4,13 @@ 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(): @@ -40,7 +43,6 @@ def test_client_http_headers_do_not_require_server() -> None: assert get_http_headers(include_all=True) == {} -@pytest.mark.asyncio async def test_multiserver_config_requires_server_for_now() -> None: from fastmcp.client.transports import MCPConfigTransport @@ -59,3 +61,32 @@ async def test_multiserver_config_requires_server_for_now() -> None: ): 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"