The GitHub MCP Server supports OAuth 2.1 authentication for stdio mode, allowing users to authenticate via their browser instead of manually creating Personal Access Tokens.
When no GITHUB_PERSONAL_ACCESS_TOKEN is configured and OAuth credentials are available, the server starts without a token. On the first tool call, it triggers the OAuth flow:
-
PKCE flow (primary): A local callback server starts, your browser opens to GitHub's authorization page, and the token is received via callback. If the browser cannot open (e.g., Docker), the authorization URL is shown via MCP URL elicitation.
-
Device flow (fallback): If the callback server cannot start (e.g., Docker without port binding), the server falls back to GitHub's device flow. A code is displayed that you enter at github.com/login/device.
| Priority | Source | Notes |
|---|---|---|
| 1 (highest) | GITHUB_PERSONAL_ACCESS_TOKEN |
PAT is used directly, OAuth is skipped |
| 2 | GITHUB_OAUTH_CLIENT_ID (env/flag) |
Explicit OAuth credentials |
| 3 | Built-in credentials | Baked into official releases via build flags |
Docker is the standard distribution method. The recommended setup uses PKCE with a bound port:
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "GITHUB_OAUTH_CLIENT_ID",
"-e", "GITHUB_OAUTH_CLIENT_SECRET",
"-e", "GITHUB_OAUTH_CALLBACK_PORT=8085",
"-p", "127.0.0.1:8085:8085",
"ghcr.io/github/github-mcp-server"
],
"env": {
"GITHUB_OAUTH_CLIENT_ID": "your_client_id",
"GITHUB_OAUTH_CLIENT_SECRET": "your_client_secret"
}
}
}
}Security: Always bind to
127.0.0.1(not0.0.0.0) to restrict the callback to localhost.
If you cannot bind a port, the server falls back to device flow:
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "GITHUB_OAUTH_CLIENT_ID",
"-e", "GITHUB_OAUTH_CLIENT_SECRET",
"ghcr.io/github/github-mcp-server"
],
"env": {
"GITHUB_OAUTH_CLIENT_ID": "your_client_id",
"GITHUB_OAUTH_CLIENT_SECRET": "your_client_secret"
}
}
}
}For native binaries, PKCE works automatically with a random port:
export GITHUB_OAUTH_CLIENT_ID="your_client_id"
export GITHUB_OAUTH_CLIENT_SECRET="your_client_secret"
./github-mcp-server stdioThe browser opens automatically. No port configuration needed.
- Go to GitHub Settings → Developer settings → OAuth Apps
- Click New OAuth App
- Fill in:
- Application name: e.g., "GitHub MCP Server"
- Homepage URL:
https://github.com/github/github-mcp-server - Authorization callback URL:
http://localhost:8085/callback(match your--oauth-callback-port)
- Click Register application
- Copy the Client ID and generate a Client Secret
Note: The callback URL must be registered even for device flow, though it won't be used.
| Environment Variable | Flag | Description |
|---|---|---|
GITHUB_OAUTH_CLIENT_ID |
--oauth-client-id |
OAuth client ID |
GITHUB_OAUTH_CLIENT_SECRET |
--oauth-client-secret |
OAuth client secret |
GITHUB_OAUTH_CALLBACK_PORT |
--oauth-callback-port |
Fixed callback port (0 = random) |
GITHUB_OAUTH_SCOPES |
--oauth-scopes |
Override automatic scope selection |
All authorization code flows use PKCE with S256 challenge, preventing authorization code interception even if an attacker can observe the callback.
Docker requires a fixed callback port for port mapping. This is acceptable because:
- PKCE verifier is generated per-flow and never leaves the process — an attacker who intercepts the callback cannot exchange the code
- State parameter prevents CSRF — the callback validates state match
- Callback server binds to 127.0.0.1 — not accessible from outside the host
- Short-lived — the server shuts down immediately after receiving the callback
- Tokens are stored in memory only — never written to disk
- OAuth token takes precedence over PAT if both become available
- The server requests only the scopes needed by the configured tools
When the browser cannot auto-open, the authorization URL is shown via MCP URL-mode elicitation. This is secure because:
- URL elicitation presents the URL to the user without exposing it to the LLM context
- The MCP client shows the full URL for user inspection before navigation
- Credentials flow directly between the user's browser and GitHub — never through the MCP channel
Device flow is more susceptible to social engineering than PKCE (the device code could theoretically be phished), which is why PKCE is always attempted first. Device flow is only used when a callback server cannot be started.