diff --git a/spring-ai-modules/spring-ai-mcp-logging/pom.xml b/spring-ai-modules/spring-ai-mcp-logging/pom.xml
new file mode 100644
index 000000000000..c6587f148da3
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+
+
+ com.baeldung
+ spring-ai-modules
+ 0.0.1
+ ../pom.xml
+
+
+ spring-ai-mcp-logging
+ 0.0.1
+ spring-ai-mcp-logging
+
+
+
+ org.springframework.ai
+ spring-ai-starter-mcp-client
+
+
+ org.springframework.ai
+ spring-ai-starter-mcp-server
+
+
+
+
+ 21
+ 1.1.2
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-bom
+ ${spring-ai.version}
+ pom
+ import
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/McpLoggingClientApplication.java b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/McpLoggingClientApplication.java
new file mode 100644
index 000000000000..e63fb6a799d5
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/McpLoggingClientApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.mcp.logging.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class McpLoggingClientApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(McpLoggingClientApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/PasswordStrengthMcpClientHandlers.java b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/PasswordStrengthMcpClientHandlers.java
new file mode 100644
index 000000000000..5c65c76aec6b
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/PasswordStrengthMcpClientHandlers.java
@@ -0,0 +1,33 @@
+package com.baeldung.mcp.logging.client;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springaicommunity.mcp.annotation.McpLogging;
+import org.springframework.stereotype.Component;
+
+import io.modelcontextprotocol.spec.McpSchema;
+
+@Component
+public class PasswordStrengthMcpClientHandlers {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PasswordStrengthMcpClientHandlers.class);
+
+ private final List receivedLogs = new CopyOnWriteArrayList<>();
+
+ @McpLogging(clients = "password-strength-logging-server")
+ public void handleLoggingMessage(McpSchema.LoggingMessageNotification notification) {
+ LOGGER.info("Received server logging notification [{}]: {}", notification.level(), notification.data());
+ receivedLogs.add(notification);
+ }
+
+ public List getReceivedLogs() {
+ return receivedLogs;
+ }
+
+ public void reset() {
+ receivedLogs.clear();
+ }
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/PasswordStrengthToolClient.java b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/PasswordStrengthToolClient.java
new file mode 100644
index 000000000000..6c250c0333a6
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/client/PasswordStrengthToolClient.java
@@ -0,0 +1,30 @@
+package com.baeldung.mcp.logging.client;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.stereotype.Service;
+
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema;
+
+@Service
+public class PasswordStrengthToolClient {
+
+ public static final String CHECK_PASSWORD_STRENGTH_TOOL = "check_password_strength";
+
+ private final McpSyncClient mcpSyncClient;
+
+ public PasswordStrengthToolClient(List mcpSyncClients) {
+ if (mcpSyncClients.isEmpty()) {
+ throw new IllegalStateException("No McpSyncClient beans were configured");
+ }
+ this.mcpSyncClient = mcpSyncClients.get(0);
+ }
+
+ public McpSchema.CallToolResult checkPasswordStrength(String password) {
+ McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(CHECK_PASSWORD_STRENGTH_TOOL,
+ Map.of("password", password));
+ return mcpSyncClient.callTool(request);
+ }
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/McpLoggingServerApplication.java b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/McpLoggingServerApplication.java
new file mode 100644
index 000000000000..83c8a28b1bb3
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/McpLoggingServerApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.mcp.logging.server;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class McpLoggingServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(McpLoggingServerApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/PasswordStrengthResult.java b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/PasswordStrengthResult.java
new file mode 100644
index 000000000000..6162361be9de
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/PasswordStrengthResult.java
@@ -0,0 +1,6 @@
+package com.baeldung.mcp.logging.server;
+
+import java.util.List;
+
+public record PasswordStrengthResult(int score, List issues) {
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/PasswordStrengthService.java b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/PasswordStrengthService.java
new file mode 100644
index 000000000000..fa61a21c9ee4
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/java/com/baeldung/mcp/logging/server/PasswordStrengthService.java
@@ -0,0 +1,59 @@
+package com.baeldung.mcp.logging.server;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.springaicommunity.mcp.context.McpSyncRequestContext;
+import org.springaicommunity.mcp.annotation.McpTool;
+import org.springaicommunity.mcp.annotation.McpToolParam;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PasswordStrengthService {
+
+ private static final int MIN_RECOMMENDED_LENGTH = 12;
+
+ private static final Set COMMON_PASSWORDS = Set.of("password", "123456", "qwerty", "letmein", "password1",
+ "admin");
+
+ @McpTool(name = "check_password_strength", description = "Evaluates password strength and returns a score with recommendations.")
+ public PasswordStrengthResult checkStrength(
+ @McpToolParam(description = "The password to evaluate", required = true) String password,
+ McpSyncRequestContext ctx) {
+ ctx.debug("Evaluating password of length " + password.length());
+
+ List issues = new ArrayList<>();
+
+ if (password.length() < MIN_RECOMMENDED_LENGTH) {
+ ctx.warn("Password shorter than recommended " + MIN_RECOMMENDED_LENGTH + " characters");
+ issues.add("too short");
+ } else {
+ ctx.debug("Length check passed");
+ }
+
+ if (!password.matches(".*[A-Z].*")) {
+ ctx.warn("Password missing uppercase letters");
+ issues.add("no uppercase");
+ } else {
+ ctx.debug("Uppercase check passed");
+ }
+
+ if (!password.matches(".*[0-9].*")) {
+ ctx.warn("Password missing digits");
+ issues.add("no digits");
+ } else {
+ ctx.debug("Digit check passed");
+ }
+
+ if (COMMON_PASSWORDS.contains(password.toLowerCase())) {
+ ctx.error("Password found in common-password list");
+ issues.add("commonly used");
+ }
+
+ int score = Math.max(0, 100 - issues.size() * 25);
+ ctx.info("Final score: " + score);
+
+ return new PasswordStrengthResult(score, issues);
+ }
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application-client.properties b/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application-client.properties
new file mode 100644
index 000000000000..fd1b33195d06
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application-client.properties
@@ -0,0 +1,17 @@
+spring.application.name=mcp-logging-demo-client
+
+spring.main.web-application-type=none
+
+spring.main.banner-mode=off
+
+logging.file.name=target/mcp-logging-demo-client.log
+
+spring.ai.mcp.client.type=SYNC
+
+spring.autoconfigure.exclude=org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration
+
+spring.ai.mcp.client.stdio.connections.password-strength-logging-server.command=java
+
+spring.ai.mcp.client.stdio.connections.password-strength-logging-server.args=-Dloader.main=com.baeldung.mcp.logging.server.McpLoggingServerApplication,-jar,${MCP_JAR:target/spring-ai-mcp-logging.jar},--spring.profiles.active=server
+
+spring.autoconfigure.exclude=org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application-server.properties b/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application-server.properties
new file mode 100644
index 000000000000..14b0330038f3
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application-server.properties
@@ -0,0 +1,15 @@
+spring.application.name=mcp-logging-demo-server
+
+spring.ai.mcp.server.stdio=true
+
+spring.ai.mcp.server.name=password-strength-logging-server
+
+spring.ai.mcp.server.version=1.0.0
+
+spring.main.web-application-type=none
+
+spring.main.banner-mode=off
+
+logging.pattern.console=
+
+logging.file.name=target/mcp-logging-demo-server.log
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application.properties b/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application.properties
new file mode 100644
index 000000000000..4b49e8e8a2cd
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/main/resources/application.properties
@@ -0,0 +1 @@
+logging.level.root=INFO
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/test/java/com/baeldung/mcp/logging/client/PasswordStrengthMcpClientHandlersUnitTest.java b/spring-ai-modules/spring-ai-mcp-logging/src/test/java/com/baeldung/mcp/logging/client/PasswordStrengthMcpClientHandlersUnitTest.java
new file mode 100644
index 000000000000..ac6c4fd0ed08
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/test/java/com/baeldung/mcp/logging/client/PasswordStrengthMcpClientHandlersUnitTest.java
@@ -0,0 +1,33 @@
+package com.baeldung.mcp.logging.client;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+import io.modelcontextprotocol.spec.McpSchema;
+
+class PasswordStrengthMcpClientHandlersUnitTest {
+
+ private final PasswordStrengthMcpClientHandlers handlers = new PasswordStrengthMcpClientHandlers();
+
+ @Test
+ void whenLoggingMessageReceived_thenStoredInReceivedLogs() {
+ McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification.builder()
+ .level(McpSchema.LoggingLevel.WARNING).logger("password-strength-logging-server")
+ .data("Password shorter than recommended 12 characters").build();
+
+ handlers.handleLoggingMessage(notification);
+
+ assertThat(handlers.getReceivedLogs()).containsExactly(notification);
+ }
+
+ @Test
+ void whenLoggingMessageCleared_thenReceivedLogsEmpty() {
+ handlers.handleLoggingMessage(McpSchema.LoggingMessageNotification.builder().level(McpSchema.LoggingLevel.INFO)
+ .logger("password-strength-logging-server").data("Final score: 25").build());
+
+ handlers.reset();
+
+ assertThat(handlers.getReceivedLogs()).isEmpty();
+ }
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-mcp-logging/src/test/java/com/baeldung/mcp/logging/server/PasswordStrengthServiceUnitTest.java b/spring-ai-modules/spring-ai-mcp-logging/src/test/java/com/baeldung/mcp/logging/server/PasswordStrengthServiceUnitTest.java
new file mode 100644
index 000000000000..1db361e72119
--- /dev/null
+++ b/spring-ai-modules/spring-ai-mcp-logging/src/test/java/com/baeldung/mcp/logging/server/PasswordStrengthServiceUnitTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.mcp.logging.server;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.jupiter.api.Test;
+
+import org.springaicommunity.mcp.context.McpSyncRequestContext;
+
+class PasswordStrengthServiceUnitTest {
+
+ private final PasswordStrengthService passwordStrengthService = new PasswordStrengthService();
+
+ @Test
+ void whenPasswordIsStrong_thenScoreIsPerfectAndNoIssuesReported() {
+ McpSyncRequestContext ctx = mock(McpSyncRequestContext.class);
+
+ PasswordStrengthResult result = passwordStrengthService.checkStrength("Tr0ubad0urCastle!", ctx);
+
+ assertThat(result.score()).isEqualTo(100);
+ assertThat(result.issues()).isEmpty();
+ }
+
+ @Test
+ void whenPasswordIsShortWithNoUppercaseOrDigits_thenAllThreeIssuesAreReported() {
+ McpSyncRequestContext ctx = mock(McpSyncRequestContext.class);
+
+ PasswordStrengthResult result = passwordStrengthService.checkStrength("lowercase", ctx);
+
+ assertThat(result.issues()).containsExactlyInAnyOrder("too short", "no uppercase", "no digits");
+ assertThat(result.score()).isEqualTo(25);
+ }
+
+ @Test
+ void whenPasswordIsCommonlyUsed_thenErrorLevelNotificationIsSent() {
+ McpSyncRequestContext ctx = mock(McpSyncRequestContext.class);
+
+ PasswordStrengthResult result = passwordStrengthService.checkStrength("password1", ctx);
+
+ assertThat(result.issues()).contains("commonly used");
+ verify(ctx).error("Password found in common-password list");
+ }
+
+ @Test
+ void whenCheckStrengthIsCalled_thenNotificationsAreSentAtLevelsMatchingEachFinding() {
+ McpSyncRequestContext ctx = mock(McpSyncRequestContext.class);
+
+ passwordStrengthService.checkStrength("weak", ctx);
+
+ verify(ctx).debug("Evaluating password of length 4");
+ verify(ctx).warn("Password shorter than recommended 12 characters");
+ verify(ctx).warn("Password missing uppercase letters");
+ verify(ctx).warn("Password missing digits");
+ verify(ctx).info("Final score: 25");
+ }
+}
\ No newline at end of file