Skip to content

feat(java): add SchemaGenerator compile-time type-to-JSON-Schema utility#1766

Merged
edburns merged 13 commits into
edburns/1682-java-tool-ergonomicsfrom
copilot/edburns1682-java-tool-ergonomics
Jun 23, 2026
Merged

feat(java): add SchemaGenerator compile-time type-to-JSON-Schema utility#1766
edburns merged 13 commits into
edburns/1682-java-tool-ergonomicsfrom
copilot/edburns1682-java-tool-ergonomics

Conversation

Copilot AI commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Compile-time schema generation utility that maps javax.lang.model types to JSON Schema represented as Map.of(...) source code literals, for use by the annotation processor (task 4.2 of the tool ergonomics plan).

SchemaGenerator.java

  • Operates exclusively with javax.lang.model APIs — no java.lang.reflect
  • generateSchemaSource(TypeMirror, Types, Elements) → schema source for a single type
  • generateParametersSchemaSource(List<VariableElement>, Types, Elements) → full parameters object schema with properties/required
  • Handles all 24 specified type mappings: primitives, boxed types, String, UUID, OffsetDateTime, JsonNode, Object, Optional variants, List/Collection/Set, Map with typed values, arrays, enums (with constant enumeration), records, POJOs, and sealed interfaces (oneOf)
// Example: given a method parameter of type List<String>, produces:
"Map.of(\"type\", \"array\", \"items\", Map.of(\"type\", \"string\"))"

// For a record like `record Person(String name, Optional<String> nick)`, produces:
"Map.of(\"type\", \"object\", \"properties\", Map.of(\"name\", Map.of(\"type\", \"string\"), \"nickname\", Map.of(\"type\", \"string\")), \"required\", List.of(\"name\"))"

SchemaGeneratorTest.java

  • Uses compilation-testing approach: compiles in-memory Java source snippets with javax.tools.JavaCompiler and a custom SchemaCapturingProcessor that exercises SchemaGenerator during annotation processing
  • Covers all minimum required type mappings plus nested types, sealed interfaces, and generated source validity (re-compiles generated expressions to prove syntactic correctness)

Copilot AI added 2 commits June 23, 2026 01:03
Creates SchemaGenerator.java that maps javax.lang.model TypeMirror
instances to JSON Schema source code literals (Map.of(...) expressions).

Implements all 24 type mappings from the specification including:
- Primitives and boxed types (int/Integer, long/Long, etc.)
- String, UUID, OffsetDateTime
- Collections (List<T>, Collection<T>, Set<T>)
- Maps (Map<String, V> with typed values)
- Arrays (String[])
- Enums (with constant enumeration)
- Records and POJOs (with properties/required)
- Optional<T>, OptionalInt, OptionalDouble
- Sealed interfaces (oneOf)
- JsonNode and Object (any)

Also adds SchemaGeneratorTest using compilation-testing approach
with javax.tools.JavaCompiler to exercise the generator at compile time.

Closes #1759
Copilot AI changed the title [WIP] Add schema generation utility for TypeMirror to JSON Schema feat(java): add SchemaGenerator compile-time type-to-JSON-Schema utility Jun 23, 2026
Copilot AI requested a review from edburns June 23, 2026 01:07
@github-actions

This comment has been minimized.

@edburns edburns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@copilot the test run failed a very basic test:

https://github.com/github/copilot-sdk/actions/runs/27994992918/

Please investigate, fix and re-run the tests. Get a clean run before asking for review.

… getContent

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

Copilot AI commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

@copilot the test run failed a very basic test:

https://github.com/github/copilot-sdk/actions/runs/27994992918/...

Fixed in f5bde69. The compilation error was SimpleJavaFileObject.getContent()getCharContent(boolean ignoreEncodingErrors) — the correct method signature for the override at line 57.

Copilot AI requested a review from edburns June 23, 2026 16:55
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot 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.

Generated by SDK Consistency Review Agent for issue #1766 · sonnet46 1.5M

Comment thread java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java Outdated
@github-actions

This comment has been minimized.

@edburns edburns marked this pull request as ready for review June 23, 2026 17:41
@edburns edburns requested a review from a team as a code owner June 23, 2026 17:41
Copilot AI review requested due to automatic review settings June 23, 2026 17:41
@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.

Pull request overview

Adds a new Java compile-time schema generation utility intended for the Java annotation processor: it converts javax.lang.model type information into JSON Schema expressed as Java source literals (e.g., Map.of(...)), with a new compilation-testing style JUnit suite to validate mappings and generated-source syntax.

Changes:

  • Introduces SchemaGenerator to map TypeMirror (and method parameters) to JSON Schema source expressions.
  • Adds SchemaGeneratorTest that compiles in-memory snippets with a custom annotation processor to capture and validate generated schemas.
  • Adds a Maven wrapper script under java/ to support self-contained Maven execution for the Java module.
Show a summary per file
File Description
java/src/main/java/com/github/copilot/tool/SchemaGenerator.java New compile-time type-to-JSON-Schema source generator (Map/List literals, records/classes/enums/sealed, optionals, collections/maps).
java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java New compilation-testing harness and unit tests validating core schema mappings and generated Java expression validity.
java/mvnw Adds Maven wrapper shell script for the Java submodule.

Copilot's findings

  • Files reviewed: 2/3 changed files
  • Comments generated: 11

Comment thread java/src/main/java/com/github/copilot/tool/SchemaGenerator.java
Comment thread java/src/main/java/com/github/copilot/tool/SchemaGenerator.java
Comment thread java/src/main/java/com/github/copilot/tool/SchemaGenerator.java Outdated
Comment thread java/src/main/java/com/github/copilot/tool/SchemaGenerator.java Outdated
Comment thread java/src/main/java/com/github/copilot/tool/SchemaGenerator.java Outdated
Comment thread java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java
Comment thread java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java
Comment thread java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java
Comment thread java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java
Comment thread java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java
Address review comment r3461777483: Map.of() only supports up to 10
key-value pairs. Switch properties maps in SchemaGenerator to use
Map.ofEntries(Map.entry(...), ...) so records/POJOs/methods with >10
fields won't cause generated source compilation failures.

Update SchemaGeneratorTest expectations to match the new format.
edburns added 5 commits June 23, 2026 14:04
Address review comment r3461777428: Byte and Short now map to
"integer", Character maps to "string", matching their primitive
equivalents. Add tests for all three.
Address review comment r3461777459: OptionalLong was handled in
isOptionalType/unwrapOptional but missing from generateDeclaredTypeSchema,
causing it to fall through to POJO introspection when used as a direct
return type. Add the mapping and tests for OptionalInt, OptionalLong,
and OptionalDouble.
…dling

Address review comment r3461777579: the implementation uses
getPermittedSubclasses() (Java sealed types), not Jackson annotations.
Address review comment r3461777685: the processor had special handling
for TestSealed* types but no test exercised generateSealedSchema().
Add a test with a sealed interface (TestSealedShape) and two record
permits (Circle, Rect) verifying the oneOf schema output.
Address review comment r3461777706: add a test with an 11-component
record that verifies the generated Map.ofEntries(...) expression
actually compiles, proving the Map.of 10-entry limit fix works
end-to-end.
@github-actions

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

No cross-SDK consistency issues found.

Summary

This PR adds SchemaGenerator.java — a compile-time utility that maps javax.lang.model types to JSON Schema (Map.of(...) literals), intended for use by Java annotation processors (@CopilotTool).

Why this is Java-specific (by design)

The SchemaGenerator relies entirely on javax.lang.model APIs, which are part of Java's annotation processing (JSR 269) framework. There is no equivalent framework in other languages:

SDK Schema generation mechanism
Java Compile-time (javax.lang.model) via SchemaGenerator + annotation processor
Go Runtime reflection (reflect.TypeOf) with struct tag-driven generateSchemaForType()
.NET Microsoft.Extensions.AI.AIFunctionFactory (reflection-based, runtime)
Python Runtime introspection in tools.py
Node.js/TypeScript Type-based schema generation at runtime
Rust Runtime type-based tooling

All SDKs provide the same capability (JSON Schema generation for tool definitions), but each uses the idiomatic mechanism for its language ecosystem. The Java choice of compile-time annotation processing is the correct Java-idiomatic approach, consistent with how @CopilotTool works.

Scope

All 3 changed files are strictly Java-specific:

  • java/mvnw — Maven wrapper (build tooling)
  • java/src/main/java/com/github/copilot/tool/SchemaGenerator.java — compile-time utility
  • java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java — tests for above

No changes to other SDK implementations are needed.

Generated by SDK Consistency Review Agent for issue #1766 · sonnet46 944.7K ·

@edburns edburns merged commit 89aafd3 into edburns/1682-java-tool-ergonomics Jun 23, 2026
13 checks passed
@edburns edburns deleted the copilot/edburns1682-java-tool-ergonomics branch June 23, 2026 20:06
edburns added a commit that referenced this pull request Jun 23, 2026
…ity (#1766)

* Initial plan

* feat(java): add SchemaGenerator compile-time type-to-JSON-Schema utility

Creates SchemaGenerator.java that maps javax.lang.model TypeMirror
instances to JSON Schema source code literals (Map.of(...) expressions).

Implements all 24 type mappings from the specification including:
- Primitives and boxed types (int/Integer, long/Long, etc.)
- String, UUID, OffsetDateTime
- Collections (List<T>, Collection<T>, Set<T>)
- Maps (Map<String, V> with typed values)
- Arrays (String[])
- Enums (with constant enumeration)
- Records and POJOs (with properties/required)
- Optional<T>, OptionalInt, OptionalDouble
- Sealed interfaces (oneOf)
- JsonNode and Object (any)

Also adds SchemaGeneratorTest using compilation-testing approach
with javax.tools.JavaCompiler to exercise the generator at compile time.

Closes #1759

* fix: address code review - remove unused param, handle all primitive types

* fix(java): correct SimpleJavaFileObject override - getCharContent not getContent

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* spotless

* Remove .class files generated by test

* spotless

* fix: use Map.ofEntries for properties to avoid Map.of 10-entry limit

Address review comment r3461777483: Map.of() only supports up to 10
key-value pairs. Switch properties maps in SchemaGenerator to use
Map.ofEntries(Map.entry(...), ...) so records/POJOs/methods with >10
fields won't cause generated source compilation failures.

Update SchemaGeneratorTest expectations to match the new format.

* fix: add missing Byte/Short/Character boxed type mappings

Address review comment r3461777428: Byte and Short now map to
"integer", Character maps to "string", matching their primitive
equivalents. Add tests for all three.

* fix: add missing OptionalLong mapping in generateDeclaredTypeSchema

Address review comment r3461777459: OptionalLong was handled in
isOptionalType/unwrapOptional but missing from generateDeclaredTypeSchema,
causing it to fall through to POJO introspection when used as a direct
return type. Add the mapping and tests for OptionalInt, OptionalLong,
and OptionalDouble.

* fix: correct misleading @JsonSubTypes comment on sealed interface handling

Address review comment r3461777579: the implementation uses
getPermittedSubclasses() (Java sealed types), not Jackson annotations.

* test: add sealed interface test for oneOf schema generation

Address review comment r3461777685: the processor had special handling
for TestSealed* types but no test exercised generateSealedSchema().
Add a test with a sealed interface (TestSealedShape) and two record
permits (Circle, Rect) verifying the oneOf schema output.

* test: add >10-field record test proving Map.ofEntries compiles

Address review comment r3461777706: add a test with an 11-component
record that verifies the generated Map.ofEntries(...) expression
actually compiles, proving the Map.of 10-entry limit fix works
end-to-end.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Co-authored-by: Ed Burns <edburns@microsoft.com>
edburns added a commit that referenced this pull request Jun 23, 2026
…ity (#1766)

* Initial plan

* feat(java): add SchemaGenerator compile-time type-to-JSON-Schema utility

Creates SchemaGenerator.java that maps javax.lang.model TypeMirror
instances to JSON Schema source code literals (Map.of(...) expressions).

Implements all 24 type mappings from the specification including:
- Primitives and boxed types (int/Integer, long/Long, etc.)
- String, UUID, OffsetDateTime
- Collections (List<T>, Collection<T>, Set<T>)
- Maps (Map<String, V> with typed values)
- Arrays (String[])
- Enums (with constant enumeration)
- Records and POJOs (with properties/required)
- Optional<T>, OptionalInt, OptionalDouble
- Sealed interfaces (oneOf)
- JsonNode and Object (any)

Also adds SchemaGeneratorTest using compilation-testing approach
with javax.tools.JavaCompiler to exercise the generator at compile time.

Closes #1759

* fix: address code review - remove unused param, handle all primitive types

* fix(java): correct SimpleJavaFileObject override - getCharContent not getContent

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* spotless

* Remove .class files generated by test

* spotless

* fix: use Map.ofEntries for properties to avoid Map.of 10-entry limit

Address review comment r3461777483: Map.of() only supports up to 10
key-value pairs. Switch properties maps in SchemaGenerator to use
Map.ofEntries(Map.entry(...), ...) so records/POJOs/methods with >10
fields won't cause generated source compilation failures.

Update SchemaGeneratorTest expectations to match the new format.

* fix: add missing Byte/Short/Character boxed type mappings

Address review comment r3461777428: Byte and Short now map to
"integer", Character maps to "string", matching their primitive
equivalents. Add tests for all three.

* fix: add missing OptionalLong mapping in generateDeclaredTypeSchema

Address review comment r3461777459: OptionalLong was handled in
isOptionalType/unwrapOptional but missing from generateDeclaredTypeSchema,
causing it to fall through to POJO introspection when used as a direct
return type. Add the mapping and tests for OptionalInt, OptionalLong,
and OptionalDouble.

* fix: correct misleading @JsonSubTypes comment on sealed interface handling

Address review comment r3461777579: the implementation uses
getPermittedSubclasses() (Java sealed types), not Jackson annotations.

* test: add sealed interface test for oneOf schema generation

Address review comment r3461777685: the processor had special handling
for TestSealed* types but no test exercised generateSealedSchema().
Add a test with a sealed interface (TestSealedShape) and two record
permits (Circle, Rect) verifying the oneOf schema output.

* test: add >10-field record test proving Map.ofEntries compiles

Address review comment r3461777706: add a test with an 11-component
record that verifies the generated Map.ofEntries(...) expression
actually compiles, proving the Map.of 10-entry limit fix works
end-to-end.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Co-authored-by: Ed Burns <edburns@microsoft.com>
edburns added a commit that referenced this pull request Jun 24, 2026
…1777)

* Resume 1682 iterating

* Phase 03 answer questions

* On branch edburns/1682-java-tool-ergonomics
Your branch is up to date with 'upstream/edburns/1682-java-tool-ergonomics'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   1682-java-tool-ergonomics-prompts-remove-before-merge/20260618-prompts.md

Signed-off-by: Ed Burns <edburns@microsoft.com>

* WIP: Phase 3. Question 3.4

* WIP: Phase 3. Question 3.6

* WIP: Phase 3. Question 3.6: Answer

* Answer 3.7

* Resolve 3.8

* Initial plan

* feat(java): create @copilotTool and @Param annotations with tests

- Add NONE constant to ToolDefer enum for annotation default value
- Create com.github.copilot.tool.CopilotTool annotation
- Create com.github.copilot.tool.Param annotation
- Export com.github.copilot.tool package in module-info.java
- Add CopilotToolAnnotationTest verifying retention, targets, defaults

Closes #1758

* spotless

* fix(java): make ToolDefer.NONE serialize as null to prevent wire leak

NONE is an annotation-only sentinel for @copilotTool(defer=...) defaults.
Its @jsonvalue now returns null so @JsonInclude(NON_NULL) omits it from
the JSON-RPC payload, matching the nullable/optional semantics used by
all other SDKs (.NET CopilotToolDefer?, Node defer?, Go omitempty,
Python | None, Rust Option<DeferMode>).

* WIP Phase 4.1

* feat(java): create @copilotTool and @Param annotations (#1763)

* WIP Phase 4.1

* Remove prompts, pre-merge

* fix(java): correct ToolDefer.NONE Javadoc on @jsonvalue null semantics

Clarify that @jsonvalue returning null does not cause field omission
by @JsonInclude(NON_NULL) — it only changes the leak from "" to null.
The primary protection is mapping NONE to a null field reference before
constructing ToolDefinition (responsibility of the annotation processor
and ToolDefinition.fromObject()).

* fix(java): address three review comments

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* Revert "Remove prompts, pre-merge"

This reverts commit a4fe9b2.

---------

Co-authored-by: Ed Burns <edburns@microsoft.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* Initial plan

* feat(java): add SchemaGenerator compile-time type-to-JSON-Schema utility (#1766)

* Initial plan

* feat(java): add SchemaGenerator compile-time type-to-JSON-Schema utility

Creates SchemaGenerator.java that maps javax.lang.model TypeMirror
instances to JSON Schema source code literals (Map.of(...) expressions).

Implements all 24 type mappings from the specification including:
- Primitives and boxed types (int/Integer, long/Long, etc.)
- String, UUID, OffsetDateTime
- Collections (List<T>, Collection<T>, Set<T>)
- Maps (Map<String, V> with typed values)
- Arrays (String[])
- Enums (with constant enumeration)
- Records and POJOs (with properties/required)
- Optional<T>, OptionalInt, OptionalDouble
- Sealed interfaces (oneOf)
- JsonNode and Object (any)

Also adds SchemaGeneratorTest using compilation-testing approach
with javax.tools.JavaCompiler to exercise the generator at compile time.

Closes #1759

* fix: address code review - remove unused param, handle all primitive types

* fix(java): correct SimpleJavaFileObject override - getCharContent not getContent

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* spotless

* Remove .class files generated by test

* spotless

* fix: use Map.ofEntries for properties to avoid Map.of 10-entry limit

Address review comment r3461777483: Map.of() only supports up to 10
key-value pairs. Switch properties maps in SchemaGenerator to use
Map.ofEntries(Map.entry(...), ...) so records/POJOs/methods with >10
fields won't cause generated source compilation failures.

Update SchemaGeneratorTest expectations to match the new format.

* fix: add missing Byte/Short/Character boxed type mappings

Address review comment r3461777428: Byte and Short now map to
"integer", Character maps to "string", matching their primitive
equivalents. Add tests for all three.

* fix: add missing OptionalLong mapping in generateDeclaredTypeSchema

Address review comment r3461777459: OptionalLong was handled in
isOptionalType/unwrapOptional but missing from generateDeclaredTypeSchema,
causing it to fall through to POJO introspection when used as a direct
return type. Add the mapping and tests for OptionalInt, OptionalLong,
and OptionalDouble.

* fix: correct misleading @JsonSubTypes comment on sealed interface handling

Address review comment r3461777579: the implementation uses
getPermittedSubclasses() (Java sealed types), not Jackson annotations.

* test: add sealed interface test for oneOf schema generation

Address review comment r3461777685: the processor had special handling
for TestSealed* types but no test exercised generateSealedSchema().
Add a test with a sealed interface (TestSealedShape) and two record
permits (Circle, Rect) verifying the oneOf schema output.

* test: add >10-field record test proving Map.ofEntries compiles

Address review comment r3461777706: add a test with an 11-component
record that verifies the generated Map.ofEntries(...) expression
actually compiles, proving the Map.of 10-entry limit fix works
end-to-end.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Co-authored-by: Ed Burns <edburns@microsoft.com>

* WIP 4.3

* Initial plan

* feat(java): Add CopilotToolProcessor annotation processor (task 4.3)

Implements JSR 269 annotation processor that finds @CopilotTool-annotated
methods and generates $$CopilotToolMeta companion classes containing tool
definitions, JSON Schema, and invocation lambdas.

Key features:
- snake_case tool name conversion from camelCase method names
- Access level enforcement (compile error for private methods)
- Return type handling (String, void, CompletableFuture<String>, etc.)
- Argument deserialization (direct cast for primitives/String, convertValue for complex)
- @Param description and defaultValue support in schema
- ToolDefer support (NONE maps to null/regular create)
- overridesBuiltInTool and skipPermission support

Also includes comprehensive test suite using javax.tools.JavaCompiler
programmatic compilation.

Closes #1760

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* fix: Address code review feedback

- Use fully qualified type names in generated code for type safety
- Fix Files.walk() resource leak in test with try-with-resources
- Rename exception variables for clarity

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>

* fix: Fix Spotless formatting and test classpath for JDK 17

- Remove unused Collections import
- Reformat boolean expressions: && at start of continuation lines
- Reformat ternary: ? at start of continuation line
- Reformat .replace() chain with one call per line
- Fix hasErrorContaining stream method chain formatting
- Fix resolveClasspath() to use System.getProperty("java.class.path")
  first, ensuring Jackson and all test deps are available when compiling
  generated $$CopilotToolMeta code

* fix: Fix remaining Spotless violations and test classpath resolution

- Merge propertyEntries.add() onto one line per formatter requirement
- Fix sb.append() chain formatting to match Eclipse formatter output
- Revert escapeJava to original line-breaking style (formatter preference)
- Fix resolveClasspath() to combine system classpath with CodeSource
  paths from key classes (SDK, Jackson, RPC types) ensuring all
  dependencies are available for javac in the annotation processor test

* fix: Add jackson-core and jackson-annotations to test classpath

The generated 6342CopilotToolMeta code uses ObjectMapper which requires
jackson-core (Versioned, JsonFactory) and jackson-annotations at
compile time. Add these transitive dependencies to the key classes
list so their CodeSource paths are included in the javac classpath.

* fix: Fix Spotless formatting for keyClasses array initializer

* fix(java): Pass ObjectMapper as parameter in generated $$CopilotToolMeta contract

Address PR #1777 review comment (r3463252393): the generated
$$CopilotToolMeta class was using `new ObjectMapper()`, which lacks
the SDK Jackson configuration (JavaTimeModule, NON_NULL inclusion,
lenient unknown-properties). This would break tool argument coercion
and return serialization at runtime for java.time.* and other types.

Instead of embedding a bare or configured ObjectMapper in the
generated code, change the generated `definitions()` method signature
from:
    definitions(MyTools instance)
to:
    definitions(MyTools instance, ObjectMapper mapper)

This establishes an internal contract: the caller (the future
ToolDefinition.fromObject() in issue #1761) is responsible for
supplying a properly configured mapper via reflective invocation.
The generated code uses `mapper` for all convertValue() and
writeValueAsString() calls.

Benefits:
- No DRY violation (mapper config stays canonical in JsonRpcClient)
- No new public API exposing ObjectMapper
- No package-visibility workarounds
- Clean separation: generated code declares its needs, caller supplies

Issue #1761 description has been updated to document this contract
so the implementing agent knows to pass ObjectMapper as the second
argument when reflectively invoking definitions().

* fix(java): restrict single-param shortcut to records only

Address review comment on PR #1777: the isRecordOrPojo heuristic
incorrectly triggered for JDK container types (List, Map, etc.)
when used as a single tool parameter. For example, a tool with
parameter List<String> would attempt to deserialize the entire
arguments object as a List, failing at runtime.

Replace the heuristic with a deterministic check: only Java records
qualify for the getArgumentsAs() shortcut. Records are immutable
data carriers with compiler-guaranteed component lists, making them
safe for whole-object deserialization. POJOs and all other class
types now fall through to the per-field extraction path, which
always works correctly.

Removed isSimpleType() helper which was only used by the old
heuristic.

* fix(java): emit typed default values in JSON Schema

Address review comment on PR #1777: @Param(defaultValue=...) was
always emitted as a JSON string in the generated schema's 'default'
field, making numeric and boolean defaults the wrong type (e.g.,
"10" instead of 10, "true" instead of true).

Changes:
- withMeta helper: String defaultValue -> Object defaultValue
- buildPropertySchema: reuse generateDefaultLiteral() to emit typed
  Java literals (int, boolean, etc.) instead of always quoting
- Add test emitsTypedDefaultValuesInSchema verifying int -> 10,
  boolean -> true, String -> "hello" in generated code

* fix(java): fix double 61059CopilotToolMeta suffix in test helper

Address review comment on PR #1777: getGeneratedSource() fallback
search appended 61059CopilotToolMeta to a simpleName that already
contained it, producing MyTools$$CopilotToolMeta$$CopilotToolMeta.
Simplify to just match on 'class <simpleName>'.

* fix(java): use record constructor for independent flag combination

Address SDK Consistency Review on PR #1777: the if/else if chain
in writeToolDefinition silently dropped combined annotation flags
(e.g., overridesBuiltInTool + skipPermission + defer). All other
SDKs support combining these flags simultaneously.

Replace the factory method dispatch with a direct call to the
ToolDefinition record constructor, which accepts all seven fields
independently. Each flag is now emitted as its own argument:
Boolean.TRUE or null for overridesBuiltInTool/skipPermission,
ToolDefer.X or null for defer.

Add test generatesCombinedFlags verifying all three flags appear
in generated code when set together.

---------

Signed-off-by: Ed Burns <edburns@microsoft.com>
Co-authored-by: Ed Burns <edburns@microsoft.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Java] @CopilotTool ergonomics 4.2: Schema generation utility (TypeMirror to JSON Schema)

3 participants