
Copilot Sdk
- 488 installs
- 281 repo stars
- Updated April 25, 2026
- intellectronica/agent-skills
copilot-sdk is a Claude Code skill that guides developers implementing Copilot SDK features, authentication, and tool wiring so agents can embed Microsoft and GitHub Copilot capabilities into custom applications and auto
About
copilot-sdk is an AI & Agent Building skill from intellectronica/agent-skills for embedding Microsoft and GitHub Copilot SDK capabilities into custom software. The skill covers SDK feature setup, authentication configuration, and tool wiring so autonomous agents can invoke Copilot-backed completions, chat, or action endpoints from application code. Developers reach for copilot-sdk when building internal dev tools, IDE extensions, or agent orchestrators that must integrate official Copilot APIs rather than raw OpenAI calls alone. It targets SDK-level integration work spanning auth, session management, and tool registration across multiple implementation steps.
- Covers Copilot SDK setup and integration patterns
- Wires auth and programmatic copilot access
- Supports custom agent and app embedding
- Bridges LLM assistance into product workflows
- Targets builder-focused SDK implementation tasks
Copilot Sdk by the numbers
- 488 all-time installs (skills.sh)
- +9 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,796 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/intellectronica/agent-skills --skill copilot-sdkAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 488 |
|---|---|
| repo stars | ★ 281 |
| Last updated | April 25, 2026 |
| Repository | intellectronica/agent-skills ↗ |
How do you embed Copilot SDK in custom apps?
Implement Copilot SDK features, auth, and tool wiring so agents can embed Microsoft/GitHub copilot capabilities into custom apps and autonomous workflows.
Who is it for?
Developers building agent apps or dev tools that must embed official Microsoft or GitHub Copilot SDK features with authenticated tool access.
Skip if: Teams needing only generic OpenAI or Anthropic API calls without Microsoft Copilot SDK licensing or integration requirements.
When should I use this skill?
A developer asks to integrate Copilot SDK, wire Copilot auth, or register tools for a GitHub or Microsoft Copilot agent host.
What you get
Copilot SDK integration code, auth configuration, registered agent tools, and working Copilot-backed endpoints in the host application.
- SDK integration module
- Auth configuration
- Registered agent tools
Files
GitHub Copilot SDK
Overview
The GitHub Copilot SDK exposes the same Copilot CLI agent runtime over JSON-RPC, so apps can drive Copilot programmatically instead of building their own orchestration layer.
Status: Public preview SDKs: Node.js/TypeScript, Python, Go, .NET, Java Architecture: Application -> SDK client -> JSON-RPC -> Copilot CLI
How to use this skill
When helping with the Copilot SDK:
1. Prefer the official docs index and the language-specific README over memory. 2. Treat the top-level SDK README plus docs/ as the source of truth for shared behavior. 3. Call out preview status when stability or breaking changes matter. 4. Avoid hardcoding model lists when runtime discovery via listModels() is available. 5. Watch for stale guidance around permissions, lifecycle methods, and event names.
Current source of truth
Core SDK docs
- GitHub Copilot SDK repository
- Documentation index
- Getting started guide
- Setup guides
- Local CLI setup
- Bundled CLI setup
- Backend services setup
- Scaling and multi-tenancy
- Azure Managed Identity with BYOK
- Authentication
- BYOK
- Features index
- Image input
- Steering and queueing
- OpenTelemetry instrumentation
- Troubleshooting
- SDK/CLI compatibility
Language-specific docs
Copilot CLI and GitHub Docs
- About GitHub Copilot CLI
- Using GitHub Copilot CLI
- Custom agents configuration reference
- Enhancing agent mode with MCP
- Supported models
Recipes and examples
---
High-value facts
Authentication and prerequisites
- A GitHub Copilot subscription is required for normal SDK use.
- BYOK is supported and does not require GitHub Copilot authentication.
- Node.js, Python, and .NET bundle the Copilot CLI automatically.
- Go can use an installed CLI or embed/bundle one with the
go tool bundlerworkflow. - Java currently lives in
github/copilot-sdk-javaand expects the CLI to be installed separately. - Azure Managed Identity / Entra auth is supported as a documented BYOK pattern by passing short-lived bearer tokens from
DefaultAzureCredential.
Permissions
- The SDK uses a deny-by-default permission model.
- In practice, create/resume flows should provide an explicit permission handler such as:
- TypeScript:
approveAll - Python:
PermissionHandler.approve_all - Go:
copilot.PermissionHandler.ApproveAll - .NET:
PermissionHandler.ApproveAll - Java:
PermissionHandler.APPROVE_ALL
Session lifecycle
- Preferred cleanup method:
disconnect() - Deprecated cleanup method:
destroy() - To resume sessions reliably, provide your own
sessionIdwhen creating them. - BYOK provider configuration must be provided again when resuming because keys are not persisted.
Transport and deployment
- Default transport is stdio with an SDK-managed CLI process.
- You can connect to an external headless CLI server via
cliUrl. - Current external server docs use:
copilot --headless --port 4321Models
- Do not hardcode model support unless the user specifically needs a fixed list.
- Prefer
client.listModels()and the official supported-models page. reasoningEffortexists for models that support it.
---
Installation
| SDK | Install |
|---|---|
| Node.js / TypeScript | npm install @github/copilot-sdk |
| Python | pip install github-copilot-sdk |
| Go | go get github.com/github/copilot-sdk/go |
| .NET | dotnet add package GitHub.Copilot.SDK |
| Java | Maven/Gradle package com.github:copilot-sdk-java |
Setup and deployment choices
Pick the setup that matches the application shape:
- Local CLI - simplest path for personal tools and development.
- Bundled CLI - ship a CLI binary with your app for desktop/distributable tooling.
- Backend services - run the CLI in headless mode and connect with
cliUrl. - Scaling and multi-tenancy - shared CLI vs CLI-per-user, shared storage, and session locking.
- Azure Managed Identity - use BYOK with short-lived bearer tokens instead of static API keys when Azure auth is the real requirement.
Quick start pattern
Use the same mental model in every language:
1. Create/start the client. 2. Create a session with a permission handler. 3. Register event handlers before send() if you need streaming or progress. 4. Send with send() or sendAndWait(). 5. Wait for session.idle or the returned final message. 6. disconnect() the session and stop/dispose the client.
TypeScript example
import { CopilotClient, approveAll } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
model: "gpt-5",
streaming: true,
onPermissionRequest: approveAll,
});
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent ?? "");
});
await session.sendAndWait({ prompt: "What is 2+2?" });
await session.disconnect();
await client.stop();---
Core capabilities to remember
Client and session APIs
Common operations across SDKs:
- Client lifecycle:
start(),stop(),forceStop() - Session lifecycle:
createSession(),resumeSession(),disconnect() - Messaging:
send(),sendAndWait(),abort(),getMessages() - Discovery:
listModels(),listSessions(),getStatus()/ping()
Events and streaming
- Final assistant output arrives in
assistant.message. - Streaming text arrives in
assistant.message_delta. session.idleis the reliable "turn complete" signal.- The event system now includes reasoning, tool progress, permission, elicitation, sub-agent, and skill events.
See references/event-system.md.
Custom tools
- Node uses
defineTool(...)with Zod or raw JSON Schema. - Python uses
@define_toolwith Pydantic models. - Go prefers
DefineTool(...). - .NET uses
AIFunctionFactory.Create(...). - Overriding built-ins always requires explicit opt-in:
- TypeScript:
overridesBuiltInTool: true - Python:
overrides_built_in_tool=True - Go:
OverridesBuiltInTool = true - .NET:
AdditionalProperties["is_override"] = true - Custom tools can also opt into
skipPermission.
Custom agents, MCP, hooks, and skills
customAgentslets you define sub-agents per session.mcpServersattaches local or remote MCP servers.- Hooks provide control points such as
onPreToolUse,onPostToolUse,onUserPromptSubmitted, and lifecycle/error hooks. - Skills are loaded with
skillDirectories; disable selectively withdisabledSkills.
See references/cli-agents-mcp.md.
Attachments, commands, and interaction
- Sessions can send file, directory, and image attachments.
- Image input supports both file and blob attachments, and vision should be checked through model capabilities.
- In-flight messaging supports
mode: "immediate"for steering andmode: "enqueue"for queueing. - The SDK can register custom slash
commands. - Apps can answer user questions with
onUserInputRequest. - Rich UI prompts are available through elicitation handlers and
session.uiwhen the connected client supports them.
Telemetry and observability
- The SDK supports OpenTelemetry configuration through
TelemetryConfig. - Trace context propagation is built in, with Node using an explicit
onGetTraceContextcallback for outbound propagation.
Persistence and long-running work
- Use a stable
sessionIdfor resumable sessions. - Use
infiniteSessionsfor long-running workflows that may need compaction. - Session state is stored under
~/.copilot/session-state/unless configuration overrides it.
SDK vs. CLI-only features
- The SDK exposes programmatic surfaces for sessions, models, plans, mode switching, workspace files, custom agents, hooks, MCP, skills, and telemetry.
- Many terminal UX features remain CLI-only, such as most slash-command workflows, interactive pickers, and export/share commands.
- When translating a CLI workflow into app code, check the compatibility guide before assuming a slash command has an SDK equivalent.
---
Language conventions
| Concept | TypeScript | Python | Go | .NET | Java |
|---|---|---|---|---|---|
| Create session | createSession() | create_session() | CreateSession() | CreateSessionAsync() | createSession() |
| Resume session | resumeSession() | resume_session() | ResumeSession() | ResumeSessionAsync() | resumeSession() |
| Final content | event.data.content | event.data.content | *event.Data.Content | evt.Data.Content | event.getData().content() |
| Delta content | event.data.deltaContent | event.data.delta_content | *event.Data.DeltaContent | evt.Data.DeltaContent | event.getData().deltaContent() |
| Skills field | skillDirectories | skill_directories | SkillDirectories | SkillDirectories | setSkillDirectories(...) |
---
Common gotchas
- The SDK is public preview, so older examples drift quickly.
- Hardcoded model tables get stale; prefer runtime discovery.
destroy()still appears in older examples butdisconnect()is the current method.- A missing permission handler causes confusion fast; treat it as required for real sessions.
assistant.messageandassistant.message_deltauseevent.data.*, not top-levelevent.content.- Streaming/event subscriptions should be attached before
send(). - Session resumption without a caller-provided
sessionIdis awkward to operationalize.
---
Local reference files in this skill
references/working-examples.md- current starter examples, including tools and resume patternsreferences/event-system.md- event names, lifecycle, and language access patternsreferences/cli-agents-mcp.md- custom agents, skills, MCP, headless CLI, and config locationsreferences/troubleshooting.md- common failures, debug logging, auth, permissions, and transport issues
CLI, Custom Agents, Skills, and MCP
This file covers the overlap between the Copilot SDK and Copilot CLI configuration.
First distinction: SDK config vs CLI config
There are two related but different ways to shape Copilot behavior:
1. SDK session configuration - pass options like customAgents, mcpServers, skillDirectories, disabledSkills, and hooks directly when creating a session. 2. Copilot CLI configuration - use local config files, custom agent profiles, and the CLI UI/commands.
If you are building an application on the SDK, prefer SDK session configuration first and use CLI-level config when you want reusable local behavior outside your app.
Copilot CLI config locations
By default the CLI stores config under ~/.copilot/. This location can be overridden with COPILOT_HOME.
Common files and directories:
config.json- general CLI configmcp-config.json- MCP server definitionsagents/- user-level custom agent profilessession-state/- resumable sessions, plans, and session artifacts
Headless CLI server
The SDK can manage the CLI for you, or you can connect to a separately running server.
Start a headless CLI
copilot --headless --port 4321Connect from the SDK
import { CopilotClient, approveAll } from "@github/copilot-sdk";
const client = new CopilotClient({
cliUrl: "localhost:4321",
});
const session = await client.createSession({
onPermissionRequest: approveAll,
});Use this when you want persistent logs, resource sharing, or a separately managed runtime.
SDK custom agents
Use customAgents in session config to define sub-agents inside your app.
TypeScript example
const session = await client.createSession({
model: "gpt-4.1",
onPermissionRequest: approveAll,
customAgents: [
{
name: "researcher",
displayName: "Research Agent",
description: "Explores codebases and answers questions with read-only tools",
tools: ["grep", "glob", "view"],
prompt: "Analyze code and answer questions. Do not modify files.",
},
{
name: "editor",
displayName: "Editor Agent",
description: "Makes targeted code changes",
tools: ["view", "edit", "bash"],
prompt: "Make minimal, surgical changes only.",
},
],
});Useful fields include:
| Field | Purpose |
|---|---|
name | Stable identifier |
displayName | UI-friendly label |
description | Helps the runtime infer when to use the agent |
tools | Restricts tools for that agent |
prompt | Agent-specific instructions |
mcpServers | Agent-local MCP config |
infer | Controls auto-selection behavior |
CLI custom agent profiles
Copilot CLI also supports standalone agent profile files with YAML frontmatter and Markdown instructions.
Useful docs:
Common profile fields:
| Field | Purpose |
|---|---|
name | Optional display name |
description | Required description |
tools | Tool filter |
target | Environment target |
infer | Whether it can be auto-selected |
mcp-servers | Embedded MCP server definitions |
Useful tool aliases in CLI docs include:
executereadeditsearchagentwebtodo
Skills in the SDK
Skills are loaded from directories containing child folders with SKILL.md.
TypeScript example
const session = await client.createSession({
model: "gpt-4.1",
onPermissionRequest: approveAll,
skillDirectories: ["./skills"],
disabledSkills: ["deprecated-tooling"],
});Key points:
skillDirectoriespoints to the parent directory.- The runtime discovers immediate subdirectories containing
SKILL.md. disabledSkillsdisables by skill name or directory name.
MCP servers
Attach MCP servers with mcpServers in session config or define them in CLI config.
Supported server shapes:
| Type | Meaning |
|---|---|
local / stdio | Spawn a local subprocess |
http / sse | Connect to a remote MCP server |
Local MCP example
const session = await client.createSession({
model: "gpt-5",
onPermissionRequest: approveAll,
mcpServers: {
filesystem: {
type: "local",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
tools: ["*"],
},
},
});Remote MCP example
mcpServers: {
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
headers: {
Authorization: "Bearer ${TOKEN}",
},
tools: ["*"],
},
}Tool filtering rules
tools: ["*"]enables all tools from that server.tools: []disables all tools from that server.tools: ["tool-a", "tool-b"]exposes only those tools.
CLI-managed MCP
The CLI includes built-in support for configuring MCP servers interactively:
/mcp addThat writes to mcp-config.json under ~/.copilot/ unless COPILOT_HOME overrides it.
The CLI also ships with the GitHub MCP server already configured, which is why GitHub repository and workflow operations are often available out of the box in Copilot CLI sessions.
When to use what
| Need | Prefer |
|---|---|
| App-specific agents and tools | SDK session config |
| App-specific skill loading | SDK skillDirectories |
| Reusable local MCP setup for the CLI | ~/.copilot/mcp-config.json |
| Reusable custom agent profile outside app code | CLI agent profile files |
| Shared server or advanced debugging | headless CLI + cliUrl |
Gotchas
- SDK and CLI examples often look similar but are not the same API surface.
- MCP tools still go through permission and tool filtering.
- When a server starts but no tools appear, check
toolsfirst. - If sub-agent behavior is inconsistent, improve agent
descriptiontext and tool scoping.
Event System
The Copilot SDK emits a rich stream of session events. Event names and payloads have expanded since early preview builds, so older examples are often incomplete.
Event envelope
Every event includes the same top-level structure:
| Field | Meaning |
|---|---|
id | Event UUID |
timestamp | ISO 8601 timestamp |
parentId | Previous event in the chain, if any |
ephemeral | true for transient events that are not persisted |
type | Event name |
data | Event-specific payload |
Subscription patterns
TypeScript
session.on((event) => {
console.log(event.type, event.data);
});
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent ?? "");
});Python
from copilot.generated.session_events import SessionEventType
def handle(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
print(event.data.delta_content, end="", flush=True)
session.on(handle)Go
session.On(func(event copilot.SessionEvent) {
if event.Type == "assistant.message_delta" && event.Data.DeltaContent != nil {
fmt.Print(*event.Data.DeltaContent)
}
}).NET
session.On(evt =>
{
if (evt is AssistantMessageDeltaEvent delta)
{
Console.Write(delta.Data.DeltaContent);
}
});Java
session.on(AssistantMessageDeltaEvent.class, event -> {
System.out.print(event.getData().deltaContent());
});Typical turn flow
Exact order varies, but a normal streamed turn often looks like this:
1. user.message 2. assistant.turn_start 3. assistant.intent 4. assistant.reasoning_delta / assistant.reasoning 5. assistant.message_delta 6. tool.execution_start 7. tool.execution_partial_result / tool.execution_progress 8. tool.execution_complete 9. assistant.message 10. assistant.turn_end 11. session.idle 12. session.usage_info
For a completed task you may also see session.task_complete.
High-signal event types
Assistant events
| Event | Purpose | Key fields |
|---|---|---|
assistant.turn_start | Start of a model turn | turnId, interactionId |
assistant.intent | Human-readable current intent | intent |
assistant.reasoning | Full reasoning block | reasoningId, content |
assistant.reasoning_delta | Streaming reasoning chunk | reasoningId, deltaContent |
assistant.message | Final assistant message | messageId, content, toolRequests, reasoningText |
assistant.message_delta | Streaming response chunk | messageId, deltaContent |
assistant.turn_end | End of a model turn | turnId |
assistant.usage | Per-call token/cost metadata | model, inputTokens, outputTokens, cost, duration |
assistant.streaming_delta | Network-level stream progress | totalResponseSizeBytes |
Tool events
| Event | Purpose | Key fields |
|---|---|---|
tool.execution_start | Tool started | toolCallId, toolName, arguments |
tool.execution_partial_result | Streaming tool output | toolCallId, partialOutput |
tool.execution_progress | Progress message | toolCallId, progressMessage |
tool.execution_complete | Tool finished | toolCallId, success, result, error |
tool.user_requested | Tool execution was explicitly user-requested | tool call metadata |
Session lifecycle events
| Event | Purpose | Notes |
|---|---|---|
session.idle | Turn is done and session can accept more work | Best completion signal |
session.error | Session-level error | Check data for details |
session.compaction_start / session.compaction_complete | Infinite-session compaction lifecycle | Relevant for long-running sessions |
session.title_changed | Session title updated | Useful for UIs |
session.context_changed | Context window changed | Useful for monitoring |
session.usage_info | Aggregate session usage | Session-wide stats |
session.task_complete | Agent considers the task complete | Useful in managed workflows |
session.shutdown | Session is shutting down | Cleanup signal |
Permission, user input, and elicitation
| Event | Purpose |
|---|---|
permission.requested / permission.completed | Permission workflow around tool usage |
user_input.requested / user_input.completed | Agent is asking the user a question |
elicitation.requested / elicitation.completed | UI form/dialog requests |
Sub-agent and skill events
| Event | Purpose |
|---|---|
subagent.started / subagent.completed / subagent.failed | Sub-agent lifecycle |
subagent.selected / subagent.deselected | Agent selection state |
skill.invoked | A skill was loaded or used |
Other useful events
| Event | Purpose |
|---|---|
abort | In-flight work was cancelled |
system.message | Non-user/system message |
external_tool.requested / external_tool.completed | External tool bridging |
exit_plan_mode.requested / exit_plan_mode.completed | Plan-mode transition |
command.queued / command.completed | Custom slash command execution |
Streaming behavior
streaming: trueadds delta events such asassistant.message_delta.- Final events like
assistant.messagestill fire even when streaming is enabled. - Tool partial/progress events are often ephemeral and should be treated as live UI updates, not durable history.
Data access conventions by language
| Concept | TypeScript | Python | Go | .NET | Java |
|---|---|---|---|---|---|
| Event type | event.type | event.type or event.type.value depending on API surface | event.Type | event class or EventType | event class |
| Final text | event.data.content | event.data.content | *event.Data.Content | evt.Data.Content | event.getData().content() |
| Delta text | event.data.deltaContent | event.data.delta_content | *event.Data.DeltaContent | evt.Data.DeltaContent | event.getData().deltaContent() |
| Nil / null checks | optional chaining | Python None | pointer checks required | nullable strings | nullable accessors |
Practical guidance
1. Use assistant.message_delta for live rendering. 2. Use assistant.message for the final canonical text. 3. Use session.idle to know when a turn is done. 4. Use assistant.usage for per-call accounting and session.usage_info for session totals. 5. If you need every detail for audits or replay, store the full event stream from getMessages().
Troubleshooting
Fast checklist
1. Confirm which SDK and version you are using. 2. Confirm whether the session is using normal Copilot auth or BYOK. 3. Confirm whether the client is spawning the CLI or connecting via cliUrl. 4. Turn on debug logging. 5. Check permission handling before debugging anything else.
Common failures
| Problem | Likely cause | Fix |
|---|---|---|
| CLI not found | CLI missing from PATH, or the app did not point the SDK at its CLI binary | Install copilot, set cliPath / CLIPath, or use the Go bundler workflow for embedded CLI distribution |
| Not authenticated | No CLI login, no token, or wrong auth path | Run copilot auth login, pass githubToken, or configure BYOK |
| Tool requests never run | No permission handler, or handler denies them | Provide explicit permission handler |
| Streaming output missing | Handler attached after send() | Subscribe before sending |
| Content appears empty | Reading wrong field | Use event.data.content / deltaContent |
| Resume fails | Session ID not stable or not found | Create sessions with your own sessionId |
| BYOK resume fails | Provider config not re-supplied | Pass provider again on resume |
| MCP server starts but tools do not show up | tools filtered them out, or server init failed | Check tools, command, args, URL, and auth |
| Session cleanup confusion | Using deprecated lifecycle docs | Prefer disconnect(), not destroy() |
Permission handling
This is one of the most common sources of confusion.
- Treat permission handling as required for real application sessions.
- The SDK is deny-by-default for tool execution.
- A session can appear healthy while silently refusing the work you expected it to do.
Examples
TypeScript
const session = await client.createSession({
onPermissionRequest: approveAll,
});Python
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
)Authentication issues
Standard Copilot auth
Supported paths include:
- Stored Copilot CLI login
- Explicit
githubToken - Environment variables such as
COPILOT_GITHUB_TOKEN,GH_TOKEN,GITHUB_TOKEN
BYOK
Use a custom provider when supplying your own model credentials.
Important BYOK rules:
modelis required.- API keys are not persisted.
- Resume requires the provider config again.
type: "azure"andtype: "openai"are not interchangeable.- Azure Managed Identity / Entra is a documented pattern via short-lived
bearerToken/bearer_tokenvalues generated outside the SDK (for example withDefaultAzureCredential).
Debug logging
Enable SDK debug logging
TypeScript
const client = new CopilotClient({
logLevel: "debug",
});Python
client = CopilotClient({"log_level": "debug"})Go
client := copilot.NewClient(&copilot.ClientOptions{
LogLevel: "debug",
}).NET
var client = new CopilotClient(new CopilotClientOptions
{
LogLevel = "debug"
});CLI log directory
- Node.js and .NET can pass CLI args such as
--log-dir. - Python can pass CLI flags via
SubprocessConfig(..., cli_args=[...]). - Go and Java are more limited here; use a separately managed headless CLI if you need custom CLI logging.
Transport issues
The default transport is stdio. If you are debugging startup or sharing a server, switch mentally to the headless/TCP model.
Start a headless CLI
copilot --headless --port 4321Connect to it
const client = new CopilotClient({
cliUrl: "localhost:4321",
});If cliUrl is wrong, you will get connection failures that look unrelated to your app logic.
Event handling mistakes
assistant.messageis the final assistant output.assistant.message_deltais only a chunk.session.idleis the cleanest completion signal.- Usage lives in
assistant.usageandsession.usage_info, not just the final message.
Resume and persistence mistakes
- If you do not supply a meaningful
sessionId, operational resume is clumsy. disconnect()preserves session state on disk.deleteSession()removes it permanently.- Resumable state lives under
~/.copilot/session-state/unless config overrides it.
MCP debugging hints
1. Run the MCP server command independently first. 2. Check whether the server supports the expected transport. 3. Verify auth headers or environment variables. 4. Set tools: ["*"] while debugging. 5. Watch for permission denials after the tool appears.
Platform-specific notes
macOS
- GUI applications may not inherit your shell PATH; set
cliPathexplicitly if needed.
Windows
- Use explicit executable paths if PATH resolution is inconsistent.
Linux
- Verify execute permissions and shared-library dependencies if the CLI fails to launch.
Packaging note for Go
The Go SDK also supports bundling the CLI with go get -tool github.com/github/copilot-sdk/go/cmd/bundler followed by go tool bundler. If your Go app is distributed rather than run only on developer machines, do not assume a global copilot install is the only option.
Good default debugging sequence
1. Enable debug logging. 2. Swap to a trivial prompt like What is 2+2?. 3. Use approveAll temporarily. 4. Remove MCP and custom tools until the base session works. 5. Add features back one by one.
Working Examples
Before you start
- Use an explicit permission handler for real sessions.
- Register streaming/event handlers before
send(). - Prefer
disconnect()overdestroy(). - If you want resumable sessions, provide your own
sessionId.
Minimal chat examples
TypeScript / Node.js
import { CopilotClient, approveAll } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
model: "gpt-5",
streaming: true,
onPermissionRequest: approveAll,
});
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent ?? "");
});
await session.sendAndWait({ prompt: "What is 2+2?" });
await session.disconnect();
await client.stop();Python
import asyncio
from copilot import CopilotClient
from copilot.session import PermissionHandler
async def main():
async with CopilotClient() as client:
session = await client.create_session(
model="gpt-5",
streaming=True,
on_permission_request=PermissionHandler.approve_all,
)
def on_event(event):
if event.type.value == "assistant.message_delta":
print(event.data.delta_content or "", end="", flush=True)
session.on(on_event)
await session.send_and_wait("What is 2+2?")
await session.disconnect()
asyncio.run(main())Go
package main
import (
"context"
"fmt"
"log"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-5",
Streaming: true,
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
log.Fatal(err)
}
defer session.Disconnect()
session.On(func(event copilot.SessionEvent) {
if event.Type == "assistant.message_delta" && event.Data.DeltaContent != nil {
fmt.Print(*event.Data.DeltaContent)
}
})
if _, err := session.SendAndWait(ctx, copilot.MessageOptions{
Prompt: "What is 2+2?",
}); err != nil {
log.Fatal(err)
}
}.NET
using GitHub.Copilot.SDK;
await using var client = new CopilotClient();
await client.StartAsync();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-5",
Streaming = true,
OnPermissionRequest = PermissionHandler.ApproveAll,
});
session.On(evt =>
{
if (evt is AssistantMessageDeltaEvent delta)
{
Console.Write(delta.Data.DeltaContent);
}
});
await session.SendAndWaitAsync(new MessageOptions
{
Prompt = "What is 2+2?"
});Java
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.AssistantMessageDeltaEvent;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.PermissionHandler;
import com.github.copilot.sdk.json.SessionConfig;
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setModel("claude-sonnet-4.5")
.setStreaming(true)
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
session.on(AssistantMessageDeltaEvent.class, event ->
System.out.print(event.getData().deltaContent())
);
session.sendAndWait(new MessageOptions().setPrompt("What is 2+2?")).get();
}Custom tool examples
TypeScript
import { z } from "zod";
import { CopilotClient, approveAll, defineTool } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
model: "gpt-5",
onPermissionRequest: approveAll,
tools: [
defineTool("lookup_issue", {
description: "Fetch issue details from our tracker",
parameters: z.object({
id: z.string().describe("Issue identifier"),
}),
handler: async ({ id }) => ({ id, status: "open" }),
}),
],
});Python
from pydantic import BaseModel, Field
from copilot import define_tool
class IssueParams(BaseModel):
id: str = Field(description="Issue identifier")
@define_tool(description="Fetch issue details from our tracker")
async def lookup_issue(params: IssueParams) -> dict:
return {"id": params.id, "status": "open"}Go
type IssueParams struct {
ID string `json:"id" jsonschema:"Issue identifier"`
}
lookupIssue := copilot.DefineTool(
"lookup_issue",
"Fetch issue details from our tracker",
func(params IssueParams, inv copilot.ToolInvocation) (any, error) {
return map[string]string{"id": params.ID, "status": "open"}, nil
},
).NET
using Microsoft.Extensions.AI;
var lookupIssue = AIFunctionFactory.Create(
(string id) => new { Id = id, Status = "open" },
"lookup_issue",
"Fetch issue details from our tracker"
);Java
// Java SDK support lives in github/copilot-sdk-java.
// Follow its current documentation for tool wiring and event classes:
// https://github.com/github/copilot-sdk-javaResume example
TypeScript
const session = await client.createSession({
sessionId: "user-123-review-42",
model: "gpt-5.2-codex",
onPermissionRequest: approveAll,
});
await session.sendAndWait({ prompt: "Review the repository" });
await session.disconnect();
const resumed = await client.resumeSession("user-123-review-42", {
onPermissionRequest: approveAll,
});
await resumed.sendAndWait({ prompt: "Continue from where you left off" });Important resume note for BYOK
If the session uses a custom provider, pass that provider again when resuming. API keys and bearer tokens are not persisted to disk.
Small but important current rules
Overriding a built-in tool requires explicit opt-in
TypeScript
import { z } from "zod";
import { defineTool } from "@github/copilot-sdk";
defineTool("edit_file", {
description: "Custom editor",
parameters: z.object({ path: z.string(), content: z.string() }),
overridesBuiltInTool: true,
handler: async () => "ok",
});Python
from pydantic import BaseModel, Field
from copilot import define_tool
class EditFileParams(BaseModel):
path: str = Field(description="File path")
content: str = Field(description="New file content")
@define_tool(
name="edit_file",
description="Custom editor",
overrides_built_in_tool=True,
)
async def edit_file(params: EditFileParams) -> str:
return "ok"Go
type EditFileParams struct {
Path string `json:"path" jsonschema:"File path"`
Content string `json:"content" jsonschema:"New file content"`
}
editFile := copilot.DefineTool("edit_file", "Custom editor",
func(params EditFileParams, inv copilot.ToolInvocation) (any, error) {
return "ok", nil
})
editFile.OverridesBuiltInTool = true.NET
using Microsoft.Extensions.AI;
using System.Collections.Generic;
var editFile = AIFunctionFactory.Create(
(string path, string content) => "ok",
"edit_file",
"Custom editor",
new AIFunctionFactoryOptions
{
AdditionalProperties = new Dictionary<string, object?> { ["is_override"] = true }
}
);Other advanced patterns worth remembering
- Image input supports file and blob attachments; vision-capable models are required.
- Mid-turn steering uses
mode: "immediate"and queueing usesmode: "enqueue". - Telemetry uses
TelemetryConfigand can export to OTLP or file.
Related skills
FAQ
What does copilot-sdk help developers implement?
copilot-sdk helps developers implement Copilot SDK features, authentication, and tool wiring so custom applications and autonomous workflows can embed Microsoft and GitHub Copilot capabilities through official SDK APIs.
How is copilot-sdk different from generic LLM integration?
copilot-sdk focuses on official Microsoft and GitHub Copilot SDK auth, feature flags, and tool registration rather than calling standalone OpenAI or Anthropic REST endpoints without Copilot licensing.