diff --git a/docs/auth/byok.md b/docs/auth/byok.md
index a4a131913b..83602c5743 100644
--- a/docs/auth/byok.md
+++ b/docs/auth/byok.md
@@ -164,6 +164,36 @@ Console.WriteLine(response?.Data.Content);
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient();
+client.start().get();
+
+var session = client.createSession(new SessionConfig()
+ .setModel("gpt-5.2-codex") // Your deployment name
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setProvider(new ProviderConfig()
+ .setType("openai")
+ .setBaseUrl("https://your-resource.openai.azure.com/openai/v1/")
+ .setWireApi("responses") // Use "completions" for older models
+ .setApiKey(System.getenv("FOUNDRY_API_KEY")))
+).get();
+
+var response = session.sendAndWait(new MessageOptions()
+ .setPrompt("What is 2+2?")).get();
+System.out.println(response.getData().content());
+
+client.stop().get();
+```
+
+
+
## Provider Configuration Reference
### ProviderConfig Fields
@@ -412,6 +442,28 @@ var client = new CopilotClient(new CopilotClientOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.json.*;
+import java.util.concurrent.CompletableFuture;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ .setOnListModels(() -> CompletableFuture.completedFuture(List.of(
+ new ModelInfo()
+ .setId("my-custom-model")
+ .setName("My Custom Model")
+ .setCapabilities(new ModelCapabilities()
+ .setSupports(new ModelSupports().setVision(false).setReasoningEffort(false))
+ .setLimits(new ModelLimits().setMaxContextWindowTokens(128000)))
+ )))
+);
+```
+
+
+
Results are cached after the first call, just like the default behavior. The handler completely replaces the CLI's `models.list` RPC — no fallback to the server occurs.
## Limitations
diff --git a/docs/auth/index.md b/docs/auth/index.md
index 2f36d8b21d..069556a9b6 100644
--- a/docs/auth/index.md
+++ b/docs/auth/index.md
@@ -85,6 +85,19 @@ await using var client = new CopilotClient();
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+
+// Default: uses logged-in user credentials
+var client = new CopilotClient();
+client.start().get();
+```
+
+
+
**When to use:**
- Desktop applications where users interact directly
- Development and testing environments
@@ -189,6 +202,22 @@ await using var client = new CopilotClient(new CopilotClientOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ .setGitHubToken(userAccessToken) // Token from OAuth flow
+ .setUseLoggedInUser(false) // Don't use stored CLI credentials
+);
+client.start().get();
+```
+
+
+
**Supported token types:**
- `gho_` - OAuth user access tokens
- `ghu_` - GitHub App user access tokens
@@ -351,6 +380,21 @@ await using var client = new CopilotClient(new CopilotClientOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ .setUseLoggedInUser(false) // Only use explicit tokens
+);
+client.start().get();
+```
+
+
+
## Next Steps
- [BYOK Documentation](./byok.md) - Learn how to use your own API keys
diff --git a/docs/features/custom-agents.md b/docs/features/custom-agents.md
index c1d01ba322..cc5d709213 100644
--- a/docs/features/custom-agents.md
+++ b/docs/features/custom-agents.md
@@ -205,6 +205,41 @@ await using var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setCustomAgents(List.of(
+ new CustomAgentConfig()
+ .setName("researcher")
+ .setDisplayName("Research Agent")
+ .setDescription("Explores codebases and answers questions using read-only tools")
+ .setTools(List.of("grep", "glob", "view"))
+ .setPrompt("You are a research assistant. Analyze code and answer questions. Do not modify any files."),
+ new CustomAgentConfig()
+ .setName("editor")
+ .setDisplayName("Editor Agent")
+ .setDescription("Makes targeted code changes")
+ .setTools(List.of("view", "edit", "bash"))
+ .setPrompt("You are a code editor. Make minimal, surgical changes to files as requested.")
+ ))
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+}
+```
+
+
+
## Configuration Reference
| Property | Type | Required | Description |
@@ -316,6 +351,28 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+
+```java
+var session = client.createSession(
+ new SessionConfig()
+ .setCustomAgents(List.of(
+ new CustomAgentConfig()
+ .setName("researcher")
+ .setPrompt("You are a research assistant. Analyze code and answer questions."),
+ new CustomAgentConfig()
+ .setName("editor")
+ .setPrompt("You are a code editor. Make minimal, surgical changes.")
+ ))
+ .setAgent("researcher") // Pre-select the researcher agent
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+```
+
+
+
## How Sub-Agent Delegation Works
When you send a prompt to a session with custom agents, the runtime evaluates whether to delegate to a sub-agent:
@@ -561,6 +618,34 @@ await session.SendAndWaitAsync(new MessageOptions
+
+Java
+
+```java
+session.on(event -> {
+ if (event instanceof SubagentStartedEvent e) {
+ System.out.println("▶ Sub-agent started: " + e.getData().agentDisplayName());
+ System.out.println(" Description: " + e.getData().agentDescription());
+ System.out.println(" Tool call ID: " + e.getData().toolCallId());
+ } else if (event instanceof SubagentCompletedEvent e) {
+ System.out.println("✅ Sub-agent completed: " + e.getData().agentName());
+ } else if (event instanceof SubagentFailedEvent e) {
+ System.out.println("❌ Sub-agent failed: " + e.getData().agentName());
+ System.out.println(" Error: " + e.getData().error());
+ } else if (event instanceof SubagentSelectedEvent e) {
+ System.out.println("🎯 Agent selected: " + e.getData().agentDisplayName());
+ } else if (event instanceof SubagentDeselectedEvent e) {
+ System.out.println("↩ Agent deselected, returning to parent");
+ }
+});
+
+var response = session.sendAndWait(
+ new MessageOptions().setPrompt("Research how authentication works in this codebase")
+).get();
+```
+
+
+
## Building an Agent Tree UI
Sub-agent events include `toolCallId` fields that let you reconstruct the execution tree. Here's a pattern for tracking agent activity:
diff --git a/docs/features/hooks.md b/docs/features/hooks.md
index 1a01c5f1a4..e1b9d88d8f 100644
--- a/docs/features/hooks.md
+++ b/docs/features/hooks.md
@@ -195,6 +195,33 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var hooks = new SessionHooks()
+ .setOnSessionStart((input, inv) -> CompletableFuture.completedFuture(null))
+ .setOnPreToolUse((input, inv) -> CompletableFuture.completedFuture(null))
+ .setOnPostToolUse((input, inv) -> CompletableFuture.completedFuture(null));
+ // ... add only the hooks you need
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setHooks(hooks)
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+}
+```
+
+
+
> **Tip:** Every hook handler receives an `invocation` parameter containing the `sessionId`, which is useful for correlating logs and maintaining per-session state.
---
@@ -380,6 +407,32 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+var readOnlyTools = Set.of("read_file", "glob", "grep", "view");
+
+var hooks = new SessionHooks()
+ .setOnPreToolUse((input, invocation) -> {
+ if (!readOnlyTools.contains(input.getToolName())) {
+ return CompletableFuture.completedFuture(
+ PreToolUseHookOutput.deny(
+ "Only read-only tools are allowed. \"" + input.getToolName() + "\" was blocked.")
+ );
+ }
+ return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
+ });
+
+var session = client.createSession(
+ new SessionConfig()
+ .setHooks(hooks)
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+```
+
+
+
### Restrict file access to specific directories
```typescript
diff --git a/docs/features/image-input.md b/docs/features/image-input.md
index 047dc62806..a5902c0d40 100644
--- a/docs/features/image-input.md
+++ b/docs/features/image-input.md
@@ -219,6 +219,34 @@ await session.SendAsync(new MessageOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ session.send(new MessageOptions()
+ .setPrompt("Describe what you see in this image")
+ .setAttachments(List.of(
+ new Attachment("file", "/absolute/path/to/screenshot.png", "screenshot.png")
+ ))
+ ).get();
+}
+```
+
+
+
## Quick Start — Blob Attachment
When you already have image data in memory (e.g., a screenshot captured by your app, or an image fetched from an API), use a blob attachment to send it directly without writing to disk.
@@ -400,6 +428,38 @@ await session.SendAsync(new MessageOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ var base64ImageData = "..."; // your base64-encoded image
+ session.send(new MessageOptions()
+ .setPrompt("Describe what you see in this image")
+ .setAttachments(List.of(
+ new BlobAttachment()
+ .setData(base64ImageData)
+ .setMimeType("image/png")
+ .setDisplayName("screenshot.png")
+ ))
+ ).get();
+}
+```
+
+
+
## Supported Formats
Supported image formats include JPG, PNG, GIF, and other common image types. For file attachments, the runtime reads the image from disk and converts it as needed. For blob attachments, you provide the base64 data and MIME type directly. Use PNG or JPEG for best results, as these are the most widely supported formats.
diff --git a/docs/features/index.md b/docs/features/index.md
index 02fabe5ab9..bbd005cb09 100644
--- a/docs/features/index.md
+++ b/docs/features/index.md
@@ -1,6 +1,6 @@
# Features
-These guides cover the capabilities you can add to your Copilot SDK application. Each guide includes examples in all supported languages (TypeScript, Python, Go, and .NET).
+These guides cover the capabilities you can add to your Copilot SDK application. Each guide includes examples in all supported languages (TypeScript, Python, Go, .NET, and Java).
> **New to the SDK?** Start with the [Getting Started tutorial](../getting-started.md) first, then come back here to add more capabilities.
diff --git a/docs/features/skills.md b/docs/features/skills.md
index 3bc9294aa1..9456d7e7a4 100644
--- a/docs/features/skills.md
+++ b/docs/features/skills.md
@@ -140,6 +140,36 @@ await session.SendAndWaitAsync(new MessageOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setSkillDirectories(List.of(
+ "./skills/code-review",
+ "./skills/documentation"
+ ))
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ // Copilot now has access to skills in those directories
+ session.sendAndWait(new MessageOptions()
+ .setPrompt("Review this code for security issues")
+ ).get();
+}
+```
+
+
+
## Disabling Skills
Disable specific skills while keeping others active:
@@ -243,6 +273,20 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+var session = client.createSession(
+ new SessionConfig()
+ .setSkillDirectories(List.of("./skills"))
+ .setDisabledSkills(List.of("experimental-feature", "deprecated-tool"))
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+```
+
+
+
## Skill Directory Structure
Each skill is a named subdirectory containing a `SKILL.md` file:
diff --git a/docs/features/steering-and-queueing.md b/docs/features/steering-and-queueing.md
index a3e1b6d2b1..f4acd00069 100644
--- a/docs/features/steering-and-queueing.md
+++ b/docs/features/steering-and-queueing.md
@@ -178,6 +178,38 @@ await session.SendAsync(new MessageOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ // Start a long-running task
+ session.send(new MessageOptions()
+ .setPrompt("Refactor the authentication module to use sessions")
+ ).get();
+
+ // While the agent is working, steer it
+ session.send(new MessageOptions()
+ .setPrompt("Actually, use JWT tokens instead of sessions")
+ .setMode("immediate")
+ ).get();
+}
+```
+
+
+
### How Steering Works Internally
1. The message is added to the runtime's `ImmediatePromptProcessor` queue
@@ -388,6 +420,43 @@ await session.SendAsync(new MessageOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ // Send an initial task
+ session.send(new MessageOptions().setPrompt("Set up the project structure")).get();
+
+ // Queue follow-up tasks while the agent is busy
+ session.send(new MessageOptions()
+ .setPrompt("Add unit tests for the auth module")
+ .setMode("enqueue")
+ ).get();
+
+ session.send(new MessageOptions()
+ .setPrompt("Update the README with setup instructions")
+ .setMode("enqueue")
+ ).get();
+
+ // Messages are processed in FIFO order after each turn completes
+}
+```
+
+
+
### How Queueing Works Internally
1. The message is added to the session's `itemQueue` as a `QueuedItem`
diff --git a/docs/features/streaming-events.md b/docs/features/streaming-events.md
index d03ed95fa7..926af1b9ef 100644
--- a/docs/features/streaming-events.md
+++ b/docs/features/streaming-events.md
@@ -191,6 +191,21 @@ session.On(evt =>
+
+Java
+
+```java
+// All events
+session.on(event -> System.out.println(event.getType()));
+
+// Specific event type — data is narrowed to the matching class
+session.on(AssistantMessageDeltaEvent.class, event ->
+ System.out.print(event.getData().deltaContent())
+);
+```
+
+
+
> **Tip (Python / Go):** These SDKs use a single `Data` class/struct with all possible fields as optional/nullable. Only the fields listed in the tables below are populated for each event type — the rest will be `None` / `nil`.
>
> **Tip (.NET):** The .NET SDK uses separate, strongly-typed data classes per event (e.g., `AssistantMessageDeltaData`), so only the relevant fields exist on each type.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index e0dc9e1cea..dea074aeda 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -20,7 +20,7 @@ Before you begin, make sure you have:
- **GitHub Copilot CLI** installed and authenticated ([Installation guide](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli))
- Your preferred language runtime:
- - **Node.js** 18+ or **Python** 3.8+ or **Go** 1.21+ or **.NET** 8.0+
+ - **Node.js** 18+ or **Python** 3.11+ or **Go** 1.21+ or **Java** 17+ or **.NET** 8.0+
Verify the CLI is working:
@@ -92,6 +92,29 @@ dotnet add package GitHub.Copilot.SDK
+
+Java
+
+First, create a new directory and initialize your project.
+
+**Maven** — add to your `pom.xml`:
+
+```xml
+
+ com.github
+ copilot-sdk-java
+ ${copilot.sdk.version}
+
+```
+
+**Gradle** — add to your `build.gradle`:
+
+```groovy
+implementation 'com.github:copilot-sdk-java:${copilotSdkVersion}'
+```
+
+
+
## Step 2: Send Your First Message
Create a new file and add the following code. This is the simplest way to use the SDK—about 5 lines of code.
@@ -228,6 +251,45 @@ dotnet run
+
+Java
+
+Create `HelloCopilot.java`:
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+public class HelloCopilot {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig().setModel("gpt-4.1")
+ ).get();
+
+ var response = session.sendAndWait(
+ new MessageOptions().setPrompt("What is 2 + 2?")
+ ).get();
+
+ System.out.println(response.getData().content());
+
+ client.stop().get();
+ }
+ }
+}
+```
+
+Run it:
+
+```bash
+javac -cp copilot-sdk.jar HelloCopilot.java && java -cp .:copilot-sdk.jar HelloCopilot
+```
+
+
+
**You should see:**
```
@@ -394,6 +456,47 @@ await session.SendAndWaitAsync(new MessageOptions { Prompt = "Tell me a short jo
+
+Java
+
+Update `HelloCopilot.java`:
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+public class HelloCopilot {
+ public static void main(String[] args) throws Exception {
+ try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setStreaming(true)
+ ).get();
+
+ // Listen for response chunks
+ session.on(AssistantMessageDeltaEvent.class, delta -> {
+ System.out.print(delta.getData().deltaContent());
+ });
+ session.on(SessionIdleEvent.class, idle -> {
+ System.out.println(); // New line when done
+ });
+
+ session.sendAndWait(
+ new MessageOptions().setPrompt("Tell me a short joke")
+ ).get();
+
+ client.stop().get();
+ }
+ }
+}
+```
+
+
+
Run the code again. You'll see the response appear word by word.
### Event Subscription Methods
@@ -591,6 +694,30 @@ unsubscribe.Dispose();
+
+Java
+
+```java
+// Subscribe to all events
+var unsubscribe = session.on(event -> {
+ System.out.println("Event: " + event.getType());
+});
+
+// Subscribe to a specific event type
+session.on(AssistantMessageEvent.class, msg -> {
+ System.out.println("Message: " + msg.getData().content());
+});
+
+session.on(SessionIdleEvent.class, idle -> {
+ System.out.println("Session is idle");
+});
+
+// Later, to unsubscribe:
+unsubscribe.close();
+```
+
+
+
## Step 4: Add a Custom Tool
Now for the powerful part. Let's give Copilot the ability to call your code by defining a custom tool. We'll create a simple weather lookup tool.
@@ -841,6 +968,79 @@ await session.SendAndWaitAsync(new MessageOptions
+
+Java
+
+Update `HelloCopilot.java`:
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+
+public class HelloCopilot {
+ public static void main(String[] args) throws Exception {
+ var random = new Random();
+ var conditions = List.of("sunny", "cloudy", "rainy", "partly cloudy");
+
+ // Define a tool that Copilot can call
+ var getWeather = ToolDefinition.create(
+ "get_weather",
+ "Get the current weather for a city",
+ Map.of(
+ "type", "object",
+ "properties", Map.of(
+ "city", Map.of("type", "string", "description", "The city name")
+ ),
+ "required", List.of("city")
+ ),
+ invocation -> {
+ var city = (String) invocation.getArguments().get("city");
+ var temp = random.nextInt(30) + 50;
+ var condition = conditions.get(random.nextInt(conditions.size()));
+ return CompletableFuture.completedFuture(Map.of(
+ "city", city,
+ "temperature", temp + "°F",
+ "condition", condition
+ ));
+ }
+ );
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setStreaming(true)
+ .setTools(List.of(getWeather))
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ session.on(AssistantMessageDeltaEvent.class, delta -> {
+ System.out.print(delta.getData().deltaContent());
+ });
+ session.on(SessionIdleEvent.class, idle -> {
+ System.out.println();
+ });
+
+ session.sendAndWait(
+ new MessageOptions().setPrompt("What's the weather like in Seattle and Tokyo?")
+ ).get();
+
+ client.stop().get();
+ }
+ }
+}
+```
+
+
+
Run it and you'll see Copilot call your tool to get weather data, then respond with the results!
## Step 5: Build an Interactive Assistant
@@ -1165,6 +1365,100 @@ dotnet run
+
+Java
+
+Create `WeatherAssistant.java`:
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Scanner;
+import java.util.concurrent.CompletableFuture;
+
+public class WeatherAssistant {
+ public static void main(String[] args) throws Exception {
+ var random = new Random();
+ var conditions = List.of("sunny", "cloudy", "rainy", "partly cloudy");
+
+ var getWeather = ToolDefinition.create(
+ "get_weather",
+ "Get the current weather for a city",
+ Map.of(
+ "type", "object",
+ "properties", Map.of(
+ "city", Map.of("type", "string", "description", "The city name")
+ ),
+ "required", List.of("city")
+ ),
+ invocation -> {
+ var city = (String) invocation.getArguments().get("city");
+ var temp = random.nextInt(30) + 50;
+ var condition = conditions.get(random.nextInt(conditions.size()));
+ return CompletableFuture.completedFuture(Map.of(
+ "city", city,
+ "temperature", temp + "°F",
+ "condition", condition
+ ));
+ }
+ );
+
+ try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setModel("gpt-4.1")
+ .setStreaming(true)
+ .setOnPermissionRequest(request ->
+ CompletableFuture.completedFuture(PermissionDecision.allow())
+ )
+ .setTools(List.of(getWeather))
+ ).get();
+
+ session.on(AssistantMessageDeltaEvent.class, delta -> {
+ System.out.print(delta.getData().deltaContent());
+ });
+ session.on(SessionIdleEvent.class, idle -> {
+ System.out.println();
+ });
+
+ System.out.println("🌤️ Weather Assistant (type 'exit' to quit)");
+ System.out.println(" Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n");
+
+ var scanner = new Scanner(System.in);
+ while (true) {
+ System.out.print("You: ");
+ if (!scanner.hasNextLine()) break;
+ var input = scanner.nextLine();
+ if (input.equalsIgnoreCase("exit")) break;
+
+ System.out.print("Assistant: ");
+ session.sendAndWait(
+ new MessageOptions().setPrompt(input)
+ ).get();
+ System.out.println("\n");
+ }
+
+ client.stop().get();
+ }
+ }
+}
+```
+
+Run with:
+
+```bash
+javac -cp copilot-sdk.jar WeatherAssistant.java && java -cp .:copilot-sdk.jar WeatherAssistant
+```
+
+
+
**Example session:**
@@ -1273,7 +1567,7 @@ Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_conte
Each override supports four actions: `replace`, `remove`, `append`, and `prepend`. Unknown section IDs are handled gracefully — content is appended to additional instructions and a warning is emitted; `remove` on unknown sections is silently ignored.
-See the language-specific SDK READMEs for examples in [TypeScript](../nodejs/README.md), [Python](../python/README.md), [Go](../go/README.md), and [C#](../dotnet/README.md).
+See the language-specific SDK READMEs for examples in [TypeScript](../nodejs/README.md), [Python](../python/README.md), [Go](../go/README.md), [Java](../java/README.md), and [C#](../dotnet/README.md).
---
@@ -1412,6 +1706,27 @@ await using var session = await client.CreateSessionAsync(new()
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(
+ new CopilotClientOptions().setCliUrl("localhost:4321")
+);
+client.start().get();
+
+// Use the client normally
+var session = client.createSession(
+ new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+// ...
+```
+
+
+
**Note:** When `cli_url` / `cliUrl` / `CLIUrl` is provided, the SDK will not spawn or manage a CLI process - it will only connect to the existing server at the specified URL.
---
@@ -1494,15 +1809,32 @@ No extra dependencies — uses built-in `System.Diagnostics.Activity`.
+
+Java
+
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ .setTelemetry(new TelemetryConfig()
+ .setOtlpEndpoint("http://localhost:4318")));
+```
+
+Dependency: `io.opentelemetry:opentelemetry-api`
+
+
+
### TelemetryConfig Options
-| Option | Node.js | Python | Go | .NET | Description |
-|---|---|---|---|---|---|
-| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
-| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | File path for JSON-lines trace output |
-| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
-| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | Instrumentation scope name |
-| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | Whether to capture message content |
+| Option | Node.js | Python | Go | Java | .NET | Description |
+|---|---|---|---|---|---|---|
+| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `otlpEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
+| File path | `filePath` | `file_path` | `FilePath` | `filePath` | `FilePath` | File path for JSON-lines trace output |
+| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `exporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
+| Source name | `sourceName` | `source_name` | `SourceName` | `sourceName` | `SourceName` | Instrumentation scope name |
+| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `captureContent` | `CaptureContent` | Whether to capture message content |
### File Export
@@ -1537,6 +1869,7 @@ Trace context is propagated automatically — no manual instrumentation is neede
- [Python SDK Reference](../python/README.md)
- [Go SDK Reference](../go/README.md)
- [.NET SDK Reference](../dotnet/README.md)
+- [Java SDK Reference](../java/README.md)
- [Using MCP Servers](./features/mcp.md) - Integrate external tools via Model Context Protocol
- [GitHub MCP Server Documentation](https://github.com/github/github-mcp-server)
- [MCP Servers Directory](https://github.com/modelcontextprotocol/servers) - Explore more MCP servers
diff --git a/docs/hooks/error-handling.md b/docs/hooks/error-handling.md
index b575db0ce8..e66dc32ffa 100644
--- a/docs/hooks/error-handling.md
+++ b/docs/hooks/error-handling.md
@@ -99,6 +99,22 @@ public delegate Task ErrorOccurredHandler(
+
+Java
+
+```java
+// Note: Java SDK does not have an onErrorOccurred hook.
+// Use EventErrorPolicy and EventErrorHandler instead:
+import com.github.copilot.sdk.*;
+
+session.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);
+session.setEventErrorHandler((event, ex) -> {
+ System.err.println("Error in " + event.getType() + ": " + ex.getMessage());
+});
+```
+
+
+
## Input
| Field | Type | Description |
@@ -251,6 +267,30 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+import com.github.copilot.sdk.*;
+import com.github.copilot.sdk.json.*;
+
+// Note: Java SDK does not have an onErrorOccurred hook.
+// Use EventErrorPolicy and EventErrorHandler instead:
+
+var session = client.createSession(
+ new SessionConfig()
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+
+session.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);
+session.setEventErrorHandler((event, ex) -> {
+ System.err.println("[" + session.getSessionId() + "] Error: " + ex.getMessage());
+ System.err.println(" Event: " + event.getType());
+});
+```
+
+
+
### Send Errors to Monitoring Service
```typescript
diff --git a/docs/hooks/index.md b/docs/hooks/index.md
index f0bf9af3c5..b5b711888d 100644
--- a/docs/hooks/index.md
+++ b/docs/hooks/index.md
@@ -156,6 +156,41 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+import com.github.copilot.sdk.*;
+import com.github.copilot.sdk.json.*;
+import java.util.concurrent.CompletableFuture;
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var hooks = new SessionHooks()
+ .setOnPreToolUse((input, invocation) -> {
+ System.out.println("Tool called: " + input.getToolName());
+ return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
+ })
+ .setOnPostToolUse((input, invocation) -> {
+ System.out.println("Tool result: " + input.getToolResult());
+ return CompletableFuture.completedFuture(null);
+ })
+ .setOnSessionStart((input, invocation) -> {
+ return CompletableFuture.completedFuture(
+ new SessionStartHookOutput("User prefers concise answers.", null)
+ );
+ });
+
+ var session = client.createSession(
+ new SessionConfig()
+ .setHooks(hooks)
+ ).get();
+}
+```
+
+
+
## Hook Invocation Context
Every hook receives an `invocation` parameter with context about the current session:
diff --git a/docs/hooks/post-tool-use.md b/docs/hooks/post-tool-use.md
index d0b5f789aa..f7c4089c92 100644
--- a/docs/hooks/post-tool-use.md
+++ b/docs/hooks/post-tool-use.md
@@ -99,6 +99,17 @@ public delegate Task PostToolUseHandler(
+
+Java
+
+```java
+import com.github.copilot.sdk.json.*;
+
+PostToolUseHandler postToolUseHandler;
+```
+
+
+
## Input
| Field | Type | Description |
@@ -250,6 +261,31 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+import com.github.copilot.sdk.*;
+import com.github.copilot.sdk.json.*;
+import java.util.concurrent.CompletableFuture;
+
+var hooks = new SessionHooks()
+ .setOnPostToolUse((input, invocation) -> {
+ System.out.println("[" + invocation.getSessionId() + "] Tool: " + input.getToolName());
+ System.out.println(" Args: " + input.getToolArgs());
+ System.out.println(" Result: " + input.getToolResult());
+ return CompletableFuture.completedFuture(null);
+ });
+
+var session = client.createSession(
+ new SessionConfig()
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setHooks(hooks)
+).get();
+```
+
+
+
### Redact Sensitive Data
```typescript
diff --git a/docs/hooks/pre-tool-use.md b/docs/hooks/pre-tool-use.md
index c87b32be0f..c8e8504f07 100644
--- a/docs/hooks/pre-tool-use.md
+++ b/docs/hooks/pre-tool-use.md
@@ -99,6 +99,17 @@ public delegate Task PreToolUseHandler(
+
+Java
+
+```java
+import com.github.copilot.sdk.json.*;
+
+PreToolUseHandler preToolUseHandler;
+```
+
+
+
## Input
| Field | Type | Description |
@@ -261,6 +272,30 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+import com.github.copilot.sdk.*;
+import com.github.copilot.sdk.json.*;
+import java.util.concurrent.CompletableFuture;
+
+var hooks = new SessionHooks()
+ .setOnPreToolUse((input, invocation) -> {
+ System.out.println("[" + invocation.getSessionId() + "] Calling " + input.getToolName());
+ System.out.println(" Args: " + input.getToolArgs());
+ return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
+ });
+
+var session = client.createSession(
+ new SessionConfig()
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setHooks(hooks)
+).get();
+```
+
+
+
### Block Specific Tools
```typescript
diff --git a/docs/hooks/session-lifecycle.md b/docs/hooks/session-lifecycle.md
index 980b6926bb..1c8723854f 100644
--- a/docs/hooks/session-lifecycle.md
+++ b/docs/hooks/session-lifecycle.md
@@ -103,6 +103,17 @@ public delegate Task SessionStartHandler(
+
+Java
+
+```java
+import com.github.copilot.sdk.json.*;
+
+SessionStartHandler sessionStartHandler;
+```
+
+
+
### Input
| Field | Type | Description |
@@ -304,6 +315,17 @@ public delegate Task SessionEndHandler(
+
+Java
+
+```java
+import com.github.copilot.sdk.json.*;
+
+SessionEndHandler sessionEndHandler;
+```
+
+
+
### Input
| Field | Type | Description |
diff --git a/docs/hooks/user-prompt-submitted.md b/docs/hooks/user-prompt-submitted.md
index 5065c5efdb..0c0751980e 100644
--- a/docs/hooks/user-prompt-submitted.md
+++ b/docs/hooks/user-prompt-submitted.md
@@ -99,6 +99,17 @@ public delegate Task UserPromptSubmittedHandler(
+
+Java
+
+```java
+import com.github.copilot.sdk.json.*;
+
+UserPromptSubmittedHandler userPromptSubmittedHandler;
+```
+
+
+
## Input
| Field | Type | Description |
@@ -236,6 +247,29 @@ var session = await client.CreateSessionAsync(new SessionConfig
+
+Java
+
+```java
+import com.github.copilot.sdk.*;
+import com.github.copilot.sdk.json.*;
+import java.util.concurrent.CompletableFuture;
+
+var hooks = new SessionHooks()
+ .setOnUserPromptSubmitted((input, invocation) -> {
+ System.out.println("[" + invocation.getSessionId() + "] User: " + input.prompt());
+ return CompletableFuture.completedFuture(null);
+ });
+
+var session = client.createSession(
+ new SessionConfig()
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setHooks(hooks)
+).get();
+```
+
+
+
### Add Project Context
```typescript
diff --git a/docs/integrations/microsoft-agent-framework.md b/docs/integrations/microsoft-agent-framework.md
index 8e794759b7..01c785f27f 100644
--- a/docs/integrations/microsoft-agent-framework.md
+++ b/docs/integrations/microsoft-agent-framework.md
@@ -13,7 +13,7 @@ The Microsoft Agent Framework is the unified successor to Semantic Kernel and Au
| **Orchestrator** | A MAF component that coordinates agents in sequential, concurrent, or handoff workflows |
| **A2A protocol** | Agent-to-Agent communication standard supported by the framework |
-> **Note:** MAF integration packages are available for **.NET** and **Python**. For TypeScript and Go, use the Copilot SDK directly — the standard SDK APIs already provide tool calling, streaming, and custom agents.
+> **Note:** MAF integration packages are available for **.NET** and **Python**. For TypeScript, Go, and Java, use the Copilot SDK directly — the standard SDK APIs already provide tool calling, streaming, and custom agents.
## Prerequisites
@@ -46,6 +46,23 @@ pip install copilot-sdk agent-framework-github-copilot
+
+Java
+
+> **Note:** The Java SDK does not have a dedicated MAF integration package. Use the standard Copilot SDK directly — it provides tool calling, streaming, and custom agents out of the box.
+
+```xml
+
+
+
+ com.github
+ copilot-sdk-java
+ ${copilot.sdk.version}
+
+```
+
+
+
## Basic Usage
Wrap the Copilot SDK client as a MAF agent with a single method call. The resulting agent conforms to the framework's standard interface and can be used anywhere a MAF agent is expected.
@@ -92,6 +109,32 @@ async def main():
+
+Java
+
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient();
+client.start().get();
+
+var session = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+
+var response = session.sendAndWait(new MessageOptions()
+ .setPrompt("Explain how dependency injection works in Spring Boot")).get();
+System.out.println(response.getData().content());
+
+client.stop().get();
+```
+
+
+
## Adding Custom Tools
Extend your Copilot agent with custom function tools. Tools defined through the standard Copilot SDK are automatically available when the agent runs inside MAF.
@@ -180,6 +223,45 @@ await session.sendAndWait({ prompt: "What's the weather like in Seattle?" });
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+import java.util.concurrent.CompletableFuture;
+
+var getWeather = ToolDefinition.create(
+ "GetWeather",
+ "Get the current weather for a given location.",
+ Map.of(
+ "type", "object",
+ "properties", Map.of(
+ "location", Map.of("type", "string", "description", "City name")),
+ "required", List.of("location")),
+ invocation -> {
+ var location = (String) invocation.getArguments().get("location");
+ return CompletableFuture.completedFuture(
+ "The weather in " + location + " is sunny, 25°C.");
+ });
+
+try (var client = new CopilotClient()) {
+ client.start().get();
+
+ var session = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setTools(List.of(getWeather))
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ ).get();
+
+ session.sendAndWait(new MessageOptions()
+ .setPrompt("What's the weather like in Seattle?")).get();
+}
+```
+
+
+
## Multi-Agent Workflows
The primary benefit of MAF integration is composing Copilot alongside other agent providers in orchestrated workflows. Use the framework's built-in orchestrators to create pipelines where different agents handle different steps.
@@ -259,6 +341,44 @@ async def main():
+
+Java
+
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+// Java uses the standard SDK directly — no MAF orchestrator needed
+var client = new CopilotClient();
+client.start().get();
+
+// Step 1: Code review session
+var reviewer = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+
+var review = reviewer.sendAndWait(new MessageOptions()
+ .setPrompt("Review this PR for bugs, security issues, and best practices: "
+ + "added retry logic to the HTTP client")).get();
+
+// Step 2: Documentation session using review output
+var documentor = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+
+var docs = documentor.sendAndWait(new MessageOptions()
+ .setPrompt("Write documentation for these changes: " + review.getData().content())).get();
+System.out.println(docs.getData().content());
+
+client.stop().get();
+```
+
+
+
### Concurrent Workflow
Run multiple agents in parallel and aggregate their results:
@@ -296,6 +416,46 @@ Console.WriteLine(combinedResult);
+
+Java
+
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+import java.util.concurrent.CompletableFuture;
+
+// Java uses CompletableFuture for concurrent execution
+var client = new CopilotClient();
+client.start().get();
+
+var securitySession = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+
+var perfSession = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+
+// Run both reviews concurrently
+var securityFuture = securitySession.sendAndWait(new MessageOptions()
+ .setPrompt("Focus on security vulnerabilities in this database query module"));
+var perfFuture = perfSession.sendAndWait(new MessageOptions()
+ .setPrompt("Focus on performance bottlenecks in this database query module"));
+
+CompletableFuture.allOf(securityFuture, perfFuture).get();
+
+System.out.println("Security: " + securityFuture.get().getData().content());
+System.out.println("Performance: " + perfFuture.get().getData().content());
+
+client.stop().get();
+```
+
+
+
## Streaming Responses
When building interactive applications, stream agent responses to show real-time output. The MAF integration preserves the Copilot SDK's streaming capabilities.
@@ -369,6 +529,36 @@ await session.sendAndWait({ prompt: "Write a quicksort implementation in TypeScr
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient();
+client.start().get();
+
+var session = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setStreaming(true)
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+
+session.on(AssistantMessageDeltaEvent.class, event -> {
+ System.out.print(event.getData().deltaContent());
+});
+
+session.sendAndWait(new MessageOptions()
+ .setPrompt("Write a quicksort implementation in Java")).get();
+System.out.println();
+
+client.stop().get();
+```
+
+
+
## Configuration Reference
### MAF Agent Options
diff --git a/docs/observability/opentelemetry.md b/docs/observability/opentelemetry.md
index b59e61a4cd..3ac1bca9c3 100644
--- a/docs/observability/opentelemetry.md
+++ b/docs/observability/opentelemetry.md
@@ -68,15 +68,31 @@ var client = new CopilotClient(new CopilotClientOptions
+
+Java
+
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ .setTelemetry(new TelemetryConfig()
+ .setOtlpEndpoint("http://localhost:4318"))
+);
+```
+
+
+
### TelemetryConfig Options
-| Option | Node.js | Python | Go | .NET | Description |
-|---|---|---|---|---|---|
-| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
-| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | File path for JSON-lines trace output |
-| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
-| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | Instrumentation scope name |
-| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | Whether to capture message content |
+| Option | Node.js | Python | Go | .NET | Java | Description |
+|---|---|---|---|---|---|---|
+| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | `otlpEndpoint` | OTLP HTTP endpoint URL |
+| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | `filePath` | File path for JSON-lines trace output |
+| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `exporterType` | `"otlp-http"` or `"file"` |
+| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | `sourceName` | Instrumentation scope name |
+| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | `captureContent` | Whether to capture message content |
### Trace Context Propagation
@@ -149,6 +165,7 @@ session.registerTool(myTool, async (args, invocation) => {
| Python | `opentelemetry-api` | Install with `pip install copilot-sdk[telemetry]` |
| Go | `go.opentelemetry.io/otel` | Required dependency |
| .NET | — | Uses built-in `System.Diagnostics.Activity` |
+| Java | `io.opentelemetry:opentelemetry-api` | Add this dependency for SDK-based setup; trace context injection is automatic when the OpenTelemetry Java agent or SDK is configured |
## References
diff --git a/docs/setup/backend-services.md b/docs/setup/backend-services.md
index 96d8adafcf..b308438801 100644
--- a/docs/setup/backend-services.md
+++ b/docs/setup/backend-services.md
@@ -223,6 +223,36 @@ var response = await session.SendAndWaitAsync(
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ .setCliUrl("localhost:4321")
+);
+
+try {
+ client.start().get();
+
+ var session = client.createSession(new SessionConfig()
+ .setSessionId(String.format("user-%s-%d", userId, System.currentTimeMillis() / 1000))
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(request -> request.allow())
+ ).get();
+
+ var response = session.sendAndWait(new MessageOptions()
+ .setPrompt(message)).get();
+} finally {
+ client.stop().get();
+}
+```
+
+
+
## Authentication for Backend Services
### Environment Variable Tokens
diff --git a/docs/setup/bundled-cli.md b/docs/setup/bundled-cli.md
index 289857182a..3360f07769 100644
--- a/docs/setup/bundled-cli.md
+++ b/docs/setup/bundled-cli.md
@@ -170,6 +170,34 @@ Console.WriteLine(response?.Data.Content);
+
+Java
+
+> **Note:** The Java SDK does not bundle or embed the Copilot CLI. You must install the CLI separately and configure its path via `cliPath` or the `COPILOT_CLI_PATH` environment variable.
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ // Point to the CLI binary installed on the system
+ .setCliPath("/path/to/vendor/copilot")
+);
+client.start().get();
+
+var session = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")).get();
+
+var response = session.sendAndWait(new MessageOptions()
+ .setPrompt("Hello!")).get();
+System.out.println(response.getData().content());
+
+client.stop().get();
+```
+
+
+
## Authentication Strategies
When bundling, you need to decide how your users will authenticate. Here are the common patterns:
diff --git a/docs/setup/github-oauth.md b/docs/setup/github-oauth.md
index e9bb581b9d..200c7dfd42 100644
--- a/docs/setup/github-oauth.md
+++ b/docs/setup/github-oauth.md
@@ -275,6 +275,37 @@ var response = await session.SendAndWaitAsync(
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+CopilotClient createClientForUser(String userToken) throws Exception {
+ var client = new CopilotClient(new CopilotClientOptions()
+ .setGitHubToken(userToken)
+ .setUseLoggedInUser(false)
+ );
+ client.start().get();
+ return client;
+}
+
+// Usage — use try-with-resources to ensure cleanup
+try (var client = createClientForUser("gho_user_access_token")) {
+ var session = client.createSession(new SessionConfig()
+ .setSessionId(String.format("user-%s-session", userId))
+ .setModel("gpt-4.1")
+ ).get();
+
+ var response = session.sendAndWait(new MessageOptions()
+ .setPrompt("Hello!")).get();
+}
+```
+
+
+
## Enterprise & Organization Access
GitHub OAuth naturally supports enterprise scenarios. When users authenticate with GitHub, their org memberships and enterprise associations come along.
diff --git a/docs/setup/local-cli.md b/docs/setup/local-cli.md
index 4b0e5a1cd6..d5b168bd28 100644
--- a/docs/setup/local-cli.md
+++ b/docs/setup/local-cli.md
@@ -125,6 +125,30 @@ Console.WriteLine(response?.Data.Content);
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.events.*;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient();
+client.start().get();
+
+var session = client.createSession(new SessionConfig()
+ .setModel("gpt-4.1")
+ .setOnPermissionRequest(request -> PermissionDecision.allow())).get();
+
+var response = session.sendAndWait(new MessageOptions()
+ .setPrompt("Hello!")).get();
+System.out.println(response.getData().content());
+
+client.stop().get();
+```
+
+
+
That's it. The SDK handles everything: starting the CLI, authenticating, and managing the session.
## What's Happening Under the Hood
diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md
index 146d3fd5aa..802798b219 100644
--- a/docs/troubleshooting/debugging.md
+++ b/docs/troubleshooting/debugging.md
@@ -94,6 +94,20 @@ var client = new CopilotClient(new CopilotClientOptions
+
+Java
+
+```java
+import com.github.copilot.sdk.CopilotClient;
+import com.github.copilot.sdk.json.*;
+
+var client = new CopilotClient(new CopilotClientOptions()
+ .setLogLevel("debug")
+);
+```
+
+
+
### Log Directory
The CLI writes logs to a directory. You can specify a custom location:
@@ -157,6 +171,17 @@ var client = new CopilotClient(new CopilotClientOptions
+
+Java
+
+```java
+// The Java SDK does not currently support passing extra CLI arguments.
+// For custom log directories, run the CLI manually with --log-dir
+// and connect via cliUrl.
+```
+
+
+
---
## Common Issues
@@ -215,6 +240,16 @@ var client = new CopilotClient(new CopilotClientOptions
```
+
+ Java
+
+ ```java
+ var client = new CopilotClient(new CopilotClientOptions()
+ .setCliPath("/usr/local/bin/copilot")
+ );
+ ```
+
+
### "Not authenticated"
**Cause:** The CLI is not authenticated with GitHub.
@@ -268,6 +303,16 @@ var client = new CopilotClient(new CopilotClientOptions
```
+
+ Java
+
+ ```java
+ var client = new CopilotClient(new CopilotClientOptions()
+ .setGitHubToken(System.getenv("GITHUB_TOKEN"))
+ );
+ ```
+
+
### "Session not found"
**Cause:** Attempting to use a session that was destroyed or doesn't exist.