Skip to content

Conversation

@SamMorrowDrums
Copy link
Contributor

@SamMorrowDrums SamMorrowDrums commented Jan 20, 2026

Hey @findleyr we have an issue where a customer is using Gateways and technically the SDK throws non-compliant method not allowed headers, when they try to inspect server with a HEAD request. It's fine that it fails, but it highlights that it doesn't specify the required allowed methods, and so I think this is a change that should be made. Let me know 🙏

Summary

405 Method Not Allowed responses MUST include an Allow header listing supported methods, per RFC 9110 Section 15.5.6. This fixes issues with strict HTTP gateways (like Apigee) that treat 405 responses without an Allow header as malformed, returning 502 Bad Gateway errors.

Changes

  • SSEHandler: Add Allow: GET, POST header for unsupported methods
  • StreamableHTTPHandler: Add Allow header for GET-without-session case:
    • Stateless mode: Allow: POST, DELETE (GET is never valid)
    • Stateful mode: Allow: GET, POST, DELETE (GET is valid once you have a session)
  • Add tests to verify Allow header presence in all 405 responses

RFC 9110 Reference

Section 15.5.6 (405 Method Not Allowed):

The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource's currently supported methods.

Testing

All existing tests pass, plus new tests added:

  • TestSSE405AllowHeader - verifies SSE handler compliance
  • TestStreamable405AllowHeader - verifies Streamable handler compliance in both stateful and stateless modes

@SamMorrowDrums SamMorrowDrums force-pushed the fix/rfc9110-allow-header-405 branch from 1e80e30 to 566bb29 Compare January 20, 2026 15:34
@SamMorrowDrums SamMorrowDrums marked this pull request as ready for review January 20, 2026 15:34
jba
jba previously approved these changes Jan 21, 2026
405 Method Not Allowed responses MUST include an Allow header listing
supported methods, per RFC 9110 Section 15.5.6. This fixes issues with
strict HTTP gateways (like Apigee) that treat 405 responses without an
Allow header as malformed, returning 502 Bad Gateway errors.

Changes:
- SSEHandler: Add Allow header (GET, POST) for unsupported methods
- StreamableHTTPHandler: Add Allow header for GET-without-session case,
  differentiating between stateless mode (POST, DELETE) and stateful
  mode (GET, POST, DELETE)
- Add tests to verify Allow header presence in all 405 responses

Co-authored-by: omgitsads <4619+omgitsads@users.noreply.github.com>
@SamMorrowDrums
Copy link
Contributor Author

Pushed amended commit with gofmt applied properly.

@SamMorrowDrums SamMorrowDrums requested a review from jba January 21, 2026 06:42
- Remove unused withSession and wantStatus fields from streamable test
- Use got, want pattern for status code checks to avoid repetition
@SamMorrowDrums
Copy link
Contributor Author

Thanks for the review @maciej-kisiel, I agree with your comments.

Copy link

@maciej-kisiel maciej-kisiel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for quick action. I thought about this behavior a bit deeper and have two more questions. Sorry for not including them in my initial review.

@SamMorrowDrums
Copy link
Contributor Author

@maciej-kisiel they are great questions. I was thinking about some of them too when writing.

I also suggested in MCP discord the idea of adding some HTTP conformance tests to the SDK conformance testing, and so I think as well as trying to get this correct first time, I think we will end up with http conformance being checked and consistent across SDKs with community decision on some of these things, I hope this PR will be the guide for some that work, but I also think that there will likely be follow-ups.

Per MCP spec and reviewer feedback:

- In stateful mode, GET without session now returns 400 Bad Request
  (like DELETE), not 405 - because GET IS a supported method, it just
  requires a session ID as a precondition.

- In stateless mode, GET correctly returns 405 Method Not Allowed
  since the server doesn't offer SSE streaming.

- Unsupported methods (PUT, PATCH, etc.) now return the correct Allow
  header based on mode:
  - Stateless: Allow: POST
  - Stateful: Allow: GET, POST, DELETE

This aligns with MCP spec section 'Listening for Messages from the Server':
'The server MUST either return Content-Type: text/event-stream in response
to this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating
that the server does not offer an SSE stream at this endpoint.'
@SamMorrowDrums
Copy link
Contributor Author

Thanks for the review @maciej-kisiel, I agree with your comments.

After researching the MCP spec, I've made the following changes:

GET without session in stateful mode → 400 Bad Request

  • You're right - returning 405 with GET in the Allow header was confusing
  • GET IS a supported method in stateful mode, it just requires a session ID as a precondition
  • This now matches the behavior of DELETE without session (line 263)

Allow header in stateless mode → POST only

  • Since stateless mode doesn't support sessions, neither GET (SSE streaming) nor DELETE (session termination) are useful
  • The MCP spec says: 'The server MUST either return Content-Type: text/event-stream in response to this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server does not offer an SSE stream at this endpoint.'

Summary of behavior now:

Mode Method Status Allow Header
Stateful GET without session 400 Bad Request (none)
Stateful Unsupported method 405 GET, POST, DELETE
Stateless GET 405 POST
Stateless Unsupported method 405 POST

Copy link

@maciej-kisiel maciej-kisiel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot, looks good to me!

I added one more suggestion. If the change is not urgent, I would give @findleyr and @jba 24 hours for a chance to double check this before merging.

@maciej-kisiel maciej-kisiel self-requested a review January 21, 2026 15:06
maciej-kisiel
maciej-kisiel previously approved these changes Jan 21, 2026
@findleyr
Copy link
Contributor

LGTM! Thanks @SamMorrowDrums for the contribution, and @maciej-kisiel for the review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants