How to Create a Custom GitHub Copilot Agent
A custom agent is a markdown file that gives Copilot a role and a restricted set of tools. That is the whole idea, and the second half is the part that matters.
Plenty of writing about custom agents treats them as elaborate prompts — a persona, some instructions, a tone. That version is worth very little, because the model is the same model and telling it to be a security expert does not make it one. What a custom agent genuinely provides is a boundary: a named configuration whose tool list decides what it can do at all.
Key takeaways
- A custom agent is a file with YAML frontmatter and markdown instructions.
descriptionis the required field. It is how the right agent gets picked.toolsis the only field that constrains. Omitting it grants access to all available tools.- Six built-in agents already exist — check them before writing one.
- Unrecognised frontmatter keys are ignored silently, so an invented key produces a file that looks configured and is not.
Check the built-ins first
Before writing a file, know what you already have.
exploreRead onlyRead-only codebase analysis.taskCan actRuns development commands.general-purposeCan actFull Copilot capabilities.code-reviewRead onlyAnalyses changes for substantive issues.researchRead onlyExhaustive investigation using GitHub and web sources.rubber-duckRead onlyAn independent critique, using a different model.
Three of these are read-only by construction, which makes them the correct
starting point for anything investigative. explore for understanding a
codebase, code-review for looking at changes, research for questions that
need external sources. Reaching for one of these before writing your own is a
habit worth forming — a surprising proportion of “I need a custom agent” turns
out to be explore with a clearer prompt.
Write your own when you need something the built-ins do not offer: your team’s specific knowledge, a narrower tool list than any built-in provides, or a role you invoke often enough that describing it every time is waste.
The file format
Everything below the frontmatter is instructions — plain markdown, addressed to the model, describing how this agent should work.
Most agents need two or three of those fields. description because it is
required, tools because it is the only one that constrains, and occasionally
name. The rest exist for specific situations worth knowing about:
target limits an agent to vscode or github-copilot. Useful when an
agent only makes sense in one place — an agent that runs your local test suite
has no business being offered to the cloud agent.
model pins a model for this agent. A maintenance obligation, because models
retire, so pin only where a task genuinely needs a particular capability.
disable-model-invocation and user-invocable control who may select the
agent. Between them they cover the cases where an agent should be available to a
person and not chosen automatically, or the reverse.
mcp-servers scopes MCP configuration to this agent, and is the cleanest way
to keep a powerful server out of every other session. Not used in VS Code.
metadata annotates the agent with data of your own. Not used in VS Code or
other IDE custom agents.
---
name: test-writer
description: Writes and extends pytest test suites. Use when adding
coverage to existing Python code.
tools: ["read", "search", "edit", "shell"]
---
You write tests for existing Python code. You do not change the code
under test.
## How this repository tests
- pytest, with fixtures in `tests/conftest.py`.
- One test file per module, mirroring the source layout.
- Prefer parametrised tests over loops inside a test body.
- Never mock what you can construct.
## What to cover
Happy path, boundary values, and the error branches that already have
raise statements. Do not write tests for private helpers.
## Output
Run the tests you write. If a test fails because the code is wrong
rather than the test, say so and stop — do not change the code to make
your test pass.Note how much of that is repository-specific. The value is not “you write tests” — any model does that when asked. The value is the four conventions underneath, which are things a colleague learns in their first month and a model cannot know.
Where the file goes
Where agent profiles are discovered
- Repository
.github/agents/NAME.agent.md - Personal
~/.copilot/agents/ - Organization
/agents/NAME.md in the org's .github or .github-private repository - Enterprise
/agents/NAME.md in the designated .github-private repository
The scope decides who gets it, and the decision is usually obvious once stated.
Repository for anything tied to this codebase — its conventions, its test framework, its deployment story. These belong in version control, reviewed like any other configuration.
Personal for how you work rather than how the team works. Experiments live here until they are worth sharing.
Organization and enterprise for agents that should exist everywhere. These are powerful and easy to over-use; an organisation-wide agent nobody asked for is noise in every repository.
The tools list is the actual configuration
This is the part worth reading twice.
Instructions are advisory. A well-written instruction is usually followed, and “usually” is doing load-bearing work in that sentence. The tools list is not advisory: an agent without an edit tool cannot edit, regardless of what it concludes it ought to do.
Three rules follow.
Write the tools list first. Decide what the agent must not be able to do before you write a word of instruction. Doing it in the other order reliably produces a file whose description and capabilities disagree.
Omit rather than forbid. “Do not run deployment commands” is a sentence. Not having a shell tool is a fact. Prefer the fact.
Start narrow and widen when it fails. An agent that cannot do its job is a five-second fix. An agent that could do something you did not anticipate is an incident.
Choosing a tool set
Which tools an agent needs is a shorter conversation than it looks, because most agents fall into one of four shapes.
Read-only investigator. Read and search, nothing else. Reviewers, explainers, auditors, anything whose output is words rather than changes. This is the shape you should reach for first, and it covers more real use than people expect.
Editor. Read, search and edit. It changes files and you review the diff. No shell, which means it cannot run anything — including your tests, which is a real limitation and sometimes the right trade.
Editor that validates. Read, search, edit and shell. It can run the test suite, which makes it substantially more useful and substantially more capable. This is the point at which “what can the shell reach” becomes a question you have to answer.
Everything. Occasionally correct, usually a sign that the agent has not been scoped. If your agent needs every tool, it probably needs to be two agents.
The step from the second shape to the third is the one worth deliberating. Shell
access turns an agent from something that proposes text into something that acts
on your machine, and where the surface supports finer control — Copilot CLI’s
--allow-tool and --deny-tool patterns, covered in
Cluster 5 — that is where to use it.
Writing the description
description is required, and it does more work than its length suggests: it is
how the correct agent gets selected when several exist.
Write it for the selector, not for a human reader. Say what the agent does and when to use it, in terms that distinguish it from your other agents.
Weak: “Helps with testing.”
Strong: “Writes and extends pytest suites for existing Python code. Use when adding coverage. Not for debugging failing tests.”
The second version contains a negative, which is unusually valuable here. Two agents with overlapping descriptions get picked interchangeably, and the fix is almost always to say what each one is not for.
Writing the instructions
The instructions below the frontmatter are where an agent stops being generic, and there is a reliable test for whether they are pulling their weight: could this text have been written by someone who has never seen your repository? If so, it is decoration.
Four kinds of content are genuinely worth writing down.
Conventions that are not visible from one file. Which layer owns validation, where shared helpers live, what the naming scheme means. A model reading a single file cannot infer the system it belongs to.
Decisions and their reasons. “We moved off the ORM’s lazy loading because it produced N+1 queries in the report endpoints” prevents an agent from helpfully reintroducing it.
Failures you have actually had. This is the highest-value content in any agent file. Real incidents are specific, memorable and impossible for a model to guess.
Explicit non-goals. What this agent should decline to do, and what it should hand back to a human.
Two kinds are usually waste. Generic best practice — “write clean code”, “follow SOLID” — adds tokens without adding information. And restating what the tools list already enforces: if the agent has no shell, telling it not to run commands is redundant, and redundancy in an instruction file trains you to skim it.
What does not exist
Third-party writing about custom agents circulates frontmatter keys that GitHub does not document. Because unrecognised keys are ignored rather than reported, using one produces a file that appears configured and behaves as though the key were absent.
Keys that circulate but are not documented
temperatureNot documentedmax-tokensNot documentedsystem-promptNot documentedpermissionsNot documentedcapabilitiesNot documented
None of these are documented agent profile fields as of 2026-08-25. If you find one recommended somewhere, check GitHub’s configuration reference before building on it — and note that “it did not error” is not evidence that it worked.
A worked example: the reviewer
A concrete agent, built the recommended way round — constraints first.
What must it not do? Change code. A reviewer that edits is not a reviewer.
So the tools list is read-only. Read and search. No edit, no shell.
What does it know that a generic reviewer does not? Your team’s actual recurring mistakes.
What should its output look like? Specific, actionable, and honest about finding nothing.
---
name: reviewer
description: Reviews changed code for correctness and for this team's
specific recurring mistakes. Read-only. Use before opening a pull
request.
tools: ["read", "search"]
---
You review code. You never modify it.
## What this team gets wrong
- Database sessions opened outside a context manager. We have leaked
connections in production twice this way.
- Background tasks that swallow exceptions instead of logging them.
- New endpoints that skip the tenant check in `require_tenant`.
- Timestamps stored without timezone information.
## How to report
For each finding: the file and line, what is wrong, and what to do
instead. Order by severity.
If you find nothing worth reporting, say exactly that. Do not pad the
review with observations to appear thorough.The last instruction is not a stylistic preference. An agent with no explicit empty case will manufacture marginal findings, because producing output feels like succeeding — and a review full of trivia is worse than no review, since it trains people to skim.
When an agent is the wrong container
Three things get built as custom agents that should have been something else, and the misplacement is worth catching early because it is annoying to undo.
A rule that should apply everywhere. If you want the constraint honoured no matter which agent is running — a security rule, a house style, a forbidden pattern — it belongs in custom instructions. Putting it in one agent file means it silently stops applying the moment someone uses a different agent, which is exactly when you would want it most.
A task you run repeatedly. “Generate a release summary from the commits since the last tag” is a request, not a role. That is a prompt file: invoked deliberately, parameterised, no tool boundary needed.
Procedural knowledge several roles need. How to write a migration in this codebase, how the team’s API testing conventions work, how to structure a runbook. Duplicating that into three agent files guarantees three divergent copies. That is what skills are for.
The distinction that resolves most cases: an agent answers who is working and what may they touch. Instructions answer how do we work here. A prompt file answers do this specific thing. A skill answers how is this particular job done. If your file is answering more than one of those questions, it is doing two jobs.
Verify it works
An agent that reports nothing might be working perfectly or might be misconfigured. Without a positive control the two look identical.
Plant a defect of the kind it exists to catch. Introduce a database session outside a context manager. Run the agent. If it does not report it, the agent is not working, and you have learned that before trusting it.
Check the boundary. Ask a read-only agent to make a change. It should decline or fail rather than comply — and if it complies, your tools list is wrong.
Check selection. With several agents available, confirm the one you expect is chosen. If not, the descriptions are not distinct enough.
Keeping agents maintainable
Agents accumulate. Nobody deletes them. A year in, teams routinely have a dozen files where four would serve better.
One job per agent. If the description needs “and”, consider two agents — or consider whether it should be a skill that several agents can use.
Shared rules go in instructions, not in every agent. Anything you would want to apply when a different agent runs belongs in a custom instructions file. Duplicating it into six agent files guarantees they will diverge.
Review tool list changes as permission changes. A pull request adding shell
to an agent’s tools is a capability grant. Read it as one.
Delete what nobody uses. An agent nobody invokes is a file that will eventually be copied by someone who assumes it was maintained.
Sharing agents across a team
The moment a second person writes one, custom agents become shared configuration with the usual problems.
Commit repository agents. They are reviewable, versioned and visible. An agent that exists only on one machine is a source of “it works differently for me”.
Review changes as permission changes. Adding a tool is the diff that matters.
A pull request titled “tweak reviewer wording” that also adds shell should be
read for the second part.
Write a short README beside them. What each agent is for, when to reach for
it, who maintains it. The description field is written for the model; people
need a different document, and ten minutes here prevents most of the confusion
that otherwise accumulates.
Prefer four agents everyone understands to fifteen nobody can distinguish. Overlapping agents produce unpredictable selection, and unpredictable selection looks like the feature being unreliable.
Prune on a schedule. Agents are never deleted spontaneously. A quarterly pass asking “has anyone used this” costs almost nothing and keeps the set legible.
Troubleshooting
The agent is never selected. Its description does not distinguish it from the alternatives, or it does not describe the situation you are actually in. Rewrite the description in terms of when to use it, and add what it is not for.
It does something you told it not to. Check whether the prohibition is only in prose. If the tool exists, the instruction is a request. Remove the tool.
A frontmatter key seems to have no effect. It probably has none. Unrecognised keys are ignored silently, so verify the key against GitHub’s configuration reference rather than inferring from behaviour.
It works for you and not a colleague. The file is at personal scope on your machine. Move it to the repository, and check it is committed rather than merely present in the working tree.
It works in one surface and not another. Custom agent support differs by surface; the matrix in the pillar records the current position, and a file that works in the CLI is not guaranteed to work elsewhere.
Two agents keep fighting over the same requests. Their descriptions overlap. Add an explicit negative to each — what it is not for — which resolves selection far more reliably than making the positive descriptions longer.
Its output changed without the file changing. The model underneath moves. That is an argument for instructions that state hard constraints explicitly rather than relying on the model’s defaults holding steady.
The description is a routing decision
It is worth being explicit about what description does, because treating it as
documentation produces agents that never get chosen.
When several agents exist, something has to decide which one applies. That decision reads the descriptions and nothing else — not the instructions, not the tool list, not the file name. A perfectly written agent with a vague description is an agent that sits unused while a worse one handles its work.
Three properties make a description do its job.
It names the situation, not the capability. “Use when adding coverage to existing Python code” describes a moment you can recognise being in. “Writes tests” describes a capability that several agents share.
It uses the words people actually type. If your team says “spec” rather than “test”, the description should contain both.
It says what it is not for. The negative is the strongest disambiguator available, and it is the one thing a longer positive description cannot substitute for.
A second worked example: the explainer
The reviewer above is the canonical case. A second, less obvious one shows where the pattern generalises.
The problem. New joiners spend their first weeks asking where things are and why the codebase is shaped the way it is. Answering is valuable and repetitive.
What must it not do? Change anything. This is a question-answering role.
So the tools list is read-only. Read and search.
What does it know that a generic assistant does not? The architecture, the history, and the parts that look wrong and are deliberate.
---
name: explainer
description: Explains how this codebase is structured and why. Read-only —
it answers questions and never changes code. Use when you need to
understand something rather than modify it. Not for writing code.
tools: ["read", "search"]
---
You explain this codebase. You never change it.
## Shape of the system
- `src/routes/` handles HTTP only. No business logic, no database.
- `src/services/` owns business logic and is the only layer that
touches the database.
- `src/lib/` is shared utilities with no project-specific knowledge.
## Things that look wrong and are not
- The duplicated validation in `users.py` and `invoices.py` is
deliberate. They diverged in 2025 and merging them reintroduced a
bug twice.
- `legacy/` is still in use by the export job. It is not dead code.
- The retry wrapper looks over-engineered because the upstream
provider returns 200 with an error body.
## How to answer
Point at files and line ranges. Say when you are not sure rather than
constructing a plausible explanation — a confident wrong answer about
architecture will be repeated by whoever you told.The “things that look wrong and are not” section is where the value concentrates. Every codebase has a handful, they are invisible from the code, and explaining them is the conversation senior people have most often.
Common questions
Do custom agents work in every surface? No. Support varies, and the matrix in the pillar records the current position. Copilot CLI’s implementation is covered in Cluster 5.
Can an agent use MCP servers? The mcp-servers field scopes MCP
configuration to an agent, which is a good way to keep a powerful server
available to exactly one narrow role.
Connecting servers covers the
configuration.
What is the difference between an agent and a prompt file? A prompt file is a task you invoke. An agent is a role with a tool boundary. If you want to run the same request repeatedly, that is a prompt file; if you want a constrained way of working, that is an agent.
Where do I put an agent that should exist in every repository? Organisation
scope, in the .github or .github-private repository. Use it sparingly — an
agent that appears everywhere is one everyone has to understand.
Does the agent inherit my custom instructions? Repository instructions apply generally. Think of instructions as the floor and the agent file as the specific role standing on it.
How many agents should a team have? Fewer than you expect. Most teams are well served by a read-only reviewer, one or two role-specific editors, and nothing else. Additional agents earn their place by being reached for, not by seeming plausible when written.
Do agents nest? Delegating to another agent is a subagent question rather than an agent-profile one, and support varies by surface — the pillar covers what is documented where.
Can I pin a model? Yes — the model field. Treat it as a maintenance
obligation rather than a default, because models retire and a pinned agent stops
working when its model does. Cluster 6
covers the trade-off.
Next
Agent skills covers packaging expertise so several agents can share it, which is usually the right answer when an agent file starts growing. Building a skill is the hands-on version. Later in this cluster, three complete agents — DevOps, code review and documentation — apply everything here to real roles.
Sources
Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.
Your progress
Saved in this browser only. No account, no server, and nothing leaves your device. Clearing site data resets it.Sync across devices if you want it everywhere.
Saved in this browser and synced to your account, so it follows you between devices. Manage or delete it.
Was this lesson helpful?
We record which lesson you rated and whether it helped. Nothing identifies you — no account, no cookie, no session.