ADR-005 introduced an ergonomic Java tools API based on @CopilotTool, @CopilotToolParam, and ToolDefinition.fromObject(...). That model works well when teams define tools as methods on a class.
The next ergonomics goal is an inline style comparable to C# CopilotTool.DefineTool(...), where developers can define a tool at the call site without creating a separate tool container class.
For this decision, we evaluated two alternatives:
- Method-reference registration (
ToolDefinition.from(tools::setCurrentPhase)) - Inline lambda registration (
ToolDefinition.from(..., phase -> ...))
The key factor is metadata quality: tool name, description, parameter names, parameter descriptions, required/default semantics, and schema stability.
Example:
ToolDefinition setPhase = ToolDefinition.from(tools::setCurrentPhase);In this model, metadata is sourced from existing method-level annotations (@CopilotTool, @Param) on the referenced method.
Advantages:
- Closest Java analog to C# method-group ergonomics
- High-quality metadata with minimal additional API surface
- Reuses ADR-005 metadata and invocation behavior directly
Drawbacks:
- Not truly inline: still requires a declared method (and usually annotations) elsewhere
- Does not solve the "define the whole tool at the call site" use case
- Method-reference resolution adds runtime/reflection complexity
Example:
ToolDefinition setPhase = ToolDefinition.from(
"set_current_phase",
"Sets the current phase of the agent",
Param.of(String.class, "phase", "The phase to transition to"),
phase -> {
currentPhase = phase;
return "Phase set to " + phase;
});In this model, handler logic is inline, and metadata is provided explicitly through a small parameter-definition DSL.
Advantages:
- True inline authoring at the session construction site
- No dependence on lambda parameter-name reflection or
-parameters - Deterministic metadata and schema generation
- Independent from annotation processing and generated companion classes
Drawbacks:
- Slightly more verbose than method-reference style because metadata is explicit
- Introduces new public API types for parameter definitions and typed lambda overloads
- Requires careful API design to stay concise for common one-parameter tools
Chosen: Option 2 for ADR-006 scope — inline lambda API with explicit metadata.
Rationale:
- The primary requirement for this ADR is inline definition. Option 2 satisfies it directly; Option 1 does not.
- Metadata quality is the critical requirement. Option 2 keeps metadata explicit and stable, instead of relying on fragile lambda introspection.
- Option 2 can ship independently of method-reference support and without changes to annotation processing.
- Option 2 preserves behavior parity with existing tool execution by delegating to
ToolDefinitionconstruction and current invocation semantics.
Option 1 remains valuable and can be added independently as a separate ergonomic layer. It is not blocked by this decision.
Constraints for the inline lambda API:
- Require explicit tool name and description.
- Require explicit parameter metadata (at minimum name and type, with optional description/required/default).
- Support both sync and async handlers (
RandCompletableFuture<R>). - Keep result semantics aligned with existing behavior (
Stringpassthrough,voidmaps to"Success", non-string objects serialized to JSON). - Keep override/permission/defer flags available through options, consistent with existing
ToolDefinitionfields.
Non-goals for this ADR:
- Replacing
@CopilotTool/fromObjectAPIs. - Defining method-reference registration behavior in detail.
- Introducing compile-time code generation for lambda metadata.
If implemented, the SDK gains an explicit inline path for developers who prefer to keep tool declarations at session creation while preserving high-quality schema metadata.
The annotation-driven API from ADR-005 remains the recommended path for larger tool surfaces where co-locating metadata with method implementations improves maintainability.
The Java README now includes concise examples for ToolDefinition.from(...), fromAsync(...), fromWithToolInvocation(...), and fromAsyncWithToolInvocation(...) using Param<T> required/default behavior and fluent option modifiers.
- #1682
- #1792
- #1810