# How the Copilot CLI Server Interacts with SDK Clients: Architecture and Implementation Guide

> Learn how the Copilot CLI server interacts with SDK clients using JSON-RPC over stdio or TCP. Explore session management, streaming, and permission hooks. github/copilot-sdk

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: architecture
- Published: 2026-07-18

---

**The Copilot CLI server interacts with SDK clients by running as a child process that communicates via JSON-RPC over stdio or TCP, exposing session management, message streaming, and permission hooks through auto-generated language bindings.**

The GitHub Copilot SDK enables developers to embed AI-powered coding assistance into applications across multiple languages. Understanding how the Copilot CLI server interacts with SDK clients requires examining the standardized JSON-RPC protocol, process management lifecycle, and transport abstraction layers implemented consistently across Python, Rust, and Go.

## Architectural Overview

The interaction model follows a client-server architecture where the SDK acts as the client and the Copilot CLI binary functions as the server. This design separates the heavy model inference workloads from the host application.

**Core Components**

- **Copilot CLI Server**: A standalone binary implementing the GitHub Copilot JSON-RPC protocol. It exposes methods for conversation management, tool execution, and streaming completions. The server listens on **stdio** by default or an optional **TCP** socket.
- **SDK Clients**: Language-specific libraries (`github/copilot-sdk`) that embed or reference the CLI binary. They manage server lifecycle, transport framing, and expose high-level APIs for sessions and hooks.
- **Transport Layer**: Handles low-level I/O and JSON-RPC message framing. Implemented in [[`copilot/_jsonrpc.py`](https://github.com/github/copilot-sdk/blob/main/copilot/_jsonrpc.py)](https://github.com/github/copilot-sdk/blob/main/python/copilot/_jsonrpc.py) (Python), [[`rpc.rs`](https://github.com/github/copilot-sdk/blob/main/rpc.rs)](https://github.com/github/copilot-sdk/blob/main/rust/src/rpc.rs) (Rust), and [[`rpc.go`](https://github.com/github/copilot-sdk/blob/main/rpc.go)](https://github.com/github/copilot-sdk/blob/main/go/rpc.go) (Go).
- **Session Management**: Isolated conversation contexts created via `session.create` and managed through the `client.session` namespace.

**Interaction Flow**

1. **Binary Resolution**: The SDK locates the CLI binary via embedded bundles, environment variables, or downloads it to a cache directory using [[`python/copilot/_cli_download.py`](https://github.com/github/copilot-sdk/blob/main/python/copilot/_cli_download.py)](https://github.com/github/copilot-sdk/blob/main/python/copilot/_cli_download.py).
2. **Process Spawning**: The client spawns the CLI as a child process (stdio mode) or connects to a TCP endpoint.
3. **JSON-RPC Handshake**: The transport layer establishes bidirectional communication with request/response correlation.
4. **Session Creation**: The client calls `session.create` on the server, receiving a unique session ID.
5. **Message Exchange**: User prompts transmit via `session.sendMessage`, with responses streaming back as completion events.
6. **Hook Routing**: Server-side permission requests and lifecycle hooks forward to application-registered callbacks.

## Python SDK Implementation

The Python SDK manages the CLI server lifecycle through the `CopilotClient` class, abstracting process management behind an async/await interface.

### Client Initialization and Binary Resolution

The [`CopilotClient`](https://github.com/github/copilot-sdk/blob/main/python/copilot/client.py) class handles binary discovery and process spawning. Upon instantiation, it determines the appropriate [`ClientMode`](https://github.com/github/copilot-sdk/blob/main/python/copilot/_mode.py) (defaulting to `CopilotCli`) and prepares the transport configuration.

```python
from copilot.client import CopilotClient

# Initialize client - resolves binary via _cli_download if needed

client = CopilotClient()

# Internal method that spawns the CLI subprocess

await client._start_cli_server()

```

The [`_start_cli_server()`](https://github.com/github/copilot-sdk/blob/main/python/copilot/client.py) method launches the CLI binary as a subprocess, establishing the stdio transport pipe for JSON-RPC communication.

### JSON-RPC Transport Layer

Low-level message framing and request correlation reside in [[`copilot/_jsonrpc.py`](https://github.com/github/copilot-sdk/blob/main/copilot/_jsonrpc.py)](https://github.com/github/copilot-sdk/blob/main/python/copilot/_jsonrpc.py). This module handles message serialization, batching, and the correlation of request IDs to asynchronous responses.

### Session Management and Messaging

The SDK exposes session operations through the `client.session` namespace, mapping directly to CLI server methods:

```python

# Create a new conversation session

session = await client.session.create()

# Send a prompt and receive streamed response

await session.send_message("Refactor this function to use async/await patterns")

```

Hooks and permissions integrate via callback registration:

```python
def on_permission(request):
    return {"grant": True}

client.hooks.on_permission_requested = on_permission

```

## Rust SDK Implementation

The Rust SDK provides type-safe bindings with optional CLI bundling, offering both sync and async interfaces.

### Client Entry Point and Process Spawning

The [`CopilotClient::new()`](https://github.com/github/copilot-sdk/blob/main/rust/src/lib.rs) constructor in [[`rust/src/lib.rs`](https://github.com/github/copilot-sdk/blob/main/rust/src/lib.rs)](https://github.com/github/copilot-sdk/blob/main/rust/src/lib.rs) initializes the client, resolving the binary path using the `bundled-cli` feature flag when available. The [[`mode.rs`](https://github.com/github/copilot-sdk/blob/main/mode.rs)](https://github.com/github/copilot-sdk/blob/main/rust/src/mode.rs) file defines `ClientMode::CopilotCli` configuration defaults that mirror CLI expectations.

```rust
use copilot_sdk::client::CopilotClient;

// Initialize client with bundled binary support
let client = CopilotClient::new()?;

```

### RPC Bindings and Session Handling

Auto-generated JSON-RPC method bindings live in [[`rust/src/rpc.rs`](https://github.com/github/copilot-sdk/blob/main/rust/src/rpc.rs)](https://github.com/github/copilot-sdk/blob/main/rust/src/rpc.rs), while high-level session abstraction is implemented in [[`rust/src/session.rs`](https://github.com/github/copilot-sdk/blob/main/rust/src/session.rs)](https://github.com/github/copilot-sdk/blob/main/rust/src/session.rs):

```rust
use copilot_sdk::session::Session;

// Create session context
let mut session = client.session().create().await?;

// Exchange messages
let response = session.send_message("Explain memory safety in Rust").await?;

```

## Go SDK Communication Pattern

The Go SDK follows similar architectural patterns with idiomatic error handling and context propagation.

The [`copilot.NewClient()`](https://github.com/github/copilot-sdk/blob/main/go/client.go) function in [[`go/client.go`](https://github.com/github/copilot-sdk/blob/main/go/client.go)](https://github.com/github/copilot-sdk/blob/main/go/client.go) manages the CLI server lifecycle, while [[`go/session.go`](https://github.com/github/copilot-sdk/blob/main/go/session.go)](https://github.com/github/copilot-sdk/blob/main/go/session.go) implements the `Session` interface:

```go
import (
    "context"
    "github.com/github/copilot-sdk/go/copilot"
)

func main() {
    // Start CLI server and initialize client
    client, err := copilot.NewClient()
    if err != nil { panic(err) }
    
    // Create isolated conversation context
    sess, err := client.Session().Create(context.Background())
    if err != nil { panic(err) }
    
    // Send prompt
    resp, err := sess.SendMessage(context.Background(), "Generate unit tests for this handler")
}

```

## Protocol Details and Lifecycle Management

### Session Lifecycle Methods

Each SDK exposes the same underlying CLI server capabilities through the `client.session` namespace:

- **`session.create`**: Initializes new conversation context with optional system prompts
- **`session.list`**: Returns active session IDs
- **`session.delete`**: Terminates specific session and frees resources

### Hooks and Permission Handling

The server emits lifecycle events and permission requests that the SDK routes to application callbacks. When the CLI requires user consent for tool execution or filesystem access, it sends a hook request via JSON-RPC. The SDK deserializes this in [[`client.py`](https://github.com/github/copilot-sdk/blob/main/client.py)](https://github.com/github/copilot-sdk/blob/main/python/copilot/client.py) (and equivalents), invoking the registered handler before returning the response to the server.

### Transport Configuration

While **stdio** is the default transport for embedded use cases, the SDK supports **TCP** connections for remote CLI instances. The [`ClientMode`](https://github.com/github/copilot-sdk/blob/main/python/copilot/_mode.py) configuration determines transport parameters, with optional TLS encryption for TCP sockets.

## Summary

- **Process Isolation**: The SDK spawns the Copilot CLI as a child process, isolating model inference from the host application
- **JSON-RPC Protocol**: All communication uses the standardized Copilot CLI JSON-RPC schema, with auto-generated bindings for Python, Rust, and Go
- **Transport Flexibility**: Supports both stdio (default) and TCP transports, configurable via `ClientMode` settings
- **Session Abstraction**: Conversations are scoped to session objects created via `session.create`, allowing multiple isolated contexts per process
- **Hook Integration**: Permission requests and lifecycle events flow from server to SDK to application callbacks, enabling fine-grained control over AI actions

## Frequently Asked Questions

### What transport mechanism does the Copilot SDK use to communicate with the CLI server?

The SDK communicates via **JSON-RPC** over **stdio** by default, where the CLI server runs as a child process with stdin/stdout pipes. Alternatively, SDK clients can connect to remote CLI instances using **TCP** sockets, with optional TLS encryption for secure communication.

### How does the SDK resolve the Copilot CLI binary location?

The SDK employs a hierarchical resolution strategy: first checking for embedded binaries (Rust's `bundled-cli` feature), then environment variables, and finally downloading the appropriate platform binary to a local cache using [[`python/copilot/_cli_download.py`](https://github.com/github/copilot-sdk/blob/main/python/copilot/_cli_download.py)](https://github.com/github/copilot-sdk/blob/main/python/copilot/_cli_download.py) or equivalent language-specific resolvers.

### Can SDK clients connect to an existing CLI server process?

Yes, while the default mode spawns a new child process via `_start_cli_server()`, SDKs support connecting to existing standalone CLI servers using TCP transport configuration. This enables scenarios where the CLI runs in a separate container or remote host.

### How are streaming responses handled between the server and client?

Streaming completions transmit as discrete JSON-RPC notification messages from server to client. The transport layer ([[`copilot/_jsonrpc.py`](https://github.com/github/copilot-sdk/blob/main/copilot/_jsonrpc.py)](https://github.com/github/copilot-sdk/blob/main/python/copilot/_jsonrpc.py) in Python) maintains the request context while yielding response chunks to the application as they arrive, without blocking the main execution thread.