diff --git a/README.md b/README.md
index b2b077d..b8191b0 100644
--- a/README.md
+++ b/README.md
@@ -114,7 +114,7 @@ The Spring integration module provides seamless integration with Spring AI and S
- **`@McpLogging`** - Annotates methods that handle logging message notifications from MCP servers (requires `clientId` parameter)
- **`@McpSampling`** - Annotates methods that handle sampling requests from MCP servers
- **`@McpElicitation`** - Annotates methods that handle elicitation requests to gather additional information from users
-- **`@McpProgress`** - Annotates methods that handle progress notifications for long-running operations
+- **`@McpProgress`** - Annotates methods that handle progress notifications for long-running operations (requires `clientId` parameter)
- **`@McpToolListChanged`** - Annotates methods that handle tool list change notifications from MCP servers
- **`@McpResourceListChanged`** - Annotates methods that handle resource list change notifications from MCP servers
- **`@McpPromptListChanged`** - Annotates methods that handle prompt list change notifications from MCP servers
@@ -1103,9 +1103,10 @@ public class ProgressHandler {
/**
* Handle progress notifications with a single parameter.
+ * Note: clientId is now required for all @McpProgress annotations.
* @param notification The progress notification
*/
- @McpProgress
+ @McpProgress(clientId = "default-client")
public void handleProgressNotification(ProgressNotification notification) {
System.out.println(String.format("Progress: %.2f%% - %s",
notification.progress() * 100,
@@ -1114,12 +1115,13 @@ public class ProgressHandler {
/**
* Handle progress notifications with individual parameters.
+ * Note: clientId is now required for all @McpProgress annotations.
* @param progressToken The progress token identifying the operation
* @param progress The current progress (0.0 to 1.0)
* @param total Optional total value for the operation
* @param message Optional progress message
*/
- @McpProgress
+ @McpProgress(clientId = "default-client")
public void handleProgressWithParams(String progressToken, double progress, Double total, String message) {
if (total != null) {
System.out.println(String.format("Progress [%s]: %.0f/%.0f - %s",
diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpProgress.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpProgress.java
index 8f6f35b..ecb71e7 100644
--- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpProgress.java
+++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpProgress.java
@@ -21,9 +21,9 @@
*
*
* Example usage:
{@code
- * @McpProgress
+ * @McpProgress(clientId = "my-client-id")
* public void handleProgressMessage(ProgressNotification notification) {
- * // Handle the notification *
+ * // Handle the progress notification
* }
*
* @author Christian Tzolov
@@ -36,9 +36,9 @@
public @interface McpProgress {
/**
- * Used as connection or client identifier to select the MCP client, the logging
- * consumer is associated with. If not specified, is applied to all clients.
+ * Used as connection or client identifier to select the MCP client, the progress
+ * consumer is associated with.
*/
- String clientId() default "";
+ String clientId();
}
diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/AsyncProgressSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/AsyncProgressSpecification.java
index 9e38290..74b9f67 100644
--- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/AsyncProgressSpecification.java
+++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/AsyncProgressSpecification.java
@@ -4,6 +4,7 @@
package org.springaicommunity.mcp.method.progress;
+import java.util.Objects;
import java.util.function.Function;
import io.modelcontextprotocol.spec.McpSchema.ProgressNotification;
@@ -17,4 +18,12 @@
* @author Christian Tzolov
*/
public record AsyncProgressSpecification(String clientId, Function> progressHandler) {
+ public AsyncProgressSpecification {
+ Objects.requireNonNull(clientId, "clientId must not be null");
+ if (clientId.trim().isEmpty()) {
+ throw new IllegalArgumentException("clientId must not be empty");
+ }
+ Objects.requireNonNull(progressHandler, "progressHandler must not be null");
+ }
+
}
diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/SyncProgressSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/SyncProgressSpecification.java
index 3a14b67..e72f708 100644
--- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/SyncProgressSpecification.java
+++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/progress/SyncProgressSpecification.java
@@ -4,6 +4,7 @@
package org.springaicommunity.mcp.method.progress;
+import java.util.Objects;
import java.util.function.Consumer;
import io.modelcontextprotocol.spec.McpSchema.ProgressNotification;
@@ -16,4 +17,13 @@
* @author Christian Tzolov
*/
public record SyncProgressSpecification(String clientId, Consumer progressHandler) {
+
+ public SyncProgressSpecification {
+ Objects.requireNonNull(clientId, "clientId must not be null");
+ if (clientId.trim().isEmpty()) {
+ throw new IllegalArgumentException("clientId must not be empty");
+ }
+ Objects.requireNonNull(progressHandler, "progressHandler must not be null");
+ }
+
}
diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackExample.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackExample.java
index 2d7ec6f..b1aa4f6 100644
--- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackExample.java
+++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackExample.java
@@ -33,7 +33,7 @@ public static class AsyncProgressService {
* @param notification the progress notification
* @return Mono completing when processing is done
*/
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono handleProgressNotificationAsync(ProgressNotification notification) {
return Mono.fromRunnable(() -> {
int count = notificationCount.incrementAndGet();
@@ -51,7 +51,7 @@ public Mono handleProgressNotificationAsync(ProgressNotification notificat
* @param progressToken the progress token identifier
* @param total the total value as string
*/
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithParams(Double progress, String progressToken, String total) {
System.out.printf("[Sync in Async] Progress: %.2f%% for token %s (Total: %s)%n", progress * 100,
progressToken, total);
@@ -64,7 +64,7 @@ public void handleProgressWithParams(Double progress, String progressToken, Stri
* @param total the total value as string
* @return Mono completing when processing is done
*/
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono handleProgressWithParamsAsync(Double progress, String progressToken, String total) {
return Mono.fromRunnable(() -> {
System.out.printf("[Async Params] Progress: %.2f%% for token %s (Total: %s)%n", progress * 100,
@@ -78,7 +78,7 @@ public Mono handleProgressWithParamsAsync(Double progress, String progress
* @param progressToken the progress token identifier
* @param total the total value as string
*/
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressPrimitive(double progress, String progressToken, String total) {
System.out.printf("[Primitive] Processing: %.1f%% complete (Token: %s)%n", progress * 100, progressToken);
}
diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackTests.java
index a07c933..a2c5416 100644
--- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackTests.java
+++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/AsyncMcpProgressMethodCallbackTests.java
@@ -45,25 +45,25 @@ static class ValidMethods {
private String lastTotal;
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressVoid(ProgressNotification notification) {
this.lastNotification = notification;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono handleProgressMono(ProgressNotification notification) {
this.lastNotification = notification;
return Mono.empty();
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithParams(Double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
this.lastTotal = total;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono handleProgressWithParamsMono(Double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
@@ -71,7 +71,7 @@ public Mono handleProgressWithParamsMono(Double progress, String progressT
return Mono.empty();
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithPrimitiveDouble(double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
@@ -85,42 +85,42 @@ public void handleProgressWithPrimitiveDouble(double progress, String progressTo
*/
static class InvalidMethods {
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public String invalidReturnType(ProgressNotification notification) {
return "Invalid";
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono invalidMonoReturnType(ProgressNotification notification) {
return Mono.just("Invalid");
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidParameterCount(ProgressNotification notification, String extra) {
// Invalid parameter count
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidParameterType(String invalidType) {
// Invalid parameter type
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidParameterTypes(String progress, int progressToken, boolean total) {
// Invalid parameter types
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidFirstParameterType(String progress, String progressToken, String total) {
// Invalid first parameter type
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidSecondParameterType(Double progress, int progressToken, String total) {
// Invalid second parameter type
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidThirdParameterType(Double progress, String progressToken, int total) {
// Invalid third parameter type
}
diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackExample.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackExample.java
index fb7aa99..791a3a9 100644
--- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackExample.java
+++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackExample.java
@@ -28,7 +28,7 @@ public static class ProgressService {
* Handle progress notification with the full notification object.
* @param notification the progress notification
*/
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressNotification(ProgressNotification notification) {
notificationCount++;
System.out.printf("Progress Update #%d: Token=%s, Progress=%.2f%%, Total=%.0f, Message=%s%n",
@@ -42,7 +42,7 @@ public void handleProgressNotification(ProgressNotification notification) {
* @param progressToken the progress token identifier
* @param total the total value as string
*/
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithParams(Double progress, String progressToken, String total) {
System.out.printf("Progress: %.2f%% for token %s (Total: %s)%n", progress * 100, progressToken, total);
}
@@ -53,7 +53,7 @@ public void handleProgressWithParams(Double progress, String progressToken, Stri
* @param progressToken the progress token identifier
* @param total the total value as string
*/
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressPrimitive(double progress, String progressToken, String total) {
System.out.printf("Processing: %.1f%% complete (Token: %s)%n", progress * 100, progressToken);
}
diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackTests.java
index 7909ad3..4f31369 100644
--- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackTests.java
+++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/method/progress/SyncMcpProgressMethodCallbackTests.java
@@ -43,19 +43,19 @@ static class ValidMethods {
private String lastTotal;
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressNotification(ProgressNotification notification) {
this.lastNotification = notification;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithParams(Double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
this.lastTotal = total;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithPrimitiveDouble(double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
@@ -69,37 +69,37 @@ public void handleProgressWithPrimitiveDouble(double progress, String progressTo
*/
static class InvalidMethods {
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public String invalidReturnType(ProgressNotification notification) {
return "Invalid";
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidParameterCount(ProgressNotification notification, String extra) {
// Invalid parameter count
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidParameterType(String invalidType) {
// Invalid parameter type
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidParameterTypes(String progress, int progressToken, boolean total) {
// Invalid parameter types
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidFirstParameterType(String progress, String progressToken, String total) {
// Invalid first parameter type
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidSecondParameterType(Double progress, int progressToken, String total) {
// Invalid second parameter type
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void invalidThirdParameterType(Double progress, String progressToken, int total) {
// Invalid third parameter type
}
diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/AsyncMcpProgressProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/AsyncMcpProgressProviderTests.java
index 2dd9d48..2287266 100644
--- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/AsyncMcpProgressProviderTests.java
+++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/AsyncMcpProgressProviderTests.java
@@ -37,25 +37,25 @@ static class AsyncProgressHandler {
private String lastTotal;
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressVoid(ProgressNotification notification) {
this.lastNotification = notification;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono handleProgressMono(ProgressNotification notification) {
this.lastNotification = notification;
return Mono.empty();
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithParams(Double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
this.lastTotal = total;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono handleProgressWithParamsMono(Double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
@@ -63,7 +63,7 @@ public Mono handleProgressWithParamsMono(Double progress, String progressT
return Mono.empty();
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithPrimitiveDouble(double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
@@ -77,13 +77,13 @@ public Mono notAnnotatedMethod(ProgressNotification notification) {
}
// This method has invalid return type and should be ignored
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public String invalidReturnType(ProgressNotification notification) {
return "Invalid";
}
// This method has invalid Mono return type and should be ignored
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono invalidMonoReturnType(ProgressNotification notification) {
return Mono.just("Invalid");
}
@@ -171,8 +171,8 @@ void testClientIdExtraction() {
List specifications = provider.getProgressSpecifications();
- // All specifications should have empty clientId (default value from annotation)
- assertThat(specifications).allMatch(spec -> spec.clientId().equals(""));
+ // All specifications should have non-empty clientId
+ assertThat(specifications).allMatch(spec -> !spec.clientId().isEmpty());
}
@Test
@@ -180,7 +180,7 @@ void testErrorHandling() {
// Test class with method that throws an exception
class ErrorHandler {
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public Mono handleProgressWithError(ProgressNotification notification) {
return Mono.error(new RuntimeException("Test error"));
}
diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/SyncMcpProgressProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/SyncMcpProgressProviderTests.java
index b554b1e..958f6b9 100644
--- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/SyncMcpProgressProviderTests.java
+++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/progress/SyncMcpProgressProviderTests.java
@@ -35,19 +35,19 @@ static class ProgressHandler {
private String lastTotal;
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressNotification(ProgressNotification notification) {
this.lastNotification = notification;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithParams(Double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
this.lastTotal = total;
}
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public void handleProgressWithPrimitiveDouble(double progress, String progressToken, String total) {
this.lastProgress = progress;
this.lastProgressToken = progressToken;
@@ -60,7 +60,7 @@ public void notAnnotatedMethod(ProgressNotification notification) {
}
// This method has invalid return type and should be ignored
- @McpProgress
+ @McpProgress(clientId = "my-client-id")
public String invalidReturnType(ProgressNotification notification) {
return "Invalid";
}
@@ -145,8 +145,8 @@ void testClientIdExtraction() {
List specifications = provider.getProgressSpecifications();
- // All specifications should have empty clientId (default value from annotation)
- assertThat(specifications).allMatch(spec -> spec.clientId().equals(""));
+ // All specifications should have non-empty clientId
+ assertThat(specifications).allMatch(spec -> !spec.clientId().isEmpty());
}
}