vet/mcp/server/sse.go
Copilot cd7caffb4a
Add HTTP HEAD request support to SSE MCP server (#533)
* Initial plan

* Add HTTP HEAD request support to SSE MCP server

- Created sseHandlerWithHeadSupport wrapper to handle HEAD requests to /sse endpoint
- HEAD requests return same headers as GET (text/event-stream, no-cache, etc.) without body
- Modified NewMcpServerWithSseTransport to use the wrapper
- Added comprehensive unit and integration tests
- Updated documentation to mention HEAD support for SSE endpoint
- Enables tools like Langchain to probe endpoint for health/capability checks

Co-authored-by: abhisek <31844+abhisek@users.noreply.github.com>

* Add HTTP HEAD request support to SSE MCP server

Co-authored-by: abhisek <31844+abhisek@users.noreply.github.com>

* Fix linter issues: remove trailing whitespace and handle w.Write error

Co-authored-by: abhisek <31844+abhisek@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: abhisek <31844+abhisek@users.noreply.github.com>
2025-07-05 13:41:37 +00:00

50 lines
1.7 KiB
Go

package server
import (
"net/http"
"github.com/mark3labs/mcp-go/server"
"github.com/safedep/vet/pkg/common/logger"
)
// sseHandlerWithHeadSupport wraps the SSE handler to add support for HTTP HEAD requests.
// HEAD requests will return the same headers as GET requests but without a body,
// allowing tools like Langchain to probe the endpoint for health or capability checks.
func sseHandlerWithHeadSupport(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Only handle HEAD requests to the SSE endpoint specifically
if r.Method == http.MethodHead && r.URL.Path == "/sse" {
// For HEAD requests to SSE endpoint, set the same headers as SSE connections but don't send a body
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(http.StatusOK)
return
}
// For all other requests (including GET, and HEAD to other endpoints), delegate to the original handler
handler.ServeHTTP(w, r)
})
}
func NewMcpServerWithSseTransport(config McpServerConfig) (*mcpServer, error) {
srv := newServer(config)
return &mcpServer{
config: config,
server: srv,
servingFunc: func(srv *mcpServer) error {
logger.Infof("Starting MCP server with SSE transport: %s", config.SseServerAddr)
s := server.NewSSEServer(srv.server, server.WithStaticBasePath(config.SseServerBasePath))
// Wrap the SSE server with HEAD request support
wrappedHandler := sseHandlerWithHeadSupport(s)
httpServer := &http.Server{
Addr: config.SseServerAddr,
Handler: wrappedHandler,
}
return httpServer.ListenAndServe()
},
}, nil
}