You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem? Please describe.
The MCP 2026-07-28 Streamable HTTP specification requires servers to validate every present Origin header and return HTTP 403 Forbidden when it is invalid.
RMCP currently differs in two ways:
An empty allowed_origins list skips Origin validation, so any present Origin is accepted.
Malformed or non-UTF-8 Origin values return HTTP 400 instead of the required 403.
Requests without an Origin header should continue to pass for non-browser MCP clients.
Describe the solution you'd like
Keep Origin validation enabled by default.
Treat an empty allowed_origins list as allowing no present Origin values.
Preserve disable_allowed_origins() as the explicit opt-out from validation.
Return HTTP 403 for malformed, non-UTF-8, and non-allowlisted Origin values.
Leave existing port-matching behavior unchanged because the MCP specification does not define allowlist matching semantics.
Describe alternatives you've considered
Keeping an empty allowlist as an implicit way to disable validation. This makes an empty security policy accept every present Origin and duplicates the explicit disable_allowed_origins() option.
Returning HTTP 400 for malformed Origin values. This does not match the specification's required HTTP 403 response for an invalid present Origin.
Changing port-matching behavior at the same time. This is left unchanged because the specification does not define allowlist matching semantics.
Additional context
This changes the default behavior only for requests that carry an Origin header. Non-browser clients without that header remain unaffected. Deployments that intentionally accept arbitrary Origins can continue to call disable_allowed_origins().
Acceptance criteria:
Empty allowlist + present Origin returns 403.
Empty allowlist + missing Origin succeeds.
Explicitly disabled validation accepts a present Origin.
Malformed and non-UTF-8 Origin values return 403.
Existing allowlist and missing-Origin tests continue to pass.
I'd like to pick this up, and I think #1188 should land in the same change.
#1188 is the malformed-Origin case of this issue: it reports 400 where the 2026-07-28 spec requires 403. That is one branch of the validation logic described here, so splitting them would mean touching the same code path twice and leaving the tree briefly inconsistent between the two merges.
Proposed behavior, matching what you outlined:
Origin validation stays on by default
A present Origin that is malformed or non-UTF-8 returns 403, not 400
An empty allowed_origins list allows no present Origin values, rather than skipping validation
Requests with no Origin header continue to pass, for non-browser clients
disable_allowed_origins() remains the explicit opt-out
Tests would cover the four cases separately: absent, valid-and-allowed, valid-but-not-allowlisted, and malformed. The last two both return 403 but for different reasons, and they're worth asserting independently so a future refactor can't collapse them.
Happy to split it into two PRs if you'd rather review them separately.
Activity
acamarata commentedon Aug 25, 2026
I'd like to pick this up, and I think #1188 should land in the same change.
#1188 is the malformed-Origin case of this issue: it reports 400 where the 2026-07-28 spec requires 403. That is one branch of the validation logic described here, so splitting them would mean touching the same code path twice and leaving the tree briefly inconsistent between the two merges.
Proposed behavior, matching what you outlined:
Originthat is malformed or non-UTF-8 returns 403, not 400allowed_originslist allows no present Origin values, rather than skipping validationOriginheader continue to pass, for non-browser clientsdisable_allowed_origins()remains the explicit opt-outTests would cover the four cases separately: absent, valid-and-allowed, valid-but-not-allowlisted, and malformed. The last two both return 403 but for different reasons, and they're worth asserting independently so a future refactor can't collapse them.
Happy to split it into two PRs if you'd rather review them separately.
lucarlig commentedon Aug 25, 2026
@acamarata I already have #1192