diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d8d8edb..13557042 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,8 +28,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e ".[telemetry]" - pip install pytest pytest-asyncio pytest-cov + pip install -e ".[dev]" - name: Run tests run: | @@ -57,8 +56,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e ".[telemetry]" - pip install ruff mypy + pip install -e ".[dev]" - name: Run ruff format check run: ruff format --check src/ diff --git a/pyproject.toml b/pyproject.toml index 6483888a..5b275ef8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,11 +28,10 @@ classifiers = [ dependencies = [ "typer>=0.15.4", "rich>=14.0.0", - "fastmcp>=2.14.0,<2.15.0", + "fastmcp>=3.2.0,<4.0.0", "pydantic>=2.11.0", "pydantic-settings>=2.0.0", "python-dotenv>=1.1.0", - "black>=24.10.0", "pyjwt>=2.0.0", "httpx>=0.28.1", "posthog>=4.1.0", @@ -47,6 +46,14 @@ dependencies = [ metrics = [ "prometheus-client>=0.22.1" ] +dev = [ + "black>=26.3.1", + "pytest>=7.4.0", + "pytest-asyncio>=0.23.0", + "pytest-cov>=4.1.0", + "ruff>=0.1.0", + "mypy>=1.6.0" +] [project.scripts] golf = "golf.cli.main:app" @@ -85,13 +92,12 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.8" # Match requires-python -fastmcp = ">=2.14.0,<2.15.0" +fastmcp = ">=3.2.0,<4.0.0" typer = {extras = ["all"], version = ">=0.15.4"} pydantic = ">=2.11.0" pydantic-settings = ">=2.0.0" rich = ">=14.0.0" python-dotenv = ">=1.1.0" -black = ">=24.10.0" pyjwt = ">=2.0.0" httpx = ">=0.28.1" posthog = ">=4.1.0" @@ -111,6 +117,7 @@ pytest-asyncio = "^0.23.0" ruff = "^0.1.0" mypy = "^1.6.0" pytest-cov = "^4.1.0" +black = ">=26.3.1" [tool.black] line-length = 120 diff --git a/src/golf/core/builder.py b/src/golf/core/builder.py index 7f960ba2..c114916a 100644 --- a/src/golf/core/builder.py +++ b/src/golf/core/builder.py @@ -8,7 +8,6 @@ from pathlib import Path from typing import Any -import black from rich.console import Console from golf.auth import is_auth_configured @@ -1279,10 +1278,6 @@ def _generate_server(self) -> None: for key, value in auth_components["fastmcp_args"].items(): mcp_constructor_args.append(f"{key}={value}") - # Add stateless HTTP parameter if enabled - if self.settings.stateless_http: - mcp_constructor_args.append("stateless_http=True") - # Add OpenTelemetry parameters if enabled if self.settings.opentelemetry_enabled: mcp_constructor_args.append("lifespan=telemetry_lifespan") @@ -1323,6 +1318,7 @@ def _generate_server(self) -> None: f' transport_to_run = "{self.settings.transport}"', "", ] + stateless_kwarg = ", stateless_http=True" if self.settings.stateless_http else "" main_code.append("") @@ -1434,8 +1430,8 @@ def _generate_server(self) -> None: main_code.extend( [ " # Run HTTP server with middleware using FastMCP's run method", - ' mcp.run(transport="streamable-http", host=host, ' - 'port=port, log_level="info", middleware=middleware, show_banner=False)', + f' mcp.run(transport="streamable-http", host=host, ' + f'port=port, log_level="info", middleware=middleware, show_banner=False{stateless_kwarg})', ] ) else: @@ -1444,7 +1440,7 @@ def _generate_server(self) -> None: " # Run HTTP server with middleware using FastMCP's run method", f' mcp.run(transport="streamable-http", host=host, ' f'port=port, path="{endpoint_path}", log_level="info", ' - f"middleware=middleware, show_banner=False)", + f"middleware=middleware, show_banner=False{stateless_kwarg})", ] ) else: @@ -1452,8 +1448,8 @@ def _generate_server(self) -> None: main_code.extend( [ " # Run HTTP server using FastMCP's run method", - ' mcp.run(transport="streamable-http", host=host, ' - 'port=port, log_level="info", show_banner=False)', + f' mcp.run(transport="streamable-http", host=host, ' + f'port=port, log_level="info", show_banner=False{stateless_kwarg})', ] ) else: @@ -1462,7 +1458,7 @@ def _generate_server(self) -> None: " # Run HTTP server using FastMCP's run method", f' mcp.run(transport="streamable-http", host=host, ' f'port=port, path="{endpoint_path}", log_level="info", ' - f"show_banner=False)", + f"show_banner=False{stateless_kwarg})", ] ) else: @@ -1504,9 +1500,16 @@ def _generate_server(self) -> None: + main_code ) - # Format with black + # Format with black (build-time dep; install with `pip install golf-mcp[dev]`) try: + import black + code = black.format_str(code, mode=black.Mode()) + except ImportError: + console.print( + "[yellow]Warning: black is not installed; generated server.py will not be formatted. " + "Install with `pip install golf-mcp[dev]`.[/yellow]" + ) except Exception as e: console.print(f"[yellow]Warning: Could not format server.py: {e}[/yellow]") @@ -1908,14 +1911,18 @@ def build_project( server_code_content[:app_pos] + auth_routes_code + "\n\n" + server_code_content[app_pos:] ) - # Format with black before writing + # Format with black before writing (build-time dep) + final_code_to_write = modified_code try: + import black + final_code_to_write = black.format_str(modified_code, mode=black.Mode()) + except ImportError: + pass # already warned above when generating server.py except Exception as e: console.print( f"[yellow]Warning: Could not format server.py after auth routes injection: {e}[/yellow]" ) - final_code_to_write = modified_code with open(server_file, "w") as f: f.write(final_code_to_write)