@@ -38,6 +38,14 @@ To configure, import the dependency in your project.
3838
3939// TODO: add import instructions for both maven and gradle
4040
41+ Ensure that MCP server is enabled in your ` application.properties ` :
42+
43+ ``` properties
44+ spring.ai.mcp.server.name =my-cool-mcp-server
45+ # Supported protocols: STREAMABLE, STATELESS
46+ spring.ai.mcp.server.protocol =STREAMABLE
47+ ```
48+
4149Then, configure the security for your project in the usual Spring-Security way, adding the provided configurer.
4250Create a configuration class, and reference the authorization server's URI.
4351In this example, we have set the authz server's issuer URI in the well known Spring property
@@ -148,8 +156,8 @@ public String greet(ToolContext toolContext) {
148156
149157- The deprecated SSE transport is not supported.
150158 Use [ Streamable HTTP] ( https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http )
151- or [ stateless] ( https://modelcontextprotocol.io/sdk/java/mcp-server#stateless-streamable-http-webmvc ) . (the link for
152- stateless does not work out of the box, reload the page if required)
159+ or [ stateless transport ] ( https://modelcontextprotocol.io/sdk/java/mcp-server#stateless-streamable-http-webmvc ) . (the
160+ link for stateless does not work out of the box, reload the page if required)
153161- WebFlux-based servers are not supported.
154162- Opaque tokens are not supported. Please use JWT.
155163
@@ -182,37 +190,116 @@ For our MCP clients, there are three flows available for obtaining tokens:
182190
183191🤔 Which flow should I use?
184192
193+ - If there are user-level permission, AND you know every MCP request will be made within the context of a user request
194+ (such as: adding tools manually in the GUI), then use the ` authorization_code ` flow, with either
195+ ` OAuth2AuthorizationCodeSyncHttpRequestCustomizer ` or ` McpOAuth2AuthorizationCodeExchangeFilterFunction ` .
185196- If there are no user-level permissions, and you want to secure "client-to-server" communication with an access token,
186197 use the ` client_credentials ` flow, either with ` OAuth2ClientCredentialsSyncHttpRequestCustomizer ` or
187198 ` McpOAuth2ClientCredentialsExchangeFilterFunction ` .
188199- If there are user-level permission, AND you configure your MCP clients using Spring Boot properties (such as
189200 ` spring.ai.mcp.client.streamable-http.connections.<server-name>.url=<server-url> ` ), then, on application startup,
190201 Spring AI will try to list the tools. And startup happens without a user present. In that specific case, use a hybrid
191202 flow, with either ` OAuth2HybridSyncHttpRequestCustomizer ` or ` McpOAuth2HybridExchangeFilterFunction ` .
192- - If there are user-level permission, AND you know every MCP request will be made within the context of a user request
193- (such as: adding tools manually in the GUI), then use the ` authorization_code ` flow, with either
194- ` OAuth2AuthorizationCodeSyncHttpRequestCustomizer ` or ` McpOAuth2AuthorizationCodeExchangeFilterFunction ` .
195- - If, in your server, only the tool calls are secured, and all tool calls
196203
197204### Setup for all use-cases
198205
199206In very case, you need to activate Spring Security's OAuth2 client support.
207+ Add the following properties to your ` application.properties ` file.
208+ Depending on the flow you chose (see above), you may need one or both client registrations:
200209
201210``` properties
202- # TODO
211+ # Ensure MCP clients are sync
212+ spring.ai.mcp.client.type =SYNC
213+ #
214+ #
215+ # For obtaining tokens for calling the tool
216+ # When using the hybrid flow or authorization_code flow, this registers a client
217+ # called "authserver". If using client_credentials, do not include this:
218+ spring.security.oauth2.client.registration.authserver.client-id =<THE CLIENT ID>
219+ spring.security.oauth2.client.registration.authserver.client-secret =<THE CLIENT SECRET>
220+ spring.security.oauth2.client.registration.authserver.authorization-grant-type =authorization_code
221+ spring.security.oauth2.client.registration.authserver.provider =authserver
222+ #
223+ # When using the hybrid flow or client_credentials flow, this registers a client
224+ # called "authserver-client-credentials". If using authorization_code, do not include this:
225+ spring.security.oauth2.client.registration.authserver-client-credentials.client-id =<THE CLIENT ID>
226+ spring.security.oauth2.client.registration.authserver-client-credentials.client-secret =<THE CLIENT SECRET>
227+ spring.security.oauth2.client.registration.authserver-client-credentials.authorization-grant-type =client_credentials
228+ spring.security.oauth2.client.registration.authserver-client-credentials.provider =authserver
229+ #
230+ # Both clients above rely on the authorization server, specified by its issuer URI:
231+ spring.security.oauth2.client.provider.authserver.issuer-uri =<THE ISSUER URI OF YOUR AUTH SERVER>
203232```
204233
234+ Then, create a configuration class, activating the OAuth2 client capabilities with a ` SecurityFilterChain ` .
235+
236+ ``` java
237+
238+ @Configuration
239+ @EnableWebSecurity
240+ class SecurityConfiguration {
241+
242+ @Bean
243+ SecurityFilterChain securityFilterChain (HttpSecurity http ) throws Exception {
244+ return http
245+ // in this example, the client app has no security on its endpoints
246+ .authorizeHttpRequests(auth - > auth. anyRequest(). permitAll())
247+ // turn on OAuth2 support
248+ .oauth2Client(Customizer . withDefaults())
249+ .build();
250+ }
251+
252+ }
253+ ```
254+
255+ If you already have a filter chain configured, ensure that ` .oauth2Client(...) ` is on.
256+
205257### Use with ` spring-ai-starter-mcp-client `
206258
207- Then, configure the security for your project in the usual Spring-Security way, adding the provided configurer.
208- Create a configuration class, and reference the authorization server's URI.
209- In this example, we have set the authz server's issuer URI in the well known Spring property
210- ` spring.security.oauth2.resourceserver.jwt.issuer-uri ` . This property is not a requirement.
259+ When using ` spring-ai-starter-mcp-client ` , the underlying MCP client transport will be based on the Java SDK's
260+ ` HttpClient ` .
261+ In that case, you can expose a bean of type ` McpSyncHttpClientRequestCustomizer ` .
262+ Depending on your [ authorization flow] ( #authorization-flows ) of choice, you may use one of the following
263+ implementations:
264+
265+ - ` OAuth2AuthorizationCodeSyncHttpRequestCustomizer ` (preferred)
266+ - ` OAuth2ClientCredentialsSyncHttpRequestCustomizer ` (machine-to-machine)
267+ - ` OAuth2HybridSyncHttpRequestCustomizer ` (last resort)
268+
269+ All these request customizers rely on request and authentication data.
270+ That data is passed through
271+ ` McpTransportContext ` ([ MCP docs] ( https://modelcontextprotocol.io/sdk/java/mcp-client#adding-context-information ) ).
272+ To make that information available, you also need to add an ` AuthenticationMcpTransportContextProvider ` to your MCP Sync
273+ Client.
274+ Tying it all together:
275+
276+ ``` java
277+
278+ @Configuration
279+ class McpConfiguration {
280+
281+ @Bean
282+ McpSyncClientCustomizer syncClientCustomizer () {
283+ return (name, syncSpec) - > syncSpec. transportContextProvider(new AuthenticationMcpTransportContextProvider ());
284+ }
285+
286+ @Bean
287+ McpSyncHttpClientRequestCustomizer requestCustomizer (
288+ OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager ,
289+ ClientRegistrationRepository clientRegistrationRepository ) {
290+ // The clientRegistration name, "authserver", must match the name in application.properties
291+ return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer (oAuth2AuthorizedClientManager, " authserver" );
292+ }
293+
294+ }
295+ ```
211296
212297### Known limitations
213298
214299TODO
215300
301+ - Mention Tool confusion on initialization
302+
216303## Authorization Server
217304
218305TODO
0 commit comments