From 30b29c93c01f9a5b835899aa03bfdf6703e6c9f9 Mon Sep 17 00:00:00 2001 From: guglielmoc Date: Fri, 4 Sep 2026 08:04:43 +0000 Subject: [PATCH] refactor: remove legacy MCPGODEBUG compatibility parameters for security and error handling --- docs/mcpgodebug.md | 17 +++++++++++++++++ internal/docs/mcpgodebug.src.md | 17 +++++++++++++++++ mcp/protocol.go | 12 +----------- mcp/sse.go | 4 ++-- mcp/streamable.go | 33 ++++----------------------------- 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/docs/mcpgodebug.md b/docs/mcpgodebug.md index d25b9b49..57766ce9 100644 --- a/docs/mcpgodebug.md +++ b/docs/mcpgodebug.md @@ -44,6 +44,23 @@ Options listed below were added and will be removed in the 1.9.0 version of the soon as its context is cancelled and cannot be delayed by a slow or unresponsive peer. See issue #1150. +Options below were removed, according to plan: + +- `seterroroverwrite`. `SetError` now always preserves existing `Content`. + +- `enableoriginverification`. A nil + `StreamableHTTPOptions.CrossOriginProtection` now always means no + cross-origin protection. Wrap the handler with cross-origin protection + middleware instead. + +- `disablecontenttypecheck`. Content-Type validation on HTTP POST requests can + no longer be disabled. + +- `disablelocalhostprotection`, whose removal had been postponed to this + release. Set the `DisableLocalhostProtection` field in + `StreamableHTTPOptions` or `SSEOptions` to opt out of DNS rebinding + protection. + ### 1.7.0 Options listed below were added and will be removed in the 1.9.0 version of the SDK. diff --git a/internal/docs/mcpgodebug.src.md b/internal/docs/mcpgodebug.src.md index 32f8af26..fd0f255e 100644 --- a/internal/docs/mcpgodebug.src.md +++ b/internal/docs/mcpgodebug.src.md @@ -43,6 +43,23 @@ Options listed below were added and will be removed in the 1.9.0 version of the soon as its context is cancelled and cannot be delayed by a slow or unresponsive peer. See issue #1150. +Options below were removed, according to plan: + +- `seterroroverwrite`. `SetError` now always preserves existing `Content`. + +- `enableoriginverification`. A nil + `StreamableHTTPOptions.CrossOriginProtection` now always means no + cross-origin protection. Wrap the handler with cross-origin protection + middleware instead. + +- `disablecontenttypecheck`. Content-Type validation on HTTP POST requests can + no longer be disabled. + +- `disablelocalhostprotection`, whose removal had been postponed to this + release. Set the `DisableLocalhostProtection` field in + `StreamableHTTPOptions` or `SSEOptions` to opt out of DNS rebinding + protection. + ### 1.7.0 Options listed below were added and will be removed in the 1.9.0 version of the SDK. diff --git a/mcp/protocol.go b/mcp/protocol.go index 4867e544..63d70151 100644 --- a/mcp/protocol.go +++ b/mcp/protocol.go @@ -329,23 +329,13 @@ type CallToolResult struct { err error } -// seterroroverwrite is a compatibility parameter that restores the pre-1.6.0 -// behavior of [CallToolResult.SetError], where Content was always overwritten -// with the error text. See the documentation for the mcpgodebug package for -// instructions on how to enable it. -// The option will be removed in the 1.8.0 version of the SDK. -var seterroroverwrite = mcpgodebug.Value("seterroroverwrite") - // SetError sets the error for the tool result and sets IsError to true. // If Content has not already been populated, it is set to the error text. // If Content has already been populated, it is left unchanged, allowing callers // to provide a user-friendly message while still recording the underlying error // for inspection via [GetError] in server middleware. -// -// To restore the previous behavior where Content was always overwritten, -// set MCPGODEBUG=seterroroverwrite=1. func (r *CallToolResult) SetError(err error) { - if len(r.Content) == 0 || seterroroverwrite == "1" { + if len(r.Content) == 0 { r.Content = []Content{&TextContent{Text: err.Error()}} } r.IsError = true diff --git a/mcp/sse.go b/mcp/sse.go index c7926b9c..0a5bf7fe 100644 --- a/mcp/sse.go +++ b/mcp/sse.go @@ -225,7 +225,7 @@ func (t *SSEServerTransport) SupportsProtocolVersion(version string) bool { func (h *SSEHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // DNS rebinding protection: auto-enabled for localhost servers. // See: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise - if !h.opts.DisableLocalhostProtection && disablelocalhostprotection != "1" { + if !h.opts.DisableLocalhostProtection { if localAddr, ok := req.Context().Value(http.LocalAddrContextKey).(net.Addr); ok && localAddr != nil { if util.IsLoopback(localAddr.String()) && !util.IsLoopback(req.Host) { http.Error(w, fmt.Sprintf("Forbidden: invalid Host header %q", req.Host), http.StatusForbidden) @@ -235,7 +235,7 @@ func (h *SSEHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // Validate 'Content-Type' header. - if disablecontenttypecheck != "1" && req.Method == http.MethodPost { + if req.Method == http.MethodPost { mediaType, _, err := mime.ParseMediaType(req.Header.Get("Content-Type")) if err != nil || mediaType != "application/json" { http.Error(w, "Content-Type must be 'application/json'", http.StatusUnsupportedMediaType) diff --git a/mcp/streamable.go b/mcp/streamable.go index d1462d9f..f24badbb 100644 --- a/mcp/streamable.go +++ b/mcp/streamable.go @@ -184,8 +184,7 @@ type StreamableHTTPOptions struct { // CrossOriginProtection allows to customize cross-origin protection. // The deny handler set in the CrossOriginProtection through SetDenyHandler // is ignored. - // If nil, no cross-origin protection is applied. Use the `enableoriginverification` - // MCPGODEBUG compatibility parameter to enable the default protection until v1.8.0. + // If nil, no cross-origin protection is applied. // // Deprecated: wrap the handler with cross-origin protection middleware // instead. For example: @@ -240,10 +239,6 @@ func NewStreamableHTTPHandler(getServer func(*http.Request) *Server, opts *Strea h.opts.Logger = ensureLogger(h.opts.Logger) - if h.opts.CrossOriginProtection == nil && enableoriginverification == "1" { - h.opts.CrossOriginProtection = &http.CrossOriginProtection{} - } - if h.opts.MaxRequestBodyBytes == 0 { h.opts.MaxRequestBodyBytes = DefaultMaxRequestBodyBytes } @@ -274,20 +269,6 @@ func (h *StreamableHTTPHandler) closeAll() { } } -// disablelocalhostprotection is a compatibility parameter that allows to disable -// DNS rebinding protection, which was added in the 1.4.0 version of the SDK. -// See the documentation for the mcpgodebug package for instructions how to enable it. -// The option will be removed in the 1.8.0 version of the SDK. -var disablelocalhostprotection = mcpgodebug.Value("disablelocalhostprotection") - -// enableoriginverification is a compatibility parameter that restores the -// default cross-origin protection behavior from v1.4.1-v1.5.0. When set to -// "1", a zero-value CrossOriginProtection will be applied if none is -// explicitly provided in StreamableHTTPOptions. -// See the documentation for the mcpgodebug package for instructions how to enable it. -// The option will be removed in the 1.8.0 version of the SDK. -var enableoriginverification = mcpgodebug.Value("enableoriginverification") - // allowsessionsinstateless is a compatibility parameter that restores the old // behavior of reading and using Mcp-Session-Id headers in stateless mode. When // set to "1", stateless servers will read the session ID from the request @@ -308,12 +289,6 @@ var allowsessionsinstateless = mcpgodebug.Value("allowsessionsinstateless") // permanently fails the connection. var noprotocolerrorbody = mcpgodebug.Value("noprotocolerrorbody") -// disablecontenttypecheck is a compatibility parameter that allows to disable -// Content-Type validation on POST requests. -// See the documentation for the mcpgodebug package for instructions how to enable it. -// The option will be removed in the 1.8.0 version of the SDK. -var disablecontenttypecheck = mcpgodebug.Value("disablecontenttypecheck") - // plaintextstatefulrejection is a compatibility parameter that restores the // previous behavior of a stateful [StreamableHTTPHandler] when it receives a // request carrying SEP-2575 per-request metadata (i.e. an @@ -341,7 +316,7 @@ func writeJSONRPCError(w http.ResponseWriter, status int, id jsonrpc.ID, jerr *j func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // DNS rebinding protection: auto-enabled for localhost servers. // See: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise - if !h.opts.DisableLocalhostProtection && disablelocalhostprotection != "1" { + if !h.opts.DisableLocalhostProtection { if localAddr, ok := req.Context().Value(http.LocalAddrContextKey).(net.Addr); ok && localAddr != nil { if util.IsLoopback(localAddr.String()) && !util.IsLoopback(req.Host) { http.Error(w, fmt.Sprintf("Forbidden: invalid Host header %q", req.Host), http.StatusForbidden) @@ -410,7 +385,7 @@ func (h *StreamableHTTPHandler) serveStateless(w http.ResponseWriter, req *http. return } - if disablecontenttypecheck != "1" && baseMediaType(req.Header.Get("Content-Type")) != "application/json" { + if baseMediaType(req.Header.Get("Content-Type")) != "application/json" { http.Error(w, "Content-Type must be 'application/json'", http.StatusUnsupportedMediaType) return } @@ -644,7 +619,7 @@ func (h *StreamableHTTPHandler) serveStatefulDELETE(w http.ResponseWriter, req * // ID, a new session is created (this is the normal path for the first // initialize request). func (h *StreamableHTTPHandler) serveStatefulPOST(w http.ResponseWriter, req *http.Request) { - if disablecontenttypecheck != "1" && baseMediaType(req.Header.Get("Content-Type")) != "application/json" { + if baseMediaType(req.Header.Get("Content-Type")) != "application/json" { http.Error(w, "Content-Type must be 'application/json'", http.StatusUnsupportedMediaType) return }