# Advanced Usage
> ⚠️ **Disclaimer:** This is an **unofficial, community-driven SDK** and is **not supported or endorsed by GitHub**. Use at your own risk.
This guide covers advanced scenarios for extending and customizing your Copilot integration.
---
## Custom Tools
Let the AI call back into your application to fetch data or perform actions.
```java
// Define strongly-typed arguments with a record
record IssueArgs(String id) {}
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 -> {
IssueArgs args = invocation.getArgumentsAs(IssueArgs.class);
return CompletableFuture.completedFuture(fetchIssue(args.id()));
}
);
var session = client.createSession(
new SessionConfig()
.setTools(List.of(lookupTool))
).get();
```
See [ToolDefinition](apidocs/com/github/copilot/sdk/ToolDefinition.html) Javadoc for schema details.
---
## System Messages
Customize the AI's behavior by adding rules or replacing the default prompt.
### Adding Rules
Use `APPEND` mode to add constraints while keeping default guardrails:
```java
var session = client.createSession(
new SessionConfig()
.setSystemMessage(new SystemMessageConfig()
.setMode(SystemMessageMode.APPEND)
.setContent("""
- Always check for security vulnerabilities
- Suggest performance improvements
"""))
).get();
```
### Full Control
Use `REPLACE` mode for complete control (removes default guardrails):
```java
var session = client.createSession(
new SessionConfig()
.setSystemMessage(new SystemMessageConfig()
.setMode(SystemMessageMode.REPLACE)
.setContent("You are a helpful coding assistant."))
).get();
```
---
## File Attachments
Include files as context for the AI to analyze.
```java
session.send(new MessageOptions()
.setPrompt("Review this file for bugs")
.setAttachments(List.of(
new Attachment()
.setType("file")
.setPath("/path/to/file.java")
.setDisplayName("MyService.java")
))
).get();
```
---
## Bring Your Own Key (BYOK)
Use your own OpenAI or Azure OpenAI API key instead of GitHub Copilot.
```java
var session = client.createSession(
new SessionConfig()
.setProvider(new ProviderConfig()
.setType("openai")
.setBaseUrl("https://api.openai.com/v1")
.setApiKey("sk-..."))
).get();
```
---
## Infinite Sessions
Run long conversations without hitting context limits.
When enabled (default), the session automatically compacts older messages as the context window fills up.
```java
var session = client.createSession(
new SessionConfig()
.setInfiniteSessions(new InfiniteSessionConfig()
.setEnabled(true)
.setBackgroundCompactionThreshold(0.80) // Start compacting at 80%
.setBufferExhaustionThreshold(0.95)) // Block at 95%
).get();
// Access the workspace where session state is persisted
String workspace = session.getWorkspacePath();
```
### Compaction Events
When compaction occurs, the session emits events that you can listen for:
```java
session.on(event -> {
if (event instanceof SessionCompactionStartEvent start) {
System.out.println("Compaction started");
} else if (event instanceof SessionCompactionCompleteEvent complete) {
var data = complete.getData();
System.out.println("Compaction completed - success: " + data.isSuccess()
+ ", tokens removed: " + data.getTokensRemoved());
}
});
```
For short conversations, disable to avoid overhead:
```java
new InfiniteSessionConfig().setEnabled(false)
```
---
## MCP Servers
Extend the AI with external tools via the Model Context Protocol.
```java
Map server = Map.of(
"type", "local",
"command", "npx",
"args", List.of("-y", "@modelcontextprotocol/server-filesystem", "/tmp"),
"tools", List.of("*")
);
var session = client.createSession(
new SessionConfig()
.setMcpServers(Map.of("filesystem", server))
).get();
```
📖 **[Full MCP documentation →](mcp.html)** for local/remote servers and all options.
---
## Skills Configuration
Load custom skills from directories to extend the AI's capabilities with domain-specific knowledge.
### Loading Skills
Skills are loaded from `SKILL.md` files in subdirectories of the specified skill directories:
```java
var session = client.createSession(
new SessionConfig()
.setSkillDirectories(List.of("/path/to/skills"))
).get();
```
Each skill subdirectory should contain a `SKILL.md` file with YAML frontmatter:
```markdown
---
name: my-skill
description: A skill that provides domain-specific knowledge
---
# Skill Instructions
Your skill instructions go here...
```
### Disabling Skills
Disable specific skills by name:
```java
var session = client.createSession(
new SessionConfig()
.setSkillDirectories(List.of("/path/to/skills"))
.setDisabledSkills(List.of("my-skill"))
).get();
```
---
## Custom Configuration Directory
Use a custom configuration directory for session settings:
```java
var session = client.createSession(
new SessionConfig()
.setConfigDir("/path/to/custom/config")
).get();
```
This is useful when you need to isolate session configuration or use different settings for different environments.
---
## Permission Handling
Approve or deny permission requests from the AI.
```java
var session = client.createSession(
new SessionConfig()
.setOnPermissionRequest((request, invocation) -> {
// Inspect request and approve/deny
var result = new PermissionRequestResult();
result.setKind("user-approved");
return CompletableFuture.completedFuture(result);
})
).get();
```
---
## Manual Server Control
Control the CLI lifecycle yourself instead of auto-start.
```java
var client = new CopilotClient(
new CopilotClientOptions().setAutoStart(false)
);
client.start().get(); // Start manually
// ... use client ...
client.stop().get(); // Stop manually
```
---
## Error Handling
All SDK methods return `CompletableFuture`. Errors surface via `ExecutionException`:
```java
try {
session.send(new MessageOptions().setPrompt("Hello")).get();
} catch (ExecutionException ex) {
System.err.println("Error: " + ex.getCause().getMessage());
}
```
For reactive error handling, use `exceptionally()` or `handle()`:
```java
session.send(new MessageOptions().setPrompt("Hello"))
.exceptionally(ex -> {
System.err.println("Failed: " + ex.getMessage());
return null;
});
```