Three small input validation issues across different subsystems.
1. Settings accepts invalid port numbers (settings.py)
from fastmcp.settings import Settings
Settings(port=-1) # accepted
Settings(port=99999) # accepted
No validation on the port field. Should reject ports outside 1-65535.
2. Form boolean backfill misses Optional[bool] (apps/form.py)
from fastmcp.apps.form import _backfill_boolean_defaults
import pydantic
class M(pydantic.BaseModel):
flag: bool = False
opt: bool | None = False
_backfill_boolean_defaults(M, {}) # returns {'flag': False} — 'opt' missing
Line 70 uses `field_info.annotation is bool` which doesn't match `bool | None`. Unchecked `Optional[bool]` checkboxes are missing from form data, causing Pydantic validation to fail.
3. ErrorHandlingMiddleware silently drops async error callbacks
async def my_callback(error, context):
await log_to_service(error) # never runs
mcp.add_middleware(ErrorHandlingMiddleware(error_callback=my_callback))
Line 77 of `error_handling.py` calls `self.error_callback(error, context)` without `await`. If the callback is async, the coroutine is created but never awaited — silently dropped with no warning.
Note: the type hint says `Callable[..., None]` (sync), but nothing prevents passing an async callable, and the silent failure is surprising.
Three small input validation issues across different subsystems.
1. Settings accepts invalid port numbers (settings.py)
No validation on the port field. Should reject ports outside 1-65535.
2. Form boolean backfill misses Optional[bool] (apps/form.py)
Line 70 uses `field_info.annotation is bool` which doesn't match `bool | None`. Unchecked `Optional[bool]` checkboxes are missing from form data, causing Pydantic validation to fail.
3. ErrorHandlingMiddleware silently drops async error callbacks
Line 77 of `error_handling.py` calls `self.error_callback(error, context)` without `await`. If the callback is async, the coroutine is created but never awaited — silently dropped with no warning.
Note: the type hint says `Callable[..., None]` (sync), but nothing prevents passing an async callable, and the silent failure is surprising.