Onboarding your AI peer programmer: Setting up GitHub Copilot coding agent for success

Learn how to configure Copilot coding agent’s environment, optimize project structure, use custom instructions, and extend its capabilities with MCP servers.

| 12 minutes

We often describe GitHub Copilot as an AI peer programmer, or an AI member of the team. With agentic features like coding agent, you can assign issues to Copilot, and it will diligently get to work behind the scenes, creating a proposed solution to the problem, all without even asking for a cup of coffee.

Much of the initial setup for Copilot coding agent is similar to onboarding a new developer – like providing good documentation and streamlining the setup process. But since it’s AI, there are a few things that make Copilot unique as a team member (aside from it not needing caffeine).

So let’s explore how this is done. We’ll start by examining the flow Copilot coding agent follows, and key strategies to ensure Copilot has the resources it needs to generate the best possible pull request.

Inside Copilot coding agent’s workflow: From issue to ready‑to‑review pull request

When you assign an issue to Copilot, it follows a set pattern:

  1. Creates a branch for the code it will create.
  2. Creates a pull request to track its work and communicate with the team.
  3. Creates a contained environment for its work (running inside GitHub Actions).
  4. Reads the issue or prompt to understand the requested task.
  5. Explores the project to determine the best approach to tackle the problem.
  6. Works iteratively toward a solution.
  7. Finalizes its work, updates the pull request, and notifies the team the pull request is ready to be reviewed.

By understanding this flow, we can work to ensure Copilot is set up for success.

The first two steps — creating the pull request and branch — are self-contained, and there’s no additional work for us to do to help Copilot. 

So, let’s skip right to the third step — the environment — where we can configure everything Copilot might need in the environment where it’ll write the code and run tasks as it generates the pull request.

Configure Copilot’s environment with GitHub Actions

In keeping with the analogy of onboarding a new developer, let’s consider the environment in which GitHub Copilot — or really any developer — does its work. Before you’re able to be productive, you need specific services, libraries, and frameworks installed.

Copilot is the exact same. In order for Copilot to add a new feature and run the necessary tests to ensure everything works, it needs access to all the tooling the rest of your team has. We’ll do this with a custom workflow file.

Coding agent uses a container running inside GitHub Actions. If you’re not already familiar with Actions, it’s our automation platform, and is configured using YAML files, which describe the necessary tasks that need to be completed. 

Actions is often used for CI/CD, so tasks like testing, deployment, etc. In this case, Actions hosts the container coding agent will use for its work. And we can take advantage of the ability to script tasks in YAML to ensure said container is set up correctly!

💡Pro tip: There’s a good chance you already have a workflow for creating an environment, which could be used for development — say like the one used when running various tests or validation scripts. You can absolutely reuse those workflows for Copilot coding agent’s environment!

Example Copilot setup workflow file

To do this, create a new workflow file located at .github/workflows/copilot-setup-steps.yml with a job titled copilot-setup-steps. Inside the job, you’ll list all of the steps to install the necessary requirements for the environment. Let’s say, for example, we’re building a Python app that uses SQLite. We could have a workflow file like the following, which will be run to set up the environment for Copilot:

name: "Copilot Setup Steps"

# Automatically run the setup steps when they are changed
# Allows for streamlined validation,
# and allow manual testing through the repository's "Actions" tab

on:
  workflow_dispatch:
  push:
    paths:
      - .github/workflows/copilot-setup-steps.yml
  pull_request:
    paths:
      - .github/workflows/copilot-setup-steps.yml

jobs:
  # The job MUST be called `copilot-setup-steps`
  # otherwise it will not be picked up by Copilot.
  copilot-setup-steps:
    runs-on: ubuntu-latest

    # Permissions set just for the setup steps
    # Copilot has permissions to its branch
    
    permissions:
      # To allow us to clone the repo for setup
      contents: read

    # The setup steps - install Python and our dependencies
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.13"
          cache: "pip"

      - name: Install Python dependencies
        run: pip install -r requirements.txt

      - name: Install SQLite
        run: sudo apt update && sudo apt install sqlite3

Whenever an issue is assigned to Copilot, it will run this workflow to configure its environment so it’ll have everything it needs!

💡 Pro tip: If you know something should be done a particular way, tell Copilot! In our above example, Copilot could install the requisite services on its own. However, doing so may lead to unexpected versions or other mistakes. As I always like to joke, don’t be passive aggressive with Copilot. 😀

Set Copilot up for success with well-written issues and prompts

Speaking of not being passive aggressive, now’s the perfect time to focus on the next step Copilot follows: reading the issue. This will be Copilot’s entry point into creating the pull request you’ll later review. Remember that the more clearly defined the issue, the better quality the pull request. 

The best approach is to think about how you’d like to see the first issue you’re assigned on a new project. Chances are, you’d appreciate the following:

  • Clearly defined problem statement or user story
  • If it’s a bug, the full error message, stack trace, or output, and steps to reproduce the problem
  • Any relevant history or approaches that have been tried previously
  • Suggestions on how to approach the issue

The same holds true with Copilot. Let’s say you needed to migrate a set of tests from unittest to pytest. Sure,Copilot could likely figure out the best approach on its own, but taking a couple of minutes to write out a good issue goes a long way to ensuring Copilot’s PR will be accepted and merged into your codebase.

Here’s an example of a good issue (for developers and Copilot, alike!): 

Title: Migrate server tests from unittest to pytest

Body:

We are looking to migrate from unittest to pytest to take advantage of some pytest specific features.

Requirements:

- A new folder called `migrated_tests` will be created with the new pytest tests.
- All existing unittests are rewritten using pytest style in the `migrated_tests` folder, keeping the exact same functionality and code coverage.
- Documentation is updated, highlighting the migration and steps required to run the new tests.
- All new tests pass.

Existing resources:

- All existing tests exist in `server/tests`
- There is a script at `scripts/run-server-tests.sh` which is used to run tests and generate code coverage reports

Recommended approach:

- Explore existing tests to determine their functionality
- Read the coverage reports to determine existing code coverage
- Recreate the tests one by one, testing along the way, to ensure compatibility
- Run all tests at the end to ensure everything passes
- Generate a code coverage report to demonstrate code coverage has been maintained
- Generate documentation of the migration and how to run the new tests

Make your repository welcoming (for developers and AI)

Let’s stick with the analogy of onboarding a developer to a new team and project, something I’ve done a handful of times throughout my career. 

When I am onboarding somewhere new, I get my laptop set up and try to overcome my first-day-of-school butterflies — because I know there’s a lot of work to be done. Setting aside a conversation about my anxiety, let’s explore everything I need to do in order to be productive.

First, I need to figure out where, and how, my code should be created. What’s the project structure? What are the rules and guidelines that need to be followed? Are we using tabs or spaces? (The right answer, of course, is spaces.)

GitHub Copilot needs to know these same things! Fortunately, standard best practices around documentation and project structure will help Copilot the same way it helps developers.

💡Pro tip: Understanding how Copilot tackles problems can help you improve how you use it. Here’s how to do that: On the PR Copilot created you’ll see a View session button that shows you everything Copilot did (or is doing if the session is currently active). This is both a great way to validate Copilot’s work and see how it approaches tasks. You can then use this information to further refine your approach to assigning tasks and configuring Copilot’s environment.

Optimize project structure and docs so Copilot finds the right info fast

Let’s say we assign an issue to Copilot, and ask it to add search functionality to an app. When Copilot takes on an issue, the first thing it does is explore the codebase. It’ll look for README files, and, unlike this developer, actually reads the documentation to learn about the project before writing code. It’ll perform searches in the codebase for anything related to interacting with the database, and read through the files it discovers. Then it’ll get to work.

This is the same approach you’d likely take as a developer if you were assigned the task. So ensuring the project structure has the resources and entities to make it as welcoming to new developers as possible sets up everyone (including your AI teammates) for success.

This includes having a robust and up-to-date README for the project and services, comments in code to describe what and how operations are performed, and good practices followed in naming classes, functions and variables. Additionally, having a logical project structure that follows accepted best practices in folder names and entity groupings will provide a more predictable environment for Copilot (and the rest of your team).

Document institutional knowledge with Copilot custom instructions

One of the best ways to provide guidance to Copilot is through the use of custom instructions. Custom instructions are just like they sound: a set of instructions specifically for Copilot. They can be about the various rules and guidelines you have around formatting code, or the institutional knowledge that all developers “inherently know,” but isn’t written down anywhere.

Copilot coding agent supports two types of instructions files: copilot-instructions.md, which are repository-wide and applied to all requests, and <file-name>.instructions.md, which can be targeted at specific types of files.

Repository level instructions files

Repository level instructions filed, stored in .github/copilot-instructions.md in the codebase, house notes which are generally relevant to all requests made to Copilot. Some key pieces of information to include would be:

  • An overview of what you’re building, and how you’re building it
  • Any overarching user stories
  • Frameworks and libraries in use
  • The project structure, highlighting key files and folders
  • Global coding guidelines and rules

In the example below, note how we’ve started with a quick overview of the app, the expected flow for the user, frameworks and rules, and the resources available.

# Classic arcade

This project hosts a classic arcade, themed after the 1980s 8-bit games.

## Standard player flow

1. Player opens app and sees list of games.
2. Player selects game to play.
3. Player sees a splash screen with the message "Insert quarter".
4. Player presses space to start game and plays game
6. After game ends, the "Game over" message is displayed.
7. The player score is checked against high scores. If the score is in top 10, user is prompted for their initials (3 initials).
8. High scores are displayed, and an option to return to the main menu to start over again.

## Frameworks

- Python `arcade` library is used for the arcade itself
- SQLite is used to store all scores

## Coding guidelines

- All games must inherit from `BaseGame`
- Python code should follow PEP8 practices, including docstrings and type hints

## Project structure

- `data`: Stores data abstraction layer and SQLite database
- `games`: Stores collection of games and `BaseGame`
- `app`: Stores core app components including menuing system

Everything documented in the Copilot instructions document above could likely be figured out by Copilot as it does its investigation. But listing your requirements and resources helps ensure it has the right information, especially since there’s likely code that deviates from the accepted best practices of your organization.

> Note: This author’s code is always perfect, so that isn’t applicable to me, of course. 😉

Targeted instructions files

Obviously the code that resides in a unit test is very different from code in a data abstraction layer, which is very different from … While having repository-wide instructions is powerful, we typically have rules around specific types of files as well. Copilot coding agent supports this through the use of <file-name>.instructions.md files, which reside in the codebase in the .github/instructions/ folder (or subfolders inside of there).

These targeted instructions files can contain an applyTo section, which allows you to set a glob pattern to identify the files to which the rules should apply. Sticking with the scenario from above — building out a classic arcade — we might have all games in a games folder as Python files. The pattern we’d use would be **/games/*.py. Within that section, you can add instructions specific to these files, such as which base class to inherit from and any testing requirements.. An example .github/instructions/game.instructions.py might look like the following:

---
applyTo: **/games/*.py
---

## Resources and requirements

- All games inherit from `BaseGame`
- Unit tests are required for all games, focused on core functionality
- When adding a new game to the arcade ensure sample high scores are added to the database

## Arcade framework notes

- `rectangle` is always abbreviated as `rect` in the framework
- The `BaseGame` class contains numerous abstractions to streamline game creation

Notice how we’re listing requirements and resources. We also added a note about rectangle being abbreviated as rect, which is there to help Copilot with common mistakes it might make.

💡Pro tip: Instructions files are a great way to guide Copilot in the right direction when you see it making particular types of mistakes.

A closing thought on instructions files

Investing time in creating a robust set of instructions files will both aid Copilot when you use it in the IDE and when you assign tasks to Copilot coding agent. In fact, there’s a good chance you’re already familiar with the concepts in this section if you’re experienced with Copilot. Because these become artifacts in the project, they’ll continue to pay dividends in both productivity and suggestion quality.

Extend Copilot with MCP to give it more context and tools

All developers need a helping hand at some point. It might be retrieving a specific discussion from your GitHub repository to learn more about the history of a feature, or looking up the syntax for implementing a particular algorithm or block of code.

AI agents can perform these tasks by using MCP, or Model Context Protocol. MCP is an open standard from Anthopic designed to help AI connect to key services and tools. This helps AI agents execute tasks on your behalf — but it also allows you to give the model more context by connecting it to more sources of data and information (like GitHub!). 

Here’s why this matters: GitHub Copilot can use MCP servers! By default, two MCP servers are available when you use its coding agent: GitHub and Playwright. The former allows Copilot to interact with your repository, searching for issues and other information while the latter allows Copilot to generate Playwright tests for end-to-end or acceptance testing.

Example: Azure MCP server for Bicep generation

Let’s take Azure Bicep as an example, which is a domain-specific language (DSL) for defining Azure resources. To do its best work with a DSL, like Azure Bicep, Copilot benefits from a virtual helping hand in generating the code. Fortunately, there’s an Azure MCP server available that Copilot can use to get a bit of support.

If your team is already using MCP servers for the project in VS Code, the existing .vscode/mcp.json file in your project can be used by Copilot to identify MCP servers! Otherwise, you can configure MCP servers specifically for Copilot coding agent in the Settings tab of your project, under Copilot then Coding agent, where you’ll have a textbox to paste in the JSON.

Keeping with our example of wanting to better support Bicep creation, the JSON below enables the Azure MCP server while specifying we only want Bicep schema support. 

{
  "mcpServers": {
    "AzureBicep": {
      "type": "local",
      "command": "npx",
      "args": [
        "-y",
        "@azure/mcp@latest",
        "server",
        "start",
        "--namespace",
        "bicepschema",
        "--read-only"
      ]
    }
  }
}

Manage internet access and data exfiltration risks with Copilot’s firewall

You may have noticed the type option in the previous MCP example is set to local. This means that the server will be accessed inside the container without Copilot needing to contact an external service. But this also implies the possibility of a remote server, which begs the question: Is Copilot allowed to access the internet? And if so, how can we control it?

Copilot coding agent has a default firewall, which effectively limits Copilot’s access to core services, such as package hosting services like npm and pip. This helps you manage data exfiltration risks. For instance, if malicious instructions are somehow given to GitHub Copilot, it could lead to code or other sensitive information being leaked to remote locations.

If you are adding a remote MCP server, or need to allow Copilot to access internet resources, you will need to update the firewall, which you can do by updating the allow list. This is available under your repository’s settings, inside Copilot, then Coding agent (conveniently, that’s the same screen as configuring your MCP servers!). 

Welcome to the team, Copilot!

Just like any good teammate, GitHub Copilot coding agent thrives when it’s set up for success! By investing a little time upfront to configure its environment, craft clear issues, optimize your project, and leverage custom instructions and MCP servers, you’ll empower Copilot to deliver its best work.

With these tips in your arsenal, you’ll be well on your way to getting higher quality pull requests from Copilot coding agent, and a more productive development experience. 

Ready to see the magic happen? Learn more about GitHub Copilot!

As for me, I need to refill my coffee cup.

Learn how to assign and complete issues with coding agent in GitHub Copilot >

Written by

Related posts