How to Use the GitHub Copilot SDK with Rust: Building Async AI Assistants
The GitHub Copilot SDK for Rust provides an async-first JSON-RPC client that wraps the Copilot CLI, enabling you to embed AI-powered assistants with managed sessions, custom tools, and real-time event streaming.
The github/copilot-sdk repository delivers a high-level Rust crate designed specifically for integrating GitHub Copilot capabilities into native Rust applications. This GitHub Copilot SDK with Rust abstracts the complexity of process management and authentication by communicating with the Copilot CLI over JSON-RPC, offering a typed, non-blocking API built on Tokio that handles everything from binary resolution to OpenTelemetry trace propagation.
Core Architecture
The SDK centers on four primary components that manage the CLI lifecycle and conversation state.
Client (rust/src/lib.rs#L47-L75)
The Client struct serves as the main entry point, spawning or connecting to the Copilot CLI process and managing JSON-RPC traffic. It is Clone-able, allowing shared access across async tasks without restarting the underlying process.
ClientOptions (rust/src/lib.rs#L90-L108)
Configuration follows a builder pattern through ClientOptions, which specifies the CLI binary resolution strategy (CliProgram), transport method (Transport), authentication tokens, telemetry settings, and operational mode (ClientMode).
Session (rust/src/session.rs#L136-L155)
A Session represents a single Copilot conversation thread. It exposes methods like send, send_and_wait, and subscribe for event streaming, plus disconnect for lifecycle management.
Handler and Tool Modules
The handler module (rust/src/handler.rs) defines traits for UserInputHandler and PermissionHandler, letting you inject custom logic for approvals and prompts. The tool module (rust/src/tool.rs) provides a typed framework for exposing Rust functions as callable tools to the Copilot agent.
Initializing the Client
To begin, create a Client using Client::start with default or custom options. The SDK automatically resolves the CLI binary through COPILOT_CLI_PATH, embedded binaries (via the bundled-cli feature), or development cache paths.
use github_copilot_sdk::{Client, ClientOptions};
#[tokio::main]
async fn main() -> Result<(), github_copilot_sdk::Error> {
// Spawns the Copilot CLI process automatically
let client = Client::start(ClientOptions::default()).await?;
Ok(())
}
Transport Options
The Transport enum supports three modes:
- Stdio (default): Spawns CLI as a subprocess and communicates over stdin/stdout.
- Tcp: Spawns CLI with
--portand connects via TCP. - External: Connects to a pre-existing server instance.
Creating and Configuring Sessions
Before spawning a session, build a SessionConfig (rust/src/types.rs#L109-120) to define behavior handlers and streaming preferences.
use github_copilot_sdk::types::{SessionConfig, SessionId};
use github_copilot_sdk::handler::{ApproveAllHandler, UserInputHandler};
use std::sync::Arc;
let config = SessionConfig::default()
.with_permission_handler(Arc::new(ApproveAllHandler))
.with_streaming(true);
Create the session using client.create_session(config).await?, which returns a Session object containing a unique SessionId and event channels.
Sending Messages and Streaming Responses
The SDK supports both fire-and-forget and blocking message patterns via MessageOptions.
Blocking with Timeout
Use send_and_wait when you need the complete response before proceeding:
use github_copilot_sdk::types::MessageOptions;
use std::time::Duration;
session
.send_and_wait(
MessageOptions::new("Explain this code")
.with_wait_timeout(Duration::from_secs(120))
)
.await?;
Streaming Events
For real-time token streaming, subscribe to the session's event receiver before sending:
let mut events = session.subscribe();
// Spawn listener task
tokio::spawn(async move {
while let Ok(event) = events.recv().await {
if event.event_type == "assistant.message_delta" {
if let Some(delta) = event.data.get("deltaContent").and_then(|c| c.as_str()) {
print!("{delta}");
}
}
}
});
Handling User Input and Permissions
Implement the UserInputHandler trait to intercept agent prompts and provide responses programmatically or via UI.
use async_trait::async_trait;
use github_copilot_sdk::handler::{UserInputHandler, UserInputResponse};
use github_copilot_sdk::types::SessionId;
struct StdinHandler;
#[async_trait]
impl UserInputHandler for StdinHandler {
async fn handle(
&self,
_session_id: SessionId,
question: String,
_choices: Option<Vec<String>>,
_allow_freeform: Option<bool>
) -> Option<UserInputResponse> {
println!("[agent asks] {}", question);
let mut input = String::new();
std::io::stdin().read_line(&mut input).ok()?;
Some(UserInputResponse {
answer: input.trim().to_string(),
was_freeform: true
})
}
}
For automated environments, ApproveAllHandler automatically grants all permission requests, while ClientMode::Empty disables global keychain access for multi-tenant security.
Implementing Custom Tools
Expose internal Rust functions to the Copilot agent by implementing the Tool trait from rust/src/tool.rs.
use async_trait::async_trait;
use github_copilot_sdk::tool::{Tool, ToolResult, ToolResultOutput};
struct EchoTool;
#[async_trait]
impl Tool for EchoTool {
fn name(&self) -> &'static str { "echo" }
fn description(&self) -> &'static str {
"Echoes the provided input back to the agent."
}
async fn run(&self, input: String) -> ToolResult {
ToolResult::new(ToolResultOutput::new(input))
}
}
Register tools via SessionConfig::with_tool_set before creating the session. See rust/examples/tool_server.rs for a complete implementation including the tool server setup.
Working with Virtual Filesystems
For sandboxed environments, configure SessionFS (rust/src/session_fs.rs#L60-L85) to provide a virtual filesystem backed by SQLite instead of host disk access.
use github_copilot_sdk::session_fs::{SessionFsConfig, SessionFsCapabilities};
use std::path::PathBuf;
let fs_config = SessionFsConfig {
initial_cwd: PathBuf::from("/workspace"),
session_state_path: PathBuf::from("/tmp/session_state.sqlite"),
capabilities: Some(SessionFsCapabilities { sqlite: true }),
..Default::default()
};
let client = Client::start(
ClientOptions::default()
.with_session_fs(fs_config)
).await?;
This pattern is essential for stateful agents or code generation scenarios requiring isolated filesystem contexts.
Security and Telemetry Considerations
The SDK implements several security-conscious design decisions:
- Token Redaction: Authentication tokens display as
<redacted>inDebugoutput to prevent accidental logging. - Environment Isolation: Telemetry and W3C trace context propagate via environment variables to the CLI process without exposing sensitive data to the application logs.
- Binary Verification: Automatic resolution follows a strict precedence: explicit
COPILOT_CLI_PATH, embedded binary, then development cache.
Summary
- The GitHub Copilot SDK with Rust wraps the CLI via JSON-RPC, providing async methods in
rust/src/lib.rsandrust/src/session.rs. - Client manages process lifecycle and supports
Clonefor shared access; Session handles individual conversation state. - Configure behavior through ClientOptions (transport, auth, telemetry) and SessionConfig (handlers, streaming, tools).
- Implement UserInputHandler and PermissionHandler traits to customize interaction flows.
- Use Tool trait implementations to expose Rust functions as callable capabilities to Copilot agents.
- Enable SessionFS for sandboxed filesystem operations via SQLite-backed virtual directories.
Frequently Asked Questions
How does the SDK communicate with the Copilot service?
The SDK does not communicate directly with GitHub's APIs. Instead, it spawns the official Copilot CLI as a subprocess and exchanges JSON-RPC messages over stdio (or TCP), as implemented in rust/src/lib.rs#L78-L86. The CLI handles all upstream authentication and API calls, while the SDK manages the process lifecycle and Rust-side abstractions.
Can I use the SDK in a multi-tenant or serverless environment?
Yes. Set ClientMode::Empty in your ClientOptions to disable global keychain access, and use ApproveAllHandler for automated permission management. Combine this with SessionFS for isolated virtual filesystems per session, ensuring tenants cannot access each other's data or host filesystem.
What async runtime does the GitHub Copilot SDK require?
The SDK is built exclusively on Tokio and requires a Tokio runtime. All I/O operations are non-blocking, and the internal jsonrpc::JsonRpcClient uses background tasks to dispatch notifications to subscribers while buffering requests and responses.
How do I handle authentication tokens securely?
The SDK reads tokens from environment variables or configuration but redacts them in all Debug implementations, displaying <redacted> instead of the actual value. Never log ClientOptions using {:?} if you have manually included tokens in custom fields; rely on the built-in redaction mechanisms in rust/src/lib.rs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →