GitHub Copilot MCP Security: Governing External Tools

Security, Code Review & EnterpriseAcademy lesson 95Cluster 8 · Lesson 7 of 12Advanced15 min readVersion-sensitive
Published
Updated
Last technically verified
GitHub Copilot MCP Security: Governing External ToolsSecurity, Code Review & Enterprise7Advanced/github-copilot/security-enterprise/mcp-security/

Cluster 7 covered MCP as a protocol and how to configure a server. This is the same subject from the position of someone responsible for what an organisation of developers can connect Copilot to.

The governance problem is specific: MCP is the mechanism by which a system that reads text gains the ability to act on systems that matter, and it is configured by whoever is closest to the work.

The trust chain

Every boundary is somewhere risk can enter
  1. CopilotDecides a tool is relevant.
  2. MCP clientThe IDE, the CLI, or the cloud agent.
  3. MCP serverCode someone wrote, running with credentials.
  4. ToolA specific capability the server exposes.
  5. External systemYour issue tracker, cloud account, monitoring, deployments.

Two properties make this different from other integrations.

The invocation decision is probabilistic. Copilot picks a tool by matching your request against tool descriptions. Nothing deterministic decides that a particular tool call was appropriate.

Results become instructions. Whatever a server returns is text the model reads and acts on. If the server can return content other people wrote, that content has reached your agent’s input.

Classify tools, not servers

A server is not safe or unsafe. Its individual tools are, and the useful assessment is a sorting exercise.

Tool riskA representative internal server
The same server's tools, sorted by what they can affect
ToolClassWhat that means
get_issueRead onlyRetrieves information. Nothing changes as a result.
search_ticketsRead onlyReturns text customers wrote. Read-only, and an injection channel.
create_issueMutatingChanges state inside the system it belongs to. Usually reversible.
post_commentMutatingVisible to other people. Not undoable once seen.
restart_serviceExternal effectCauses something to happen outside the system, often visible to other people.
update_iam_policyPrivilegedChanges who can do what. Rarely reversible without an audit trail.

These four classes are this site's editorial framework for reasoning about tool permissions. GitHub does not publish a tool risk classification.

The four classes are this site’s editorial framework rather than a GitHub classification, and they exist because “this server exposes forty tools” tells an approver nothing while “three of these can change production” tells them what to decide.

The controls GitHub documents

The allowlist format

{
  "allowedMcpServers": [
    { "serverUrl": "https://api.githubcopilot.com/*" },
    { "name": "internal-monitoring" }
  ],
  "deniedMcpServers": [
    { "serverCommand": ["npx", "@example/unreviewed-server"] }
  ]
}

Entries match on serverUrl, serverCommand or name, and paths support wildcards.

The precedence rules matter more than the syntax:

How a server is resolved

  • Built-in defaultsAlways allowed.
  • Deny rulesHighestA deny rule from any source blocks the server outright.
  • Allowlist presentAny server not listed is blocked.
  • Every layerA server must match an allowlist entry at every layer that defines one.
  • Unresolved variablesA configuration containing an unresolved variable causes the server to be blocked.

The combination that actually hurts

Most MCP risk assessments evaluate servers one at a time. The failure mode is a pair.

The threat model, concretely

Abstract threat models produce abstract controls. These are the four things that actually go wrong, in the order they are likely to.

Over-broad credentials on a legitimate server. The most common by a distance, and it involves no attacker. A team connects a cloud provider’s own server using an existing admin credential because that is the credential they had. The server is fine, the vendor is fine, and the agent now holds administrative access to an account.

Prompt injection through returned content. A server surfaces text an outsider wrote — a ticket description, a public issue, a monitoring alert containing a user-supplied field. That text reaches the model as input. Whether it does anything depends entirely on what tools are available in the same session.

Tool poisoning. A server describes its tools, and Copilot selects on those descriptions. A server can describe a tool in ways designed to attract calls it should not receive. This is why “who maintains it” is the first approval question and not a formality.

Exfiltration through a mutating tool. A tool that writes somewhere — creates an issue, posts a message, writes a file to a bucket — is a channel out. Combined with repository read access, that is a path from your code to somewhere else, using only capabilities each of which looked reasonable in isolation.

Writing servers for internal systems

Most organisations reach a point where no public server covers the system they actually want to connect, and someone writes one. A few decisions at that point determine how governable it is later.

Start read-only, and stay there longer than feels necessary. A server that answers questions about internal systems — what is deployed, who owns this service, what the schema looks like — delivers most of the value with almost none of the risk.

One tool, one operation. A single execute tool taking free-form instructions is quick to write and impossible to govern. Ten specific tools give an approver ten specific decisions.

Give the server its own identity. Not a person’s credential, and not the broadest role available. A read-only database role is a fundamentally different object from an admin connection regardless of which tools you expose today.

Validate arguments server-side. They arrive from a model. Treat them the way you would treat arguments from a browser — constrain and reject rather than interpret.

Return small results. Everything a tool returns enters the context window. A tool that dumps a hundred rows crowds out the conversation and degrades everything after it.

Log tool calls. An internal server is the one place you fully control, which makes it the natural place to record what was called, by whom, and with what arguments. That record is what an incident review will want and what nothing else in the chain provides.

Approving a server

A process short enough to actually run, in the order that eliminates most candidates fastest.

Who maintains it? A vendor’s own server for their own product, a registry-listed server, and an unfamiliar package are three different levels of trust. GitHub’s MCP Registry narrows the problem without removing it.

What tools does it expose, sorted by class? If the answer is “I would have to read the source”, that is the next step rather than a reason to skip it.

What credentials does it hold? A server acts with its own access. A monitoring server with an admin key is an admin key wired to a system that takes instructions from text.

Does it return outside-controlled content? If yes, apply the combination rule above.

Can it be scoped down? Enumerated tools lists, read-only credentials and narrower API scopes are usually all available. Most servers can do considerably less than their default configuration grants.

Who reviews it next time, and when? Servers gain tools between versions and nothing announces it, so an approval without a re-review date is an approval that silently becomes permanent.

Scoping servers to one agent

The most underused control in this area.

An agent profile’s mcp-servers field scopes MCP configuration to that agent, so a powerful server does not have to be present in every session. The general rule: the more a server can do, the fewer sessions should have it.

A read-only monitoring server can reasonably be available broadly. A server that can restart services should belong to exactly one narrow agent, invoked deliberately, ideally by someone who knows they are doing operations work. Custom agents covers the profile format.

Surface differences that change the design

Security controlsVerified 2026-08-26
  • MCP allowlist in managed-settings.json

    Scope
    Enterprise, distributed via a .github-private repository or MDM
    Requires
    Enterprise-managed clients
    Applies to
    Copilot clients that read enterprise managed settings
    What it does not cover
    A deny rule from any source blocks a server, and a server must match an allowlist entry at every layer. Built-in defaults are always allowed. Unresolved variables cause a server to be blocked.
  • MCP private registry

    Scope
    Enterprise or organization, with Allow all or Registry only
    Requires
    Public preview
    Applies to
    Copilot CLI and IDEs
    What it does not cover
    Does not apply to cloud agents running on GitHub. GitHub documents managed settings as the more secure, generally available method.

Every control here has a documented edge. The limitation line is the part worth designing around.

Three differences decide how conservative a configuration needs to be.

The cloud agent does not ask. GitHub states it uses available MCP tools autonomously without requesting approval. There is no per-call gate, so the configuration is the only control.

Copilot CLI prompts per tool call, with allow and deny patterns. This is the finest-grained control available and it is covered in Cluster 5.

Registries do not reach the cloud agent. Enforcement for that surface comes from the repository-settings configuration and whatever managed settings apply, not from a registry policy.

An example least-privilege policy

Educational rather than prescriptive — the point is the shape.

Initial MCP permissions for the delivery team

Repository read           ALLOW
Issue read                ALLOW
Pull request read         ALLOW

Create issue              DENY
Merge pull request        DENY
Deploy                    DENY
Modify IAM                DENY

Everything that only reads is permitted; everything that acts is denied, and each denial gets revisited when somebody arrives with a concrete need rather than in anticipation of one that may never materialise. Starting here and widening on request produces a much better final configuration than starting broad and narrowing after an incident.

Incident response

MCP incidents look different from ordinary security incidents, mostly because the first question is harder to answer.

Revoke the credential the server holds, not the server. Removing a server from an allowlist stops future sessions using it. It does nothing about the token that server was configured with, which may exist in several places. Rotate first.

Then block the server. A deny rule takes precedence over every allowlist entry, and it is the fastest way to be certain nothing starts it again.

Then work out what was called. This is where most organisations discover a gap. Copilot audit events cover documented agent activity, but the record of which tools a server executed is usually the server’s own logging — which is why the logging recommendation above is not optional for anything internal.

Then check the blast radius through the external system. What the credential could reach, and what that system’s own audit trail shows.

Then review the pairing. If untrusted content and an acting tool were both available, that combination is the finding, and removing one half is the remedy.

A quarterly review that takes an hour

Servers accumulate, credentials outlive their purpose, and nothing prompts a re-look. A short recurring pass prevents most of the drift.

List what is approved and what is actually configured. These diverge. The second list is the real one.

Check for the dangerous pairing across the whole set — not server by server. This is the check that only works when done as a review.

Re-read the tool list of anything that has updated. Servers gain tools between versions.

Confirm credentials are still narrow. A token widened during an incident six months ago is still widened.

Remove what nobody uses. An unused server is an unreviewed attack surface with no compensating benefit.

Confirm the allowlist still matches reality. Particularly after anyone has edited managed settings by hand.

Telling developers what the rules are

An allowlist that arrives without explanation reads as an obstruction, and obstructions get routed around. A short document alongside it changes that.

Four things worth writing down: which servers are approved and why, how to request one, what will be asked when they do, and what the answer usually is. The last is the part people most want and least often get — knowing that read-only internal servers are routinely approved while anything holding cloud write credentials needs a conversation lets someone calibrate before asking.

Local and remote servers

The distinction changes the risk enough to be worth a section.

A local server runs as a process on the developer’s machine, started by the client. It inherits that machine’s access — filesystem, network, credentials in the environment. A local filesystem server can read what the developer can read, which is usually considerably more than the repository.

A remote server runs elsewhere and is reached over HTTP. Its access is whatever its own credentials grant, which makes the risk assessable in a way the local case is not — you can ask what the credential permits.

Three governance consequences.

Local servers are harder to reason about centrally. What they can reach depends on each developer’s machine. An allowlist controls which ones run; it does not constrain what a permitted one can see.

Remote servers concentrate risk in one credential. That is better for assessment and worse for blast radius, which is an argument for narrow credentials rather than against remote servers.

Arguments matter for local servers. A server pointed at a specific directory is a different object from the same server pointed at a home directory, and the difference lives in the args rather than anywhere a policy can see.

Common questions

Should we use the private registry or managed settings? Managed settings, for enforcement. GitHub documents them as the recommended, generally available and more secure method, and the registry is public preview.

Does one allowlist cover every surface? It covers the Copilot clients that read enterprise managed settings, which is not the same as everything. The cloud agent’s MCP configuration lives in repository settings, which is a separate place to check.

Can developers add their own servers? Unless an allowlist is defined, yes — which is the default most organisations should change deliberately rather than discover during a review.

What about a server that was fine when approved and has since changed? Nothing notifies you when a server gains tools between versions. This is the whole argument for periodic re-review, and for pinning versions wherever the transport makes that possible.

Do we need to block MCP entirely to be safe? Blocking it removes a genuine capability and tends to produce workarounds. An allowlist containing the servers you have actually assessed achieves the security goal while leaving the capability available, which is the outcome worth aiming for.

Where does the cloud agent’s MCP configuration live? In repository settings on GitHub, entered by a repository administrator — not in a file and not in the registry. It is a separate place to audit.

How many servers is reasonable? Fewer than teams expect. Beyond a handful, tool descriptions consume real context and selection degrades — a quality argument that happens to align with the security one.

Starting from nothing

For an organisation that has enabled MCP and has no governance around it yet, in order of return.

Find out what is actually configured. Not what was approved — what developers are running. The two lists differ and the second one is the real one.

Write down the approved set. Even if that means blessing what already exists. A named list is the artefact everything else depends on.

Define the allowlist in managed settings. Before anyone needs an exception, because an allowlist assembled under time pressure is a worse allowlist.

Check for the pairing. Across the whole set, once. This is the check most likely to find something.

Narrow the credentials. Most servers hold broader access than their tools require, usually because whoever configured it used a credential they already had.

Publish the criteria. So the next request arrives prepared rather than as a negotiation.

That sequence takes an afternoon and moves an organisation from no position to a defensible one. Everything after it should respond to something specific rather than to a desire for completeness.

Next

Copilot Business turns from individual controls to running the plan that carries most of them — seats, policies, and the operational decisions an organisation makes in its first month.

Sources

Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.

Primary sources