# How to Use the GitHub Copilot SDK with Go: Implementation Guide and Examples

> Learn to use the GitHub Copilot SDK with Go to integrate Copilot CLI into your applications. Explore session management, tool integration, and real-time streaming with this implementation guide.

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: how-to-guide
- Published: 2026-06-06

---

**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`](https://github.com/github/copilot-sdk/blob/main/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)](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`](https://github.com/github/copilot-sdk/blob/main/go/internal/jsonrpc2/jsonrpc2.go).

### Session ([`go/session.go`](https://github.com/github/copilot-sdk/blob/main/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)](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`](https://github.com/github/copilot-sdk/blob/main/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`](https://github.com/github/copilot-sdk/blob/main/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)](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`](https://github.com/github/copilot-sdk/blob/main/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:

```bash
go get github.com/github/copilot-sdk/go

```

To embed the Copilot CLI binary directly into your Go application, install the bundler tool:

```bash
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`](https://github.com/github/copilot-sdk/blob/main/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:

```go
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:

```go
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`](https://github.com/github/copilot-sdk/blob/main/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:

```go
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`](https://github.com/github/copilot-sdk/blob/main/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`:

```go
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`](https://github.com/github/copilot-sdk/blob/main/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:

```go
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`](https://github.com/github/copilot-sdk/blob/main/client.go)) integrates with the `ask_user` tool, processed in [`session.go`](https://github.com/github/copilot-sdk/blob/main/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)](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 `Client` and `Session` types in [`go/client.go`](https://github.com/github/copilot-sdk/blob/main/go/client.go) and [`go/session.go`](https://github.com/github/copilot-sdk/blob/main/go/session.go).
- **Tool integration** uses `DefineTool` in [`go/definetool.go`](https://github.com/github/copilot-sdk/blob/main/go/definetool.go) for automatic JSON schema generation from Go structs.
- **Streaming responses** are enabled via the `Streaming` flag in `SessionConfig`, delivering incremental content through `AssistantMessageDeltaData` events.
- **Custom providers** like Ollama are supported through `ProviderConfig` with OpenAI-compatible endpoints.
- **Permission handling** and **UI elicitation** are configured through handler functions in `SessionConfig` to 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`](https://github.com/github/copilot-sdk/blob/main/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.