DX Heroes logo
#ai
#github-copilot
#enterprise
#governance

Customizing GitHub Copilot When MCP Is Off the Table

Length: 

9 min

Published: 

September 14, 2026

Customizing GitHub Copilot When MCP Is Off the Table

Copilot licences land across the whole engineering org, and six months later most people are still using it as autocomplete. That gap is not a licensing problem. It is a configuration problem, and almost everything Copilot needs to close it lives in your repository.

The two earlier parts of this series covered getting started and then refactoring, optimization and test generation. Both stop at the point where an individual developer is productive. This part is about the layer above: the files that turn Copilot from a personal assistant into something a team shares. It comes with one constraint that keeps showing up in enterprise work.

That constraint is MCP (Model Context Protocol). We are currently building a Copilot customization track for a client whose security team plans to disallow MCP servers, so the whole workflow has to hold without them. Almost every guide to enterprise Copilot customization assumes the opposite. So this is the version that does not.

Instruction files: repository standards the model actually reads

Custom instructions are the cheapest thing on this list and the one most teams skip.

.github/copilot-instructions.md applies to every request in the repository. More usefully, .github/instructions/*.instructions.md files can be scoped to paths, so your Terraform conventions do not get loaded when someone is editing a React component. You can compose them: an @relative/path reference pulls in another file rather than duplicating it.

Two details are worth knowing. Copilot also reads AGENTS.md and CLAUDE.md. One set of standards can therefore serve Copilot, Claude Code and Cursor instead of three copies that slowly drift apart. Second, prompt files (.github/prompts/*.prompt.md) work only in the IDE. They do not work on GitHub.com and they do not work in Copilot CLI. That is fine while everyone stays in the editor, and it stops being fine the moment they do not. We come back to it below.

The usual failure mode here is not writing the file. It is letting it rot. We wrote about that in more detail in context engineering: stale instruction files are worse than none, because agents trust the documentation over the codebase.

Skills: a packaged capability instead of prompt folklore

An agent skill is a folder, .github/skills/<name>/SKILL.md, that bundles instructions, scripts and resources. Copilot loads it when it judges the skill relevant. GitHub's own example is an image-conversion skill containing SKILL.md next to a convert-svg-to-png.sh script. On invocation, every file in that folder becomes available alongside the instructions.

That last part is the mechanism the rest of this article leans on: a skill can carry an executable script, and the agent runs it through the ordinary shell tool. Not a network call. Not a server. A file in your repo, reviewed in a pull request like any other code.

Skills are also read from .claude/skills/ and .agents/skills/, so one folder can serve several tools. Same portability story as AGENTS.md.

Consent is explicit. SKILL.md frontmatter carries an allowed-tools field, and anything not listed there needs a permission prompt first. GitHub is blunt about the flip side: pre-approving shell or bash lets "attacker-controlled skills or prompt injections" execute arbitrary commands in your environment. So pre-approve them only once you have read the skill and every script it references. When in doubt, leave them out and take the prompt.

Custom agents: the distribution channel most teams miss

A custom agent is a specialist persona defined in .github/agents/NAME.md. It carries its own instructions and, the part that matters here, its own tool restrictions.

Two things follow from that. The first is distribution: agents do not have to live in one repository. Put /agents/NAME.md in your organization's .github or .github-private repository and the agent is available across the org; at enterprise level, .github-private does the same job. Most teams we talk to have no idea this exists, and it is the difference between one team's clever setup and a company-wide standard.

The second is that tool restrictions turn out to be an operational lever, not a checkbox. Jakub Vacek has been building multi-agent systems on Copilot for real work, and he hit three problems in a row. Questions meant for the human got answered by the calling agent instead of reaching the person, which needed a custom workaround. Users then did not know which agent to call, so he introduced a "router" agent to dispatch work to specialists. And the router promptly became too proactive and started doing the specialists' jobs itself. The fix was to take tools away from it.

The docs present per-agent tool restrictions as a scoping feature. In practice they are how you stop an agent overreaching: a behavioural problem solved by a permissions change.

His router was invoked from a prepared prompt file. That was the right call, because the whole system was built for the editor. Move the same setup to the CLI or the cloud agent, though, and the entry point disappears.

Asked what replaces it, Jakub is unambiguous: a custom agent. An agent is the thing that lets you strip the router's tools, so it routes instead of doing the specialists' work. The front door and the permissions lever are one decision, not two. That is easy to miss while everyone is still in the editor.

Hooks: deterministic policy, no server required

Hooks are the surface almost nobody touches, and for a governance conversation they are the most important thing in this article.

You configure hooks in .github/hooks/*.json as {"version": 1, "hooks": {...}}, with a type, a bash or powershell command, and optional cwd, env and timeoutSec. They fire on sessionStart, userPromptSubmitted, preToolUse and sessionEnd. Multiple hooks on the same event run in order.

preToolUse is the one to care about. It runs before any tool call, whether bash, edit or view, and can allow or deny it. The semantics are fail-closed: exit 2 denies the call, and it denies even when the hook's own stdout says permissionDecision: "allow". Crashes and other non-zero exits deny too. Timeouts, notably, fail open. A slow hook is therefore a real risk, and a timeoutSec you have actually measured is not optional.

Read that together with the skills section and you get a three-layer control story where each layer belongs to a different owner:

| Layer | Mechanism | Owned by | |---|---|---| | Capability | a skill bundles a script; the agent runs it through shell | whoever wrote the skill, reviewed in a PR | | Consent | allowed-tools decides prompt or no prompt | the skill author, visible in frontmatter | | Enforcement | a preToolUse hook, fail-closed | the platform team, not the skill author |

The bottom row is the point. Enforcement is deterministic, auditable, local, and owned by someone other than the person who wrote the capability. It needs no MCP server, no network egress and no vendor allow-list. That is close to a literal description of what a security team asks for the week after it bans MCP.

What "no MCP" actually means

Now the constraint itself, and the first thing to say is that a blanket ban is usually not what happened.

Copilot's admin settings have an MCP servers in Copilot policy, enabled or disabled, at organization or enterprise level. But they also have a second control: Restrict MCP access to registry servers, which switches between Allow all and Registry only with a custom registry URL. Registry only is currently in public preview, which is probably why it so rarely reaches the decision at all. A lot of blanket prohibitions exist because the choice arrived on the security team's desk as on or off, with the middle option still behind a preview flag.

Two limits are worth knowing before you rely on the policy at all. It governs Copilot Business and Enterprise seats only, so Free, Pro, Pro+ and Max are not covered. And it does not control the GitHub MCP server when it is used from a third-party host like Cursor, Claude or Windsurf. If your threat model is "no agent reaches internal systems over MCP," the Copilot policy alone does not get you there.

GitHub's own answer for stronger control is managed-settings.json, generally available since July 2026. You deploy it server-side from .github-private (copilot/managed-settings.json). It can also be pushed through MDM such as Intune, Jamf and Group Policy, or through config management like Chef, Puppet and Ansible. It is enforced in VS Code, Copilot CLI, the Copilot app and the cloud agent. It governs installable plugins and marketplaces, and whether a developer can bypass approval prompts. Managed values take precedence over user configuration, and since August 2026 they can be targeted per enterprise team. If you are having the governance conversation, this is the lever to put on the table. It is a great deal more precise than a ban.

So what do you actually lose? Most of what teams wanted from MCP is reachable through the surfaces above. A skill bundles the script and the agent runs it via shell. A preToolUse hook decides fail-closed whether that is allowed. A custom agent published from .github-private then ships the whole arrangement to every repository in the org.

What does not survive is live, interactive access to a system that has no CLI and no API you can wrap. If the value was "ask about a ticket and get the current state," a script can fetch it. If the value was an open session against a system that only speaks MCP, that is gone, and no amount of instruction files brings it back.

And the honest counterweight: no MCP is not the same as no risk. Pre-approving shell is itself a prompt-injection surface, and GitHub says so in its own documentation. What the ban does is move the risk from a network boundary to a local one. The hook is what makes the local one auditable. If you want the fuller version of that argument, we wrote it up while building MCP governance for enterprise, and it is the core of our AI security and governance workshop.

What to standardize first, if you have one sprint

Jakub's own summary is the right closing thought, and it is not about features. Standing up a relatively complex system on Claude Code or Copilot, he argues, is not the hard part. What increasingly decides the outcome is architecture: which agent owns what, whether an agent is needed at all, what each agent's output should be, and how you check that output efficiently.

He is worth listening to on the comparison too. All three major platforms now ship the standard surfaces, meaning skills, agent definitions and instructions, so those are table stakes rather than a differentiator. What separates them is what they are aiming at:

"Cursor is optimized for quality output without needing customization. It focuses heavily on UX and DX, and it does a very good job of meeting the expectations of developers who are only just trying agentic development."

— Jakub Vacek, Applied AI Architect at DX Heroes (translated from Czech)

We compared the three in production and reached the same place from a different direction.

With that framing, here is the order we would work in:

  1. One instruction file, path-scoped. Standards that are true today, not aspirational. Budget maintenance time from the start.
  2. One skill that replaces a runbook. Pick something a person currently does by following a wiki page. Leave shell out of allowed-tools until someone has reviewed the script.
  3. One preToolUse hook. Even a permissive one, so the seam exists and someone owns it before you need it. Measure the timeout, because timeouts fail open.
  4. One custom agent in .github-private. Whatever you standardized above, distributed org-wide instead of copy-pasted per repo.

Notice how little of that depends on MCP. The hard part is deciding what should be an agent and what should stay a script. That decision is architecture, and it is the one thing you cannot install. If you want to work through it with your own repositories, that is what our agentic development workshop is for.

Want to stay one step ahead?

Don't miss our best insights. No spam, just practical analyses, invitations to exclusive events, and podcast summaries delivered straight to your inbox.