diff --git a/pyproject.toml b/pyproject.toml index 0ad8e258a..7a1d1c084 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -185,9 +185,13 @@ ignore = [ extend-select = [ "B", # flake8-bugbear: Catches actual bugs like mutable default arguments "C4", # flake8-comprehensions: More efficient/readable comprehensions + "DTZ", # flake8-datetimez: Enforce timezone-aware datetime usage + "ERA", # eradicate: Detect commented-out code "FA", # flake8-future-annotations: Enforce from __future__ import annotations "FLY", # flynt: Static string joins that should be f-strings "I", # isort: Import sorting + "INP", # flake8-no-pep420: Require __init__.py in namespace packages + "ISC", # flake8-implicit-str-concat: Prevent accidental string concatenation "LOG", # flake8-logging: Catches logging module misuse "PIE", # flake8-pie: More idiomatic Python code "PLE", # pylint-error: Catches actual errors (invalid operations, syntax issues) @@ -209,8 +213,12 @@ known-first-party = ["fastmcp"] "!src/**.py" = [ # Only enforce extended ruff rules for code in src/ "B", # flake8-bugbear "C4", # flake8-comprehensions + "DTZ", # flake8-datetimez + "ERA", # eradicate "FA", # flake8-future-annotations "FLY", # flynt + "INP", # flake8-no-pep420 + "ISC", # flake8-implicit-str-concat "LOG", # flake8-logging "PIE", # flake8-pie "PLE", # pylint-error diff --git a/src/fastmcp/apps/file_upload.py b/src/fastmcp/apps/file_upload.py index b3acc90e2..aeb890379 100644 --- a/src/fastmcp/apps/file_upload.py +++ b/src/fastmcp/apps/file_upload.py @@ -60,7 +60,7 @@ except ImportError as _exc: ) from _exc import base64 -from datetime import datetime +from datetime import datetime, timezone from typing import Any from fastmcp.apps.app import FastMCPApp @@ -209,7 +209,7 @@ class FileUpload(FastMCPApp): "size": f["size"], "type": f["type"], "data": f["data"], - "uploaded_at": datetime.now().isoformat(timespec="seconds"), + "uploaded_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), } return [_make_summary(e) for e in session_files.values()] diff --git a/src/fastmcp/client/auth/oauth.py b/src/fastmcp/client/auth/oauth.py index 64416393a..49f1a7653 100644 --- a/src/fastmcp/client/auth/oauth.py +++ b/src/fastmcp/client/auth/oauth.py @@ -279,8 +279,8 @@ class OAuth(OAuthClientProvider): warn( message="Using in-memory token storage -- tokens will be lost when the client restarts. " - + "For persistent storage across multiple MCP servers, provide an encrypted AsyncKeyValue backend. " - + "See https://gofastmcp.com/clients/auth/oauth#token-storage for details.", + "For persistent storage across multiple MCP servers, provide an encrypted AsyncKeyValue backend. " + "See https://gofastmcp.com/clients/auth/oauth#token-storage for details.", stacklevel=2, ) diff --git a/src/fastmcp/server/auth/handlers/__init__.py b/src/fastmcp/server/auth/handlers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/fastmcp/server/auth/oauth_proxy/proxy.py b/src/fastmcp/server/auth/oauth_proxy/proxy.py index dbc888bd7..52c891258 100644 --- a/src/fastmcp/server/auth/oauth_proxy/proxy.py +++ b/src/fastmcp/server/auth/oauth_proxy/proxy.py @@ -376,7 +376,7 @@ class OAuthProxy(OAuthProvider, ConsentMixin): ): logger.warning( "allowed_client_redirect_uris is empty list; no redirect URIs will be accepted. " - + "This will block all OAuth clients." + "This will block all OAuth clients." ) self._allowed_client_redirect_uris: list[str] | None = ( allowed_client_redirect_uris @@ -402,7 +402,7 @@ class OAuthProxy(OAuthProvider, ConsentMixin): elif not require_authorization_consent: logger.warning( "Authorization consent screen disabled - only use for local development or testing. " - + "In production, this screen protects against confused deputy attacks." + "In production, this screen protects against confused deputy attacks." ) # Extra parameters for authorization and token endpoints @@ -429,7 +429,7 @@ class OAuthProxy(OAuthProvider, ConsentMixin): if len(jwt_signing_key) < 12: logger.warning( "jwt_signing_key is less than 12 characters; it is recommended to use a longer. " - + "string for the key derivation." + "string for the key derivation." ) jwt_signing_key = derive_jwt_key( low_entropy_material=jwt_signing_key, diff --git a/src/fastmcp/server/dependencies.py b/src/fastmcp/server/dependencies.py index 839c11cf6..447b8e7c7 100644 --- a/src/fastmcp/server/dependencies.py +++ b/src/fastmcp/server/dependencies.py @@ -767,7 +767,7 @@ def get_http_headers( } if include: exclude_headers -= {h.lower() for h in include} - # (just in case) + # Sanity check: all entries must already be lowercase if not all(h.lower() == h for h in exclude_headers): raise ValueError("Excluded headers must be lowercase") headers: dict[str, str] = {} diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py index 18fe1f7a0..967ef1ceb 100644 --- a/src/fastmcp/server/server.py +++ b/src/fastmcp/server/server.py @@ -262,7 +262,7 @@ def _lifespan_proxy( if not fastmcp_server._lifespan_result_set: raise RuntimeError( "FastMCP server has a lifespan defined but no lifespan result is set, which means the server's context manager was not entered. " - + " Are you running the server in a way that supports lifespans? If so, please file an issue at https://github.com/PrefectHQ/fastmcp/issues." + " Are you running the server in a way that supports lifespans? If so, please file an issue at https://github.com/PrefectHQ/fastmcp/issues." ) yield fastmcp_server._lifespan_result # ty:ignore[invalid-yield] diff --git a/src/fastmcp/utilities/cli.py b/src/fastmcp/utilities/cli.py index cac3e65e3..ad03691c5 100644 --- a/src/fastmcp/utilities/cli.py +++ b/src/fastmcp/utilities/cli.py @@ -240,7 +240,6 @@ def log_server_banner(server: FastMCP[Any]) -> None: panel_content, border_style="dim", padding=(1, 4), - # expand=False, width=80, # Set max width for the panel )