Add Server-Sent Events (SSE) streaming support to MCP transport#90
Conversation
Add Server-Sent Events (SSE) streaming alongside existing HTTP POST handling so Claude Desktop can connect directly without a bridge script. - Refactor ServeHTTP to dispatch GET (SSE) and POST requests - Add SSE session management with keepalive and message routing - Support bidirectional communication: GET opens SSE stream, POST with Mcp-Session-Id header sends JSON-RPC messages to the session - Send endpoint event on connection for client discovery - Use cryptographic session IDs and cap concurrent sessions at 100 - Add CORS headers and nginx buffering bypass for SSE streams - Add comprehensive tests for SSE connection, session management, helper functions, and POST-to-SSE integration https://claude.ai/code/session_0158JDGAyxGQo7eofDYbFXSz
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dda093ca2a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| w.Header().Set("Access-Control-Allow-Origin", "*") | ||
| w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") | ||
| w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Mcp-Session-Id") | ||
| w.Header().Set("Mcp-Session-Id", sessionID) |
There was a problem hiding this comment.
Expose session ID header for cross-origin clients
The SSE response sets Access-Control-Allow-Origin: * but never exposes Mcp-Session-Id via Access-Control-Expose-Headers. For browser clients using fetch() to open the stream, the session ID header is not readable unless it is explicitly exposed, which prevents the client from attaching the required Mcp-Session-Id on subsequent POSTs. This breaks the advertised GET-then-POST flow for cross-origin usage even though CORS headers are being sent.
Useful? React with 👍 / 👎.
| // Send to session for processing | ||
| if !session.SendRequest(&req) { | ||
| http.Error(w, "Session closed", http.StatusGone) | ||
| return |
There was a problem hiding this comment.
Avoid treating a full request buffer as a closed session
SendRequest returns false both when the session is closed and when the request channel is full. Here, any false is converted into a 410 Session closed response, so a burst of >16 queued requests (or slow processing) will drop a request and tell the client the session is closed even though the SSE stream is still alive. This can cause lost messages and unnecessary reconnects under load; the error should distinguish backpressure from a closed session (e.g., 429/503 or blocking).
Useful? React with 👍 / 👎.
Summary
This PR adds Server-Sent Events (SSE) streaming support to the MCP (Model Context Protocol) HTTP transport layer, enabling bidirectional communication between clients and the Gitea MCP server.
Changes
New SSE module (
modules/mcp/sse.go):SSESessionfor managing individual SSE connectionsSSESessionManagerfor tracking active sessions with a configurable limit (100 concurrent sessions)serveSSE()handler for GET requests to establish streaming connectionsEnhanced transport layer (
modules/mcp/transport.go):ServeHTTP()to support both GET (SSE) and POST (single request) methodshandlePost()to process single JSON-RPC requestshandleSessionMessage()to route POST requests withMcp-Session-Idheader to the correct SSE sessionComprehensive test coverage (
modules/mcp/transport_test.go):How it works
Accept: text/event-streamMcp-Session-Idheader and endpoint URIMcp-Session-IdheaderFeatures
Validation
Testing
https://claude.ai/code/session_0158JDGAyxGQo7eofDYbFXSz