Skip to content

feat(java): implement ToolDefinition.from* lambda overloads (Phase 4.2)#1857

Merged
edburns merged 7 commits into
edburns/1810-java-tool-ergonomics-tool-as-lambdafrom
copilot/edburns-1810-java-tool-ergonomics-tool-as-lambda
Jun 30, 2026
Merged

feat(java): implement ToolDefinition.from* lambda overloads (Phase 4.2)#1857
edburns merged 7 commits into
edburns/1810-java-tool-ergonomics-tool-as-lambdafrom
copilot/edburns-1810-java-tool-ergonomics-tool-as-lambda

Conversation

Copilot AI commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Phase 4.2 of #1810: adds typed ToolDefinition.from* factory methods so tools can be defined inline at the call site using standard JDK lambdas, without annotation processing.

New API surface

ToolDefinition factory overloads — 10 new @CopilotExperimental static methods:

Family Arities
from(name, desc, …) 0-arg Supplier<R>, 1-arg Function<T1,R>, 2-arg BiFunction<T1,T2,R>
fromAsync(name, desc, …) same shapes, handlers return CompletableFuture<R>
fromWithToolInvocation(name, desc, …) 0-arg Function<ToolInvocation,R>, 1-arg BiFunction<T1,ToolInvocation,R>
fromAsyncWithToolInvocation(name, desc, …) same, async

Fluent copy-style modifiers on ToolDefinition (return a new record copy):

  • overridesBuiltInTool(boolean), skipPermission(boolean), defer(ToolDefer)

Param<T> (from Phase 4.1 base branch) — runtime parameter metadata mirroring @CopilotToolParam semantics.

Example

Param<String> phase = Param.of(String.class, "phase", "Current phase");

// sync, 1 arg
ToolDefinition report = ToolDefinition.from(
        "report_phase", "Sets the current phase",
        phase,
        p -> "Phase set to " + p)
    .skipPermission(true);

// async + ToolInvocation context
ToolDefinition ctx = ToolDefinition.fromAsyncWithToolInvocation(
        "report_phase_async", "Sets phase, returns call id",
        phase,
        (p, inv) -> CompletableFuture.completedFuture(
                "phase=" + p + ", callId=" + inv.getToolCallId()));

Internals

  • buildSchemaFromParams(Param<?>...) — runtime JSON Schema generation from Param<T> descriptors, mirroring the compile-time SchemaGenerator
  • coerceArg() / coerceDefaultValue() — type-safe argument extraction via ObjectMapper; default values are parsed explicitly (integers, booleans, enums, etc.) rather than relying on Jackson string coercion
  • formatResult() — enforces existing result contract: String passthrough, null → "Success", anything else JSON-serialized
  • Fail-fast validation at construction: blank name/description, null params/handlers, duplicate param names
…ed tools (Phase 4.2)

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement ToolDefinition overloads for Tool-as-lambda Jun 30, 2026
Copilot AI requested a review from edburns June 30, 2026 18:44
@github-actions

This comment has been minimized.

@edburns edburns changed the base branch from main to edburns/1810-java-tool-ergonomics-tool-as-lambda June 30, 2026 19:33
…copilot/edburns-1810-java-tool-ergonomics-tool-as-lambda
@github-actions

This comment has been minimized.

@edburns edburns marked this pull request as ready for review June 30, 2026 22:11
@edburns edburns requested a review from a team as a code owner June 30, 2026 22:11
Copilot AI review requested due to automatic review settings June 30, 2026 22:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Java ergonomic API for defining Copilot tools inline using JDK lambdas (sync/async, with/without ToolInvocation), plus fluent “copy” modifiers to set tool flags on ToolDefinition records. This expands the Java SDK’s tool-definition options beyond annotation processing while keeping schema generation and argument/result coercion inside the runtime.

Changes:

  • Added 10 new ToolDefinition.from* static factory overloads to define tools via lambdas (sync/async, with/without ToolInvocation, 0–2 args).
  • Added fluent copy-style modifiers on ToolDefinition to set overridesBuiltInTool, skipPermission, and defer.
  • Implemented runtime JSON schema generation (buildSchemaFromParams / schemaForClass) and argument/default/result coercion helpers.
Show a summary per file
File Description
java/src/main/java/com/github/copilot/rpc/ToolDefinition.java Adds lambda-based tool factory overloads, fluent modifiers, and runtime schema/coercion helpers for Param<?>-described tool arguments.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 3
  • Review effort level: Low
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java Outdated
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
- buildSchemaFromParams: parse default values to declared type before
  placing in JSON schema (avoids String defaults for numeric/boolean)
- schemaForClass: emit items schema for Java array types using
  getComponentType() for schema fidelity
- coerceDefaultValue: use boxed valueOf() instead of type.cast() for
  primitive types to avoid ClassCastException

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Low
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
- formatResult: pass ToolResultObject through directly instead of
  JSON-serializing, preserving structured result semantics
- schemaForClass: add OptionalInt/OptionalLong/OptionalDouble support
  to match compile-time SchemaGenerator behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
- Return OptionalInt.empty()/OptionalLong.empty()/OptionalDouble.empty()
  for missing non-required params instead of null
- Construct Optional*.of(...) from Number when value is present
- Avoids NPE and aligns with annotation-processor behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Low
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 7
  • Review effort level: Low
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java Outdated
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java Outdated
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java
Comment thread java/src/main/java/com/github/copilot/rpc/ToolDefinition.java Outdated
…tags

- All async fromAsync*/fromAsyncWithToolInvocation handlers now check
  for null future and return failedFuture with clear NPE message
- Optional* coercion catches ClassCastException for non-numeric values
  and throws IllegalArgumentException with diagnostic message
- Fixed @SInCE 1.0.2 -> 1.0.6 on all new API entries (15 in
  ToolDefinition, 1 in Param)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Cross-SDK Consistency Review

Summary: This PR brings Java to feature parity with the other five SDKs. No cross-SDK consistency issues found.

What this PR adds

New Java API Equivalent in other SDKs
ToolDefinition.from(name, desc, () -> ...) — 0-arg Node.js handler?, Python 0-param def fn(): ..., .NET/Rust
ToolDefinition.from(name, desc, p1, p -> ...) — 1 typed arg Go DefineTool[T,U](...), Python 1-param handler, .NET Delegate
fromWithToolInvocation(...) — passes ToolInvocation context Python 1-param def fn(inv: ToolInvocation): ..., Go func(T, inv), .NET binding
fromAsync(...) / fromAsyncWithToolInvocation(...) Python async def, Node.js Promise, .NET Task, Rust async
.overridesBuiltInTool(bool) / .skipPermission(bool) / .defer(...) fluent modifiers Node.js overridesBuiltInTool/skipPermission/defer fields, Python kwargs, Go struct fields, .NET CopilotToolOptions, Rust with_overrides_built_in_tool()/with_skip_permission()/with_defer()

Cross-SDK feature matrix (post-PR)

Capability Node.js Python Go .NET Rust Java
Inline 0-arg handler
Inline typed-param handler
ToolInvocation context in handler
Async handler support
overridesBuiltInTool
skipPermission
defer mode

The Param<T> metadata class is an idiomatic Java solution for carrying runtime parameter type information — other languages address this differently (Python Pydantic models, Go generics, .NET reflection, Rust schemars derive), so no changes are needed elsewhere.

API naming is consistent with each language's conventions: overridesBuiltInTool (camelCase) in Java and Node.js, overrides_built_in_tool (snake_case) in Python and Rust, OverridesBuiltInTool (PascalCase) in Go and .NET.

Generated by SDK Consistency Review Agent for issue #1857 · sonnet46 1.3M ·

@edburns edburns merged commit 14826ad into edburns/1810-java-tool-ergonomics-tool-as-lambda Jun 30, 2026
14 checks passed
@edburns edburns deleted the copilot/edburns-1810-java-tool-ergonomics-tool-as-lambda branch June 30, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants