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 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.
-
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.
-
Install the preview SDK for Copilot extensions and Octokit's core package:
npm install @copilot-extensions/preview-sdk @octokit/core
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, }, ); //
… -
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:
-
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) });
-
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; }
-
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:
-
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.
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,
});
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
- Enter a GitHub App name, e.g. my copilot extension
- Enter a URL for the homepage. This can be the same as the test URL for now.
-
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.
-
Disable webhooks if they're enabled.
-
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.
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.
-
Expand the Account permissions sections and set the Copilot Chat permission to read-only. The default is No access.
-
Click Save changes. Don't be alarmed by the Are you sure you want to update permissions? message.
Copilot settings
- Set the App Type to Agent. It's set to Disabled by default.
-
Set the URL to the root of the public URL you exposed via tunneling/port forwarding.
Click Save.
Congratulations! You've configured your first Copilot extension!
Install Your Copilot Extension
Before we can use the extension, it has to be installed.
-
Navigate to your GitHub apps in your developer settings.
Click the Edit button to edit the app.
-
Go to the Install App section of the GitHub app's settings.
Click the Install button to install the application
-
You're brought to an intermediary page to confirm the installation of the GitHub app. Click the Install button.
-
Your Copilot extension is installed for your GitHub account.
Testing Your Extension
You can test your Copilot extension in a few environments:
- GitHub.com's Copilot chat
- VS Code's Copilot Chat
- Visual Studio's Copilot Chat
For these environments, follow these steps:
In the GitHub Copilot chat, type "@" to see available extensions.
-
Your extension should appear as, e.g. "@my-copilot-extension".
Select your extension and ask a question or perform an operation.
-
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.
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? 👇
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:
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.
Installation
Run the following commands to install and start the application locally
npm install
npm run dev
open http://localhost:3000
Development Environment
To get up and running with your development environment, see the Development 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:
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
Top comments (1)
If you'd like to see the preview SDK support other languages, I encourage you to open an issue, or even fork and try yourself! I'm going to see if I can port it to Swift.