AI-Assisted Dev NotesRohit’s interview study guide
Chapter 02

GitHub Copilot

What Copilot is in October 2026 (completions, Chat, plan and agent mode, the cloud agent, code review, CLI and the Copilot app), how instruction and prompt files steer it, how agents ask for approval and use MCP, what admins control, and how to talk about it in an interview without overclaiming.

6 topics
Parts marked Advanced are extra depth. Skip them on a first read or a quick revision.

#Copilot features at a glance

Added

GitHub Copilot started in 2021 as autocomplete for code. In October 2026 it is a family of tools that differ mainly in how much of the work Copilot does before you look at it. At one end it suggests the next few characters while you type and you accept or ignore each one. At the other end you assign it an issue, it works alone in a cloud environment for up to an hour, and you meet its work as a pull request. In between sit chat, a planning agent and an agent that edits files and runs commands in your editor while you watch.

A good mental model is a delegation dial. Turning it to the right gives Copilot more steps to take on its own, which means it can do bigger jobs, but you see less of the work as it happens and you have to review more of it afterwards. Every feature below is a position on that dial, and choosing the right position for a task is most of the skill. GitHub's own overview (docs.github.com, "About GitHub Copilot", read 27 October 2026) groups the features the same way: assistive (it responds while you work, you apply each suggestion) and agentic (you describe a goal and it works through several steps), plus customisations and third-party agents and MCP servers.

Everything on this page is checked against GitHub's documentation as it read on 27 October 2026 (and VS Code's documentation dated 21 October 2026). Copilot changes monthly: two things you may remember from 2025 have already been renamed. The Copilot coding agent is now the Copilot cloud agent (first announced in GitHub's changelog of 1 April 2026, "Research, plan, and code with Copilot cloud agent": "Copilot cloud agent (formerly known as Copilot coding agent)"; later posts, such as the 23 April 2026 one, refer back to "the Copilot coding agent to Copilot cloud agent rename"), and premium requests were replaced by GitHub AI Credits on 1 June 2026. Copilot can't be run from this study site, so none of the Copilot features here were run; the runnable models on this page compute things the documentation states (prices, rules, precedence) and say so.

IN YOUR EDITOR (VS CODE, JETBRAINS, …) ON GITHUB.COM Completions + next edit it suggests you accept Chat (Ask) + inline chat it answers you ask Plan read-only tools it proposes you approve Agent mode edits + terminal it acts you approve Cloud agent branch → PR it works alone you review Code review PR comments it comments you decide you drive every keystroke more steps without you → more review afterwards Copilot CLI and the Copilot app run the same kind of agent sessions in a terminal or desktop app, locally or in a cloud sandbox
The delegation dial. Positions differ in who takes the next step and where the work runs. Names as in GitHub's docs, 27 October 2026.

The features, one line each

FeatureWhat it doesWhereWho acts
Inline suggestions (code completion)Grey "ghost text" at the cursor, from the code around it and your open filesIDEs (VS Code, Visual Studio, JetBrains, Eclipse, Xcode, Neovim)You accept with Tab
Next edit suggestions (NES)Predicts where your next edit is likely to be and what it is, after you change somethingVS Code, Visual Studio; preview in JetBrains, Eclipse, XcodeYou jump and accept
Copilot Chat (Ask) and inline chatAnswers questions about code, explains, suggests fixes; on GitHub.com it can also create branches and issuesIDEs, GitHub.com, GitHub Mobile, Windows TerminalYou ask, you apply
Plan (plan mode / Plan agent)Researches with read-only tools and drafts a step-by-step plan with open questions; no code changes until you approveVS Code, JetBrains, Xcode, Eclipse, Copilot CLI (not Visual Studio)You approve, then hand off
Agent modeDecides which files to change, edits them, runs terminal commands and tools (including MCP), iterates until doneVS Code, Visual Studio, JetBrains, Xcode, EclipseYou approve commands, review the diff
Copilot cloud agent (was "coding agent")Works in an ephemeral GitHub Actions environment, pushes to a copilot/… branch, opens a draft pull request (or, since April 2026, lets you review and refine the branch before asking for one)GitHub.com (issues, Agents tab, Chat), also started from IDEs, CLI, Slack, Teams, Jira…You review and merge
Copilot code reviewReviews a pull request, leaves comments with severity and suggested changesGitHub.com, GitHub Mobile, GitHub CLI, VS Code, Visual Studio, JetBrains, XcodeYou and a human reviewer decide
Copilot CLIAn agent in your terminal (copilot), interactive or scripted, with a plan mode, MCP and the GitHub MCP server built inLinux, macOS, Windows (PowerShell, WSL)You steer or let it run
GitHub Copilot appA desktop app built on Copilot CLI for running several agent sessions in parallel, each in its own git worktree and branchmacOS, Linux, WindowsYou direct sessions

Sources for the table: docs.github.com "About GitHub Copilot", "GitHub Copilot in IDEs", "GitHub Copilot on GitHub.com", "Using plan mode in your IDE", "Copilot feature matrix" (marked public preview) and "GitHub Copilot app", all read 27 October 2026. Around these sit customisations you'll meet in the next topics: custom instructions, prompt files, agent skills, custom agents, hooks and MCP servers.

Plans and what they include (October 2026)

PlanPriceAI credits per monthNotable limits
Copilot Freefree"an allowance" (no figure given)2,000 completions a month; auto model selection only; no cloud agent; code review only "Review selection" in VS Code
Copilot Studentfree for verified students"an allowance"unlimited completions; auto model selection only; no third-party agents
Copilot Pro$10 a month1,500 (1,000 base + 500 flex)a selection of models
Copilot Pro+$39 a month7,000 (3,900 base + 3,100 flex)premium models
Copilot Max$100 a month20,000 (10,000 base + 10,000 flex)priority access to premium models
Copilot Business$19 per granted seat a month1,900 per user, pooledorganisation policies, content exclusion, org instructions, audit logs
Copilot Enterprise$39 per granted seat a month3,900 per user, pooledeverything in Business, for GitHub Enterprise Cloud

From "Plans for GitHub Copilot" and "Usage-based billing for individuals" (docs.github.com, read 27 October 2026). All plans include Copilot CLI and the Copilot app. Code completions and next edit suggestions are not billed in AI credits and stay unlimited on paid plans; chat, agent mode, the cloud agent, the CLI and code review consume credits. Usage beyond an organisation's pool is charged at $0.01 per credit. Copilot isn't built into GitHub Enterprise Server: licences and policies live on GitHub.com or GHE.com, and a company whose code is on Enterprise Server uses Copilot through local clients such as the IDE extensions and the CLI ("About GitHub Copilot on GitHub Enterprise Server", Enterprise Server 3.22 docs, read 27 October 2026).

The first runnable model turns that billing rule into numbers. Under usage-based billing the cost of an interaction is the tokens it uses times the model's price per token, converted to credits at 1 credit = $0.01. The prices are Claude Sonnet 5.5's row in GitHub's "Models and pricing" table (read 27 October 2026); the token counts are assumptions, stated in the code.

credits.mjs
// What a chat question and an agent session cost in GitHub AI Credits (usage-based billing, from 1 June 2026).
// Prices: Claude Sonnet 5.5 in "Models and pricing for GitHub Copilot" (docs.github.com, read 2026-10-27),
// US dollars per 1 million tokens. 1 AI credit = $0.01. Token counts are assumptions, not measurements.
const price = { input: 2.0, cachedInput: 0.1, cacheWrite: 2.5, output: 10.0 };

function credits(call) {
  const dollars =
    (call.input * price.input +
      call.cachedInput * price.cachedInput +
      call.cacheWrite * price.cacheWrite +
      call.output * price.output) / 1e6;
  return dollars / 0.01;
}

// 1. One chat question: a 6k-token prompt (instructions + open file + question), a 700-token answer.
const chat = credits({ input: 6000, cachedInput: 0, cacheWrite: 0, output: 700 });

// 2. An agent-mode task: 30 model calls in one loop. Each call re-sends the whole conversation so far.
//    Assumption: what the model saw last time is a cache hit; what is new since the last call (its own
//    600-token output plus 2,500 tokens of tool results such as file contents) is written to the cache.
let context = 15000; // system prompt, tools, instructions, the user's request
let agent = 0;
for (let call = 1; call <= 30; call++) {
  const fresh = call === 1 ? context : 2500 + 600;
  agent += credits({ input: 0, cachedInput: context - fresh, cacheWrite: fresh, output: 600 });
  context += 2500 + 600; // tool results and the model's own output become context for the next call
}

const fmt = (n) => n.toFixed(2).padStart(7);
console.log(`${"chat question".padEnd(21)}${fmt(chat)} credits`);
console.log(`${"agent task, 30 calls".padEnd(21)}${fmt(agent)} credits  (context at the end: ${context.toLocaleString("en")} tokens)`);
console.log(`${"agent task / chat".padEnd(21)}${fmt(agent / chat)} x`);
for (const [plan, allowance] of [["Pro", 1500], ["Business", 1900], ["Pro+", 7000]]) {
  console.log(`${plan.padEnd(9)} ${String(allowance).padStart(5)} credits/month = ${Math.floor(allowance / agent)} such agent tasks`);
}
Result
chat question           1.90 credits
agent task, 30 calls   61.16 credits  (context at the end: 108,000 tokens)
agent task / chat      32.19 x
Pro        1500 credits/month = 24 such agent tasks
Business   1900 credits/month = 31 such agent tasks
Pro+       7000 credits/month = 114 such agent tasks
What’s happening
  1. credits() applies the documented formula: each token kind (fresh input, cached input, cache write, output) times its price per million, divided by a million to get dollars, divided by 0.01 to get credits. Sonnet 5.5's output price ($10) is five times its input price ($2), and reading from the cache ($0.10) is a twentieth of fresh input.
  2. The chat question is one model call: 6000 × $2 + 700 × $10 = $0.019, which is 1.90 credits. That's the shape of Ask mode: one prompt, one answer.
  3. The agent task is a loop of 30 model calls, because an agent works by calling the model, running a tool, and calling the model again with the tool's result (the agent loop in Agent mode and the coding agent). Every call re-sends the whole conversation, so the context grows by 3,100 tokens a call, from 15,000 to 108,000 at the end.
  4. Prompt caching keeps that affordable: call 1 writes all 15,000 tokens to the cache (about 4.35 credits including its output), and every later call reads the old context at the cheap cached rate and writes only the new 3,100 tokens. The cost per call creeps up from about 1.5 to 2.4 credits as the cached part grows, and the 600 output tokens are a large share of every call.
  5. The total, 61.16 credits, is about 32 chat questions. A Pro allowance of 1,500 credits covers 24 tasks like that a month; Business seats pool 1,900 credits each across the organisation. Change the model (Haiku 5.5's default tier, up to 100K input tokens, costs a twentieth of Sonnet 5.5 for input) or the number of calls and the answer changes in proportion.
  6. What this demonstrates: under usage-based billing agentic features cost more because they make many model calls, not because they're priced differently. That's why GitHub's docs say agent mode "consumes GitHub AI Credits" on every prompt while completions don't, and why "keep the task small and the context lean" is a cost rule as well as a quality rule.
AdvancedCompletions, NES and the old "premium requests"
Advanced"Copilot" means different things on different surfaces
AdvancedCopilot CLI, the closest thing to Claude Code

Copilot CLI is the surface most like the terminal agents Rohit already uses. Its documented basics ("GitHub Copilot CLI", read 27 October 2026), shown as commands that weren't run here:

Shell
copilot                                                  # interactive session; Shift+Tab cycles into plan mode
copilot -p "Summarise this week's commits" --allow-tool='shell(git)'   # one prompt, then exit
copilot --cloud                                          # the session runs in a cloud sandbox (public preview)
What’s happening
  1. Plain copilot starts an interactive session in the current directory: it can answer questions, edit files and run commands, asking before it uses a tool unless you've allowed it. Plan mode asks clarifying questions and writes a plan before any code.
  2. -p (or --prompt) is the programmatic mode for scripts and CI. Approval options such as --allow-tool='shell(git)' grant just the tools the task needs; the docs warn that --allow-all-tools gives it "the same access as you do to files on your computer".
  3. /sandbox enable inside a session restricts its file system, network and system access locally; --cloud runs the whole session in a GitHub-hosted sandbox whose policies inherit from the cloud agent's, firewall included.
  4. It reads the same repository instruction files (plus ~/.copilot/copilot-instructions.md for personal ones), has the GitHub MCP server built in, and supports local and remote MCP servers. The Copilot app is a desktop front end built on it, for running several sessions in parallel worktrees.
Which position on the dial?

For each task, which Copilot feature would you reach for first, and why? (a) Renaming a prop across 40 React components in a repo you know well. (b) "Why does our device-pairing endpoint return 409 sometimes?" (c) A well-specified issue: add pagination to GET /api/devices, with tests, matching the existing GET /api/calls pattern. (d) Changing how authentication tokens are refreshed on the device's admin web UI.

Show answer

(a) agent mode in your IDE, (b) Chat in Ask mode (or Plan), (c) the cloud agent, (d) do it yourself, with Chat or Plan to help you think, and Copilot code review plus a human reviewer on the result.

What’s happening
  1. (a) is mechanical, multi-file and easy to verify with the type checker and tests, and you want to watch it happen: agent mode, with npx tsc --noEmit and the tests as the agent's validation steps. Completions alone would mean 40 manual edits.
  2. (b) is a question, not a change. Ask mode (or the Plan agent, which only uses read-only tools) can read the handler, the conflict logic and the tests and explain the 409 path. Nothing should be edited until you understand the cause.
  3. (c) is the cloud agent's sweet spot as GitHub describes it ("Best practices for using GitHub Copilot to work on tasks"): a clear problem, acceptance criteria (tests) and a pattern to copy. It works in the background while you do something else, and you meet it as a draft pull request.
  4. (d) touches authentication. GitHub's own guidance lists "tasks involving security, personally identifiable information, authentication repercussions" among those you may want to keep for yourself. The assistant can still help you think it through in Chat, and review the result, but a human owns the design.
  5. The pattern: the less ambiguous and the easier to verify a task is, the further right on the dial you can safely turn it.
AdvancedUsing several positions at once

Where it shows up in practice. Most teams use several positions at once: completions all day, Chat for questions, agent mode for multi-file changes, code review on every pull request, and the cloud agent for a queue of small, well-specified issues. The trade-off on the dial is always the same: more autonomy saves your time while it works and costs your time when you review. Chapter 01's The developer's job with an assistant is the general version of that rule.

#Custom instructions and prompt files

Added

An AI assistant starts every conversation knowing nothing about your project: not your framework versions, not your test command, not that the device API must never block the request thread. You can paste that context into every prompt, or you can write it down once in files Copilot reads automatically. Those files are custom instructions. Prompt files are the other half: reusable prompts for a particular job that you run on demand, like a checklist you hand someone for one task.

The mental model is onboarding a contractor. Repository-wide instructions are the "read this first" page every new contractor gets: what the project is, how to build and test it, the rules that always apply. Path-specific instructions are the team handbooks: the API team's rules apply when you touch the API, the web team's when you touch the web UI. AGENTS.md is the same "read me first" page written for any AI agent, not only Copilot. Prompt files are job sheets: "how we add a new REST endpoint", handed over when you start that job. Nothing in this model makes the contractor obey: GitHub's docs say plainly that "Copilot may not always follow your custom instructions in exactly the same way every time" ("Custom instructions for GitHub Copilot", read 27 October 2026).

Copilot recognises these files (docs.github.com, read 27 October 2026, and VS Code's docs dated 21 October 2026):

FileScopeRead by
.github/copilot-instructions.mdevery request in the repositoryChat (IDEs, GitHub.com), agent mode, cloud agent, code review, CLI
.github/instructions/**/NAME.instructions.md with applyTo globsrequests about files matching the globsVS Code, Visual Studio, JetBrains and Xcode chat; cloud agent; code review on GitHub.com; CLI. Not GitHub.com Chat
AGENTS.md (any folder; nearest one wins), or one root CLAUDE.md or GEMINI.md"agent instructions"cloud agent, code review on GitHub.com, CLI, VS Code chat (AGENTS.md)
.github/prompts/NAME.prompt.mdone interaction, when you run itVS Code (Local harness), Visual Studio, JetBrains, Xcode
Personal instructions (GitHub.com settings, or ~/.copilot/copilot-instructions.md for the CLI)youGitHub.com Chat; CLI
Organisation instructions (org settings; Business and Enterprise only)the organisationGitHub.com Chat, code review and cloud agent only

The files below are for an example repository shaped like the role this site prepares for: a Node and TypeScript REST API for meeting-room devices (server/) and a React admin web UI (web/). They are written to GitHub's documented formats; they were not loaded into Copilot, because Copilot can't be run here. First, the repository-wide file:

.github/copilot-instructions.md
# Device admin: project instructions

This repository holds the admin web UI and REST API for meeting-room devices.
- `server/`: Node 24, TypeScript, Express 5. REST API under `/api/v1`. Talks to devices over their local HTTPS API.
- `web/`: React 19 + TypeScript, Vite, React Router, TanStack Query.
- `docs/api/openapi.yaml` is the API contract. Change it in the same pull request as any endpoint change.

## Build and validate (run these before you say a change is done)
- Install: `npm ci` at the repository root (npm workspaces).
- Type-check: `npm run typecheck`. Lint: `npm run lint`. Tests: `npm test` (Vitest, both workspaces).
- The server tests use a simulated device (`server/test/fake-device.ts`); never call real devices in tests.

## Rules that always apply
- Never log device credentials, admin passwords or session tokens, even at debug level.
- Device calls can take seconds: use the existing `deviceClient` with its timeout; never call `fetch` directly.
- Keep pull requests small. If a task needs more than about 400 changed lines, stop and propose a plan.
- If an instruction here is wrong, say so in the pull request instead of working around it.
What’s happening
  1. The opening lines answer "what is this?" in four lines, with versions. The model's default guesses (an older Express, class components, Jest) are exactly the "hallucinated API" problem from chapter 01, and naming versions is the cheapest fix.
  2. The "Build and validate" section matters most for agents. GitHub's own instruction-generating prompt (in "Adding repository custom instructions") asks for the bootstrap, build, test and lint commands "validated by running" them, because an agent that can't build and test its change produces pull requests that fail CI.
  3. The rules are short, self-contained statements, as both GitHub and VS Code recommend, and two of them carry their reason ("device calls can take seconds"). VS Code's docs (21 October 2026) suggest including the why so the model decides edge cases better.
  4. Every chat message sends this file along, so everything in it costs tokens on every request (see credits.mjs above) and competes for the model's attention. Keep it to what is true for most requests; push file-type rules into path-specific files.
  5. The last rule invites the agent to report a wrong instruction rather than silently follow it. Instructions go stale like any documentation, and a stale build command is worse than none.
  6. What this demonstrates: instructions are context, not configuration. They change what the model is likely to do; they enforce nothing. Lint rules, type checks, tests and branch protection enforce.
AdvancedPath-specific instructions and applyTo globs

Path-specific instructions

Rules that apply only to some files go in .github/instructions/, each with an applyTo frontmatter of comma-separated globs. When Copilot works on a matching file, it uses that file and the repository-wide file. An optional excludeAgent keeps a file away from either "code-review" or "cloud-agent".

.github/instructions/api.instructions.md
---
applyTo: "server/src/**/*.ts"
---
# REST API rules
- Every route validates its input with the zod schema next to it in `server/src/schemas/`.
- Errors use the RFC 9457 problem shape from `server/src/http/problem.ts`; never send stack traces.
- Changing a response shape is a breaking change: add a field, don't rename or remove one, and update `docs/api/openapi.yaml`.
- A route that changes device state (reboot, factory reset, firmware update) requires the `device:admin` scope.
.github/instructions/review-security.instructions.md
---
applyTo: "server/src/routes/**,server/src/auth/**"
excludeAgent: "cloud-agent"
---
When reviewing changes here, check in this order:
1. Is every new route behind `requireAuth` and the right scope?
2. Can user input reach a device command, a file path or a shell without validation?
3. Are timeouts and retries bounded, so one slow device can't exhaust the server?
Report each problem with the line and a concrete fix.
What’s happening
  1. api.instructions.md uses the **/*.ts form, so it matches TypeScript files at any depth under server/src/. A file such as server/test/devices.test.ts is outside that glob and gets only the repository-wide instructions: the next model shows it.
  2. The rules are ones a reviewer would otherwise repeat in every pull request (validate input, one error shape, don't break the contract). Naming the files (problem.ts, the schemas folder) points the model at the existing pattern instead of letting it invent a new one.
  3. review-security.instructions.md has two globs separated by a comma and excludeAgent: "cloud-agent", so the cloud agent never reads it but code review does. It is written as a review checklist, which would be noise for an agent that's writing code.
  4. GitHub's docs give the glob semantics by example: src/*.py matches src/foo.py but not src/foo/bar.py; src/**/*.py matches at any depth; **/subdir/**/*.py matches inside any subdir. The model below checks those examples against a standard glob matcher.
applyto.mjs
// Which path-specific instruction files apply to which files? A model of the documented rule
// (docs.github.com, "Adding repository custom instructions", read 2026-10-27): each
// .github/instructions/**/*.instructions.md file has `applyTo` globs (comma-separated) and an optional
// `excludeAgent` ("code-review" or "cloud-agent"). Node's path.matchesGlob stands in for Copilot's matcher.
import { matchesGlob } from "node:path";

// 1. The glob examples GitHub's docs give, checked against the matcher (true = docs say it matches).
const documented = [
  ["src/*.py", "src/foo.py", true],
  ["src/*.py", "src/foo/bar.py", false],
  ["src/**/*.py", "src/foo/bar/baz.py", true],
  ["**/subdir/**/*.py", "deep/parent/subdir/nested/qux.py", true],
  ["**/subdir/**/*.py", "foo.py", false],
  ["*.py", "src/foo.py", false],
];
const wrong = documented.filter(([g, f, want]) => matchesGlob(f, g) !== want);
console.log(`documented examples: ${documented.length - wrong.length}/${documented.length} agree`);

// 2. Three instruction files from the example repository on this page (frontmatter only).
const instructionFiles = {
  "api.instructions.md": { applyTo: "server/src/**/*.ts" },
  "web.instructions.md": { applyTo: "web/src/**/*.ts,web/src/**/*.tsx" },
  "review-security.instructions.md": { applyTo: "server/src/routes/**,server/src/auth/**", excludeAgent: "cloud-agent" },
};

function applicable(file, agent) {
  return Object.entries(instructionFiles)
    .filter(([, fm]) => fm.excludeAgent !== agent)
    .filter(([, fm]) => fm.applyTo.split(",").some((glob) => matchesGlob(file, glob.trim())))
    .map(([name]) => name);
}

const changed = ["server/src/routes/devices.ts", "web/src/pages/Calls.tsx", "server/test/devices.test.ts", "README.md"];
for (const agent of ["cloud-agent", "code-review"]) {
  console.log(`\n${agent}:`);
  for (const file of changed) {
    const names = applicable(file, agent);
    console.log(`  ${file.padEnd(30)} copilot-instructions.md${names.map((n) => " + " + n).join("")}`);
  }
}
Result
documented examples: 6/6 agree

cloud-agent:
  server/src/routes/devices.ts   copilot-instructions.md + api.instructions.md
  web/src/pages/Calls.tsx        copilot-instructions.md + web.instructions.md
  server/test/devices.test.ts    copilot-instructions.md
  README.md                      copilot-instructions.md

code-review:
  server/src/routes/devices.ts   copilot-instructions.md + api.instructions.md + review-security.instructions.md
  web/src/pages/Calls.tsx        copilot-instructions.md + web.instructions.md
  server/test/devices.test.ts    copilot-instructions.md
  README.md                      copilot-instructions.md
What’s happening
  1. Part 1 runs GitHub's six documented glob examples through Node's path.matchesGlob (stable in Node 26). All six agree, so the model's matcher behaves like the documentation on these cases; it is still a stand-in, since GitHub doesn't publish its matcher.
  2. applicable() applies the two documented rules in order: drop files whose excludeAgent names this agent, then keep files where any comma-separated glob matches. The repository-wide file is printed for every file because it always applies.
  3. server/src/routes/devices.ts gets the API rules from both agents, and code review also gets the security checklist; the cloud agent doesn't, because of excludeAgent: "cloud-agent".
  4. server/test/devices.test.ts gets no API rules: tests live in server/test/, outside server/src/**. If your API rules matter for tests too (they mention the fake device, say), widen the glob to server/**/*.ts or write a separate test instructions file.
  5. What this demonstrates: path-specific instructions are selected by file path, not by topic, so where your code lives decides which rules the agent sees. Checking globs against your real tree, as here, is cheaper than discovering a missed rule in review.
AdvancedAGENTS.md, CLAUDE.md and how instruction files combine

AGENTS.md is a cross-tool format (the agentsmd/agents.md project): Copilot, OpenAI Codex and others read it. GitHub's docs say you can have several AGENTS.md files and "the nearest AGENTS.md file in the directory tree will take precedence", or "a single CLAUDE.md or GEMINI.md file stored in the root of the repository" ("Adding repository custom instructions", read 27 October 2026). That matters if you mostly use Claude Code, as Rohit does: a repository that already has a good CLAUDE.md or AGENTS.md gives the Copilot cloud agent and code review those instructions too, and Copilot's agent skills can live in .claude/skills as well as .github/skills ("Adding agent skills for GitHub Copilot", same date).

AGENTS.md
# Agent instructions (read by Copilot, Claude Code, Codex and other agents)

- Run `npm run typecheck && npm run lint && npm test` before you report a task as done, and paste the summary.
- Never commit secrets, `.env` files or captured device logs (they can contain room names and IPs).
- Don't change `docs/api/openapi.yaml` and the code in separate pull requests.
- Ask before adding a dependency; prefer the standard library and what is already in package.json.
What’s happening
  1. This file holds only rules that are true for every agent and every task, so it can be shared across tools without contradicting .github/copilot-instructions.md.
  2. VS Code's docs (21 October 2026) warn that instruction sources are additive: "Do not depend on a file order or precedence rule to resolve conflicts". Most surfaces simply combine all applicable files. If AGENTS.md said "use Jest" and the Copilot file said "use Vitest", you'd get unpredictable results.
  3. Only Copilot Chat on GitHub.com documents an order: personal instructions first, then path-specific, then repository-wide, then agent instructions, then organisation instructions. Treat it as a tie-breaker, not a design tool.
  4. The first rule asks the agent to show the test summary. That turns "it says it's done" into evidence you can check, which is the habit chapter 01 asks for.
AdvancedPrompt files, and two instruction traps

Prompt files

A prompt file is a Markdown file ending in .prompt.md, stored in .github/prompts/ (or your user profile), that you run as a slash command. In VS Code (docs dated 21 October 2026) the optional frontmatter has description, name (the slash command; defaults to the file name), argument-hint, agent (ask, agent, plan or a custom agent), model and tools; the body can link other files with Markdown links or #file:, refer to tools with #tool:<name>, and use ${input:name} placeholders and ${selection}.

.github/prompts/add-endpoint.prompt.md
---
description: "Add a REST endpoint to the device API, with schema, tests and OpenAPI update"
name: add-endpoint
argument-hint: "METHOD /api/v1/path and what it should return"
agent: agent
tools: ['search/codebase', 'read/readFile', 'edit/editFiles', 'execute/runInTerminal', 'execute/testFailure', 'vscode/askQuestions']
---
Add the endpoint ${input:endpoint:GET /api/v1/devices/{id}/calls} to the device API.

Follow the existing pattern in [the calls route](../../server/src/routes/calls.ts) and its
[tests](../../server/test/calls.test.ts). Before writing code:
1. Find the closest existing route and say which one you are copying.
2. If the request or response shape is unclear, use #tool:vscode/askQuestions to ask me. Don't guess fields.

Then:
- Add a zod schema in `server/src/schemas/`, the route, and tests against the fake device (success, 400, 404, device timeout).
- Update `docs/api/openapi.yaml`.
- Run `npm run typecheck && npm test` and show me the summary. If a test fails, fix the code, not the test.
What’s happening
  1. Running /add-endpoint GET /api/v1/rooms/{id}/devices in VS Code's chat runs this prompt in agent mode. The argument-hint shows in the input box; ${input:endpoint:…} is a placeholder with a default, which VS Code's docs describe as a syntax "most language models understand" rather than a strict template engine.
  2. tools is an allowlist: with it set, the prompt gets only these tools ahead of the custom agent's or the default ones (VS Code's "tool list priority"), and a tool that isn't available is ignored. Leaving out web fetch and MCP tools keeps this job inside the repository.
  3. The Markdown links are relative to the prompt file (../../server/... from .github/prompts/), so the agent reads the real pattern instead of inventing one. That's the single biggest quality lever for "add another one like this" tasks.
  4. "Ask me, don't guess fields" and "fix the code, not the test" are there because guessing and weakening tests are the two failure modes GitHub's "Review AI-generated code" tutorial warns about ("Watch for tests that are deleted or skipped, instead of fixed").
  5. Prompt files run in VS Code's Local harness only. VS Code 2026 can also run Claude, Codex and the Copilot CLI-based harness in the same chat view, and for those it tells you to turn the workflow into an agent skill (SKILL.md) instead.
  6. What this demonstrates: a prompt file is a team's best prompt, versioned in the repository. A new developer runs the same well-tested job sheet as the senior who wrote it.
AdvancedInstructions versus skills, custom agents and hooks

Copilot now has more customisation types than instructions and prompt files. GitHub's "Comparing Copilot customization features" (read 27 October 2026) separates them by what they are for:

TypeTells CopilotExampleFile
Custom instructionshow to behave, always"Use Vitest; run npm test before finishing".github/copilot-instructions.md, *.instructions.md, AGENTS.md
Prompt filewhat to do for one job, when you run it/add-endpoint.github/prompts/*.prompt.md
Agent skillhow to handle a kind of task, loaded when relevant or on demandrelease notes, a code-review checklist.github/skills/<name>/SKILL.md (also .claude/skills, .agents/skills)
Custom agenta specialised role with its own instructions, tools and model"API reviewer" with read-only tools.github/agents/<name>.agent.md
Hooksrun your own command at points in the agent's workblock a tool call, log every commandper surface (cloud agent, CLI, VS Code)
MCP serversnew tools from external systemsan issue tracker, a device labrepository settings, .vscode/mcp.json, .mcp.json

A rule of thumb from the same page: keep instructions minimal, and move anything that applies to only one kind of task into a skill or a custom agent, so it doesn't ride along on every request.

When to use what. Start with a short .github/copilot-instructions.md (or AGENTS.md if the team uses several agents) containing versions, commands and the handful of non-obvious rules. Add path-specific files only when a folder has rules that would be noise elsewhere. Write a prompt file when you notice the same multi-step prompt being retyped. Review these files like code, because they are code that steers code.

#Agent mode and the coding agent

Added

An agent is a language model in a loop with tools. Instead of answering once, it decides on an action (read a file, search, edit, run a command, call an MCP tool), the tool runs, the result goes back into its context, and it decides again, until it judges the task done. GitHub's definition ("About GitHub Copilot agents", read 27 October 2026) is the same: an agent "can plan a task, use tools to take action, inspect the results, and adjust its approach".

Copilot runs that loop in two very different places, and the difference is the whole topic. Agent mode runs in your editor, on your machine, while you watch: it uses your files, your terminal and your MCP servers, and by default it stops to ask before running a command. The cloud agent (called the coding agent until 2026) runs on GitHub, in an ephemeral GitHub Actions environment, while you do something else: it can't touch your machine, it uses its own sandbox, the repository's MCP servers and a firewall, and it doesn't ask you anything until it hands back a draft pull request. A useful mental model: agent mode is pair programming with someone at your keyboard; the cloud agent is a contractor working from a branch, whose work you meet in review.

Agent mode in your IDE Cloud agent on GitHub You: prompt + context Model chooses next action Approval gate ask · auto-approve rule · allow all Tool runs on your machine edit · terminal · MCP Result back into context You review the diff, keep or undo loop until done Issue assigned / prompt copilot-setup-steps job Actions runner, firewall on Agent loop, up to 59 min MCP tools run without asking Push to copilot/… branch draft PR, signed commits Human: Approve and run workflows Human review, then merge the requester can't approve
The same agent loop in two places. Agent mode puts a gate before each risky action; the cloud agent puts the gates around the pull request. From GitHub's and VS Code's docs, October 2026.
Agent mode (IDE)Copilot cloud agent
Runs onyour machine (or a dev container / remote host you chose)an ephemeral GitHub Actions runner (Ubuntu by default; Windows, larger or self-hosted runners possible)
You arepresent, steering, approvingaway; you review a draft PR
Approvalsper tool call or command, unless auto-approved or "Allow all"none during the run; MCP tools in the repository config run autonomously
Can touchyour workspace, your terminal, your credentials, your MCP serversone repository, one branch (copilot/… or the PR's branch), behind a firewall
Starts fromthe chat view (Agent, or Plan first)an assigned issue, the Agents tab, Chat, a PR comment @copilot, the CLI or IDE, Slack, Teams, Jira, Linear, Azure Boards, the GitHub MCP server
Ends withchanged files in your working treea draft pull request (for an assigned issue; from a prompt you can iterate on the branch first); by default GitHub Actions don't run until a human approves
Limitsyour patience and AI credits59 minutes per session; one repository; one PR per task

Sources: "Using agent mode in your IDE", "GitHub Copilot on GitHub.com", "Risks and mitigations for GitHub Copilot cloud agent", "Configure the development environment" (docs.github.com, 27 October 2026) and VS Code's "Manage approvals and permissions" (21 October 2026).

The approval gate in agent mode is the part you configure most. In VS Code the agent runs every shell command through one tool, so "approve the terminal tool" would mean "approve everything". Instead VS Code checks each command against chat.tools.terminal.autoApprove rules: true auto-approves, false always asks, a /regex/ key matches a pattern, and in a compound command every subcommand must match a true rule and none a false one. This model applies those documented rules to a team's rule set:

terminal-approve.mjs
// A model of VS Code's documented terminal auto-approve rules (chat.tools.terminal.autoApprove, VS Code docs
// "Manage approvals and permissions", DateApproved 2026-10-21): rules match each subcommand; a compound command
// is auto-approved only if every subcommand matches a `true` rule and none matches a `false` rule; `false` wins.
// VS Code parses commands with tree-sitter and has built-in rules too; this model splits on && || ; | and
// knows only the rules below.
const rules = {
  "npm test": true,
  "npm run lint": true,
  "/^git (status|diff|log)\\b/": true,
  "/^npx tsc --noEmit$/": true,
  rm: false,
  "/^git push\\b/": false,
};

function matches(rule, cmd) {
  if (rule.startsWith("/") && rule.endsWith("/")) return new RegExp(rule.slice(1, -1)).test(cmd);
  return cmd === rule || cmd.startsWith(rule + " ");
}

function decide(line) {
  const subcommands = line.split(/&&|\|\||;|\|/).map((s) => s.trim()).filter(Boolean);
  const verdicts = subcommands.map((cmd) => {
    const hits = Object.entries(rules).filter(([rule]) => matches(rule, cmd));
    if (hits.some(([, allow]) => allow === false)) return `${cmd} → matches a false rule`;
    if (hits.some(([, allow]) => allow === true)) return null; // approved
    return `${cmd} → no rule matches`;
  });
  const reasons = verdicts.filter(Boolean);
  return reasons.length ? `ask   ${reasons.join("; ")}` : "auto";
}

for (const line of [
  "npm test",
  "git status && git diff --stat",
  "npm test && git push origin main",
  "npm run lint; rm -rf dist",
  "npx tsc --noEmit",
  "npx tsc --noEmit && node scripts/migrate.js",
  "cat package.json | grep version",
]) {
  console.log(`${line.padEnd(44)} ${decide(line)}`);
}
Result
npm test                                     auto
git status && git diff --stat                auto
npm test && git push origin main             ask   git push origin main → matches a false rule
npm run lint; rm -rf dist                    ask   rm -rf dist → matches a false rule
npx tsc --noEmit                             auto
npx tsc --noEmit && node scripts/migrate.js  ask   node scripts/migrate.js → no rule matches
cat package.json | grep version              ask   cat package.json → no rule matches; grep version → no rule matches
What’s happening
  1. The rule set is the kind a team might share: the validation commands the agent runs constantly (npm test, lint, the type check, read-only git) are true, so the loop isn't interrupted every few seconds; rm and git push are false.
  2. decide() splits each line into subcommands and judges each one. git status && git diff --stat is two subcommands, both matched by the read-only git regex, so it runs without asking.
  3. npm test && git push origin main is the case the per-subcommand rule exists for: the first half is approved, but one false subcommand makes the whole line ask. Without that rule, an approved prefix could smuggle anything after it. The same happens to rm -rf dist after a ;.
  4. node scripts/migrate.js matches no rule, so the line asks: approval is required unless something explicitly allows it. A false rule "requires approval. It does not block the command", in the docs' words; the user can still click Allow.
  5. The last line asks only because this model has no built-in rules. VS Code ships defaults under which "common read-only commands run automatically" (such as cat and grep), which you can turn off with the experimental chat.tools.terminal.ignoreDefaultAutoApproveRules.
  6. What this demonstrates: auto-approval is a convenience allowlist, not a security boundary. VS Code's docs say so explicitly ("Terminal auto-approval is a best-effort convenience, not a security boundary": parsers miss subcommands, obfuscated commands can evade matching) and point to sandboxing or a container when prompt injection is a concern.
AdvancedApprovals and sandboxing in VS Code

VS Code (docs dated 21 October 2026) layers these controls for Copilot agent sessions:

ControlWhat it does
Permission level for the session (chat.permissions.default)Manual (default) uses your approval settings and asks for the rest; Assisted (preview) asks an LLM judge to assess each tool call and asks you when it doesn't approve; Allow all runs every tool call without asking
Autopilotan agent mode that auto-approves like Allow all, retries on errors and answers its own blocking questions until it decides the task is complete
Tool approvalper tool or per MCP server: approve once, for the session, the workspace or always; separately, "post-approval" lets a tool's result into the context without review
URL approvaltwo decisions for web fetches: may it contact the URL, and may the response enter the context (prompt injection risk)
Terminal approvalthe per-command rules in the model above
Sandboxing (preview on macOS, Linux, WSL2)restricts terminal commands' file system and network access, and keeps doing so under Allow all and Autopilot

Organisations can set these through enterprise-managed settings, including hiding Allow all and requiring the sandbox. VS Code warns that Allow all and Autopilot "skip confirmation for potentially destructive actions"; use them only in a workspace you trust, ideally in a container.

AdvancedThe cloud agent's guardrails

GitHub's "Risks and mitigations for GitHub Copilot cloud agent" (read 27 October 2026) lists what's built in, and it is worth knowing well enough to explain:

  • Only people with write access can trigger it, and comments from people without write access are never shown to it.
  • It pushes to one branch: a new copilot/… branch, or the existing pull request's branch when you mention @copilot on it. Branch protection and required checks still apply, and it can't run git push itself.
  • It opens a draft pull request and can't mark it ready, approve it or merge it. The person who asked for it can't approve it either, and when the PR is attributed only to Copilot's app identity one extra approval is required, as long as the repository already requires at least one (on by default in rulesets, always for branch protection rules).
  • By default, GitHub Actions workflows don't run on its pushes until someone with write access clicks "Approve and run workflows", because workflows can hold secrets (an admin can configure them to run automatically, which removes this gate); look especially at any change under .github/workflows/.
  • Its internet access goes through a firewall you can customise per organisation and repository, with a recommended allowlist for package registries. Know its limits: "Customizing or disabling the firewall" says it applies only to processes the agent starts with its Bash tool, not to MCP server processes or to your setup steps, and that sophisticated attacks may bypass it. An MCP server you add can reach whatever its own code reaches.
  • Hidden text is filtered: for example, an HTML comment in an issue isn't passed to the agent (a defence against prompt injection; chapter 06 covers the attack).
  • It checks its own work: CodeQL, the GitHub Advisory Database for new dependencies, secret scanning, and a Copilot code review pass, before it finishes.
  • It's auditable: commits are authored by Copilot with the requester as co-author, signed, and linked to the session log.

Practical limits from "GitHub Copilot on GitHub.com": one repository per task, one branch and one pull request per task, and a hard 59-minute session limit. When you assign an issue, it receives the title, description, existing comments and your optional prompt; later comments on the issue aren't seen, so put corrections on the pull request, batched in one review so it works on them together.

AdvancedConfiguring the cloud agent's environment

The cloud agent figures out how to install dependencies by trial and error unless you tell it. A workflow file named .github/workflows/copilot-setup-steps.yml with a single job called copilot-setup-steps runs before the agent starts. Only steps, permissions, runs-on, services, snapshot and timeout-minutes (at most 59) are honoured; other job settings are ignored, and the file is used only from the default branch ("Configure the development environment", read 27 October 2026). The action versions follow GitHub's own example on that page (actions/checkout@v6, actions/setup-node@v7); the docs don't require a particular version, and the latest releases on 27 October 2026 were checkout v7.0.1 (20 July 2026) and setup-node v7.1.0 (8 October 2026), so actions/checkout@v7 should also work but wasn't tried. This one was checked with actionlint, not run on GitHub:

.github/workflows/copilot-setup-steps.yml
name: "Copilot Setup Steps"

# Runs automatically when this file changes (so you see it work), and manually from the Actions tab.
on:
  workflow_dispatch:
  pull_request:
    paths:
      - .github/workflows/copilot-setup-steps.yml

jobs:
  # The job MUST be called copilot-setup-steps or the agent won't use it.
  copilot-setup-steps:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: read # the agent gets its own token for its work; keep this one minimal
    steps:
      - name: Check out the repository
        uses: actions/checkout@v6
      - name: Set up Node.js
        uses: actions/setup-node@v7
        with:
          node-version: "24"
          cache: "npm"
      - name: Install dependencies
        run: npm ci
      - name: Build the fake device used by the tests
        run: npm run build --workspace server/test/fake-device
What’s happening
  1. The on: triggers aren't how the agent runs it (the agent runs the job itself); they make GitHub run the file as a normal workflow when it changes, so you can see it pass before relying on it.
  2. The job name is the contract: GitHub looks for exactly copilot-setup-steps.
  3. permissions: contents: read is the least the checkout needs. The docs' own comment: "Copilot will be given its own token for its operations", so don't hand this job more.
  4. Installing dependencies here is deterministic, where the agent's trial and error isn't, and it's the only way to give it private packages. If a step fails, the docs say Copilot skips the remaining steps and starts with whatever state it has, so watch the session log.
  5. Copilot code review reuses this file for its own environment unless you add .github/workflows/copilot-code-review.yml.
AdvancedCustom agents: a role with its own tools

A custom agent is a Markdown "agent profile" in .github/agents/NAME.agent.md: YAML frontmatter (description is required; name, tools, model, target, disable-model-invocation, user-invocable and, for the cloud agent only, mcp-servers are optional) and a prompt of up to 30,000 characters ("Custom agents configuration", read 27 October 2026). You pick it when assigning an issue or in the agents dropdown in the IDE, and the cloud agent can also choose it by itself from its description unless you turn that off. This profile is written to that format and wasn't loaded into Copilot:

.github/agents/api-contract-checker.agent.md
---
name: api-contract-checker
description: Checks a change to the device REST API against docs/api/openapi.yaml and lists breaking changes. Read-only; never edits code.
tools: ["read", "search", "github/*"]
disable-model-invocation: true
---
You check REST API changes for contract problems. For the changed routes under server/src/routes/:
1. Compare each request and response shape with docs/api/openapi.yaml.
2. List every breaking change (removed or renamed field, new required parameter, changed status code).
3. List routes that change device state but don't require the device:admin scope.
Write the result as a checklist with file and line. Don't propose code; a person decides the fix.
What’s happening
  1. tools uses GitHub's documented aliases: read and search (file reading and searching) and github/*, all tools of the built-in GitHub MCP server, which for the cloud agent are read-only and scoped to the repository. Leaving out edit and execute makes this agent unable to change code or run commands, whatever its prompt says.
  2. disable-model-invocation: true means the cloud agent won't pick this agent on its own from the task's wording; someone has to choose it. That suits an agent with a narrow job.
  3. The prompt is a procedure with an output format, like the review instructions earlier. Unrecognised tool names are ignored rather than rejected, so check the session log to confirm which tools it actually had.
  4. When to use one instead of instructions: when a recurring job needs a different tool set or a different set of rules from everyday work (a contract checker, a test writer, a docs writer), so those rules don't ride along on every request.
AdvancedHow agents use MCP: IDE and cloud

How agents use MCP

Both agents get most of their reach from tools, and MCP (the Model Context Protocol, taught from zero in chapter 04) is how new tools are added. In the IDE you configure MCP servers per workspace or user, and agent mode asks before calling their tools unless you've approved them. In VS Code that's .vscode/mcp.json (top-level servers) or the portable .mcp.json (top-level mcpServers) that Copilot CLI-based sessions read; chapter 05's Connecting a server to a host covers those files and what happens at startup. This one, for the device tools used later in this course, wasn't loaded into VS Code:

.vscode/mcp.json
{
  "inputs": [
    { "type": "promptString", "id": "device-lab-token", "description": "Device lab API token", "password": true }
  ],
  "servers": {
    "devices": {
      "type": "stdio",
      "command": "node",
      "args": ["${workspaceFolder}/tools/device-mcp/server.js"],
      "env": { "DEVICE_API_TOKEN": "${input:device-lab-token}" }
    }
  }
}
What’s happening
  1. servers.devices describes a local server that VS Code starts as a child process and talks to over stdio. The key devices becomes the server's name in tool pickers and in prompt files (devices/*).
  2. The token is an inputs entry of type promptString with password: true: VS Code asks for it the first time the server starts and stores it securely, so it never sits in the committed file.
  3. VS Code's docs (21 October 2026) note that servers needing ${input:…} values aren't forwarded to Copilot harness sessions; for those, the portable .mcp.json with ${NAME} environment-variable references is the route. Prompted secrets are a Local-harness convenience.

The cloud agent gets MCP servers from the repository's settings (Settings → Copilot → MCP servers), as JSON with a top-level mcpServers object. The GitHub MCP server (read-only, scoped to the repository) and the Playwright MCP server (localhost only) are on by default. The rules that matter, from "Configure MCP servers for your repository" (read 27 October 2026): every server needs tools (a list, or "*") and a type of local, stdio, http or sse; local servers need command and args, remote ones url; secrets come only from the repository's Agents secrets whose names start with COPILOT_MCP_, referenced as $VAR, ${VAR} or ${VAR:-default}; the agent calls these tools without asking; only tools are supported (not resources or prompts); and remote servers that need OAuth aren't supported. This model checks a configuration against those rules:

mcp-config-check.mjs
// Checks a repository MCP configuration for Copilot cloud agent against the rules in GitHub's docs
// ("Configure MCP servers for your repository", read 2026-10-27), then resolves its secret references.
// This is a model of the documented rules, not GitHub's validator.
const config = {
  mcpServers: {
    devices: {
      type: "local",
      command: "node",
      args: ["tools/device-mcp/server.js", "--base-url=${COPILOT_MCP_DEVICE_URL:-http://127.0.0.1:8080}"],
      env: { DEVICE_API_TOKEN: "$COPILOT_MCP_DEVICE_TOKEN" },
      tools: ["get_device_status", "list_call_logs"],
    },
    tracker: {
      type: "http",
      url: "https://tracker.example.com/mcp",
      headers: { Authorization: "Bearer $TRACKER_TOKEN" },
      tools: ["*"],
    },
    wiki: { type: "remote", url: "https://wiki.example.com/mcp" },
  },
};

const REF = /\$\{([A-Z0-9_]+)(?::-([^}]*))?\}|\$([A-Z0-9_]+)/g;
const problems = [];
for (const [name, s] of Object.entries(config.mcpServers)) {
  if (!Array.isArray(s.tools)) problems.push(`${name}: "tools" is required (list tools, or "*" for all)`);
  else if (s.tools.includes("*")) problems.push(`warning ${name}: tools ["*"] lets the agent call every tool without asking`);
  if (!["local", "stdio", "http", "sse"].includes(s.type)) problems.push(`${name}: type "${s.type}" is not local, stdio, http or sse`);
  if (["local", "stdio"].includes(s.type) && !(s.command && Array.isArray(s.args))) problems.push(`${name}: local servers need command and args`);
  if (["http", "sse"].includes(s.type) && !s.url) problems.push(`${name}: remote servers need url`);
  for (const [key, value] of Object.entries({ ...s.env, ...s.headers })) {
    for (const m of value.matchAll(REF)) {
      const ref = m[1] ?? m[3];
      if (!ref.startsWith("COPILOT_MCP_")) problems.push(`${name}: ${key} refers to $${ref}; only COPILOT_MCP_* secrets are passed in`);
    }
  }
}
console.log(problems.map((p) => (p.startsWith("warning ") ? "⚠ " + p.slice(8) : "✗ " + p)).join("\n") || "✓ no problems");

// Substitution as documented: $VAR, ${VAR} and ${VAR:-default}, from the repository's Agents secrets/variables.
const secrets = { COPILOT_MCP_DEVICE_TOKEN: "s3cr3t", COPILOT_MCP_DEVICE_URL: "https://lab-devices.internal" };
const resolve = (v, vars) => v.replace(REF, (_, braced, dflt, bare) => vars[braced ?? bare] ?? dflt ?? "");
const env = Object.fromEntries(Object.entries(config.mcpServers.devices.env).map(([k, v]) => [k, resolve(v, secrets)]));
const arg = config.mcpServers.devices.args[1];
console.log("\ndevices env      :", JSON.stringify({ DEVICE_API_TOKEN: env.DEVICE_API_TOKEN.replace(/./g, "*") }));
console.log("devices arg      :", resolve(arg, secrets));
console.log("arg, URL not set :", resolve(arg, { COPILOT_MCP_DEVICE_TOKEN: "s3cr3t" }));
Result
⚠ tracker: tools ["*"] lets the agent call every tool without asking
✗ tracker: Authorization refers to $TRACKER_TOKEN; only COPILOT_MCP_* secrets are passed in
✗ wiki: "tools" is required (list tools, or "*" for all)
✗ wiki: type "remote" is not local, stdio, http or sse

devices env      : {"DEVICE_API_TOKEN":"******"}
devices arg      : --base-url=https://lab-devices.internal
arg, URL not set : --base-url=http://127.0.0.1:8080
What’s happening
  1. devices passes: a local server with command and args, an explicit allowlist of two read-only tools, and its token taken from the Agents secret COPILOT_MCP_DEVICE_TOKEN. That is the shape GitHub recommends: "allowlist specific read-only tools, since the agent will be able to use these tools autonomously".
  2. tracker gets a warning for ["*"] (allowed, but every tool the server ever adds becomes callable without a human) and an error for $TRACKER_TOKEN: a secret not named COPILOT_MCP_… isn't passed to the configuration, so the header would be sent without a real token.
  3. wiki fails twice: no tools, and "remote" isn't one of the four documented types (remote servers use http or sse).
  4. The substitution part resolves ${COPILOT_MCP_DEVICE_URL:-http://127.0.0.1:8080}: with the secret set it becomes the lab URL; without it the :- default applies. The token is masked when printed, which is the habit to keep in any tool that touches secrets.
  5. What this demonstrates: for the cloud agent, the MCP configuration is the permission model, because nobody approves calls at run time. The tools list decides what it can do, and the secrets decide what it can reach. Chapter 06 covers least privilege, confirmations and sandboxing in depth.
Why did the agent ignore the new requirement?

You assigned an issue to Copilot at 10:00. At 10:05 a product manager commented on the issue: "Also support filtering by room." At 10:40 the draft pull request arrives without room filtering, and its Actions checks show as waiting. What happened, and what do you do?

Show answer

Both are documented behaviour: the agent only saw the issue as it was at assignment, and workflows on its pushes wait for a human.

What’s happening
  1. When you assign an issue, Copilot "gets sent the issue title, description, any comments that currently exist, and any additional instructions you provide", and afterwards "will not be aware of, and therefore won't react to, any further comments" on the issue (GitHub, "Using Copilot cloud agent on GitHub").
  2. So the 10:05 comment never reached it. The fix is to put the requirement on the pull request: start a review, write the comment mentioning @copilot, and submit the review so it works on all comments together. Copilot pushes new commits to the same branch.
  3. The waiting checks are the "Approve and run workflows" guard: by default GitHub Actions don't run on the agent's pushes until someone with write access approves, because workflows can expose secrets.
  4. Before approving, look at the diff, especially anything under .github/workflows/. Then approve, let CI run, and review the result as you would a colleague's.
  5. The lesson for issue-writing: treat the issue as the prompt. Settle requirements before assigning, or assign later.
AdvancedChoosing agent mode or the cloud agent

When to use which. Agent mode for multi-file changes you want to watch, especially when the right answer needs your judgement mid-way. Plan first when the task is big or vague. The cloud agent for a backlog of small, clear issues (bugs with a reproduction, test coverage, docs, dependency bumps) where review is cheap. Neither for production incidents or security-critical design, where GitHub's own guidance says to work yourself.

#Copilot code review

Added

Copilot code review is an automated reviewer you can add to a pull request like a teammate: it reads the change, gathers context from the repository, and leaves review comments, each labelled High, Medium or Low severity, many with a suggested change you can commit in a click. It runs on GitHub.com (and Mobile, the GitHub CLI, VS Code, Visual Studio, JetBrains and Xcode), and it can be set to review every pull request automatically.

The mental model: a fast, tireless first-pass reviewer who never gets to approve by default. It catches the things a careful reviewer catches on a first read (a missing null check, an unvalidated input, a confusing name), it is never too busy, and it applies your written review rules every time. But it isn't accountable, it can be wrong, and by default it leaves a "Comment" review, not "Approve" or "Request changes", so it doesn't count toward required approvals. GitHub's own words: "Copilot is not guaranteed to spot all problems or issues in a pull request. Sometimes it will make mistakes" ("GitHub Copilot code review", read 27 October 2026).

What you need to know about it, from that page and "Using GitHub Copilot code review" (both read 27 October 2026):

  • Requesting: add Copilot under Reviewers (or request copilot-pull-request-reviewer[bot] through the REST API). A review "usually takes less than 30 seconds".
  • Automatic reviews: users on paid plans can have their own PRs reviewed automatically; repository and organisation owners do it with rulesets. Options: review new pushes, review drafts. Without "review new pushes" it reviews a PR once; re-request with the button next to its name.
  • Agentic context: it runs in GitHub Actions to gather "full project context", and can hand suggestions to the cloud agent ("Fix with Copilot" opens a new PR or a commit on yours).
  • Effort levels: Lite (fast feedback on common issues) and Balanced (a higher-reasoning model for complex logic, security-sensitive and cross-service changes; GitHub's default). GitHub estimates a review at $0.05–$1 of AI credits on Lite and $0.25–$5 on Balanced, plus Actions minutes. You can't choose the model.
  • Customisation: repository-wide and path-specific instructions, AGENTS.md, CLAUDE.md, GEMINI.md and REVIEW.md, review-focused agent skills (for example .github/skills/code-review/), and the repository's MCP servers, all read from the PR's head branch.
  • What it skips: dependency manifests and lock files such as package.json and Gemfile.lock, log files and SVGs; files under content exclusion.
  • Replies: you can react to, resolve and reply to its comments, but "they won't be visible to Copilot, and Copilot won't reply". On a re-review it may repeat comments you resolved.
  • Approvals (public preview, off by default): when enabled at enterprise, organisation and repository level, Copilot can submit an approving review that counts toward required approvals; new commits dismiss it.
  • Without a licence: Business and Enterprise organisations can let members without a Copilot licence use it on GitHub.com, billed to the organisation.

When several settings could set the effort level, GitHub documents an order and uses the first one that is set. This model applies it:

review-effort.mjs
// Which review effort does Copilot code review use? The documented order (docs.github.com, "GitHub Copilot
// code review", read 2026-10-27): the first of these that is set wins, ending in GitHub's default, Balanced.
const order = [
  ["chosen when the review is requested", (r) => r.requested],
  ["previously used on this pull request", (r) => r.previousOnPr],
  ["the requestor's own setting", (r) => r.requestor],
  ["the repository's setting", (r) => r.repository],
  ["the organization's setting", (r) => r.organization],
  ["GitHub's built-in default", () => "Balanced"],
];

function effort(review) {
  for (const [source, pick] of order) {
    const level = pick(review);
    if (level) return `${level.padEnd(8)} (${source})`;
  }
}

const org = { organization: "Lite" }; // the org keeps routine reviews cheap
const cases = [
  ["new PR, nobody set anything", {}],
  ["new PR in the org", { ...org }],
  ["firmware-api repo overrides to Balanced", { ...org, repository: "Balanced" }],
  ["author prefers Lite in that repo", { ...org, repository: "Balanced", requestor: "Lite" }],
  ["second push to the same PR", { ...org, repository: "Balanced", previousOnPr: "Lite" }],
  ["reviewer picks Balanced on the PR", { ...org, previousOnPr: "Lite", requested: "Balanced" }],
];
for (const [label, review] of cases) console.log(`${label.padEnd(40)} → ${effort(review)}`);
Result
new PR, nobody set anything              → Balanced (GitHub's built-in default)
new PR in the org                        → Lite     (the organization's setting)
firmware-api repo overrides to Balanced  → Balanced (the repository's setting)
author prefers Lite in that repo         → Lite     (the requestor's own setting)
second push to the same PR               → Lite     (previously used on this pull request)
reviewer picks Balanced on the PR        → Balanced (chosen when the review is requested)
What’s happening
  1. order is GitHub's list, most specific first: an explicit choice on this request, the level used before on this PR, the requestor's own default, the repository's, the organisation's, and finally Balanced. effort() returns the first one that's set.
  2. With nothing configured the review is Balanced, the more expensive level. An organisation that wants routine reviews cheap sets Lite (second line), and a security-sensitive repository such as firmware-api overrides back to Balanced (third line).
  3. The fourth line is the surprise: the requestor's personal setting beats the repository's. A developer who set Lite for themselves gets Lite even in the repository that asked for Balanced. For a new PR the requestor is the author; when someone marks a draft ready, it's that person.
  4. Once a PR has been reviewed, its previous level sticks (fifth line), so a repository change of policy doesn't affect PRs already in flight. Only an explicit choice on the request (last line) overrides it.
  5. What this demonstrates: if a repository must get deep reviews, a repository default isn't a guarantee. Say in the team's guidelines that sensitive changes need Balanced chosen on the request, and keep a human reviewer as the real gate.
AdvancedReview instructions that work

Copilot code review reads the same instruction files as the agents. GitHub's example puts review rules in .github/copilot-instructions.md as plain sentences ("When performing a code review, apply the checks in the /security/security-checklist.md file"). For anything longer, a path-specific file with excludeAgent: "cloud-agent" keeps review checklists out of the agent's context, like review-security.instructions.md in Custom instructions and prompt files. Good review instructions are checks with a reason and an expected output ("Is every new route behind requireAuth? Report the line and a fix"), not adjectives ("be thorough").

AdvancedWhat code review can see through MCP

The repository's MCP servers are shared by the cloud agent and code review, but code review is stricter: "each tool in the MCP server's tools/list response must set annotations.readOnlyHint to true" or it's excluded ("Configure MCP servers for your repository", read 27 October 2026). This model runs a real MCP server with the official TypeScript SDK and applies both documented filters to what the server lists:

readonly-tools.mjs
// What Copilot code review and Copilot cloud agent would get from one MCP server, using the real SDK
// (@modelcontextprotocol/sdk 1.32.2) over an in-memory transport. The filters are GitHub's documented rules
// (docs.github.com, read 2026-10-27): the cloud agent gets the tools listed in the repository config's
// `tools`; code review also drops any tool whose annotations.readOnlyHint isn't true.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { z } from "zod";

const server = new McpServer({ name: "devices", version: "1.0.0" });
const ok = (text) => ({ content: [{ type: "text", text }] });
server.registerTool(
  "get_device_status",
  { description: "Read a room device's status.", inputSchema: { deviceId: z.string() }, annotations: { readOnlyHint: true } },
  async ({ deviceId }) => ok(`${deviceId}: online, firmware 4.2.1, in call`),
);
server.registerTool(
  "list_call_logs",
  { description: "List recent call logs for a device.", inputSchema: { deviceId: z.string() } }, // no annotations
  async ({ deviceId }) => ok(`${deviceId}: 3 calls today`),
);
server.registerTool(
  "reboot_device",
  { description: "Reboot a device.", inputSchema: { deviceId: z.string() }, annotations: { readOnlyHint: false, destructiveHint: true } },
  async ({ deviceId }) => ok(`${deviceId}: rebooting`),
);

const [clientSide, serverSide] = InMemoryTransport.createLinkedPair();
const client = new Client({ name: "copilot-model", version: "1.0.0" });
await server.connect(serverSide);
await client.connect(clientSide);

const { tools } = await client.listTools();
for (const t of tools) console.log(`${t.name.padEnd(18)} annotations: ${JSON.stringify(t.annotations ?? null)}`);

const repoConfigTools = ["get_device_status", "list_call_logs", "reboot_device"]; // what the admin allowlisted
const cloudAgent = tools.filter((t) => repoConfigTools.includes(t.name));
const codeReview = cloudAgent.filter((t) => t.annotations?.readOnlyHint === true);
console.log("\ncloud agent sees:", cloudAgent.map((t) => t.name).join(", "));
console.log("code review sees:", codeReview.map((t) => t.name).join(", "));

const result = await client.callTool({ name: "get_device_status", arguments: { deviceId: "room-4b" } });
console.log("\ncall get_device_status →", result.content[0].text);
await client.close();
Result
get_device_status  annotations: {"readOnlyHint":true}
list_call_logs     annotations: null
reboot_device      annotations: {"readOnlyHint":false,"destructiveHint":true}

cloud agent sees: get_device_status, list_call_logs, reboot_device
code review sees: get_device_status

call get_device_status → room-4b: online, firmware 4.2.1, in call
What’s happening
  1. The server registers three tools with registerTool. Only get_device_status declares readOnlyHint: true; list_call_logs declares nothing (it is read-only, but doesn't say so); reboot_device says it's destructive.
  2. The client connects over InMemoryTransport.createLinkedPair(), the SDK's in-process transport, and listTools() returns exactly what a host would receive in the tools/list response, annotations included. The printed null is a tool with no annotations at all.
  3. The cloud agent's filter is the admin's tools allowlist, which here names all three, including the reboot. Nothing asks a human before it's called.
  4. Code review's extra filter keeps only readOnlyHint === true, so it sees just get_device_status. list_call_logs is dropped although it's harmless, because a missing annotation counts as "not read-only".
  5. What this demonstrates: annotations are the server's promise about a tool, and Copilot code review uses that promise to stay read-only. Annotating your own MCP tools honestly is part of designing them, which chapter 05 covers in Designing good tools. Annotations are hints from the server, not enforcement: a dishonest server could lie, which is why you review third-party servers before adding them.
Copilot repeats a comment you resolved

Copilot's first review flagged a "possible unhandled promise rejection" in devices.ts. You replied "this is handled by the error middleware" and resolved the thread. After your next push the automatic re-review raises the same comment. Why, and how do you stop it happening across the team?

Show answer

Copilot never sees your reply, and re-reviews may repeat resolved comments. Fix the cause in the instructions, or in the code.

What’s happening
  1. Replies to Copilot's comments are visible to humans only: "they won't be visible to Copilot, and Copilot won't reply" (GitHub, "Using GitHub Copilot code review").
  2. GitHub also notes that when re-reviewing, "Copilot may repeat the same comments again, even if they have been dismissed with the 'Resolve conversation' button or downvoted".
  3. So the reply carried no information to the next review. If the pattern is intentional, write it down where reviews read it: an instruction such as "Route handlers may throw; server/src/http/errors.ts turns rejections into problem responses. Don't flag unhandled rejections inside route handlers."
  4. If the comment keeps coming back because the code genuinely isn't obvious, consider making it obvious (a wrapper like asyncRoute(handler) with a comment), which helps the next human too.
  5. The general rule: feedback to an AI reviewer goes into files, not threads.
AdvancedWhere code review pays off

When it pays off. Large volumes of routine pull requests, distributed teams where the first human review can be hours away (a first pass is waiting when the reviewer wakes up), and codebases with written rules nobody applies consistently. It's weakest on design questions, product intent and cross-repository effects, which is where human review time should go.

#Copilot for teams: policies, privacy and content exclusion

Added

When a company buys Copilot Business or Enterprise, the questions change from "is it useful?" to "who can use which parts, what can it see, and what happens to our code?". GitHub answers with four separate controls, and the first thing to understand is that they are separate: turning one on says nothing about the others.

A mental model: a building with badges, locked rooms and a visitor log. Seats decide who gets a badge. Policies decide which floors a badge opens (Copilot CLI, the cloud agent, MCP servers, preview features, which models). Content exclusion locks particular rooms (files) so Copilot can't read them even with a badge, though, as below, not every door has that lock fitted. Data terms and audit logs decide what the building does with what visitors saw and who can check the log afterwards.

ControlSet byWhat it governs
Seats (licences)organisation or enterprise ownerswho has Copilot Business or Enterprise; their personal plan is cancelled when they get a seat
Policiesenterprise "AI controls" first, then organisation settingswhich features, agents, clients and models seat holders may use, e.g. "Copilot CLI", "MCP servers in Copilot", cloud agent per organisation, preview features, suggestions matching public code
Content exclusionrepository admins, organisation owners, enterprise ownersfiles Copilot must ignore, on the surfaces that support it
Managed settingsenterprise (managed-settings.json)client settings users can't override, such as an MCP server allowlist or permission levels
Data use and auditGitHub's terms; enterprise audit logtraining use, session data, who changed which policy

Sources: "GitHub Copilot policies for enterprises and organizations", "Content exclusion for GitHub Copilot", "MCP server usage in your company", "Managing GitHub Copilot policies as an individual subscriber" (docs.github.com, read 27 October 2026).

Content exclusion is written as YAML-like lines in settings, not as a file in the repository. At organisation level each entry is a repository reference (or "*" for any path on disk) followed by path patterns, using Ruby's fnmatch syntax, case-insensitive. This example follows GitHub's documented format; it wasn't applied to a real organisation:

Organisation settings → Copilot → Content exclusion
# Any repository, and files outside git too: environment files and private keys.
"*":
  - "**/.env"
  - "**/.env.*"
  - "**/*.pem"

# The device API repository in this organisation:
device-admin:
  - "/config/device-provisioning.yml"   # factory credentials for lab devices
  - "/server/test/fixtures/captures/**" # recorded traffic from customer rooms

# A partner's SDK we mirror but may not send to third-party services, however it was cloned:
git@github.com:partner-org/room-sdk.git:
  - "**"
What’s happening
  1. "*": applies to every repository and to files that aren't in a git repository at all, so .env files and private keys are excluded wherever a developer opens them. Patterns are fnmatch style: ** crosses directories.
  2. device-admin: names a repository in the same organisation and lists paths from its root. A leading / anchors a pattern to the repository root; without one, GitHub's examples show it matching anywhere ("secrets.json" "anywhere in this repository").
  3. The last entry uses a full git URL. GitHub matches repository references "regardless of how the repository was cloned locally" (HTTPS, SSH, git://), ignoring the user@ and port parts.
  4. What exclusion means where it works: no inline suggestions in those files, their content doesn't inform suggestions elsewhere or chat answers, and code review skips them.
  5. What it doesn't do: the docs warn Copilot "may use semantic information from an excluded file if the information is provided by the IDE indirectly" (types, hover definitions, build configuration); exclusions don't apply to symlinks or remote file systems; and changes can take up to 30 minutes to reach IDEs that already loaded the settings.
  6. The bigger gap is coverage, which the next model makes explicit: several agent surfaces ignore content exclusion entirely.
AdvancedWhere content exclusion doesn't reach
exclusion-coverage.mjs
// Does content exclusion protect a file everywhere your team uses Copilot? Support data copied from two GitHub
// pages read on 2026-10-27: the table and the note in "Content exclusion for GitHub Copilot" (concepts), and the
// note in "Excluding content from GitHub Copilot" (how-to). They disagree about Copilot CLI and about agent modes
// outside VS Code. GitHub.com support is "in public preview"; Visual Studio is left out here.
const support = {
  "VS Code: inline suggestions": "yes",
  "VS Code: Chat (ask)": "yes",
  "VS Code: Edit mode": "no",
  "VS Code: agent mode": "no",
  "JetBrains: inline suggestions": "yes",
  "JetBrains: chat": "yes",
  "JetBrains: agent mode": "docs disagree", // concepts table: supported; concepts note and how-to note: not in agent modes
  "Xcode: chat and agent": "no",
  "Eclipse: chat and agent": "no",
  "GitHub.com: Chat": "yes",
  "GitHub.com: code review": "yes",
  "Copilot CLI": "docs disagree", // concepts table: supported; how-to note: not supported
  "Copilot app": "yes",
};

const team = ["VS Code: inline suggestions", "VS Code: Chat (ask)", "VS Code: agent mode", "GitHub.com: code review", "Copilot CLI"];
console.log("Is config/device-provisioning.yml kept out of Copilot?");
for (const surface of team) console.log(`  ${surface.padEnd(28)} ${support[surface]}`);
const unsafe = team.filter((s) => support[s] !== "yes");
console.log(`not protected for certain: ${unsafe.length} of ${team.length} → ${unsafe.join(", ")}`);
Result
Is config/device-provisioning.yml kept out of Copilot?
  VS Code: inline suggestions  yes
  VS Code: Chat (ask)          yes
  VS Code: agent mode          no
  GitHub.com: code review      yes
  Copilot CLI                  docs disagree
not protected for certain: 2 of 5 → VS Code: agent mode, Copilot CLI
What’s happening
  1. support is GitHub's table typed in, with one change: where GitHub's pages contradict each other (or the concepts page contradicts its own table, as for JetBrains agent mode: its note says exclusion is "not supported in Edit and Agent modes of Copilot Chat in Visual Studio Code and other editors"), the value is "docs disagree" instead of a guess. The concepts table marks Copilot CLI as supported; the how-to page says "GitHub Copilot CLI and Agent mode in Copilot Chat in IDEs do not support content exclusion".
  2. team is a realistic set of surfaces for one developer in October 2026: completions and chat in VS Code, agent mode for multi-file work, code review on pull requests, and the CLI.
  3. Three of the five honour the exclusion. Agent mode in VS Code doesn't: the agent can open device-provisioning.yml with its file-reading tool like any other file. For the CLI, the documentation can't tell you, so treat it as unprotected until you've tested it.
  4. What this demonstrates: content exclusion was designed for completions and chat. It is not a security boundary for agents. Secrets and customer data should not be in the repository or the workspace in the first place: use a secrets manager, .gitignore, secret scanning with push protection, and test fixtures with fake data.
AdvancedHow policies combine

Policies are set at the enterprise level first, and for most of them the enterprise can enable, disable or "let organizations decide". A person can hold seats from more than one place, and GitHub gives a general rule with exceptions: within one enterprise, the least restrictive setting usually applies; across different enterprises, the most restrictive "almost always" applies. This model applies only the general rule:

policy-resolve.mjs
// Effective Copilot policy for a user with licenses from several places, per GitHub's general rule
// ("GitHub Copilot policies for enterprises and organizations", read 2026-10-27): across organizations in the
// SAME enterprise the least restrictive setting usually applies; across DIFFERENT enterprises the most
// restrictive almost always applies. The docs list per-policy exceptions; this model ignores them.
const seats = [
  { enterprise: "acme", org: "acme-platform", policies: { "Copilot CLI": "disabled", "MCP servers in Copilot": "enabled" } },
  { enterprise: "acme", org: "acme-devices", policies: { "Copilot CLI": "enabled", "MCP servers in Copilot": "disabled" } },
  { enterprise: "partner", org: "partner-labs", policies: { "Copilot CLI": "enabled", "MCP servers in Copilot": "disabled" } },
];

function effective(policy, seats) {
  const byEnterprise = Object.groupBy(seats, (s) => s.enterprise);
  const perEnterprise = Object.entries(byEnterprise).map(([name, list]) => {
    const anyOn = list.some((s) => s.policies[policy] === "enabled"); // least restrictive inside one enterprise
    return [name, anyOn ? "enabled" : "disabled"];
  });
  const allOn = perEnterprise.every(([, v]) => v === "enabled"); // most restrictive across enterprises
  return { perEnterprise: Object.fromEntries(perEnterprise), result: allOn ? "enabled" : "disabled" };
}

for (const scenario of [["acme only", seats.slice(0, 2)], ["acme + partner", seats]]) {
  console.log(scenario[0]);
  for (const policy of ["Copilot CLI", "MCP servers in Copilot"]) {
    const { perEnterprise, result } = effective(policy, scenario[1]);
    console.log(`  ${policy.padEnd(24)} ${JSON.stringify(perEnterprise).padEnd(46)} → ${result}`);
  }
}
Result
acme only
  Copilot CLI              {"acme":"enabled"}                             → enabled
  MCP servers in Copilot   {"acme":"enabled"}                             → enabled
acme + partner
  Copilot CLI              {"acme":"enabled","partner":"enabled"}         → enabled
  MCP servers in Copilot   {"acme":"enabled","partner":"disabled"}        → disabled
What’s happening
  1. The user has seats from two organisations of the acme enterprise, which disagree on both policies, and later a third seat from a different enterprise, partner.
  2. Object.groupBy groups the seats by enterprise. Inside acme, either organisation enabling a policy is enough (least restrictive), so both policies come out enabled even though each organisation disabled one of them.
  3. Across enterprises the rule flips: every enterprise must allow it (most restrictive). Adding the partner seat turns MCP off for this user everywhere, although nothing changed in acme.
  4. The consequence for a tech lead: an organisation owner who disables a feature for their organisation hasn't necessarily disabled it for people who also have a seat from a sibling organisation. Controls that must hold are set at the enterprise level; GitHub's "Feature availability when GitHub Copilot policies conflict" page lists the per-policy exceptions this model ignores.
AdvancedMCP for organisations

The "MCP servers in Copilot" policy is disabled by default and governs only Copilot Business and Enterprise users ("Model Context Protocol (MCP)", read 27 October 2026); Free, Pro, Pro+ and Max users aren't governed by it. GitHub recommends enabling it and restricting servers to an approved list. Two ways exist ("MCP server usage in your company"): an allowlist in the enterprise's managed-settings.json, generally available, matching servers securely by name, URL or stdio command, which users can't override; or a self-hosted MCP registry, in public preview, matching by name or ID only, which "users can bypass … by editing configuration files". Note too that this policy "does not control access and permissions for the GitHub MCP server in third-party host applications" such as Cursor or Claude.

AdvancedWhat happens to your code and prompts
  • Training: "GitHub does not use Copilot Business or Copilot Enterprise customer data to train AI models", which GitHub's Data Protection Agreement prohibits without authorisation. For Copilot Free, Pro, Pro+ and Max, starting 24 April 2026, "GitHub may use your interactions … including inputs, outputs, code snippets, and associated context—to train and improve AI models", with an opt-out ("Allow GitHub to use my data for AI model training" → Disabled) in personal Copilot settings ("Managing GitHub Copilot policies as an individual subscriber", read 27 October 2026; announced in GitHub's changelog on 25 March 2026). The practical point: company code should be worked on with a company seat, not a personal plan.
  • Public code matching: with "Block" set, suggestions are checked with about 150 characters of surrounding code against public code on GitHub and matches aren't shown; with "Allow", matches come with references to the source repositories and their licences. GitHub says matches occur in under one percent of suggestions. For seat holders the organisation sets this.
  • Session data: cloud agent sessions run in an environment destroyed at the end, but their logs stay on GitHub and are visible to anyone with access to the repository. CLI and Copilot app sessions are stored locally under ~/.copilot/session-state/ and synced to your GitHub account by default (for Business and Enterprise only if the "Store local sessions in the Cloud" policy allows it); syncing doesn't give administrators access ("About GitHub Copilot session data").
  • Retention of IDE prompts and suggestions is described in GitHub's Copilot Trust Center, which couldn't be read for this page (the site renders with JavaScript), so no retention figures are given here; check it before quoting one.
  • Audit: policy changes and agent activity appear in the enterprise audit log; cloud agent commits are signed, co-authored by the requester and linked to the session log.
AdvancedSensitive data on a device platform

Where this shows up in the role. On a device platform the sensitive material is concrete: device credentials and provisioning files, customer room names and IP addresses in logs and captures, partner SDKs under licence. A lead who can say which control covers which of those, and where the gaps are, is answering the question the security team will ask.

#Talking about Copilot in an interview

Added

When a job description says "hands-on experience with AI-assisted development tools such as GitHub Copilot", the interviewer usually isn't testing whether you know where the Tab key is. They want to know whether you use these tools with judgement: for which tasks, how you check the output, what changed for you or the team, and where you don't use them. A vague "I use Copilot every day, it makes me much faster" fails that test; a specific, honest workflow with a check and a limit passes it.

The mental model is show your working, like a maths exam. A good answer has four parts: the task, the workflow (which feature, what context you gave it), the check (how you knew it was right), and the result and limit (what you'd measure, where it fell short). It also has to be true. Rohit's main assistant is Claude Code, not Copilot, so this topic gives structures, workflows and questions to adapt, and every sample answer below is illustrative: replace the bracketed parts with things you have actually done, and don't claim Copilot experience you don't have.

What transfers from Claude Code, and what to try before the interview. The agent loop, planning before editing, instruction files, tests as the agent's check, and reviewing diffs are the same skills in both tools. Some files even carry over: the Copilot cloud agent and code review read AGENTS.md and a root CLAUDE.md, Copilot agent skills can live in .claude/skills, and MCP servers are the same protocol in both. What's Copilot-specific, and worth an hour of hands-on time with Copilot Free or Pro before an interview, is the list in the checklist below.

Before the interview: an hour with Copilot (Free or Pro)
1. VS Code + the GitHub Copilot extension. Write code in a small repo for 10 minutes: notice completions and next edit suggestions.
2. Add .github/copilot-instructions.md (versions, test command, two rules). Ask Chat a question; check the References list shows the file.
3. Ask the Plan agent for a plan for a small feature. Answer its questions. Click Start Implementation.
4. In agent mode, watch it ask to run a command. Add one chat.tools.terminal.autoApprove rule for your test command.
5. Write one .github/prompts/*.prompt.md and run it as a slash command.
6. Add one MCP server in .vscode/mcp.json (chapter 05 builds one) and see its tools in the tool picker.
7. On a paid plan: assign a small issue to Copilot on GitHub.com; read the session log; request a Copilot code review on the PR.
Write down what surprised you. Those notes are your real, first-hand answers.
What’s happening
  1. Steps 1 to 3 cover the assistive end of the dial and the instruction file, the two things almost every Copilot user touches. Checking the References list (GitHub documents it for both GitHub.com and VS Code) is how you verify the instructions were used, which is itself a good interview detail.
  2. Steps 3 and 4 give you first-hand experience of plan mode and of the approval gate, the two places Copilot's agent mode differs most visibly from a tool you already know.
  3. Step 5 and step 6 are the team-scale features: shared prompt files and MCP servers. The job description names MCP explicitly, and chapters 04 to 07 of this course build one; connecting it to Copilot closes the loop.
  4. Step 7 needs a paid plan (Copilot Free doesn't include the cloud agent, and its code review is limited to "Review selection" in VS Code). If you can't do it, say so and explain from the documentation how it works, which is honest and still shows understanding.
  5. The last line is the point: after this hour, statements like "the plan agent asked me two questions I hadn't thought of" are true and specific. Invented anecdotes are neither, and follow-up questions expose them quickly.
AdvancedWorkflows you can describe

Each of these is a workflow, not a story: what you do, which Copilot feature, and the check. Adapt them to tasks you've really done (with Claude Code or Copilot), naming the tool you used.

1. A new REST endpoint, planned then built in agent mode. Plan agent: "Add GET /api/v1/devices/{id}/calls, copying the calls route pattern; list the files you'd change and your questions." Answer the questions, then Start Implementation. The check is the agent running npm run typecheck && npm test against the fake device, then you reading the diff, especially error handling and the OpenAPI change. The limit: it copies patterns well and invents fields when the contract is vague, so the contract comes first.

2. A small backlog for the cloud agent. Pick issues with a reproduction or acceptance criteria (a flaky test, missing pagination, a docs gap). Assign them to Copilot with a one-line prompt pointing at the pattern to copy. Check: the session log (did it run the tests?), CI after "Approve and run workflows", and a normal human review. Measure how many of its PRs merge with few changes. The limit: anything ambiguous or cross-repository comes back wrong or not at all, and later issue comments aren't seen.

3. Tests first, then the change. Ask for failing tests that describe the behaviour (edge cases: device offline, timeout, 404), review the tests yourself, then let the agent make them pass with "fix the code, not the tests". This is chapter 03's Tests first with an assistant. The check is that the tests failed first and pass now, and that none were weakened.

4. Code review as a first pass for a distributed team. Automatic Copilot review on every PR, a security checklist in a path-specific instructions file, a human approval still required. For a team across time zones the benefit is that the first round of comments is waiting when the author's day starts. Measure time to first review and how many Copilot comments lead to a change.

5. Shared instructions and prompt files as onboarding. Commit .github/copilot-instructions.md, AGENTS.md and two or three prompt files (/add-endpoint, /write-tests) so a new developer, for example on the India-based team, gets the same conventions and the same best prompts on day one. Review changes to them like code. Chapter 08's Helping a distributed team use AI tools well goes further.

AdvancedMeasured impact: what you can honestly claim

Interviewers often ask "how much faster are you?". The honest answer separates adoption (are people using it?), activity (how much code does it produce?) and outcomes (do PRs merge faster, with fewer defects?). GitHub's usage metrics cover the first two well: total and daily active users, agent adoption, acceptance rate, languages and models, lines-of-code metrics GitHub itself calls "directional", plus pull request lifecycle metrics. Their dashboard "primarily reflect[s] activity in supported IDEs" and doesn't include Copilot Chat on GitHub.com, Mobile, code review or the CLI ("Measuring the success of a GitHub Copilot trial", read 27 October 2026). Outcomes need your own before/after comparison, and that's where most claims go wrong. The data below is made up for illustration:

impact.mjs
// Reading a before/after comparison honestly. The numbers are ILLUSTRATIVE (made up for this page), shaped
// like a team's pull request export: hours from opening to merge, and whether the change was reverted later.
const before = {
  hours: [5, 7, 8, 9, 10, 11, 12, 14, 15, 16, 18, 20, 22, 24, 26, 30, 34, 40, 52, 70],
  reverted: 1,
};
const after = {
  hours: [3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 22, 30, 48, 96, 160],
  reverted: 3,
};

const sorted = (xs) => [...xs].sort((a, b) => a - b);
const pct = (xs, p) => sorted(xs)[Math.ceil((p / 100) * xs.length) - 1]; // nearest-rank percentile
const mean = (xs) => xs.reduce((a, b) => a + b, 0) / xs.length;

console.log("               median   p90    mean   reverted");
for (const [name, d] of [["before", before], ["after", after]]) {
  console.log(
    `${name.padEnd(14)} ${String(pct(d.hours, 50)).padStart(5)}h ${String(pct(d.hours, 90)).padStart(4)}h ${mean(d.hours).toFixed(1).padStart(6)}h   ${d.reverted}/${d.hours.length}`,
  );
}
const change = (a, b) => `${(((b - a) / a) * 100).toFixed(0)}%`;
console.log(`\nmedian ${change(pct(before.hours, 50), pct(after.hours, 50))}, mean ${change(mean(before.hours), mean(after.hours))}, p90 ${change(pct(before.hours, 90), pct(after.hours, 90))}`);
Result
               median   p90    mean   reverted
before            16h   40h   22.1h   1/20
after             10h   48h   24.4h   3/20

median -38%, mean 10%, p90 20%
What’s happening
  1. Each period has 20 merged pull requests, with hours from open to merge and a count of later reverts. pct() is a nearest-rank percentile: for the median of 20 sorted values it takes the 10th.
  2. The median fell from 16 to 10 hours, a 38% drop: the typical PR got through faster. That's the number a hopeful summary would quote.
  3. The mean rose 10% (443 to 489 total hours over 20 PRs), because two PRs in the "after" period took 96 and 160 hours. The p90 rose from 40 to 48 hours: the slow tail got slower.
  4. Reverts tripled, 1 to 3. Faster merges with more reverts may mean review got lighter, not that the work got better. With 20 PRs per period, none of these differences is reliable anyway, and anything else that changed between the periods (a release crunch, a new hire, smaller tickets) is mixed in.
  5. What this demonstrates: an honest impact statement names the metric, the spread and the counter-metric: "median time to merge fell from about 16 to 10 hours over two months, but the slow tail and reverts went up, so we added X". A single percentage hides all of that.

Published research points both ways, which is itself a good interview point. A 2022 controlled experiment by researchers from Microsoft Research, GitHub and MIT (Peng, Kalliamvakou, Cihon and Demirer, arXiv 2302.06590, February 2023) found developers with Copilot finished one task, writing an HTTP server in JavaScript, 55.8% faster (GitHub's September 2022 blog post rounds it to 55%). A randomised trial by METR published in July 2025, with 16 experienced open-source developers on 246 tasks in their own large repositories (mostly using Cursor with Claude models), found AI assistance made them 19% slower, while the developers believed it had made them about 20% faster. Neither settles the question for your team: tools, tasks and developers differ. Chapter 08's Measuring whether it helps treats this properly.

Critique the claim

A candidate says: "Copilot makes developers 55% faster. When we rolled it out, our acceptance rate was 35%, so it's writing a third of our code." What's wrong with each half, and what would you say instead?

Show answer

The 55% is one lab task, not your team; acceptance rate is not share of code, and neither is an outcome.

What’s happening
  1. 55.8% comes from one controlled experiment on one well-defined greenfield task (an HTTP server in JavaScript). It doesn't transfer to a mature codebase with reviews, and METR's 2025 trial on real repositories found the opposite sign. Quoting it as a general fact is the overclaim interviewers listen for.
  2. An acceptance rate is the share of shown suggestions that were accepted. It says nothing about how much of the merged code came from Copilot (accepted text gets edited or deleted) or whether that code was good.
  3. Both numbers measure activity, not outcomes. An outcome would be time to merge, change failure rate, escaped defects or developer-reported friction, compared before and after with the caveats from impact.mjs.
  4. A better version: "In my own work I use it for [specific tasks]. I can't give you a reliable team-wide percentage. What I'd measure on a team is [time to first review, time to merge, reverts], and I'd look at the developer survey alongside it."
AdvancedLimits worth saying out loud

Naming limits makes the rest of an answer more credible. Ones you can back with GitHub's own documentation: it "may not always follow your custom instructions"; the cloud agent works in one repository for at most 59 minutes and doesn't see issue comments added after assignment; code review "is not guaranteed to spot all problems"; content exclusion doesn't apply to agent mode; terminal auto-approval "is a best-effort convenience, not a security boundary"; and GitHub's own guidance keeps security-sensitive, ambiguous and production-critical work with humans. From general experience with assistants (chapter 01): plausible but wrong APIs, tests weakened to pass, and confident explanations of code it hasn't read.

AdvancedThe two ways to fail this question
AdvancedWhat to prepare before the interview

What to prepare. Two or three true workflows in the four-part shape (task, workflow, check, result and limit); the agent mode versus cloud agent explanation; one sentence each on instructions, prompt files and MCP in Copilot; one on content exclusion's limits; and one on how you'd measure impact. The review questions in chapter 09's GitHub Copilot questions let you practise them out loud.

Written fresh for a role that asks for GitHub Copilot and MCP experience. MCP examples were run with @modelcontextprotocol/sdk 1.32.2; Copilot facts come from GitHub's documentation, cited with dates.

RohitDownloads · All chapters

Sync progress across devices

Type the same private phrase on your Mac and your phone, and your Learned ticks follow you between them — on every study site.

The phrase never leaves this device: only a fingerprint of it is sent, and the server stores a fingerprint of that. Anyone who knows the phrase could see or change your ticks, so pick something you don’t use elsewhere.

Sync is on in this browser.

Sync ID

This ID must be the same on every device. If another device shows a different one, its phrase is different (capital letters count): tap “Turn off here” on it and type the phrase again exactly.