MCP 클라이언트 Boot Starter

MCP 클라이언트 Boot Starter (MCP Client Boot Starter)

Spring AI MCP(Model Context Protocol) Client Boot Starter는 Spring Boot 애플리케이션에서 MCP 클라이언트 기능에 대한 자동 설정을 제공해요. 동기·비동기 클라이언트 구현을 다양한 전송 옵션과 함께 지원하는데, 다중 클라이언트 관리부터 도구 필터링, 이름 프리픽스 생성까지 폭넓은 기능을 담고 있어요. 이 글에서 차근차근 살펴볼게요.

출처: 문서

본문

MCP 클라이언트 Boot Starter (MCP Client Boot Starter)

Spring AI MCP(Model Context Protocol) Client Boot Starter는 Spring Boot 애플리케이션에서 MCP 클라이언트 기능에 대한 자동 설정을 제공해요. 다양한 전송 옵션과 함께 동기·비동기 클라이언트 구현을 모두 지원해요.

MCP Client Boot Starter는 다음을 제공해요:

  • 여러 클라이언트 인스턴스 관리
  • 자동 클라이언트 초기화 (활성화 시)
  • 여러 이름 있는 전송 지원 (STDIO, Http/SSE, Streamable HTTP)
  • Spring AI의 도구 실행 프레임워크와의 통합
  • 선택적 도구 포함/제외를 위한 도구 필터링 기능
  • 이름 충돌 방지를 위한 커스터마이즈 가능한 도구 이름 프리픽스 생성
  • 애플리케이션 컨텍스트가 닫힐 때 리소스 자동 정리를 포함한 적절한 라이프사이클 관리
  • 커스터마이저를 통한 커스터마이즈 가능한 클라이언트 생성

Starters

표준 MCP 클라이언트 (Standard MCP Client)

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

표준 starter는 STDIO(프로세스 내), SSE, Streamable-HTTP, Stateless Streamable-HTTP 전송으로 하나 이상의 MCP 서버에 동시에 연결해요. SSE와 Streamable-Http 전송은 JDK HttpClient 기반 전송 구현을 사용해요. MCP 서버에 대한 각 연결이 새 MCP 클라이언트 인스턴스를 만들어요.

SYNC 또는 ASYNC MCP 클라이언트 중에서 선택할 수 있어요 (참고: 동기와 비동기 클라이언트는 혼합할 수 없어요). 프로덕션 배포에는 spring-ai-starter-mcp-client-webflux와 함께 WebFlux 기반 SSE & StreamableHttp 연결을 사용하는 것을 권장해요.

WebFlux 클라이언트 (WebFlux Client)

WebFlux starter는 표준 starter와 유사한 기능을 제공하지만 WebFlux 기반 Streamable-Http, Stateless Streamable-Http, SSE 전송 구현을 사용해요.

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

구성 프로퍼티 (Configuration Properties)

공통 프로퍼티 (Common Properties)

공통 프로퍼티는 spring.ai.mcp.client 프리픽스가 붙어요:

Property Description Default Value
enabled MCP 클라이언트 활성화/비활성화 true
name MCP 클라이언트 인스턴스의 이름 spring-ai-mcp-client
version MCP 클라이언트 인스턴스의 버전 1.0.0
initialized 생성 시 클라이언트를 초기화할지 여부 true
request-timeout MCP 클라이언트 요청의 타임아웃 기간 20s
type 클라이언트 타입 (SYNC 또는 ASYNC). 모든 클라이언트는 동기 또는 비동기 중 하나여야 하며 혼합은 지원되지 않아요 SYNC
root-change-notification 모든 클라이언트에 대한 루트 변경 알림 활성화/비활성화 true
toolcallback.enabled Spring AI의 도구 실행 프레임워크와의 MCP 도구 콜백 통합 활성화/비활성화 true

MCP 어노테이션 프로퍼티 (MCP Annotations Properties)

MCP 클라이언트 어노테이션은 Java 어노테이션을 사용해 MCP 클라이언트 핸들러를 선언적으로 구현하는 방법을 제공해요. 클라이언트 mcp-annotations 프로퍼티는 spring.ai.mcp.client.annotation-scanner 프리픽스가 붙어요:

Property Description Default Value
enabled MCP 클라이언트 어노테이션 자동 스캔 활성화/비활성화 true

Stdio 전송 프로퍼티 (Stdio Transport Properties)

표준 I/O 전송 프로퍼티는 spring.ai.mcp.client.stdio 프리픽스가 붙어요:

Property Description Default Value
servers-configuration MCP 서버 구성을 담은 JSON 형식의 리소스 -
connections 이름 있는 stdio 연결 구성의 맵 -
connections.[name].command MCP 서버를 위해 실행할 명령 -
connections.[name].args 명령 인자 목록 -
connections.[name].env 서버 프로세스를 위한 환경 변수 맵 -

구성 예시:

spring:
  ai:
    mcp:
      client:
        stdio:
          root-change-notification: true
          connections:
            server1:
              command: /path/to/server
              args:
                - --port=8080
                - --mode=production
              env:
                API_KEY: your-api-key
                DEBUG: "true"

또는 Claude Desktop 형식의 외부 JSON 파일을 사용해 stdio 연결을 구성할 수 있어요:

spring:
  ai:
    mcp:
      client:
        stdio:
          servers-configuration: classpath:mcp-servers.json

Claude Desktop 형식은 다음과 같아요:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/Desktop",
        "/Users/username/Downloads"
      ]
    }
  }
}

Windows STDIO 구성 (Windows STDIO Configuration)

참고: Windows에서 npx, npm, node 같은 명령은 네이티브 실행 파일이 아니라 배치 파일(.cmd)로 구현돼 있어요. Java의 ProcessBuilder는 배치 파일을 직접 실행할 수 없고 cmd.exe /c 래퍼가 필요해요.

Windows가 특별한 처리를 필요로 하는 이유 (Why Windows Needs Special Handling)

Java의 ProcessBuilder(내부적으로 StdioClientTransport가 사용)가 Windows에서 프로세스를 생성하려고 할 때, 실행할 수 있는 것은 다음과 같아요:

  • 네이티브 실행 파일 (.exe 파일)
  • cmd.exe가 사용할 수 있는 시스템 명령

npx.cmd, npm.cmd, 심지어 python.cmd(Microsoft Store에서 온 것) 같은 Windows 배치 파일은 실행에 cmd.exe 셸이 필요해요.

해결책: cmd.exe 래퍼 (Solution: cmd.exe Wrapper)

배치 파일 명령을 cmd.exe /c로 감싸세요:

Windows 구성:

{
  "mcpServers": {
    "filesystem": {
      "command": "cmd.exe",
      "args": [
        "/c",
        "npx",
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "C:\\Users\\username\\Desktop"
      ]
    }
  }
}

Linux/macOS 구성:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/Desktop"
      ]
    }
  }
}

크로스 플랫폼 프로그래밍 구성 (Cross-Platform Programmatic Configuration)

별도의 구성 파일 없이 여러 플랫폼에서 동작해야 하는 애플리케이션의 경우, Spring Boot 애플리케이션에서 OS 감지를 사용하세요:

@Bean(destroyMethod = "close")
@ConditionalOnMissingBean(McpSyncClient.class)
public McpSyncClient mcpClient() {
    ServerParameters stdioParams;

    if (isWindows()) {
        // Windows: cmd.exe /c npx approach
        var winArgs = new ArrayList<>(Arrays.asList(
            "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "target"));
        stdioParams = ServerParameters.builder("cmd.exe")
                .args(winArgs)
                .build();
    } else {
        // Linux/Mac: direct npx approach
        stdioParams = ServerParameters.builder("npx")
                .args("-y", "@modelcontextprotocol/server-filesystem", "target")
                .build();
    }

    return McpClient.sync(new StdioClientTransport(stdioParams, McpJsonDefaults.getMapper()))
            .requestTimeout(Duration.ofSeconds(10))
            .build()
            .initialize();
}

private static boolean isWindows() {
    return System.getProperty("os.name").toLowerCase().contains("win");
}

참고: @Bean으로 프로그래밍 구성할 때는 JSON 파일의 자동 설정과의 충돌을 피하기 위해 @ConditionalOnMissingBean(McpSyncClient.class)를 추가하세요.

경로 고려 사항 (Path Considerations)

상대 경로 (이식성에 권장):

{
  "command": "cmd.exe",
  "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "target"]
}

MCP 서버는 애플리케이션의 작업 디렉터리를 기준으로 상대 경로를 해석해요.

절대 경로 (Windows는 백슬래시나 이스케이프된 슬래시 필요):

{
  "command": "cmd.exe",
  "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\Users\\username\\project\\target"]
}

cmd.exe가 필요한 일반 Windows 배치 파일 (Common Windows Batch Files Requiring cmd.exe)

  • npx.cmd, npm.cmd - Node 패키지 관리자
  • python.cmd - Python (Microsoft Store 설치)
  • pip.cmd - Python 패키지 관리자
  • mvn.cmd - Maven wrapper
  • gradle.cmd - Gradle wrapper
  • 커스텀 .cmd 또는 .bat 스크립트

참조 구현 (Reference Implementation)

OS를 자동으로 감지하고 클라이언트를 적절히 구성하는 완전한 크로스 플랫폼 MCP 클라이언트 구현은 Spring AI Examples - Filesystem을 참고하세요.

Streamable-HTTP 전송 프로퍼티 (Streamable-HTTP Transport Properties)

Streamable-HTTP 및 Stateless Streamable-HTTP MCP 서버에 연결할 때 사용돼요. Streamable-HTTP 전송 프로퍼티는 spring.ai.mcp.client.streamable-http 프리픽스가 붙어요:

Property Description Default Value
connections 이름 있는 Streamable-HTTP 연결 구성의 맵 -
connections.[name].url MCP 서버와 Streamable-Http 통신을 위한 베이스 URL 엔드포인트 -
connections.[name].endpoint 연결에 사용할 streamable-http 엔드포인트 (url 접미사) /mcp

구성 예시:

spring:
  ai:
    mcp:
      client:
        streamable-http:
          connections:
            server1:
              url: http://localhost:8080
            server2:
              url: http://otherserver:8081
              endpoint: /custom-sse

SSE 전송 프로퍼티 (SSE Transport Properties)

Server-Sent Events (SSE) 전송 프로퍼티는 spring.ai.mcp.client.sse 프리픽스가 붙어요:

Property Description Default Value
connections 이름 있는 SSE 연결 구성의 맵 -
connections.[name].url MCP 서버와 SSE 통신을 위한 베이스 URL 엔드포인트 -
connections.[name].sse-endpoint 연결에 사용할 sse 엔드포인트 (url 접미사) /sse

구성 예시:

spring:
  ai:
    mcp:
      client:
        sse:
          connections:
            # Simple configuration using default /sse endpoint
            server1:
              url: http://localhost:8080
            # Custom SSE endpoint
            server2:
              url: http://otherserver:8081
              sse-endpoint: /custom-sse
            # Complex URL with path and token (like MCP Hub)
            mcp-hub:
              url: http://localhost:3000
              sse-endpoint: /mcp-hub/sse/cf9ec4527e3c4a2cbb149a85ea45ab01
            # SSE endpoint with query parameters
            api-server:
              url: https://api.example.com
              sse-endpoint: /v1/mcp/events?token=abc123&format=json

URL 분할 가이드라인 (URL Splitting Guidelines)

전체 SSE URL이 있으면 베이스 URL과 엔드포인트 경로로 나누세요:

Full URL Configuration
http://localhost:3000/mcp-hub/sse/token123 url: localhost:3000 sse-endpoint: /mcp-hub/sse/token123
https://api.service.com/v2/events?key=secret url: api.service.com sse-endpoint: /v2/events?key=secret
http://localhost:8080/sse url: localhost:8080 sse-endpoint: /sse (또는 기본값을 위해 생략)

SSE 연결 문제 해결 (Troubleshooting SSE Connections)

404 Not Found 오류:

  • URL 분할을 확인하세요: 베이스 url에 스킴, 호스트, 포트만 포함되어 있는지 확인.
  • sse-endpoint가 /로 시작하고 전체 경로와 쿼리 파라미터를 포함하는지 확인.
  • 브라우저나 curl에서 전체 URL을 직접 테스트해 접근 가능한지 확인.

Streamable Http 전송 프로퍼티 (Streamable Http Transport Properties)

Streamable Http 전송 프로퍼티는 spring.ai.mcp.client.streamable-http 프리픽스가 붙어요:

Property Description Default Value
connections 이름 있는 Streamable Http 연결 구성의 맵 -
connections.[name].url MCP 서버와 Streamable-Http 통신을 위한 베이스 URL 엔드포인트 -
connections.[name].endpoint 연결에 사용할 streamable-http 엔드포인트 (url 접미사) /mcp

구성 예시:

spring:
  ai:
    mcp:
      client:
        streamable-http:
          connections:
            server1:
              url: http://localhost:8080
            server2:
              url: http://otherserver:8081
              endpoint: /custom-sse

기능 (Features)

동기/비동기 클라이언트 타입 (Sync/Async Client Types)

stater는 두 가지 타입의 클라이언트를 지원해요:

  • 동기(Synchronous) - 기본 클라이언트 타입 (spring.ai.mcp.client.type=SYNC). 블로킹 작업이 있는 전통적인 요청-응답 패턴에 적합해요. 참고: SYNC 클라이언트는 동기 MCP 어노테이션 메서드만 등록해요. 비동기 메서드는 무시돼요.
  • 비동기(Asynchronous) - 블로킹되지 않는 연산이 있는 반응형 애플리케이션에 적합. spring.ai.mcp.client.type=ASYNC로 구성. 참고: ASYNC 클라이언트는 비동기 MCP 어노테이션 메서드만 등록해요. 동기 메서드는 무시돼요.

클라이언트 커스터마이징 (Client Customization)

자동 설정은 콜백 인터페이스를 통해 광범위한 클라이언트 스펙 커스터마이징 기능을 제공해요. 이 커스터마이저를 사용해 요청 타임아웃부터 이벤트 처리, 메시지 처리까지 MCP 클라이언트 동작의 다양한 측면을 구성할 수 있어요.

커스터마이징 타입 (Customization Types)

다음 커스터마이징 옵션을 사용할 수 있어요:

  • 요청 구성 (Request Configuration) - 커스텀 요청 타임아웃 설정

  • 커스텀 샘플링 핸들러 (Custom Sampling Handlers) - 서버가 클라이언트를 통해 LLM에서 LLM 샘플링(completions 또는 generations)을 요청하는 표준화된 방법. 이 흐름은 서버가 AI 기능을 활용할 수 있게 하면서 클라이언트가 모델 접근, 선택, 권한을 제어할 수 있게 해 줘요 — 서버 API 키가 필요 없어요.

  • 파일 시스템 (Roots) 접근 (File system (Roots) Access) - 클라이언트가 파일시스템 roots를 서버에 노출하는 표준화된 방법. Roots는 서버가 파일시스템에서 동작할 수 있는 경계를 정의해서, 접근할 수 있는 디렉터리와 파일을 파악하게 해 줘요. 서버는 지원하는 클라이언트에 roots 목록을 요청하고 그 목록이 바뀌면 알림을 받을 수 있어요.

  • Elicitation 핸들러 (Elicitation Handlers) - 서버가 상호작용 중에 클라이언트를 통해 사용자에게 추가 정보를 요청하는 표준화된 방법.

  • 이벤트 핸들러 (Event Handlers) - 특정 서버 이벤트가 발생했을 때 알림을 받는 클라이언트의 핸들러:

    • 도구 변경 알림 - 사용 가능한 서버 도구 목록이 바뀔 때
    • 리소스 변경 알림 - 사용 가능한 서버 리소스 목록이 바뀔 때
    • 프롬프트 변경 알림 - 사용 가능한 서버 프롬프트 목록이 바뀔 때
  • 로깅 핸들러 (Logging Handlers) - 서버가 구조화된 로그 메시지를 클라이언트에 보내는 표준화된 방법.

  • 진행 핸들러 (Progress Handlers) - 서버가 구조화된 진행 메시지를 클라이언트에 보내는 표준화된 방법. 클라이언트는 최소 로그 수준을 설정해 로깅 상세도를 제어할 수 있어요.

클라이언트 커스터마이징 예제 (Client Customization Example)

애플리케이션의 필요에 따라 동기 클라이언트용 McpCustomizer<McpClient.SyncSpec> 또는 비동기 클라이언트용 McpCustomizer<McpClient.AsyncSpec>을 구현할 수 있어요.

  • Sync
  • Async
@Component
public class CustomMcpSyncClientCustomizer implements McpClientCustomizer<McpClient.SyncSpec> {
    @Override
    public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {

        // Customize the request timeout configuration
        spec.requestTimeout(Duration.ofSeconds(30));

        // Sets the root URIs that this client can access.
        spec.roots(roots);

        // Sets a custom sampling handler for processing message creation requests.
        spec.sampling((CreateMessageRequest messageRequest) -> {
            // Handle sampling
            CreateMessageResult result = ...
            return result;
        });

        // Sets a custom elicitation handler for processing elicitation requests.
        spec.elicitation((ElicitRequest request) -> {
          // handle elicitation
          return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
        });

        // Adds a consumer to be notified when progress notifications are received.
        spec.progressConsumer((ProgressNotification progress) -> {
         // Handle progress notifications
        });

        // Adds a consumer to be notified when the available tools change, such as tools
        // being added or removed.
        spec.toolsChangeConsumer((List<McpSchema.Tool> tools) -> {
            // Handle tools change
        });

        // Adds a consumer to be notified when the available resources change, such as resources
        // being added or removed.
        spec.resourcesChangeConsumer((List<McpSchema.Resource> resources) -> {
            // Handle resources change
        });

        // Adds a consumer to be notified when the available prompts change, such as prompts
        // being added or removed.
        spec.promptsChangeConsumer((List<McpSchema.Prompt> prompts) -> {
            // Handle prompts change
        });

        // Adds a consumer to be notified when logging messages are received from the server.
        spec.loggingConsumer((McpSchema.LoggingMessageNotification log) -> {
            // Handle log messages
        });
    }
}
@Component
public class CustomMcpAsyncClientCustomizer implements McpClientCustomizer<McpClient.AsyncSpec> {
    @Override
    public void customize(String serverConfigurationName, McpClient.AsyncSpec spec) {
        // Customize the async client configuration
        spec.requestTimeout(Duration.ofSeconds(30));
    }
}

serverConfigurationName 파라미터는 커스터마이저가 적용되는(그리고 MCP 클라이언트가 생성되는) 서버 구성의 이름이에요. MCP 클라이언트 자동 설정은 애플리케이션 컨텍스트에서 발견된 모든 커스터마이저를 자동으로 감지하고 적용해요.

전송 지원 (Transport Support)

자동 설정은 여러 전송 타입을 지원해요:

  • 표준 I/O (Stdio) (spring-ai-starter-mcp-client와 spring-ai-starter-mcp-client-webflux에 의해 활성화)
  • (HttpClient) HTTP/SSE 및 Streamable-HTTP (spring-ai-starter-mcp-client에 의해 활성화)
  • (WebFlux) HTTP/SSE 및 Streamable-HTTP (spring-ai-starter-mcp-client-webflux에 의해 활성화)

도구 필터링 (Tool Filtering)

MCP Client Boot Starter는 McpToolFilter 인터페이스를 통해 발견된 도구의 필터링을 지원해요. 이를 통해 MCP 연결 정보나 도구 속성 같은 커스텀 기준에 따라 도구를 선택적으로 포함하거나 제외할 수 있어요.

도구 필터링을 구현하려면 McpToolFilter 인터페이스를 구현한 빈을 만드세요:

@Component
public class CustomMcpToolFilter implements McpToolFilter {

    @Override
    public boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool) {
        // Filter logic based on connection information and tool properties
        // Return true to include the tool, false to exclude it

        // Example: Exclude tools from a specific client
        if (connectionInfo.clientInfo().name().equals("restricted-client")) {
            return false;
        }

        // Example: Only include tools with specific names
        if (tool.name().startsWith("allowed_")) {
            return true;
        }

        // Example: Filter based on tool description or other properties
        if (tool.description() != null &&
            tool.description().contains("experimental")) {
            return false;
        }

        return true; // Include all other tools by default
    }
}

McpConnectionInfo 레코드는 다음에 접근을 제공해요:

  • clientCapabilities - MCP 클라이언트의 기능(capabilities)
  • clientInfo - MCP 클라이언트에 대한 정보 (이름과 버전)
  • initializeResult - MCP 서버의 초기화 결과

필터는 자동으로 감지되어 동기·비동기 MCP 도구 콜백 provider 모두에 적용돼요. 커스텀 필터가 없으면 기본적으로 모든 발견된 도구가 포함돼요.

참고: 애플리케이션 컨텍스트에는 McpToolFilter 빈을 하나만 정의해야 해요. 여러 필터가 필요하면 단일 복합(composite) 필터 구현으로 결합하세요.

도구 이름 프리픽스 생성 (Tool Name Prefix Generation)

MCP Client Boot Starter는 McpToolNamePrefixGenerator 인터페이스를 통해 커스터마이즈 가능한 도구 이름 프리픽스 생성을 지원해요. 이 기능은 여러 MCP 서버의 도구를 통합할 때 도구 이름에 고유한 프리픽스를 추가해 이름 충돌을 방지해 줘요.

기본적으로 커스텀 McpToolNamePrefixGenerator 빈이 없으면, starter는 모든 MCP 클라이언트 연결에서 도구 이름의 고유성을 보장하는 DefaultMcpToolNamePrefixGenerator를 사용해요. 기본 생성기는:

  • 모든 기존 연결과 도구 이름을 추적해 고유성을 보장해요.
  • 알파벳·숫자가 아닌 문자를 밑줄로 바꿔 도구 이름을 포맷해요 (예: my-tool → my_tool).
  • 서로 다른 연결에서 중복 도구 이름이 감지되면 카운터 프리픽스를 추가해요 (예: alt_1_toolName, alt_2_toolName).
  • 스레드 안전하며 멱등성을 유지해요 - 동일한 (client, server, tool) 조합은 항상 같은 고유 이름을 얻어요.
  • 최종 이름이 64자를 초과하지 않도록 보장해요 (필요하면 앞에서 잘라냄).

예를 들어:

  • 도구 search 첫 등장 → search
  • 다른 연결에서 온 도구 search 두 번째 등장 → alt_1_search
  • 특수 문자가 있는 도구 my-special-tool → my_special_tool

이 동작을 직접 구현으로 커스터마이즈할 수 있어요:

@Component
public class CustomToolNamePrefixGenerator implements McpToolNamePrefixGenerator {

    @Override
    public String prefixedToolName(McpConnectionInfo connectionInfo, Tool tool) {
        // Custom logic to generate prefixed tool names

        // Example: Use server name and version as prefix
        String serverName = connectionInfo.initializeResult().serverInfo().name();
        String serverVersion = connectionInfo.initializeResult().serverInfo().version();
        return serverName + "_v" + serverVersion.replace(".", "_") + "_" + tool.name();
    }
}

McpConnectionInfo 레코드는 MCP 연결에 대한 포괄적인 정보를 제공해요:

  • clientCapabilities - MCP 클라이언트의 기능
  • clientInfo - MCP 클라이언트에 대한 정보 (이름, 제목, 버전)
  • initializeResult - 서버 정보를 포함한 MCP 서버의 초기화 결과

내장 프리픽스 생성기 (Built-in Prefix Generators)

프레임워크는 몇 가지 내장 프리픽스 생성기를 제공해요:

  • DefaultMcpToolNamePrefixGenerator - 중복을 추적하고 필요할 때 카운터 프리픽스를 추가해 도구 이름의 고유성을 보장해요 (커스텀 빈이 없으면 기본적으로 사용됨).
  • McpToolNamePrefixGenerator.noPrefix() - 프리픽스 없이 도구 이름을 반환해요 (여러 서버가 같은 이름의 도구를 제공하면 충돌이 생길 수 있음).

프리픽싱을 아예 비활성화하고 원시 도구 이름을 사용하려면 (여러 MCP 서버를 사용한다면 권장하지 않아요), no-prefix 생성기를 빈으로 등록하세요:

@Configuration
public class McpConfiguration {

    @Bean
    public McpToolNamePrefixGenerator mcpToolNamePrefixGenerator() {
        return McpToolNamePrefixGenerator.noPrefix();
    }
}

프리픽스 생성기는 Spring의 ObjectProvider 메커니즘을 통해 자동으로 감지되어 동기·비동기 MCP 도구 콜백 provider 모두에 적용돼요. 커스텀 생성기 빈이 없으면 DefaultMcpToolNamePrefixGenerator가 자동으로 사용돼요.

참고: 여러 MCP 서버와 함께 McpToolNamePrefixGenerator.noPrefix()를 사용하면 중복 도구 이름이 IllegalStateException을 일으킬 거예요. 기본 DefaultMcpToolNamePrefixGenerator는 중복 도구 이름에 자동으로 고유 프리픽스를 추가해 이를 방지해요.

도구 컨텍스트를 MCP 메타로 변환 (Tool Context to MCP Meta Converter)

MCP Client Boot Starter는 ToolContextToMcpMetaConverter 인터페이스를 통해 Spring AI의 ToolContext를 MCP 도구 호출 메타데이터로 변환하는 것을 지원해요. 이 기능을 사용하면 LLM이 생성한 호출 인자와 함께 추가 컨텍스트 정보 (예: user id, secrets token)를 메타데이터로 전달할 수 있어요.

예를 들어 MCP progressToken을 도구 컨텍스트의 MCP Progress Flow에 전달해 장기 실행 작업의 진행을 추적할 수 있어요:

ChatModel chatModel = ...

String response = ChatClient.create(chatModel)
        .prompt("Tell me more about the customer with ID 42")
        .toolContext(Map.of("progressToken", "my-progress-token"))
        .call()
        .content();

기본적으로 커스텀 변환기 빈이 없으면, starter는 다음을 수행하는 ToolContextToMcpMetaConverter.defaultConverter()를 사용해요:

  • MCP exchange 키 (McpToolUtils.TOOL_CONTEXT_MCP_EXCHANGE_KEY)를 필터링
  • null 값이 있는 항목을 필터링
  • 그 외의 모든 컨텍스트 항목을 메타데이터로 통과시킴

이 동작을 직접 구현으로 커스터마이즈할 수 있어요:

@Component
public class CustomToolContextToMcpMetaConverter implements ToolContextToMcpMetaConverter {

    @Override
    public Map<String, Object> convert(ToolContext toolContext) {
        if (toolContext == null || toolContext.getContext() == null) {
            return Map.of();
        }

        // Custom logic to convert tool context to MCP metadata
        Map<String, Object> metadata = new HashMap<>();

        // Example: Add custom prefix to all keys
        for (Map.Entry<String, Object> entry : toolContext.getContext().entrySet()) {
            if (entry.getValue() != null) {
                metadata.put("app_" + entry.getKey(), entry.getValue());
            }
        }

        // Example: Add additional metadata
        metadata.put("timestamp", System.currentTimeMillis());
        metadata.put("source", "spring-ai");

        return metadata;
    }
}

내장 변환기 (Built-in Converters)

프레임워크는 내장 변환기를 제공해요:

  • ToolContextToMcpMetaConverter.defaultConverter() - MCP exchange 키와 null 값을 필터링해요 (커스텀 빈이 없으면 기본적으로 사용됨).
  • ToolContextToMcpMetaConverter.noOp() - 빈 맵을 반환해 컨텍스트-메타데이터 변환을 사실상 비활성화해요.

컨텍스트-메타데이터 변환을 완전히 비활성화하려면:

@Configuration
public class McpConfiguration {

    @Bean
    public ToolContextToMcpMetaConverter toolContextToMcpMetaConverter() {
        return ToolContextToMcpMetaConverter.noOp();
    }
}

변환기는 Spring의 ObjectProvider 메커니즘을 통해 자동으로 감지되어 동기·비동기 MCP 도구 콜백 모두에 적용돼요. 커스텀 변환기 빈이 없으면 기본 변환기가 자동으로 사용돼요.

MCP ToolCallback 자동 설정 비활성화 (Disable the MCP ToolCallback Auto-Configuration)

MCP ToolCallback 자동 설정은 기본적으로 활성화돼 있지만, spring.ai.mcp.client.toolcallback.enabled=false 프로퍼티로 비활성화할 수 있어요. 비활성화하면 사용 가능한 MCP 도구에서 ToolCallbackProvider 빈이 생성되지 않아요.

MCP 클라이언트 어노테이션 (MCP Client Annotations)

MCP Client Boot Starter는 다양한 MCP 클라이언트 작업을 처리하기 위한 어노테이션된 메서드를 자동으로 감지하고 등록해요:

  • @McpLogging - MCP 서버의 로깅 메시지 알림 처리
  • @McpSampling - LLM 완성을 위한 MCP 서버의 샘플링 요청 처리
  • @McpElicitation - 사용자에게 추가 정보를 수집하기 위한 elicitation 요청 처리
  • @McpProgress - 장기 실행 작업의 진행 알림 처리
  • @McpToolListChanged - 서버의 도구 목록이 바뀔 때 알림 처리
  • @McpResourceListChanged - 서버의 리소스 목록이 바뀔 때 알림 처리
  • @McpPromptListChanged - 서버의 프롬프트 목록이 바뀔 때 알림 처리

사용 예시:

@Component
public class McpClientHandlers {

    @McpLogging(clients = "server1")
    public void handleLoggingMessage(LoggingMessageNotification notification) {
        System.out.println("Received log: " + notification.level() +
                          " - " + notification.data());
    }

    @McpSampling(clients = "server1")
    public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
        // Process the request and generate a response
        String response = generateLLMResponse(request);

        return CreateMessageResult.builder(Role.ASSISTANT, response, "gpt-4")
            .build();
    }

    @McpProgress(clients = "server1")
    public void handleProgressNotification(ProgressNotification notification) {
        double percentage = notification.progress() * 100;
        System.out.println(String.format("Progress: %.2f%% - %s",
            percentage, notification.message()));
    }

    @McpToolListChanged(clients = "server1")
    public void handleToolListChanged(List<McpSchema.Tool> updatedTools) {
        System.out.println("Tool list updated: " + updatedTools.size() + " tools available");
        // Update local tool registry
        toolRegistry.updateTools(updatedTools);
    }
}

어노테이션은 동기·비동기 구현을 모두 지원하며, clients 파라미터로 특정 클라이언트에 대해 구성할 수 있어요:

@McpLogging(clients = "server1")
public void handleServer1Logs(LoggingMessageNotification notification) {
    // Handle logs from specific server
    logToFile("server1.log", notification);
}

@McpSampling(clients = "server1")
public Mono<CreateMessageResult> handleAsyncSampling(CreateMessageRequest request) {
    return Mono.fromCallable(() -> {
        String response = generateLLMResponse(request);
        return CreateMessageResult.builder(Role.ASSISTANT, response, "gpt-4")
            .build();
    }).subscribeOn(Schedulers.boundedElastic());
}

사용 가능한 모든 어노테이션과 사용 패턴에 대한 자세한 정보는 MCP Client Annotations 문서를 참고하세요.

사용 예시 (Usage Example)

프로젝트에 적절한 starter 의존성을 추가하고 application.properties나 application.yml에서 클라이언트를 구성하세요:

spring:
  ai:
    mcp:
      client:
        enabled: true
        name: my-mcp-client
        version: 1.0.0
        request-timeout: 30s
        type: SYNC  # or ASYNC for reactive applications
        sse:
          connections:
            server1:
              url: http://localhost:8080
            server2:
              url: http://otherserver:8081
        streamable-http:
          connections:
            server3:
              url: http://localhost:8083
              endpoint: /mcp
        stdio:
          root-change-notification: false
          connections:
            server1:
              command: /path/to/server
              args:
                - --port=8080
                - --mode=production
              env:
                API_KEY: your-api-key
                DEBUG: "true"

MCP 클라이언트 빈은 자동으로 구성되어 주입 가능해요:

@Autowired
private List<McpSyncClient> mcpSyncClients;  // For sync client

// OR

@Autowired
private List<McpAsyncClient> mcpAsyncClients;  // For async client

도구 콜백이 활성화되면(기본 동작), 모든 MCP 클라이언트와 함께 등록된 MCP 도구가 ToolCallbackProvider 인스턴스로 제공돼요:

@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();

예제 애플리케이션 (Example Applications)

추가 자료 (Additional Resources)

더 알아보기 (Learn more)