Advanced

Copilot Extensions

Extend GitHub Copilot's capabilities by building custom extensions that bring third-party tools, internal APIs, and domain-specific knowledge directly into Copilot Chat.

What Are Copilot Extensions?

Copilot Extensions allow you to integrate external tools and services directly into the Copilot Chat experience. Instead of switching between Copilot and other tools, you can invoke extensions using @mentions right inside Copilot Chat — whether in VS Code, GitHub.com, or any other surface where Copilot Chat is available.

For example, you might type @sentry what are the top 5 unresolved errors in production? and get a response sourced directly from your Sentry account, formatted and presented within the chat interface. The extension handles authentication, API calls, and response formatting behind the scenes.

Extensions solve a real problem: developers use dozens of tools daily (monitoring, databases, project management, documentation), and context-switching between them breaks flow. Extensions bring those tools to where you already are — inside your editor talking to Copilot.

💡
Good to know: Copilot Extensions are available on GitHub Copilot Business and Enterprise plans. Individual plan users can use publicly available extensions from the GitHub Marketplace but building custom private extensions requires a Business or Enterprise subscription.

Types of Extensions: GitHub Apps vs. Skillsets

There are two architectural approaches to building Copilot Extensions, each suited to different use cases.

GitHub Apps-Based Extensions (Agents)

These are full-featured extensions backed by a server you control. When a user mentions your extension in Copilot Chat, GitHub routes the message to your server endpoint. Your server processes the request, calls external APIs, runs logic, and returns a response. You have complete control over the interaction.

  • Full control — Your server receives the raw user message and chat history, and you decide exactly what to do with it
  • Multi-step reasoning — You can make multiple API calls, run calculations, and compose complex responses
  • State management — Your server can maintain session state across multiple messages in a conversation
  • Custom AI models — You can use any LLM you want on your backend, not just the one powering Copilot

Skillsets (Lightweight Extensions)

Skillsets are a simpler, declarative approach. Instead of running a server, you define a set of API endpoints and descriptions in a JSON manifest. Copilot's own model decides when to call your APIs based on the user's question, handles the API calls itself, and incorporates the results into its response.

  • No server needed — Just define your existing API endpoints and let Copilot call them
  • Faster to build — A skillset can be configured in minutes if you already have APIs
  • Copilot handles reasoning — The model decides when and how to use your APIs
  • Limited control — You cannot customize the response format or add multi-step logic
FeatureGitHub Apps (Agents)Skillsets
Server requiredYesNo (uses existing APIs)
Setup complexityHigh — build and host a serviceLow — JSON configuration
Response controlFull (you craft the response)Limited (Copilot formats it)
Multi-step logicYesNo
Custom AI modelsYesNo (uses Copilot's model)
Best forComplex integrations, custom UXExposing existing APIs quickly

How Extensions Work: The @mention Flow

Understanding the request flow helps you design extensions effectively. Here is what happens when a user types @your-extension how do I fix this bug?:

  1. User sends message — The user types a message mentioning your extension in Copilot Chat
  2. GitHub routes the request — GitHub identifies the @mention, looks up your extension's registered endpoint, and forwards the message along with conversation context
  3. Your server receives it — The request arrives as an HTTP POST to your endpoint with the user's message, conversation history, and a verification token
  4. You process and respond — Your server calls APIs, runs logic, and returns a streamed response in Server-Sent Events (SSE) format
  5. Copilot displays the response — The response appears inline in the chat, formatted as Markdown with optional references and suggestions
JSON
// Example incoming request payload to your extension endpoint
{
  "messages": [
    {
      "role": "user",
      "content": "@my-db-extension show me the schema for the users table",
      "copilot_references": [
        {
          "type": "github.repository",
          "data": {
            "owner": "my-org",
            "name": "my-app"
          }
        }
      ]
    }
  ],
  "copilot_thread_id": "abc-123-thread",
  "agent": "my-db-extension"
}

Building Your First Copilot Extension

Let us build a practical extension: a database schema explorer that lets developers query their database structure directly from Copilot Chat. When someone types @db-explorer describe the orders table, the extension queries the database metadata and returns the schema.

Step 1: Create the Server

JavaScript
// server.js - Copilot Extension endpoint
const express = require('express');
const { Pool } = require('pg');
const { createNodeMiddleware } = require('@octokit/webhooks');

const app = express();
app.use(express.json());

const db = new Pool({ connectionString: process.env.DATABASE_URL });

// Verify the request comes from GitHub
const verifyGitHubSignature = (req, res, next) => {
  const signature = req.headers['github-public-key-signature'];
  // In production, verify this signature using GitHub's public key
  // See: https://docs.github.com/en/copilot/building-copilot-extensions
  next();
};

app.post('/agent', verifyGitHubSignature, async (req, res) => {
  const { messages } = req.body;
  const userMessage = messages[messages.length - 1].content;

  // Set headers for Server-Sent Events streaming
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  try {
    // Parse the user's intent
    const tableName = extractTableName(userMessage);

    if (!tableName) {
      sendSSE(res, "I can help you explore your database schema. Try asking:\n" +
        "- `describe the users table`\n" +
        "- `list all tables`\n" +
        "- `show indexes on orders`");
      res.end();
      return;
    }

    // Query the database schema
    const columns = await db.query(`
      SELECT column_name, data_type, is_nullable, column_default
      FROM information_schema.columns
      WHERE table_name = $1
      ORDER BY ordinal_position
    `, [tableName]);

    if (columns.rows.length === 0) {
      sendSSE(res, `No table found with name \`${tableName}\`. Use "list all tables" to see available tables.`);
    } else {
      let response = `## Schema: \`${tableName}\`\n\n`;
      response += '| Column | Type | Nullable | Default |\n';
      response += '|--------|------|----------|---------|\n';
      for (const col of columns.rows) {
        response += `| ${col.column_name} | ${col.data_type} | ${col.is_nullable} | ${col.column_default || '-'} |\n`;
      }
      sendSSE(res, response);
    }
  } catch (error) {
    sendSSE(res, `Error querying schema: ${error.message}`);
  }

  res.end();
});

function sendSSE(res, content) {
  const event = {
    choices: [{
      delta: { content, role: "assistant" },
      index: 0
    }]
  };
  res.write(`data: ${JSON.stringify(event)}\n\n`);
  res.write('data: [DONE]\n\n');
}

function extractTableName(message) {
  const match = message.match(/(?:describe|show|schema for|columns in)\s+(?:the\s+)?(\w+)\s*(?:table)?/i);
  return match ? match[1] : null;
}

app.listen(3000, () => console.log('Extension running on port 3000'));

Step 2: Register as a GitHub App

  1. Go to your GitHub organization settings and navigate to Developer settings > GitHub Apps > New GitHub App
  2. Set the Copilot agent endpoint URL to your server's /agent route (e.g., https://your-server.com/agent)
  3. Under permissions, enable Copilot Chat access
  4. Set the extension name (this becomes the @mention handle, e.g., @db-explorer)
  5. Write a clear description — Copilot shows this to users when they type @
  6. Install the app on your organization

Step 3: Test It

Open Copilot Chat in VS Code or on GitHub.com and type @db-explorer describe the users table. GitHub routes the request to your server, which queries the database and returns the schema formatted as a Markdown table.

Key takeaway: Start with a narrow, well-defined use case for your first extension. A single-purpose extension (like schema lookup) is easier to build, test, and maintain than a Swiss Army knife that tries to do everything.

Building a Skillset Extension

If you already have REST APIs, a skillset extension is the fastest path. You define your endpoints declaratively and Copilot handles the rest. Here is an example skillset definition for a project management tool:

JSON
{
  "name": "project-tracker",
  "description": "Query and manage project tasks and sprints",
  "skills": [
    {
      "name": "get_current_sprint",
      "description": "Gets the current active sprint with its tasks and progress",
      "endpoint": {
        "url": "https://api.mytracker.com/v1/sprints/current",
        "method": "GET",
        "headers": {
          "Authorization": "Bearer {{secrets.TRACKER_API_KEY}}"
        }
      },
      "parameters": []
    },
    {
      "name": "search_tasks",
      "description": "Search for tasks by keyword, status, or assignee",
      "endpoint": {
        "url": "https://api.mytracker.com/v1/tasks/search",
        "method": "GET",
        "headers": {
          "Authorization": "Bearer {{secrets.TRACKER_API_KEY}}"
        }
      },
      "parameters": [
        {
          "name": "query",
          "type": "string",
          "description": "Search keyword",
          "required": true
        },
        {
          "name": "status",
          "type": "string",
          "enum": ["open", "in-progress", "review", "done"],
          "description": "Filter by task status",
          "required": false
        },
        {
          "name": "assignee",
          "type": "string",
          "description": "GitHub username of the assignee",
          "required": false
        }
      ]
    },
    {
      "name": "get_my_tasks",
      "description": "Gets all tasks assigned to the current user",
      "endpoint": {
        "url": "https://api.mytracker.com/v1/tasks/mine",
        "method": "GET",
        "headers": {
          "Authorization": "Bearer {{secrets.TRACKER_API_KEY}}"
        }
      },
      "parameters": []
    }
  ]
}

With this skillset, a developer can type @project-tracker what are my open tasks? and Copilot will automatically call the get_my_tasks endpoint, receive the JSON response, and format it into a readable answer. No server code needed on your end.

Publishing and Sharing Extensions

Once your extension is working, you have several distribution options depending on your audience.

  • Organization-only (private) — Install the GitHub App only on your organization. Only members of your org can use the extension via @mention. This is ideal for internal tools like database explorers, deployment helpers, or internal documentation search.
  • GitHub Marketplace (public) — List your extension on the GitHub Marketplace for any Copilot user to install. You will need to go through GitHub's review process, provide documentation, and meet their quality guidelines.
  • Direct installation link — Share the GitHub App installation URL with specific organizations or users. Useful for B2B SaaS products that want to offer a Copilot integration to their customers.

Extensions Available Today

Several third-party extensions are already available on the GitHub Marketplace. Here are notable ones that demonstrate the range of possibilities:

ExtensionWhat It DoesExample Usage
@dockerGenerate Dockerfiles, troubleshoot container issues@docker create a Dockerfile for my Node.js app
@sentryQuery errors, find root causes, link to stack traces@sentry what caused the spike in 500 errors today?
@datastaxQuery and manage Cassandra/Astra databases@datastax show the schema for my keyspace
@mermaidGenerate diagrams from natural language descriptions@mermaid draw a sequence diagram for user login flow
@LaunchDarklyManage feature flags from chat@launchdarkly what flags are enabled in production?
💡
Good to know: The extensions ecosystem is growing rapidly. Check the GitHub Marketplace regularly for new Copilot extensions that might save your team significant context-switching time.