Spring AI MCP 통합 가이드: AI 에이전트와 외부 도구를 연결하는 표준 프로토콜

이번 포스팅에서는 Spring AI MCP 통합에 대해서 정리하고자 한다. 2026년 3월 기준 Spring AI는 1.1.3까지 릴리스되었고, Anthropic이 제안한 Model Context Protocol(MCP)을 Boot Starter 수준에서 공식 지원한다. MCP는 AI 모델이 데이터베이스, 파일 시스템, 외부 API 같은 리소스에 접근할 때 사용하는 표준 인터페이스다. 기존 Spring Boot 프로젝트에 의존성 하나와 어노테이션 몇 개를 추가하면 AI 에이전트가 호출 가능한 MCP 서버가 만들어진다. 아래에서 서버 구축, 클라이언트 연동, Transport 선택, 접근 제어까지 코드와 함께 살펴본다.

MCP가 풀려는 문제 — AI 모델과 외부 세계의 간극

AI 모델은 학습 데이터에 갇혀 있다. 실시간 날씨를 묻거나 사내 데이터베이스를 조회하려면 모델 바깥의 도구를 호출해야 한다. 문제는 도구마다 호출 방식이 제각각이라는 점이다. OpenAI의 Function Calling, Anthropic의 Tool Use, Google의 Function Declaration이 모두 다른 스키마를 사용한다.

Spring AI MCP는 이 파편화를 해결한다. Anthropic이 2024년 말 공개한 Model Context Protocol은 AI 호스트(애플리케이션)와 외부 도구 사이에 놓이는 표준 인터페이스다. Spring 팀은 MCP Java SDK 개발에 직접 참여했고, Spring AI 1.0부터 Boot Starter를 제공하여 어노테이션 몇 개만으로 MCP 서버와 클라이언트를 구성할 수 있게 만들었다. 2026년 3월 현재 Spring AI 1.1.3이 최신 안정 버전이며, STDIO·SSE·Streamable-HTTP·Stateless 네 가지 Transport를 모두 지원한다.

MCP 아키텍처는 세 계층으로 구성된다. 최상위 Client/Server 계층이 도구 등록과 호출을 처리하고, 중간의 Session 계층이 프로토콜 협상과 상태 관리를 담당하며, 최하위 Transport 계층이 JSON-RPC 메시지를 실제로 주고받는다. Spring AI MCP Boot Starter는 이 세 계층을 자동 구성하므로 개발자가 직접 세션이나 트랜스포트를 다룰 필요가 없다.

Spring AI MCP 서버를 5분 만에 띄우는 법

MCP 서버는 AI 모델이 호출할 수 있는 도구(Tool), 리소스(Resource), 프롬프트(Prompt)를 외부에 노출한다. 의존성 하나, 어노테이션 하나면 된다.

Maven 의존성 추가

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
XML

위 의존성은 WebMVC 기반의 HTTP MCP 서버를 자동 구성한다. 리액티브 스택이 필요하면 spring-ai-starter-mcp-server-webflux로 교체하면 된다. STDIO 방식으로 로컬 프로세스 간 통신만 필요한 경우에는 spring-ai-starter-mcp-server를 사용한다.

@McpTool로 도구 노출하기

import org.springframework.ai.mcp.server.McpTool;      // MCP 도구 등록 어노테이션
import org.springframework.ai.mcp.server.McpToolParam;  // 도구 파라미터 메타데이터
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    // 주문 상태를 조회하는 MCP 도구 — AI 모델이 이 메서드를 직접 호출할 수 있다
    @McpTool(name = "getOrderStatus",
             description = "Retrieve the current status of an order by its ID")
    public OrderStatus getOrderStatus(
            @McpToolParam(description = "The unique order identifier", required = true)
            String orderId) {

        // 실제 구현에서는 DB 조회 로직이 들어간다
        return orderRepository.findStatusById(orderId);
    }

    // 최근 주문 목록을 반환하는 도구 — limit 파라미터로 건수를 제한한다
    @McpTool(name = "getRecentOrders",
             description = "Get the most recent orders for a given customer")
    public List<Order> getRecentOrders(
            @McpToolParam(description = "Customer ID", required = true) String customerId,
            @McpToolParam(description = "Maximum number of orders to return") int limit) {

        return orderRepository.findRecentByCustomerId(customerId, limit);
    }
}
Java

@McpTool 어노테이션은 메서드를 MCP 도구로 등록한다. namedescription은 AI 모델이 도구를 선택할 때 참조하는 메타데이터이므로 영문으로 명확하게 작성해야 한다. @McpToolParamdescription도 마찬가지로 AI 모델이 파라미터 용도를 판단하는 데 사용된다. Spring AI는 이 어노테이션 정보를 기반으로 JSON Schema를 자동 생성하여 MCP 클라이언트에 전달한다.

application.yml 설정

spring:
  ai:
    mcp:
      server:
        name: order-mcp-server       # MCP 서버 식별 이름
        version: 1.0.0               # 서버 버전
        type: SYNC                   # 동기 처리 (기본값)
        protocol: STREAMABLE         # Streamable-HTTP 프로토콜 사용
        annotation-scanner:
          enabled: true              # @McpTool 자동 스캔 활성화
YAML

protocol 값에 따라 Transport 방식이 결정된다. STREAMABLE은 Spring AI 1.1부터 권장되는 프로토콜로, 다수 클라이언트의 동시 접속을 지원하면서도 SSE 대비 연결 관리가 단순하다. 로컬 개발 시에는 SSE(기본값)도 충분하다.

Spring AI MCP 클라이언트 — ChatClient와 도구 연결

MCP 클라이언트는 MCP 서버에 접속하여 사용 가능한 도구 목록을 가져오고, AI 모델의 요청에 따라 도구를 실행한다. Spring AI의 ChatClient와 결합하면 “사용자 질문 → AI 판단 → 도구 호출 → 결과 반환” 흐름이 자동으로 동작한다.

Maven 의존성

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

<!-- AI 모델 프로바이더 (예: OpenAI) -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
XML

spring-ai-starter-mcp-client는 STDIO, SSE, Streamable-HTTP Transport를 모두 지원한다. WebFlux 기반 리액티브 클라이언트가 필요하면 spring-ai-starter-mcp-client-webflux를 사용한다. 프로덕션 환경에서는 WebFlux 스타터가 권장된다.

클라이언트 설정

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}     # AI 모델 API 키
      chat:
        options:
          model: gpt-4o              # 도구 호출을 지원하는 모델 선택
    mcp:
      client:
        enabled: true
        type: SYNC
        request-timeout: 30s         # MCP 서버 응답 대기 시간
        toolcallback:
          enabled: true              # MCP 도구를 ToolCallback으로 자동 등록
        streamable-http:
          connections:
            order-server:                     # 연결 식별자
              url: http://localhost:8081      # MCP 서버 주소
              endpoint: /mcp                  # Streamable-HTTP 엔드포인트
YAML

toolcallback.enabledtrue로 설정하면 MCP 서버가 노출한 도구들이 Spring AI의 ToolCallbackProvider에 자동 등록된다. 이 설정 덕분에 ChatClient에서 별도의 도구 등록 코드 없이 MCP 도구를 바로 사용할 수 있다.

ChatClient에서 MCP 도구 사용

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AiAgentConfig {

    @Bean
    CommandLineRunner orderAgent(
            ChatClient.Builder chatClientBuilder,
            ToolCallbackProvider mcpTools) {  // MCP 도구가 자동 주입된다

        ChatClient chatClient = chatClientBuilder.build();

        return args -> {
            // 사용자 질문을 AI 모델에 전달하면서 MCP 도구를 함께 제공한다
            String response = chatClient
                    .prompt("주문번호 ORD-2026-0319의 현재 상태를 알려줘")
                    .toolCallbacks(mcpTools)  // MCP 서버의 모든 도구를 AI에 제공
                    .call()
                    .content();

            System.out.println(response);
            // 출력 예: "주문번호 ORD-2026-0319는 현재 '배송중' 상태입니다.
            //          3월 20일 도착 예정입니다."
        };
    }
}
Java

이 코드의 실행 흐름은 다음과 같다. 먼저 ChatClient가 사용자 프롬프트와 함께 MCP 도구 목록을 AI 모델에 전달한다. AI 모델이 질문을 분석하여 getOrderStatus 도구를 호출해야 한다고 판단하면, Spring AI가 MCP 클라이언트를 통해 서버의 해당 도구를 실행한다. 도구가 반환한 결과를 AI 모델이 자연어로 가공하여 최종 응답을 생성한다. 개발자는 이 전체 흐름을 .toolCallbacks(mcpTools) 한 줄로 연결할 수 있다.

실전 예제 — 날씨 조회 Spring AI MCP 서버·클라이언트 전체 코드

공식 문서에서도 사용하는 날씨 조회 예제를 완성된 형태로 구현해 본다. 서버와 클라이언트를 별도 Spring Boot 모듈로 분리하는 구조다.

서버 모듈: weather-mcp-server

import org.springframework.ai.mcp.server.McpTool;
import org.springframework.ai.mcp.server.McpToolParam;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class WeatherTools {

    private final RestClient restClient = RestClient.builder()
            .baseUrl("https://api.open-meteo.com/v1")
            .build();

    // 도시명으로 현재 기온을 조회한다 — Open-Meteo 무료 API 사용
    @McpTool(name = "getCurrentTemperature",
             description = "Get the current temperature for a given city in Celsius")
    public WeatherResponse getCurrentTemperature(
            @McpToolParam(description = "City name in English", required = true)
            String city) {

        // 도시명 → 위도·경도 변환은 별도 Geocoding API로 처리
        GeoLocation location = geocode(city);
        
        String json = restClient.get()
                .uri(uriBuilder -> uriBuilder
                  .path("/forecast")
                  .queryParam("latitude", location.latitude())
                  .queryParam("longitude", location.longitude())
                  .queryParam("current", "temperature_2m")
                  .build())
                .retrieve()
                .body(String.class);

        // 응답 파싱 후 WeatherResponse 반환
        return parseWeatherResponse(json, city);
    }

    @McpTool(name = "getWeeklyForecast",
             description = "Get a 7-day weather forecast for a given city")
    public List<DailyForecast> getWeeklyForecast(
            @McpToolParam(description = "City name in English", required = true)
            String city) {

        GeoLocation location = geocode(city);
        // Open-Meteo daily forecast API 호출
        String json = restClient.get()
                .uri("/forecast?latitude={lat}&longitude={lon}"
                   + "&daily=temperature_2m_max,temperature_2m_min,precipitation_sum"
                   + "&timezone=auto",
                        location.latitude(), location.longitude())
                .retrieve()
                .body(String.class);

        return parseDailyForecasts(json, city);
    }

    // Geocoding 및 파싱 메서드는 지면 관계상 생략
    private GeoLocation geocode(String city) { /* ... */ }
    private WeatherResponse parseWeatherResponse(String json, String city) { /* ... */ }
    private List<DailyForecast> parseDailyForecasts(String json, String city) { /* ... */ }
}
Java

WeatherTools 클래스는 두 개의 MCP 도구를 노출한다. getCurrentTemperature는 실시간 기온을, getWeeklyForecast는 7일 예보를 반환한다. Open-Meteo API는 무료이므로 별도의 API 키가 필요 없다. 눈여겨볼 부분은 RestClient로 외부 API를 호출하는 평범한 Spring Boot 코드가 그대로 MCP 도구가 된다는 점이다. 특별한 SDK나 래퍼 없이 기존 코드에 어노테이션만 얹으면 된다.

서버 측 application.yml은 다음과 같다.

server:
  port: 8081

spring:
  ai:
    mcp:
      server:
        name: weather-mcp-server
        version: 1.0.0
        protocol: STREAMABLE
YAML

클라이언트 모듈: weather-mcp-client

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class WeatherClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(WeatherClientApplication.class, args);
    }

    @Bean
    CommandLineRunner weatherAgent(
            ChatClient.Builder builder,
            ToolCallbackProvider mcpTools) {

        ChatClient chatClient = builder.build();

        return args -> {
            // AI 모델이 도구를 선택하여 날씨 정보를 조회한 뒤 자연어로 응답한다
            String answer = chatClient
                    .prompt("서울과 도쿄의 이번 주 날씨를 비교해서 알려줘")
                    .toolCallbacks(mcpTools)
                    .call()
                    .content();

            System.out.println(answer);
        };
    }
}
Java

클라이언트 측 application.yml은 아래와 같다.

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4o
    mcp:
      client:
        streamable-http:
          connections:
            weather:
              url: http://localhost:8081
YAML

이 구성으로 클라이언트를 실행하면 AI 모델이 “서울과 도쿄의 날씨를 비교하라”는 요청을 분석하여 getCurrentTemperaturegetWeeklyForecast 도구를 도시별로 호출한다. Spring AI MCP 클라이언트가 서버와의 통신을 자동으로 처리하므로 개발자는 ChatClient 코드만 작성하면 된다.

Transport 프로토콜, 어떤 걸 골라야 하는가

Spring AI MCP는 네 가지 Transport를 지원한다. 어떤 걸 써야 하는지는 배포 환경에 따라 다르다.

Transport프로토콜 설정값특징적합한 시나리오
STDIOstdio=true프로세스 간 표준 입출력 통신CLI 도구, 로컬 개발, IDE 플러그인
SSEprotocol=SSEHTTP 기반 Server-Sent Events단일 클라이언트, 실시간 스트리밍
Streamable-HTTPprotocol=STREAMABLEHTTP 기반 양방향 스트리밍다중 클라이언트, 프로덕션 배포
Statelessprotocol=STATELESS세션 없는 HTTP 통신클라우드 네이티브, 서버리스 환경

STDIO는 별도 HTTP 서버 없이 프로세스 간 파이프로 통신하므로 설정이 가장 단순하다. Claude Desktop이나 VS Code 같은 AI 호스트가 로컬 MCP 서버를 실행할 때 주로 사용한다. SSE는 Spring AI 1.0부터 지원된 기본 프로토콜이지만, 단일 연결에서 서버→클라이언트 방향만 스트리밍이 가능한 한계가 있다.

Streamable-HTTP는 Spring AI 1.1에서 추가된 프로토콜로, 다수 클라이언트의 동시 접속과 양방향 통신을 지원한다. 공식 문서에서도 프로덕션 환경에는 Streamable-HTTP를 권장하고 있다. Stateless는 세션 상태를 서버에 저장하지 않으므로 Lambda나 Cloud Run 같은 서버리스 환경에 배포할 때 유리하다.

@McpTool 너머 — 리소스와 프롬프트까지 다루기

Spring AI MCP 서버가 노출할 수 있는 것은 도구만이 아니다. @McpResource로 URI 기반 데이터 접근을, @McpPrompt로 프롬프트 템플릿을 제공할 수 있다.

import org.springframework.ai.mcp.server.McpResource;
import org.springframework.ai.mcp.server.McpPrompt;
import org.springframework.stereotype.Component;

@Component
public class ProjectMcpCapabilities {

    // URI 템플릿으로 프로젝트 설정값에 접근하는 리소스
    @McpResource(uri = "config://{key}", name = "ProjectConfig",
                 description = "Access project configuration values by key")
    public String getConfig(String key) {
        return configRepository.findByKey(key);
    }

    // AI 모델에 코드 리뷰 프롬프트 템플릿을 제공한다
    @McpPrompt(name = "code-review",
               description = "Generate a code review prompt for the given file")
    public String codeReviewPrompt(String filePath, String language) {
        return """
            다음 %s 코드를 리뷰해 주세요.
            파일: %s
            관점: 보안 취약점, 성능 이슈, 코드 컨벤션
            """.formatted(language, filePath);
    }
}
Java

@McpResourceconfig://database.url처럼 URI 패턴으로 데이터에 접근하는 인터페이스를 정의한다. AI 모델이 설정값을 참조해야 할 때 도구 호출 대신 리소스 조회를 사용할 수 있어 의미적으로 더 명확하다. @McpPrompt는 자주 사용하는 프롬프트를 서버에 미리 등록해 두는 기능으로, AI 호스트가 프롬프트 목록을 조회하여 사용자에게 선택지를 제공하는 데 활용된다.

Spring AI는 어노테이션 스캐너가 @McpTool, @McpResource, @McpPrompt가 붙은 빈을 자동 감지하여 MCP 서버에 등록한다. spring.ai.mcp.server.annotation-scanner.enabled=true(기본값)일 때 별도 설정 없이 동작한다.

도구 필터링으로 접근 제어 구현하기

프로덕션 환경에서는 모든 MCP 도구를 모든 클라이언트에 노출하지 않아야 할 수 있다. Spring AI MCP는 McpToolFilter 인터페이스로 도구 수준의 접근 제어를 제공한다.

import org.springframework.ai.mcp.McpToolFilter;
import org.springframework.ai.mcp.McpConnectionInfo;
import io.modelcontextprotocol.spec.McpSchema;
import org.springframework.stereotype.Component;

@Component
public class ToolAccessFilter implements McpToolFilter {

    @Override
    public boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool) {
        String clientName = connectionInfo.clientInfo().name();

        // internal-agent에게만 관리자 도구를 허용한다
        if (tool.name().startsWith("admin_")) {
            return "internal-agent".equals(clientName);
        }
        // 그 외 도구는 모든 클라이언트에 허용
        return true;
    }
}
Java

McpToolFilter는 MCP 클라이언트가 도구 목록을 조회할 때 호출된다. 연결 정보(McpConnectionInfo)와 도구 메타데이터를 기반으로 노출 여부를 결정하므로, 클라이언트 식별자나 도구 이름 접두어를 활용한 필터링이 가능하다. 이 방식은 MCP 프로토콜 수준에서 동작하기 때문에 AI 모델이 비허가 도구의 존재 자체를 인지하지 못한다.

Spring AI MCP 도입 시 알아 두면 좋은 것들

Spring AI MCP를 실무에 적용할 때 자주 마주치는 설정과 주의사항을 정리한다.

먼저 버전 호환성부터 짚어 두자. Spring AI 1.1.x는 Spring Boot 3.2~3.4와 호환된다. 현재 개발 중인 Spring AI 2.0은 Spring Boot 4.x를 타겟으로 하므로 혼동하면 안 된다. MCP Java SDK는 Spring AI에 내장되어 있어 별도 의존성은 필요 없다.

타임아웃은 신경 써야 할 부분이다. MCP 클라이언트의 기본 요청 타임아웃은 20초인데, 외부 API를 호출하는 도구가 있다면 spring.ai.mcp.client.request-timeout을 30~60초로 늘려야 한다. 서버 측에서 도구 실행이 오래 걸리는 경우에는 @McpTool의 반환 타입을 Mono로 바꾸고 서버 타입을 ASYNC로 설정하면 비동기 처리가 가능하다.

하나의 클라이언트에서 여러 MCP 서버에 동시 접속하는 것도 가능하다. spring.ai.mcp.client.streamable-http.connections 아래에 서버별 연결 정보를 나열하면 각 서버의 도구가 ToolCallbackProvider 하나에 합쳐져 ChatClient에 제공된다. Piotr Minkowski의 블로그 Spring AI with External MCP Servers에서 GitHub, SonarQube MCP 서버를 Spring AI에 연결하는 사례를 다루고 있다.

프로젝트를 처음 시작할 때는 Spring Initializr가 편하다. “MCP Server”나 “MCP Client” 의존성을 선택하면 스타터와 기본 설정이 포함된 프로젝트가 생성된다.

마치며

지금까지 Spring AI MCP 통합에 대해서 정리해 보았다. 결국 MCP가 해주는 일은 “AI 모델이 외부 도구를 호출하는 방식”을 하나로 통일하는 것이다. Spring AI는 그 위에 Boot Starter와 어노테이션을 얹어서 Spring 개발자가 익숙한 방식으로 작업할 수 있게 만들었다. @McpTool 하나면 기존 서비스 메서드가 AI 호출 가능한 도구가 되고, .toolCallbacks(mcpTools) 한 줄이면 AI 모델과 도구가 연결된다. MCP 명세 자체가 아직 빠르게 바뀌고 있으므로 공식 MCP 명세Spring AI 레퍼런스 문서는 북마크해 두고 수시로 확인하는 것이 좋다.