Repository navigation
Bump starlette from 0.49.1 to 1.3.1 - #3116
dependabot[bot] wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
LGTM — lockfile-only Dependabot bump of starlette to 1.3.1. The SDK's own constraint (starlette>=0.27, unbounded) means end users already resolve 1.3.1; this only updates the CI/dev pin, and the full test matrix runs against it. The one inline note is a non-blocking follow-up suggestion, not a merge blocker.
Extended reasoning...
Overview
This PR is a single-file Dependabot change: uv.lock bumps starlette from 0.49.1 to 1.3.1. No source code, no pyproject.toml constraint change. The SDK's runtime requirement remains starlette>=0.27 (>=0.48 on 3.14) with no upper bound, so published-package users already resolve 1.3.1 today — this pin only affects the SDK's own CI/dev environment.
Security risks
The delta is upstream hardening (FormParser max_fields/max_part_size enforcement, StaticFiles absolute-path rejection, FileResponse range clamping) — security-positive rather than risky. The one behavioral interaction found is the inline nit: starlette's new 1 MB form-field limit raises an HTTPException(400) that the OAuth handlers' except ValidationError blocks don't translate into the RFC 6749 JSON error shape. The trigger is adversarial (>1 MB field or >1000 fields), the status code is still a correct 400, and the exposure exists independently of this PR given the unbounded floor — so it's a follow-up, not a blocker.
Level of scrutiny
Lockfile-only dependency bumps warrant moderate scrutiny focused on behavioral deltas in the upgraded package, which the bug hunting system performed: all imported starlette symbols verified present in 1.3.1, removed 1.0 APIs (on_startup/on_event/@app.route/WSGIMiddleware) confirmed unused, sse-starlette 3.0.2 compatibility checked, and the request.url structured-components change confirmed not to touch server-side SDK code. The full CI matrix (3.10–3.14 × ubuntu/windows, 100% coverage gate) runs against the new pin, which is the definitive compatibility check.
Other factors
No human reviewer comments and no prior bot reviews on this PR. The major-version jump (0.49 → 1.3) looks larger than it is — the audited delta shows no removed APIs in use and the repo does not use starlette's multipart/form features outside the OAuth handlers flagged inline. Approving with the inline nit standing as a suggested follow-up.
There was a problem hiding this comment.
🟡 Starlette 1.3.1 newly enforces max_part_size=1MB / max_fields=1000 on application/x-www-form-urlencoded bodies (Kludex/starlette#3329, #3331), raising an HTTPException(400) that the SDK's OAuth handlers (token.py, revoke.py, authorize.py POST, client_auth.py) don't catch — they only catch pydantic ValidationError around request.form(), so oversized posts to /token or /revoke now return a text/plain 400 instead of the RFC 6749 §5.2 JSON error object. Not a merge blocker (pyproject's unbounded starlette>=0.27 floor means users can already resolve 1.3.1, and the trigger is adversarial: a >1MB field or >1000 fields), but a follow-up could catch starlette's HTTPException around request.form() in these handlers and translate it into the OAuth invalid_request JSON shape.
Extended reasoning...
What changed in starlette
In starlette 0.49.1, FormParser (the parser for application/x-www-form-urlencoded bodies) had no size limits — Request._get_form simply ran FormParser(self.headers, self.stream()); await form_parser.parse() with no exception path. Starting in 1.3.1 (Kludex/starlette#3329 and #3331), FormParser enforces max_fields=1000 and max_part_size=1MB per field (verified in the installed wheel, formparsers.py:82-97). Exceeding either limit raises MultiPartException, which Request._get_form converts into a starlette HTTPException(status_code=400, detail='Field exceeded maximum size of 1024KB.' / 'Too many fields...') when app is in scope (requests.py:296-308).
Why the SDK's OAuth handlers are affected
The SDK's OAuth handlers all call await request.form() inside try blocks that catch only pydantic ValidationError (or AuthenticationError further up):
src/mcp/server/auth/handlers/token.py:121src/mcp/server/auth/handlers/revoke.py:50src/mcp/server/auth/handlers/authorize.py:150(POST branch)src/mcp/server/auth/middleware/client_auth.py:54(called by token/revoke handlers)
These handlers deliberately construct RFC 6749 §5.2-conformant JSON error objects (TokenErrorResponse / RevocationErrorResponse via PydanticJSONResponse, with Cache-Control: no-store). The new starlette HTTPException is not caught by any of them, so it escapes to the Starlette app's ExceptionMiddleware and is rendered as a text/plain 400 response. If the handlers are composed as raw ASGI without a Starlette app in scope, the raw MultiPartException propagates and produces a 500 instead.
Step-by-step proof
- A client POSTs to
/tokenwithContent-Type: application/x-www-form-urlencodedand a body containing one field larger than 1 MiB (e.g. a hugeclient_assertionvalue):grant_type=client_credentials&client_assertion=AAAA...(>1MB). TokenHandler.handle(token.py:121) callsawait request.form()insidetry: ... except ValidationError.- In starlette 1.3.1,
FormParser.parse()accumulates the field value, hits the 1 MiBmax_part_sizecheck added in EnforceFormParserlimits in parser callbacks Kludex/starlette#3331, and raisesMultiPartException('Field exceeded maximum size of 1024KB.'). Request._get_formcatches it and re-raises asstarlette.exceptions.HTTPException(400)(because the request is served by a Starlette app).- This is not a
ValidationError, so the handler'sexceptdoesn't fire — the carefully builtTokenErrorResponse(error='invalid_request', ...)JSON path is bypassed. ExceptionMiddlewarecatches theHTTPExceptionand returns400with bodyField exceeded maximum size of 1024KB.astext/plain— violating RFC 6749 §5.2, which requires the token endpoint to return a JSON error object.
Under 0.49.1, the same request parses successfully (unbounded buffering) and reaches the handler's normal validation path — so the changed error shape is introduced by this bump.
Why this is not a merge blocker
pyproject.tomlconstrainsstarlette>=0.27with no upper bound, so end users installing the publishedmcppackage already resolve 1.3.1 today regardless of this PR — the lockfile only pins the SDK's own CI/dev environment. Blocking this bump would shield no one.- The trigger is adversarial or an extreme edge case: no realistic OAuth client sends a single >1 MiB form field or >1000 fields (even large RFC 7523
client_assertionJWTs are orders of magnitude below 1 MiB). The status code is still an RFC-correct 400; only the body shape/content-type differ. - The upstream change is intentional DoS hardening — 0.49.1's unbounded in-memory buffering of urlencoded bodies was strictly worse. Real deployments behind reverse proxies (e.g. nginx's 1 MB default body limit) would often preempt the app with a 413 anyway.
Suggested follow-up fix
In the four handlers listed above, catch starlette's HTTPException (or MultiPartException) around await request.form() and translate it into the OAuth invalid_request JSON error shape, e.g. TokenErrorResponse(error='invalid_request', error_description='request body too large') with status 400. Alternatively, pass explicit max_part_size/max_fields to request.form() and handle the failure there.
For completeness: the rest of the 0.49.1 → 1.3.1 delta was audited against SDK usage and no other affected paths were found — all imported starlette symbols still exist, removed 1.0 APIs (on_startup/on_event/@app.route/WSGIMiddleware) are unused, sse-starlette 3.0.2's imports remain valid, and request.url host-header behavior changes (starlette#3326) don't touch server-side SDK code.
Bumps [starlette](https://github.com/Kludex/starlette) from 0.49.1 to 1.3.1. - [Release notes](https://github.com/Kludex/starlette/releases) - [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md) - [Commits](Kludex/starlette@0.49.1...1.3.1) --- updated-dependencies: - dependency-name: starlette dependency-version: 1.3.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
4d84b63 to
52eb078
Compare

Bumps starlette from 0.49.1 to 1.3.1.
Release notes
Sourced from starlette's releases.
... (truncated)
Changelog
Sourced from starlette's changelog.
... (truncated)
Commits
8ebffd0Version 1.3.1 (#3330)25b8e17EnforceFormParserlimits in parser callbacks (#3331)dba1c4bEnforcemax_fieldsandmax_part_sizeinFormParser(#3329)45e51dcUseStarletteDeprecationWarninginstead ofDeprecationWarning(#3119)5f8610cVersion 1.3.0 (#3327)167b585Buildrequest.urlfrom structured components (#3326)3730925Useremoveprefixto strip weak ETag indicator inis_not_modified(#3193)e6f7ad1avoid collapsing exception groups from user code (#2830)115228fAnnotate URLPath protocol parameter with Literal (#3285)113f193docs: replace inline ASGI server list with link to canonical implemen… (#3204)