MCP セキュリティ
| これはまだ開発中の作業です。ドキュメントと API は今後のリリースで変更される可能性があります。 |
Spring AI MCP セキュリティモジュールは、Spring AI のモデルコンテキストプロトコル実装に対し、包括的な OAuth 2.0 および API キーベースのセキュリティサポートを提供します。このコミュニティ主導のプロジェクトにより、開発者は業界標準の認証および認可メカニズムを使用して、MCP サーバーとクライアントの両方を保護できます。
| このモジュールは spring-ai-community/mcp-security [GitHub] (英語) プロジェクトの一部であり、現在は Spring AI の 1.1.x(ブランチ)でのみ動作します。これはコミュニティ主導のプロジェクトであり、Spring AI または MCP プロジェクトによる公式承認はまだ受けていません。 |
概要
MCP セキュリティモジュールは、次の 3 つの主要コンポーネントを提供します。
MCP サーバーセキュリティ - OAuth 2.0 リソースサーバーと Spring AI MCP サーバーの API キー認証
MCP クライアントセキュリティ - Spring AI MCP クライアント向けの OAuth 2.0 クライアントサポート
MCP Authorization Server - MCP 固有の機能を備えた強化された Spring Authorization Server
このプロジェクトにより、開発者は次のことが可能になります。
OAuth 2.0 認証と API キーベースのアクセスによる MCP サーバーのセキュリティ保護
OAuth 2.0 認証フローを使用して MCP クライアントを構成する
MCP ワークフロー専用に設計された認可サーバーをセットアップする
MCP ツールとリソースに対するきめ細かなアクセス制御を実装する
MCP サーバーセキュリティ
MCP サーバーセキュリティモジュールは、Spring AI’s MCP servers に OAuth 2.0 リソースサーバー機能を提供します。また、API キーベースの認証の基本サポートも提供します。
| このモジュールは、Spring WebMVC ベースのサーバーとのみ互換性があります。 |
依存関係
プロジェクトに次の依存関係を追加します。
<dependencies>
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>mcp-server-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- OPTIONAL: For OAuth2 support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
</dependencies>implementation 'org.springaicommunity:mcp-server-security'
implementation 'org.springframework.boot:spring-boot-starter-security'
// OPTIONAL: For OAuth2 support
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'OAuth 2.0 構成
基本的な OAuth 2.0 設定
まず、application.properties で MCP サーバーを有効にします。
spring.ai.mcp.server.name=my-cool-mcp-server
# Supported protocols: STREAMABLE, STATELESS
spring.ai.mcp.server.protocol=STREAMABLE次に、提供されている MCP コンフィギュレーターを使用して、Spring Security の標準 API を使用してセキュリティを構成します。
@Configuration
@EnableWebSecurity
class McpServerConfiguration {
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuerUrl;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// Enforce authentication with token on EVERY request
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
// Configure OAuth2 on the MCP server
.with(
McpServerOAuth2Configurer.mcpServerOAuth2(),
(mcpAuthorization) -> {
// REQUIRED: the issuerURI
mcpAuthorization.authorizationServer(issuerUrl);
// OPTIONAL: enforce the `aud` claim in the JWT token.
// Not all authorization servers support resource indicators,
// so it may be absent. Defaults to `false`.
// See RFC 8707 Resource Indicators for OAuth 2.0
// https://www.rfc-editor.org/rfc/rfc8707.html
mcpAuthorization.validateAudienceClaim(true);
}
)
.build();
}
}ツール呼び出しのみを保護する
他の MCP 操作 (initialize や tools/list など) をパブリックのままにして、ツール呼び出しのみを保護するようにサーバーを構成できます。
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // Enable annotation-driven security
class McpServerConfiguration {
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuerUrl;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// Open every request on the server
.authorizeHttpRequests(auth -> {
auth.requestMatcher("/mcp").permitAll();
auth.anyRequest().authenticated();
})
// Configure OAuth2 on the MCP server
.with(
McpResourceServerConfigurer.mcpServerOAuth2(),
(mcpAuthorization) -> {
// REQUIRED: the issuerURI
mcpAuthorization.authorizationServer(issuerUrl);
}
)
.build();
}
}Then, secure your tool calls using the @PreAuthorize annotation with method security:
@Service
public class MyToolsService {
@PreAuthorize("isAuthenticated()")
@McpTool(name = "greeter", description = "A tool that greets you, in the selected language")
public String greet(
@ToolParam(description = "The language for the greeting (example: english, french, ...)") String language
) {
if (!StringUtils.hasText(language)) {
language = "";
}
return switch (language.toLowerCase()) {
case "english" -> "Hello you!";
case "french" -> "Salut toi!";
default -> "I don't understand language \"%s\". So I'm just going to say Hello!".formatted(language);
};
}
}SecurityContextHolder を使用してツールメソッドから現在の認証に直接アクセスすることもできます。
@McpTool(name = "greeter", description = "A tool that greets the user by name, in the selected language")
@PreAuthorize("isAuthenticated()")
public String greet(
@ToolParam(description = "The language for the greeting (example: english, french, ...)") String language
) {
if (!StringUtils.hasText(language)) {
language = "";
}
var authentication = SecurityContextHolder.getContext().getAuthentication();
var name = authentication.getName();
return switch (language.toLowerCase()) {
case "english" -> "Hello, %s!".formatted(name);
case "french" -> "Salut %s!".formatted(name);
default -> ("I don't understand language \"%s\". " +
"So I'm just going to say Hello %s!").formatted(language, name);
};
}API キー認証
MCP Server Security モジュールは API キーベースの認証もサポートしています。ApiKeyEntity オブジェクトを保存するには、独自の ApiKeyEntityRepository 実装を用意する必要があります。
サンプル実装は、デフォルトの ApiKeyEntityImpl とともに InMemoryApiKeyEntityRepository で利用できます。
InMemoryApiKeyEntityRepository は API キーの保存に bcrypt を使用しますが、これは計算コストが高いため、高トラフィックの本番環境での使用には適していません。本番環境では、独自の ApiKeyEntityRepository を実装してください。 |
@Configuration
@EnableWebSecurity
class McpServerConfiguration {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
.with(
mcpServerApiKey(),
(apiKey) -> {
// REQUIRED: the repo for API keys
apiKey.apiKeyRepository(apiKeyRepository());
// OPTIONAL: name of the header containing the API key.
// Here for example, api keys will be sent with "CUSTOM-API-KEY: <value>"
// Replaces .authenticationConverter(...) (see below)
//
// apiKey.headerName("CUSTOM-API-KEY");
// OPTIONAL: custom converter for transforming an http request
// into an authentication object. Useful when the header is
// "Authorization: Bearer <value>".
// Replaces .headerName(...) (see above)
//
// apiKey.authenticationConverter(request -> {
// var key = extractKey(request);
// return ApiKeyAuthenticationToken.unauthenticated(key);
// });
}
)
.build();
}
/**
* Provide a repository of {@link ApiKeyEntity}.
*/
private ApiKeyEntityRepository<ApiKeyEntityImpl> apiKeyRepository() {
var apiKey = ApiKeyEntityImpl.builder()
.name("test api key")
.id("api01")
.secret("mycustomapikey")
.build();
return new InMemoryApiKeyEntityRepository<>(List.of(apiKey));
}
} この構成では、ヘッダー X-API-key: api01.mycustomapikey を使用して MCP サーバーを呼び出すことができます。
既知の制限
|
MCP クライアントセキュリティ
MCP クライアントセキュリティモジュールは、Spring AI’s MCP clients に OAuth 2.0 サポートを提供し、HttpClient ベースのクライアント (spring-ai-starter-mcp-client から) と WebClient ベースのクライアント (spring-ai-starter-mcp-client-webflux から) の両方をサポートします。
このモジュールは McpSyncClient のみをサポートします。 |
依存関係
Maven
Gradle
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>mcp-client-security</artifactId>
</dependency>implementation 'org.springaicommunity:mcp-client-security'認可フロー
トークンを取得するには、次の 3 つの OAuth 2.0 フローが利用できます。
認証コードフロー - すべての MCP リクエストがユーザーリクエストのコンテキスト内で行われる場合のユーザーレベルの権限の場合
Client Credentials Flow - 人間が関与しないマシンツーマシンのユースケース向け
Hybrid Flow - 一部の操作(
initializeやtools/listなど)はユーザーがいなくても実行されるが、ツールの呼び出しにはユーザーレベルの権限が必要となるシナリオでは、両方のフローを組み合わせます。
| ユーザーレベルの権限があり、すべての MCP リクエストがユーザーコンテキスト内で発生する場合は、認可コードフローを使用してください。マシン間通信にはクライアント資格情報を使用してください。MCP クライアント構成に Spring Boot プロパティを使用する場合は、ユーザーが存在しない起動時にツール検出が行われるため、ハイブリッドフローを使用してください。 |
一般的な設定
すべてのフローについて、application.properties で Spring Security の OAuth2 クライアントサポートを有効にします。
# Ensure MCP clients are sync
spring.ai.mcp.client.type=SYNC
# For authorization_code or hybrid flow
spring.security.oauth2.client.registration.authserver.client-id=<THE CLIENT ID>
spring.security.oauth2.client.registration.authserver.client-secret=<THE CLIENT SECRET>
spring.security.oauth2.client.registration.authserver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.authserver.provider=authserver
# For client_credentials or hybrid flow
spring.security.oauth2.client.registration.authserver-client-credentials.client-id=<THE CLIENT ID>
spring.security.oauth2.client.registration.authserver-client-credentials.client-secret=<THE CLIENT SECRET>
spring.security.oauth2.client.registration.authserver-client-credentials.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.authserver-client-credentials.provider=authserver
# Authorization server configuration
spring.security.oauth2.client.provider.authserver.issuer-uri=<THE ISSUER URI OF YOUR AUTH SERVER>Then, create a configuration class activating OAuth2 client capabilities:
@Configuration
@EnableWebSecurity
class SecurityConfiguration {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// in this example, the client app has no security on its endpoints
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
// turn on OAuth2 support
.oauth2Client(Customizer.withDefaults())
.build();
}
}HttpClient-Based Clients
When using spring-ai-starter-mcp-client, configure a McpSyncHttpClientRequestCustomizer bean:
@Configuration
class McpConfiguration {
@Bean
McpSyncClientCustomizer syncClientCustomizer() {
return (name, syncSpec) ->
syncSpec.transportContextProvider(
new AuthenticationMcpTransportContextProvider()
);
}
@Bean
McpSyncHttpClientRequestCustomizer requestCustomizer(
OAuth2AuthorizedClientManager clientManager
) {
// The clientRegistration name, "authserver",
// must match the name in application.properties
return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(
clientManager,
"authserver"
);
}
}Available customizers:
OAuth2AuthorizationCodeSyncHttpRequestCustomizer- For authorization code flowOAuth2ClientCredentialsSyncHttpRequestCustomizer- For client credentials flowOAuth2HybridSyncHttpRequestCustomizer- For hybrid flow
WebClient-Based Clients
When using spring-ai-starter-mcp-client-webflux, configure a WebClient.Builder with an MCP ExchangeFilterFunction:
@Configuration
class McpConfiguration {
@Bean
McpSyncClientCustomizer syncClientCustomizer() {
return (name, syncSpec) ->
syncSpec.transportContextProvider(
new AuthenticationMcpTransportContextProvider()
);
}
@Bean
WebClient.Builder mcpWebClientBuilder(OAuth2AuthorizedClientManager clientManager) {
// The clientRegistration name, "authserver", must match the name in application.properties
return WebClient.builder().filter(
new McpOAuth2AuthorizationCodeExchangeFilterFunction(
clientManager,
"authserver"
)
);
}
}Available filter functions:
McpOAuth2AuthorizationCodeExchangeFilterFunction- For authorization code flowMcpOAuth2ClientCredentialsExchangeFilterFunction- For client credentials flowMcpOAuth2HybridExchangeFilterFunction- For hybrid flow
Working Around Spring AI Autoconfiguration
Spring AI’s autoconfiguration initializes MCP clients at startup, which can cause issues with user-based authentication. To avoid this:
オプション 1: Disable @Tool Auto-configuration
Disable Spring AI’s @Tool autoconfiguration by publishing an empty ToolCallbackResolver bean:
@Configuration
public class McpConfiguration {
@Bean
ToolCallbackResolver resolver() {
return new StaticToolCallbackResolver(List.of());
}
}オプション 2: Programmatic Client Configuration
Configure MCP clients programmatically instead of using Spring Boot properties. For HttpClient-based clients:
@Bean
McpSyncClient client(
ObjectMapper objectMapper,
McpSyncHttpClientRequestCustomizer requestCustomizer,
McpClientCommonProperties commonProps
) {
var transport = HttpClientStreamableHttpTransport.builder(mcpServerUrl)
.clientBuilder(HttpClient.newBuilder())
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
.httpRequestCustomizer(requestCustomizer)
.build();
var clientInfo = new McpSchema.Implementation("client-name", commonProps.getVersion());
return McpClient.sync(transport)
.clientInfo(clientInfo)
.requestTimeout(commonProps.getRequestTimeout())
.transportContextProvider(new AuthenticationMcpTransportContextProvider())
.build();
}For WebClient-based clients:
@Bean
McpSyncClient client(
WebClient.Builder mcpWebClientBuilder,
ObjectMapper objectMapper,
McpClientCommonProperties commonProperties
) {
var builder = mcpWebClientBuilder.baseUrl(mcpServerUrl);
var transport = WebClientStreamableHttpTransport.builder(builder)
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
.build();
var clientInfo = new McpSchema.Implementation("clientName", commonProperties.getVersion());
return McpClient.sync(transport)
.clientInfo(clientInfo)
.requestTimeout(commonProperties.getRequestTimeout())
.transportContextProvider(new AuthenticationMcpTransportContextProvider())
.build();
}Then add the client to your chat client:
var chatResponse = chatClient.prompt("Prompt the LLM to do the thing")
.toolCallbacks(new SyncMcpToolCallbackProvider(mcpClient1, mcpClient2, mcpClient3))
.call()
.content();既知の制限
|
MCP Authorization Server
The MCP Authorization Server module enhances Spring Security’s OAuth 2.0 Authorization Server with features relevant to the MCP authorization spec (英語) , such as Dynamic Client Registration and Resource Indicators.
依存関係
Maven
Gradle
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>mcp-authorization-server</artifactId>
</dependency>implementation 'org.springaicommunity:mcp-authorization-server'構成
Configure the authorization server in your application.yml:
spring:
application:
name: sample-authorization-server
security:
oauth2:
authorizationserver:
client:
default-client:
token:
access-token-time-to-live: 1h
registration:
client-id: "default-client"
client-secret: "{noop}default-secret"
client-authentication-methods:
- "client_secret_basic"
- "none"
authorization-grant-types:
- "authorization_code"
- "client_credentials"
redirect-uris:
- "http://127.0.0.1:8080/authorize/oauth2/code/authserver"
- "http://localhost:8080/authorize/oauth2/code/authserver"
# mcp-inspector
- "http://localhost:6274/oauth/callback"
# claude code
- "https://claude.ai/api/mcp/auth_callback"
user:
# A single user, named "user"
name: user
password: password
server:
servlet:
session:
cookie:
# Override the default cookie name (JSESSIONID).
# This allows running multiple Spring apps on localhost, and they'll each have their own cookie.
# Otherwise, since the cookies do not take the port into account, they are confused.
name: MCP_AUTHORIZATION_SERVER_SESSIONIDThen activate the authorization server capabilities with a security filter chain:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// all requests must be authenticated
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
// enable authorization server customizations
.with(McpAuthorizationServerConfigurer.mcpAuthorizationServer(), withDefaults())
// enable form-based login, for user "user"/"password"
.formLogin(withDefaults())
.build();
}Samples and Integrations
The samples directory [GitHub] (英語) contains working examples for all modules in this project, including integration tests.
With mcp-server-security and a supporting mcp-authorization-server, you can integrate with:
カーソル
Claude デスクトップ
| When using the MCP Inspector (英語) , you may need to disable CSRF and CORS protection. |