@@ -18,9 +18,9 @@ const queryParamFeatures = "features"
1818
1919// WithRequestConfig is a middleware that extracts MCP-related headers and sets them in the request context.
2020// This includes readonly mode, toolsets, tools, lockdown mode, insiders mode, and feature flags.
21- // Feature flags may also arrive via the `features` URL query parameter; when
22- // both are present the query parameter wins, matching how the toolset path
23- // segments take precedence over their headers .
21+ // Feature flags may also arrive via the `features` URL query parameter. When
22+ // both are present, the X-MCP-Features header takes precedence and the two
23+ // channels are never combined .
2424func WithRequestConfig (next http.Handler ) http.Handler {
2525 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
2626 ctx := r .Context ()
@@ -57,18 +57,26 @@ func WithRequestConfig(next http.Handler) http.Handler {
5757
5858 // Feature flags: presence-based selection. The URL query parameter and
5959 // the X-MCP-Features header are separate channels — whichever is
60- // present is used as-is, and they are never combined. When both are
61- // present the query parameter wins, so a client composing the server
62- // URL can always express its intent even when it cannot control
63- // headers . Unknown flags are dropped later by ResolveFeatureFlags
64- // against AllowedFeatureFlags, so neither channel is privileged .
60+ // present is used as-is, and they are never combined. Header presence
61+ // takes precedence even when its value is empty or contains only
62+ // unknown flags, so a query parameter cannot override an explicit
63+ // header . Unknown flags are dropped later by ResolveFeatureFlags
64+ // against AllowedFeatureFlags.
6565 queryFeatures , hasQuery := r .URL .Query ()[queryParamFeatures ]
66- headerFeatures := r .Header . Get (headers .MCPFeaturesHeader )
66+ headerFeatures , hasHeader := r .Header [ http . CanonicalHeaderKey (headers .MCPFeaturesHeader )]
6767 switch {
68- case hasQuery && strings .TrimSpace (queryFeatures [0 ]) != "" :
69- ctx = ghcontext .WithHeaderFeatures (ctx , headers .ParseCommaSeparated (queryFeatures [0 ]))
70- case headerFeatures != "" :
71- ctx = ghcontext .WithHeaderFeatures (ctx , headers .ParseCommaSeparated (headerFeatures ))
68+ case hasHeader :
69+ headerValue := ""
70+ if len (headerFeatures ) > 0 {
71+ headerValue = headerFeatures [0 ]
72+ }
73+ ctx = ghcontext .WithHeaderFeatures (ctx , headers .ParseCommaSeparated (headerValue ))
74+ case hasQuery :
75+ queryValue := ""
76+ if len (queryFeatures ) > 0 {
77+ queryValue = queryFeatures [0 ]
78+ }
79+ ctx = ghcontext .WithHeaderFeatures (ctx , headers .ParseCommaSeparated (queryValue ))
7280 }
7381
7482 next .ServeHTTP (w , r .WithContext (ctx ))
0 commit comments