diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index b9d8af64f..ed5ea961a 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -96,6 +96,7 @@ var ( Short: "Start HTTP server", Long: `Start an HTTP server that listens for MCP requests over HTTP.`, RunE: func(_ *cobra.Command, _ []string) error { + ttl := viper.GetDuration("repo-access-cache-ttl") httpConfig := ghhttp.HTTPServerConfig{ Version: version, Host: viper.GetString("host"), @@ -104,6 +105,8 @@ var ( EnableCommandLogging: viper.GetBool("enable-command-logging"), LogFilePath: viper.GetString("log-file"), ContentWindowSize: viper.GetInt("content-window-size"), + LockdownMode: viper.GetBool("lockdown-mode"), + RepoAccessCacheTTL: &ttl, } return ghhttp.RunHTTPServer(httpConfig) diff --git a/pkg/context/request.go b/pkg/context/request.go index 8b9169955..94882a3ce 100644 --- a/pkg/context/request.go +++ b/pkg/context/request.go @@ -49,3 +49,19 @@ func GetTools(ctx context.Context) []string { } return nil } + +// lockdownCtxKey is a context key for lockdown mode +type lockdownCtxKey struct{} + +// WithLockdownMode adds lockdown mode state to the context +func WithLockdownMode(ctx context.Context, enabled bool) context.Context { + return context.WithValue(ctx, lockdownCtxKey{}, enabled) +} + +// IsLockdownMode retrieves the lockdown mode state from the context +func IsLockdownMode(ctx context.Context) bool { + if enabled, ok := ctx.Value(lockdownCtxKey{}).(bool); ok { + return enabled + } + return false +} diff --git a/pkg/github/dependencies.go b/pkg/github/dependencies.go index d9f9b602d..bdcafe933 100644 --- a/pkg/github/dependencies.go +++ b/pkg/github/dependencies.go @@ -87,7 +87,7 @@ type ToolDependencies interface { GetT() translations.TranslationHelperFunc // GetFlags returns feature flags - GetFlags() FeatureFlags + GetFlags(ctx context.Context) FeatureFlags // GetContentWindowSize returns the content window size for log truncation GetContentWindowSize() int @@ -165,7 +165,7 @@ func (d BaseDeps) GetRepoAccessCache(_ context.Context) (*lockdown.RepoAccessCac func (d BaseDeps) GetT() translations.TranslationHelperFunc { return d.T } // GetFlags implements ToolDependencies. -func (d BaseDeps) GetFlags() FeatureFlags { return d.Flags } +func (d BaseDeps) GetFlags(_ context.Context) FeatureFlags { return d.Flags } // GetContentWindowSize implements ToolDependencies. func (d BaseDeps) GetContentWindowSize() int { return d.ContentWindowSize } @@ -262,7 +262,6 @@ func NewRequestDeps( lockdownMode bool, repoAccessOpts []lockdown.RepoAccessOption, t translations.TranslationHelperFunc, - flags FeatureFlags, contentWindowSize int, featureChecker inventory.FeatureFlagChecker, ) *RequestDeps { @@ -272,7 +271,6 @@ func NewRequestDeps( lockdownMode: lockdownMode, RepoAccessOpts: repoAccessOpts, T: t, - Flags: flags, ContentWindowSize: contentWindowSize, featureChecker: featureChecker, } @@ -379,7 +377,11 @@ func (d *RequestDeps) GetRepoAccessCache(ctx context.Context) (*lockdown.RepoAcc func (d *RequestDeps) GetT() translations.TranslationHelperFunc { return d.T } // GetFlags implements ToolDependencies. -func (d *RequestDeps) GetFlags() FeatureFlags { return d.Flags } +func (d *RequestDeps) GetFlags(ctx context.Context) FeatureFlags { + return FeatureFlags{ + LockdownMode: d.lockdownMode && ghcontext.IsLockdownMode(ctx), + } +} // GetContentWindowSize implements ToolDependencies. func (d *RequestDeps) GetContentWindowSize() int { return d.ContentWindowSize } diff --git a/pkg/github/feature_flags_test.go b/pkg/github/feature_flags_test.go index fb50448af..f9cfe4acb 100644 --- a/pkg/github/feature_flags_test.go +++ b/pkg/github/feature_flags_test.go @@ -45,7 +45,7 @@ func HelloWorldTool(t translations.TranslationHelperFunc) inventory.ServerTool { if deps.IsFeatureEnabled(ctx, RemoteMCPEnthusiasticGreeting) { greeting += " Welcome to the future of MCP! 🎉" } - if deps.GetFlags().InsiderMode { + if deps.GetFlags(ctx).InsiderMode { greeting += " Experimental features are enabled! 🚀" } diff --git a/pkg/github/issues.go b/pkg/github/issues.go index 309e03ff3..c4cc54175 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -334,7 +334,7 @@ func GetIssue(ctx context.Context, client *github.Client, deps ToolDependencies, if err != nil { return nil, fmt.Errorf("failed to get repo access cache: %w", err) } - flags := deps.GetFlags() + flags := deps.GetFlags(ctx) issue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber) if err != nil { @@ -389,7 +389,7 @@ func GetIssueComments(ctx context.Context, client *github.Client, deps ToolDepen if err != nil { return nil, fmt.Errorf("failed to get repo access cache: %w", err) } - flags := deps.GetFlags() + flags := deps.GetFlags(ctx) opts := &github.IssueListCommentsOptions{ ListOptions: github.ListOptions{ @@ -449,7 +449,7 @@ func GetSubIssues(ctx context.Context, client *github.Client, deps ToolDependenc if err != nil { return nil, fmt.Errorf("failed to get repo access cache: %w", err) } - featureFlags := deps.GetFlags() + featureFlags := deps.GetFlags(ctx) opts := &github.IssueListOptions{ ListOptions: github.ListOptions{ diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 614e4496f..5d9be21f1 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -139,7 +139,7 @@ func GetPullRequest(ctx context.Context, client *github.Client, deps ToolDepende if err != nil { return nil, fmt.Errorf("failed to get repo access cache: %w", err) } - ff := deps.GetFlags() + ff := deps.GetFlags(ctx) pr, resp, err := client.PullRequests.Get(ctx, owner, repo, pullNumber) if err != nil { @@ -350,7 +350,7 @@ func GetPullRequestReviewComments(ctx context.Context, gqlClient *githubv4.Clien if err != nil { return nil, fmt.Errorf("failed to get repo access cache: %w", err) } - ff := deps.GetFlags() + ff := deps.GetFlags(ctx) // Convert pagination parameters to GraphQL format gqlParams, err := pagination.ToGraphQLParams() @@ -437,7 +437,7 @@ func GetPullRequestReviews(ctx context.Context, client *github.Client, deps Tool if err != nil { return nil, fmt.Errorf("failed to get repo access cache: %w", err) } - ff := deps.GetFlags() + ff := deps.GetFlags(ctx) reviews, resp, err := client.PullRequests.ListReviews(ctx, owner, repo, pullNumber, nil) if err != nil { diff --git a/pkg/github/server_test.go b/pkg/github/server_test.go index 614738429..59d39c9dc 100644 --- a/pkg/github/server_test.go +++ b/pkg/github/server_test.go @@ -56,7 +56,7 @@ func (s stubDeps) GetRepoAccessCache(ctx context.Context) (*lockdown.RepoAccessC return s.repoAccessCache, nil } func (s stubDeps) GetT() translations.TranslationHelperFunc { return s.t } -func (s stubDeps) GetFlags() FeatureFlags { return s.flags } +func (s stubDeps) GetFlags(ctx context.Context) FeatureFlags { return s.flags } func (s stubDeps) GetContentWindowSize() int { return s.contentWindowSize } func (s stubDeps) IsFeatureEnabled(_ context.Context, _ string) bool { return false } diff --git a/pkg/http/headers/headers.go b/pkg/http/headers/headers.go index b73104c34..a1580cf96 100644 --- a/pkg/http/headers/headers.go +++ b/pkg/http/headers/headers.go @@ -32,6 +32,8 @@ const ( MCPToolsetsHeader = "X-MCP-Toolsets" // MCPToolsHeader is a comma-separated list of MCP tools that the request is for. MCPToolsHeader = "X-MCP-Tools" + // MCPLockdownHeader indicates whether lockdown mode is enabled. + MCPLockdownHeader = "X-MCP-Lockdown" // MCPFeaturesHeader is a comma-separated list of feature flags to enable. MCPFeaturesHeader = "X-MCP-Features" ) diff --git a/pkg/http/middleware/request_config.go b/pkg/http/middleware/request_config.go index 0f7d3b2c7..da9127bf1 100644 --- a/pkg/http/middleware/request_config.go +++ b/pkg/http/middleware/request_config.go @@ -26,6 +26,10 @@ func WithRequestConfig(next http.Handler) http.Handler { ctx = ghcontext.WithTools(ctx, tools) } + if relaxedParseBool(r.Header.Get(headers.MCPLockdownHeader)) { + ctx = ghcontext.WithLockdownMode(ctx, true) + } + next.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/pkg/http/server.go b/pkg/http/server.go index c14ae9eee..106e65e9e 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -89,9 +89,6 @@ func RunHTTPServer(cfg HTTPServerConfig) error { cfg.LockdownMode, repoAccessOpts, t, - github.FeatureFlags{ - LockdownMode: cfg.LockdownMode, - }, cfg.ContentWindowSize, nil, )