Skip to content

Commit 831b4ff

Browse files
tzolovscottslewis
authored andcommitted
feat!: make clientId required for @McpSampling annotation (spring-ai-community#38)
BREAKING CHANGE: The clientId parameter is now required for @McpSampling annotation - Remove default empty string from McpSampling.clientId() - Add validation in AsyncSamplingSpecification and SyncSamplingSpecification constructors - Update documentation and examples to show required clientId parameter - Update all test cases to include clientId parameter - Add null and empty string validation for clientId in specification classes Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
1 parent 12739ca commit 831b4ff

File tree

8 files changed

+41
-21
lines changed

8 files changed

+41
-21
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ The Spring integration module provides seamless integration with Spring AI and S
112112

113113
#### Client
114114
- **`@McpLogging`** - Annotates methods that handle logging message notifications from MCP servers (requires `clientId` parameter)
115-
- **`@McpSampling`** - Annotates methods that handle sampling requests from MCP servers
115+
- **`@McpSampling`** - Annotates methods that handle sampling requests from MCP servers (requires `clientId` parameter)
116116
- **`@McpElicitation`** - Annotates methods that handle elicitation requests to gather additional information from users (requires `clientId` parameter)
117117
- **`@McpProgress`** - Annotates methods that handle progress notifications for long-running operations (requires `clientId` parameter)
118118
- **`@McpToolListChanged`** - Annotates methods that handle tool list change notifications from MCP servers
@@ -997,10 +997,11 @@ public class SamplingHandler {
997997

998998
/**
999999
* Handle sampling requests with a synchronous implementation.
1000+
* Note: clientId is now required for all @McpSampling annotations.
10001001
* @param request The create message request
10011002
* @return The create message result
10021003
*/
1003-
@McpSampling
1004+
@McpSampling(clientId = "default-client")
10041005
public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
10051006
// Process the request and generate a response
10061007
return CreateMessageResult.builder()
@@ -1029,10 +1030,11 @@ public class AsyncSamplingHandler {
10291030

10301031
/**
10311032
* Handle sampling requests with an asynchronous implementation.
1033+
* Note: clientId is now required for all @McpSampling annotations.
10321034
* @param request The create message request
10331035
* @return A Mono containing the create message result
10341036
*/
1035-
@McpSampling
1037+
@McpSampling(clientId = "default-client")
10361038
public Mono<CreateMessageResult> handleAsyncSamplingRequest(CreateMessageRequest request) {
10371039
return Mono.just(CreateMessageResult.builder()
10381040
.role(Role.ASSISTANT)

mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpSampling.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@
2828
*
2929
* <p>
3030
* Example usage: <pre>{@code
31-
* &#64;McpSampling
31+
* &#64;McpSampling(clientId = "test-client")
3232
* public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
3333
* // Process the request and return a result
3434
* return CreateMessageResult.builder()
3535
* .message("Generated response")
3636
* .build();
3737
* }
3838
*
39-
* &#64;McpSampling
39+
* &#64;McpSampling(clientId = "test-client")
4040
* public Mono<CreateMessageResult> handleAsyncSamplingRequest(CreateMessageRequest request) {
4141
* // Process the request asynchronously and return a result
4242
* return Mono.just(CreateMessageResult.builder()
@@ -56,8 +56,8 @@
5656

5757
/**
5858
* Used as connection or client identifier to select the MCP client, the sampling
59-
* method is associated with. If not specified, is applied to all clients.
59+
* method is associated with.
6060
*/
61-
String clientId() default "";
61+
String clientId();
6262

6363
}

mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/AsyncSamplingSpecification.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.springaicommunity.mcp.method.sampling;
22

3+
import java.util.Objects;
34
import java.util.function.Function;
45

56
import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
@@ -9,4 +10,12 @@
910
public record AsyncSamplingSpecification(String clientId,
1011
Function<CreateMessageRequest, Mono<CreateMessageResult>> samplingHandler) {
1112

13+
public AsyncSamplingSpecification {
14+
Objects.requireNonNull(clientId, "clientId must not be null");
15+
if (clientId.trim().isEmpty()) {
16+
throw new IllegalArgumentException("clientId must not be empty");
17+
}
18+
Objects.requireNonNull(samplingHandler, "samplingHandler must not be null");
19+
}
20+
1221
}

mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/SyncSamplingSpecification.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.springaicommunity.mcp.method.sampling;
22

3+
import java.util.Objects;
34
import java.util.function.Function;
45

56
import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
@@ -8,4 +9,12 @@
89
public record SyncSamplingSpecification(String clientId,
910
Function<CreateMessageRequest, CreateMessageResult> samplingHandler) {
1011

12+
public SyncSamplingSpecification {
13+
Objects.requireNonNull(clientId, "clientId must not be null");
14+
if (clientId.trim().isEmpty()) {
15+
throw new IllegalArgumentException("clientId must not be empty");
16+
}
17+
Objects.requireNonNull(samplingHandler, "samplingHandler must not be null");
18+
}
19+
1120
}

mcp-annotations/src/test/java/org/springaicommunity/mcp/method/sampling/AsyncMcpSamplingMethodCallbackExample.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class AsyncMcpSamplingMethodCallbackExample {
2525
* @param request The sampling request
2626
* @return The sampling result as a Mono
2727
*/
28-
@McpSampling
28+
@McpSampling(clientId = "test-client")
2929
public Mono<CreateMessageResult> handleAsyncSamplingRequest(CreateMessageRequest request) {
3030
// Process the request asynchronously and return a result
3131
return Mono.just(CreateMessageResult.builder()
@@ -40,7 +40,7 @@ public Mono<CreateMessageResult> handleAsyncSamplingRequest(CreateMessageRequest
4040
* @param request The sampling request
4141
* @return The sampling result directly
4242
*/
43-
@McpSampling
43+
@McpSampling(clientId = "test-client")
4444
public CreateMessageResult handleDirectSamplingRequest(CreateMessageRequest request) {
4545
// Process the request and return a direct result
4646
return CreateMessageResult.builder()
@@ -55,7 +55,7 @@ public CreateMessageResult handleDirectSamplingRequest(CreateMessageRequest requ
5555
* @param request The sampling request
5656
* @return A Mono with an invalid type
5757
*/
58-
@McpSampling
58+
@McpSampling(clientId = "test-client")
5959
public Mono<String> invalidMonoReturnType(CreateMessageRequest request) {
6060
return Mono.just("This method has an invalid return type");
6161
}
@@ -65,7 +65,7 @@ public Mono<String> invalidMonoReturnType(CreateMessageRequest request) {
6565
* @param invalidParam An invalid parameter type
6666
* @return The sampling result as a Mono
6767
*/
68-
@McpSampling
68+
@McpSampling(clientId = "test-client")
6969
public Mono<CreateMessageResult> invalidParameterType(String invalidParam) {
7070
return Mono.just(CreateMessageResult.builder()
7171
.role(Role.ASSISTANT)
@@ -78,7 +78,7 @@ public Mono<CreateMessageResult> invalidParameterType(String invalidParam) {
7878
* Example method with no parameters.
7979
* @return The sampling result as a Mono
8080
*/
81-
@McpSampling
81+
@McpSampling(clientId = "test-client")
8282
public Mono<CreateMessageResult> noParameters() {
8383
return Mono.just(CreateMessageResult.builder()
8484
.role(Role.ASSISTANT)
@@ -93,7 +93,7 @@ public Mono<CreateMessageResult> noParameters() {
9393
* @param extraParam An extra parameter
9494
* @return The sampling result as a Mono
9595
*/
96-
@McpSampling
96+
@McpSampling(clientId = "test-client")
9797
public Mono<CreateMessageResult> tooManyParameters(CreateMessageRequest request, String extraParam) {
9898
return Mono.just(CreateMessageResult.builder()
9999
.role(Role.ASSISTANT)

mcp-annotations/src/test/java/org/springaicommunity/mcp/method/sampling/SyncMcpSamplingMethodCallbackExample.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class SyncMcpSamplingMethodCallbackExample {
2424
* @param request The sampling request
2525
* @return The sampling result
2626
*/
27-
@McpSampling
27+
@McpSampling(clientId = "test-client")
2828
public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
2929
// Process the request and return a result
3030
return CreateMessageResult.builder()
@@ -39,7 +39,7 @@ public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
3939
* @param request The sampling request
4040
* @return A string (invalid return type)
4141
*/
42-
@McpSampling
42+
@McpSampling(clientId = "test-client")
4343
public String invalidReturnType(CreateMessageRequest request) {
4444
return "This method has an invalid return type";
4545
}
@@ -49,7 +49,7 @@ public String invalidReturnType(CreateMessageRequest request) {
4949
* @param invalidParam An invalid parameter type
5050
* @return The sampling result
5151
*/
52-
@McpSampling
52+
@McpSampling(clientId = "test-client")
5353
public CreateMessageResult invalidParameterType(String invalidParam) {
5454
return CreateMessageResult.builder()
5555
.role(Role.ASSISTANT)
@@ -62,7 +62,7 @@ public CreateMessageResult invalidParameterType(String invalidParam) {
6262
* Example method with no parameters.
6363
* @return The sampling result
6464
*/
65-
@McpSampling
65+
@McpSampling(clientId = "test-client")
6666
public CreateMessageResult noParameters() {
6767
return CreateMessageResult.builder()
6868
.role(Role.ASSISTANT)
@@ -77,7 +77,7 @@ public CreateMessageResult noParameters() {
7777
* @param extraParam An extra parameter
7878
* @return The sampling result
7979
*/
80-
@McpSampling
80+
@McpSampling(clientId = "test-client")
8181
public CreateMessageResult tooManyParameters(CreateMessageRequest request, String extraParam) {
8282
return CreateMessageResult.builder()
8383
.role(Role.ASSISTANT)

mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/sampling/AsyncMcpSamplingProviderTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void testGetSamplingHandler() {
3535
// Create a class with only one valid sampling method
3636
class SingleValidMethod {
3737

38-
@McpSampling
38+
@McpSampling(clientId = "test-client")
3939
public Mono<CreateMessageResult> handleAsyncSamplingRequest(CreateMessageRequest request) {
4040
return Mono.just(CreateMessageResult.builder()
4141
.role(io.modelcontextprotocol.spec.McpSchema.Role.ASSISTANT)
@@ -77,7 +77,7 @@ void testDirectResultMethod() {
7777
// Create a class with only the direct result method
7878
class DirectResultOnly {
7979

80-
@McpSampling
80+
@McpSampling(clientId = "test-client")
8181
public CreateMessageResult handleDirectSamplingRequest(CreateMessageRequest request) {
8282
return CreateMessageResult.builder()
8383
.role(io.modelcontextprotocol.spec.McpSchema.Role.ASSISTANT)

mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/sampling/SyncMcpSamplingProviderTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ void testGetSamplingHandler() {
3232
// Create a class with only one valid sampling method
3333
class SingleValidMethod {
3434

35-
@McpSampling
35+
@McpSampling(clientId = "test-client")
3636
public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
3737
return CreateMessageResult.builder()
3838
.role(io.modelcontextprotocol.spec.McpSchema.Role.ASSISTANT)

0 commit comments

Comments
 (0)