Creating a GitHub Copilot Extension: A Step-by-Step Guide

Nick Taylor - Sep 30 - - Dev Community

GitHub Copilot, the AI-powered coding assistant, has recently introduced Copilot Extensions to enhance its ecosystem. This feature, now in public beta, allows developers to create custom extensions that integrate with Copilot. In this blog post, we'll walk through the process of creating your first GitHub Copilot extension.

Before we begin, it's important to note that you need to have an active GitHub Copilot subscription to create and use Copilot extensions.

Creating the Endpoint for Your Copilot Extension

A Copilot extension is essentially a GitHub app with a specific endpoint. Let's set up the project and create this endpoint, which together will form your Copilot extension.

Setting Up Your Project

In this guide, we're using Hono.js as our web framework, but you can use any web framework or web server of your choice.

The story of web framework Hono, from the creator of Hono

Hono is a web framework that is fast, lightweight, and built using the Web Standards API. Hear the story of Hono by the creator of Hono.

favicon blog.cloudflare.com

The core concepts will remain the same regardless of the framework you choose. The only thing to be aware of about the SDK is, for the moment, the only languages supported are TypeScript and JavaScript.

  1. Create a new Hono project using the Hono CLI:

    npm create hono@latest
    

    Follow the prompts to set up your project. This will create a new TypeScript project using Hono.js, a lightweight and fast web framework.

  2. Install the preview SDK for Copilot extensions and Octokit's core package:

    npm install @copilot-extensions/preview-sdk @octokit/core
    

    GitHub logo copilot-extensions / preview-sdk.js

    An SDK that streamlines extension development by automating request verification, response formatting, and API interactions

    @copilot-extensions/preview-sdk

    This SDK simplifies the process of building GitHub Copilot Extensions. Building Copilot Extensions previously required manual handling of request verification, response formatting, and API interactions. This SDK simplifies these tasks, allowing you to focus on your extension's core functionality rather than building boilerplate code. Use it to integrate your tools, APIs, or data sources directly into Copilot Chat.

    We consider this SDK alpha software in terms of API stability, but we adhere to semantic-versioning, so it's safe to use today.

    Key features

    • Request payload verification
    • Payload parsing
    • Response building

    Benefits

    • Handles security and response formatting requirements
    • Provides utilities for common extension tasks
    • Streamlines the development process

    Usage

    Verify a request

    import { verifyRequestByKeyId } from "@copilot-extensions/preview-sdk";
    const { isValid, cache } = await verifyRequestByKeyId(
      request.body,
      signature,
      keyId,
      {
        token: process.env.GITHUB_TOKEN,
      },
    );
    //
    โ€ฆ
    Enter fullscreen mode Exit fullscreen mode
  3. Open your main file (e.g., src/index.ts) and let's start by importing the necessary dependencies:

    import { Hono } from 'hono'
    import { serve } from '@hono/node-server';
    import { Octokit } from "@octokit/core";
    import {
      createAckEvent,
      createDoneEvent,
      createErrorsEvent,
      createTextEvent,
      getUserMessage,
      verifyAndParseRequest,
    } from "@copilot-extensions/preview-sdk";
    
    const app = new Hono();
    

Implementing the Endpoint

Now, let's implement the endpoint that will handle requests from GitHub Copilot:

  1. Create a root route that receives a form post, /. This is the endpoint that Copilot will interact with:

    app.post("/", async (c) => {
      // ... (we'll fill this in next)
    });
    
  2. When a message comes in, you need to verify the request and parse the payload:

    // Identify the user, using the GitHub API token provided in the request headers.
    const tokenForUser = c.req.header("X-GitHub-Token") ?? "";
    const body = await c.req.text();
    const signature = c.req.header("github-public-key-signature") ?? "";
    const keyID = c.req.header("github-public-key-identifier") ?? "";
    
    const { isValidRequest, payload } = await verifyAndParseRequest(
      body,
      signature,
      keyID,
      {
        token: tokenForUser,
      }
    );
    
    if (!isValidRequest) {
      console.error("Request verification failed");
      c.header("Content-Type", "text/plain");
      c.status(401);
      c.text("Request could not be verified");
      return;
    }
    
  3. After verifying the request, process the message and create a response. Here's a simple example that greets the user:

    c.header("Content-Type", "text/html");
    c.header("X-Content-Type-Options", "nosniff");
    
    return stream(c, async (stream) => {
      try {
        // Let GitHub Copilot know we are doing something
        await stream.write(createAckEvent());
    
        const octokit = new Octokit({ auth: tokenForUser });
        const user = await octokit.request("GET /user");
        const prompt = getUserMessage(payload);
    
        await stream.write(
          createTextEvent(
            `Welcome ${user.data.login}! It looks like you asked the following question, "${prompt}". `
          )
        );
    
        await stream.write(
          createTextEvent(
            "This is a GitHub Copilot extension template, so it's up to you to decide what you want to implement to answer prompts."
          )
        );
    
        await stream.write(createDoneEvent());
      } catch (error) {
        await stream.write(
          createErrorsEvent([
            {
              type: "agent",
              message: error instanceof Error ? error.message : "Unknown error",
              code: "PROCESSING_ERROR",
              identifier: "processing_error",
            },
          ])
        );
      }
    });
    

This example uses the GitHub Octokit package to get the user's login name and greets them. The createTextEvent function is used to create the response that GitHub Copilot will display. Note as well, we are streaming the response in so that we can send a signal to GitHub Copilot that something is happening, i.e. createAckEvent(). Acknowledging the event will updated the Copilot user interface to a spinning indicator that something is happening.

Exposing Your Extension

To test your Copilot extension, you need to make it publicly accessible:

  1. If using Visual Studio Code (VS Code), enable port forwarding. Note that the port is private by default, a good thing, but for our use case, we need to set it to public.

    Port forwarding tab in the bottom dock of VS Code

  2. Alternatively, use tools like cloudflared or ngrok to expose a public URL.

In the provided code, the server is set up to run on port 3000:

const port = 3000;
console.log(`Server is running on port ${port}`);

serve({
  fetch: app.fetch,
  port,
});
Enter fullscreen mode Exit fullscreen mode

It's worth mentioning that this setup is great for testing, but once you're ready to make your extension public, you'll need to deploy the web app (which acts as the GitHub app) to a publicly accessible location.

Creating a GitHub App

Create a new GitHub app on your personal account for testing purposes. Head to your settings page on GitHub, and at the bottom, to the left, click on the Developer Settings link. This will bring you to your GitHub apps. You can also directly navigate to your GitHub apps page at https://github.com/settings/apps.

General settings

  1. Enter a GitHub App name, e.g. my copilot extension
  2. Enter a URL for the homepage. This can be the same as the test URL for now.
  3. Set the Callback URL (currently required). This can be the same as the test URL for now. Even if you're not using OAuth you still need to put a URL here. I'm told in future this may no longer be required.

    A new GitHub app's settings page

  4. Disable webhooks if they're enabled.

    Webhooks settings section

  5. Make sure the app is initially accessible only to you. You can enable it for everyone when you're ready to make your GitHub Copilot extension publicly available.

    GitHub app visibility setting of just you or everyone

  6. Click the Create GitHub App button to create the GitHub app.

Permissions & events settings

Next up, we'll need to configure permissions. We want to provide the bare minimum permissions for a Copilot extension to work.

Permissions settings for a GitHub app

  1. Expand the Account permissions sections and set the Copilot Chat permission to read-only. The default is No access.

    Account permissions section of a GitHub app

  2. Click Save changes. Don't be alarmed by the Are you sure you want to update permissions? message.

    Save changes button

Copilot settings

  1. Set the App Type to Agent. It's set to Disabled by default.
  2. Set the URL to the root of the public URL you exposed via tunneling/port forwarding.

    A GitHub App's Copilot settings section

  3. Click Save.

Congratulations! You've configured your first Copilot extension!

GitHub apps section of developer settings on GitHub

Install Your Copilot Extension

Before we can use the extension, it has to be installed.

  1. Navigate to your GitHub apps in your developer settings.

    The GitHub apps section of GitHub developer settings

  2. Click the Edit button to edit the app.

  3. Go to the Install App section of the GitHub app's settings.

    GitHub App's Install App settings panel

  4. Click the Install button to install the application

  5. You're brought to an intermediary page to confirm the installation of the GitHub app. Click the Install button.

    GitHub app installation confirmation step

  6. Your Copilot extension is installed for your GitHub account.

    Post GitHub app installation screen showing the app installed

Testing Your Extension

You can test your Copilot extension in a few environments:

  1. GitHub.com's Copilot chat
  2. VS Code's Copilot Chat
  3. Visual Studio's Copilot Chat

For these environments, follow these steps:

  1. In the GitHub Copilot chat, type "@" to see available extensions.

  2. Your extension should appear as, e.g. "@my-copilot-extension".

    Copilot chat on GitHub.com displaying the available Copilot extensions

    Copilot chat in Visual Studio Code displaying the available Copilot extensions

  3. Select your extension and ask a question or perform an operation.

  4. The Copilot extension will return a response of Welcome your_github_username! It looks like you asked the following question, "your_question". This is a GitHub Copilot extension template, so it's up to you to decide what you want to implement to answer prompts.

    Copilot chat responding,

It won't respond to your specific question as that functionality has not been implemented. This is where you can explore the preview SDK or integrate with a third-party service to provide more meaningful responses.

A Real-World Example

I've created a proof-of-concept (POC) Copilot extension for OpenSauced, where I work, that demonstrates these principles in action.

OpenSauced Copilot Extension Proof of Concept (POC)

A very rough POC of a GitHub Copilot Extension using OpenSauced that leverages the GitHub Copilot SDK Preview.

Better instructions eventually, but for now:

npm install
npm run dev
open http://localhost:3000

Use tunneling to expose your localhost to the internet via VS Code's Port forwarding, ngrok, or similar.

You'll need to create a GitHub app for the Copilot extension to work and use the URL from the from the tunneling above in your GitHub app configuration.

Check out https://github.blog/news-insights/product-news/introducing-github-copilot-extensions/ for more info.

This extension utilizes OpenSauced's StarSearch feature to provide open-source insights directly within Copilot.

Curious about StarSearch? ๐Ÿ‘‡

StarSearch: Creating Open Source Connections

Explore how StarSearch revolutionizes open source collaboration by connecting projects with top talent using AI-driven insights into GitHub events and contributor activities.

favicon opensauced.pizza

This POC showcases how you can leverage external APIs and services to create powerful, context-aware extensions that enhance the Copilot experience.

Grab the GitHub Copilot Extension Template

If you're interested in starting your own GitHub Copilot Extension, I've created a template to help you get started quickly:

GitHub logo nickytonline / copilot-extension-template

A GitHub Copilot Extension Template

A Template for Your First Copilot Extension

This is a template for creating your first Copilot extension. It's a simple Node.js app that uses Hono.

Getting Started

npm install
npm run dev
open http://localhost:3000

Use tunneling to expose your localhost to the internet via VS Code's Port forwarding, ngrok, or similar.

You'll need to create a GitHub app for the Copilot extension to work and use the URL from the from the tunneling above in your GitHub app configuration.

For a full breakdown of how to get started, check out Nick's Blog post, Creating a GitHub Copilot Extension: A Step-by-Step Guide.




This template provides a solid foundation for building your extension, incorporating best practices and a basic structure to build upon. The template uses the Hono.js Node.js server, but it's worth noting that Hono.js offers various adapters for different deployment environments. You can use adapters for platforms like Netlify, Vercel, Cloudflare Workers, and more. For more information on Hono.js and its adapters, check out their Getting Started guide.

I also encourage you to take a peek at the examples in the copilot-extensions organization on GitHub, including some examples in the preview SDK:

Copilot Extensions ยท GitHub

A toolkit for building integrations into GitHub Copilot. Access code samples, a debugging tool, an SDK, and user feedback repo. Docs: https://gh.io/builder-docs - Copilot Extensions

favicon github.com

These examples can provide inspiration and guidance as you develop your own extensions.

Take a deep dive into Copilot extensions and the preview SDK

For a deeper dive into the preview SDK and Copilot extensions in general, check out this video: "Let's build a Copilot extension!" Shout out to Gregor Martynus (@gr2m) and Francis Batac (@francisfuzz) for hanging with me!

This video provides valuable insights into the development process and can help you understand the nuances of working with Copilot extensions.

I also encourage you to check out the official GitHub Copilot documentation.

Wrapping up

Creating a GitHub Copilot extension opens up new possibilities for enhancing your development workflow. By following this guide, you can start building custom extensions that leverage the power of Copilot while adding your own unique functionality.

Remember, the Copilot Extensions feature is still in beta, so keep an eye out for updates and improvements as the ecosystem evolves. This is an exciting time to get involved and potentially shape the future of AI-assisted coding!

Until the next one!

Other places you can find me:

๐Ÿ—ž๏ธ One Tip a Week Newsletter
๐ŸŽฌ YouTube
๐ŸŽฌ Twitch
๐ŸŽฌ nickyt.live
๐Ÿ’ป GitHub
๐Ÿ‘พ My Discord
๐Ÿฆ Twitter/X
๐Ÿงต Threads
๐ŸŒ My Website

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .