Skip to content

Commit fefaede

Browse files
committed
Fix formatting. Remove security docs
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
1 parent 664f86b commit fefaede

File tree

3 files changed

+1
-333
lines changed

3 files changed

+1
-333
lines changed

docs/client.md

Lines changed: 0 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -437,160 +437,3 @@ The prompt system enables interaction with server-side prompt templates. These t
437437
client.getPrompt(new GetPromptRequest("greeting", Map.of("name", "World")))
438438
.subscribe();
439439
```
440-
441-
## MCP Security
442-
443-
The [MCP Security](https://github.com/spring-ai-community/mcp-security) community library provides OAuth 2.0 authorization support for Spring AI MCP clients. It supports both `HttpClient`-based and `WebClient`-based (WebFlux) MCP clients used with the [Spring AI MCP Client Boot Starter](https://docs.spring.io/spring-ai/reference/2.0-SNAPSHOT/api/mcp/mcp-client-boot-starter-docs.html).
444-
445-
!!! note
446-
This is a community project (`org.springaicommunity`), not officially part of the MCP Java SDK. It requires Spring AI 2.0.x (`mcp-client-security` version 0.1.x). For Spring AI 1.1.x, use version 0.0.6.
447-
448-
### Add Dependency
449-
450-
=== "Maven"
451-
452-
```xml
453-
<dependency>
454-
<groupId>org.springaicommunity</groupId>
455-
<artifactId>mcp-client-security</artifactId>
456-
<version>0.1.1</version>
457-
</dependency>
458-
```
459-
460-
=== "Gradle"
461-
462-
```groovy
463-
implementation("org.springaicommunity:mcp-client-security:0.1.1")
464-
```
465-
466-
### Authorization Flows
467-
468-
Three OAuth 2.0 flows are available:
469-
470-
- **Authorization Code** — For user-present scenarios. The client sends requests with a bearer token on behalf of the user. Use `OAuth2AuthorizationCodeSyncHttpRequestCustomizer` (HttpClient) or `McpOAuth2AuthorizationCodeExchangeFilterFunction` (WebClient).
471-
- **Client Credentials** — For machine-to-machine communication without a user in the loop. Use `OAuth2ClientCredentialsSyncHttpRequestCustomizer` (HttpClient) or `McpOAuth2ClientCredentialsExchangeFilterFunction` (WebClient).
472-
- **Hybrid** — For mixed scenarios where some calls (e.g., `tools/list` on startup) use client credentials, while user-specific calls (e.g., `tools/call`) use authorization code tokens. Use `OAuth2HybridSyncHttpRequestCustomizer` (HttpClient) or `McpOAuth2HybridExchangeFilterFunction` (WebClient).
473-
474-
!!! tip
475-
Use the **Hybrid** flow when Spring AI's autoconfiguration initializes MCP clients on startup (listing tools before a user is present), but tool calls are user-authenticated.
476-
477-
### Setup
478-
479-
Add the following to `application.properties`:
480-
481-
```properties
482-
# Ensure MCP clients are sync
483-
spring.ai.mcp.client.type=SYNC
484-
# Disable auto-initialization (most MCP servers require authentication on every request)
485-
spring.ai.mcp.client.initialized=false
486-
487-
# Authorization Code client registration (for user-present flows)
488-
spring.security.oauth2.client.registration.authserver.client-id=<CLIENT_ID>
489-
spring.security.oauth2.client.registration.authserver.client-secret=<CLIENT_SECRET>
490-
spring.security.oauth2.client.registration.authserver.authorization-grant-type=authorization_code
491-
spring.security.oauth2.client.registration.authserver.provider=authserver
492-
493-
# Client Credentials registration (for machine-to-machine or hybrid flows)
494-
spring.security.oauth2.client.registration.authserver-client-credentials.client-id=<CLIENT_ID>
495-
spring.security.oauth2.client.registration.authserver-client-credentials.client-secret=<CLIENT_SECRET>
496-
spring.security.oauth2.client.registration.authserver-client-credentials.authorization-grant-type=client_credentials
497-
spring.security.oauth2.client.registration.authserver-client-credentials.provider=authserver
498-
499-
# Authorization server issuer URI
500-
spring.security.oauth2.client.provider.authserver.issuer-uri=<ISSUER_URI>
501-
```
502-
503-
Then activate OAuth2 client support in a security configuration class:
504-
505-
```java
506-
@Configuration
507-
@EnableWebSecurity
508-
class SecurityConfiguration {
509-
510-
@Bean
511-
SecurityFilterChain securityFilterChain(HttpSecurity http) {
512-
return http
513-
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
514-
.oauth2Client(Customizer.withDefaults())
515-
.build();
516-
}
517-
}
518-
```
519-
520-
### HttpClient-Based Client
521-
522-
When using `spring-ai-starter-mcp-client`, the transport is backed by the JDK's `HttpClient`. Expose a `McpSyncHttpClientRequestCustomizer` bean and an `AuthenticationMcpTransportContextProvider` on the client:
523-
524-
```java
525-
@Configuration
526-
class McpConfiguration {
527-
528-
@Bean
529-
McpSyncClientCustomizer syncClientCustomizer() {
530-
return (name, syncSpec) ->
531-
syncSpec.transportContextProvider(
532-
new AuthenticationMcpTransportContextProvider()
533-
);
534-
}
535-
536-
@Bean
537-
McpSyncHttpClientRequestCustomizer requestCustomizer(
538-
OAuth2AuthorizedClientManager clientManager
539-
) {
540-
// "authserver" must match the registration name in application.properties
541-
return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(
542-
clientManager,
543-
"authserver"
544-
);
545-
}
546-
}
547-
```
548-
549-
Replace `OAuth2AuthorizationCodeSyncHttpRequestCustomizer` with `OAuth2ClientCredentialsSyncHttpRequestCustomizer` or `OAuth2HybridSyncHttpRequestCustomizer` for the corresponding flow.
550-
551-
### WebClient-Based Client
552-
553-
When using `spring-ai-starter-mcp-client-webflux`, the transport is backed by Spring's reactive `WebClient`. Expose a `WebClient.Builder` bean configured with an `ExchangeFilterFunction`:
554-
555-
```java
556-
@Configuration
557-
class McpConfiguration {
558-
559-
@Bean
560-
McpSyncClientCustomizer syncClientCustomizer() {
561-
return (name, syncSpec) ->
562-
syncSpec.transportContextProvider(
563-
new AuthenticationMcpTransportContextProvider()
564-
);
565-
}
566-
567-
@Bean
568-
WebClient.Builder mcpWebClientBuilder(OAuth2AuthorizedClientManager clientManager) {
569-
// "authserver" must match the registration name in application.properties
570-
return WebClient.builder().filter(
571-
new McpOAuth2AuthorizationCodeExchangeFilterFunction(
572-
clientManager,
573-
"authserver"
574-
)
575-
);
576-
}
577-
}
578-
```
579-
580-
Replace `McpOAuth2AuthorizationCodeExchangeFilterFunction` with `McpOAuth2ClientCredentialsExchangeFilterFunction` or `McpOAuth2HybridExchangeFilterFunction` for the corresponding flow.
581-
582-
When using the chat client's `.stream()` method, Reactor does not preserve thread-locals. Inject the authentication into the Reactor context manually:
583-
584-
```java
585-
chatClient
586-
.prompt("<your prompt>")
587-
.stream()
588-
.content()
589-
.contextWrite(AuthenticationMcpTransportContextProvider.writeToReactorContext());
590-
```
591-
592-
### Known Limitations
593-
594-
- Only `McpSyncClient` is supported; async clients are not.
595-
- Set `spring.ai.mcp.client.initialized=false` when servers require authentication on every request, as Spring AI autoconfiguration will otherwise attempt to list tools on startup without a token.
596-
- The SSE transport is supported by the client module (unlike the server module).

docs/server.md

Lines changed: 0 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -756,182 +756,6 @@ mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO);
756756
Clients can control the minimum logging level they receive through the `mcpClient.setLoggingLevel(level)` request. Messages below the set level will be filtered out.
757757
Supported logging levels (in order of increasing severity): DEBUG (0), INFO (1), NOTICE (2), WARNING (3), ERROR (4), CRITICAL (5), ALERT (6), EMERGENCY (7)
758758

759-
## MCP Security
760-
761-
The [MCP Security](https://github.com/spring-ai-community/mcp-security) community library provides OAuth 2.0 resource server support and API key authentication for Spring AI MCP servers.
762-
763-
!!! note
764-
This is a community project (`org.springaicommunity`), not officially part of the MCP Java SDK. It requires Spring AI 2.0.x (`mcp-server-security` version 0.1.x) and is compatible with **Spring WebMVC-based servers only**. For Spring AI 1.1.x, use version 0.0.6.
765-
766-
### Add Dependency
767-
768-
=== "Maven"
769-
770-
```xml
771-
<dependencies>
772-
<dependency>
773-
<groupId>org.springaicommunity</groupId>
774-
<artifactId>mcp-server-security</artifactId>
775-
<version>0.1.1</version>
776-
</dependency>
777-
<dependency>
778-
<groupId>org.springframework.boot</groupId>
779-
<artifactId>spring-boot-starter-security</artifactId>
780-
</dependency>
781-
<!-- Optional: required for OAuth2 resource server support -->
782-
<dependency>
783-
<groupId>org.springframework.boot</groupId>
784-
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
785-
</dependency>
786-
</dependencies>
787-
```
788-
789-
=== "Gradle"
790-
791-
```groovy
792-
implementation("org.springaicommunity:mcp-server-security:0.1.1")
793-
implementation("org.springframework.boot:spring-boot-starter-security")
794-
// Optional: required for OAuth2 resource server support
795-
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
796-
```
797-
798-
### OAuth2 Authentication
799-
800-
Enable a Streamable HTTP or Stateless MCP server in `application.properties`:
801-
802-
```properties
803-
spring.ai.mcp.server.name=my-mcp-server
804-
# Supported protocols: STREAMABLE, STATELESS
805-
spring.ai.mcp.server.protocol=STREAMABLE
806-
```
807-
808-
Then configure Spring Security with the `McpServerOAuth2Configurer` to require authentication on every request:
809-
810-
```java
811-
@Configuration
812-
@EnableWebSecurity
813-
class McpServerConfiguration {
814-
815-
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
816-
private String issuerUrl;
817-
818-
@Bean
819-
SecurityFilterChain securityFilterChain(HttpSecurity http) {
820-
return http
821-
// Require authentication on every request
822-
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
823-
.with(
824-
McpServerOAuth2Configurer.mcpServerOAuth2(),
825-
mcpAuthorization -> {
826-
// Required: the issuer URI of the authorization server
827-
mcpAuthorization.authorizationServer(issuerUrl);
828-
// Optional: validate the `aud` claim (RFC 8707 Resource Indicators)
829-
// Not all authorization servers include this claim. Defaults to false.
830-
mcpAuthorization.validateAudienceClaim(true);
831-
}
832-
)
833-
.build();
834-
}
835-
}
836-
```
837-
838-
### Fine-Grained: Secure Tool Calls Only
839-
840-
To allow `initialize` and `tools/list` publicly while requiring authentication for `tools/call`, enable method security and open the `/mcp` endpoint:
841-
842-
```java
843-
@Configuration
844-
@EnableWebSecurity
845-
@EnableMethodSecurity // enables @PreAuthorize on individual methods
846-
class McpServerConfiguration {
847-
848-
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
849-
private String issuerUrl;
850-
851-
@Bean
852-
SecurityFilterChain securityFilterChain(HttpSecurity http) {
853-
return http
854-
.authorizeHttpRequests(auth -> {
855-
auth.requestMatchers("/mcp").permitAll();
856-
auth.anyRequest().authenticated();
857-
})
858-
.with(
859-
McpServerOAuth2Configurer.mcpServerOAuth2(),
860-
mcpAuthorization -> mcpAuthorization.authorizationServer(issuerUrl)
861-
)
862-
.build();
863-
}
864-
}
865-
```
866-
867-
Then annotate individual tool methods with `@PreAuthorize`. The current authentication is also accessible via `SecurityContextHolder`:
868-
869-
```java
870-
@Service
871-
public class MyToolsService {
872-
873-
@PreAuthorize("isAuthenticated()")
874-
@McpTool(name = "greeter", description = "A tool that greets the authenticated user")
875-
public String greet(
876-
@McpToolParam(description = "The language for the greeting") String language
877-
) {
878-
var authentication = SecurityContextHolder.getContext().getAuthentication();
879-
return switch (language.toLowerCase()) {
880-
case "english" -> "Hello, " + authentication.getName() + "!";
881-
case "french" -> "Salut " + authentication.getName() + "!";
882-
default -> "Hello " + authentication.getName() + "!";
883-
};
884-
}
885-
}
886-
```
887-
888-
### API Key Authentication
889-
890-
For API key-based authentication, provide an `ApiKeyEntityRepository` implementation and configure the `mcpServerApiKey()` configurer:
891-
892-
```java
893-
@Configuration
894-
@EnableWebSecurity
895-
class McpServerConfiguration {
896-
897-
@Bean
898-
SecurityFilterChain securityFilterChain(HttpSecurity http) {
899-
return http
900-
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
901-
.with(
902-
mcpServerApiKey(),
903-
apiKey -> {
904-
// Required: repository of valid API keys
905-
apiKey.apiKeyRepository(apiKeyRepository());
906-
// Optional: customize the header name (default: X-API-Key)
907-
// apiKey.headerName("CUSTOM-API-KEY");
908-
}
909-
)
910-
.build();
911-
}
912-
913-
private ApiKeyEntityRepository<ApiKeyEntityImpl> apiKeyRepository() {
914-
var apiKey = ApiKeyEntityImpl.builder()
915-
.name("my api key")
916-
.id("api01")
917-
.secret("mycustomapikey")
918-
.build();
919-
return new InMemoryApiKeyEntityRepository<>(List.of(apiKey));
920-
}
921-
}
922-
```
923-
924-
Clients send the key using the `X-API-Key: api01.mycustomapikey` header format (`<id>.<secret>`).
925-
926-
!!! warning
927-
`InMemoryApiKeyEntityRepository` uses bcrypt for key hashing and is not suited for high-traffic production use. Provide a custom `ApiKeyEntityRepository` implementation for production deployments.
928-
929-
### Known Limitations
930-
931-
- The deprecated SSE transport is not supported. Use Streamable HTTP or Stateless transport.
932-
- Spring WebFlux-based servers are not supported — WebMVC only.
933-
- Opaque tokens are not supported. JWT is required for OAuth2.
934-
935759
## Error Handling
936760

937761
The SDK provides comprehensive error handling through the McpError class, covering protocol compatibility, transport communication, JSON-RPC messaging, tool execution, resource management, prompt handling, timeouts, and connection issues. This unified error handling approach ensures consistent and reliable error management across both synchronous and asynchronous operations.

mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientResiliencyTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public abstract class AbstractMcpAsyncClientResiliencyTests {
4545
private static final Logger logger = LoggerFactory.getLogger(AbstractMcpAsyncClientResiliencyTests.class);
4646

4747
static Network network = Network.newNetwork();
48+
4849
public static String host = "http://localhost:3001";
4950

5051
@SuppressWarnings("resource")

0 commit comments

Comments
 (0)