From 9b73a9276a45c70c03d1eb0ef49499aa9b5560e4 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Tue, 5 Aug 2025 18:17:42 +0530 Subject: [PATCH 01/10] mcp-client-oauth2 --- mcp-spring/mcp-client-oauth2/pom.xml | 66 +++++++++++++++++ .../mcpclientoauth2/CalculatorController.java | 48 +++++++++++++ .../McpClientOauth2Application.java | 46 ++++++++++++ .../McpSyncClientExchangeFilterFunction.java | 72 +++++++++++++++++++ .../src/main/resources/application.properties | 38 ++++++++++ 5 files changed, 270 insertions(+) create mode 100644 mcp-spring/mcp-client-oauth2/pom.xml create mode 100644 mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java create mode 100644 mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java create mode 100644 mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java create mode 100644 mcp-spring/mcp-client-oauth2/src/main/resources/application.properties diff --git a/mcp-spring/mcp-client-oauth2/pom.xml b/mcp-spring/mcp-client-oauth2/pom.xml new file mode 100644 index 000000000000..5f1969edfda5 --- /dev/null +++ b/mcp-spring/mcp-client-oauth2/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.4 + + + com.baeldung.mcp + mcp-client-oauth2 + 0.0.1-SNAPSHOT + mcp-client-oauth2 + mcp-client-oauth2 + + + 17 + 1.0.0 + + + + + org.springframework.ai + spring-ai-starter-model-anthropic + + + org.springframework.ai + spring-ai-starter-mcp-client-webflux + + + org.springframework.boot + spring-boot-starter-test + 3.5.4 + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + + + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java b/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java new file mode 100644 index 000000000000..7bddf8ea6f42 --- /dev/null +++ b/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java @@ -0,0 +1,48 @@ +package com.baeldung.mcp.mcpclientoauth2; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CalculatorController { + + private final ChatClient chatClient; + + public CalculatorController(ChatClient chatClient) { + this.chatClient = chatClient; + } + + @GetMapping("/calculate") + public String calculate(@RequestParam String expression, @RegisteredOAuth2AuthorizedClient("authserver") OAuth2AuthorizedClient authorizedClient) { + + String prompt = String.format("Please calculate the following mathematical expression using the available calculator tools: %s", expression); + + return chatClient.prompt() + .user(prompt) + .call() + .content(); + } + + @GetMapping("/") + public String home() { + return """ + + +

MCP Calculator with OAuth2

+

Try these examples:

+ +

Note: You'll be redirected to login if not authenticated.

+ + + """; + } +} \ No newline at end of file diff --git a/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java b/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java new file mode 100644 index 000000000000..43125eaf645e --- /dev/null +++ b/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java @@ -0,0 +1,46 @@ +package com.baeldung.mcp.mcpclientoauth2; + +import java.util.List; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.mcp.SyncMcpToolCallbackProvider; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.reactive.function.client.WebClient; + +import io.modelcontextprotocol.client.McpSyncClient; + +@SpringBootApplication +public class McpClientOauth2Application { + + public static void main(String[] args) { + SpringApplication.run(McpClientOauth2Application.class, args); + } + + @Bean + ChatClient chatClient(ChatClient.Builder chatClientBuilder, List mcpClients) { + return chatClientBuilder.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpClients)) + .build(); + } + + @Bean + WebClient.Builder webClientBuilder(McpSyncClientExchangeFilterFunction filterFunction) { + return WebClient.builder() + .apply(filterFunction.configuration()); + } + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http.authorizeHttpRequests(auth -> auth.anyRequest() + .permitAll()) + .oauth2Client(Customizer.withDefaults()) + .csrf(CsrfConfigurer::disable) + .build(); + } + +} diff --git a/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java b/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java new file mode 100644 index 000000000000..111752e5ec39 --- /dev/null +++ b/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java @@ -0,0 +1,72 @@ +package com.baeldung.mcp.mcpclientoauth2; + +import java.util.function.Consumer; + +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider; +import org.springframework.security.oauth2.client.OAuth2AuthorizationContext; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import org.springframework.web.reactive.function.client.WebClient; + +import reactor.core.publisher.Mono; + +@Component +public class McpSyncClientExchangeFilterFunction implements ExchangeFilterFunction { + + private final ClientCredentialsOAuth2AuthorizedClientProvider clientCredentialTokenProvider = new ClientCredentialsOAuth2AuthorizedClientProvider(); + + private final ServletOAuth2AuthorizedClientExchangeFilterFunction delegate; + + private final ClientRegistrationRepository clientRegistrationRepository; + + private static final String AUTHORIZATION_CODE_CLIENT_REGISTRATION_ID = "authserver"; + + private static final String CLIENT_CREDENTIALS_CLIENT_REGISTRATION_ID = "authserver-client-credentials"; + + public McpSyncClientExchangeFilterFunction(OAuth2AuthorizedClientManager clientManager, ClientRegistrationRepository clientRegistrationRepository) { + this.delegate = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientManager); + this.delegate.setDefaultClientRegistrationId(AUTHORIZATION_CODE_CLIENT_REGISTRATION_ID); + this.clientRegistrationRepository = clientRegistrationRepository; + } + + @Override + public Mono filter(ClientRequest request, ExchangeFunction next) { + if (RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes) { + return this.delegate.filter(request, next); + } else { + var accessToken = getClientCredentialsAccessToken(); + var requestWithToken = ClientRequest.from(request) + .headers(headers -> headers.setBearerAuth(accessToken)) + .build(); + return next.exchange(requestWithToken); + } + } + + private String getClientCredentialsAccessToken() { + var clientRegistration = this.clientRegistrationRepository.findByRegistrationId(CLIENT_CREDENTIALS_CLIENT_REGISTRATION_ID); + + var authRequest = OAuth2AuthorizationContext.withClientRegistration(clientRegistration) + .principal(new AnonymousAuthenticationToken("client-credentials-client", "client-credentials-client", + AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"))) + .build(); + return this.clientCredentialTokenProvider.authorize(authRequest) + .getAccessToken() + .getTokenValue(); + } + + public Consumer configuration() { + return builder -> builder.defaultRequest(this.delegate.defaultRequest()) + .filter(this); + } + +} \ No newline at end of file diff --git a/mcp-spring/mcp-client-oauth2/src/main/resources/application.properties b/mcp-spring/mcp-client-oauth2/src/main/resources/application.properties new file mode 100644 index 000000000000..f7de042acd55 --- /dev/null +++ b/mcp-spring/mcp-client-oauth2/src/main/resources/application.properties @@ -0,0 +1,38 @@ +spring.application.name=mcp-client-oauth2 + +server.port=8080 + +spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8090 +spring.ai.mcp.client.type=SYNC + +spring.security.oauth2.client.provider.authserver.issuer-uri=http://localhost:9000 + +# OAuth2 Client for User-Initiated Requests (Authorization Code Grant) +spring.security.oauth2.client.registration.authserver.client-id=mcp-client +spring.security.oauth2.client.registration.authserver.client-secret=mcp-secret +spring.security.oauth2.client.registration.authserver.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.authserver.provider=authserver +spring.security.oauth2.client.registration.authserver.scope=openid,profile,mcp.read,mcp.write +spring.security.oauth2.client.registration.authserver.redirect-uri={baseUrl}/authorize/oauth2/code/{registrationId} + +# OAuth2 Client for Machine-to-Machine Requests (Client Credentials Grant) +spring.security.oauth2.client.registration.authserver-client-credentials.client-id=mcp-client +spring.security.oauth2.client.registration.authserver-client-credentials.client-secret=mcp-secret +spring.security.oauth2.client.registration.authserver-client-credentials.authorization-grant-type=client_credentials +spring.security.oauth2.client.registration.authserver-client-credentials.provider=authserver +spring.security.oauth2.client.registration.authserver-client-credentials.scope=mcp.read,mcp.write + +spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY} + +# Logging Configuration +logging.level.com.baeldung.mcp=DEBUG +logging.level.org.springframework.security.oauth2=INFO +logging.level.org.springframework.ai.mcp=DEBUG +logging.level.org.springframework.web.reactive.function.client=INFO +logging.level.io.modelcontextprotocol=INFO + +# Spring Boot Configuration +spring.main.lazy-initialization=false +spring.task.execution.pool.core-size=4 +spring.task.execution.pool.max-size=8 + From 96eb6d278a90a1c4defe856ee021423b63907631 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Tue, 5 Aug 2025 18:18:11 +0530 Subject: [PATCH 02/10] mcp-server-oauth2 --- mcp-spring/mcp-server-oauth2/pom.xml | 51 +++++++++++++++++++ .../mcpserveroauth2/CalculatorService.java | 43 ++++++++++++++++ .../McpServerOauth2Application.java | 22 ++++++++ .../model/CalculationResult.java | 5 ++ .../src/main/resources/application.properties | 10 ++++ 5 files changed, 131 insertions(+) create mode 100644 mcp-spring/mcp-server-oauth2/pom.xml create mode 100644 mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java create mode 100644 mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java create mode 100644 mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java create mode 100644 mcp-spring/mcp-server-oauth2/src/main/resources/application.properties diff --git a/mcp-spring/mcp-server-oauth2/pom.xml b/mcp-spring/mcp-server-oauth2/pom.xml new file mode 100644 index 000000000000..e1416e9bfc13 --- /dev/null +++ b/mcp-spring/mcp-server-oauth2/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.5.4 + + + + com.baeldung.mcp + mcp-server-oauth2 + 1.0.0 + mcp-server-oauth2 + + + + com.fasterxml + classmate + 1.7.0 + + + org.springframework.ai + spring-ai-starter-mcp-server-webmvc + 1.0.0-M7 + + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + 17 + 1.0.0 + + \ No newline at end of file diff --git a/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java b/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java new file mode 100644 index 000000000000..2a14b1a1869a --- /dev/null +++ b/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java @@ -0,0 +1,43 @@ +package com.baeldung.mcp.mcpserveroauth2; + +import org.springframework.ai.tool.annotation.Tool; +import org.springframework.ai.tool.annotation.ToolParam; +import org.springframework.stereotype.Service; + +import com.baeldung.mcp.mcpserveroauth2.model.CalculationResult; + +@Service +public class CalculatorService { + + @Tool(description = "Add two numbers") + public CalculationResult add(@ToolParam(description = "First number") double a, @ToolParam(description = "Second number") double b) { + + double result = a + b; + return new CalculationResult("addition", a, b, result); + } + + @Tool(description = "Subtract two numbers") + public CalculationResult subtract(@ToolParam(description = "First number") double a, @ToolParam(description = "Second number") double b) { + + double result = a - b; + return new CalculationResult("subtraction", a, b, result); + } + + @Tool(description = "Multiply two numbers") + public CalculationResult multiply(@ToolParam(description = "First number") double a, @ToolParam(description = "Second number") double b) { + + double result = a * b; + return new CalculationResult("multiplication", a, b, result); + } + + @Tool(description = "Divide two numbers") + public CalculationResult divide(@ToolParam(description = "First number") double a, @ToolParam(description = "Second number") double b) { + + if (b == 0) { + throw new IllegalArgumentException("Cannot divide by zero"); + } + + double result = a / b; + return new CalculationResult("division", a, b, result); + } +} \ No newline at end of file diff --git a/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java b/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java new file mode 100644 index 000000000000..a1bf401127f3 --- /dev/null +++ b/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java @@ -0,0 +1,22 @@ +package com.baeldung.mcp.mcpserveroauth2; + +import org.springframework.ai.tool.ToolCallbackProvider; +import org.springframework.ai.tool.method.MethodToolCallbackProvider; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class McpServerOauth2Application { + + public static void main(String[] args) { + SpringApplication.run(McpServerOauth2Application.class, args); + } + + @Bean + public ToolCallbackProvider calculatorTools(CalculatorService calculatorService) { + return MethodToolCallbackProvider.builder() + .toolObjects(calculatorService) + .build(); + } +} \ No newline at end of file diff --git a/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java b/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java new file mode 100644 index 000000000000..2781d27e5b9e --- /dev/null +++ b/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java @@ -0,0 +1,5 @@ +package com.baeldung.mcp.mcpserveroauth2.model; + +public record CalculationResult(String operation, double operand1, double operand2, double result) { + +} \ No newline at end of file diff --git a/mcp-spring/mcp-server-oauth2/src/main/resources/application.properties b/mcp-spring/mcp-server-oauth2/src/main/resources/application.properties new file mode 100644 index 000000000000..a57b9576e8c9 --- /dev/null +++ b/mcp-spring/mcp-server-oauth2/src/main/resources/application.properties @@ -0,0 +1,10 @@ +spring.application.name=mcp-server-oauth2 +server.port=8090 +spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9000 +spring.ai.mcp.server.enabled=true +spring.ai.mcp.server.name=mcp-calculator-server +spring.ai.mcp.server.version=1.0.0 +spring.ai.mcp.server.stdio=false +logging.level.org.springframework.security=DEBUG +logging.level.org.springframework.ai.mcp=INFO +logging.level.root=INFO From 1556f32d42054d6a6cb1c86b18dddfd5d3806bf5 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Tue, 5 Aug 2025 18:19:58 +0530 Subject: [PATCH 03/10] oauth2-authorization-server --- .../oauth2-authorization-server/pom.xml | 70 +++++++++++++ .../Oauth2AuthorizationServerApplication.java | 13 +++ .../config/AuthorizationServerConfig.java | 97 +++++++++++++++++++ .../src/main/resources/application.yml | 28 ++++++ 4 files changed, 208 insertions(+) create mode 100644 mcp-spring/oauth2-authorization-server/pom.xml create mode 100644 mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java create mode 100644 mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java create mode 100644 mcp-spring/oauth2-authorization-server/src/main/resources/application.yml diff --git a/mcp-spring/oauth2-authorization-server/pom.xml b/mcp-spring/oauth2-authorization-server/pom.xml new file mode 100644 index 000000000000..a0a2e85e19c2 --- /dev/null +++ b/mcp-spring/oauth2-authorization-server/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.4 + + + com.baeldung.mcp + oauth2-authorization-server + 0.0.1-SNAPSHOT + oauth2-authorization-server + oauth2-authorization-server + + + + + + + + + + + + + + + 17 + + + + org.springframework.boot + spring-boot-starter-oauth2-authorization-server + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.security + spring-security-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java b/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java new file mode 100644 index 000000000000..58393e97b6af --- /dev/null +++ b/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.mcp.oauth2authorizationserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Oauth2AuthorizationServerApplication { + + public static void main(String[] args) { + SpringApplication.run(Oauth2AuthorizationServerApplication.class, args); + } + +} diff --git a/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java b/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java new file mode 100644 index 000000000000..b8b5419698d9 --- /dev/null +++ b/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java @@ -0,0 +1,97 @@ +package com.baeldung.mcp.oauth2authorizationserver.config; + +import java.time.Duration; +import java.util.UUID; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.http.MediaType; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; +import org.springframework.security.oauth2.core.oidc.OidcScopes; +import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; +import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer; +import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; +import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; +import org.springframework.security.oauth2.server.authorization.settings.TokenSettings; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher; + +@Configuration +@EnableWebSecurity +public class AuthorizationServerConfig { + + @Bean + @Order(1) + public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { + OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); + + http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) + .oidc(Customizer.withDefaults()); + + http.exceptionHandling(exceptions -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"), + new MediaTypeRequestMatcher(MediaType.TEXT_HTML))) + .oauth2ResourceServer(resourceServer -> resourceServer.jwt(Customizer.withDefaults())); + + return http.build(); + } + + @Bean + @Order(2) + public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authorize -> authorize.anyRequest() + .authenticated()) + .formLogin(Customizer.withDefaults()); + + return http.build(); + } + + @Bean + public RegisteredClientRepository registeredClientRepository() { + RegisteredClient mcpClient = RegisteredClient.withId(UUID.randomUUID() + .toString()) + .clientId("mcp-client") + .clientSecret("{noop}mcp-secret") + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .redirectUri("http://localhost:8080/authorize/oauth2/code/authserver") + .redirectUri("http://127.0.0.1:8080/authorize/oauth2/code/authserver") + .postLogoutRedirectUri("http://localhost:8080/") + // Standard OAuth2/OIDC scopes + .scope(OidcScopes.OPENID) + .scope(OidcScopes.PROFILE) + // Custom MCP scopes + .scope("mcp.read") + .scope("mcp.write") + .clientSettings(ClientSettings.builder() + .requireAuthorizationConsent(false) + .requireProofKey(false) + .build()) + .tokenSettings(TokenSettings.builder() + .accessTokenTimeToLive(Duration.ofHours(1)) + .refreshTokenTimeToLive(Duration.ofDays(1)) + .reuseRefreshTokens(false) + .build()) + .build(); + + return new InMemoryRegisteredClientRepository(mcpClient); + } + + @Bean + public AuthorizationServerSettings authorizationServerSettings() { + return AuthorizationServerSettings.builder() + .issuer("http://localhost:9000") + .build(); + } +} \ No newline at end of file diff --git a/mcp-spring/oauth2-authorization-server/src/main/resources/application.yml b/mcp-spring/oauth2-authorization-server/src/main/resources/application.yml new file mode 100644 index 000000000000..0ce17329286f --- /dev/null +++ b/mcp-spring/oauth2-authorization-server/src/main/resources/application.yml @@ -0,0 +1,28 @@ +server: + port: 9000 + +spring: + security: + user: + name: user + password: password + oauth2: + authorizationserver: + client: + oidc-client: + registration: + client-id: "mcp-client" + client-secret: "{noop}mcp-secret" + client-authentication-methods: + - "client_secret_basic" + authorization-grant-types: + - "authorization_code" + - "client_credentials" + - "refresh_token" + redirect-uris: + - "http://localhost:8080/authorize/oauth2/code/authserver" + scopes: + - "openid" + - "profile" + - "calc.read" + - "calc.write" \ No newline at end of file From f896d1e008679c50b651d4e6fd6b2bf60e6f68b0 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Tue, 5 Aug 2025 18:20:25 +0530 Subject: [PATCH 04/10] mcp-spring module --- mcp-spring/pom.xml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 mcp-spring/pom.xml diff --git a/mcp-spring/pom.xml b/mcp-spring/pom.xml new file mode 100644 index 000000000000..2524c17be27f --- /dev/null +++ b/mcp-spring/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + mcp-spring + pom + mcp-spring + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + + + mcp-client-oauth2 + mcp-server-oauth2 + oauth2-authorization-server + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + From b123ed2d7dd1508f06bf780835f0bfc4aa4d557d Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Tue, 5 Aug 2025 18:21:21 +0530 Subject: [PATCH 05/10] added-new-module --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 427d29eb0249..bb6a0f6df96c 100644 --- a/pom.xml +++ b/pom.xml @@ -355,6 +355,7 @@ spring-swagger-codegen-modules video-tutorials jhipster-6 + mcp-spring From 0d8029aabafb992c97ef2e98a77a666c6ef590b6 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Sun, 10 Aug 2025 21:55:07 +0530 Subject: [PATCH 06/10] [BAEL-9326] moved to spring-ai-modules/spring-ai-mcp --- pom.xml | 1 - .../spring-ai-mcp/mcp-spring}/mcp-client-oauth2/pom.xml | 0 .../baeldung/mcp/mcpclientoauth2/CalculatorController.java | 0 .../mcp/mcpclientoauth2/McpClientOauth2Application.java | 0 .../mcpclientoauth2/McpSyncClientExchangeFilterFunction.java | 0 .../src/main/resources/application.properties | 0 .../spring-ai-mcp/mcp-spring}/mcp-server-oauth2/pom.xml | 0 .../com/baeldung/mcp/mcpserveroauth2/CalculatorService.java | 0 .../mcp/mcpserveroauth2/McpServerOauth2Application.java | 0 .../baeldung/mcp/mcpserveroauth2/model/CalculationResult.java | 0 .../src/main/resources/application.properties | 0 .../mcp-spring}/oauth2-authorization-server/pom.xml | 0 .../Oauth2AuthorizationServerApplication.java | 0 .../config/AuthorizationServerConfig.java | 0 .../src/main/resources/application.yml | 0 .../spring-ai-mcp/mcp-spring}/pom.xml | 4 ++-- 16 files changed, 2 insertions(+), 3 deletions(-) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-client-oauth2/pom.xml (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-client-oauth2/src/main/resources/application.properties (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-server-oauth2/pom.xml (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/mcp-server-oauth2/src/main/resources/application.properties (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/oauth2-authorization-server/pom.xml (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/oauth2-authorization-server/src/main/resources/application.yml (100%) rename {mcp-spring => spring-ai-modules/spring-ai-mcp/mcp-spring}/pom.xml (91%) diff --git a/pom.xml b/pom.xml index 7820d2f36862..a2cf091e9c9b 100644 --- a/pom.xml +++ b/pom.xml @@ -355,7 +355,6 @@ spring-swagger-codegen-modules video-tutorials jhipster-6 - mcp-spring diff --git a/mcp-spring/mcp-client-oauth2/pom.xml b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/pom.xml similarity index 100% rename from mcp-spring/mcp-client-oauth2/pom.xml rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/pom.xml diff --git a/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java similarity index 100% rename from mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/CalculatorController.java diff --git a/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java similarity index 100% rename from mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpClientOauth2Application.java diff --git a/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java similarity index 100% rename from mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/java/com/baeldung/mcp/mcpclientoauth2/McpSyncClientExchangeFilterFunction.java diff --git a/mcp-spring/mcp-client-oauth2/src/main/resources/application.properties b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/resources/application.properties similarity index 100% rename from mcp-spring/mcp-client-oauth2/src/main/resources/application.properties rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/main/resources/application.properties diff --git a/mcp-spring/mcp-server-oauth2/pom.xml b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/pom.xml similarity index 100% rename from mcp-spring/mcp-server-oauth2/pom.xml rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/pom.xml diff --git a/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java similarity index 100% rename from mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/CalculatorService.java diff --git a/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java similarity index 100% rename from mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/McpServerOauth2Application.java diff --git a/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java similarity index 100% rename from mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/java/com/baeldung/mcp/mcpserveroauth2/model/CalculationResult.java diff --git a/mcp-spring/mcp-server-oauth2/src/main/resources/application.properties b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/resources/application.properties similarity index 100% rename from mcp-spring/mcp-server-oauth2/src/main/resources/application.properties rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/main/resources/application.properties diff --git a/mcp-spring/oauth2-authorization-server/pom.xml b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/pom.xml similarity index 100% rename from mcp-spring/oauth2-authorization-server/pom.xml rename to spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/pom.xml diff --git a/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java similarity index 100% rename from mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/Oauth2AuthorizationServerApplication.java diff --git a/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java similarity index 100% rename from mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/main/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfig.java diff --git a/mcp-spring/oauth2-authorization-server/src/main/resources/application.yml b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/main/resources/application.yml similarity index 100% rename from mcp-spring/oauth2-authorization-server/src/main/resources/application.yml rename to spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/main/resources/application.yml diff --git a/mcp-spring/pom.xml b/spring-ai-modules/spring-ai-mcp/mcp-spring/pom.xml similarity index 91% rename from mcp-spring/pom.xml rename to spring-ai-modules/spring-ai-mcp/mcp-spring/pom.xml index 2524c17be27f..2cce68be578b 100644 --- a/mcp-spring/pom.xml +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/pom.xml @@ -7,9 +7,9 @@ mcp-spring - parent-modules com.baeldung - 1.0.0-SNAPSHOT + spring-ai-mcp + 0.0.1 From 05fa4bad1abdd72c659f49674cdc0d04be619058 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Wed, 20 Aug 2025 10:40:07 +0530 Subject: [PATCH 07/10] [BAEL-9326] migrating to dependency versions as properties --- .../mcp-spring/mcp-client-oauth2/pom.xml | 3 ++- .../mcp-spring/mcp-server-oauth2/pom.xml | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/pom.xml b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/pom.xml index 5f1969edfda5..54e599d905fa 100644 --- a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/pom.xml +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/pom.xml @@ -17,6 +17,7 @@ 17 1.0.0 + 3.5.4 @@ -31,7 +32,7 @@ org.springframework.boot spring-boot-starter-test - 3.5.4 + ${spring-boot.starter.test} org.springframework.boot diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/pom.xml b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/pom.xml index e1416e9bfc13..c79adea28aff 100644 --- a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/pom.xml +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/pom.xml @@ -17,22 +17,36 @@ 1.0.0 mcp-server-oauth2 + + 1.7.0 + 1.0.0-M7 + 17 + 1.0.0 + 5.10.2 + + com.fasterxml classmate - 1.7.0 + ${classmate.version} org.springframework.ai spring-ai-starter-mcp-server-webmvc - 1.0.0-M7 + ${spring-ai.version} org.springframework.boot spring-boot-starter-oauth2-resource-server + + org.junit.jupiter + junit-jupiter + ${junit-version} + test + @@ -44,8 +58,4 @@ - - 17 - 1.0.0 - \ No newline at end of file From 9ed5f720cffb8a888069f0356beeb10956d641e2 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Wed, 20 Aug 2025 10:44:31 +0530 Subject: [PATCH 08/10] [BAEL-9326] fixes for packaging --- .../CalculatorControllerTest.java | 51 +++++++++++++++ .../config/AuthorizationServerConfigTest.java | 63 +++++++++++++++++++ .../spring-ai-mcp/mcp-spring/pom.xml | 1 + spring-ai-modules/spring-ai-mcp/pom.xml | 1 + 4 files changed, 116 insertions(+) create mode 100644 spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerTest.java create mode 100644 spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigTest.java diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerTest.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerTest.java new file mode 100644 index 000000000000..242370158793 --- /dev/null +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerTest.java @@ -0,0 +1,51 @@ +package com.baeldung.mcp.mcpclientoauth2; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +public class CalculatorControllerTest { + + private MockMvc mockMvc; + private ChatClient chatClient; + + @BeforeEach + void setUp() { + chatClient = mock(ChatClient.class, Mockito.RETURNS_DEEP_STUBS); + CalculatorController controller = new CalculatorController(chatClient); + mockMvc = MockMvcBuilders.standaloneSetup(controller) + .build(); + } + + @Test + void givenValidExpression_whenCalculateEndpointCalled_thenReturnsExpectedResult() throws Exception { + when(chatClient.prompt() + .user(anyString()) + .call() + .content()).thenReturn("42"); + mockMvc.perform(MockMvcRequestBuilders.get("/calculate") + .param("expression", "40 + 2")) + .andExpect(MockMvcResultMatchers.status() + .isOk()) + .andExpect(MockMvcResultMatchers.content() + .string("42")); + } + + @Test + void givenHomeRequest_whenHomeEndpointCalled_thenReturnsHtmlWithTitle() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/")) + .andExpect(MockMvcResultMatchers.status() + .isOk()) + .andExpect(MockMvcResultMatchers.content() + .string(org.hamcrest.Matchers.containsString("MCP Calculator with OAuth2"))); + } +} diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigTest.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigTest.java new file mode 100644 index 000000000000..d19c87711cdb --- /dev/null +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigTest.java @@ -0,0 +1,63 @@ +package com.baeldung.mcp.oauth2authorizationserver.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; +import org.springframework.security.web.SecurityFilterChain; + +@SpringBootTest(classes = AuthorizationServerConfig.class) +class AuthorizationServerConfigTest { + + private final ApplicationContext context; + + private final RegisteredClientRepository registeredClientRepository; + + private final AuthorizationServerSettings authorizationServerSettings; + + public AuthorizationServerConfigTest(ApplicationContext context, RegisteredClientRepository registeredClientRepository, + AuthorizationServerSettings authorizationServerSettings) { + this.authorizationServerSettings = authorizationServerSettings; + this.registeredClientRepository = registeredClientRepository; + this.context = context; + } + + @Test + void givenContext_whenLoaded_thenSecurityFilterChainsPresent() { + SecurityFilterChain chain1 = (SecurityFilterChain) context.getBean("authorizationServerSecurityFilterChain"); + SecurityFilterChain chain2 = (SecurityFilterChain) context.getBean("defaultSecurityFilterChain"); + assertNotNull(chain1); + assertNotNull(chain2); + } + + @Test + void givenRegisteredClientRepository_whenQueried_thenContainsExpectedClient() { + RegisteredClient client = registeredClientRepository.findByClientId("mcp-client"); + assertNotNull(client); + assertEquals("mcp-client", client.getClientId()); + assertTrue(client.getClientAuthenticationMethods() + .stream() + .anyMatch(m -> m.getValue() + .equals("client_secret_basic"))); + assertTrue(client.getAuthorizationGrantTypes() + .stream() + .anyMatch(g -> g.getValue() + .equals("authorization_code"))); + assertTrue(client.getScopes() + .contains("mcp.read")); + assertTrue(client.getScopes() + .contains("mcp.write")); + } + + @Test + void givenAuthorizationServerSettings_whenLoaded_thenIssuerIsCorrect() { + assertEquals("http://localhost:9000", authorizationServerSettings.getIssuer()); + } +} + diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/pom.xml b/spring-ai-modules/spring-ai-mcp/mcp-spring/pom.xml index 2cce68be578b..f0197fe1d1a8 100644 --- a/spring-ai-modules/spring-ai-mcp/mcp-spring/pom.xml +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/pom.xml @@ -10,6 +10,7 @@ com.baeldung spring-ai-mcp 0.0.1 + ../pom.xml diff --git a/spring-ai-modules/spring-ai-mcp/pom.xml b/spring-ai-modules/spring-ai-mcp/pom.xml index 1d124c0256d1..ef18c1fbb5ef 100644 --- a/spring-ai-modules/spring-ai-mcp/pom.xml +++ b/spring-ai-modules/spring-ai-mcp/pom.xml @@ -13,6 +13,7 @@ com.baeldung spring-ai-mcp 0.0.1 + pom spring-ai-mcp From 7625c2b9f1aeb9e273201e58a32af5d3e4dabccc Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Wed, 20 Aug 2025 10:44:51 +0530 Subject: [PATCH 09/10] [BAEL-9326] unit tests --- .../CalculatorServiceTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceTest.java diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceTest.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceTest.java new file mode 100644 index 000000000000..22faf57cf86e --- /dev/null +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceTest.java @@ -0,0 +1,66 @@ +package com.baeldung.mcp.mcpserveroauth2; + +import com.baeldung.mcp.mcpserveroauth2.model.CalculationResult; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class CalculatorServiceTest { + private final CalculatorService calculatorService = new CalculatorService(); + + @Test + void givenTwoNumbers_whenAdd_thenReturnsSum() { + CalculationResult result = calculatorService.add(5.0, 3.0); + assertEquals("addition", result.operation()); + assertEquals(5.0, result.operand1()); + assertEquals(3.0, result.operand2()); + assertEquals(8.0, result.result()); + } + + @Test + void givenTwoNumbers_whenSubtract_thenReturnsDifference() { + CalculationResult result = calculatorService.subtract(10.0, 4.0); + assertEquals("subtraction", result.operation()); + assertEquals(10.0, result.operand1()); + assertEquals(4.0, result.operand2()); + assertEquals(6.0, result.result()); + } + + @Test + void givenTwoNumbers_whenMultiply_thenReturnsProduct() { + CalculationResult result = calculatorService.multiply(6.0, 7.0); + assertEquals("multiplication", result.operation()); + assertEquals(6.0, result.operand1()); + assertEquals(7.0, result.operand2()); + assertEquals(42.0, result.result()); + } + + @Test + void givenTwoNumbers_whenDivide_thenReturnsQuotient() { + CalculationResult result = calculatorService.divide(15.0, 3.0); + assertEquals("division", result.operation()); + assertEquals(15.0, result.operand1()); + assertEquals(3.0, result.operand2()); + assertEquals(5.0, result.result()); + } + + @Test + void givenZeroDivisor_whenDivide_thenThrowsException() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> + calculatorService.divide(10.0, 0.0) + ); + assertEquals("Cannot divide by zero", ex.getMessage()); + } + + @Test + void givenNegativeNumbers_whenAdd_thenReturnsSum() { + CalculationResult result = calculatorService.add(-2.0, -3.0); + assertEquals(-5.0, result.result()); + } + + @Test + void givenLargeNumbers_whenMultiply_thenReturnsProduct() { + CalculationResult result = calculatorService.multiply(1e6, 1e6); + assertEquals(1e12, result.result()); + } +} + From 16e0f26c4819bf8aa028cd576c67923b05c68365 Mon Sep 17 00:00:00 2001 From: venkat1701 Date: Sat, 23 Aug 2025 22:17:27 +0530 Subject: [PATCH 10/10] [BAEL-9326] unit test class name changed --- ...rControllerTest.java => CalculatorControllerUnitTest.java} | 2 +- ...culatorServiceTest.java => CalculatorServiceUnitTest.java} | 2 +- ...ConfigTest.java => AuthorizationServerConfigUnitTest.java} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/{CalculatorControllerTest.java => CalculatorControllerUnitTest.java} (97%) rename spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/{CalculatorServiceTest.java => CalculatorServiceUnitTest.java} (98%) rename spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/{AuthorizationServerConfigTest.java => AuthorizationServerConfigUnitTest.java} (93%) diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerTest.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerUnitTest.java similarity index 97% rename from spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerTest.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerUnitTest.java index 242370158793..03316dccf55c 100644 --- a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerTest.java +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-client-oauth2/src/test/java/com/baeldung/mcp/mcpclientoauth2/CalculatorControllerUnitTest.java @@ -13,7 +13,7 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -public class CalculatorControllerTest { +public class CalculatorControllerUnitTest { private MockMvc mockMvc; private ChatClient chatClient; diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceTest.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceUnitTest.java similarity index 98% rename from spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceTest.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceUnitTest.java index 22faf57cf86e..0e73a06633d1 100644 --- a/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceTest.java +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/mcp-server-oauth2/src/test/java/com/baeldung/mcp/mcpserveroauth2/CalculatorServiceUnitTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -class CalculatorServiceTest { +class CalculatorServiceUnitTest { private final CalculatorService calculatorService = new CalculatorService(); @Test diff --git a/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigTest.java b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigUnitTest.java similarity index 93% rename from spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigTest.java rename to spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigUnitTest.java index d19c87711cdb..cd2d7fe54141 100644 --- a/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigTest.java +++ b/spring-ai-modules/spring-ai-mcp/mcp-spring/oauth2-authorization-server/src/test/java/com/baeldung/mcp/oauth2authorizationserver/config/AuthorizationServerConfigUnitTest.java @@ -13,7 +13,7 @@ import org.springframework.security.web.SecurityFilterChain; @SpringBootTest(classes = AuthorizationServerConfig.class) -class AuthorizationServerConfigTest { +class AuthorizationServerConfigUnitTest { private final ApplicationContext context; @@ -21,7 +21,7 @@ class AuthorizationServerConfigTest { private final AuthorizationServerSettings authorizationServerSettings; - public AuthorizationServerConfigTest(ApplicationContext context, RegisteredClientRepository registeredClientRepository, + public AuthorizationServerConfigUnitTest(ApplicationContext context, RegisteredClientRepository registeredClientRepository, AuthorizationServerSettings authorizationServerSettings) { this.authorizationServerSettings = authorizationServerSettings; this.registeredClientRepository = registeredClientRepository;