Skip to main content

Servidor ACP de CLI de Copilot

Obtenga información sobre el servidor de protocolo de cliente del agente CLI de GitHub Copilot.

Nota:

La compatibilidad con ACP en CLI de GitHub Copilot está en versión preliminar pública y está sujeta a cambios.

Visión general

El Protocolo de cliente del agente (ACP) es un protocolo que normaliza la comunicación entre clientes (como editores de código e IDE) y agentes (como CLI de Copilot). Para obtener más información sobre este protocolo, consulte la introducción oficial.

Casos de uso

  • Integraciones de IDE: Incorpore Copilot soporte en cualquier editor o en cualquier entorno de desarrollo.
  • Canalizaciones de CI/CD: orqueste tareas de codificación de agente en flujos de trabajo automatizados.
  • Portales personalizados: Cree interfaces especializadas para los flujos de trabajo específicos de los desarrolladores.
  • Sistemas multiagente: Coordinarse Copilot con otros agentes de IA mediante un protocolo estándar.

Iniciar el servidor ACP

Use la --acp opción del copilot comando para iniciar el servidor ACP de la CLI. Puede especificar el modo de transporte con las --stdio opciones o --port . Si no se especifica ningún modo de transporte, el servidor tiene como valor predeterminado el modo stdio.

Opciones aplicadas a cada sesión

La solicitud ACP session/new solo permite que un cliente establezca algunos parámetros de sesión, como el directorio de trabajo y los servidores MCP que se van a usar. No incluye filtros de herramientas ni ajustes de razonamiento. Para configurarlos, pase las opciones correspondientes al iniciar el servidor. El servidor almacena los valores y los aplica como configuración inicial para cada sesión que crea o carga, para cualquier cliente que se conecte. El cliente que se conecta no elige estos valores; los elige quien inicia el servidor.

Opción de servidorValor aceptadoEfecto en cada sesión
--available-tools=TOOL ...Lista entrecomillada y separada por comas de nombres de herramientasLa sesión solo puede usar las herramientas enumeradas.
--excluded-tools=TOOL ...Lista entrecomillada y separada por comas de nombres de herramientasLas herramientas enumeradas se quitan de la sesión.
--effort=LEVEL, --reasoning-effort=LEVEL
low, medium, high, xhigh o maxEstablece el esfuerzo inicial de razonamiento de la sesión.

Por ejemplo, este comando inicia un servidor cuyas sesiones todas utilizan el nivel máximo de esfuerzo de razonamiento y exponen solo las herramientas bash y view:

copilot --acp --port 3000 --effort=max --available-tools="bash,view"

Todas las sesiones que el cliente conectado abre en ese servidor heredan esa configuración. Dado que los valores se fijan cuando se inicia el servidor, un cliente no puede cambiarlos por sesión a través de session/new.

modo stdio

El modo stdio se deduce de forma predeterminada cuando se inicia el servidor ACP. También puede usar la --stdio opción para la desambiguación.

copilot --acp --stdio

Modo TCP

Si la --port opción se proporciona en combinación con la --acp opción , el servidor se inicia en modo TCP.

copilot --acp --port 3000

Elección entre stdio y TCP

Ambos modos de transporte llevan los mismos mensajes ACP, codificados como JSON delimitado por nueva línea (NDJSON). Solo difieren en cómo se conecta un cliente al servidor y cómo se administra el ciclo de vida del servidor. Los dos modos son mutuamente excluyentes: pasar a la vez --stdio y --port se rechaza.

Aspectomodo stdioModo TCP
Cómo se conecta el clienteEl cliente lanza copilot --acp como un proceso hijo e intercambia mensajes a través de la entrada estándar y la salida estándar del proceso.El servidor abre un agente de escucha TCP al que se conectan los clientes a través de un socket de red. De forma predeterminada, se vincula a la dirección de loopback 127.0.0.1.
Número de clientesUn solo cliente: el proceso que generó el servidor y posee la canalización.El agente de escucha acepta conexiones de socket, cada una controlada como su propia conexión de agente.
Ciclo de vidaVinculado al proceso primario. Cuando se cierra el flujo de entrada ,porque el elemento primario sale o cierra la canalización, el servidor se cierra automáticamente.Independiente de cualquier cliente único. El servidor sigue escuchando en el puerto hasta que se detiene, por ejemplo, con Ctrl+C.
Salida estándarReservado para el flujo de protocolo NDJSON, por lo que no se puede usar para registros u otro texto.Gratis para otro uso, ya que el tráfico de protocolo viaja a través del socket.

Cuándo usar cada modo:

  • Use el modo stdio cuando un editor, un IDE o un script inicie CLI de Copilot directamente como subproceso. Este es el valor predeterminado y la configuración recomendada para la integración del IDE, ya que el transporte se establece automáticamente cuando el proceso se inicia y se descompone cuando se cierra.
  • Use el modo TCP cuando un cliente necesite llegar al servidor a través de un socket en lugar de una canalización, por ejemplo, desde un proceso o contenedor independiente, o al conectarse a un servidor de larga duración en un puerto conocido.

Ejemplo: integración con el servidor ACP

El ejemplo siguiente es una aplicación cliente que usa Copilot mediante la interacción con CLI de GitHub Copilotel servidor ACP. Inicia el servidor ACP en modo stdio, abre una sesión, le pide que escriba un mensaje, lo envíe e imprima la respuesta transmitida.

Hay un ecosistema creciente de bibliotecas para interactuar con servidores ACP mediante programación. En este ejemplo se usa la biblioteca TypeScript ACP.

Para ejecutar este ejemplo, necesita las siguientes dependencias:

  • Node.js versión 18 o posterior.
  • CLI de GitHub Copilot, instalado y autenticado.
  • El @agentclientprotocol/sdk paquete, que proporciona la biblioteca TypeScript ACP. Instálela mediante la ejecución de npm install @agentclientprotocol/sdk.
TypeScript
import * as acp from "@agentclientprotocol/sdk";
import { spawn } from "node:child_process";
import { Readable, Writable } from "node:stream";
import * as readline from "node:readline/promises";

async function main() {
  const executable = process.env.COPILOT_CLI_PATH ?? "copilot";

  // ACP uses standard input/output (stdin/stdout) for transport; we pipe these for the NDJSON stream.
  const copilotProcess = spawn(executable, ["--acp", "--stdio"], {
    stdio: ["pipe", "pipe", "inherit"],
  });

  if (!copilotProcess.stdin || !copilotProcess.stdout) {
    throw new Error("Failed to start Copilot ACP process with piped stdio.");
  }

  // Create ACP streams (NDJSON over stdio)
  const output = Writable.toWeb(copilotProcess.stdin) as WritableStream<Uint8Array>;
  const input = Readable.toWeb(copilotProcess.stdout) as ReadableStream<Uint8Array>;
  const stream = acp.ndJsonStream(output, input);

  const client: acp.Client = {
    async requestPermission(params) {
      // This example should not trigger tool calls; if it does, refuse.
      return { outcome: { outcome: "cancelled" } };
    },

    async sessionUpdate(params) {
      const update = params.update;

      if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
        process.stdout.write(update.content.text);
      }
    },
  };

  const connection = new acp.ClientSideConnection((_agent) => client, stream);

  await connection.initialize({
    protocolVersion: acp.PROTOCOL_VERSION,
    clientCapabilities: {},
  });

  const sessionResult = await connection.newSession({
    cwd: process.cwd(),
    mcpServers: [],
  });

  process.stdout.write("Session started!\n");

  // Ask the user to enter a prompt instead of using a hard-coded one.
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  const promptText = await rl.question("Enter a prompt: ");
  rl.close();

  const promptResult = await connection.prompt({
    sessionId: sessionResult.sessionId,
    prompt: [{ type: "text", text: promptText }],
  });

  process.stdout.write("\n");

  if (promptResult.stopReason !== "end_turn") {
    process.stderr.write(`Prompt finished with stopReason=${promptResult.stopReason}\n`);
  }

  // Best-effort cleanup
  copilotProcess.stdin.end();
  copilotProcess.kill("SIGTERM");
  await new Promise<void>((resolve) => {
    copilotProcess.once("exit", () => resolve());
    setTimeout(() => resolve(), 2000);
  });
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Para ejecutar el ejemplo:

  1. Guarde el código anterior en un archivo denominado acp-client.ts.

  2. Ejecute el archivo con npx tsx, que ejecuta TypeScript directamente sin un paso de compilación independiente:

    npx tsx acp-client.ts
    

Uso de comandos de barra

CLI de GitHub CopilotLos comandos de barra diagonal integrados se pueden ejecutar a través de ACP. Para invocar uno, envíalo como una solicitud normal cuyo texto sea el comando, pasado como un único bloque de contenido de texto; por ejemplo, /context o /session info. El servidor reconoce el comando y lo ejecuta directamente: comandos informativos como /usage o /context devuelven su salida sin invocar el modelo, mientras que comandos de acción como /plan o /review inician la tarea del agente correspondiente. En cualquier caso, el texto del comando no se envía al modelo como una pregunta.

Detección de comandos disponibles

El servidor anuncia los comandos que admite a través de la notificación de sesión ACP available_commands_update estándar. Se envía después de que se cree o se cargue una sesión, y de nuevo cada vez que cambie el conjunto de habilidades; por ejemplo, cuando las habilidades terminen de cargarse. Esta lista anunciada es el conjunto de comandos autoritativo y siempre actual que se puede ejecutar a través de ACP y los clientes suelen exponerlo en un menú de comandos.

La lista anunciada contiene:

  • Comandos integrados, como /compact, /context, /usage, /env, /model, /mcp, /plan, /review, /research, /session y /rename.
  • Aptitudes habilitadas, invocables por el usuario, que aparecen como /SKILL-NAME comandos.

Los comandos que registra el propio cliente no se le vuelven a anunciar.

Acceso a la lista desde el cliente

Dado que la lista llega como una notificación en lugar de en respuesta a una solicitud, no hay ningún método para capturarla a petición. El cliente accede a él controlando la session/update notificación y reaccionando a las actualizaciones cuyo tipo es available_commands_update. Cada entrada tiene un name (sin la barra diagonal inicial), un description y un input.hint opcional que describe los argumentos del comando. La notificación se vuelve a enviar cada vez que cambia el conjunto, por lo que cada una de ellas se trata como un reemplazo completo de cualquier lista que haya almacenado en caché.

El controlador siguiente sessionUpdate captura los comandos anunciados, ampliando el client objeto del ejemplo mostrado anteriormente.

TypeScript
// Track the latest advertised commands for the session.
let availableCommands: acp.AvailableCommand[] = [];

const client: acp.Client = {
  async sessionUpdate(params) {
    const update = params.update;

    if (update.sessionUpdate === "available_commands_update") {
      // This notification is a full snapshot—replace any cached list.
      availableCommands = update.availableCommands;
      for (const command of availableCommands) {
        // command.name has no leading slash; invoke it by sending "/<name>" as a prompt.
        console.log(`/${command.name}${command.description}`);
      }
      return;
    }

    // ...handle other updates, such as agent_message_chunk
  },

  // ...other client methods, such as requestPermission
};

Para ejecutar uno de los comandos anunciados, envíe su nombre como una solicitud en un único bloque de contenido de texto (por ejemplo, { type: "text", text: "/context" }), como se describe en Uso de comandos de barra diagonal.

Comandos que no se pueden usar a través de ACP

Los comandos con barra diagonal que dependen de la interfaz de terminal interactiva no son gestionados por el servidor ACP. Esto incluye comandos que abren un selector, un cuadro de diálogo o una vista de pantalla completa, como /diff, /resume, /theme, /settings, /login, /help, /tasksy /undo. Como regla, si un comando no aparece en la available_commands_update lista, no se ejecutará a través de ACP: el servidor trata el texto como una solicitud normal y lo reenvía al modelo en lugar de ejecutarlo.

Dado que los clientes ACP no tienen selectores interactivos, un comando integrado que normalmente abriría un submenú en su lugar devuelve sus opciones como texto. Proporcione explícitamente el subcomando para obtener un resultado directo, por ejemplo, /session info o /mcp list en lugar de /session o /mcp por sí mismo.

Para obtener una lista completa de los comandos de barra diagonal para CLI de Copilot, vea Referencia de comandos de la CLI de GitHub Copilot.

Lectura adicional