Using Custom Agents with GitHub Copilot CLI

GitHub Copilot CLIAcademy lesson 61Cluster 5 · Lesson 10 of 12Intermediate → Advanced15 min readVersion-sensitive
Published
Updated
Last technically verified
Using Custom Agents with GitHub Copilot CLIGitHub Copilot CLI10Intermediate → Advanced/github-copilot/cli/custom-agents/

The intuitive reason to build a custom agent is to give it a personality — a security expert, a documentation writer. That is the least important part.

The reason that matters is the tools list. An agent defined without edit cannot modify files. Not “has been asked not to” — cannot. Everything else about agent design is prompt engineering, which is useful; the tool restriction is architecture, which is enforceable.

The built-in agents you already have

Before writing one, know what ships.

Built-in agents
CommandPurposeRisk (editorial guidance, not a GitHub classification)
exploreRead onlyRead-only codebase analysis.Low
taskRuns development commands.Medium
general-purposeFull Copilot capabilities.Medium
code-reviewRead onlyAnalyses changes for substantive issues. Behind /review.Low
researchRead onlyExhaustive investigation. Behind /research.Low
rubber-duckRead onlyAn alternative perspective, using a different model. Behind /rubber-duck.Low

Copilot selects an appropriate built-in agent based on your prompt and context, and several have dedicated slash commands: /review runs code-review, /research runs research, /rubber-duck runs the rubber duck.

The read-only three are the ones to reach for habitually. explore in particular is the correct tool for understanding an unfamiliar repository, because the question “what does this code do” should never carry a risk of changing it.

copilot --agent explore -i "How does authentication work in this project?"

When a custom agent is worth building

Custom agents earn their place in three situations.

A repeated task with a repeated shape. Reviewing pull requests against your team’s conventions, auditing dependencies, checking migrations. Anything you have prompted three times the same way.

A task that should be structurally constrained. A reviewer that cannot edit. An auditor that cannot reach the network. The restriction is the point.

A task needing specific context every time. Your architecture, your conventions, the failure modes you have seen before.

They are not worth building for one-off work, or where the only difference is tone. If the prompt would be the same, a saved prompt is simpler.

Reading the built-in list as a design lesson

The six built-in agents are worth studying as examples rather than only as tools, because their split is instructive.

Three are read-only and three are not, and the division follows the task rather than the difficulty. explore reads a codebase. code-review analyses changes. research investigates. None of them needs to write anything to do its job, so none of them can.

task runs development commands and general-purpose has full capabilities, because those roles are defined by acting.

The lesson is that the tool list should follow from the verb in the agent’s description. If an agent’s job is described with “review”, “analyse”, “explain”, “audit” or “find”, it almost certainly wants a read-only tool list — and writing that list is what turns the description into a property of the system rather than an intention.

The file format

Markdown with YAML frontmatter. The filename determines the invocation name: security-reviewer.agent.md is invoked as security-reviewer.

---
name: Security reviewer
description: Reviews changes for security problems without modifying anything.
tools: ["read", "search"]
---

You review code for security problems. You do not fix them.

## What to look for

- Input reaching a shell, a query, a path or a deserialiser without validation
- Credentials in source, configuration or test fixtures
- Authentication and authorisation checks that are missing or bypassable
- Unsafe defaults: permissive CORS, disabled verification, debug mode
- Dependencies with known problems

## How to report

For each finding: the file and line, what an attacker could do, and how
confident you are. Separate what you verified from what you suspect.

If you find nothing, say so plainly. Do not manufacture findings to
appear thorough.

## What you do not do

You do not modify files. You do not run commands that change state.
You report, and a human decides.

Tool names and aliases go in the list, and tools from MCP servers can be included with server-qualified names — tools: ["read", "search", "some-mcp-server/tool-1"]. An agent file may also declare mcp-servers of its own.

Invoking an agent

Interactively:

/agent

Browse and select, or name it directly with /agent security-reviewer.

Programmatically:

copilot --agent security-reviewer --prompt "Check src/auth/"

The --agent value is the filename without .agent.md. That pairing — --agent with -p/--prompt — is what makes agents useful in automation, since the agent’s tool restrictions travel with it into a non-interactive run.

Repository versus personal agents

Repository agents in .github/agents/ are shared, versioned and reviewed — appropriate for team conventions.

Personal agents in ~/.copilot/agents/ follow you across projects — appropriate for how you like to work.

When both define the same name, the home-directory version wins.

Designing agents that work

Restrict tools first

Decide what the agent must not be able to do, then write the list. This is easier than the reverse and produces better agents.

Example policyRoles, expressed as capability rather than instruction.
Example tool permission policy
PatternDecisionWhy
reviewer — read, searchAllowedReads and reports. Cannot modify, so cannot be talked into it.
reviewer — editDeniedReviewing and fixing are different jobs with different review requirements.
test-writer — read, search, editAllowedNeeds to write test files.
test-writer — shellAsks firstRunning the suite is useful; running arbitrary commands is not required.
auditor — read, searchAllowedNo network: an auditor has no reason to fetch anything.

Written for this lesson to illustrate least privilege. Not a GitHub default and not a security guarantee — read every rule before using it.

Give it one job

An agent that reviews security, writes tests and updates documentation is a general-purpose agent with extra words. The value comes from narrowness — a narrow agent can be given a restrictive tool list, and its output is predictable enough to be useful in automation.

Say what “done” looks like

Agents used repeatedly should produce consistent output. Specify the format: what a finding contains, how confidence is expressed, what to say when there is nothing to report.

That last one matters more than it seems. An agent with no instruction for the empty case will often produce marginal findings rather than reporting nothing, because reporting nothing feels like failing the task.

Separate observation from inference

Copilot prompt

Separate what you verified from what you suspect.

If you read the code and confirmed the behaviour, say so. If you are inferring from a pattern, say that instead.

Never present an inference as a verified finding.

A second example: the migration checker

Security review is the obvious agent. A less obvious and often more useful one encodes institutional knowledge — the specific mistakes your team has actually made.

---
name: Migration checker
description: Reviews database migrations against the rules that have caused
  incidents here before.
tools: ["read", "search"]
---

You review database migrations. You do not write or run them.

## Rules this team has learned the hard way

- A migration that adds a NOT NULL column with no default locks the
  table for the duration of the rewrite. On tables over a million rows,
  that is an outage.
- Dropping a column must come at least one deploy after the code that
  read it stopped shipping. Otherwise a rollback breaks.
- Creating an index on a large table must be CONCURRENTLY, and
  CONCURRENTLY cannot run inside a transaction.
- Renaming is a drop plus an add as far as running code is concerned.
- Every migration needs a down path that has actually been considered,
  not one that was generated and never read.

## How to report

For each migration file: what it does, which of the rules above it
touches, and the expected impact on a table of a million rows.

State plainly if a migration looks safe. Do not invent concerns.

That agent contains nothing a model does not already know about databases. What it contains is which of those facts your team keeps forgetting, in a form that gets applied every time rather than when someone remembers.

Agents versus instructions versus permissions

Three mechanisms with overlapping-looking purposes. The distinction is what happens when the model disagrees.

Instructions are guidance loaded into context. The model usually follows them. It can decline, misread, or lose them to context compaction. Use for “usually do it this way”.

Agent tool lists are structural. An agent without edit has no edit tool to call — there is nothing to decline. Use for “this role cannot do that”.

Permission flags are enforced by the CLI at invocation time, and denial beats everything including --allow-all-tools. Use for “this must never happen in this session”.

They compose usefully. An agent restricted to ["read", "search"], invoked in a session that also denies write and url, run against a repository whose instructions describe your conventions — each layer catching what the others miss.

Subagents and parallelism

Delegating bounded questions

/tasks views and manages tasks including subagents. /fleet enables parallel subagent execution. /subagents configures default and per-agent subagent models.

The real benefit is context isolation. A subagent asked “find every call site of this function” does that work in its own context and returns an answer, rather than filling the main session with file listings. Since context pressure directly degrades behaviour, that is a substantive gain rather than a tidiness one.

A worked example: the security reviewer

Create .github/agents/security-reviewer.agent.md with the content above, then:

Copilot prompt

/agent security-reviewer

Review the changes on this branch against main. Report findings with file, line, impact and confidence. Do not modify anything.

Two things to check the first time.

Did it stay in role? An agent that starts proposing fixes despite a read-only tool list is being blocked by the tool list, which is the system working. If it proposes fixes in prose, that is fine — it is reporting, not acting.

Are the findings real? Read them against the code. The code review lesson covers evaluating findings, and the short version is that plausible and correct are different things.

Agent architecture in practice

The tempting diagram is a hierarchy — a main agent delegating to security, testing, documentation and DevOps specialists, each with its own context.

A specialist set, in the order they are useful

The reason this works is not that each agent is expert. It is that each stage has a different tool list and a different framing, so a mistake made in one stage is being looked at by something that did not make it.

Read-only review after write-capable implementation is the useful pattern here. The reviewer cannot quietly fix what it finds, so findings surface as findings rather than disappearing into a diff — which is what makes them reviewable by you.

Testing an agent

An agent file is configuration that changes behaviour, and like any configuration it can be wrong in ways that are invisible until it matters. Two checks are worth running when you write one.

Check the restriction holds. Ask a read-only agent to make a change. It should be unable to — not decline politely, but lack the tool. If it edits a file, the tools list is wrong or missing.

Check it finds a planted problem. Introduce a flaw of the kind the agent exists to catch — a hard-coded credential for a security reviewer, a non-concurrent index for a migration checker — and confirm it is reported. An agent that reports nothing might be working perfectly or might be misconfigured, and the two are indistinguishable without a positive control.

Copilot prompt

/agent security-reviewer

Review src/config.py. I have deliberately introduced one issue of the kind you exist to find. Tell me what it is.

If you find nothing, say so — I want to know either way.

That second sentence is doing real work. Telling the agent a problem exists risks it inventing one, so the final line gives it explicit permission to report nothing. Running the same check both with and without a planted flaw is the stronger version, and it is worth the extra minute for an agent you intend to rely on in CI.

Where agents disappoint

They do not add knowledge. A “security expert” agent is the same model with different instructions. It does not know more about security; it attends differently.

Narrow roles miss cross-cutting problems. An agent looking for security problems will not tell you the architecture is wrong.

They inherit the model’s blind spots. Which is exactly why rubber-duck using a different model is a genuinely different check rather than a rephrasing.

Over-specification makes them brittle. An agent with forty rules follows the first few and averages the rest.

Agents as a team asset

Once agents live in .github/agents/, they are shared infrastructure and behave like it — which changes how they should be written and maintained.

They go through review. A pull request adding an agent is a pull request granting a set of capabilities, and the tools list is the part that deserves the attention. An agent quietly gaining shell in a later commit is a meaningful change that looks small in a diff.

They accumulate knowledge. The migration checker above is more valuable in its second year than its first, because each incident adds a rule. This makes agents a better home for hard-won operational knowledge than a wiki page, since they are consulted automatically rather than when someone remembers.

They need pruning. An agent encoding a convention the team abandoned is actively harmful, because it argues confidently for something nobody believes any more.

When not to build one

Worth stating plainly, because the failure mode here is enthusiasm.

For a task you do once. A prompt is fine. An agent is a file to maintain.

To make the model better at something. It will not. Role descriptions change attention, not capability.

As a substitute for permissions. If something must not happen in a session, the flag is the mechanism. An agent restricts what that agent can do; it does not constrain the session around it.

Before you have prompted the task a few times. The third repetition is where you learn what the agent should actually say. Writing it earlier means writing a guess.

Skills, plugins and where agents stop

An agent is one of four packaging mechanisms, and picking the wrong one produces something awkward.

Skills are SKILL.md files describing how to perform a particular task, discovered from .github/skills/, .agents/skills/, .claude/skills/, ~/.copilot/skills/ and ~/.agents/skills/. Where an agent is a role with capabilities, a skill is a procedure available to whatever role is active. “How to cut a release in this repository” is a skill; “release manager” is an agent, and the first is usually what you actually wanted.

Plugins bundle skills, agents, hooks, MCP servers and LSP servers into one installable unit, from a marketplace or any GitHub repository. Two marketplaces ship by default: github/copilot-plugins and github/awesome-copilot.

MCP servers give agents tools that reach outside the CLI. An agent’s frontmatter can declare its own mcp-servers, which is how a specialised agent gets specialised capabilities rather than only specialised instructions.

What to carry forward

Write the tools list first. It is the only part that constrains rather than suggests.

Remember the permissive default. No tools key means every tool.

One job per agent. Narrowness is what makes a restrictive tool list possible and what makes the output predictable enough to automate.

Specify the output format, including the empty case.

Prefer read-only agents wherever the task is analysis.

Check for personal agents shadowing team ones when behaviour surprises you.

The through-line of this lesson is a single asymmetry worth keeping. Prose in an agent file describes intent, and a model interprets intent. The tools list describes capability, and capability is not open to interpretation. When those two disagree — and they disagree more often than anyone expects, because the prose is easy to write and the list is easy to forget — the capability is what governs. Writing the list first is the habit that keeps them aligned.

Starting small

If you build one agent, make it a read-only reviewer for the mistakes your team actually repeats. It is the highest-value agent for most teams, it cannot break anything, and writing it forces a useful conversation about what those mistakes are.

If you build a second, make it an explorer scoped to your codebase’s particular shape — the conventions, the layout, the places where things are not where a newcomer would look. That one pays back every time somebody works in an unfamiliar part of the system.

Beyond two, be honest about whether you are solving a problem or collecting configuration.

Next

Code review goes deep on the built-in code-review agent — what it examines and where it stops being sufficient. GitHub Actions is where restricted agents pay off most, because a reviewed tool list in version control is a much better CI control than flags copied between workflows.

For the permission grammar these tool lists interact with, see the commands cheat sheet and the pillar.

Sources

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

Primary sources

Additional references