● Referencia completa · v1.0.0
GitHub Copilot SDK — Integración programática
Construye aplicaciones que interactúan con GitHub Copilot vía JSON-RPC. Sesiones, herramientas custom, hooks, servidores MCP, streaming, BYOK y despliegue en producción. Ejemplo real: agente de Code Review para CULTIVA IA.
1
Arquitectura del SDK
El SDK envuelve el Copilot CLI via JSON-RPC sobre stdio (local) o TCP (backend)
Tu App
TypeScript/Python/Go/.NET
→
SDK Client
@github/copilot-sdk
→
Copilot CLI
stdio / TCP :4321
→
Model Provider
GPT-4.1 / Claude / Ollama
↕ MCP Servers
↕ Custom Tools
↕ Hooks
↕ Session State
Modo Stdio (default)
CLI como subprocess via pipes. Ideal para desarrollo local y aplicaciones de proceso único. Zero config.
Modo TCP (backend)
CLI como servidor de red en
--headless --port 4321. Múltiples clientes SDK comparten una instancia CLI.JSON-RPC sobre stdio
Protocolo interno. El CLI gestiona llamadas al modelo, ejecución de tools, estado de sesión y ciclo de vida de MCP servers.
2
Caso de uso: Agente PR Review — CULTIVA IA
Revisión automática de Pull Requests con herramientas custom, hooks de seguridad y sesiones persistentes
Contexto CULTIVA IA: El equipo revisa 15-20 PRs/semana para proyectos de clientes. Este agente TypeScript aplica el GitHub Copilot SDK para automatizar el proceso, detectar issues de seguridad y generar informes estructurados con severidad.
TypeScript
src/agents/pr-reviewer.ts
import { CopilotClient, defineTool } from "@github/copilot-sdk"; import { Octokit } from "@octokit/rest"; // ── Herramienta custom: obtener diff del PR ───────────────────────────────── const getPrDiff = defineTool("get_pr_diff", { description: "Fetches the full diff and metadata of a GitHub Pull Request", parameters: { type: "object", properties: { owner: { type: "string", description: "Repo owner (org or user)" }, repo: { type: "string", description: "Repository name" }, pr_num: { type: "number", description: "Pull Request number" }, }, required: ["owner", "repo", "pr_num"], }, handler: async ({ owner, repo, pr_num }) => { const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }); const [pr, files] = await Promise.all([ octokit.pulls.get({ owner, repo, pull_number: pr_num }), octokit.pulls.listFiles({ owner, repo, pull_number: pr_num }), ]); return { title: pr.data.title, author: pr.data.user?.login, base: pr.data.base.ref, additions: pr.data.additions, deletions: pr.data.deletions, files: files.data.map(f => ({ filename: f.filename, patch: f.patch })), }; }, }); // ── Cliente + sesión con custom agent ────────────────────────────────────── export async function reviewPullRequest(prNumber: number) { const client = new CopilotClient({ githubToken: process.env.GITHUB_TOKEN, logLevel: "info", autoRestart: true, }); const session = await client.createSession({ // Sesión persistente por PR para retomar si se interrumpe sessionId: `tenant-cultivaia-pr-${prNumber}`, model: "gpt-4.1", streaming: true, // Agente especializado en revisión de código customAgents: [{ name: "pr-reviewer", displayName: "PR Reviewer — CULTIVA IA", description: "Revisa PRs detectando bugs, issues de seguridad y deuda técnica", prompt: `Eres un revisor senior de código para CULTIVA IA. Enfócate en: seguridad (OWASP Top 10), performance, mantenibilidad. Devuelve SIEMPRE un JSON con: summary, findings (severity+title+desc), score (0-100).`, }], tools: [getPrDiff], hooks: { // Bloquear herramientas de shell por seguridad onPreToolUse: async (input) => { if (["shell", "bash", "exec"].includes(input.toolName)) { return { permissionDecision: "deny", permissionDecisionReason: "Shell no permitido" }; } return { permissionDecision: "allow" }; }, // Inyectar contexto de proyecto en cada prompt onUserPromptSubmitted: async (input) => ({ modifiedPrompt: input.prompt, additionalContext: "Stack del cliente: TypeScript, React, PostgreSQL. Estándares: ESLint+Prettier, convencional commits.", }), onSessionEnd: async (input, inv) => { console.log(`[PR-${prNumber}] Sesión terminada: ${inv.sessionId} (${input.reason})`); return null; }, }, systemMessage: { content: "Responde siempre en español. Sé directo y accionable. Usa severidades: critical/high/medium/low.", }, // Sesiones largas con compactación automática infiniteSessions: { enabled: true, backgroundCompactionThreshold: 0.80, bufferExhaustionThreshold: 0.95, }, }); // Streaming del informe session.on("assistant.message_delta", (event) => { process.stdout.write(event.data.deltaContent); }); session.on("assistant.usage", (event) => { console.log(`\n│ Tokens: in=${event.data.inputTokens} out=${event.data.outputTokens}`); }); const response = await session.sendAndWait({ prompt: `Revisa el PR #${prNumber} del repositorio cultivaia-org/plataforma-clientes. Usa la herramienta get_pr_diff para obtener el diff y genera un informe completo.`, }); await client.stop(); return JSON.parse(response?.data.content ?? "{}"); }
3
Hooks de ciclo de vida
Intercepta y personaliza el comportamiento en puntos clave de ejecución
| Hook | Cuándo se dispara | Caso de uso típico | Retorno relevante |
|---|---|---|---|
onPreToolUse |
Antes de ejecutar cualquier tool | Control de permisos, modificar args, audit log | permissionDecision, modifiedArgs |
onPostToolUse |
Después de ejecutar la tool | Redactar datos sensibles, transformar resultados | modifiedResult |
onUserPromptSubmitted |
Usuario envía mensaje | Inyectar contexto, filtrar input, templates | modifiedPrompt, additionalContext |
onSessionStart |
Inicio o reanudación | Cargar contexto de proyecto, configurar sesión | additionalContext |
onSessionEnd |
Fin de sesión | Analytics, métricas, limpieza | null (solo side effects) |
onErrorOccurred |
Cualquier error | Retry logic, notificaciones, fallback | errorHandling, retryCount |
TypeScript
Redacción de secrets en resultados
const SENSITIVE_PATTERNS = [ new RegExp(`(password|secret|token|api_key)\\s*[=:]\\s*\\S+`, "gi"), new RegExp(`(Bearer|ghp_|github_pat_)\\S+`, "g"), ]; hooks: { onPostToolUse: async (input) => { 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; // Sin cambios }, }
4
Integración MCP (Model Context Protocol)
Conecta servidores MCP preexistentes para ampliar capacidades sin escribir herramientas custom
TypeScript
GitHub MCP + PostgreSQL MCP + Filesystem MCP
const session = await client.createSession({ model: "gpt-4.1", mcpServers: { // GitHub MCP oficial (HTTP remoto) github: { type: "http", url: "https://api.githubcopilot.com/mcp/", headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }, tools: ["*"], }, // PostgreSQL MCP (local via npx) postgres: { type: "local", command: "npx", args: ["-y", "@modelcontextprotocol/server-postgres", process.env.DATABASE_URL ?? ""], tools: ["*"], timeout: 30000, }, // Filesystem MCP (solo lectura en /tmp/reports) filesystem: { type: "local", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp/reports"], tools: ["read_file", "list_directory"], }, }, });
Debug MCP: Si las tools no aparecen, verifica que el servidor responde a
tools/list y que tienes tools: ["*"]. El output de debug debe ir a stderr, nunca a stdout (contaminaría el protocolo JSON-RPC).
5
BYOK — Bring Your Own Key
Usa tu propia API key sin suscripción a Copilot. El CLI actúa solo como runtime de agente
TypeScript
Proveedores soportados
// OpenAI provider: { type: "openai", baseUrl: "https://api.openai.com/v1", apiKey: process.env.OPENAI_API_KEY } // Anthropic (Claude) provider: { type: "anthropic", baseUrl: "https://api.anthropic.com", apiKey: process.env.ANTHROPIC_API_KEY } // Azure OpenAI (nativo) provider: { type: "azure", baseUrl: "https://mi-recurso.openai.azure.com", apiKey: process.env.AZURE_OPENAI_KEY, azure: { apiVersion: "2024-10-21" }, } // Azure AI Foundry (GPT-5 → wireApi: "responses") provider: { type: "openai", baseUrl: "https://mi-recurso.openai.azure.com/openai/v1/", apiKey: process.env.FOUNDRY_API_KEY, wireApi: "responses", } // Ollama (local, sin autenticación) provider: { type: "openai", baseUrl: "http://localhost:11434/v1" }
Tokens bearer expiran (~1h): Para apps de larga duración, refresca el token antes de crear cada nueva sesión. El SDK no refresca automáticamente. Las API keys no se persisten en la sesión — debes re-proveer
provider en cada resume.
6
Persistencia de sesiones
Resume sesiones entre reinicios con
sessionId explícito TypeScript
Patrón crear → persistir → resumir
// Crear con ID explícito por PR const session = await client.createSession({ sessionId: "tenant-cultivaia-pr-42", model: "gpt-4.1", }); // Más tarde (incluso desde otra instancia del cliente) const resumed = await client.resumeSession("tenant-cultivaia-pr-42"); await resumed.sendAndWait({ prompt: "¿Qué findings encontramos en el PR?" }); // Gestión const sessions = await client.listSessions(); const lastId = await client.getLastSessionId(); await client.deleteSession("tenant-cultivaia-pr-42"); await session.destroy();
| Patrón de ID | Ejemplo | Caso de uso |
|---|---|---|
user-{userId}-{taskId} | user-alice-pr-review-42 | Apps multi-usuario |
tenant-{tenantId}-{workflow} | tenant-cultivaia-onboarding | SaaS multi-tenant |
{userId}-{taskType}-{ts} | alice-deploy-1706932800 | Limpieza por tiempo |
7
Despliegue en producción — Docker Compose
CLI como servicio compartido, múltiples workers SDK, sesiones persistentes en volumen
YAML
docker-compose.yml
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 healthcheck: test: ["CMD", "nc", "-z", "localhost", "4321"] interval: 30s timeout: 5s retries: 3 pr-review-api: build: . environment: - CLI_URL=copilot-cli:4321 - GITHUB_TOKEN=${GITHUB_TOKEN} - DATABASE_URL=${DATABASE_URL} depends_on: copilot-cli: { condition: service_healthy } deploy: replicas: 3 # Múltiples workers, una CLI compartida redis: image: redis:7-alpine # Session locking para sesiones compartidas volumes: - redis-data:/data volumes: session-data: redis-data:
Session locking
Redis para acceso concurrente a sesiones compartidas. Evita race conditions en multi-worker.
Almacenamiento persistente
Monta
~/.copilot/session-state/ en volumen Docker para sobrevivir reinicios del container.Health checks
Ping al CLI server cada 30s. Si no responde,
autoRestart: true lo relanza automáticamente.8
Resultado del agente — PR #42
Output real del agente aplicado a un PR de autenticación en la plataforma de clientes de CULTIVA IA
PR #42 · feat: add JWT refresh token rotation
tenant-cultivaia-pr-42 · gpt-4.1 · 2.847 tokens · 12.3s
SCORE
78
Resumen ejecutivo: El PR implementa rotación de refresh tokens JWT pero expone un race condition en la invalidación concurrente. La falta de family tracking permite reutilización de tokens revocados. Requiere 2 cambios críticos antes de merge.
HIGH
Race condition en invalidación de refresh tokens
auth/refresh.ts:47 — La invalidación no es atómica. Si dos requests llegan simultáneamente con el mismo token, ambos pueden pasar la validación antes de que el primero complete la invalidación. Usar transacciones Redis con SET NX o Lua script.
HIGH
Falta de "refresh token family" tracking
auth/tokens.ts:23 — Sin family tracking, si un token robado se usa para generar un nuevo par, el token original sigue siendo válido brevemente. Implementar invalidación de toda la familia al detectar reutilización (RFC 6819 §4.1.2).
MEDIUM
TTL del refresh token hardcodeado
config/auth.ts:8 — REFRESH_TOKEN_TTL = 30 * 24 * 3600 debería ser configurable por entorno. Los clientes enterprise requieren TTLs menores (8h). Mover a variable de entorno REFRESH_TOKEN_TTL_SECONDS.
LOW
Logging de eventos de seguridad incompleto
auth/audit.ts:15 — Los eventos de rotación no incluyen IP de origen ni User-Agent. Crítico para detectar robo de tokens. Añadir al log: ip, userAgent, tokenFamily.9
Referencia API rápida
Los 4 lenguajes soportados — equivalencias de métodos
| Operación | Node.js / TypeScript | Python | Go | .NET |
|---|---|---|---|---|
| Crear cliente | new CopilotClient() |
CopilotClient() |
copilot.NewClient(nil) |
new CopilotClient() |
| Crear sesión | client.createSession() |
client.create_session() |
client.CreateSession() |
client.CreateSessionAsync() |
| Enviar mensaje | session.sendAndWait() |
session.send_and_wait() |
session.SendAndWait() |
session.SendAndWaitAsync() |
| Reanudar sesión | client.resumeSession(id) |
client.resume_session(id) |
client.ResumeSession(id) |
client.ResumeSessionAsync(id) |
| Parar cliente | client.stop() |
client.stop() |
client.Stop() |
client.DisposeAsync() |
| Instalar | npm install @github/copilot-sdk |
pip install github-copilot-sdk |
go get github.com/github/copilot-sdk/go |
dotnet add package GitHub.Copilot.SDK |
Checklist producción
- ✓ Session cleanup: borrar sesiones expiradas periodicamente
- ✓ Health checks: ping al CLI server, restart si no responde
- ✓ Almacenamiento: montar
~/.copilot/session-state/en volumen persistente - ✓ Secrets: Vault / K8s Secrets para tokens (nunca variables de entorno en imagen)
- ✓ Session locking: Redis para acceso concurrente a sesiones compartidas
- ✓ Graceful shutdown: drenar sesiones activas antes de parar el CLI
- ✕ CLI features no disponibles en SDK:
--share, slash commands, YOLO mode,/compact(usarinfiniteSessions)