GitHub Copilot SDK for Java: A Framework-Agnostic Path to Agentic AI
Java developers building AI into enterprise applications have faced a tradeoff: LangChain4j abstracts vendor specifics but binds you to its own API, and Spring AI ties you to Spring design decisions. The GitHub Copilot SDK for Java sidesteps both constraints. It's a client library for server-side Java code to create Copilot agent sessions, register tools, send prompts, and receive structured responses—without requiring a specific Java framework or AI vendor. Its BYOK support means you can even point it at providers like OpenAI, Azure, or Anthropic directly.
The SDK targets JDK 17 or 25 (25 recommended for virtual threads), Maven 3.9+, a GitHub account with an active Copilot subscription, and the Copilot CLI at version 1.0.71 or later. It's available as a standard Maven dependency, and the API leans on familiar Java constructs: CompletableFuture, annotations, and lambdas.
Sample Application Walkthrough
To see the SDK in practice, Microsoft provides a reference application on GitHub—a real-estate lead-management agent pipeline. A customer enquiry like "I'm looking for a 3-bedroom house in London under £800,000" triggers an isolated Copilot Agent on a virtual thread. The agent processes the request through pipeline phases, with Jakarta WebSocket pushing real-time status updates to the browser. You can submit multiple inquiries simultaneously to watch concurrent agents work independently in their own Copilot sessions.
Notably, the sample uses Jakarta EE 11—chosen by the author, who served as lead release coordinator for that release—but the SDK isn't limited to it. It works in any server environment, including Spring.
Defining Tools with @CopilotTool
The core API is the @CopilotTool annotation, which is reminiscent of JAX-RS endpoint definitions. Mark a Java method with it, describe parameters using @CopilotToolParam, and the SDK handles JSON Schema generation, argument parsing, and dispatch to your method.
Because the annotation-based tool API is currently experimental, two Maven build configurations are required:
- Pass
-Acopilot.experimental.allowed=trueto the compiler; without this, the annotation processor won't generate tool metadata. - Register the SDK as an
annotationProcessorPathso the compiler finds the processor and generates the$$CopilotToolMetaclasses at compile time.
After building, register all annotated tools from an object with a single call. For tools that don't warrant a dedicated method, define them inline as lambdas using ToolDefinition.from(...). This style supports flags like .overridesBuiltInTool(true) to deliberately replace a built-in tool of the same name with custom behavior.
Tools can also live across classes. If a tool like searchProperties sits in a separate CDI bean, you'd typically register it with ToolDefinition.fromObject(propertyDatabase). In the sample, a lambda wrapper is used instead because CDI client proxies can obscure annotation metadata.
System Message Customization and the Agentic Loop
Fine-grained control over the agent's system message is available via SystemMessageMode.CUSTOMIZE, which replaces specific sections (such as the IDENTITY block that describes the model) while leaving safety guardrails intact. Java text blocks keep multi-line prompts readable. For simpler needs, SystemMessageMode.APPEND adds your content after the default message without modifying it.
Invoking the full agentic loop—where the model reasons, calls your tools potentially multiple times, and returns a final response—is a one-liner with sendAndWait(...). Behind the .get(), the SDK automatically dispatches tool calls to registered handlers and feeds results back to the model. On a virtual thread, this blocking wait is inexpensive; no platform thread is consumed.
Real-Time Events and Headless Operation
For responsive UIs, subscribe to session events with session.on(...). Every tool call, result, and assistant message triggers an event. The sample captures these and pushes them to the browser via Jakarta WebSocket, making the pipeline dashboard update live. Java pattern matching can discriminate event types for targeted handling.
Server-side configuration uses CopilotClientMode.EMPTY, meaning the client talks directly to the Copilot CLI without IDE integration. A custom Executor ensures tool callbacks carry container context. For permissions, the sample defaults to APPROVE_ALL—suitable only for demos; production should implement a real policy validating which tools the model can invoke.
Jakarta EE Integration Patterns
The SDK composes cleanly with Jakarta EE through the Executor parameter. Jakarta Concurrency requires application-created threads to come from a ManagedThreadFactory so the container tracks lifecycle, applies concurrency policies, and propagates context automatically. Open Liberty 26.x supports virtual-thread ManagedThreadFactory via the virtual attribute in server.xml.
After injecting the factory, it creates the Executor passed to the Copilot SDK. This produces virtual threads carrying the container's context, so when the SDK dispatches a tool call, that method can @Inject a JPA repository and query the database directly.
Other integration patterns include CDI @ApplicationScoped for a singleton CopilotClient, Jakarta Faces f:websocket push for browser updates, and Jakarta Data @Repository for type-safe queries.
Tool Access Control with ToolSet
Production deployments benefit from fine-grained control over session capabilities via SessionConfig. Instead of exposing every built-in tool—including file system access and shell execution—you explicitly opt in. The sample allows all custom tools plus web_fetch so agents can look up real-time property information during searches.
Key Takeaways and Next Steps
- Java-native API:
CompletableFuture, annotations, lambdas, and virtual threads throughout. - Three tool-definition styles: annotations for enterprise patterns, lambdas for inline convenience, JSON Schema for direct control.
- Section-level system message overrides provide precise agent behavior control.
- One-line agentic loop:
sendAndWait(...)handles the full tool-calling sequence. - Event streaming via
session.on(...)for UIs and observability. - Headless operation runs anywhere the Copilot CLI is available.
For hands-on exploration: explore BYOK usage by passing a provider/ProviderConfig with your own baseUrl and apiKey or bearer token; clone the sample app and submit concurrent enquiries; experiment with different models via session.setModel(...); or add your own @CopilotTool method to see the agent discover it. The sample application is available on GitHub, with deployment guidance for Azure App Service, AKS, or Azure Container Apps available via the Jakarta EE on Azure documentation.



