Skip to content

Commit d16f98b

Browse files
committed
make headers parsing more robust, draft composite ff checker
1 parent ad7a49a commit d16f98b

File tree

4 files changed

+47
-8
lines changed

4 files changed

+47
-8
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"mode": "auto",
3232
"cwd": "${workspaceFolder}",
3333
"program": "cmd/github-mcp-server/main.go",
34-
"args": ["http"],
34+
"args": ["http", "--port", "8082"],
3535
"console": "integratedTerminal",
3636
}
3737
]

pkg/http/handler.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
"github.com/github/github-mcp-server/pkg/github"
9+
"github.com/github/github-mcp-server/pkg/http/headers"
910
"github.com/github/github-mcp-server/pkg/http/middleware"
1011
"github.com/github/github-mcp-server/pkg/inventory"
1112
"github.com/github/github-mcp-server/pkg/lockdown"
@@ -78,28 +79,55 @@ func (s *HTTPMcpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
7879
middleware.ExtractUserToken()(mcpHandler).ServeHTTP(w, r)
7980
}
8081

81-
func DefaultInventoryFactory(cfg *HTTPServerConfig, t translations.TranslationHelperFunc) InventoryFactoryFunc {
82+
func DefaultInventoryFactory(cfg *HTTPServerConfig, t translations.TranslationHelperFunc, staticChecker inventory.FeatureFlagChecker) InventoryFactoryFunc {
8283
return func(r *http.Request) *inventory.Inventory {
8384
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+
8492
b = InventoryFiltersForRequestHeaders(r, b)
8593
return b.Build()
8694
}
8795
}
8896

97+
// InventoryFiltersForRequestHeaders applies inventory filters based on HTTP request headers.
98+
// Whitespace is trimmed from comma-separated values; empty values are ignored.
8999
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) != "" {
91101
builder = builder.WithReadOnly(true)
92102
}
93103

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)
96106
builder = builder.WithToolsets(toolsets)
97107
}
98108

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)
101111
builder = builder.WithTools(github.CleanTools(tools))
102112
}
103113

104114
return builder
105115
}
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+
}

pkg/http/headers/headers.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,15 @@ const (
2323

2424
// RequestHmacHeader is used to authenticate requests to the Raw API.
2525
RequestHmacHeader = "Request-Hmac"
26+
27+
// MCP-specific headers.
28+
29+
// MCPReadOnlyHeader indicates whether the MCP is in read-only mode.
30+
MCPReadOnlyHeader = "X-MCP-Readonly"
31+
// MCPToolsetsHeader is a comma-separated list of MCP toolsets that the request is for.
32+
MCPToolsetsHeader = "X-MCP-Toolsets"
33+
// MCPToolsHeader is a comma-separated list of MCP tools that the request is for.
34+
MCPToolsHeader = "X-MCP-Tools"
35+
// MCPFeaturesHeader is a comma-separated list of feature flags to enable.
36+
MCPFeaturesHeader = "X-MCP-Features"
2637
)

pkg/http/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func RunHTTPServer(cfg HTTPServerConfig) error {
8282
repoAccessOpts = append(repoAccessOpts, lockdown.WithTTL(*cfg.RepoAccessCacheTTL))
8383
}
8484

85-
handler := NewHTTPMcpHandler(&cfg, t, &apiHost, repoAccessOpts, logger, DefaultInventoryFactory(&cfg, t))
85+
handler := NewHTTPMcpHandler(&cfg, t, &apiHost, repoAccessOpts, logger, DefaultInventoryFactory(&cfg, t, nil))
8686

8787
r := chi.NewRouter()
8888
r.Mount("/", handler)

0 commit comments

Comments
 (0)