Avanzado Premium · 5€
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.

Node.js 18+
·
Python 3.8+
·
Go 1.21+
·
.NET 8.0+
·
MIT
·
Sin Copilot sub. con BYOK
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 IDEjemploCaso de uso
user-{userId}-{taskId}user-alice-pr-review-42Apps multi-usuario
tenant-{tenantId}-{workflow}tenant-cultivaia-onboardingSaaS multi-tenant
{userId}-{taskType}-{ts}alice-deploy-1706932800Limpieza 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:8REFRESH_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 (usar infiniteSessions)