Skip to content

The GitHub MCP Server

Lesson 6 of 10Advanced16 min readGitHub Copilot & AI Engineering · AI AgentsVerified: GitHub MCP server documentation, September 2026

An agent working in a repository sees files. An agent connected to the GitHub MCP server sees issues, pull requests, workflow runs, releases, discussions and code search — the context that actually explains why the code looks the way it does.

That is a large increase in useful context and a large increase in what a session can reach. Both halves matter, and the configuration decisions in this lesson are about getting the first without the second.

Model Context Protocol is a standard for connecting a model to external tools and data. A server exposes a set of tools; a client — an IDE, a CLI, an agent harness — connects and makes them available to the model.

The point of the standard is that the same server works with any compliant client. Configure the GitHub MCP server once and it is available wherever you work.

The GitHub MCP server exposes GitHub itself: repositories, issues, pull requests, Actions, discussions, code search, and more. It runs remotely as a hosted service, or locally as a container you run yourself.

Concretely, with the server connected an agent can:

Read the issue a change relates to, rather than working from a one-line prompt.

Read the discussion on a pull request, including why an earlier approach was rejected.

Search code across repositories, which matters when a change spans a service and its client.

Read workflow run logs, so a failure explanation has the failure in front of it.

Read release history, which is where “when did this behaviour change” is usually answered.

The theme is that most of the context explaining a codebase lives outside the codebase, in the issues, reviews and runs that produced it. MCP is how an agent reaches that, and it is a genuine step-change in output quality for tasks where the history matters.

The most important configuration decision, and it is a single flag.

The server supports a read-only mode — via a /readonly path on the remote endpoint, or an X-MCP-Readonly header — in which only non-mutating tools are exposed. No creating issues, no commenting, no modifying pull requests, no triggering workflows.

Start here. Stay here for most work.

The overwhelming majority of the value is in reading. An agent that can read every issue and pull request in your organisation but cannot write anything is enormously more useful than one that cannot read, and carries a fraction of the risk of one that can write.

Enable write access only for a specific task that needs it, and prefer scoping it to that session rather than leaving it configured permanently. “It might be convenient later” is how a read-only configuration becomes a write-enabled one that nobody remembers deciding on.

Beyond read-only, restrict which toolsets are enabled at all.

The server groups its tools — repositories, issues, pull requests, Actions, code security, discussions, and others. Enabling only what a task needs has two benefits, and the second is the one people miss:

Less reach. Obvious.

Better behaviour. A model choosing among twelve relevant tools chooses better than one choosing among ninety. Tool selection degrades with inventory size, and a narrower server is a more accurate one.

Sensible configurations:

TaskToolsets
Understanding a codebaserepositories, code search
Triaging issuesissues, repositories
Reviewing a pull requestpull requests, repositories
Debugging CIActions, repositories
Security investigationcode security, repositories

Enabling everything because it is easier is the same instinct as shell instead of shell(npm test), and it is wrong for the same reasons.

Enough detail to make scoping decisions without guessing. The exact tool names change; the groupings are stable.

Repositories. Reading file contents, listing directories, branches, tags, commits, and the metadata that describes a repository. The baseline toolset — almost every useful configuration includes it.

Issues. Reading issues and their comments, searching them, and — with write access — creating and commenting. The read half is where most of the “why is this code like this” context lives.

Pull requests. Reading pull requests, their diffs, reviews and review comments. Reading the review discussion on the pull request that introduced a piece of code is frequently more informative than the code.

Actions. Workflow runs, job status, logs. The toolset that makes CI failure analysis possible, and the one to leave out of any configuration that does not need it, because triggering workflows is a write capability with real consequences.

Code search. Searching across repositories the token can reach. Powerful for cross-repository work and the toolset most affected by how broadly the token is scoped.

Code security. Code scanning alerts, secret scanning alerts, Dependabot alerts. Genuinely useful for security investigation work, and a toolset whose results are sensitive — an agent with it enabled can read a list of your unfixed vulnerabilities.

Discussions, projects, notifications, organisation data. Peripheral for most engineering tasks and worth leaving out unless a specific task needs them.

How to choose in practice: start from the task, list what it must read, enable those, and add more only when a session visibly fails for lack of a tool. Building the configuration up from nothing produces a much smaller server than paring one down from everything, because paring down requires noticing that something is unnecessary and nothing ever prompts you to.

The server offers a lockdown mode that filters content from potentially untrusted sources.

Read the documentation’s own characterisation carefully: it is described as a best-effort content filter, not a security boundary. That phrasing is doing real work and it should shape how you use it.

What it is good for: reducing incidental exposure to content designed to influence a model. A useful layer.

What it is not: a guarantee. Do not build an architecture whose safety depends on the filter catching something.

Where the actual boundary is: read-only mode, toolset scoping, and the permissions on the token the server authenticates with. Those are deterministic. A filter is a heuristic, and heuristics belong on top of boundaries rather than instead of them.

The core security consideration, and the reason read-only matters so much.

MCP tools return content other people wrote. An issue body, a pull request description, a comment, a discussion post. On any repository with external contributors, that is text from people you do not know.

An agent reads tool results as context. Text in an issue is in the same window as your instructions. That is how the architecture works, and no amount of prompting reliably separates them.

The consequence: content in a repository can attempt to influence what the agent does next.

The defence is bounded authority. Concretely:

Read-only means the worst case is a bad answer. Text that persuades an agent to do something unhelpful, in a session that can only read, produces an unhelpful reading. That is recoverable.

Scoped toolsets narrow what “something unhelpful” can be. An agent without the Actions toolset cannot be persuaded to trigger a workflow.

Token permissions are the hard limit. The server acts with an identity. That identity’s permissions bound everything regardless of what the model concluded.

Human review remains the gate. Nothing the agent produces reaches a protected branch without a person approving it.

We are describing the shape of the problem rather than providing crafted phrasings. The defensive value is in the architecture, and the architecture works whether or not you can recognise a specific attempt.

What the server acts as determines what it can reach.

Least privilege on the token. A fine-grained token scoped to the repositories the task needs, with the permissions the task needs. Not a classic token with broad scopes because it was quicker.

Repository selection matters as much as permission selection. A token valid for three repositories is a much smaller surface than one valid for the organisation, even at identical permission levels.

Prefer a GitHub App identity for anything shared. An App has its own identity, declared permissions, and audit trail. A personal access token acts as a person, which confuses attribution and stops working when they leave. See GitHub Apps.

Never an organisation-admin token. There is no agent task that requires the ability to change organisation settings, and configuring one creates a risk with no corresponding benefit.

Rotate on the same schedule as everything else. An MCP token is a credential like any other.

An individual configuring a read-only server for themselves is a personal decision. An organisation where fifty engineers each configure their own is a governance question.

Copilot policy settings control MCP availability for organisation members, and administrators can restrict which servers are permitted. Whether an engineer may configure an arbitrary MCP server is a policy decision, not a preference.

The question worth answering explicitly: may people connect third-party MCP servers to sessions that have access to company code? For most organisations the answer should be “only from an approved list”, and the list should be short and reviewed.

Provide a sanctioned configuration. The fastest way to get good posture across a team is to publish a read-only, scoped GitHub server configuration that people can adopt without thinking about it. Left to themselves, people copy whatever they found first, and what they found first has write access and every toolset enabled.

Identity for shared automation should be an App. Fifty personal tokens with organisation scope is a credential-management problem waiting for somebody to leave.

Audit what tokens exist. Fine-grained tokens with organisation resources are visible to owners. Reviewing that list occasionally finds the broad token somebody created for a one-off task in March.

What not to do: ban it outright because the security model is unfamiliar. The read-only configuration is genuinely low-risk and materially improves output quality, and a blanket ban pushes people toward worse-configured alternatives outside your visibility.

Two ways to run it, and the choice is mostly about control.

Remote (hosted). No infrastructure, always current. The right default for most people.

Local (container). You control the version, the network path and the configuration. Worth it when policy requires it, when you need a pinned version, or when you want the network boundary to be yours.

Neither is more secure by construction. The security properties come from read-only mode, toolset scoping and token permissions, which apply identically to both.

The shape is consistent across clients: a server entry naming the endpoint or command, plus authentication.

{
"mcpServers": {
"github-readonly": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/readonly",
"headers": {
"Authorization": "Bearer ${input:github_token}"
}
}
}
}

Three things to note:

The /readonly path is the configuration. Not a runtime decision the model makes.

The token is referenced, not embedded. Never commit a token into a configuration file, and never into a repository. Use your client’s secret input mechanism.

A named server per posture. A github-readonly entry and, if genuinely needed, a separate write-enabled entry used deliberately. Two named servers make the posture visible; one server whose mode you have to remember does not.

Configuration is half of it. The other half is knowing what to ask for.

Ask questions whose answers are in the history. “Why does user-service.ts cache for exactly 60 seconds” is answerable from the pull request that introduced it and unanswerable from the file. That is the class of question MCP makes possible.

Ask for the discussion, not just the change. “Find the pull request that introduced this function and summarise the review comments on it” surfaces the objection somebody raised and the reason it was overridden — which is information that exists nowhere else.

Use it before writing, not after. An agent that reads the related issues before proposing a change proposes better changes. This is the highest-return use and the one people reach for least, because the instinct is to ask for the code first.

Ask about patterns across repositories. “How do our other services handle idempotency keys” is a code search question that a single-repository agent cannot answer at all.

Ask it to cite. “Include the issue or pull request number for each claim” makes the output checkable. An unsourced summary of repository history is exactly the kind of confident narrative that is hard to verify and easy to believe.

Where it disappoints. Questions whose answers are in a chat thread, a document, or somebody’s memory. MCP reaches GitHub. Decisions made in a meeting and never written down remain unreachable, and an agent asked about them will construct a plausible account from what it can see — which is the failure mode the citation instruction is guarding against.

MCP is a standard, so the GitHub server is usually not the only one configured.

The inventory problem compounds. Three servers with twenty tools each is sixty tools, and tool selection accuracy degrades noticeably at that scale. The scoping argument applies to the total, not to each server in isolation.

Enable per task, not permanently. A session investigating a CI failure needs GitHub and nothing else. A session working on a database change needs different tools. Configuring the union of every task you might do produces a worse experience for all of them.

Each server is its own trust decision. A third-party MCP server runs code and returns content that enters the model’s context. It deserves the scrutiny you would give a dependency — who maintains it, what it can reach, what it returns.

Untrusted content compounds too. Content from one server can influence a call to another. An agent with a read-only GitHub server and a write-enabled server elsewhere has a path from repository content to that other system, and the boundary that matters is the weakest one configured.

The practical posture: the fewest servers, in the narrowest mode, for the task in front of you. That is more effort per session than a permanent configuration, and it is the difference between a tool inventory you understand and one you have accumulated.

Three failures account for most of the time lost to this, and they present similarly — the agent behaves as though the server is not there.

The server is not connected. Check the client’s MCP status listing before anything else. In Copilot CLI, /mcp shows configured servers and whether they connected. A server that failed to start looks exactly like a server whose tools the model chose not to use.

The token lacks the permission. A tool call that returns an authorisation error is often reported by the agent as “I could not find that issue”, which sends you looking for the wrong problem. When results are mysteriously empty, check the token’s repository selection and permissions before assuming the data is not there.

The toolset is not enabled. The agent will work around a missing tool rather than announcing its absence — usually by inferring from what it can reach, confidently. This is the most misleading of the three, because the output looks like an answer.

The diagnostic that separates them: ask the agent to list the tools it has available. If the GitHub tools are absent, it is a connection or configuration problem. If they are present and the results are empty, it is a token problem. That distinction takes ten seconds and saves the twenty minutes people otherwise spend rewriting the prompt.

Read-only refusals are not failures. An agent reporting that it cannot create an issue in a read-only configuration is the configuration working. Worth recognising so you do not “fix” it.

Enabling write access by default. Read-only carries most of the value and a fraction of the risk.

Enabling every toolset. More reach, and worse tool selection.

Treating lockdown as a security boundary. The documentation says it is not.

A broad token. Organisation-wide when three repositories would do.

A personal token for a shared integration. Wrong identity, wrong audit trail, breaks when they leave.

Committing the configuration with the token in it. Extremely common; enable push protection.

Assuming local is safer. The security properties come from configuration, not from location.

Forgetting that tool results are untrusted content. Every issue body is text somebody else wrote.

One engineer, one week’s work, and how the posture changes across it.

Monday — understanding an unfamiliar service. Read-only, toolsets: repositories, issues, pull requests, code search. The questions are historical: why does this exist, what did the review discussion say, where else does this pattern appear. No write capability is needed and none is configured.

Tuesday — debugging a CI failure. Read-only, add the Actions toolset. The agent reads the failed run’s logs alongside the diff. Actions is enabled for this and removed afterwards, because a permanently enabled Actions toolset in a write-capable configuration can trigger workflows.

Wednesday — writing the change. MCP largely out of the way. The work is local, in the editor, with the repository’s own files. The context gathered on Monday is in the notes rather than being re-fetched.

Thursday — triaging a backlog. Read-only, issues and repositories. Producing a summary and a suggested grouping that a person then applies. The labels are applied by the person, because a write capability that exists to save thirty seconds of clicking is not worth configuring.

Friday — a genuine write case. Filing twelve follow-up issues from a design review, each with a consistent template. Write access enabled deliberately for one session, scoped to issues only, on a token valid for one repository, and disabled after.

What that pattern demonstrates: the write configuration existed for one afternoon out of five days, and the rest of the week ran on a posture where the worst possible outcome was a wrong answer. That is achievable without friction once the two named server entries exist, and it is what “least privilege” looks like when it is a habit rather than a policy document.

MCP is a set of doors, and the token is what the key opens.

Read-only is a door that only opens outward: things come out, nothing goes in. Toolset scoping is choosing which rooms have doors at all. The token is what the key actually opens, and no configuration on your side of the door changes that.

Content coming through the door is data. Some of it was written by people who do not work for you. The architecture that survives that is one where the worst thing the agent can do with what it read is produce a wrong answer.

  • MCP connects agents to GitHub context that lives outside the repository — issues, reviews, runs, releases
  • Read-only mode via /readonly or X-MCP-Readonly is the single highest-value configuration
  • Toolset scoping reduces reach and improves tool selection accuracy
  • Lockdown is a best-effort content filter by GitHub’s own description, not a boundary
  • Tool results are untrusted content; the defence is bounded authority
  • Token permissions are the hard limit — fine-grained, repository-scoped, App identity for anything shared
  • Never commit MCP configuration containing a token

Use a disposable repository and a fine-grained token scoped to it alone.

  1. Configure the GitHub MCP server in read-only mode. Ask an agent to summarise the open issues.

  2. Ask it to create an issue. Predict: which layer refuses — the model, or the tool list?

  3. Restrict the enabled toolsets to repositories and issues. Ask a question needing Actions data. Predict: what does it do when the tool is absent?

  4. Enable every toolset. Ask a question with several plausible tool routes. Compare the tool choices to the scoped configuration.

  5. Put text in an issue body addressed to an automated reader — do not craft an evasion, just observe. Ask the agent to summarise the issues. Note that in read-only mode the worst outcome is a wrong summary.

  6. Scope the token to a single repository, then ask about a different one. Predict: which layer stops it?

  7. Revoke the token and delete the repository.

AI-assisted engineering learning pathEleven lessons on getting value from Copilot and agents without giving up review discipline.