Answers · MCP security
How do I give an MCP server least-privilege access to GitHub?
Last updated
TL;DR: Give the GitHub MCP server a credential that can reach only the repositories the agent works on, read-only by default, and make pushes and merges a separate, approved step. Use a fine-grained personal access token set to Only select repositories, with Contents and Pull requests read-only and an expiry date, or a GitHub App installed on just those repositories, whose installation tokens expire after an hour and can be narrowed further per request. Never use a classic token with the repo scope: it reaches every repository you can access. Then cut the tools down: GitHub's MCP server has a read-only mode (the /readonly URL or X-MCP-Readonly header remotely, GITHUB_READ_ONLY locally) and toolsets so you load only repos or issues. Finally, protect the branches: a ruleset that requires a pull request and an approval from someone other than the last pusher means even a write-enabled token can't land code on main by itself.
What should I check, step by step?
1. Pick the credential: a fine-grained personal access token for one person's agent, or a GitHub App for a team or a service. Avoid classic tokens; the repo scope covers every repository the account can reach, public and private.
- Limit repositories: set Repository access to Only select repositories (fine-grained token) or install the GitHub App on selected repositories only. Leave out private repositories the task doesn't touch.
- Read-only permissions by default: Contents read, Pull requests read, Issues read, plus Metadata read, which every token gets. Add write permissions only for a separate token or task that needs them.
- Set an expiry and rotate: give fine-grained tokens a short expiration; organization owners can require approval for fine-grained tokens and cap their lifetime. GitHub App installation tokens last one hour and can be minted for a subset of the installed repositories and permissions.
- Run the server read-only: remotely, use https://api.githubcopilot.com/mcp/readonly (or /x/<toolset>/readonly) or send X-MCP-Readonly: true; locally, set GITHUB_READ_ONLY=1 or --read-only. Load only the toolsets you need with X-MCP-Toolsets or GITHUB_TOOLSETS, for example repos,pull_requests.
- Separate approval for writes: use a second, write-enabled configuration only for tasks that push or open pull requests, keep the client's confirmation on for create, push, merge and delete tools, and show the full arguments.
- Protect the default branch: a ruleset or branch protection that requires a pull request, at least one approval, approval of the most recent push by someone other than the pusher, passing status checks, and blocks force pushes and deletions.
- Treat repository content as untrusted: issue bodies, comments and files can carry instructions. Don't give one session both access to private repositories and a way to publish to public ones; use one repository per session where you can.
Why does repository scope matter as much as read-only?
In May 2025 Invariant Labs showed a malicious issue in a public repository steering an agent connected to the GitHub MCP server into reading the user's private repositories and publishing their contents in a pull request on the public one. The token could reach both, so the agent could too.
- A token limited to the one repository being worked on breaks that chain: there is no private repository in reach to leak. Read-only mode alone doesn't, because reading private data is the first half of the attack.
- GitHub's lockdown mode, which hides public issue content from users without push access, helps against this, but GitHub describes it as a best-effort content filter, not a security boundary.
Should I use a fine-grained token or a GitHub App?
Fine-grained personal access token: quick to set up for your own agent; acts as you, limited to the repositories and permissions you pick; expires on the date you set.
- GitHub App: acts as the app, not a person, so its actions are labelled as the app in logs; permissions are fixed by the app's settings and the repositories it is installed on; installation tokens expire after one hour, so nothing long-lived sits in the agent's config.
- Either way, give read and write different credentials, so the token the agent uses most of the time can't push at all.
How do I ask a2ally about this (free, no key)?
Add the MCP server https://www.a2ally.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Before I connect the GitHub MCP server to my agent, is it a listed server, what tools does it expose, and have its tool definitions changed since they were recorded?”
{
"tool": "precall_check",
"arguments": {
"url": "https://api.githubcopilot.com/mcp/readonly",
"tool": "<optional tool name, for example get_file_contents>"
}
}- The answer combines the external registry entry, a live integrity probe against the recorded tool fingerprint, an invisible-text scan of the definitions and any security flags. It is evidence to decide from, never a finding that a server or token is safe.
- check_tool_integrity lists tools added, removed or changed since the definitions were recorded, so a write tool that appears in the read-only toolset after an update is visible before your agent uses it.
- scan_text checks an issue body, comment or file for hidden text and instruction-like language before your agent acts on it.
a2ally does not create GitHub tokens, install GitHub Apps or change your repository settings. Token scopes, read-only mode and branch rules are set in GitHub and your MCP client; a2ally checks the MCP server's listing and tool definitions.
What are the sources?
- GitHub Docs: Managing your personal access tokens (fine-grained tokens)
- GitHub Docs: Generating an installation access token for a GitHub App
- GitHub Docs: Setting a personal access token policy for your organization
- GitHub Docs: Available rules for rulesets
- GitHub MCP Server: remote server (toolsets, /readonly, X-MCP-Readonly, lockdown)
- Invariant Labs: GitHub MCP exploited: accessing private repositories via MCP (May 2025)
Related
- How to scope OAuth tokens per task for AI agents
- How to restrict an MCP database tool to read-only queries
- How to vet an MCP server before installing it
- How to detect MCP tool description changes (rug pulls)
Not legal advice. JSON · Markdown · All answers
Get a free API key in one call
No card and no email needed. 100 free credits; the license and policy checks stay free without a key.
curl -s -X POST https://www.a2ally.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export A2ALLY_API_KEY=rs_... # the api_key from the reply (shown once)
curl -s https://www.a2ally.com/v1/licenses/check -H "Authorization: Bearer $A2ALLY_API_KEY" \
-H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'
