{"meta":{"title":"Copilot CLI ACP server","intro":"Learn about GitHub Copilot CLI's Agent Client Protocol server.","product":"GitHub Copilot","breadcrumbs":[{"href":"/en/enterprise-cloud@latest/copilot","title":"GitHub Copilot"},{"href":"/en/enterprise-cloud@latest/copilot/reference","title":"Reference"},{"href":"/en/enterprise-cloud@latest/copilot/reference/copilot-cli-reference","title":"Copilot CLI reference"},{"href":"/en/enterprise-cloud@latest/copilot/reference/copilot-cli-reference/acp-server","title":"ACP server"}],"documentType":"article"},"body":"# Copilot CLI ACP server\n\nLearn about GitHub Copilot CLI's Agent Client Protocol server.\n\n> \\[!NOTE]\n> ACP support in GitHub Copilot CLI is in public preview and subject to change.\n\n## Overview\n\nThe Agent Client Protocol (ACP) is a protocol that standardizes communication between clients (such as code editors and IDEs) and agents (such as Copilot CLI). For more details about this protocol, see the [official introduction](https://agentclientprotocol.com/get-started/introduction).\n\n## Use cases\n\n* **IDE integrations:** Build Copilot support into any editor or development environment.\n* **CI/CD pipelines:** Orchestrate agentic coding tasks in automated workflows.\n* **Custom frontends:** Create specialized interfaces for specific developer workflows.\n* **Multi-agent systems:** Coordinate Copilot with other AI agents using a standard protocol.\n\n## Starting the ACP server\n\nUse the `--acp` option of the `copilot` command to start the CLI's ACP server. You can specify the transport mode with either the `--stdio` or `--port` options. If no transport mode is specified, the server defaults to stdio mode.\n\nACP mode allows sessions with a configured bring-your-own-key (BYOK) provider (`COPILOT_PROVIDER_*` environment variables) to run without GitHub login, matching the behavior of `-p`/interactive mode.\n\n### Options applied to every session\n\nThe ACP `session/new` request only lets a client set a few session parameters, such as the working directory and the MCP servers to use. It does not carry tool-filtering or reasoning settings. To configure those, pass the corresponding options when you **start the server**. The server stores the values and applies them as the initial configuration for every session it creates or loads, for any client that connects. A connecting client does not choose these values—whoever launches the server does.\n\n| Server option                                | Accepted value                               | Effect on every session                        |\n| -------------------------------------------- | -------------------------------------------- | ---------------------------------------------- |\n| `--available-tools=TOOL ...`                 | A quoted, comma-separated list of tool names | The session can use only the listed tools.     |\n| `--excluded-tools=TOOL ...`                  | A quoted, comma-separated list of tool names | The listed tools are removed from the session. |\n| `--effort=LEVEL`, `--reasoning-effort=LEVEL` | `low`, `medium`, `high`, `xhigh`, or `max`   | Sets the session's initial reasoning effort.   |\n\nFor example, this command starts a server whose sessions all use maximum reasoning effort and expose only the `bash` and `view` tools:\n\n```bash\ncopilot --acp --port 3000 --effort=max --available-tools=\"bash,view\"\n```\n\nEvery session the connected client opens against that server inherits those settings. Because the values are fixed when the server starts, a client cannot change them per session through `session/new`.\n\n### stdio mode\n\nstdio mode is inferred by default when you start the ACP server. You can also use the `--stdio` option for disambiguation.\n\n```bash\ncopilot --acp --stdio\n```\n\n### TCP mode\n\nIf the `--port` option is provided in combination with the `--acp` option, the server is started in TCP mode.\n\n```bash\ncopilot --acp --port 3000\n```\n\n### Choosing between stdio and TCP\n\nBoth transport modes carry the same ACP messages, encoded as newline-delimited JSON (NDJSON). They differ only in how a client connects to the server and how the server's lifecycle is managed. The two modes are mutually exclusive: passing both `--stdio` and `--port` is rejected.\n\n| Aspect                      | stdio mode                                                                                                                                | TCP mode                                                                                                                                     |\n| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |\n| **How the client connects** | The client launches `copilot --acp` as a child process and exchanges messages over the process's standard input and output.               | The server opens a TCP listener that clients connect to over a network socket. By default it binds to the loopback address `127.0.0.1`.      |\n| **Number of clients**       | A single client—the process that spawned the server and owns the pipe.                                                                    | The listener accepts socket connections, each handled as its own agent connection.                                                           |\n| **Lifecycle**               | Tied to the parent process. When the input stream closes—because the parent exits or closes the pipe—the server shuts down automatically. | Independent of any single client. The server keeps listening on the port until it is stopped, for example with <kbd>Ctrl</kbd>+<kbd>C</kbd>. |\n| **Standard output**         | Reserved for the NDJSON protocol stream, so it can't be used for logs or other text.                                                      | Free for other use, because protocol traffic travels over the socket.                                                                        |\n\nWhen to use each mode:\n\n* Use **stdio mode** when an editor, IDE, or script spawns Copilot CLI directly as a subprocess. This is the default and the recommended setup for IDE integration, because the transport is established automatically when the process starts and torn down when it exits.\n* Use **TCP mode** when a client needs to reach the server over a socket instead of a pipe—for example, from a separate process or container, or when connecting to a longer-lived server on a known port.\n\n## Example: integrating with the ACP server\n\nThe following example is a client application that uses Copilot by interacting with GitHub Copilot CLI's ACP server. It starts the ACP server in stdio mode, opens a session, asks you to enter a prompt, sends it, and prints the streamed response.\n\nThere is a growing ecosystem of libraries for interacting with ACP servers programmatically. This example uses the [ACP TypeScript library](https://agentclientprotocol.com/libraries/typescript).\n\nTo run this example, you need the following dependencies:\n\n* [Node.js](https://nodejs.org) version 18 or later.\n* GitHub Copilot CLI, installed and either authenticated with GitHub or configured with a BYOK provider (see [Starting the ACP server](#starting-the-acp-server)).\n* The `@agentclientprotocol/sdk` package, which provides the ACP TypeScript library. Install it by running `npm install @agentclientprotocol/sdk`.\n\n```typescript copy\nimport * as acp from \"@agentclientprotocol/sdk\";\nimport { spawn } from \"node:child_process\";\nimport { Readable, Writable } from \"node:stream\";\nimport * as readline from \"node:readline/promises\";\n\nasync function main() {\n  const executable = process.env.COPILOT_CLI_PATH ?? \"copilot\";\n\n  // ACP uses standard input/output (stdin/stdout) for transport; we pipe these for the NDJSON stream.\n  const copilotProcess = spawn(executable, [\"--acp\", \"--stdio\"], {\n    stdio: [\"pipe\", \"pipe\", \"inherit\"],\n  });\n\n  if (!copilotProcess.stdin || !copilotProcess.stdout) {\n    throw new Error(\"Failed to start Copilot ACP process with piped stdio.\");\n  }\n\n  // Create ACP streams (NDJSON over stdio)\n  const output = Writable.toWeb(copilotProcess.stdin) as WritableStream<Uint8Array>;\n  const input = Readable.toWeb(copilotProcess.stdout) as ReadableStream<Uint8Array>;\n  const stream = acp.ndJsonStream(output, input);\n\n  const client: acp.Client = {\n    async requestPermission(params) {\n      // This example should not trigger tool calls; if it does, refuse.\n      return { outcome: { outcome: \"cancelled\" } };\n    },\n\n    async sessionUpdate(params) {\n      const update = params.update;\n\n      if (update.sessionUpdate === \"agent_message_chunk\" && update.content.type === \"text\") {\n        process.stdout.write(update.content.text);\n      }\n    },\n  };\n\n  const connection = new acp.ClientSideConnection((_agent) => client, stream);\n\n  await connection.initialize({\n    protocolVersion: acp.PROTOCOL_VERSION,\n    clientCapabilities: {},\n  });\n\n  const sessionResult = await connection.newSession({\n    cwd: process.cwd(),\n    mcpServers: [],\n  });\n\n  process.stdout.write(\"Session started!\\n\");\n\n  // Ask the user to enter a prompt instead of using a hard-coded one.\n  const rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout,\n  });\n  const promptText = await rl.question(\"Enter a prompt: \");\n  rl.close();\n\n  const promptResult = await connection.prompt({\n    sessionId: sessionResult.sessionId,\n    prompt: [{ type: \"text\", text: promptText }],\n  });\n\n  process.stdout.write(\"\\n\");\n\n  if (promptResult.stopReason !== \"end_turn\") {\n    process.stderr.write(`Prompt finished with stopReason=${promptResult.stopReason}\\n`);\n  }\n\n  // Best-effort cleanup\n  copilotProcess.stdin.end();\n  copilotProcess.kill(\"SIGTERM\");\n  await new Promise<void>((resolve) => {\n    copilotProcess.once(\"exit\", () => resolve());\n    setTimeout(() => resolve(), 2000);\n  });\n}\n\nmain().catch((error) => {\n  console.error(error);\n  process.exitCode = 1;\n});\n```\n\nTo run the example:\n\n1. Save the code above to a file named `acp-client.ts`.\n2. Run the file with `npx tsx`, which runs the TypeScript directly without a separate build step:\n\n   ```bash\n   npx tsx acp-client.ts\n   ```\n\n## Using slash commands\n\nGitHub Copilot CLI's built-in slash commands can be run over ACP. To invoke one, send it as an ordinary prompt whose text is the command, passed as a single text content block—for example, `/context` or `/session info`. The server recognizes the command and runs it directly: informational commands such as `/usage` or `/context` return their output without invoking the model, while action commands such as `/plan` or `/review` start the corresponding agent task. Either way, the command text is not sent to the model as a question.\n\n### Discovering available commands\n\nThe server advertises the commands it supports through the standard ACP `available_commands_update` session notification. It is sent after a session is created or loaded, and again whenever the set changes—for example, when skills finish loading. This advertised list is the authoritative, always-current set of commands you can run over ACP, and clients typically surface it in a command menu.\n\nThe advertised list contains:\n\n* **Built-in commands**, such as `/compact`, `/context`, `/usage`, `/env`, `/model`, `/mcp`, `/plan`, `/review`, `/research`, `/session`, and `/rename`.\n* **Enabled, user-invocable skills**, which appear as `/SKILL-NAME` commands.\n\nCommands that the client itself registers are not advertised back to it.\n\n### Accessing the list from your client\n\nBecause the list arrives as a notification rather than in response to a request, there is no method to fetch it on demand. Your client accesses it by handling the `session/update` notification and reacting to updates whose type is `available_commands_update`. Each entry has a `name` (without the leading slash), a `description`, and an optional `input.hint` that describes the command's arguments. The notification is re-sent whenever the set changes, so treat each one as a complete replacement of any list you have cached.\n\nThe following `sessionUpdate` handler captures the advertised commands, extending the `client` object from the example shown earlier.\n\n```typescript copy\n// Track the latest advertised commands for the session.\nlet availableCommands: acp.AvailableCommand[] = [];\n\nconst client: acp.Client = {\n  async sessionUpdate(params) {\n    const update = params.update;\n\n    if (update.sessionUpdate === \"available_commands_update\") {\n      // This notification is a full snapshot—replace any cached list.\n      availableCommands = update.availableCommands;\n      for (const command of availableCommands) {\n        // command.name has no leading slash; invoke it by sending \"/<name>\" as a prompt.\n        console.log(`/${command.name} — ${command.description}`);\n      }\n      return;\n    }\n\n    // ...handle other updates, such as agent_message_chunk\n  },\n\n  // ...other client methods, such as requestPermission\n};\n```\n\nTo run one of the advertised commands, send its name as a prompt in a single text content block—for example, `{ type: \"text\", text: \"/context\" }`—as described in [Using slash commands](#using-slash-commands).\n\n### Commands that cannot be used over ACP\n\nSlash commands that depend on the interactive terminal interface are not handled by the ACP server. This includes commands that open a picker, dialog, or full-screen view, such as `/diff`, `/resume`, `/theme`, `/settings`, `/login`, `/help`, `/tasks`, and `/undo`. As a rule, if a command does not appear in the `available_commands_update` list, it will not run over ACP: the server treats the text as an ordinary prompt and forwards it to the model instead of executing it.\n\nBecause ACP clients have no interactive pickers, a built-in command that would normally open a submenu instead returns its options as text. Provide the subcommand explicitly to get a direct result—for example, `/session info` or `/mcp list` rather than `/session` or `/mcp` on its own.\n\nFor a complete list of slash commands for Copilot CLI, see [GitHub Copilot CLI command reference](/en/enterprise-cloud@latest/copilot/reference/copilot-cli-reference/cli-command-reference#slash-commands-in-the-interactive-interface).\n\n## Further reading\n\n* [Official ACP documentation](https://agentclientprotocol.com/protocol/overview)"}