Scope your GitHub token
A token is a permission slip. Most people write one that says “anything, anywhere, forever” because that’s the option that definitely works. Here’s the one that takes three extra minutes.
Connecting an assistant to GitHub is the point where this stops being about documents. It can read your code, search issues, review pull requests. Genuinely useful, especially if you’re not the one who wrote the code and you need to understand it.
But the same connection can create branches, push commits, open and merge pull requests, and reach every private repository you have access to. Whether it can do those things is decided by one screen, and most people click through it in about eight seconds.
First: use a fine-grained token, not a classic one
GitHub has two kinds of personal access token, and the old kind is the default in most people’s muscle memory.
Classic tokens grant broad scopes across your whole account. Tick repo and you’ve granted read and write to every repository you can see: personal, client, and any organization you’re a member of.
Fine-grained tokens let you choose specific repositories and set each permission to read-only or read-and-write. GitHub recommends them over classic tokens for exactly this reason.
The difference in practice: a classic token with repo on an assistant that only needs to read one project can push code, delete branches, merge pull requests, and read every private repository you have. That’s not a hypothetical, it’s just what that checkbox means.
Decide what it actually needs to do this week
Same discipline as the folder guide, and same reason. Decide before you’re looking at a screen full of checkboxes.
Most first uses are read-only on one repository: understand this codebase, find where this function is used, summarize these issues, explain this pull request. None of that requires write access.
Write access is a separate decision for later, once you’ve watched it work for a while. Nothing is lost by starting read-only. If you hit the limit, you’ll know precisely what you need and why.
Create the token
In GitHub: Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
Resource owner. Your own account, or the organization if the repo belongs to a client’s org. Organization tokens may need an admin to approve them, worth knowing before you promise someone it’ll be working this afternoon.
Repository access. Choose Only select repositories and pick the one. Never “All repositories” for an assistant: that’s the classic-token mistake wearing new clothes.
Permissions. Grant as little as the task needs:
| What you want it to do | Permission | Level |
|---|---|---|
| Read code, search files | Contents | Read-only |
| Read and search issues | Issues | Read-only |
| Review pull requests | Pull requests | Read-only |
| Read CI results | Actions | Read-only |
| Later: commit, branch, open PRs | Contents, Pull requests | Read and write |
Expiration. Set one. Ninety days is reasonable. An expiring token is a forced review. You’ll either renew it deliberately or discover you stopped needing it.
Copy the token when it’s shown. You won’t see it again.
Let the toolkit hold it, if you can
If you’re running MCP servers through the Docker MCP Toolkit (guide 5), prefer its OAuth flow over pasting a token into a config file:
- Add the GitHub server from the Catalog
- Open its Configuration tab
- Choose the OAuth authentication method and follow the link
- Authorize in the browser, then return to Docker Desktop
Authorized services are listed in the OAuth tab with a Revoke next to each one. That’s the part worth having, because revoking access later shouldn’t require remembering which JSON file you edited in August.
If you do use a token, it goes in the server’s configuration, never in a file that could end up in a repository. If you’ve ever pasted a token into something you later committed, assume it’s compromised and revoke it now.
Test the ceiling, not the floor
Everyone tests that it works. Almost nobody tests that it can’t do more.
Confirm the floor: “List the open issues on this repo.” You should get them.
Then test the ceiling: “Create a branch called test-permissions.”
It should fail with a permissions error. If a branch appears, your token is broader than you intended — go back to step 2.
One gotcha worth knowing. With fine-grained tokens, the GitHub MCP server can’t detect your token’s scopes, so it will show you all the tools it has, including write ones. The permissions are still enforced at GitHub’s end, so the calls will fail, but the tool list is not a reliable picture of what’s permitted. Don’t read the menu as the boundary; the token is the boundary.
You can shorten the menu too. The GitHub MCP server takes a --read-only flag, and read-only mode takes priority: write tools are skipped even when something explicitly asks for them. That is a second, independent limit on top of the token. Belt and braces, and worth setting while you’re only reading anyway.
The two are not interchangeable. The flag decides what the server offers. The token decides what GitHub permits. Set the token correctly regardless, because a flag on your machine is not a boundary anyone else is bound by.
Write down what you granted
One line somewhere you’ll find it: which repo, which permissions, which expiry, and why.
In eleven weeks a token will expire and something will stop working, and you’ll want to know what it was for before you renew it with a shrug.
If it’s a client’s repository
Two things change.
Ask first. Connecting an AI assistant to a client’s codebase is usually covered by whatever you signed, and the honest version of this conversation is short: I’d like to use an AI assistant to read the code, read-only, on this repo only. Fine with you? Most say yes. The ones who say no would have been very unhappy to find out later.
Read-only isn’t optional there. Whatever you’d allow on your own projects, a client repo gets the narrow token.
The short version
- Fine-grained token, never classic
- Only select repositories — one of them
- Read-only permissions, ninety-day expiry
- Prefer the toolkit’s OAuth over pasting the token anywhere
- Run the server with
--read-onlywhile you’re only reading - Ask it to create a branch. It should fail
- Write down what you granted and when
Sources
- Introducing fine-grained personal access tokens — GitHub Blog — why fine-grained tokens are recommended over classic ones
- Managing your personal access tokens — GitHub Docs — the creation flow, repository selection and expiry settings
- GitHub MCP server — PAT scope filtering — why fine-grained tokens still show all tools
- GitHub MCP server — README — the
--read-onlyflag, and that read-only takes priority over explicitly requested tools - Docker MCP Toolkit — OAuth authentication — the OAuth flow and the Revoke list
Written August 2026.
Prints to PDF from your browser — colours and all.