Copilot SDK for Java - Documentation
⚠️ Disclaimer: This is an unofficial, community-driven SDK and is not supported or endorsed by GitHub. Use at your own risk.
This document provides detailed API reference and usage examples for the Copilot SDK for Java.
Table of Contents
API Reference
CopilotClient
Constructor
new CopilotClient()
new CopilotClient(CopilotClientOptions options)
Options:
cliPath- Path to CLI executable (default: “copilot” from PATH)cliArgs- Extra arguments prepended before SDK-managed flagscliUrl- URL of existing CLI server to connect to (e.g.,"localhost:8080"). When provided, the client will not spawn a CLI process.port- Server port (default: 0 for random)useStdio- Use stdio transport instead of TCP (default: true)logLevel- Log level (default: “info”)autoStart- Auto-start server (default: true)autoRestart- Auto-restart on crash (default: true)cwd- Working directory for the CLI processenvironment- Environment variables to pass to the CLI process
Methods
start(): CompletableFuture<Void>
Start the CLI server and establish connection.
stop(): CompletableFuture<Void>
Stop the server and close all sessions.
forceStop(): CompletableFuture<Void>
Force stop the CLI server without graceful cleanup.
createSession(SessionConfig config): CompletableFuture<CopilotSession>
Create a new conversation session.
Config:
sessionId- Custom session IDmodel- Model to use (“gpt-5”, “claude-sonnet-4.5”, etc.)tools- Custom tools exposed to the CLIsystemMessage- System message customizationavailableTools- List of tool names to allowexcludedTools- List of tool names to disableprovider- Custom API provider configuration (BYOK)streaming- Enable streaming of response chunks (default: false)mcpServers- MCP server configurationscustomAgents- Custom agent configurationsonPermissionRequest- Handler for permission requests
resumeSession(String sessionId, ResumeSessionConfig config): CompletableFuture<CopilotSession>
Resume an existing session.
ping(String message): CompletableFuture<PingResponse>
Ping the server to check connectivity.
getStatus(): CompletableFuture<GetStatusResponse>
Get CLI status including version and protocol information.
getAuthStatus(): CompletableFuture<GetAuthStatusResponse>
Get current authentication status.
listModels(): CompletableFuture<List<ModelInfo>>
List available models with their metadata (id, name, capabilities, billing info).
getState(): ConnectionState
Get current connection state. Returns one of: DISCONNECTED, CONNECTING, CONNECTED, ERROR.
listSessions(): CompletableFuture<List<SessionMetadata>>
List all available sessions.
deleteSession(String sessionId): CompletableFuture<Void>
Delete a session and its data from disk.
getLastSessionId(): CompletableFuture<String>
Get the ID of the most recently used session.
CopilotSession
Represents a single conversation session.
Properties
getSessionId()- The unique identifier for this session
Methods
send(String prompt): CompletableFuture<String>
Convenience method to send a simple text message. Equivalent to send(new MessageOptions().setPrompt(prompt)).
send(MessageOptions options): CompletableFuture<String>
Send a message to the session.
Options:
prompt- The message/prompt to sendattachments- File attachmentsmode- Delivery mode (“enqueue” or “immediate”)
Returns the message ID.
sendAndWait(String prompt): CompletableFuture<AssistantMessageEvent>
Convenience method to send a simple text message and wait for the session to become idle. Equivalent to sendAndWait(new MessageOptions().setPrompt(prompt)).
sendAndWait(MessageOptions options): CompletableFuture<AssistantMessageEvent>
Send a message and wait for the session to become idle. Default timeout is 60 seconds.
sendAndWait(MessageOptions options, long timeoutMs): CompletableFuture<AssistantMessageEvent>
Send a message and wait for the session to become idle with custom timeout.
on(Consumer<AbstractSessionEvent> handler): Closeable
Subscribe to session events. Returns a Closeable to unsubscribe.
var subscription = session.on(evt -> {
System.out.println("Event: " + evt.getType());
});
// Later...
subscription.close();
abort(): CompletableFuture<Void>
Abort the currently processing message in this session.
getMessages(): CompletableFuture<List<AbstractSessionEvent>>
Get all events/messages from this session.
close()
Dispose the session and free resources.
Event Types
Sessions emit various events during processing. Each event type extends AbstractSessionEvent:
UserMessageEvent- User message addedAssistantMessageEvent- Assistant responseAssistantMessageDeltaEvent- Streaming response chunkToolExecutionStartEvent- Tool execution startedToolExecutionCompleteEvent- Tool execution completedSessionStartEvent- Session startedSessionIdleEvent- Session is idleSessionErrorEvent- Session error occurredSessionResumeEvent- Session was resumed- And more…
Use pattern matching (Java 21+) to handle specific event types:
session.on(evt -> {
if (evt instanceof AssistantMessageEvent msg) {
System.out.println(msg.getData().getContent());
} else if (evt instanceof SessionErrorEvent err) {
System.out.println("Error: " + err.getData().getMessage());
}
});
Streaming
Enable streaming to receive assistant response chunks as they're generated:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setStreaming(true)
).get();
var done = new CompletableFuture<Void>();
session.on(evt -> {
if (evt instanceof AssistantMessageDeltaEvent delta) {
// Streaming message chunk - print incrementally
System.out.print(delta.getData().getDeltaContent());
} else if (evt instanceof AssistantMessageEvent msg) {
// Final message - complete content
System.out.println("\n--- Final message ---");
System.out.println(msg.getData().getContent());
} else if (evt instanceof SessionIdleEvent) {
done.complete(null);
}
});
session.send(new MessageOptions().setPrompt("Tell me a short story")).get();
done.get();
Listing Models
Query available models and their capabilities before creating a session:
try (var client = new CopilotClient()) {
client.start().get();
// List all available models
List<ModelInfo> models = client.listModels().get();
for (ModelInfo model : models) {
System.out.println("Model: " + model.getId());
System.out.println(" Name: " + model.getName());
if (model.getCapabilities() != null) {
System.out.println(" Max Output Tokens: " + model.getCapabilities().getMaxOutputTokens());
}
if (model.getPolicy() != null) {
System.out.println(" State: " + model.getPolicy().getState());
}
}
// Use a specific model from the list
var session = client.createSession(
new SessionConfig().setModel(models.get(0).getId())
).get();
}
Each ModelInfo contains:
id- Model identifier (e.g., “claude-sonnet-4.5”, “gpt-4o”)name- Human-readable display namecapabilities- Model limits including max output tokenspolicy- Policy state informationbilling- Billing/usage information
Advanced Usage
Manual Server Control
var client = new CopilotClient(
new CopilotClientOptions().setAutoStart(false)
);
// Start manually
client.start().get();
// Use client...
// Stop manually
client.stop().get();
Tools
You can let the CLI call back into your process when the model needs capabilities you own:
var lookupTool = ToolDefinition.create(
"lookup_issue",
"Fetch issue details from our tracker",
Map.of(
"type", "object",
"properties", Map.of(
"id", Map.of("type", "string", "description", "Issue identifier")
),
"required", List.of("id")
),
invocation -> {
String id = ((Map<String, Object>) invocation.getArguments()).get("id").toString();
return CompletableFuture.completedFuture(fetchIssue(id));
}
);
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setTools(List.of(lookupTool))
).get();
System Message Customization
Control the system prompt using SystemMessageConfig in session config:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setSystemMessage(new SystemMessageConfig()
.setMode(SystemMessageMode.APPEND)
.setContent("""
<workflow_rules>
- Always check for security vulnerabilities
- Suggest performance improvements when applicable
</workflow_rules>
"""))
).get();
For full control (removes all guardrails), use REPLACE mode:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setSystemMessage(new SystemMessageConfig()
.setMode(SystemMessageMode.REPLACE)
.setContent("You are a helpful assistant."))
).get();
Multiple Sessions
var session1 = client.createSession(
new SessionConfig().setModel("gpt-5")
).get();
var session2 = client.createSession(
new SessionConfig().setModel("claude-sonnet-4.5")
).get();
// Both sessions are independent
session1.send(new MessageOptions().setPrompt("Hello from session 1")).get();
session2.send(new MessageOptions().setPrompt("Hello from session 2")).get();
File Attachments
session.send(new MessageOptions()
.setPrompt("Analyze this file")
.setAttachments(List.of(
new Attachment()
.setType("file")
.setPath("/path/to/file.java")
.setDisplayName("My File")
))
).get();
Bring Your Own Key (BYOK)
Use a custom API provider:
var session = client.createSession(
new SessionConfig()
.setProvider(new ProviderConfig()
.setType("openai")
.setBaseUrl("https://api.openai.com/v1")
.setApiKey("your-api-key"))
).get();
Permission Handling
Handle permission requests from the CLI:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setOnPermissionRequest((request, invocation) -> {
// Approve or deny the permission request
var result = new PermissionRequestResult();
result.setKind("user-approved");
return CompletableFuture.completedFuture(result);
})
).get();
Infinite Sessions
Infinite sessions enable automatic context management for long-running conversations. When enabled (default), the session automatically manages context window limits through background compaction and persists state to a workspace directory.
How It Works
As conversations grow, they eventually approach the model's context window limit. Infinite sessions solve this by:
-
Background Compaction: When context utilization reaches the background threshold (default 80%), the session starts compacting older messages asynchronously while continuing to process new messages.
-
Buffer Exhaustion Protection: If context reaches the exhaustion threshold (default 95%) before compaction completes, the session blocks until compaction finishes to prevent overflow.
-
Workspace Persistence: Session state is persisted to a workspace directory containing:
checkpoints/- Session checkpoints for resumptionplan.md- Current conversation planfiles/- Associated files
Configuration
var infiniteConfig = new InfiniteSessionConfig()
.setEnabled(true)
.setBackgroundCompactionThreshold(0.80) // Start compacting at 80% utilization
.setBufferExhaustionThreshold(0.95); // Block at 95% until compaction completes
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setInfiniteSessions(infiniteConfig)
).get();
Configuration Options
| Option | Default | Description |
|---|---|---|
enabled |
true |
Whether infinite sessions are enabled |
backgroundCompactionThreshold |
0.80 |
Context utilization (0.0-1.0) at which background compaction starts |
bufferExhaustionThreshold |
0.95 |
Context utilization (0.0-1.0) at which the session blocks until compaction completes |
Accessing the Workspace
When infinite sessions are enabled, you can access the workspace path:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setInfiniteSessions(new InfiniteSessionConfig().setEnabled(true))
).get();
String workspacePath = session.getWorkspacePath();
if (workspacePath != null) {
System.out.println("Session workspace: " + workspacePath);
// Access checkpoints/, plan.md, files/ subdirectories
}
Disabling Infinite Sessions
For short conversations where context management isn't needed:
var session = client.createSession(
new SessionConfig()
.setModel("gpt-5")
.setInfiniteSessions(new InfiniteSessionConfig().setEnabled(false))
).get();
// session.getWorkspacePath() will return null
MCP Servers
The Copilot SDK can integrate with MCP servers (Model Context Protocol) to extend the assistant's capabilities with external tools. MCP servers run as separate processes and expose tools that Copilot can invoke during conversations.
📖 Full MCP documentation → - Learn about local vs remote servers, all configuration options, and troubleshooting.
Quick example:
Map<String, Object> filesystemServer = new HashMap<>();
filesystemServer.put("type", "local");
filesystemServer.put("command", "npx");
filesystemServer.put("args", List.of("-y", "@modelcontextprotocol/server-filesystem", "/tmp"));
filesystemServer.put("tools", List.of("*"));
var session = client.createSession(
new SessionConfig()
.setMcpServers(Map.of("filesystem", filesystemServer))
).get();
Error Handling
try {
var session = client.createSession().get();
session.send(new MessageOptions().setPrompt("Hello")).get();
} catch (ExecutionException ex) {
Throwable cause = ex.getCause();
System.err.println("Error: " + cause.getMessage());
}
