Skip to content

GitHub MCP Server: Remote, Local, Toolsets and Lockdown Mode

The GitHub MCP Server is GitHub’s official Model Context Protocol server. It lets Claude Code, Codex and Cursor read and act on pull requests, issues, GitHub Actions logs and security alerts. The hosted endpoint https://api.githubcopilot.com/mcp/ works with a personal access token. What matters is scope: expose only the toolsets a task needs, and enable lockdown mode on public repositories.

PR #412 has been red since lunch: 4,000 lines of failing log and six review comments. You would rather hand the loop to your agent, but only if it can read Actions logs and review threads, and only if you trust what it may push and merge.

This page is for developers wiring the server into their agent and tech leads deciding the shared configuration: token, toolsets, and the tools the agent may never call.

  • A tested install for Claude Code, Codex and Cursor that keeps the token out of the repository.
  • A PAT scope table that grants each workflow only the permissions it uses.
  • Toolset, exclusion and lockdown settings that expose only what a task needs and filter content from outsiders.
  • Prompts that turn a red pull request into a pushed fix and answer review threads, without merging.
  • The GitLab equivalents, for teams that host code there.

Should the agent use GitHub MCP or the gh CLI?

Section titled “Should the agent use GitHub MCP or the gh CLI?”

All three agents run shell commands, so gh and git need no MCP server. The GitHub MCP Server earns its place when the agent needs typed, paginated access to GitHub state, or when it runs somewhere without a shell. Many teams run both.

JobBest routeWhy
Commit, branch, rebase, diff in the working treeLocal git through the agent’s shellFastest, no API calls, no token
Read one failing CI loggh run view RUN_ID --log-failed, or get_job_logs with failed_only and tail_linesgh costs fewer tokens; the MCP tool can trim the log for you
Read review threads and reply to each oneGitHub MCP (pull_request_read method get_review_comments, add_reply_to_pull_request_comment, pull_request_review_write method resolve_thread)Thread IDs and replies are awkward to script with gh
Triage 50 issues into a tableGitHub MCP (search_issues, issue_read)Structured results, pagination handled by the server
An agent with no shell (a desktop chat client, a hosted agent)GitHub MCPIt is the only route
Structured git history in a client that cannot run gitThe reference Git server, uvx mcp-server-git --repository . (PyPI only)See reference MCP servers

Both gh and the MCP server spend the same budget when they use the same token: the primary rate limit for authenticated users is 5,000 requests per hour on github.com.

Remote or local: which GitHub MCP server should you run?

Section titled “Remote or local: which GitHub MCP server should you run?”

GitHub publishes one codebase in two forms. The remote server is the default choice; the local server is for GitHub Enterprise Server, air-gapped machines and teams that want OAuth without a PAT.

Remote (https://api.githubcopilot.com/mcp/)Local (Docker ghcr.io/github/github-mcp-server or a release binary)
Auth in Claude Code, Codex, CursorPAT in an Authorization: Bearer header, per GitHub’s install guides. OAuth works only in hosts that registered a GitHub App or OAuth AppBrowser OAuth login built into the official image, no token needed; token kept in memory only. A PAT in GITHUB_PERSONAL_ACCESS_TOKEN takes precedence. github.com only; GitHub Enterprise Server and ghe.com need your own OAuth or GitHub App, or a PAT
Toolset scopingURL path /mcp/x/TOOLSET and /readonly, or X-MCP-Toolsets, X-MCP-Tools, X-MCP-Exclude-Tools, X-MCP-Readonly headers--toolsets, --tools, --exclude-tools, --read-only flags or GITHUB_TOOLSETS, GITHUB_TOOLS, GITHUB_EXCLUDE_TOOLS, GITHUB_READ_ONLY
Lockdown modeX-MCP-Lockdown: true header--lockdown-mode or GITHUB_LOCKDOWN_MODE=1
GitHub EnterpriseEnterprise Cloud with data residency at https://copilot-api.SUBDOMAIN.ghe.com/mcp; not Enterprise ServerBoth, via --gh-host or GITHUB_HOST (HTTPS enforced)
Extra toolscreate_pull_request_with_copilot, Copilot Spaces, github_support_docs_searchNot available
NeedsNothing installedDocker running, or the binary on PATH

Install the GitHub MCP server in Claude Code, Codex and Cursor

Section titled “Install the GitHub MCP server in Claude Code, Codex and Cursor”

The remote server works the same way in all three agents: one URL and a bearer token. Only the config syntax differs.

  1. Create a fine-grained personal access token at Settings > Developer settings > Personal access tokens, limited to the repositories the agent works on. Pick permissions from the scope table in the next section.

  2. Export it in your shell profile, not in a file inside the repository:

    Terminal window
    # ~/.zshrc or ~/.bashrc
    export GITHUB_PAT="github_pat_..."
  3. Register the server in your agent:

    Terminal window
    # Terminal. Project scope writes .mcp.json; the single quotes keep ${GITHUB_PAT}
    # unexpanded, so teammates share the file and each supplies their own token.
    claude mcp add -s project --transport http github https://api.githubcopilot.com/mcp/ \
    -H 'Authorization: Bearer ${GITHUB_PAT}'

    Tested on 2.1.283: the resulting .mcp.json holds "Authorization": "Bearer ${GITHUB_PAT}", which Claude Code expands at connect time. Drop -s project to keep the entry private to you (the default local scope).

  4. Verify the connection. In Claude Code run claude mcp get github (it health-checks the server) or /mcp inside a session. In Codex run /mcp in the TUI. In Cursor open the MCP section of the settings and check that github lists tools. Then ask the agent: Call get_me and tell me which GitHub user you are authenticated as.

A plugin bundles the same remote server with no JSON to write. Each expects its own variable name, the most common reason a plugin shows zero tools.

AgentCommandToken variable it reads
Claude Codeclaude plugin install github@claude-plugins-officialGITHUB_PERSONAL_ACCESS_TOKEN (exactly this name)
Codexcodex plugin add github@openai-curatedGITHUB_PAT_TOKEN
Cursor/add-plugin githubNot documented in our sources; check the plugin’s mcp.json

The Codex plugin install was not tested end to end on 2026-09-26; if it fails, use codex mcp add above.

Local Docker server with OAuth and no token

Section titled “Local Docker server with OAuth and no token”

To skip the PAT entirely, run the local server. The official image carries GitHub’s OAuth app credentials, opens a browser login on first use and needs a fixed callback port published to loopback:

Terminal window
# Claude Code, terminal (GitHub's documented line)
claude mcp add github -e GITHUB_OAUTH_CALLBACK_PORT=8085 -- docker run -i --rm \
-p 127.0.0.1:8085:8085 -e GITHUB_OAUTH_CALLBACK_PORT ghcr.io/github/github-mcp-server

The Codex and Cursor equivalents pass the same docker run arguments as command/args with GITHUB_OAUTH_CALLBACK_PORT = "8085" in env. Without a published port the server falls back to GitHub’s device-code flow and prints a code to enter at github.com/login/device.

Copilot CLI ships the GitHub MCP server pre-installed as github-mcp-server, with read-only tools on by default. Check it with /mcp show github-mcp-server, widen it per session with copilot --add-github-mcp-toolset actions, or turn it off with copilot --disable-builtin-mcps. The GitHub Copilot page covers the cloud agent, whose GitHub server uses a token scoped read-only to the current repository.

Which PAT scopes does the GitHub MCP server need?

Section titled “Which PAT scopes does the GitHub MCP server need?”

The server can do what the token allows and nothing more, so the token is your real permission boundary. Grant by job:

JobClassic PAT scopeFine-grained PAT permission (selected repos only)
Read code, issues, PRs, CI logsrepoContents, Issues, Pull requests, Actions: read
Comment, open PRs, push filesrepoContents, Issues, Pull requests: read and write
Change a file under .github/workflows/repo + workflowWorkflows: read and write
Org teams and membersread:orgMembers (organization permission): read
Gists, notifications, classic projectsgist, notifications, projectthe matching permission

Two behaviours are worth knowing before you debug a missing tool:

  • Classic PATs filter tools at startup. The server reads the token’s scopes from the X-OAuth-Scopes header and hides tools that need a scope you did not grant. A repo-only token shows no gist or notification tools. Check what a token carries with curl -sI -H "Authorization: Bearer $GITHUB_PAT" https://api.github.com/user | grep -i x-oauth-scopes.
  • Fine-grained PATs show every tool. The API enforces permissions at call time, so the agent sees a tool, calls it and gets a 403. That is expected; narrow the tool list with toolsets instead.

Limit the GitHub MCP server to the toolsets a task needs

Section titled “Limit the GitHub MCP server to the toolsets a task needs”

By default the server loads context, repos, issues, pull_requests and users. A CI fix also needs actions; triage needs nothing that writes. Fewer tools means better tool choice and less room for a mistake.

On the remote server you have two levers:

  • URL path, one toolset per URL: https://api.githubcopilot.com/mcp/x/actions, and /readonly on the end of any of them (https://api.githubcopilot.com/mcp/x/actions/readonly, https://api.githubcopilot.com/mcp/readonly).
  • Headers, for combinations: X-MCP-Toolsets: repos,issues,pull_requests,actions, X-MCP-Tools for single tools, X-MCP-Readonly: true.

The path accepts a single toolset only, so a CI-fix setup that combines four uses the header:

Terminal window
claude mcp add -s project --transport http github https://api.githubcopilot.com/mcp/ \
-H 'Authorization: Bearer ${GITHUB_PAT}' \
-H "X-MCP-Toolsets: context,repos,issues,pull_requests,actions" \
-H "X-MCP-Lockdown: true"

On the local server the same scoping is --toolsets context,repos,issues,pull_requests,actions (or GITHUB_TOOLSETS), plus --read-only. Read-only mode wins over an explicit --tools list: a write tool you name is still skipped.

What the GitHub MCP server costs in context

Section titled “What the GitHub MCP server costs in context”

Tool search is on by default in Claude Code and Codex, so the schemas are loaded on demand. claude plugin details github shows about zero always-on tokens, but that command does not count MCP tool schemas (checked on 2.1.283), so measure with /context instead. The cost that bites is tool results: a full Actions log or a 30-item issue list lands in the context window. Run /context in Claude Code before and after a CI-fix run to see it, and tell the agent to call get_job_logs with failed_only: true and tail_lines set, as the prompt below does. For more on trimming results, see reducing MCP token cost.

Turn on lockdown mode for public repositories

Section titled “Turn on lockdown mode for public repositories”

An agent that reads issues and comments on a public repository reads text written by strangers, and that text can carry instructions. Lockdown mode is GitHub’s filter for that: for public repositories the server checks whether each item’s author has push access, and withholds content from people who do not. Private repositories are unaffected.

What the agent sees under lockdown:

  • issue_read (get) and pull_request_read (get, get_diff, get_files, get_commits) return an error when the author lacks push access.
  • Comments, sub-issues, review comments and reviews are filtered: items by users without push access are dropped.
  • Content from github-actions[bot] and copilot always passes, so CI output stays visible.

Switch it on with --lockdown-mode or GITHUB_LOCKDOWN_MODE=1 locally, or X-MCP-Lockdown: true on the remote server. In HTTP mode an operator’s flag is an upper bound: the header can turn lockdown on but not off.

Fix a red pull request from its Actions log

Section titled “Fix a red pull request from its Actions log”

This is the full usage example. It works the same in Claude Code, Codex and Cursor once the server is connected with the actions toolset and your local checkout is on the PR branch.

What you should see, with the tool names observed on the live remote server on 2026-09-26:

  • actions_list returns the branch’s runs, and the agent picks the newest with conclusion: failure.
  • get_job_logs returns a few hundred lines of each failed job instead of thousands.
  • A new workflow run starts after the push, and the agent reports the evidence and stops.

The agent reads GitHub through MCP but edits the local checkout, so every change shows in git diff and passes your local hooks.

From issue to merge: the GitHub MCP workflow

Section titled “From issue to merge: the GitHub MCP workflow”

The CI fix above is one step of a loop from issue to merged pull request, with a person at the one gate that matters.

  1. Triage. The agent reads open issues with search_issues and issue_read, and proposes labels and a priority. With the issues toolset in read-only mode, it cannot change anything yet.

  2. Branch and build. You pick an issue. The agent creates a local branch, writes the change and the tests, and opens a pull request with create_pull_request, linking the issue.

  3. CI-log-driven fix. When checks fail, the agent runs the prompt above: read the failed log, reproduce, fix, push. It repeats until the required checks pass.

  4. Review comments. Reviewers, human or AI review bots, leave comments. The agent reads them with pull_request_read method get_review_comments, which also returns each thread’s threadId, answers each thread with add_reply_to_pull_request_comment, pushes the fixes and resolves a thread with pull_request_review_write method resolve_thread only when the change is in.

  5. Merge. A person approves and merges once branch protection is satisfied. The agent never calls merge_pull_request (see the guardrail below).

The token can merge if the user can, so remove the tool on the client side as well:

In .claude/settings.json. A deny rule on a bare tool name removes the tool from the model’s context:

{
"permissions": {
"deny": ["mcp__github__merge_pull_request", "mcp__github__delete_file"]
}
}

The X-MCP-Exclude-Tools header works in any client that sends custom headers, and the local server takes the same list as --exclude-tools or GITHUB_EXCLUDE_TOOLS. Use it as a server-side complement to the Claude Code deny rule and the Codex disabled_tools list: the tool is removed before any client sees it.

Branch protection with required status checks and a required approving review is the backstop: even a misconfigured agent cannot merge past it.

How do you verify the agent’s GitHub work?

Section titled “How do you verify the agent’s GitHub work?”

You do not read every line the agent pushes. You check the evidence; the repository enforces the rest.

  • Reproduce before fix, pass after fix. The prompt forces the agent to show the failing command and then the same command passing. A fix with no reproduction goes back.
  • Required checks are the oracle. Branch protection marks the CI jobs required, so “green” means the same suite that caught the failure now passes. Watch it with gh pr checks 412 --watch.
  • One reviewer signs off. A person approves the pull request, reading the agent’s report and the test diff first; see reviewing agent pull requests for what to check.
  • Every action is auditable. Comments, replies and pushes appear under the token’s user; a machine-user token for automated runs separates agent actions from yours.
  • Rollback is a revert. The agent works in normal commits, so git revert undoes any change.

GitLab: the official MCP server and zereight/gitlab-mcp

Section titled “GitLab: the official MCP server and zereight/gitlab-mcp”

GitLab teams have two options. GitLab’s own server is remote at https://gitlab.com/api/v4/mcp (or https://YOUR_GITLAB/api/v4/mcp for self-managed). It is a beta GitLab Duo feature for Premium and Ultimate, according to secondary sources (search extracts of GitLab’s docs, which could not be fetched on 2026-09-26). For other tiers, the community zereight/gitlab-mcp (2.0k stars, npm @zereight/mcp-gitlab 2.1.66 on 2026-09-26) runs locally with a PAT.

Terminal window
# Official, remote with OAuth (the gitlab plugin in claude-plugins-official uses this URL)
claude mcp add --transport http gitlab https://gitlab.com/api/v4/mcp
# Community, local with a PAT exported as GITLAB_PAT in your shell profile.
# Pipeline tools are opt-in: GITLAB_TOOLSETS=pipelines (legacy: USE_PIPELINE=true)
claude mcp add -s project gitlab -e 'GITLAB_PERSONAL_ACCESS_TOKEN=${GITLAB_PAT}' \
-e GITLAB_API_URL=https://gitlab.com/api/v4 -e GITLAB_TOOLSETS=pipelines \
-- npx -y @zereight/mcp-gitlab

Tested on 2.1.283: the single quotes keep the reference unexpanded, and .mcp.json holds "${GITLAB_PAT}", not the token.

GITLAB_API_URL must be the API root (/api/v4), not the web address. For a read-only agent, set GITLAB_PERMISSION_MODE=readonly; modify allows create and update without delete tools. The GitLab PAT scopes follow the same rule as GitHub’s: read_api and read_repository for reading, api only when the agent must write.

What breaks with GitHub MCP, and how do you recover?

Section titled “What breaks with GitHub MCP, and how do you recover?”
SymptomCauseRecovery
Bad credentials or 401Expired, revoked or mistyped token; or the variable is not set in the shell that launched the agentCreate a new PAT, export it, restart the agent from that shell. For fine-grained tokens, check the repository list
Plugin installed, zero toolsThe plugin reads a fixed variable (GITHUB_PERSONAL_ACCESS_TOKEN for Claude Code, GITHUB_PAT_TOKEN for Codex; for Cursor, check the plugin’s mcp.json)Export the exact name the plugin expects
A tool you expect is missingA classic PAT without the scope hides it, or read-only mode or a toolset filter is onCheck x-oauth-scopes, the URL path and the X-MCP-* headers
Tool visible, call returns 403Fine-grained PATs show every tool; the API refuses the callGrant the permission, or drop the toolset so the agent stops trying
search_code finds nothing on your branchOnly the default branch is indexed for code search, and forks only in some casesSearch locally with rg for branch code
403 or 429 rate-limit errors mid-runThe token’s 5,000 requests per hour is shared by gh, scripts and the MCP serverPaginate with small pages, use gh for bulk reads, give automation its own token
claude mcp add github https://… connects to nothingWithout --transport http, Claude Code stores the URL as a stdio commandRemove it and add again with --transport http
Docker OAuth login never completesThe callback port is not published to loopbackAdd -p 127.0.0.1:8085:8085 and GITHUB_OAUTH_CALLBACK_PORT=8085, or use the device-code fallback
A contributor’s issue “does not exist”Lockdown mode hides content from authors without push accessExpected on public repositories. First choice: read the item yourself (gh issue view N). Lockdown cannot be scoped to one repository: a session without --lockdown-mode, GITHUB_LOCKDOWN_MODE or X-MCP-Lockdown stops filtering every public repository in that session. If the agent must read it, start that session read-only (https://api.githubcopilot.com/mcp/readonly, X-MCP-Readonly: true or --read-only locally) with no write tools, then go back to your lockdown config. The Triage role does not help: it has no push access, only Write and above do

For connection problems that are not specific to GitHub, see MCP connection issues.