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.
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.
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.
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:
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
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.consttokenForUser=c.req.header("X-GitHub-Token")??"";constbody=awaitc.req.text();constsignature=c.req.header("github-public-key-signature")??"";constkeyID=c.req.header("github-public-key-identifier")??"";const{isValidRequest,payload}=awaitverifyAndParseRequest(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");returnstream(c,async (stream)=>{try{// Let GitHub Copilot know we are doing somethingawaitstream.write(createAckEvent());constoctokit=newOctokit({auth:tokenForUser});constuser=awaitoctokit.request("GET /user");constprompt=getUserMessage(payload);awaitstream.write(createTextEvent(`Welcome ${user.data.login}! It looks like you asked the following question, "${prompt}". `));awaitstream.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."));awaitstream.write(createDoneEvent());}catch (error){awaitstream.write(createErrorsEvent([{type:"agent",message:errorinstanceofError?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:
constport=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.
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.
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.
Explore how StarSearch revolutionizes open source collaboration by connecting projects with top talent using AI-driven insights into GitHub events and contributor activities.
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:
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.
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:
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
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.
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!