Loading pom.xml +14 −1 Changes for pom.xml: 14 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client-webflux</artifactId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> Loading @@ -50,6 +50,15 @@ <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- security --> <dependency> <groupId>org.springaicommunity</groupId> <artifactId>mcp-client-security</artifactId> <version>0.0.4</version> </dependency> </dependencies> <dependencyManagement> <dependencies> Loading @@ -60,6 +69,10 @@ <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> Loading src/main/java/org/etsi/osl/mcp/backend/McpConfiguration.java 0 → 100644 +50 −0 Changes for src/main/java/org/etsi/osl/mcp/backend/McpConfiguration.java: 50 added lines, 0 removed lines. Original line number Diff line number Diff line package org.etsi.osl.mcp.backend; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; import org.springaicommunity.mcp.security.client.sync.AuthenticationMcpTransportContextProvider; import org.springaicommunity.mcp.security.client.sync.oauth2.http.client.OAuth2AuthorizationCodeSyncHttpRequestCustomizer; import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; /** * @author Daniel Garnier-Moiroux */ @Configuration class McpConfiguration { @Bean McpSyncHttpClientRequestCustomizer requestCustomizer(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager, ClientRegistrationRepository clientRegistrationRepository) { var registrationId = findUniqueClientRegistration(clientRegistrationRepository); return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(oAuth2AuthorizedClientManager, registrationId); } @Bean McpSyncClientCustomizer syncClientCustomizer() { return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider()); } /** * Returns the ID of the {@code spring.security.oauth2.client.registration}, if * unique. */ private static String findUniqueClientRegistration(ClientRegistrationRepository clientRegistrationRepository) { String registrationId; if (!(clientRegistrationRepository instanceof InMemoryClientRegistrationRepository repo)) { throw new IllegalStateException("Expected an InMemoryClientRegistrationRepository"); } var iterator = repo.iterator(); var firstRegistration = iterator.next(); if (iterator.hasNext()) { throw new IllegalStateException("Expected a single Client Registration"); } registrationId = firstRegistration.getRegistrationId(); return registrationId; } } No newline at end of file src/main/java/org/etsi/osl/mcp/backend/WebSecurityConfigKeycloak.java 0 → 100644 +25 −0 Changes for src/main/java/org/etsi/osl/mcp/backend/WebSecurityConfigKeycloak.java: 25 added lines, 0 removed lines. Original line number Diff line number Diff line package org.etsi.osl.mcp.backend; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; 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.config.annotation.web.configurers.CsrfConfigurer; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity @Profile("!testing") public class WebSecurityConfigKeycloak { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) .oauth2Client(Customizer.withDefaults()) .csrf(CsrfConfigurer::disable) .build(); } } src/main/resources/application.yaml +14 −1 Changes for src/main/resources/application.yaml: 14 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -10,7 +10,8 @@ spring: base-url: http://localhost:11434 chat: options: model: gpt-oss:20b xmodel: gpt-oss:20b model: qwen3:8b temperature: 0.7 mcp: client: Loading @@ -19,6 +20,18 @@ spring: connections: openslice-server: url: http://localhost:13015 security: oauth2: client: registration: authserver: client-id: osapiWebClientId client-secret: secret authorization-grant-type: authorization_code provider: authserver provider: authserver: issuer-uri: http://keycloak:8080/auth/realms/openslice logging: level: Loading Loading
pom.xml +14 −1 Changes for pom.xml: 14 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client-webflux</artifactId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> Loading @@ -50,6 +50,15 @@ <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- security --> <dependency> <groupId>org.springaicommunity</groupId> <artifactId>mcp-client-security</artifactId> <version>0.0.4</version> </dependency> </dependencies> <dependencyManagement> <dependencies> Loading @@ -60,6 +69,10 @@ <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> Loading
src/main/java/org/etsi/osl/mcp/backend/McpConfiguration.java 0 → 100644 +50 −0 Changes for src/main/java/org/etsi/osl/mcp/backend/McpConfiguration.java: 50 added lines, 0 removed lines. Original line number Diff line number Diff line package org.etsi.osl.mcp.backend; import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer; import org.springaicommunity.mcp.security.client.sync.AuthenticationMcpTransportContextProvider; import org.springaicommunity.mcp.security.client.sync.oauth2.http.client.OAuth2AuthorizationCodeSyncHttpRequestCustomizer; import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; /** * @author Daniel Garnier-Moiroux */ @Configuration class McpConfiguration { @Bean McpSyncHttpClientRequestCustomizer requestCustomizer(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager, ClientRegistrationRepository clientRegistrationRepository) { var registrationId = findUniqueClientRegistration(clientRegistrationRepository); return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(oAuth2AuthorizedClientManager, registrationId); } @Bean McpSyncClientCustomizer syncClientCustomizer() { return (name, syncSpec) -> syncSpec.transportContextProvider(new AuthenticationMcpTransportContextProvider()); } /** * Returns the ID of the {@code spring.security.oauth2.client.registration}, if * unique. */ private static String findUniqueClientRegistration(ClientRegistrationRepository clientRegistrationRepository) { String registrationId; if (!(clientRegistrationRepository instanceof InMemoryClientRegistrationRepository repo)) { throw new IllegalStateException("Expected an InMemoryClientRegistrationRepository"); } var iterator = repo.iterator(); var firstRegistration = iterator.next(); if (iterator.hasNext()) { throw new IllegalStateException("Expected a single Client Registration"); } registrationId = firstRegistration.getRegistrationId(); return registrationId; } } No newline at end of file
src/main/java/org/etsi/osl/mcp/backend/WebSecurityConfigKeycloak.java 0 → 100644 +25 −0 Changes for src/main/java/org/etsi/osl/mcp/backend/WebSecurityConfigKeycloak.java: 25 added lines, 0 removed lines. Original line number Diff line number Diff line package org.etsi.osl.mcp.backend; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; 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.config.annotation.web.configurers.CsrfConfigurer; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity @Profile("!testing") public class WebSecurityConfigKeycloak { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) .oauth2Client(Customizer.withDefaults()) .csrf(CsrfConfigurer::disable) .build(); } }
src/main/resources/application.yaml +14 −1 Changes for src/main/resources/application.yaml: 14 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -10,7 +10,8 @@ spring: base-url: http://localhost:11434 chat: options: model: gpt-oss:20b xmodel: gpt-oss:20b model: qwen3:8b temperature: 0.7 mcp: client: Loading @@ -19,6 +20,18 @@ spring: connections: openslice-server: url: http://localhost:13015 security: oauth2: client: registration: authserver: client-id: osapiWebClientId client-secret: secret authorization-grant-type: authorization_code provider: authserver provider: authserver: issuer-uri: http://keycloak:8080/auth/realms/openslice logging: level: Loading