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 サーバーを呼び出すことができます。

既知の制限

  • The deprecated SSE transport is not supported. Use Streamable HTTP or stateless transport.

  • WebFlux ベースのサーバーはサポートされていません。

  • Opaque トークンはサポートされていません。JWT を使用してください。

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 flow

  • OAuth2ClientCredentialsSyncHttpRequestCustomizer - For client credentials flow

  • OAuth2HybridSyncHttpRequestCustomizer - 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 flow

  • McpOAuth2ClientCredentialsExchangeFilterFunction - For client credentials flow

  • McpOAuth2HybridExchangeFilterFunction - 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();

既知の制限

  • Spring WebFlux servers are not supported.

  • Spring AI autoconfiguration initializes MCP clients at app start, requiring workarounds for user-based authentication.

  • Unlike the server module, the client implementation supports the SSE transport with both HttpClient and WebClient.

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_SESSIONID

Then 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();
}

既知の制限

  • Spring WebFlux servers are not supported.

  • Every client supports ALL resource identifiers.

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:

When using the MCP Inspector (英語) , you may need to disable CSRF and CORS protection.