SpringとKotlinで作成したWebMVC MCP Serverへ認証を追加する
WebMVC MCP ServerへSpring Securityを適用し、認証を追加します。
はじめに
前回はWebMVC MCP Serverを作成しました。ここでは認証を追加します。
Spring AIはSpring Securityを通じてOAuth2とAPIキー認証をサポートします。
2025-11-10時点でMCP Securityは開発中であり、安定版ではありません。
この例ではAPIキー認証を適用します。
認証を追加する
前回のWebMVCプロジェクトをコピーして名前を変更します。
% mv spring-ai-mcp-server-webmvc spring-ai-mcp-server-auth
ルートプロジェクト名を変更します。
rootProject.name = "spring-ai-mcp-server-auth"
Spring SecurityとMCP Server Securityを追加します。
dependencies {
implementation("org.springframework.ai:spring-ai-starter-mcp-server")
implementation("org.springframework.ai:spring-ai-starter-mcp-server-webmvc")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springaicommunity:mcp-server-security:0.0.3")
}
ログファイルを変更し、Spring SecurityのTRACEログを有効にします。
spring:
main:
banner-mode: off
ai:
mcp:
server:
name: my-weather-server
version: 0.0.1
protocol: STATELESS
logging:
file:
name: ./log/spring-ai-starter-mcp-server-auth.log
level:
org.springframework.security: TRACE
MCP Serverのセキュリティ設定を作成する
Spring SecurityへMCP APIキーを登録します。
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain =
http.authorizeHttpRequests { it.anyRequest().authenticated() }
.with(mcpServerApiKey()) { apiKey ->
apiKey.apiKeyRepository(apiKeyRepository())
}.build()
private fun apiKeyRepository(): ApiKeyEntityRepository<ApiKeyEntityImpl> {
val apiKey = ApiKeyEntityImpl.builder()
.name("test api key")
.id("api01")
.secret("mycustomapikey")
.build()
return InMemoryApiKeyEntityRepository(listOf(apiKey))
}
}
例ではID api01とsecret mycustomapikeyをメモリーへ保存します。本番環境ではデータベースや外部APIを使用してください。
テストクライアントを実装する
既存クライアントへAPIキーヘッダーを追加します。
val request = HttpRequest.newBuilder()
.header("Content-Type", "application/json")
.header("X-API-key", "api01.mycustomapikey")
val transport = HttpClientStreamableHttpTransport.builder("http://localhost:8080")
.requestBuilder(request)
.build()
val client = McpClient.sync(transport).build()
client.initialize()
client.ping()
println("Available Tools = ${client.listTools()}")
println("get_weather Response = ${client.callTool(CallToolRequest("get_weather", mapOf("city" to "seoul")))}")
client.closeGracefully()
クライアントを実行すると、利用可能なツール一覧とget_weatherのレスポンスが表示されます。