SDK GitHub Copilot
Construisez des applications qui interagissent programmatiquement avec GitHub Copilot. Le SDK encapsule la CLI Copilot via JSON-RPC, fournissant la gestion des sessions, les outils personnalisés, les hooks, l'intégration de serveur MCP et le streaming sur Node.js, Python, Go et .NET.
Prérequis
- GitHub Copilot CLI installée et authentifiée (
copilot --version) - Abonnement GitHub Copilot (Individual, Business ou Enterprise) — non requis pour BYOK
- Runtime : Node.js 18+ / Python 3.8+ / Go 1.21+ / .NET 8.0+
Installation
| Langage | Package | Installation |
|---|---|---|
| Node.js | @github/copilot-sdk |
npm install @github/copilot-sdk |
| Python | github-copilot-sdk |
pip install github-copilot-sdk |
| Go | github.com/github/copilot-sdk/go |
go get github.com/github/copilot-sdk/go |
| .NET | GitHub.Copilot.SDK |
dotnet add package GitHub.Copilot.SDK |
Architecture
Le SDK communique avec la CLI Copilot via JSON-RPC sur stdio (défaut) ou TCP. La CLI gère les appels de modèle, l'exécution des outils, l'état de la session et le cycle de vie du serveur MCP.
Votre App → Client SDK → [stdio/TCP] → CLI Copilot → Fournisseur de modèle
↕
Serveurs MCP
Modes de transport :
| Mode | Description | Cas d'usage |
|---|---|---|
| Stdio (défaut) | CLI en tant que sous-processus via pipes | Dev local, processus unique |
| TCP | CLI en tant que serveur réseau | Multi-client, services backend |
Motif fondamental : Client → Session → Message
Toute utilisation du SDK suit ce motif : créer un client, créer une session, envoyer des messages.
Node.js / TypeScript
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({ model: "gpt-4.1" });
const response = await session.sendAndWait({ prompt: "What is 2 + 2?" });
console.log(response?.data.content);
await client.stop();
Python
import asyncio
from copilot import CopilotClient
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session({"model": "gpt-4.1"})
response = await session.send_and_wait({"prompt": "What is 2 + 2?"})
print(response.data.content)
await client.stop()
asyncio.run(main())
Go
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil { log.Fatal(err) }
defer client.Stop()
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-4.1"})
response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "What is 2 + 2?"})
fmt.Println(*response.Data.Content)
.NET
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-4.1" });
var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "What is 2 + 2?" });
Console.WriteLine(response?.Data.Content);
Réponses en streaming
Activez la sortie en temps réel en définissant streaming: true et en vous abonnant aux événements delta.
Node.js
const session = await client.createSession({ model: "gpt-4.1", streaming: true });
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent);
});
session.on("session.idle", () => console.log());
await session.sendAndWait({ prompt: "Tell me a joke" });
Python
from copilot.generated.session_events import SessionEventType
session = await client.create_session({"model": "gpt-4.1", "streaming": True})
def handle_event(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
sys.stdout.write(event.data.delta_content)
sys.stdout.flush()
if event.type == SessionEventType.SESSION_IDLE:
print()
session.on(handle_event)
await session.send_and_wait({"prompt": "Tell me a joke"})
Abonnement aux événements
| Méthode | Description |
|---|---|
on(handler) |
S'abonner à tous les événements ; retourne une fonction de désabonnement |
on(eventType, handler) |
S'abonner à un type d'événement spécifique (Node.js uniquement) |
Appelez la fonction retournée pour vous désabonner. En .NET, appelez .Dispose() sur le disposable retourné.
Outils personnalisés
Définissez des outils que Copilot peut appeler pour étendre ses capacités.
Node.js
import { CopilotClient, defineTool } from "@github/copilot-sdk";
const getWeather = defineTool("get_weather", {
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: { city: { type: "string", description: "The city name" } },
required: ["city"],
},
handler: async ({ city }) => ({ city, temperature: "72°F", condition: "sunny" }),
});
const session = await client.createSession({
model: "gpt-4.1",
tools: [getWeather],
});
Python
from copilot.tools import define_tool
from pydantic import BaseModel, Field
class GetWeatherParams(BaseModel):
city: str = Field(description="The city name")
@define_tool(description="Get the current weather for a city")
async def get_weather(params: GetWeatherParams) -> dict:
return {"city": params.city, "temperature": "72°F", "condition": "sunny"}
session = await client.create_session({"model": "gpt-4.1", "tools": [get_weather]})
Go
type WeatherParams struct {
City string `json:"city" jsonschema:"The city name"`
}
getWeather := copilot.DefineTool("get_weather", "Get weather for a city",
func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {
return WeatherResult{City: params.City, Temperature: "72°F"}, nil
},
)
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-4.1",
Tools: []copilot.Tool{getWeather},
})
.NET
using Microsoft.Extensions.AI;
using System.ComponentModel;
var getWeather = AIFunctionFactory.Create(
([Description("The city name")] string city) => new { city, temperature = "72°F" },
"get_weather", "Get the current weather for a city");
await using var session = await client.CreateSessionAsync(new SessionConfig {
Model = "gpt-4.1", Tools = [getWeather],
});
Exigences des outils
- Le gestionnaire doit retourner des données sérialisables en JSON (pas
undefined) - Les paramètres doivent suivre le format JSON Schema
- La description de l'outil doit indiquer clairement quand il doit être utilisé
Hooks
Interceptez et personnalisez le comportement de la session à des points clés du cycle de vie.
| Hook | Déclencheur | Cas d'usage |
|---|---|---|
onPreToolUse |
Avant l'exécution de l'outil | Contrôle des permissions, modification des arguments |
onPostToolUse |
Après l'exécution de l'outil | Transformation des résultats, journalisation, masquage |
onUserPromptSubmitted |
L'utilisateur envoie un message | Modification du prompt, filtrage, injection de contexte |
onSessionStart |
La session commence (nouvelle ou reprise) | Ajouter du contexte, configurer la session |
onSessionEnd |
La session se termine | Nettoyage, analytiques, métriques |
onErrorOccurred |
Une erreur se produit | Gestion d'erreur personnalisée, logique de retry, surveillance |
Hook Pre-Tool Use
Contrôlez les permissions des outils, modifiez les arguments ou injectez du contexte avant l'exécution de l'outil.
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
if (["shell", "bash"].includes(input.toolName)) {
return { permissionDecision: "deny", permissionDecisionReason: "Shell access not permitted" };
}
return { permissionDecision: "allow" };
},
},
});
Champs d'entrée : timestamp, cwd, toolName, toolArgs
Champs de sortie :
| Champ | Type | Description |
|---|---|---|
permissionDecision |
"allow" | "deny" | "ask" |
S'il faut autoriser l'appel de l'outil |
permissionDecisionReason |
string | Explication pour deny/ask |
modifiedArgs |
object | Arguments modifiés à transmettre |
additionalContext |
string | Contexte supplémentaire pour la conversation |
suppressOutput |
boolean | Masquer la sortie de l'outil de la conversation |
Hook Post-Tool Use
Transformez les résultats, masquez les données sensibles ou journalisez l'activité des outils après l'exécution.
hooks: {
onPostToolUse: async (input) => {
// Redact sensitive data from results
if (typeof input.toolResult === "string") {
let redacted = input.toolResult;
for (const pattern of SENSITIVE_PATTERNS) {
redacted = redacted.replace(pattern, "[REDACTED]");
}
if (redacted !== input.toolResult) {
return { modifiedResult: redacted };
}
}
return null; // Pass through unchanged
},
}
Champs de sortie : modifiedResult, additionalContext, suppressOutput
Hook User Prompt Submitted
Modifiez ou améliorez les prompts utilisateur avant le traitement. Utile pour les templates de prompt, l'injection de contexte et la validation d'entrée.
hooks: {
onUserPromptSubmitted: async (input) => {
return {
modifiedPrompt: `[User from engineering team] ${input.prompt}`,
additionalContext: "Follow company coding standards.",
};
},
}
Champs de sortie : modifiedPrompt, additionalContext, suppressOutput
Hooks de cycle de vie de session
hooks: {
onSessionStart: async (input, invocation) => {
// input.source: "startup" | "resume" | "new"
console.log(`Session ${invocation.sessionId} started (${input.source})`);
return { additionalContext: "Project uses TypeScript and React." };
},
onSessionEnd: async (input, invocation) => {
// input.reason: "complete" | "error" | "abort" | "timeout" | "user_exit"
await recordMetrics({ sessionId: invocation.sessionId, reason: input.reason });
return null;
},
}
Hook de gestion d'erreur
hooks: {
onErrorOccurred: async (input) => {
// input.errorContext: "model_call" | "tool_execution" | "system" | "user_input"
// input.recoverable: boolean
if (input.errorContext === "model_call" && input.error.includes("rate")) {
return { errorHandling: "retry", retryCount: 3, userNotification: "Rate limited. Retrying..." };
}
return null; // Default error handling
},
}
Champs de sortie : suppressOutput, errorHandling ("retry" | "skip" | "abort"), retryCount, userNotification
Exemple de hook Python
async def on_pre_tool_use(input_data, invocation):
if input_data["toolName"] in ["shell", "bash"]:
return {"permissionDecision": "deny", "permissionDecisionReason": "Not permitted"}
return {"permissionDecision": "allow"}
session = await client.create_session({
"hooks": {"on_pre_tool_use": on_pre_tool_use}
})
Exemple de hook Go
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
Hooks: &copilot.SessionHooks{
OnPreToolUse: func(input copilot.PreToolUseHookInput, inv copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) {
return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil
},
},
})
Intégration du serveur MCP
Connectez-vous à des serveurs MCP (Model Context Protocol) pour les capacités des outils prédéfinis.
Serveur Stdio local
const session = await client.createSession({
mcpServers: {
filesystem: {
type: "local",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"],
tools: ["*"],
env: { DEBUG: "true" },
cwd: "./servers",
timeout: 30000,
},
},
});
Serveur HTTP distant
const session = await client.createSession({
mcpServers: {
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
headers: { Authorization: "Bearer ${TOKEN}" },
tools: ["*"],
},
},
});
Champs de configuration MCP
Local/Stdio :
| Champ | Type | Requis | Description |
|---|---|---|---|
type |
"local" |
Non | Par défaut local |
command |
string | Oui | Chemin de l'exécutable |
args |
string[] | Oui | Arguments de la commande |
env |
object | Non | Variables d'environnement |
cwd |
string | Non | Répertoire de travail |
tools |
string[] | Non | ["*"] pour tous, [] pour aucun |
timeout |
number | Non | Timeout en millisecondes |
HTTP distant :
| Champ | Type | Requis | Description |
|---|---|---|---|
type |
"http" |
Oui | Type de serveur |
url |
string | Oui | URL du serveur |
headers |
object | Non | En-têtes HTTP |
tools |
string[] | Non | Filtre des outils |
timeout |
number | Non | Timeout en ms |
Débogage MCP
Testez les serveurs MCP de manière indépendante avant l'intégration :
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | /path/to/your/mcp-server
Utilisez l'inspecteur MCP pour le débogage interactif :
npx @modelcontextprotocol/inspector /path/to/your/mcp-server
Problèmes MCP courants :
- Les outils n'apparaissent pas → Définissez
tools: ["*"]et vérifiez que le serveur répond àtools/list - Le serveur ne démarre pas → Utilisez des chemins de commande absolus, vérifiez
cwd - Pollution de stdout → La sortie de débogage doit aller sur stderr, pas stdout
Authentification
Méthodes (ordre de priorité)
- Token explicite —
githubTokendans le constructeur - Clé HMAC — Variables d'environnement
CAPI_HMAC_KEYouCOPILOT_HMAC_KEY - Token API direct —
GITHUB_COPILOT_API_TOKENavecCOPILOT_API_URL - Variables d'environnement —
COPILOT_GITHUB_TOKEN→GH_TOKEN→GITHUB_TOKEN - OAuth stocké — De
copilot auth login - GitHub CLI — Credentials
gh auth
Token programmatique
const client = new CopilotClient({ githubToken: process.env.GITHUB_TOKEN });
Application GitHub OAuth
Pour les applications multi-utilisateurs où les utilisateurs se connectent avec GitHub :
const client = new CopilotClient({
githubToken: userAccessToken, // gho_ ou ghu_ token du flux OAuth
useLoggedInUser: false, // Ne pas utiliser les credentials CLI stockés
});
Types de token supportés : gho_ (OAuth), ghu_ (GitHub App), github_pat_ (PAT à granularité fine).
Non supporté : ghp_ (PAT classique — déprécié).
Désactiver la connexion automatique
Empêchez le SDK d'utiliser les credentials stockés :
const client = new CopilotClient({ useLoggedInUser: false });
BYOK (Bring Your Own Key)
Utilisez vos propres clés API — aucun abonnement Copilot requis. La CLI n'agit que comme runtime d'agent.
Configurations de fournisseur
OpenAI :
provider: { type: "openai", baseUrl: "https://api.openai.com/v1", apiKey: process.env.OPENAI_API_KEY }
Azure AI Foundry (compatible OpenAI) :
provider: {
type: "openai",
baseUrl: "https://your-resource.openai.azure.com/openai/v1/",
apiKey: process.env.FOUNDRY_API_KEY,
wireApi: "responses", // Utilisez "responses" pour les séries GPT-5, "completions" pour les autres
}
Azure OpenAI (endpoint natif) :
provider: {
type: "azure",
baseUrl: "https://my-resource.openai.azure.com", // Juste l'hôte — pas /openai/v1
apiKey: process.env.AZURE_OPENAI_KEY,
azure: { apiVersion: "2024-10-21" },
}
Anthropic :
provider: { type: "anthropic", baseUrl: "https://api.anthropic.com", apiKey: process.env.ANTHROPIC_API_KEY }
Ollama (local) :
provider: { type: "openai", baseUrl: "http://localhost:11434/v1" }
Référence de configuration du fournisseur
| Champ | Type | Description |
|---|---|---|
type |
"openai" | "azure" | "anthropic" |
Type de fournisseur |
baseUrl |
string | Requis. URL du point de terminaison API |
apiKey |
string | Clé API (optionnelle pour les fournisseurs locaux) |
bearerToken |
string | Authentification par token bearer (prioritaire sur apiKey) |
wireApi |
"completions" | "responses" |
Format API (défaut : "completions") |
azure.apiVersion |
string | Version API Azure (défaut : "2024-10-21") |
Identité gérée Azure avec BYOK
Utilisez DefaultAzureCredential pour obtenir des tokens bearer de courte durée pour les déploiements Azure :
from azure.identity import DefaultAzureCredential
from copilot import CopilotClient, ProviderConfig, SessionConfig
credential = DefaultAzureCredential()
token = credential.get_token("https://cognitiveservices.azure.com/.default").token
session = await client.create_session(SessionConfig(
model="gpt-4.1",
provider=ProviderConfig(
type="openai",
base_url=f"{foundry_url}/openai/v1/",
bearer_token=token,
wire_api="responses",
),
))
Remarque : Les tokens bearer expirent (~1 heure). Pour les applications longue durée, actualisez le token avant chaque nouvelle session. Le SDK n'actualise pas automatiquement les tokens.
Limitations de BYOK
- Credentials statiques uniquement — pas de support natif Entra ID, OIDC ou identité gérée
- Pas d'actualisation automatique — les tokens expirés nécessitent de créer une nouvelle session
- Les clés ne sont pas persistées — vous devez re-fournir la configuration
providerlors de la reprise de session - Disponibilité du modèle — limité à ce que votre fournisseur propose
Persistance de session
Reprenez les sessions lors des redémarrages en fournissant votre propre ID de session.
// Créer avec un ID explicite
const session = await client.createSession({
sessionId: "user-123-task-456",
model: "gpt-4.1",
});
// Reprendre plus tard (même depuis une autre instance de client)
const resumed = await client.resumeSession("user-123-task-456");
await resumed.sendAndWait({ prompt: "What did we discuss?" });
Gestion des sessions
const sessions = await client.listSessions(); // Lister toutes
const lastId = await client.getLastSessionId(); // Obtenir la plus récente
await client.deleteSession("user-123-task-456"); // Supprimer du stockage
await session.destroy(); // Détruire la session active
Options de reprise
Lors de la reprise, vous pouvez reconfigurer : model, systemMessage, availableTools, excludedTools, provider (requis pour BYOK), reasoningEffort, streaming, mcpServers, customAgents, skillDirectories, infiniteSessions.
Meilleures pratiques d'ID de session
| Motif | Exemple | Cas d'usage |
|---|---|---|
user-{userId}-{taskId} |
user-alice-pr-review-42 |
Applications multi-utilisateurs |
tenant-{tenantId}-{workflow} |
tenant-acme-onboarding |
SaaS multi-tenants |
{userId}-{taskType}-{timestamp} |
alice-deploy-1706932800 |
Nettoyage basé sur le temps |
Qu'est-ce qui est persisté
L'état de la session est sauvegardé dans ~/.copilot/session-state/{sessionId}/:
| Données | Persistées ? | Notes |
|---|---|---|
| Historique de conversation | ✅ Oui | Thread de messages complet |
| Résultats des appels d'outils | ✅ Oui | En cache pour le contexte |
| État de planification de l'agent | ✅ Oui | Fichier plan.md |
| Artefacts de session | ✅ Oui | Dans le répertoire files/ |
| Clés de fournisseur/API | ❌ Non | Doit être re-fournie lors de la reprise |
| État des outils en mémoire | ❌ Non | Concevez les outils pour qu'ils soient stateless |
Sessions infinies
Pour les workflows longue durée qui peuvent dépasser les limites de contexte, activez la compaction automatique :
const session = await client.createSession({
infiniteSessions: {
enabled: true,
backgroundCompactionThreshold: 0.80, // Commencer la compaction en arrière-plan à 80%
bufferExhaustionThreshold: 0.95, // Bloquer et compacter à 95%
},
});
Les seuils sont des ratios d'utilisation du contexte (0.0–1.0), pas des décomptes de tokens absolus.
Agents personnalisés
Définissez des personas d'IA spécialisés :
const session = await client.createSession({
customAgents: [{
name: "pr-reviewer",
displayName: "PR Reviewer",
description: "Reviews pull requests for best practices",
prompt: "You are an expert code reviewer. Focus on security, performance, and maintainability.",
}],
});
Message système
Contrôlez le comportement et la personnalité de l'IA :
const session = await client.createSession({
systemMessage: { content: "You are a helpful assistant. Always be concise." },
});
Intégration des compétences
Chargez des répertoires de compétences pour étendre les capacités de Copilot :
const session = await client.createSession({
skillDirectories: ["./skills/code-review", "./skills/documentation"],
disabledSkills: ["experimental-feature"],
});
Les compétences peuvent être combinées avec les agents personnalisés et les serveurs MCP :
const session = await client.createSession({
skillDirectories: ["./skills/security"],
customAgents: [{ name: "auditor", prompt: "Focus on OWASP Top 10." }],
mcpServers: { postgres: { type: "local", command: "npx", args: ["-y", "@modelcontextprotocol/server-postgres"], tools: ["*"] } },
});
Gestionnaires de permissions et d'entrée
Gérez les permissions des outils et les demandes d'entrée utilisateur par programmation. Le SDK utilise un modèle de permissions deny-by-default — toutes les demandes de permission sont refusées sauf si vous fournissez un gestionnaire.
const session = await client.createSession({
onPermissionRequest: async (request) => {
if (request.kind === "shell") {
return { approved: request.command.startsWith("git") };
}
return { approved: true };
},
onUserInputRequest: async (request) => {
return { response: "yes" };
},
});
Suivi de l'utilisation des tokens
Abonnez-vous aux événements d'utilisation au lieu d'utiliser la CLI /usage :
session.on("assistant.usage", (event) => {
console.log("Tokens:", { input: event.data.inputTokens, output: event.data.outputTokens });
});
Motifs de déploiement
CLI locale (par défaut)
Le SDK lance automatiquement la CLI en tant que sous-processus. Configuration la plus simple — zéro configuration.
const client = new CopilotClient(); // Gère automatiquement le processus CLI
Serveur CLI externe (services backend)
Exécutez la CLI en mode headless, connectez-vous au SDK via TCP :
copilot --headless --port 4321
const client = new CopilotClient({ cliUrl: "localhost:4321" });
Support multi-client : Plusieurs clients SDK peuvent partager un serveur CLI.
CLI intégrée (applications de bureau)
Expédiez le binaire CLI avec votre application :
const client = new CopilotClient({ cliPath: path.join(__dirname, "vendor", "copilot") });
Docker Compose
services:
copilot-cli:
image: ghcr.io/github/copilot-cli:latest
command: ["--headless", "--port", "4321"]
environment:
- COPILOT_GITHUB_TOKEN=${COPILOT_GITHUB_TOKEN}
volumes:
- session-data:/root/.copilot/session-state
api:
build: .
environment:
- CLI_URL=copilot-cli:4321
depends_on: [copilot-cli]
volumes:
session-data:
Motifs d'isolation de session
| Motif | Isolation | Ressources | Meilleur pour |
|---|---|---|---|
| CLI par utilisateur | Complète | Élevée | SaaS multi-tenants, conformité |
| CLI partagée + IDs de session | Logique | Faible | Outils internes |
| Sessions partagées | Aucune | Faible | Collaboration d'équipe (nécessite un verrouillage) |
Liste de contrôle pour la production
- Nettoyage des sessions : suppression périodique des sessions expirées
- Vérifications de santé : ping du serveur CLI, redémarrage s'il ne répond pas
- Stockage persistant : montez
~/.copilot/session-state/pour les conteneurs - Gestion des secrets : utilisez Vault/K8s Secrets pour les tokens
- Verrouillage de session : Redis ou similaire pour l'accès aux sessions partagées
- Arrêt gracieux : videz les sessions actives avant d'arrêter la CLI
Configuration du client
| Option | Type | Défaut | Description |
|---|---|---|---|
cliPath |
string | Détection automatique | Chemin vers l'exécutable Copilot CLI |
cliUrl |
string | — | URL du serveur CLI externe |
githubToken |
string | — | Token GitHub pour l'authentification |
useLoggedInUser |
boolean | true |
Utiliser les credentials CLI stockés |
logLevel |
string | "none" |
"none" | "error" \ |