How to Actually Setup GitHub MCP Server with Docker in Cursor (The Working Guide)
Because the official documentation doesn’t work, and you’re probably pulling your hair out right now
🤖 AI Disclosure: This guide was written by Claude (Anthropic’s AI assistant) based on real troubleshooting experience. The human author believes in transparency about AI-generated content and wanted to share this working solution with the community.
The Problem Everyone’s Having
If you’re here, you’ve probably tried to set up the GitHub MCP server in Cursor following the official documentation, only to be greeted with frustrating “authorization denied” errors. You’re not alone—the official docs are misleading, and the GitHub MCP server documentation doesn’t properly explain the Docker setup.
The Issue: Your Docker container is receiving the literal string ${GITHUB_TOKEN}
instead of your actual GitHub token, causing authentication to fail.
Why The Official Docs Don’t Work
The problem stems from mixing two different approaches for environment variables:
- Cursor’s MCP
env
section (which uses JSON) - Docker’s
-e
flags (which expect shell-style environment passing)
When you use shell substitution syntax like ${GITHUB_TOKEN}
in JSON, it doesn’t get resolved—Docker literally receives the string "${GITHUB_TOKEN}"
as your token.
What You’ve Probably Tried (And Why It Fails)
Following the official docs, you likely created something like this in your ~/.cursor/mcp.json
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"-e",
"GITHUB_TOOLSETS",
"ghcr.io/github/github-mcp-server:latest"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}",
"GITHUB_TOOLSETS": "issues,pull_requests,repos"
}
}
}
}
This doesn’t work because:
- ❌ You’re mixing Docker
-e
flags with MCPenv
section - ❌ Shell substitution
${GITHUB_TOKEN}
doesn’t resolve in JSON - ❌ Docker receives the literal string
"${GITHUB_TOKEN}"
as your token
The Solution That Actually Works
Here’s the step-by-step guide to get this working properly:
Step 1: Set Up Your GitHub Token
First, make sure you have a GitHub Personal Access Token. If you don’t have one:
- Go to GitHub → Settings → Developer settings → Personal access tokens
- Generate a new token with these scopes:
repo
,issues
,pull_requests
- Copy the token (starts with
github_pat_...
)
Most people already have GITHUB_TOKEN
in their environment. The GitHub MCP server expects GITHUB_PERSONAL_ACCESS_TOKEN
, so create an alias:
1
2
3
4
5
# For your current session
export GITHUB_PERSONAL_ACCESS_TOKEN=$GITHUB_TOKEN
# Make it permanent (add to your shell profile)
echo 'export GITHUB_PERSONAL_ACCESS_TOKEN=$GITHUB_TOKEN' >> ~/.zshrc
Step 2: Create the Correct MCP Configuration
Create or edit ~/.cursor/mcp.json
with this working configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"-e",
"GITHUB_TOOLSETS=issues,pull_requests,repos",
"ghcr.io/github/github-mcp-server:latest"
]
}
}
}
What Makes This Work
✅ No MCP env
section - eliminates JSON variable substitution issues
✅ Pure Docker syntax - uses -e VARNAME
to pass environment variables from host
✅ Clean separation - Docker handles environment passing, MCP handles execution
Step 3: Verify Your Setup
Check that everything is configured correctly:
1
2
3
# Verify your token is set (shows first 12 characters for security)
echo "GITHUB_TOKEN: $(echo $GITHUB_TOKEN | cut -c1-12)..."
echo "GITHUB_PERSONAL_ACCESS_TOKEN: $(echo $GITHUB_PERSONAL_ACCESS_TOKEN | cut -c1-12)..."
Both should show the same token prefix (e.g., github_pat_1...
).
Step 4: Restart and Test
- Restart Cursor completely (not just reload)
- Check MCP Settings: Go to Cursor Settings → MCP
- Verify the server is listed and shows as available
Why This Works (Technical Explanation)
The magic is in how Docker handles environment variables:
-e GITHUB_PERSONAL_ACCESS_TOKEN
(without a value) tells Docker: “Pass the environment variable with this name from the host”- Your shell environment contains the actual token value
- Docker automatically resolves and passes the real token to the container
- No JSON parsing issues because we’re not using shell substitution in JSON
Testing Your Setup
Once everything is configured, test that it’s working:
In Cursor’s chat/composer, try asking something like:
- “List the open issues in this repository”
- “Show me the README file”
- “Create a new issue for [something]”
The Cursor Agent should automatically use the GitHub MCP tools to fulfill these requests.
✅ Proof It Works
Here’s what successful usage looks like:
- ✅ File Access: Can retrieve any file from your repositories
- ✅ Issue Management: Can list, create, and update issues
- ✅ Repository Operations: Can access branches, commits, and metadata
- ✅ No Auth Errors: No more “authorization denied” messages
When Things Go Wrong (Troubleshooting)
Still getting “authorization denied”?
Step 1: Double-check your environment variables
1
2
# This should show your token (first 20 chars)
echo $GITHUB_PERSONAL_ACCESS_TOKEN | cut -c1-20
Step 2: Verify token permissions
Your GitHub token needs these scopes:
- ✅
repo
(for repository access) - ✅
issues
(for issue management) - ✅
pull_requests
(for PR management)
Step 3: Fresh restart
Environment variable changes need a clean slate:
- Close Cursor completely
- Open a new terminal session
- Start Cursor from the new terminal
Nuclear Option: Hardcode the Token
⚠️ Only if you’re desperate and the above doesn’t work, you can hardcode the token:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/github/github-mcp-server:latest"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "github_pat_your_actual_token_here",
"GITHUB_TOOLSETS": "issues,pull_requests,repos"
}
}
}
}
⚠️ Security Warning: This puts your token in plaintext. Only use for testing or in secure environments.