44 "context"
55 "log/slog"
66 "net/http"
7- "slices"
8- "strings"
97
108 ghcontext "github.com/github/github-mcp-server/pkg/context"
119 "github.com/github/github-mcp-server/pkg/github"
@@ -99,7 +97,7 @@ func withReadonly(next http.Handler) http.Handler {
9997func withToolset (next http.Handler ) http.Handler {
10098 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
10199 toolset := chi .URLParam (r , "toolset" )
102- ctx := ghcontext .WithToolset (r .Context (), toolset )
100+ ctx := ghcontext .WithToolsets (r .Context (), [] string { toolset } )
103101 next .ServeHTTP (w , r .WithContext (ctx ))
104102 })
105103}
@@ -143,7 +141,7 @@ func DefaultInventoryFactory(cfg *HTTPServerConfig, t translations.TranslationHe
143141 b := github .NewInventory (t ).WithDeprecatedAliases (github .DeprecatedToolAliases )
144142
145143 // Feature checker composition
146- headerFeatures := parseCommaSeparatedHeader (r .Header .Get (headers .MCPFeaturesHeader ))
144+ headerFeatures := headers . ParseCommaSeparated (r .Header .Get (headers .MCPFeaturesHeader ))
147145 if checker := ComposeFeatureChecker (headerFeatures , staticChecker ); checker != nil {
148146 b = b .WithFeatureChecker (checker )
149147 }
@@ -153,62 +151,25 @@ func DefaultInventoryFactory(cfg *HTTPServerConfig, t translations.TranslationHe
153151 }
154152}
155153
156- // InventoryFiltersForRequest applies inventory filters from request context and headers
157- // Whitespace is trimmed from comma-separated values; empty values are ignored
158- // Route configuration (context) takes precedence over headers for toolsets
154+ // InventoryFiltersForRequest applies filters to the inventory builder
155+ // based on the request context and headers
159156func InventoryFiltersForRequest (r * http.Request , builder * inventory.Builder ) * inventory.Builder {
160157 ctx := r .Context ()
161158
162- // Enable readonly mode if set in context or via header
163- if ghcontext .IsReadonly (ctx ) || relaxedParseBool (r .Header .Get (headers .MCPReadOnlyHeader )) {
159+ if ghcontext .IsReadonly (ctx ) {
164160 builder = builder .WithReadOnly (true )
165161 }
166162
167- // Parse request configuration
168- contextToolset := ghcontext .GetToolset (ctx )
169- headerToolsets := parseCommaSeparatedHeader (r .Header .Get (headers .MCPToolsetsHeader ))
170- tools := parseCommaSeparatedHeader (r .Header .Get (headers .MCPToolsHeader ))
171-
172- // Apply toolset filtering (route wins, then header, then tools-only mode, else defaults)
173- switch {
174- case contextToolset != "" :
175- builder = builder .WithToolsets ([]string {contextToolset })
176- case len (headerToolsets ) > 0 :
177- builder = builder .WithToolsets (headerToolsets )
178- case len (tools ) > 0 :
179- builder = builder .WithToolsets ([]string {})
163+ if toolsets := ghcontext .GetToolsets (ctx ); len (toolsets ) > 0 {
164+ builder = builder .WithToolsets (toolsets )
180165 }
181166
182- if len (tools ) > 0 {
167+ if tools := headers .ParseCommaSeparated (r .Header .Get (headers .MCPToolsHeader )); len (tools ) > 0 {
168+ if len (ghcontext .GetToolsets (ctx )) == 0 {
169+ builder = builder .WithToolsets ([]string {})
170+ }
183171 builder = builder .WithTools (github .CleanTools (tools ))
184172 }
185173
186174 return builder
187175}
188-
189- // parseCommaSeparatedHeader splits a header value by comma, trims whitespace,
190- // and filters out empty values.
191- func parseCommaSeparatedHeader (value string ) []string {
192- if value == "" {
193- return []string {}
194- }
195-
196- parts := strings .Split (value , "," )
197- result := make ([]string , 0 , len (parts ))
198- for _ , p := range parts {
199- trimmed := strings .TrimSpace (p )
200- if trimmed != "" {
201- result = append (result , trimmed )
202- }
203- }
204- return result
205- }
206-
207- // relaxedParseBool parses a string into a boolean value, treating various
208- // common false values or empty strings as false, and everything else as true.
209- // It is case-insensitive and trims whitespace.
210- func relaxedParseBool (s string ) bool {
211- s = strings .TrimSpace (strings .ToLower (s ))
212- falseValues := []string {"" , "false" , "0" , "no" , "off" , "n" , "f" }
213- return ! slices .Contains (falseValues , s )
214- }
0 commit comments