All articles

MCP Server for GitHub: Issues, PRs, and Repo Search (2026)

SWATI BARWAL

SWATI BARWAL

·14 min read

mcp

The MCP GitHub server turns your AI coding host into a first-class GitHub client: list open issues, search code across repos, read pull request diffs, and draft PR descriptions without leaving the editor. This tutorial walks through installing @modelcontextprotocol/server-github via npx, choosing Personal Access Token (PAT) scopes carefully, wiring Cursor and Claude Desktop, and knowing when a thin custom TypeScript tool beats the official server for one repo-specific action.

If you are new to the protocol, read What is MCP? first. For host-side security patterns (global vs project mcp.json, secret handling), pair this with MCP servers in Cursor.

Author: Aneh Thakur · Last updated: 2026-07-20

What you'll learn

  • What the GitHub MCP server exposes (issues, PRs, search, file contents)

  • How to run it with npx and authenticate with a PAT

  • Which token scopes to grant — and which to avoid by default

  • Exact mcpServers JSON for Cursor and Claude Desktop

  • Example Agent prompts that actually trigger useful tool calls

  • Security rules so tokens never land in git

  • When to keep the official server vs sketch a custom TypeScript tool

  • Troubleshooting for auth failures, empty results, and spawn errors

Prerequisites

  • Node.js 18+ (tested with Node 22 / 23 on macOS)

  • A GitHub account with access to the repos you care about

  • Cursor and/or Claude Desktop

  • Comfort editing JSON config files

  • Optional: Docker, if you later switch to GitHub’s container image instead of the npm package


What the GitHub MCP server does

Model Context Protocol defines how an AI host (Cursor, Claude Desktop) discovers and calls tools on a server. The GitHub server is one of the most useful reference servers in the modelcontextprotocol/servers collection: it wraps GitHub’s REST API behind MCP tools so the model can act on your repositories with structured arguments instead of guessing URLs.

In practice, after the server connects you can ask the agent to:

Workflow

What the server enables

Triage

List open issues, filter by label/assignee, summarize themes

Code archaeology

Search code across one repo or many (query syntax similar to GitHub search)

PR review prep

Fetch PR metadata, files changed, and comments so the model can draft a review

Shipping

Create or update issues, open PRs, comment on discussions (when the PAT allows write)

Repo hygiene

Inspect branches, file contents, and repository metadata without gh in the terminal

The model does not “magically” know your private repos. Every call goes through your PAT. If the token cannot see a private repository, neither can the agent — which is exactly what you want.

Official package vs newer GitHub-hosted options. This guide focuses on @modelcontextprotocol/server-github via npx because it matches most TrinityTuts / Cursor examples and needs only Node. In 2025–2026 GitHub also ships a maintained server image (ghcr.io/github/github-mcp-server) and a remote HTTP endpoint for Copilot-aware hosts. If npx pulls a deprecated or non-functional build on your machine, switch to the Docker or remote config from GitHub’s install docs — the PAT scopes and prompt patterns in this article still apply.


Step 1 — Create a Personal Access Token (scopes)

Never paste a password into MCP config. Use a Personal Access Token.

Fine-grained PAT (recommended)

1. Open GitHub → Settings → Developer settings → Fine-grained personal access tokens 2. Create a token with a short expiration (30–90 days) 3. Restrict Repository access to only the repos the agent should touch 4. Grant permissions explicitly:

Permission

Suggested access

Why

Metadata

Read-only

Required baseline for almost every API call

Contents

Read (or Read and write)

Search / read files; write only if you want commits via tools

Issues

Read and write

List, create, comment

Pull requests

Read and write

List, draft descriptions, open PRs

Workflows

None unless needed

Only if the agent must touch GitHub Actions

Start with read-only Contents + Issues + Pull requests if you only want triage and search. Elevate write scopes when you trust the host’s tool-approval UX.

Classic PAT (simpler, broader)

Classic tokens use OAuth-style scopes. The common combo for this server:

  • `repo` — full control of private repositories (contents, issues, PRs). Powerful. Prefer fine-grained if your org allows it.

  • `read:org` — organization membership and team data. Add only when the agent needs org-level listing. Do not enable it “just in case.”

SSO note: if your org enforces SAML SSO, authorize the token for that organization after creation or API calls return 403 even with correct scopes.

Store the token in your shell environment — not in a committed file:

Bash
# tested on: 2026-07-20
# ~/.zshrc or a secrets manager — never commit this
export GITHUB_TOKEN="github_pat_…or…ghp_…"

We map that env var into MCP config in the next steps.


Step 2 — Install and smoke-test via npx

You do not need a permanent global install. Cursor and Claude Desktop will spawn the server with npx -y on each session. Still, a one-shot smoke test catches bad tokens early:

Bash
# tested on: 2026-07-20
# Confirm the package resolves (may download on first run)
npx -y @modelcontextprotocol/server-github --help 2>/dev/null || true

# Confirm the token can hit the API independently of MCP
curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/user | head -c 200
echo

If curl returns your login JSON, the token works. If MCP later fails while curl succeeds, the bug is almost always env wiring (GUI apps do not inherit your interactive shell’s exports — see Troubleshooting).

Pin a version in production configs if you need reproducibility:

Code
npx -y @modelcontextprotocol/[email protected]

(Replace the version with whatever npm view reports as current when you install.)


Step 3 — Cursor `mcpServers` JSON

Cursor reads MCP servers from project or global config (Cursor MCP docs):

Scope

Path

Use when

Project

.cursor/mcp.json

One repo’s team shares GitHub tools

Global

~/.cursor/mcp.json

You want GitHub in every workspace

Recommended global config (token via env substitution — never hard-code the secret in a committed file):

JSON
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${env:GITHUB_TOKEN}"
      }
    }
  }
}

If ${env:…} substitution fails in your Cursor build, put the literal token only in the global file that is outside git, or launch Cursor from a terminal where GITHUB_TOKEN is exported.

After saving:

1. Command Palette → Developer: Reload Window (or fully quit and reopen Cursor) 2. Open Settings → MCP and confirm github shows as connected with tools listed 3. Switch to Agent mode and run a prompt from the section below

Project-scoped alternative — useful when only one product repo should allow GitHub writes:

JSON
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${env:GITHUB_TOKEN}"
      }
    }
  }
}

Commit .cursor/mcp.json without embedding secrets. Document GITHUB_TOKEN in the team README.


Step 4 — Claude Desktop config

Claude Desktop uses a different config path but the same stdio shape.

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

JSON
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "github_pat_REPLACE_ME"
      }
    }
  }
}

Claude Desktop launched from the Dock/Spotlight often does not see variables from ~/.zshrc. Practical options:

1. Paste the token into the env block of this local config file (never sync that file to a public gist) 2. Use a wrapper script that exports the token then exec npx … 3. Prefer Cursor’s ${env:GITHUB_TOKEN} pattern if you already keep secrets in the shell for IDE work

Fully quit Claude Desktop (not just close the window) after editing, then reopen. In a new chat, ask Claude to list available MCP tools — you should see GitHub-related tools once the server connects.


Step 5 — Example prompts that work

Vague prompts like “do something with GitHub” waste a turn. Name the owner/repo and the outcome. These patterns worked reliably in Agent mode during testing on 2026-07-20:

List issues

Using the GitHub MCP server, list open issues in OWNER/REPO labeled bug. Summarize each in one line with number, title, and assignee.

Search code

Search code in OWNER/REPO for TODO: migrate mcp and return the top 10 matches with file path and a short snippet. Do not invent matches — only report what the tool returns.

Draft a PR description

Fetch pull request #42 in OWNER/REPO. Read the changed files summary and existing description. Draft an improved PR description with Summary, Test plan, and Breaking changes sections. Do not push or update the PR until I approve.

Triage across an org (needs `read:org` or org-visible repos)

List my open pull requests awaiting review across repositories I can access. Group by repo. Flag anything older than 7 days.

Safe write (after you escalate PAT scopes)

Create a draft issue in OWNER/REPO titled “Docs: document GitHub MCP token scopes” with body checklist items from our security section. Wait for my confirmation before creating.

Always approve tool calls deliberately on first use. Treat write tools like deploying to production.


Security: never commit tokens; prefer fine-grained PATs

GitHub MCP is high leverage and high risk. A leaked PAT with repo scope is a full account compromise for every private repository that token can reach.

Rules that matter

1. Never commit GITHUB_PERSONAL_ACCESS_TOKEN, classic ghp_…, or fine-grained github_pat_… values. Scan PRs for them. 2. Prefer fine-grained PATs limited to specific repositories and short TTLs. 3. Grant `read:org` only when needed — org metadata exposure is easy to overlook. 4. Use read-only permissions for onboarding and demos; enable write after the team understands tool approval. 5. Rotate tokens on a calendar (90 days or less) and immediately after any laptop loss or accidental paste into chat logs. 6. Keep MCP config examples in docs using ${env:GITHUB_TOKEN} placeholders. 7. For shared machines, use separate tokens per developer — never a shared “team bot” token in a personal IDE.

Checklist before enabling write scopes

Check

Done?

Token expiration set

Repo allow-list reviewed

Tool approval prompts understood in Cursor/Claude

Secrets not in git history (git log -p / secret scanning)

Org SSO authorization completed

If your company forbids PATs in IDE configs, stop here and use a company-approved GitHub App or remote MCP gateway with short-lived credentials instead of a personal token on disk.


Official server vs custom TypeScript tools

Use the official @modelcontextprotocol/server-github when:

  • You need standard GitHub operations (issues, PRs, search, file read)

  • Multiple hosts (Cursor + Claude Desktop) should share the same tools

  • You do not want to maintain Octokit wrappers yourself

Build a custom MCP tool when:

  • One action is repo-specific (e.g. “open a release checklist issue with our exact template and labels”)

  • You must call internal wrappers (CODEOWNERS validation, custom GraphQL, private enterprise APIs)

  • You want to deny broad search while allowing a single safe mutation

  • Compliance requires every tool call to log to your audit system

Rule of thumb: start with the official server for discovery and triage. When you find yourself pasting the same multi-step prompt three times a week, extract that workflow into one custom tool with a tight schema. For a from-scratch server walkthrough, see Build your first MCP server in TypeScript. For a domain API example, see the Zerodha MCP guide.


Optional: custom TypeScript tool sketch (one repo-specific action)

Suppose you always want the agent to open a triage issue with fixed labels and a body template — without exposing full GitHub write APIs. A minimal custom server tool (sketch):

TypeScript
// src/create-triage-issue.ts — sketch only; wire into your McpServer
import { Octokit } from "@octokit/rest";
import * as z from "zod";

const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });

export const createTriageIssueInput = z.object({
  title: z.string().min(8).max(120),
  summary: z.string().min(20).max(4000),
});

export async function createTriageIssue(input: z.infer<typeof createTriageIssueInput>) {
  const { data } = await octokit.issues.create({
    owner: "YOUR_ORG",
    repo: "YOUR_REPO",
    title: `[triage] ${input.title}`,
    body: `## Summary\n\n${input.summary}\n\n## Checklist\n\n- [ ] Repro steps\n- [ ] Impact\n- [ ] Owner\n`,
    labels: ["triage", "needs-human"],
  });
  return { number: data.number, url: data.html_url };
}

Register that function with @modelcontextprotocol/sdk registerTool the same way as in the first TypeScript MCP tutorial. Point Cursor at your built node dist/index.js instead of (or in addition to) the official GitHub server. The official server remains for search and PR reads; your custom tool owns the one mutation you care about locking down.


Troubleshooting

Server shows connected but every tool returns empty / Bad credentials

Cause: GITHUB_PERSONAL_ACCESS_TOKEN is unset at process start (common when Cursor/Claude are GUI-launched and do not inherit shell exports).

Fix: Put the token in the MCP config env block for local debugging, or launch the host from a terminal after export GITHUB_TOKEN=…. Verify with curl against https://api.github.com/user.

`403` or “Resource not accessible by personal access token”

Cause: Missing fine-grained permission, repo not on the token allow-list, or SSO not authorized.

Fix: Edit the token — enable Issues / Pull requests / Contents as needed; add the repository; authorize SSO for the org.

`spawn npx ENOENT` or server never connects

Cause: Cursor’s PATH does not include Node/npx (especially on macOS GUI apps).

Fix: Use absolute paths, e.g. "command": "/usr/local/bin/npx" or the path from which npx. Reload the window after changing config.

Search returns nothing that exists on github.com

Cause: Wrong owner/repo, private repo invisible to the token, or query syntax too narrow.

Fix: Test the same query in GitHub’s web search while logged in as the token’s user. Confirm the repo is in the fine-grained allow-list.

Writes succeed in curl but model never offers write tools

Cause: Host tool filtering, or token is read-only so the server omits write capabilities.

Fix: Confirm PAT write permissions; in Cursor use Agent mode and explicitly ask to create/update; approve the tool call when prompted.

npm package behaves oddly after a long idle period

Cause: Stale npx cache or package deprecation toward GitHub’s Docker image.

Fix: Clear the npx cache, pin a known-good version, or migrate the config to docker run … ghcr.io/github/github-mcp-server with the same GITHUB_PERSONAL_ACCESS_TOKEN env var.


FAQ

  • What is the MCP GitHub server?

  • How do I install the GitHub MCP server with npx?

  • Which PAT scopes does the GitHub MCP server need?

  • Is it safe to put my GitHub token in mcp.json?

  • Why use the official server instead of writing my own?

  • Can I use the same GitHub MCP config in Cursor and Claude Desktop?

  • Does the GitHub MCP server work with private repositories?

  • What prompts work best with GitHub MCP?

Related posts


Next steps

1. Create a fine-grained PAT limited to one sandbox repo and verify with curl /user 2. Add the Cursor global github MCP entry with ${env:GITHUB_TOKEN} 3. Run the “list issues” and “search code” prompts in Agent mode 4. Only then enable write scopes and try a draft PR description workflow 5. If one workflow becomes repetitive, extract it into a custom TypeScript tool and keep the official server for everything else

SWATI BARWAL

Written by

SWATI BARWAL

1 follower

Related posts