How to Block Private Repos from GitHub MCP

Developers want the GitHub MCP server in Claude, Cursor, and their IDE agents. Security won’t approve it, because one prompt injection in a public issue can steer an agent into every private repository the developer can reach: security tooling, infrastructure code, unreleased products, incident playbooks. GitHub’s controls scope a credential. They can’t express “this developer can open the repo, but the agent acting for them can’t.”

This guide covers where GitHub’s native controls stop and how a custom rule engine on MCP Manager’s gateway enforces one company-wide repository policy for every developer’s agent.

Key Takeaways

  • GitHub MCP calls the GitHub API with the credential it receives, so an agent reaches every repository that credential reaches.
  • In May 2025, Invariant Labs showed a malicious public issue steering an agent into leaking private repository data.
  • Fine-grained PATs, read-only mode, and lockdown mode reduce exposure, but they scope credentials and connections, not an organization-wide repository policy.
  • A gateway rule applies one repository blocklist or allowlist to every agent on the gateway, whichever token sits behind each user.
  • Blocking takes two rules: a request-leg rule for calls that name a repository, and a response-leg rule for search results.
  • The rule engine is a webhook your team builds, hosts, and maintains.

How a Public Issue Showed GitHub Permissions Are Not an Agent Boundary

The GitHub MCP server authenticates with an OAuth token, in hosts that register a GitHub App or OAuth App, or with a Personal Access Token (PAT). Every tool call runs with that credential’s reach. A classic PAT can access every repository its owner can access.

A developer opens a repository on purpose. An agent reads whatever a prompt sends it to, including text written by strangers, as Invariant Labs demonstrated in its GitHub MCP disclosure on May 26, 2025:

  • An attacker opens an issue in a public repository, with instructions hidden in the body.
  • The repository owner asks their agent to look at the open issues.
  • The agent reads the issue, then uses the same credential to pull data from the owner’s private repositories.
  • The agent publishes that data in a pull request on the public repository.

Invariant describes this as an architectural problem at the agent-system level, not a flaw in the server code, and states that GitHub alone cannot resolve it through server-side patches. The attack needs access to private data, exposure to untrusted content, and a way to exfiltrate information. Removing any one of the three breaks this attack chain.

What GitHub’s Native Controls Cover

GitHub offers real controls, and a rollout should use them first:

  • Fine-grained PATs: each token is limited to one user or organization and can be narrowed to specific repositories. Organization owners can require approval for these tokens.
  • Read-only mode: appending /readonly to a remote server URL, or sending X-MCP-Readonly, removes write tools, including the ones the Invariant flow used to publish data.
  • Lockdown mode: the X-MCP-Lockdown header limits content from public repositories to authors with push access. Private repositories are unaffected.

Three properties define where these controls stop:

  • Public repositories stay readable. Fine-grained tokens always include read-only access to all public repositories on GitHub, which is where an injected issue lives.
  • Credential scope is not policy. A token covers what one person chose at creation time. Nothing checks that every developer’s token leaves out the same sensitive repositories, and a token set to all repositories picks up every new sensitive repository automatically.
  • No repository filter on the server. A request to restrict the server to specific organizations or repositories remains open. A maintainer pointed to fine-grained tokens, lockdown mode, and Enterprise Managed Users in the meantime.

An MCP gateway rule adds the layer these controls can’t express: one repository boundary per team, enforced the same way regardless of which credential each developer brings.

Want to see how an MCP gateway separates human access from agent access? Book a demo with MCP Manager.

How a Rule Engine Sees GitHub MCP Traffic

The MCP Manager gateway sits between the AI client and the GitHub MCP server, and every tools/call passes through it. A custom rule engine is the part that decides what happens to each call. It is an HTTPS webhook, built and hosted by your team, that the gateway consults at two points: on the request leg, before the call reaches GitHub, and on the response leg, before the result reaches the client. The gateway sends the message, the engine returns a verdict, and the gateway applies it inline.

The engine is registered once under Rule Engines, then selected as the detection method of a gateway rule. The rule decides where the engine fires. The engine decides what happens.

What the Engine Receives and Returns

The engine receives the JSON-RPC body plus metadata such as serverGuid, toolName, and userEmail. It answers with one of four verdicts:

  • Pass forwards the message unchanged.
  • Block returns a JSON-RPC error instead.
  • Modify forwards a rewritten response.
  • Error falls back to the rule’s failure mode, which is Block by default.

The Repository Lives in the Body, Not the Headers

The 2026-07-28 MCP spec requires Mcp-Method and Mcp-Name headers on Streamable HTTP requests, so gateways can authorize on the tool name without parsing the body. For GitHub, Mcp-Name says get_file_contents, not which repository. The target sits in the owner and repo arguments, so repository policy still requires inspecting each body.

GitHub Tools Fall into Three Groups

  • Repository-scoped tools take owner and repo: get_file_contents, list_issues, issue_read, pull_request_read, and writes such as create_or_update_file, push_files, and create_pull_request.
  • Search tools take a query: search_code, search_issues, search_pull_requests, search_commits, and search_repositories. A repository appears only as a repo: or org: qualifier, if at all, and results span every repository the credential can read.
  • Account-level tools such as list_notifications and list_starred_repositories return items from many repositories without a repository argument.

Result Shapes Depend on the Negotiated Protocol Version

Repository tools can return typed structuredContent when protocol version 2026-07-28 or newer is negotiated, and legacy content otherwise. Legacy content itself varies by tool, from text to embedded resources and resource links. A response-leg parser has to handle each shape the connected tools return, and the filtered field should be checked against captured payloads.

Step-by-Step Guide to Building GitHub Repository Controls

The engine blocks calls to restricted repositories and filters them out of results. Build and test it with a pilot team before provisioning the gateway more widely.

Step 1: Choose the Policy Unit and List Type

Before any code is written, the policy needs a definition: which GitHub repositories agents must not reach, and how the engine will recognize them. Two choices decide the rest of the build, the unit of matching and the type of list.

Unit of Matching

Entries are written as owner/repo, or as a bare owner to cover a whole organization. Because the matching logic is custom code, a name pattern such as a sec- prefix also works. Names are compared after normalizing case, since an agent may pass them with different capitalization than the list.

List Type

  • Blocklist by owner suits a dedicated organization for security tooling or infrastructure. New repositories in that organization are blocked automatically.
  • Allowlist fails closed. A new, renamed, or transferred repository stays out of reach until someone approves it, which a name-based blocklist cannot guarantee.

Step 2: Connect GitHub and Close the Side Door

Add the GitHub server to MCP Manager as described in the Connect GitHub guide: pick a toolset URL and supply Authorization: Bearer <PAT>. A token is required because the remote server doesn’t support Dynamic Client Registration for third-party gateways.

Gateway rules only see traffic that passes through the gateway. Developers can still add GitHub’s MCP server directly to Claude, Cursor, or VS Code, so work through the MCP Enterprise Lockdown Checklist to route MCP traffic through governed gateways.

Step 3: Create the Gateway and Assign the Server

Rules, tool provisioning, and the identity scheme are all configured on a gateway, so the server needs one first. A gateway is created, the GitHub server is assigned to it, and the identity scheme is chosen on that assignment:

  • Per-user identity: each developer pastes their own PAT on first connection, and GitHub’s logs show the actual person.
  • Shared identity: one PAT serves everyone. MCP Manager’s logs still attribute each call to the real user, but GitHub’s logs show the PAT owner.

A new gateway exposes nothing until a server is assigned and a client connects. On assignment, each feature type defaults to Allowing all. The gateway stays unprovisioned to any team until the rule is in place.

Step 4: Pin the Tool Surface with Feature Provisioning

In the gateway, feature provisioning under “Allow if conditions are met” shrinks what the rule has to cover: it exposes only allowlisted tools and fails closed for anything the server adds or renames later.

  • Leave out tools the engine doesn’t parse, such as notifications and gists, unless agents need them.
  • Hide destructive tools such as delete_repository unless a use case requires them.
  • Block prompts and resources unless needed, because gateway rules inspect tools/call traffic only.

Step 5: Build the Request-Leg Rule

The request rule fires before a call reaches GitHub. Because a rule engine sees every tools/call on its gateway, it first checks serverGuid and passes traffic from other servers.

For GitHub calls, the engine:

  • Reads owner and repo from body.params.arguments and blocks restricted targets.
  • Blocks a repository-scoped call that arrives without those arguments.
  • Parses repo:, org:, and user: qualifiers in search queries and blocks queries that name restricted targets.

Blocking write tools for a repository while passing its reads gives agents read-only access to it. Restricting which repositories accept writes, especially public ones, removes the publishing step of a toxic agent flow.

Step 6: Build the Response-Leg Rule

A search_code query without qualifiers returns matches from every repository the credential can read. The response rule drops items whose repository is restricted, rewrites count and pagination fields to match, and returns a modify verdict so the agent keeps legitimate results.

MCP Manager’s allowlist recipe for channels, projects, and spaces provides example handlers for both legs. They are instructional starting points, so GitHub’s payload paths have to be mapped against captured responses.

GitHub adds three cases:

  • Field projections: search_code accepts a fields argument, and a projection without repository drops the data the rule matches on. Block results the engine can’t attribute to a repository, or block projections that drop it on the request leg.
  • Cross-repository references: an issue or pull request in an allowed repository can reference a restricted one. Decide whether a reference alone is acceptable.
  • Unparseable results: a result from a covered tool that can’t be parsed is blocked, not passed unchecked.

Step 7: Deploy, Register, and Apply

The engine needs a public HTTPS endpoint with a certificate from a public CA, HTTP 200 for every verdict, responses well within the 30-second timeout, and idempotent handling, since timeouts and 5xx errors are retried. Authenticate calls with a static header and allowlist MCP Manager’s static outbound IP. Enable header forwarding only if the engine must call GitHub as the user, because it passes the user’s credential to your service.

Register the engine under Rule Engines with the Custom provider. Then add a rule on the gateway’s Rules tab that selects the engine as its detection method, and leave the failure mode at Block.

Important: registering an engine does not apply it. If restricted repositories still appear in agent responses, check that a rule on the gateway’s Rules tab references the engine.

Step 8: Test with a Pilot Team

Following the safe rollout sequence, provision the gateway to two or three admins, then exercise each path:

  • Read a file in a restricted repository, once with different capitalization.
  • Run an unqualified search_code query that matches restricted and allowed repositories.
  • Open a pull request against a repository marked read-only.
  • Ask for open issues in a public repository with lockdown mode active.

Confirm each verdict in the logs, where blocks appear as policy_enforced_abort and rewrites as policy_enforced_mutation. Read result bodies, not only the status: under the MCP spec, a tool that fails upstream returns a normal result with isError: true. Widen access only after every path behaves as intended.

What Building a Custom GitHub Rule Engine Includes

Unlike the managed Google Drive Folder Blocker, where an admin supplies folder IDs and writes no code, a GitHub repository rule is code your team owns. No managed GitHub engine exists today, so the build is part of the decision. It includes:

  • A webhook with a parser per GitHub tool and the four verdicts (pass, block, modify, error)
  • A public HTTPS endpoint with monitoring
  • Tests against captured payloads
  • Updates when GitHub adds, renames, or changes tools

Three ownership points should be settled before the build starts:

  • Maintenance: GitHub hosts the remote server, so tool changes arrive on GitHub’s schedule. Each change can alter a payload the parser reads, so a named engineering owner is needed.
  • Security review: coverage of every path has to be demonstrable. Test cases, captured payloads, and log evidence make that case for a self-built rule.
  • Operations: the engine sits in the path of every GitHub call. With the default failure mode, an outage blocks calls until the service recovers.

Coding tips for the build: a coding agent can draft most of the engine from the MCP Manager docs for agents, and the custom rule engine tutorial covers registration end to end. Review the result like any other code before it enforces policy.

Real-World Scenarios for Blocking GitHub Repositories

Repository rules differ by what the team needs to protect: private product code next to public issues, infrastructure that only one team should reach, or personal data pasted into issue threads. The three scenarios below show which rule fits which situation.

  • Public open source next to private product code: agents read public issues with lockdown mode on, writes to public repositories are blocked, and private product repositories stay off the general gateway’s allowlist.
  • Different boundaries per team: the gateway is the smallest unit of governance, so the platform team’s gateway can allow infrastructure repositories that every other gateway blocks.
  • Personal data in issues: issue bodies and pull request comments often contain pasted email addresses. A Microsoft Presidio rule set to Replace turns detected entities into typed tags such as <EMAIL_ADDRESS>, alongside the repository rule.

GitHub MCP Without the Path from Public Issue to Private Repo

The choice between blocking GitHub MCP entirely and letting agents read every repository a token reaches is a false one. Fine-grained tokens, read-only mode, and lockdown mode narrow each connection. A custom rule engine on MCP Manager’s gateway adds one repository policy per team, applied to every agent and logged with every verdict.

If your organization is holding back GitHub MCP because of a handful of repositories, book a demo with MCP Manager and scope the rule that unblocks it.

FAQ

Can I block specific GitHub repositories from AI agents without disabling GitHub MCP?

Yes. A request-leg rule blocks calls that name a restricted repository, and a response-leg rule removes restricted repositories from search results. Everything else passes, so the GitHub MCP server stays available for all other repositories.

How is a gateway rule different from a fine-grained PAT?

A fine-grained PAT limits what one credential can reach and is set by whoever creates it. A gateway rule applies one policy to every agent on the gateway, can treat reads and writes differently per repository, and logs every verdict. Use both.

Can I block an entire GitHub organization?

Yes. The engine compares the owner argument, and the owner in search results, against blocked organizations. Any repository under a blocked owner is denied, including repositories created after the rule was written.

Does blocking a repository change anyone’s access in GitHub?

No. The rule only changes what agents receive through the MCP gateway. Repository permissions, team membership, and the GitHub UI stay untouched.

Is a managed GitHub repository blocker available?

Not today. GitHub repository blocking runs on a custom rule engine that your team builds and hosts. If you need a managed option, raise it with your MCP Manager contact or when booking a demo.

Try MCP Manager by Usercentrics for free.

Start Free Trial

Seven days to explore our platform.