|
6 | 6 | "strings" |
7 | 7 |
|
8 | 8 | "github.com/github/github-mcp-server/pkg/github" |
| 9 | + "github.com/github/github-mcp-server/pkg/http/headers" |
9 | 10 | "github.com/github/github-mcp-server/pkg/http/middleware" |
10 | 11 | "github.com/github/github-mcp-server/pkg/inventory" |
11 | 12 | "github.com/github/github-mcp-server/pkg/lockdown" |
@@ -78,28 +79,55 @@ func (s *HTTPMcpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
78 | 79 | middleware.ExtractUserToken()(mcpHandler).ServeHTTP(w, r) |
79 | 80 | } |
80 | 81 |
|
81 | | -func DefaultInventoryFactory(cfg *HTTPServerConfig, t translations.TranslationHelperFunc) InventoryFactoryFunc { |
| 82 | +func DefaultInventoryFactory(cfg *HTTPServerConfig, t translations.TranslationHelperFunc, staticChecker inventory.FeatureFlagChecker) InventoryFactoryFunc { |
82 | 83 | return func(r *http.Request) *inventory.Inventory { |
83 | 84 | b := github.NewInventory(t).WithDeprecatedAliases(github.DeprecatedToolAliases) |
| 85 | + |
| 86 | + // Feature checker composition |
| 87 | + headerFeatures := parseCommaSeparatedHeader(r.Header.Get(headers.MCPFeaturesHeader)) |
| 88 | + if checker := ComposeFeatureChecker(headerFeatures, staticChecker); checker != nil { |
| 89 | + b = b.WithFeatureChecker(checker) |
| 90 | + } |
| 91 | + |
84 | 92 | b = InventoryFiltersForRequestHeaders(r, b) |
85 | 93 | return b.Build() |
86 | 94 | } |
87 | 95 | } |
88 | 96 |
|
| 97 | +// InventoryFiltersForRequestHeaders applies inventory filters based on HTTP request headers. |
| 98 | +// Whitespace is trimmed from comma-separated values; empty values are ignored. |
89 | 99 | func InventoryFiltersForRequestHeaders(r *http.Request, builder *inventory.Builder) *inventory.Builder { |
90 | | - if r.Header.Get("X-MCP-Readonly") != "" { |
| 100 | + if r.Header.Get(headers.MCPReadOnlyHeader) != "" { |
91 | 101 | builder = builder.WithReadOnly(true) |
92 | 102 | } |
93 | 103 |
|
94 | | - if toolsetsStr := r.Header.Get("X-MCP-Toolsets"); toolsetsStr != "" { |
95 | | - toolsets := strings.Split(toolsetsStr, ",") |
| 104 | + if toolsetsStr := r.Header.Get(headers.MCPToolsetsHeader); toolsetsStr != "" { |
| 105 | + toolsets := parseCommaSeparatedHeader(toolsetsStr) |
96 | 106 | builder = builder.WithToolsets(toolsets) |
97 | 107 | } |
98 | 108 |
|
99 | | - if toolsStr := r.Header.Get("X-MCP-Tools"); toolsStr != "" { |
100 | | - tools := strings.Split(toolsStr, ",") |
| 109 | + if toolsStr := r.Header.Get(headers.MCPToolsHeader); toolsStr != "" { |
| 110 | + tools := parseCommaSeparatedHeader(toolsStr) |
101 | 111 | builder = builder.WithTools(github.CleanTools(tools)) |
102 | 112 | } |
103 | 113 |
|
104 | 114 | return builder |
105 | 115 | } |
| 116 | + |
| 117 | +// parseCommaSeparatedHeader splits a header value by comma, trims whitespace, |
| 118 | +// and filters out empty values. |
| 119 | +func parseCommaSeparatedHeader(value string) []string { |
| 120 | + if value == "" { |
| 121 | + return []string{} |
| 122 | + } |
| 123 | + |
| 124 | + parts := strings.Split(value, ",") |
| 125 | + result := make([]string, 0, len(parts)) |
| 126 | + for _, p := range parts { |
| 127 | + trimmed := strings.TrimSpace(p) |
| 128 | + if trimmed != "" { |
| 129 | + result = append(result, trimmed) |
| 130 | + } |
| 131 | + } |
| 132 | + return result |
| 133 | +} |
0 commit comments