# How to Use the Copilot SDK to Build Custom Copilot Features: A Complete Guide

> Build custom Copilot features using the GitHub Copilot SDK. Extend Copilot with agents, skills, and tools to create bespoke AI-driven functionality without complex orchestration code. Get started today.

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: how-to-guide
- Published: 2026-07-20

---

**Yes, the GitHub Copilot SDK is specifically designed for extending Copilot with custom agents, skills, tools, and hooks, enabling you to build bespoke AI-driven features without writing orchestration code.**

The `github/copilot-sdk` repository provides a multi-language toolkit that lets you programmatically control Copilot's behavior. By defining **custom agents** with specialized prompts and tool permissions, you can create domain-specific AI assistants that run inside your existing applications while the SDK handles all runtime delegation and lifecycle management.

## Understanding the Copilot SDK Architecture

The SDK communicates with the Copilot CLI through **JSON-RPC** in server mode. When you initialize a `CopilotClient`, it manages a local Copilot CLI process and establishes a persistent connection for command execution.

According to the source architecture outlined in [`README.md`](https://github.com/github/copilot-sdk/blob/main/README.md), the runtime operates through a **delegation pattern**:

1. Your application creates a session with a list of `customAgents`
2. The runtime analyzes the user's intent against each agent's `description` field
3. The best-fit **sub-agent** is automatically selected and executed in an isolated context
4. Lifecycle events stream back to your application via the session callback

This architecture eliminates the need to write complex agent orchestration logic, as the Copilot runtime handles intent matching and sub-agent coordination automatically.

## Defining Custom Agents for Specialized Tasks

Custom agents are configured during session creation through the `customAgents` array parameter. Each agent definition in [`docs/features/custom-agents.md`](https://github.com/github/copilot-sdk/blob/main/docs/features/custom-agents.md) requires specific fields that control its behavior and capabilities.

**Key configuration properties:**
- **`name`**: Machine-readable identifier for the agent
- **`displayName`**: Human-readable label shown in UI
- **`description`**: Natural language description used by the runtime for intent matching
- **`tools`**: Array of permitted tools (e.g., `["grep", "glob", "view"]`, `["view", "edit", "bash"]`)
- **`prompt`**: System instructions defining the agent's personality and constraints

The runtime uses the `description` field (and optional `infer` flag) to determine which agent should handle a specific user request. You can also override the base model and reasoning effort on a per-agent basis, or attach dedicated MCP (Model Context Protocol) servers for specialized data sources.

## Implementing Custom Copilot Features in Multiple Languages

The SDK supports **Node.js/TypeScript, Python, Go, .NET, Java, and Rust**, allowing you to implement custom features in your preferred language. Below are complete examples creating a research agent (read-only) and an editor agent (write-capable).

### Node.js / TypeScript Implementation

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session = await client.createSession({
  model: "gpt-5.4",
  customAgents: [
    {
      name: "researcher",
      displayName: "Research Agent",
      description: "Explores codebases and answers questions using read‑only tools",
      tools: ["grep", "glob", "view"],
      prompt: "You are a research assistant. Analyze code and answer questions. Do not modify any files."
    },
    {
      name: "editor",
      displayName: "Editor Agent",
      description: "Makes targeted code changes",
      tools: ["view", "edit", "bash"],
      prompt: "You are a code editor. Make minimal, surgical changes to files as requested."
    }
  ],
  onPermissionRequest: async () => ({ kind: "approve-once" })
});

```

### Python Implementation

```python
from copilot import CopilotClient, PermissionDecisionApproveOnce

client = CopilotClient()
await client.start()

session = await client.create_session(
    model="gpt-5.4",
    custom_agents=[
        {
            "name": "researcher",
            "display_name": "Research Agent",
            "description": "Explores codebases and answers questions using read‑only tools",
            "tools": ["grep", "glob", "view"],
            "prompt": "You are a research assistant. Analyze code and answer questions. Do not modify any files."
        },
        {
            "name": "editor",
            "display_name": "Editor Agent",
            "description": "Makes targeted code changes",
            "tools": ["view", "edit", "bash"],
            "prompt": "You are a code editor. Make minimal, surgical changes to files as requested."
        }
    ],
    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce()
)

```

### Go Implementation

```go
ctx := context.Background()
client := copilot.NewClient(nil)
client.Start(ctx)

session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    Model: "gpt-5.4",
    CustomAgents: []copilot.CustomAgentConfig{
        {
            Name:        "researcher",
            DisplayName: "Research Agent",
            Description: "Explores codebases and answers questions using read‑only tools",
            Tools:       []string{"grep", "glob", "view"},
            Prompt:      "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
        },
        {
            Name:        "editor",
            DisplayName: "Editor Agent",
            Description: "Makes targeted code changes",
            Tools:       []string{"view", "edit", "bash"},
            Prompt:      "You are a code editor. Make minimal, surgical changes to files as requested.",
        },
    },
    OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
        return &rpc.PermissionDecisionApproveOnce{}, nil
    },
})

```

### .NET (C#) Implementation

```csharp
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig {
    Model = "gpt-5.4",
    CustomAgents = new List<CustomAgentConfig> {
        new() {
            Name = "researcher",
            DisplayName = "Research Agent",
            Description = "Explores codebases and answers questions using read‑only tools",
            Tools = new List<string> { "grep", "glob", "view" },
            Prompt = "You are a research assistant. Analyze code and answer questions. Do not modify any files."
        },
        new() {
            Name = "editor",
            DisplayName: "Editor Agent",
            Description: "Makes targeted code changes",
            Tools = new List<string> { "view", "edit", "bash" },
            Prompt = "You are a code editor. Make minimal, surgical changes to files as requested."
        }
    },
    OnPermissionRequest = (req, inv) => Task.FromResult(PermissionDecision.ApproveOnce())
});

```

### Java Implementation

```java
var session = client.createSession(
    new SessionConfig()
        .setModel("gpt-5.4")
        .setCustomAgents(List.of(
            new CustomAgentConfig()
                .setName("researcher")
                .setDisplayName("Research Agent")
                .setDescription("Explores codebases and answers questions using read‑only tools")
                .setTools(List.of("grep", "glob", "view"))
                .setPrompt("You are a research assistant. Analyze code and answer questions. Do not modify any files."),
            new CustomAgentConfig()
                .setName("editor")
                .setDisplayName("Editor Agent")
                .setDescription("Makes targeted code changes")
                .setTools(List.of("view", "edit", "bash"))
                .setPrompt("You are a code editor. Make minimal, surgical changes to files as requested.")
        ))
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();

```

## Handling Sub-Agent Lifecycle Events

The SDK streams real-time events during sub-agent execution, allowing your application to update UI or trigger side effects. As documented in [`docs/features/custom-agents.md`](https://github.com/github/copilot-sdk/blob/main/docs/features/custom-agents.md), the session object emits events for every state transition.

**Available event types:**
- `subagent.started`: Fired when a sub-agent begins execution
- `subagent.completed`: Fired when a sub-agent finishes successfully
- `subagent.failed`: Fired when execution encounters an error
- `subagent.selected/deselected`: Fired when the runtime changes active agents

Listen to these events using the session's event handler:

```typescript
session.on(event => {
  if (event.type === "subagent.started") {
    console.log(`▶ Sub‑agent started: ${event.data.agentDisplayName}`);
  }
  // Handle completed, failed, selected, deselected similarly...
});

```

## Advanced Customization Options

Beyond custom agents, the Copilot SDK provides additional extension points for sophisticated integrations:

- **Hooks**: Attach custom logic at `pre-tool-use` or `post-tool-use` phases by implementing handlers described in [`docs/hooks/README.md`](https://github.com/github/copilot-sdk/blob/main/docs/hooks/README.md)
- **Skills**: Preload domain-specific knowledge into agent context using the patterns shown in [`docs/features/skills.md`](https://github.com/github/copilot-sdk/blob/main/docs/features/skills.md)
- **BYOK Authentication**: Deploy custom features without requiring end-users to have GitHub Copilot subscriptions using the Bring-Your-Own-Key flow documented in [`docs/auth/byok.md`](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md)
- **MCP Servers**: Attach specialized Model Context Protocol servers to individual agents for access to proprietary data sources

## Summary

- The **Copilot SDK** enables building custom Copilot features through a JSON-RPC client architecture that manages the Copilot CLI process
- **Custom agents** are defined via the `customAgents` array in `createSession()`, specifying permitted tools, prompts, and descriptions for runtime matching
- The SDK automatically handles **intent detection** and delegates to the appropriate sub-agent based on natural language descriptions
- **Lifecycle events** (`subagent.started`, `subagent.completed`, etc.) stream back to your application for real-time UI updates
- Full support for **Node.js/TypeScript, Python, Go, .NET, Java, and Rust** with identical API patterns across languages
- Advanced features include **hooks**, **skills**, **BYOK authentication**, and per-agent **MCP server** attachment

## Frequently Asked Questions

### Can I use the Copilot SDK without a GitHub subscription?

Yes. The SDK supports **Bring-Your-Own-Key (BYOK)** authentication as documented in [`docs/auth/byok.md`](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md). This allows you to build and deploy custom Copilot features that use your own API keys rather than requiring end-users to have GitHub Copilot subscriptions.

### What programming languages does the Copilot SDK support?

The SDK officially supports **Node.js/TypeScript, Python, Go, .NET (C#), Java, and Rust**. All language implementations provide the same core functionality including custom agent definition, session management, and event streaming, though naming conventions follow each language's idioms (e.g., `custom_agents` in Python vs `CustomAgents` in C#).

### How does the runtime choose which custom agent to use?

The Copilot runtime analyzes the user's request against each agent's **`description`** field using natural language understanding. Agents can optionally set an `infer` flag to control matching behavior. The runtime selects the best-fit sub-agent automatically and executes it in isolation, streaming results back to the parent session.

### Can I restrict which tools a custom agent can access?

Absolutely. Each custom agent configuration includes a **`tools`** array that explicitly whitelist which Copilot tools the agent may invoke. For example, a research agent might only receive `["grep", "glob", "view"]` while an editor agent gets `["view", "edit", "bash"]`, creating security boundaries between different agent capabilities.