MCP Server Lockdown Mode and Repository Access Restrictions Explained
The GitHub MCP server uses lockdown mode to prevent tools from returning sensitive repository content to unauthorized callers by validating every request against a cached access control layer.
The Model Context Protocol (MCP) server for GitHub provides secure access to repository data through a lockdown mechanism that restricts content visibility based on user permissions. When enabled, MCP server lockdown mode enforces repository access restrictions by verifying that the requesting user has appropriate permissions—such as push access—or is accessing their own private repositories. This security layer is implemented through middleware, context propagation, and a centralized RepoAccessCache that minimizes GraphQL API calls through intelligent caching.
How Lockdown Mode Is Activated
Lockdown mode can be triggered either per-request via HTTP headers or globally via server configuration flags. The activation flow propagates through three distinct layers of the codebase.
Header-Based Activation
The middleware in pkg/http/middleware/request_config.go (lines 33-36) inspects incoming requests for the X-MCP-Lockdown header. When this header is present and parses to true, the middleware invokes ghcontext.WithLockdownMode to mark the request context:
// From pkg/http/middleware/request_config.go
if lockdown, err := strconv.ParseBool(r.Header.Get("X-MCP-Lockdown")); err == nil && lockdown {
ctx = ghcontext.WithLockdownMode(ctx, true)
}
Context Propagation
The context helpers in pkg/context/request.go (lines 56-66) provide type-safe storage and retrieval of the lockdown flag. WithLockdownMode stores a boolean under a private context key, while IsLockdownMode retrieves it for downstream tool handlers:
// From pkg/context/request.go
func WithLockdownMode(ctx context.Context, enabled bool) context.Context {
return context.WithValue(ctx, lockdownModeKey, enabled)
}
func IsLockdownMode(ctx context.Context) bool {
v, _ := ctx.Value(lockdownModeKey).(bool)
return v
}
Global Server Configuration
Administrators can enable lockdown mode globally via the --lockdown-mode flag. The server configuration in pkg/github/server.go (lines 54-55) defines the boolean flag, while internal/ghmcp/server.go (lines 85-87) propagates this value to the request-handling layer, ensuring all incoming requests are treated as locked down regardless of headers.
Repository Access Restrictions and Safety Checks
When lockdown mode is active, tool handlers delegate authorization decisions to the RepoAccessCache, which implements a multi-tiered safety check based on repository metadata and user permissions.
The RepoAccessCache Decision Engine
The core logic resides in pkg/lockdown/lockdown.go. The RepoAccessCache provides the IsSafeContent method that tool handlers invoke:
func (c *RepoAccessCache) IsSafeContent(
ctx context.Context,
username, owner, repo string,
) (bool, error)
This method returns true only when the requesting user is authorized to view content from the specified repository under lockdown constraints.
Safety Logic and Trusted Bots
The IsSafeContent implementation (lines 122-134 in pkg/lockdown/lockdown.go) applies a hierarchical permission model:
- Trusted bots – Users listed in
trustedBotLogins(currently containing only"copilot") are automatically granted access - Private repositories – Content is allowed because GitHub's native permissions already restrict access to authorized users
- Self-viewing – Users accessing their own content (where
repoInfo.ViewerLoginmatches the requesting username) are permitted - Push access – For public repositories, only users with explicit push permissions are granted access
// From pkg/lockdown/lockdown.go lines 131-134
if c.isTrustedBot(username) || repoInfo.IsPrivate || repoInfo.ViewerLogin == strings.ToLower(username) {
return true, nil
}
return repoInfo.HasPushAccess, nil
GraphQL Metadata and Caching Strategy
To minimize API calls, RepoAccessCache uses an in-memory cache (cache2go) with the following strategy:
Cache lookup (lines 47-58): The cache key combines owner, repository name, and requesting username. Cache hits return immediately without GraphQL queries.
Cache miss handling (lines 201-229): When metadata is absent, queryRepoAccessInfo executes a GraphQL query fetching:
repository.isPrivate– Boolean privacy statusviewer.login– The authenticated user's logincollaboratorsedge filtered by the requesting username to determineWRITE,ADMIN, orMAINTAINpermissions
Cache storage (lines 86-92): Results are stored with a configurable TTL to balance freshness with performance.
Tool-Level Enforcement
Individual tool implementations check the lockdown flag before returning content to callers.
Pull Request and Issue Tools
The enforcement pattern appears consistently across tool handlers. In pkg/github/pullrequests.go (lines 72-86), the GetPullRequest handler checks:
if ff.LockdownMode {
safe, err := cache.IsSafeContent(ctx, pr.GetUser().GetLogin(), owner, repo)
if err != nil || !safe {
return utils.NewToolResultError("access to pull request is restricted by lockdown mode"), nil
}
}
Similarly, pkg/github/issues.go (lines 353-366) applies identical checks for issue content, verifying the issue author's username against the repository access cache before returning data.
Error Handling When Access Is Denied
When IsSafeContent returns false or encounters an error, tools return a standardized error result:
return utils.NewToolResultError("access to pull request is restricted by lockdown mode"), nil
This prevents any repository content from leaking to unauthorized clients while providing clear feedback that the restriction is active.
Practical Implementation Examples
Enabling Lockdown from Client Requests
Clients activate per-request lockdown by including the header:
GET /tools/pullrequest?owner=github&repo=mcp-server&number=123 HTTP/1.1
Host: mcp.example.com
X-MCP-Lockdown: true
Authorization: Bearer <token>
The middleware automatically sets IsLockdownMode(ctx) == true for this request.
Manual Safety Checks in Custom Tools
When building custom tools that respect lockdown mode:
func canView(ctx context.Context, username, owner, repo string) (bool, error) {
// Obtain the singleton cache – usually injected via dependencies
cache := lockdown.GetInstance(nil) // nil client is fine after first init
return cache.IsSafeContent(ctx, username, owner, repo)
}
Extending Trusted Bot Access
To add additional trusted bots that bypass permission checks:
cache := lockdown.GetInstance(gqlClient, lockdown.WithTTL(10*time.Minute))
cache.trustedBotLogins["my-bot"] = struct{}{}
Note that this modifies internal state; production deployments should initialize trusted bots during server startup rather than at runtime.
Summary
- MCP server lockdown mode is a security feature that prevents unauthorized access to repository content by enforcing strict permission checks on every tool request.
- Activation occurs via the
X-MCP-Lockdownheader processed by middleware inpkg/http/middleware/request_config.goor globally via the--lockdown-modeserver flag. - The RepoAccessCache in
pkg/lockdown/lockdown.goimplements the core safety logic, caching repository metadata to minimize GraphQL API calls while enforcing access rules. - Access is granted only to trusted bots (like Copilot), users viewing their own content, users accessing private repositories, or collaborators with push access to public repositories.
- Tool handlers in
pkg/github/pullrequests.goandpkg/github/issues.gocheckIsSafeContentbefore returning data, returning standardized errors when access is restricted.
Frequently Asked Questions
What triggers lockdown mode in the GitHub MCP server?
Lockdown mode triggers when the server receives a request with the X-MCP-Lockdown: true header or when started with the --lockdown-mode command-line flag. The middleware in pkg/http/middleware/request_config.go detects the header and stores the flag in the request context using ghcontext.WithLockdownMode, making it available to all downstream tool handlers.
How does the RepoAccessCache determine if content is safe to return?
The RepoAccessCache.IsSafeContent method in pkg/lockdown/lockdown.go applies a four-tier check: first, it verifies if the user is a trusted bot (like "copilot"); second, it allows access if the repository is private; third, it permits users to view their own content; finally, for public repositories, it requires the user to have push access. The cache stores these permissions temporarily to avoid repeated GraphQL queries.
Which tools enforce lockdown mode checks?
Pull request tools in pkg/github/pullrequests.go (such as GetPullRequest and GetPullRequestFiles) and issue tools in pkg/github/issues.go (like GetIssue) enforce lockdown checks. These handlers verify ff.LockdownMode and call cache.IsSafeContent before returning repository data. If the check fails, they return a ToolResultError indicating that access is restricted by lockdown mode.
Can I add custom trusted bots to bypass lockdown restrictions?
Yes, you can extend the trustedBotLogins map in the RepoAccessCache to include additional bot accounts. During server initialization, obtain the cache instance via lockdown.GetInstance() and add entries to the trustedBotLogins map (e.g., cache.trustedBotLogins["my-bot"] = struct{}{}). Note that this map is internal; production deployments should configure trusted bots during startup rather than runtime to maintain security boundaries.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →