Skip to content

Instantly share code, notes, and snippets.

@linosteenkamp
Created March 17, 2026 20:45
Show Gist options
  • Select an option

  • Save linosteenkamp/902dbf3129a28f88d307996533d7ec3d to your computer and use it in GitHub Desktop.

Select an option

Save linosteenkamp/902dbf3129a28f88d307996533d7ec3d to your computer and use it in GitHub Desktop.
GitHub MCP Server with GitHub CLI — Clean Token Setup for Claude Code

GitHub MCP Server with GitHub CLI — Clean Token Setup for Claude Code

No hardcoded tokens. No op run wrappers. Just gh auth login and a small bridge script.

The Problem

The GitHub MCP server needs a GITHUB_TOKEN or GITHUB_PERSONAL_ACCESS_TOKEN to authenticate. The naive approach is to hardcode it in your MCP config — but that's a plaintext secret sitting in a dotfile, one accidental commit away from leaking.

The Solution

Use the GitHub CLI (gh) as your credential store. It keeps your token securely in the system keychain. A small Node.js bridge script fetches the token at runtime and passes it to the MCP server — nothing ever touches a config file.


Prerequisites

# GitHub CLI
brew install gh

# Authenticate (stores token in system keychain)
gh auth login

When prompted, choose:

  • GitHub.com
  • HTTPS
  • Login with a web browser

If you need org access, make sure to include the right scopes:

gh auth refresh --scopes repo,read:org,gist

Step 1 — Create the bridge script

mkdir -p ~/.config/claude

cat > ~/.config/claude/github-mcp.mjs << 'EOF'
#!/usr/bin/env node
import { execSync, spawn } from "node:child_process";

try {
  execSync("gh auth status", { stdio: "ignore" });
} catch {
  console.error(
    "Error: Not authenticated with GitHub CLI. Run 'gh auth login' first.",
  );
  process.exit(1);
}

const token = execSync("gh auth token", { encoding: "utf-8" }).trim();

const child = spawn("npx", ["-y", "@modelcontextprotocol/server-github"], {
  env: { ...process.env, GITHUB_TOKEN: token },
  stdio: "inherit",
  shell: true,
});

child.on("exit", (code) => process.exit(code ?? 0));
EOF

chmod +x ~/.config/claude/github-mcp.mjs

What this does:

  1. Checks gh is authenticated — exits with a clear error if not
  2. Fetches the token from gh's secure keychain storage
  3. Passes it as an env var to the GitHub MCP server process
  4. Proxies the exit code cleanly

Step 2 — Register with Claude Code

Use the full path~ is not expanded by Claude's MCP runner:

claude mcp add github node /Users/YOUR_USERNAME/.config/claude/github-mcp.mjs

Replace YOUR_USERNAME with your actual username, or use $HOME:

claude mcp add github node $HOME/.config/claude/github-mcp.mjs

Step 3 — Verify

claude mcp list
# github: node /Users/you/.config/claude/github-mcp.mjs - ✓ Connected

Then launch Claude and test:

list my github repositories
list repositories for organisation YOUR_ORG_NAME
show open PRs in owner/repo

How it works

gh auth login
      │
      │  token stored in system keychain
      │
      ▼
github-mcp.mjs (runs on claude startup)
      │
      │  gh auth token  →  fetches from keychain
      │
      ▼
@modelcontextprotocol/server-github
      │
      │  GITHUB_TOKEN injected as env var
      │
      ▼
Claude MCP tools
(list repos, create issues, review PRs, etc.)

Troubleshooting

✗ Failed to connect

  • Make sure you used the full path, not ~
  • Run node /full/path/to/github-mcp.mjs directly to see the error

gh auth status shows no scopes

gh auth refresh --scopes repo,read:org,gist

Can't list org repos

  • Your PAT may need org approval — ask an org admin to approve the OAuth app under GitHub → Org Settings → Third-party Access

MCP server not appearing in Claude debug logs

tail -f ~/.claude/debug/*.txt | grep -i github

What you don't need

  • ❌ Hardcoded tokens in ~/.claude.json
  • op run wrappers
  • .env files with secrets
  • ❌ Custom shell aliases

Just gh auth login once, and plain claude works every time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment