# GitHub Copilot SDK for Java
[](https://github.com/github/copilot-sdk/actions/workflows/java-sdk-tests.yml)
[](https://openjdk.org/)
[](https://opensource.org/licenses/MIT)
#### Latest release
[](https://github.com/github/copilot-sdk/releases)
[](https://github.com/github/copilot-sdk/releases)
[](https://central.sonatype.com/artifact/com.github/copilot-sdk-java)
[](https://javadoc.io/doc/com.github/copilot-sdk-java/latest/index.html)
## Background
Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build AI-powered applications and agentic workflows. The Java SDK tracks the official GitHub Copilot SDK family (TypeScript, Python, Go, .NET, and Rust).
## Installation
### Runtime requirements
- Java 17 or later. **JDK 25 recommended**. The distributed jar is a multi-release jar (MR-JAR) and is compiled on JDK 25 with `maven.compiler.release` set to 17. This means, when run on JDK 25 and later, the SDK automatically uses virtual threads for its default internal executor.
- GitHub Copilot CLI 1.0.55-5. or later installed and in `PATH` (or provide custom `cliPath`)
### Maven
Replace `${copilot.sdk.version}` with the latest release from Maven Central.
```xml
com.github
copilot-sdk-java
1.0.1
```
### Gradle
```groovy
implementation 'com.github:copilot-sdk-java:1.0.1'
```
#### Snapshot Builds
Snapshot builds of the next development version are published to Maven Central Snapshots. To use them, add the repository and update the dependency version in your `pom.xml`:
```xml
central-snapshots
https://central.sonatype.com/repository/maven-snapshots/
true
com.github
copilot-sdk-java
1.0.2-SNAPSHOT
```
### Gradle
Replace `${copilot.sdk.version}` with the latest release from Maven Central.
```groovy
implementation 'com.github:copilot-sdk-java:1.0.2-SNAPSHOT'
```
## Quick Start
```java
import com.github.copilot.CopilotClient;
import com.github.copilot.generated.AssistantMessageEvent;
import com.github.copilot.generated.SessionUsageInfoEvent;
import com.github.copilot.rpc.MessageOptions;
import com.github.copilot.rpc.PermissionHandler;
import com.github.copilot.rpc.SessionConfig;
public class CopilotSDK {
public static void main(String[] args) throws Exception {
var lastMessage = new String[]{null};
// Create and start client
try (var client = new CopilotClient()) {
client.start().get();
// Create a session
var session = client.createSession(
new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setModel("claude-sonnet-4.5")).get();
// Handle assistant message events
session.on(AssistantMessageEvent.class, msg -> {
lastMessage[0] = msg.getData().content();
System.out.println(lastMessage[0]);
});
// Handle session usage info events
session.on(SessionUsageInfoEvent.class, usage -> {
var data = usage.getData();
System.out.println("\n--- Usage Metrics ---");
System.out.println("Current tokens: " + data.currentTokens().intValue());
System.out.println("Token limit: " + data.tokenLimit().intValue());
System.out.println("Messages count: " + data.messagesLength().intValue());
});
// Send a message
var completable = session.sendAndWait(new MessageOptions().setPrompt("What is 2+2?"));
// and wait for completion
completable.get();
}
boolean success = lastMessage[0] != null && lastMessage[0].contains("4");
System.exit(success ? 0 : -1);
}
}
```
## Try it with JBang
You can run the SDK without setting up a full Java project, by using [JBang](https://www.jbang.dev/).
See the full source of [`jbang-example.java`](jbang-example.java) for a complete example with more features like session idle handling and usage info events.
Or run it directly from the repository:
```bash
jbang https://github.com/github/copilot-sdk/blob/main/java/jbang-example.java
```
## Using experimental APIs
Some SDK APIs are marked as experimental with `@CopilotExperimental`. These APIs may change or be removed in future versions without notice.
By default, referencing an experimental API from your code causes a **compile-time error**:
```
error: Use of experimental API 'ExperimentalType' in field type is not allowed.
Add @AllowCopilotExperimental or compiler option -Acopilot.experimental.allowed=true to opt in.
```
To opt in and use experimental APIs, either:
- annotate the consuming class, method, or constructor with `@AllowCopilotExperimental`, or
- pass the annotation processor option `-Acopilot.experimental.allowed=true` to the Java compiler.
### In code
```java
import com.github.copilot.AllowCopilotExperimental;
import test.ExperimentalType;
@AllowCopilotExperimental
public class Consumer {
private ExperimentalType field;
public ExperimentalType getIt() {
return field;
}
@AllowCopilotExperimental
public ExperimentalType echo(ExperimentalType value) {
return value;
}
}
```
### Maven
```xml
org.apache.maven.plugins
maven-compiler-plugin
-Acopilot.experimental.allowed=true
```
### Gradle
```groovy
tasks.withType(JavaCompile) {
options.compilerArgs += ['-Acopilot.experimental.allowed=true']
}
```
### What the processor catches
The processor detects usage of experimental types in **declarations**:
| Usage pattern | Caught? |
|---|---|
| Field declared with experimental type | ✅ |
| Method parameter of experimental type | ✅ |
| Method return type is experimental | ✅ |
| `extends` / `implements` experimental type | ✅ |
| `throws` an experimental exception type | ✅ |
| Generic type argument is experimental (e.g., `List`) | ✅ |
### Known limitations
The processor uses standard JSR 269 annotation processing APIs for maximum portability (works with javac, ECJ/Eclipse, and any compliant compiler). This means it inspects **declarations only**, not expressions inside method bodies. The following patterns are **not caught** by the processor:
| Usage pattern | Caught? | Workaround |
|---|---|---|
| `new ExperimentalType()` in a method body (no field/param declaration) | ❌ | Use the compiler flag for a whole-compilation opt-in |
| `ExperimentalType.staticMethod()` inline call | ❌ | Use the compiler flag for a whole-compilation opt-in |
| Method reference `ExperimentalType::method` | ❌ | Use the compiler flag for a whole-compilation opt-in |
| Local variable with experimental type (including `var` inference) | ❌ | Move the usage into a declaration the processor can see, or use the compiler flag |
| Cast to experimental type | ❌ | Use the compiler flag for a whole-compilation opt-in |
In practice, these gaps rarely matter: any meaningful use of an experimental SDK type almost always appears in a field declaration, method signature, or type hierarchy — all of which are caught. A purely inline expression with no declaration footprint (e.g., `session.rpc().experimental.foo().join()`) is the only case that would slip through. See [ADR-004](docs/adr/adr-004-copilotexperimental.md) for the design rationale.
### Example
```java
import com.github.copilot.CopilotExperimental;
// This type is experimental — consumer code that references it
// in declarations will fail to compile unless the opt-in flag is provided.
@CopilotExperimental
public class ExperimentalType {
public void doSomething() {}
}
// Consumer code — compiles only with -Acopilot.experimental.allowed=true
import test.ExperimentalType;
public class Consumer {
private ExperimentalType field; // ← caught: field type
public ExperimentalType getIt() { return field; } // ← caught: return type
public void setIt(ExperimentalType v) { } // ← caught: parameter type
}
```
The gate also applies to individual methods annotated with `@CopilotExperimental` on otherwise stable types. When a type-level annotation is present, all member accesses through that type are considered experimental. `@AllowCopilotExperimental` mirrors the same declaration-level boundary: annotating a class opts in that class and its enclosed declarations, while annotating a method or constructor opts in just that executable signature.
## Projects Using This SDK
| Project | Description |
| ----------------------------------------------------------------------------- | ------------------------------------------ |
| [JMeter Copilot Plugin](https://github.com/brunoborges/jmeter-copilot-plugin) | JMeter plugin for AI-assisted load testing |
> Want to add your project? Open a PR!
### Development Setup
Requires JDK 25 or later for development. The following steps validate the artifact built with JDK 25 runs on both 25 and 17, preserving the MR-JAR behavior.
```bash
# Clone the repository
git clone https://github.com/github/copilot-sdk.git
cd copilot-sdk/java
# Enable git hooks for code formatting
git config core.hooksPath .githooks
# Build and test with JDK 25
mvn test-compile jar:jar
mvn verify -Dskip.test.harness=true
# Set your paths for JDK 17
# Run the JDK 25 built jar with JDK 17 JVM for tests. Do not re-compile the jar.
mvn jacoco:prepare-agent@wire-up-coverage-instrumentation antrun:run@print-test-jdk-banner surefire:test failsafe:integration-test failsafe:verify jacoco:report@build-coverage-report-from-tests -Denforcer.skip=true
```
## License
MIT — see [LICENSE](LICENSE) for details.