How to Use the GitHub Copilot SDK with Go: Implementation Guide and Examples
The GitHub Copilot SDK for Go enables applications to communicate with the Copilot CLI over JSON-RPC, providing session management, tool integration, and real-time streaming through the Client and Session types in the github.com/github/copilot-sdk/go package.
The GitHub Copilot SDK for Go allows developers to programmatically interact with GitHub Copilot's LLM capabilities from within Go applications. By establishing a JSON-RPC connection to the Copilot CLI, the SDK provides a type-safe interface for creating conversational sessions, registering custom tools, and handling permission requests. This implementation guide covers the complete architecture and usage patterns based on the actual source code in the github/copilot-sdk repository.
Architecture Overview
The SDK follows a layered architecture that separates connection management from conversation state. Understanding these components is essential for building robust integrations.
Client (go/client.go)
The Client manages the connection to the Copilot CLI runtime. According to the source at [go/client.go](https://github.com/github/copilot-sdk/blob/main/go/client.go), the NewClient function (lines 55-66) initializes this connection, supporting stdio transport with an embedded CLI binary, or remote TCP/URI endpoints. The client handles the low-level JSON-RPC transport via go/internal/jsonrpc2/jsonrpc2.go.
Session (go/session.go)
A Session represents a single conversation with the assistant. Implemented in [go/session.go](https://github.com/github/copilot-sdk/blob/main/go/session.go), the session holds a unique ID, workspace state for checkpointing, and event handlers. The CreateSession method in client.go (lines 92-98) instantiates sessions using a SessionConfig that specifies models, tools, and permission handlers.
Message Flow and Events
Messages flow through Session.Send or SendAndWait, which post prompts with optional attachments. The CLI returns a messageId and streams events including assistant.message, assistant.message_delta, session.idle, and tool requests. These events are delivered to handlers registered via Session.On (lines 64-73 of session.go).
Tool Integration
Custom functions are registered as tools using helpers in [go/definetool.go](https://github.com/github/copilot-sdk/blob/main/go/definetool.go). The SDK automatically marshals LLM arguments into Go structs and returns results to the CLI. The execution path runs through executeToolAndRespond in session.go (lines 321-340).
Permission and UI Handling
Before executing actions like file writes or shell commands, the SDK invokes the OnPermissionRequest handler configured in SessionConfig. Use copilot.PermissionHandler.ApproveAll for automated workflows, or return specific rpc.PermissionDecision* variants for manual approval. For user interaction, implement OnUserInputRequest to handle elicitation requests.
Installation and Setup
Install the SDK using Go modules:
go get github.com/github/copilot-sdk/go
To embed the Copilot CLI binary directly into your Go application, install the bundler tool:
go get -tool github.com/github/copilot-sdk/go/cmd/bundler
go tool bundler
This extracts the CLI at runtime via go/internal/embeddedcli/embeddedcli.go, eliminating external dependencies.
Basic Hello World Implementation
This example demonstrates the minimal setup required to send a prompt and receive a response:
package main
import (
"context"
"fmt"
"log"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
// Initialize client with default stdio transport
client := copilot.NewClient(nil)
// Start the runtime
if err := client.Start(context.Background()); err != nil {
log.Fatal(err)
}
defer client.Stop()
// Create session with permission handler
sess, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
Model: "gpt-4",
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
log.Fatal(err)
}
defer sess.Disconnect()
// Subscribe to assistant messages
unsub := sess.On(func(ev copilot.SessionEvent) {
if d, ok := ev.Data.(*copilot.AssistantMessageData); ok {
fmt.Println("Assistant:", d.Content)
}
})
defer unsub()
// Send prompt
if _, err = sess.Send(context.Background(),
copilot.MessageOptions{Prompt: "What is 2 + 2?"}); err != nil {
log.Fatal(err)
}
}
The NewClient(nil) call uses the embedded CLI when ClientOptions.Connection is empty, as implemented in the Client struct initialization.
Advanced Features
Registering Type-Safe Tools
Define custom tools using structured Go types for automatic JSON schema generation:
package main
import (
"context"
"fmt"
"log"
copilot "github.com/github/copilot-sdk/go"
)
type WeatherParams struct {
City string `json:"city" jsonschema:"The city name to look up weather for"`
}
var weatherTool = copilot.DefineTool(
"get_weather",
"Fetch the current weather for a city",
func(p WeatherParams, inv copilot.ToolInvocation) (any, error) {
return map[string]string{
"city": p.City,
"status": "Sunny",
"temp": "23°C",
}, nil
})
func main() {
client := copilot.NewClient(nil)
client.Start(context.Background())
defer client.Stop()
s, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
Model: "gpt-4",
Tools: []copilot.Tool{weatherTool},
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
log.Fatal(err)
}
defer s.Disconnect()
unsub := s.On(func(ev copilot.SessionEvent) {
if msg, ok := ev.Data.(*copilot.AssistantMessageData); ok {
fmt.Println("Assistant:", msg.Content)
}
})
defer unsub()
s.Send(context.Background(),
copilot.MessageOptions{Prompt: "What's the weather in Paris?"})
}
The DefineTool helper in definetool.go uses reflection to generate JSON schemas from struct tags, enabling type-safe argument marshaling.
Streaming Assistant Responses
Enable real-time token streaming to display responses as they are generated:
func main() {
client := copilot.NewClient(nil)
client.Start(context.Background())
defer client.Stop()
s, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
Model: "gpt-4",
Streaming: copilot.Bool(true), // Enable streaming
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
log.Fatal(err)
}
defer s.Disconnect()
s.On(func(ev copilot.SessionEvent) {
switch d := ev.Data.(type) {
case *copilot.AssistantMessageDeltaData:
fmt.Print(d.DeltaContent) // Incremental tokens
case *copilot.AssistantMessageData:
fmt.Println("\n--- Full message completed ---")
fmt.Println(d.Content)
}
})
s.Send(context.Background(),
copilot.MessageOptions{Prompt: "Tell me a short story about a robot."})
}
Set Streaming: copilot.Bool(true) in SessionConfig (lines 78-80 of client.go) to receive AssistantMessageDeltaData events for each token chunk.
Connecting to Custom Providers (Ollama)
Use alternative LLM providers by configuring the Provider field in SessionConfig:
func main() {
client := copilot.NewClient(nil)
client.Start(context.Background())
defer client.Stop()
s, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
Model: "llama2:7b",
Provider: &copilot.ProviderConfig{
Type: "openai", // Ollama's compatible endpoint
BaseURL: "http://localhost:11434/v1",
// APIKey omitted for local Ollama
},
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
log.Fatal(err)
}
defer s.Disconnect()
s.Send(context.Background(),
copilot.MessageOptions{Prompt: "Write a haiku about spring."})
}
The ProviderConfig struct (lines 1017-1022 of client.go) supports Type, BaseURL, and APIKey fields for OpenAI-compatible endpoints.
Handling UI Elicitation Requests
Implement interactive workflows by responding to user input requests:
func main() {
client := copilot.NewClient(nil)
client.Start(context.Background())
defer client.Stop()
s, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
Model: "gpt-4",
OnUserInputRequest: func(req copilot.UserInputRequest, inv copilot.UserInputInvocation) (copilot.UserInputResponse, error) {
// In production, replace with actual user input collection
answer := "yes"
return copilot.UserInputResponse{
Answer: answer,
WasFreeform: true,
}, nil
},
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
log.Fatal(err)
}
defer s.Disconnect()
s.Send(context.Background(),
copilot.MessageOptions{Prompt: "Ask me a question, then answer it."})
}
The OnUserInputRequest handler (lines 86-88 of client.go) integrates with the ask_user tool, processed in session.go lines 555-567.
Embedding the CLI Binary
The SDK supports embedding the Copilot CLI using Go 1.16's embed package. After running go tool bundler, the SDK automatically extracts and executes the embedded binary when NewClient is called with nil options. This approach ensures your application remains self-contained without requiring separate CLI installation. See the embedding implementation in [go/internal/embeddedcli/embeddedcli.go](https://github.com/github/copilot-sdk/blob/main/go/internal/embeddedcli/embeddedcli.go) and the README (lines 85-95) for configuration details.
Summary
- The GitHub Copilot SDK for Go provides JSON-RPC communication with the Copilot CLI through the
ClientandSessiontypes ingo/client.goandgo/session.go. - Tool integration uses
DefineToolingo/definetool.gofor automatic JSON schema generation from Go structs. - Streaming responses are enabled via the
Streamingflag inSessionConfig, delivering incremental content throughAssistantMessageDeltaDataevents. - Custom providers like Ollama are supported through
ProviderConfigwith OpenAI-compatible endpoints. - Permission handling and UI elicitation are configured through handler functions in
SessionConfigto control tool execution and user interaction. - Binary embedding via the bundler tool creates self-contained executables that extract the CLI at runtime.
Frequently Asked Questions
How does permission handling work in the GitHub Copilot SDK for Go?
The SDK invokes the OnPermissionRequest handler before executing sensitive actions like file writes or shell commands. You can use copilot.PermissionHandler.ApproveAll for automation, or implement custom logic returning rpc.PermissionDecisionAllow, rpc.PermissionDecisionDeny, or rpc.PermissionDecisionEscalate based on your security requirements.
Can I use the GitHub Copilot SDK with Go to connect to local LLMs like Ollama?
Yes, configure the Provider field in SessionConfig with Type: "openai" and BaseURL pointing to your local endpoint (e.g., http://localhost:11434/v1 for Ollama). This allows the SDK to communicate with any OpenAI-compatible API, including local inference servers.
What is the difference between Client and Session in the Copilot Go SDK?
The Client manages the transport layer connection to the Copilot CLI runtime and is responsible for starting and stopping the JSON-RPC communication. A Session represents a single conversation context with its own ID, workspace state, and event handlers, created via Client.CreateSession and supporting multiple concurrent instances per client.
How does the SDK automatically generate JSON schemas for tools?
The DefineTool function in go/definetool.go uses Go reflection to analyze the input struct tags (particularly the jsonschema tag) and generates JSON Schema definitions automatically. This enables type-safe unmarshaling of LLM arguments into Go structs without manual schema writing.
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 →