Skip to content

Commit c879ac3

Browse files
authored
mcp: gracefully handle a nil server in handlers (#164)
If the getServer function passed to NewSSEHandler or NewStreamableHTTPHandler returns nil, serve a 400 instead of panicking. Fixes #161.
1 parent 8dd9a81 commit c879ac3

File tree

3 files changed

+17
-6
lines changed

3 files changed

+17
-6
lines changed

examples/sse/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@ func main() {
5353
return nil
5454
}
5555
})
56-
http.ListenAndServe(*httpAddr, handler)
56+
log.Fatal(http.ListenAndServe(*httpAddr, handler))
5757
}

mcp/sse.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,14 @@ type SSEHandler struct {
5555
// Sessions are created when the client issues a GET request to the server,
5656
// which must accept text/event-stream responses (server-sent events).
5757
// For each such request, a new [SSEServerTransport] is created with a distinct
58-
// messages endpoint, and connected to the server returned by getServer. It is
59-
// up to the user whether getServer returns a distinct [Server] for each new
60-
// request, or reuses an existing server.
61-
//
58+
// messages endpoint, and connected to the server returned by getServer.
6259
// The SSEHandler also handles requests to the message endpoints, by
6360
// delegating them to the relevant server transport.
6461
//
62+
// The getServer function may return a distinct [Server] for each new
63+
// request, or reuse an existing server. If it returns nil, the handler
64+
// will return a 400 Bad Request.
65+
//
6566
// TODO(rfindley): add options.
6667
func NewSSEHandler(getServer func(request *http.Request) *Server) *SSEHandler {
6768
return &SSEHandler{
@@ -208,8 +209,12 @@ func (h *SSEHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
208209
h.mu.Unlock()
209210
}()
210211

211-
// TODO(hxjiang): getServer returns nil will panic.
212212
server := h.getServer(req)
213+
if server == nil {
214+
// The getServer argument to NewSSEHandler returned nil.
215+
http.Error(w, "no server available", http.StatusBadRequest)
216+
return
217+
}
213218
ss, err := server.Connect(req.Context(), transport)
214219
if err != nil {
215220
http.Error(w, "connection failed", http.StatusInternalServerError)

mcp/streamable.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type StreamableHTTPOptions struct {
5050
//
5151
// The getServer function is used to create or look up servers for new
5252
// sessions. It is OK for getServer to return the same server multiple times.
53+
// If getServer returns nil, a 400 Bad Request will be served.
5354
func NewStreamableHTTPHandler(getServer func(*http.Request) *Server, opts *StreamableHTTPOptions) *StreamableHTTPHandler {
5455
return &StreamableHTTPHandler{
5556
getServer: getServer,
@@ -135,6 +136,11 @@ func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Reque
135136
if session == nil {
136137
s := NewStreamableServerTransport(randText(), nil)
137138
server := h.getServer(req)
139+
if server == nil {
140+
// The getServer argument to NewStreamableHTTPHandler returned nil.
141+
http.Error(w, "no server available", http.StatusBadRequest)
142+
return
143+
}
138144
// Pass req.Context() here, to allow middleware to add context values.
139145
// The context is detached in the jsonrpc2 library when handling the
140146
// long-running stream.

0 commit comments

Comments
 (0)