Overview
Enforce a non-empty @Param.value() (description) at compile time in the CopilotToolProcessor annotation processor. Any @CopilotTool method that declares a parameter annotated with @Param and leaves value() blank must produce a build error, not a silent runtime degradation.
Parent Epic: #1682
Parent Follow-up: #1809
Motivation
The @Param.value() field carries the parameter description that is sent to the LLM as part of the tool schema. Without a meaningful description, the LLM cannot reliably select and invoke the right tool or supply the right argument.
The lambda-based ToolDefinition.from*(...) API (issue #1810) enforces non-blank descriptions at construction time via Param<T>. The annotation-based API must achieve the same guarantee, but at compile time via the existing JSR 269 annotation processor.
Currently, CopilotToolProcessor validates:
@Param(required=true) + non-empty defaultValue → compile error
@Param(defaultValue=...) + primitive without required=false → compile error
But it does not validate:
@Param(value = "") → no error today, silently sends empty description to the LLM
Deliverables
Files to modify
java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java
java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java or a dedicated processor test
java/README.md if the annotation usage examples do not already show non-empty descriptions
Implementation specification
1. Compile-time validation in CopilotToolProcessor
In the parameter validation loop (the existing block that iterates over method.getParameters()), add a check after retrieving paramAnnotation:
if (paramAnnotation != null && paramAnnotation.value().isBlank()) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"@Param value (description) must not be blank — "
+ "descriptions are required so the LLM can correctly invoke this tool",
param);
}
The check must apply to:
- every method parameter annotated with
@Param
- both instance and static
@CopilotTool methods
- inherited methods discovered through the class hierarchy
2. Exception: record-component parameters
When a @CopilotTool method takes a single record as its parameter, the @Param annotation on that wrapper parameter is not currently used for per-field descriptions (record components carry their own @Param annotations). The existing processor already emits an error if name/value/required are set on a single-record wrapper parameter. The new blank-value check must not apply to the wrapper parameter itself, only to scalar/flat parameters.
Specifically: the blank-description check fires only when the parameter is not the single-record wrapper shortcut.
3. Error message quality
The error message must:
- identify the parameter by name
- identify the enclosing method and class
- explain why a description is required
Example:
error: @Param on parameter 'query' in 'MyTools.searchItems' has a blank value (description).
Descriptions are required so the LLM can correctly select and invoke the tool.
4. @Param without any annotation
Parameters without @Param are currently accepted (they get no description in the schema). This issue does not require those to be flagged; it only enforces non-blank on parameters that do carry @Param.
Gating tests and criteria
All of the following must pass:
Compile-time error tests (annotation processor tests)
- Blank
value: A method parameter annotated @Param(value = "") produces a compile error.
- Whitespace-only
value: @Param(value = " ") also produces a compile error.
- Valid
value: @Param(value = "Search query") compiles without error.
- No
@Param at all: A parameter without @Param compiles without error (not in scope of this issue).
- Record wrapper exemption:
@Param-annotated single-record wrapper parameter is exempt from the blank-value check.
No-regression tests
- All existing
ToolDefinitionFromObjectTest tests continue to pass.
mvn clean verify passes with no regressions.
Design decisions
- Compile-time only. No runtime enforcement is added; the annotation processor is the right enforcement point.
value() only. This issue does not impose a description requirement on unannotated parameters.
- Aligned with lambda API. The lambda API enforces blank descriptions at construction time; this makes the annotation API consistent.
- Parity with
@CopilotTool description. The tool-level description is already implicitly required (an empty tool description is poor practice); this extends the same expectation to parameter descriptions.
Example — before and after
Before (currently compiles silently):
@CopilotTool("Search for items")
public String searchItems(@Param("") String query) {
return "results for " + query;
}
After (produces compile error):
error: @Param on parameter 'query' in 'MyTools.searchItems' has a blank value (description).
Descriptions are required so the LLM can correctly select and invoke the tool.
Correct usage:
@CopilotTool("Search for items")
public String searchItems(@Param("Search keyword") String query) {
return "results for " + query;
}
Branch and PR conventions
- Branch: create from
main on upstream
- PR target:
main
- Run
cd java && mvn verify before merging
- Run
cd java && mvn spotless:apply before commit
- Follow existing Javadoc conventions for any public API/documentation updates
Overview
Enforce a non-empty
@Param.value()(description) at compile time in theCopilotToolProcessorannotation processor. Any@CopilotToolmethod that declares a parameter annotated with@Paramand leavesvalue()blank must produce a build error, not a silent runtime degradation.Parent Epic: #1682
Parent Follow-up: #1809
Motivation
The
@Param.value()field carries the parameter description that is sent to the LLM as part of the tool schema. Without a meaningful description, the LLM cannot reliably select and invoke the right tool or supply the right argument.The lambda-based
ToolDefinition.from*(...)API (issue #1810) enforces non-blank descriptions at construction time viaParam<T>. The annotation-based API must achieve the same guarantee, but at compile time via the existing JSR 269 annotation processor.Currently,
CopilotToolProcessorvalidates:@Param(required=true)+ non-emptydefaultValue→ compile error@Param(defaultValue=...)+ primitive withoutrequired=false→ compile errorBut it does not validate:
@Param(value = "")→ no error today, silently sends empty description to the LLMDeliverables
Files to modify
java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.javajava/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.javaor a dedicated processor testjava/README.mdif the annotation usage examples do not already show non-empty descriptionsImplementation specification
1. Compile-time validation in
CopilotToolProcessorIn the parameter validation loop (the existing block that iterates over
method.getParameters()), add a check after retrievingparamAnnotation:The check must apply to:
@Param@CopilotToolmethods2. Exception: record-component parameters
When a
@CopilotToolmethod takes a single record as its parameter, the@Paramannotation on that wrapper parameter is not currently used for per-field descriptions (record components carry their own@Paramannotations). The existing processor already emits an error ifname/value/requiredare set on a single-record wrapper parameter. The new blank-value check must not apply to the wrapper parameter itself, only to scalar/flat parameters.Specifically: the blank-description check fires only when the parameter is not the single-record wrapper shortcut.
3. Error message quality
The error message must:
Example:
4.
@Paramwithout any annotationParameters without
@Paramare currently accepted (they get no description in the schema). This issue does not require those to be flagged; it only enforces non-blank on parameters that do carry@Param.Gating tests and criteria
All of the following must pass:
Compile-time error tests (annotation processor tests)
value: A method parameter annotated@Param(value = "")produces a compile error.value:@Param(value = " ")also produces a compile error.value:@Param(value = "Search query")compiles without error.@Paramat all: A parameter without@Paramcompiles without error (not in scope of this issue).@Param-annotated single-record wrapper parameter is exempt from the blank-value check.No-regression tests
ToolDefinitionFromObjectTesttests continue to pass.mvn clean verifypasses with no regressions.Design decisions
value()only. This issue does not impose a description requirement on unannotated parameters.@CopilotTooldescription. The tool-level description is already implicitly required (an empty tool description is poor practice); this extends the same expectation to parameter descriptions.Example — before and after
Before (currently compiles silently):
After (produces compile error):
Correct usage:
Branch and PR conventions
mainonupstreammaincd java && mvn verifybefore mergingcd java && mvn spotless:applybefore commit