conformance-tests: upgrade to MCP-security 0.1.11, implement CIMD
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
parent
d81430a532
commit
fc330a5885
3 changed files with 94 additions and 67 deletions
|
|
@ -8,17 +8,23 @@ import java.util.Optional;
|
|||
|
||||
import io.modelcontextprotocol.conformance.client.scenario.Scenario;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.metadata.McpMetadataDiscoveryService;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.DefaultMcpOAuth2ClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.DefaultMcpOAuth2DcrClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.DynamicClientRegistrationService;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.InMemoryMcpClientRegistrationRepository;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpClientRegistrationRepository;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpOAuth2ClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpOAuth2DcrClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.cimd.DefaultMcpOAuth2CimdClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.cimd.McpOAuth2CimdClientManager;
|
||||
import org.springaicommunity.mcp.security.common.url.DefaultUrlValidator;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
|
||||
/**
|
||||
* MCP Conformance Test Client - Spring HTTP Client Implementation.
|
||||
|
|
@ -42,13 +48,15 @@ public class ConformanceSpringClientApplication {
|
|||
|
||||
public static final String REGISTRATION_ID = "default_registration";
|
||||
|
||||
private final DefaultUrlValidator URL_VALIDATOR = new DefaultUrlValidator(true);
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConformanceSpringClientApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
McpMetadataDiscoveryService discovery() {
|
||||
return new McpMetadataDiscoveryService();
|
||||
return new McpMetadataDiscoveryService(URL_VALIDATOR);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -57,10 +65,24 @@ public class ConformanceSpringClientApplication {
|
|||
}
|
||||
|
||||
@Bean
|
||||
McpOAuth2ClientManager mcpOAuth2ClientManager(McpClientRegistrationRepository mcpClientRegistrationRepository,
|
||||
McpOAuth2DcrClientManager mcpOAuth2ClientManager(McpClientRegistrationRepository mcpClientRegistrationRepository,
|
||||
McpMetadataDiscoveryService mcpMetadataDiscoveryService) {
|
||||
return new DefaultMcpOAuth2ClientManager(mcpClientRegistrationRepository,
|
||||
new DynamicClientRegistrationService(), mcpMetadataDiscoveryService);
|
||||
return new DefaultMcpOAuth2DcrClientManager(mcpClientRegistrationRepository,
|
||||
new DynamicClientRegistrationService(URL_VALIDATOR), mcpMetadataDiscoveryService, URL_VALIDATOR);
|
||||
}
|
||||
|
||||
@Bean
|
||||
McpOAuth2CimdClientManager mcpOAuth2CimdClientManager(McpMetadataDiscoveryService mcpMetadataDiscoveryService,
|
||||
McpClientRegistrationRepository mcpClientRegistrationRepository) {
|
||||
return new DefaultMcpOAuth2CimdClientManager(mcpMetadataDiscoveryService, mcpClientRegistrationRepository,
|
||||
URL_VALIDATOR);
|
||||
}
|
||||
|
||||
@Bean
|
||||
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager(
|
||||
OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository,
|
||||
McpClientRegistrationRepository clientRegistrationRepository) {
|
||||
return new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
|||
|
|
@ -4,38 +4,65 @@
|
|||
|
||||
package io.modelcontextprotocol.conformance.client.configuration;
|
||||
|
||||
import io.modelcontextprotocol.conformance.client.ConformanceSpringClientApplication;
|
||||
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
|
||||
import io.modelcontextprotocol.conformance.client.scenario.DefaultScenario;
|
||||
import org.springaicommunity.mcp.security.client.sync.config.McpClientOAuth2Configurer;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.http.client.OAuth2CimdHttpClientTransportCustomizer;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.http.client.OAuth2DcrHttpClientTransportCustomizer;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpClientRegistrationRepository;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpOAuth2ClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpOAuth2DcrClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.cimd.DefaultMcpOAuth2CimdClientManager;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.cimd.McpOAuth2CimdClientManager;
|
||||
|
||||
import org.springframework.ai.mcp.customizer.McpClientCustomizer;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnExpression("#{environment['MCP_CONFORMANCE_SCENARIO'] != 'auth/pre-registration'}")
|
||||
public class DefaultConfiguration {
|
||||
|
||||
private final String TEST_CLIENT_ID_URL = "https://conformance-test.local/client-metadata.json";
|
||||
|
||||
@Bean
|
||||
DefaultScenario defaultScenario(McpClientRegistrationRepository clientRegistrationRepository,
|
||||
ServletWebServerApplicationContext serverCtx,
|
||||
OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository,
|
||||
McpOAuth2ClientManager mcpOAuth2ClientManager) {
|
||||
return new DefaultScenario(clientRegistrationRepository, serverCtx, oAuth2AuthorizedClientRepository,
|
||||
mcpOAuth2ClientManager);
|
||||
DefaultScenario defaultScenario(ServletWebServerApplicationContext serverCtx,
|
||||
McpClientCustomizer<HttpClientStreamableHttpTransport.Builder> transportCustomizer) {
|
||||
return new DefaultScenario(serverCtx, transportCustomizer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http, ConformanceSpringClientApplication.ServerUrl serverUrl) {
|
||||
McpClientCustomizer<HttpClientStreamableHttpTransport.Builder> transportCustomizer(
|
||||
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager,
|
||||
McpClientRegistrationRepository clientRegistrationRepository,
|
||||
McpOAuth2DcrClientManager mcpOAuth2ClientManager, McpOAuth2CimdClientManager mcpOAuth2CimdClientManager,
|
||||
@Value("${mcp.conformance.scenario}") String scenario) {
|
||||
if (scenario.equals("auth/basic-cimd")) {
|
||||
if (mcpOAuth2CimdClientManager instanceof DefaultMcpOAuth2CimdClientManager mgr) {
|
||||
// Hardcode the client_id
|
||||
mgr.setClientRegistrationCustomizer(
|
||||
cr -> ClientRegistration.withClientRegistration(cr).clientId(TEST_CLIENT_ID_URL).build());
|
||||
}
|
||||
return new OAuth2CimdHttpClientTransportCustomizer(oAuth2AuthorizedClientManager,
|
||||
clientRegistrationRepository, mcpOAuth2CimdClientManager);
|
||||
|
||||
}
|
||||
else {
|
||||
return new OAuth2DcrHttpClientTransportCustomizer(oAuth2AuthorizedClientManager,
|
||||
clientRegistrationRepository, mcpOAuth2ClientManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) {
|
||||
return http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll())
|
||||
.with(new McpClientOAuth2Configurer(), Customizer.withDefaults())
|
||||
.with(new McpClientOAuth2Configurer(), mcp -> mcp.cimd(true))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,10 @@ import org.jspecify.annotations.NonNull;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springaicommunity.mcp.security.client.sync.AuthenticationMcpTransportContextProvider;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.http.client.OAuth2HttpClientTransportCustomizer;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpClientRegistrationRepository;
|
||||
import org.springaicommunity.mcp.security.client.sync.oauth2.registration.McpOAuth2ClientManager;
|
||||
|
||||
import org.springframework.ai.mcp.customizer.McpClientCustomizer;
|
||||
import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
|
|
@ -34,23 +30,14 @@ public class DefaultScenario implements Scenario {
|
|||
|
||||
private final ServletWebServerApplicationContext serverCtx;
|
||||
|
||||
private final DefaultOAuth2AuthorizedClientManager authorizedClientManager;
|
||||
|
||||
private final McpClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
private final McpOAuth2ClientManager mcpOAuth2ClientManager;
|
||||
private final McpClientCustomizer<HttpClientStreamableHttpTransport.Builder> transportCustomizer;
|
||||
|
||||
private McpSyncClient client;
|
||||
|
||||
public DefaultScenario(McpClientRegistrationRepository clientRegistrationRepository,
|
||||
ServletWebServerApplicationContext serverCtx,
|
||||
OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository,
|
||||
McpOAuth2ClientManager mcpOAuth2ClientManager) {
|
||||
public DefaultScenario(ServletWebServerApplicationContext serverCtx,
|
||||
McpClientCustomizer<HttpClientStreamableHttpTransport.Builder> transportCustomizer) {
|
||||
this.serverCtx = serverCtx;
|
||||
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||
this.mcpOAuth2ClientManager = mcpOAuth2ClientManager;
|
||||
this.authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository,
|
||||
oAuth2AuthorizedClientRepository);
|
||||
this.transportCustomizer = transportCustomizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -59,12 +46,10 @@ public class DefaultScenario implements Scenario {
|
|||
var testServerUrl = "http://localhost:" + serverCtx.getWebServer().getPort();
|
||||
var testClient = buildTestClient(testServerUrl);
|
||||
|
||||
var customizer = new OAuth2HttpClientTransportCustomizer(authorizedClientManager, clientRegistrationRepository,
|
||||
mcpOAuth2ClientManager);
|
||||
var baseUri = UriComponentsBuilder.fromUriString(serverUrl).replacePath(null).toUriString();
|
||||
var path = UriComponentsBuilder.fromUriString(serverUrl).build().getPath();
|
||||
var transportBuilder = HttpClientStreamableHttpTransport.builder(baseUri).endpoint(path);
|
||||
customizer.customize("default-transport", transportBuilder);
|
||||
transportCustomizer.customize("default-transport", transportBuilder);
|
||||
HttpClientStreamableHttpTransport transport = transportBuilder.build();
|
||||
|
||||
this.client = McpClient.sync(transport)
|
||||
|
|
|
|||
Loading…
Reference in a new issue