Skip to content

Commit 738efd8

Browse files
author
Mark Pollack
committed
Javadoc: annotation runtime and its extension SPI (pass 1, batch B06)
1 parent 67b8933 commit 738efd8

12 files changed

Lines changed: 674 additions & 298 deletions

‎acp-agent-support/src/main/java/com/agentclientprotocol/sdk/agent/support/AcpAgentSupport.java‎

Lines changed: 179 additions & 94 deletions
Large diffs are not rendered by default.

‎acp-agent-support/src/main/java/com/agentclientprotocol/sdk/agent/support/AcpHandlerMethod.java‎

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,17 @@
1212
import org.jspecify.annotations.Nullable;
1313

1414
/**
15-
* Encapsulates a handler method and its target bean.
16-
* Supports both eager instance and lazy factory-based construction.
15+
* One handler method that {@link AcpAgentSupport} found: the bean it is called on, the
16+
* {@link Method}, and the ACP method it answers, such as {@code session/prompt} or an extension
17+
* method name. The builder creates one for each handler method when a bean is given to it, and the
18+
* runtime calls {@link #invoke} for each request. Applications do not need this class, and no
19+
* extension point receives it: an interceptor or argument resolver learns what is called from
20+
* {@link com.agentclientprotocol.sdk.agent.support.invocation.AcpInvocationContext#getAcpMethod()
21+
* AcpInvocationContext.getAcpMethod()} and {@link AcpMethodParameter#getMethod()}.
22+
*
23+
* <p>The constructor makes the method accessible ({@link Method#setAccessible(boolean)}), so
24+
* handler methods of any visibility can be called; it fails if the method's module does not open
25+
* its package to the SDK. Instances do not change after construction.
1726
*
1827
* @author Mark Pollack
1928
* @since 1.0.0
@@ -31,20 +40,22 @@ public final class AcpHandlerMethod {
3140
private final AcpMethodParameter returnType;
3241

3342
/**
34-
* Create a handler method with an eager bean instance.
35-
* @param bean the target bean instance
36-
* @param method the method to invoke
37-
* @param acpMethod the ACP method name (e.g., "initialize", "session/prompt")
43+
* Creates a handler method that calls {@code method} on {@code bean}.
44+
* @param bean the object the method is called on
45+
* @param method the handler method, made accessible here
46+
* @param acpMethod the ACP method it answers, such as {@code "initialize"} or
47+
* {@code "session/prompt"}
3848
*/
3949
public AcpHandlerMethod(Object bean, Method method, String acpMethod) {
4050
this(() -> bean, method, acpMethod);
4151
}
4252

4353
/**
44-
* Create a handler method with a lazy bean supplier.
45-
* @param beanSupplier supplier for the target bean
46-
* @param method the method to invoke
47-
* @param acpMethod the ACP method name
54+
* Creates a handler method that calls {@code method} on the object {@code beanSupplier} gives.
55+
* The supplier is asked on every call, so it may return a different object each time.
56+
* @param beanSupplier gives the object the method is called on
57+
* @param method the handler method, made accessible here
58+
* @param acpMethod the ACP method it answers
4859
*/
4960
public AcpHandlerMethod(Supplier<Object> beanSupplier, Method method, String acpMethod) {
5061
this.beanSupplier = beanSupplier;
@@ -63,50 +74,56 @@ public AcpHandlerMethod(Supplier<Object> beanSupplier, Method method, String acp
6374
}
6475

6576
/**
66-
* Get the target bean instance.
77+
* Returns the object the method is called on, asking the supplier if one was given.
6778
* @return the bean
6879
*/
6980
public Object getBean() {
7081
return beanSupplier.get();
7182
}
7283

7384
/**
74-
* Get the method to invoke.
85+
* Returns the handler method.
7586
* @return the method
7687
*/
7788
public Method getMethod() {
7889
return method;
7990
}
8091

8192
/**
82-
* Get the ACP method name (e.g., "initialize", "session/prompt").
93+
* Returns the ACP method this handler answers, such as {@code "initialize"},
94+
* {@code "session/prompt"} or an extension method name starting with {@code _}.
8395
* @return the ACP method name
8496
*/
8597
public String getAcpMethod() {
8698
return acpMethod;
8799
}
88100

89101
/**
90-
* Get the pre-computed parameter metadata.
91-
* @return array of parameter metadata
102+
* Returns the descriptions of the method's parameters, in order, made once at construction. The
103+
* array is shared: do not change it.
104+
* @return the parameter descriptions
92105
*/
93106
public AcpMethodParameter[] getParameters() {
94107
return parameters;
95108
}
96109

97110
/**
98-
* Get the return type metadata.
99-
* @return the return type parameter
111+
* Returns the description of the method's return type.
112+
* @return the return type description, for which {@link AcpMethodParameter#isReturnType()} is
113+
* true
100114
*/
101115
public AcpMethodParameter getReturnType() {
102116
return returnType;
103117
}
104118

105119
/**
106-
* Invoke the method with the given arguments.
107-
* @param args the arguments
108-
* @return the return value
109-
* @throws Exception if invocation fails
120+
* Calls the handler method on the bean with {@code args}, which must match its parameters in
121+
* number and type.
122+
* @param args the arguments, one for each parameter
123+
* @return what the method returned, or null for a {@code void} method
124+
* @throws Exception if the handler method throws: its exception, as it threw it (not wrapped in
125+
* an {@code InvocationTargetException}); an {@link Error} it throws is rethrown as is
126+
* @throws IllegalArgumentException if {@code args} do not match the method's parameters
110127
*/
111128
public @Nullable Object invoke(@Nullable Object[] args) throws Exception {
112129
try {

‎acp-agent-support/src/main/java/com/agentclientprotocol/sdk/agent/support/handler/ReturnValueHandler.java‎

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,25 @@
99
import org.jspecify.annotations.Nullable;
1010

1111
/**
12-
* Strategy interface for handling method return values and converting
13-
* them to protocol responses.
12+
* Turns what a handler method returned into the response of its ACP method. Implement one for a
13+
* return type the built-in handlers do not accept, such as another library's async type, and
14+
* register it with
15+
* {@link com.agentclientprotocol.sdk.agent.support.AcpAgentSupport.Builder#returnValueHandler
16+
* AcpAgentSupport.Builder.returnValueHandler(..)}. The built-in handlers accept the method's
17+
* response itself, {@code void} and {@code String} from a {@code @Prompt} method, a {@code Mono},
18+
* {@code CompletionStage} or single-value {@code Publisher} of those, and an extension method's
19+
* result.
1420
*
15-
* <p>Implementations of this interface are responsible for converting
16-
* handler method return values to the appropriate protocol response types.
21+
* <p>For each call, the runtime uses the first handler whose {@link #supportsReturnType} returns
22+
* {@code true}: custom handlers in the order they were added, then the built-in ones, so a custom
23+
* handler can also replace a built-in one. A return type that a custom handler supports is not
24+
* checked when the agent is built. A handler for an async type can wait for the value and pass it
25+
* to a built-in handler, such as {@link StringToPromptResponseHandler} for a {@code String} from a
26+
* {@code @Prompt} method.
27+
*
28+
* <p>Implementations must be thread-safe: one instance serves every call, from every session and
29+
* connection, concurrently. {@link #handleReturnValue} runs on the call's handler thread and may
30+
* block there, as the built-in handler does to wait for a {@code Mono}.
1731
*
1832
* @author Mark Pollack
1933
* @since 1.0.0
@@ -22,19 +36,25 @@
2236
public interface ReturnValueHandler {
2337

2438
/**
25-
* Determine whether this handler supports the given return type.
26-
* @param returnType the return type metadata
27-
* @return true if this handler can process the return type
39+
* Returns whether this handler accepts the return type. Decide from the type only: it is asked
40+
* when the agent is built and on every call.
41+
* @param returnType the handler method's return type
42+
* @return true if {@link #handleReturnValue} can turn values of it into the response
2843
*/
2944
boolean supportsReturnType(AcpMethodParameter returnType);
3045

3146
/**
32-
* Handle the return value and convert to protocol response.
33-
* @param returnValue the value returned by the handler method
34-
* @param returnType the return type metadata
35-
* @param context the invocation context
36-
* @return the protocol response
37-
* @throws ReturnValueHandlingException if handling fails
47+
* Turns {@code returnValue} into the response sent to the client. For a request, the result
48+
* must be the ACP method's response type, such as a {@code PromptResponse}; another type, or
49+
* null, is answered with an internal error ({@code -32603}). For a notification, the result is
50+
* ignored.
51+
* @param returnValue what the method returned, after the interceptors' {@code postInvoke}; null
52+
* for a {@code void} method
53+
* @param returnType the handler method's return type
54+
* @param context the call's context
55+
* @return the response
56+
* @throws ReturnValueHandlingException if the value cannot be turned into a response; the call
57+
* fails with an internal error ({@code -32603}) carrying its message
3858
*/
3959
@Nullable Object handleReturnValue(@Nullable Object returnValue, AcpMethodParameter returnType,
4060
AcpInvocationContext context);

‎acp-agent-support/src/main/java/com/agentclientprotocol/sdk/agent/support/handler/ReturnValueHandlerComposite.java‎

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@
1212
import org.jspecify.annotations.Nullable;
1313

1414
/**
15-
* Composite that chains multiple return value handlers.
15+
* A {@link ReturnValueHandler} that asks a list of handlers in order and uses the first that
16+
* supports a return type. The annotation runtime builds one each time an agent is built, with the
17+
* custom handlers first and the built-in ones after, and turns every handler method's result into
18+
* its response through it. Applications register handlers on {@code AcpAgentSupport.Builder} and do
19+
* not need this class.
1620
*
17-
* <p>NOTE: Unlike argument resolvers, NO caching is used here.
18-
* Return types vary more and caching provides less benefit.
21+
* <p>Unlike {@link com.agentclientprotocol.sdk.agent.support.resolver.ArgumentResolverComposite},
22+
* it remembers nothing: it asks the handlers again on every call. Handling is thread-safe once the
23+
* handlers are added; adding handlers is not, so add them all before the first use.
1924
*
2025
* @author Mark Pollack
2126
* @since 1.0.0
@@ -25,28 +30,28 @@ public class ReturnValueHandlerComposite implements ReturnValueHandler {
2530
private final List<ReturnValueHandler> handlers = new ArrayList<>();
2631

2732
/**
28-
* Add a handler to the chain.
29-
* @param handler the handler to add
30-
* @return this composite for chaining
33+
* Adds a handler, asked after the ones added before.
34+
* @param handler the handler, not null
35+
* @return this composite
3136
*/
3237
public ReturnValueHandlerComposite addHandler(ReturnValueHandler handler) {
3338
this.handlers.add(handler);
3439
return this;
3540
}
3641

3742
/**
38-
* Add multiple handlers to the chain.
39-
* @param handlers the handlers to add
40-
* @return this composite for chaining
43+
* Adds handlers, in list order, asked after the ones added before.
44+
* @param handlers the handlers, none of them null
45+
* @return this composite
4146
*/
4247
public ReturnValueHandlerComposite addHandlers(List<ReturnValueHandler> handlers) {
4348
this.handlers.addAll(handlers);
4449
return this;
4550
}
4651

4752
/**
48-
* Get the list of registered handlers.
49-
* @return unmodifiable list of handlers
53+
* Returns the handlers in the order they are asked.
54+
* @return an unmodifiable copy of the handlers
5055
*/
5156
public List<ReturnValueHandler> getHandlers() {
5257
return List.copyOf(handlers);
@@ -57,6 +62,17 @@ public boolean supportsReturnType(AcpMethodParameter returnType) {
5762
return findHandler(returnType) != null;
5863
}
5964

65+
/**
66+
* {@inheritDoc}
67+
*
68+
* <p>Delegates to the first handler that supports the return type.
69+
* @param returnValue {@inheritDoc}
70+
* @param returnType {@inheritDoc}
71+
* @param context {@inheritDoc}
72+
* @return {@inheritDoc}
73+
* @throws ReturnValueHandlingException if no handler supports the return type, or the handler
74+
* throws it
75+
*/
6076
@Override
6177
public @Nullable Object handleReturnValue(@Nullable Object returnValue, AcpMethodParameter returnType,
6278
AcpInvocationContext context) {

‎acp-agent-support/src/main/java/com/agentclientprotocol/sdk/agent/support/handler/ReturnValueHandlingException.java‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,30 @@
55
package com.agentclientprotocol.sdk.agent.support.handler;
66

77
/**
8-
* Exception thrown when return value handling fails.
8+
* Thrown when what a handler method returned cannot be turned into its response: no return value
9+
* handler accepts the type, or the value does not suit the method, such as a {@code String}
10+
* returned outside a prompt turn. The call fails, and the client receives an internal error
11+
* ({@code -32603}) whose message is this exception's message, so keep secrets out of it. Building
12+
* the agent rejects most unsuitable return types up front, so at run time it usually comes from a
13+
* custom {@link ReturnValueHandler}, which throws it for the same purpose.
914
*
1015
* @author Mark Pollack
1116
* @since 1.0.0
1217
*/
1318
public class ReturnValueHandlingException extends RuntimeException {
1419

1520
/**
16-
* Create a new exception with a message.
17-
* @param message the error message
21+
* Creates the exception.
22+
* @param message what could not be handled, sent to the client
1823
*/
1924
public ReturnValueHandlingException(String message) {
2025
super(message);
2126
}
2227

2328
/**
24-
* Create a new exception with a message and cause.
25-
* @param message the error message
26-
* @param cause the underlying cause
29+
* Creates the exception with the failure that caused it.
30+
* @param message what could not be handled, sent to the client
31+
* @param cause the underlying failure
2732
*/
2833
public ReturnValueHandlingException(String message, Throwable cause) {
2934
super(message, cause);

0 commit comments

Comments
 (0)