읽기만 하는 AI는 그만! Spring AI와 MCP로 파일 조작부터 메일 발송까지 실현하기

궁금하면 500원·2025년 12월 21일

AI 미생지능

목록 보기
75/90

이전 게시글에서 우리는 검색엔진을 통합하여 대규모 모델이 인터넷에서 실시간 정보를 획득할 수 있도록 함으로써 "세상을 향해 눈을 뜨게" 하는 데 성공했습니다. 하지만 RAG와 웹 검색은 기본적으로 AI가 더 많은 책을 "읽고" 더 많은 정보를 습득하도록 도와줍니다.
진정한 지능형 비서는 단순히 "읽는" 것뿐만 아니라 "쓰고" " 실행 하는 " 능력도 갖춰야 합니다 .
다음과 같은 상황을 상상해 보세요. 인공지능이 오늘의 주식 시장 데이터를 정리하고 바탕화면에 저장할 엑셀 보고서를 생성해 주거나, 고객에게 회의 초대 이메일을 보내는 데 도움을 주기를 원합니다.
현재의 아키텍처에서는 대형 모델이 "이메일 내용이 준비되었습니다. 복사해서 붙여넣어 보내세요."라고만 알려줄 수 있습니다. 마치 유리병에 갇힌 "슈퍼 두뇌"처럼, 학습은 되었지만 현실 세계와 연결되지 못하는 것입니다.

이러한 장벽을 허물기 위해 MCP(모델 컨텍스트 프로토콜)를 도입해야 합니다 . MCP의 개념에 대해서는 이미 온라인에 많은 자료가 있으므로 여기서는 자세히 설명하지 않겠습니다. 더 자세한 내용은 MCP 문서를 참조하십시오.
이 글은 두 부분으로 나뉘는데, 첫 번째 부분은 실제 MCP 구현에 관한 내용입니다.

1.클라이언트로서 : 기존 MCP 서비스를 호출합니다.

2.서버로서 : 자체 MCP 서비스를 개발합니다.

MCP 로컬 파일 조작 서비스 호출

Spring AI는 spring-ai-mcp-client애플리케이션이 MCP 표준을 준수하는 모든 서버에 연결할 수 있는 기능을 제공합니다.
여기서는 AI가 로컬에서 파일을 생성하고 읽을 수 있도록 공식 파일 시스템 MCP 서버를 예로 들어 설명합니다.

필수 조건 : 파일 시스템 MCP 서비스는
Node.js를 기반으로 하므로 로컬 환경에 Node.js(버전 18 이상)가 설치되어
있는지 확인하십시오.

1. 종속성 추가

문서 에 pom.xml 클라이언트모듈 관련 종속성을 소개합니다 .

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

2. MCP 서버 연결 구성

{
  "mcpServers": {
    "filesystem": {
      "command": "D:\\devolop\\node\\npx.cmd",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "D:\\devolop"
      ]
    }
  }
}

구성 분석
command: 실행 파일의 경로를 가리킵니다 npx(일반적으로 Windows에서 npx.cmd).
args:
-y자동 설치 확인.
@modelcontextprotocol/server-filesystem공식 파일 시스템 MCP 서비스 패키지입니다.
D:\devolop이곳은 AI가 접근할 수 있는 루트 디렉토리입니다 .
보안상의 이유로 AI는 이 디렉토리와 하위 디렉토리에 있는 파일만 처리할 수 있습니다.

다음으로, application.yml에서 MCP 클라이언트를 활성화하고 위의 구성을 로드합니다.

spring:
  ai:
    mcp:
      client:
        enabled: true
        name: spring-ai-mcp-client
        type: ASYNC # 비동기 논블로킹 모드 사용 권장
        stdio:
          servers-configuration: classpath:mcp-server.json

3. 코드수정

ChatServiceMCP 클라이언트가 발견한 도구를 ChatClient초기화 과정에서 시스템 에 등록 해야 합니다 .

public ChatServiceImpl(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {
    // tools는 설정된 모든 MCP 도구를 자동으로 주입합니다
    this.chatClient = chatClientBuilder
            .defaultToolCallbacks(tools)
            .build();
}

4. 효과테스트

STDERR Message received: Secure MCP Filesystem Server running on stdio
Server response with Protocol: 2024-11-05, Capabilities: ...
STDERR Message received: Client does not support MCP Roots, using allowed directories set from server args: ['D:\\develop']
Tomcat started on port 8080 (http) with context path '/'
Started Application in 7.xxx seconds

설정한 디렉토리를 열어보면 D:\devolop파일이 성공적으로 생성된 것을 확인할 수 있습니다

MCP 서비스 이메일 및 시간 관리 도구 개발

멀티 모듈 mcp-server구성 pom.xml에 필요한 종속성을 추가합니다 .
WebFlux 외에도 spring-boot-starter-mail이메일을 전송하고
flexmarkAI가 생성한 마크다운 콘텐츠를 이메일에 적합한 HTML로 변환해야 합니다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.cc</groupId>
        <artifactId>SpringAI-MCP-RAG-Dev</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>mcp-server</artifactId>

    <properties>
        <!-- Java 컴파일 버전 -->
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>

        <!-- 소스 인코딩 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Spring AI BOM 관리 -->
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

1.구성추가

설정 에서 application.ymlOpenAI 키, Redis및 SMTP 이메일 서비스 정보를 구성하십시오 .

spring:
  application:
    name: spring-ai-mcp-server

  data:
    redis:
      host: 127.0.0.1
      port: 9379
      password: 123456

  ai:
    mcp:
      server:
        name: spring-ai-mcp-server-sse
        version: 1.0.0
        sse-endpoint: /sse
        type: async

  mail:
    host: smtp.163.com
    port: 465
    username: 123@163.com
    password: 123456  # 주의: 일반 로그인 비밀번호가 아니라 이메일 서비스에서 발급한 인증 코드입니다
    protocol: smtp
    default-encoding: UTF-8
    properties:
      mail:
        smtp:
          socketFactory:
            port: 465
            class: javax.net.ssl.SSLSocketFactory
          ssl:
            enable: true

logging:
  level:
    root: info

server:
  port: 6080

프로젝트를 시작하고 접속하세요 http://localhost:6080/sse. 정상적으로 시작되면 설정이 올바른 것입니다.

2. 개발 시간 조회 도구

대형 모델 자체에는 "지금 몇 시인지"에 대한 개념이 없으므로, 이를 위한 도구를 제공해야 합니다.

package com.sleekydz86.mcp;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
@Slf4j
public class DateTool {

    // @Tool 애노테이션은 메서드를 MCP 도구로 노출합니다
    // description은 매우 중요하며, LLM은 이를 기준으로 언제 이 도구를 호출할지 판단합니다
    @Tool(description = "현재 시간을 가져옵니다")
    public String getCurrentTime() {
        log.info("================= MCP 도구 호출: 현재 시간 가져오기 =================");
        return String.format(
                "현재 시간은 %s 입니다",
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
        );
    }

}

3. 이메일 발송 도구를 개발하세요

이것은 매개변수 구조를 정의해야 하는 약간 더 복잡한 도구입니다.

package com.sleekydz86.mcp;

import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.data.MutableDataSet;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class EmailTool {

    private final JavaMailSender mailSender;
    private final String from;

    @Autowired
    private EmailTool(JavaMailSender mailSender, @Value("${spring.mail.username}") String from) {
        this.mailSender = mailSender;
        this.from = from;
    }

    // 요청 파라미터 클래스 정의
    // LLM이 이 필드들을 자동으로 채웁니다
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    public static class EmailRequest {

        @ToolParam(description = "수신자 이메일 주소")
        private String email;

        @ToolParam(description = "이메일 제목")
        private String subject;

        @ToolParam(description = "이메일 본문 내용")
        private String message;

        @ToolParam(description = "이메일 내용 타입 (1: HTML 형식, 2: 일반 텍스트 형식)")
        private Integer contentType;
    }

    @Tool(description = "지정한 이메일 주소로 메일을 전송합니다.")
    public String sendEmail(EmailRequest emailRequest) {
        log.info("================= MCP 도구 호출: sendEmail =================");
        log.info("요청 상세 정보: {}", emailRequest);

        Integer contentType = emailRequest.getContentType();

        try {
            MimeMessage mimeMessage = mailSender.createMimeMessage();
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);

            mimeMessageHelper.setFrom(from);
            mimeMessageHelper.setTo(emailRequest.getEmail());
            mimeMessageHelper.setSubject(emailRequest.getSubject());

            // 스마트 처리: Markdown 형식이면 자동으로 HTML로 변환
            if (contentType != null && contentType == 1) {
                mimeMessageHelper.setText(
                        convertMarkdownToHtml(emailRequest.getMessage()),
                        true
                );
            } else if (contentType != null && contentType == 2) {
                mimeMessageHelper.setText(emailRequest.getMessage(), true);
            } else {
                // 기본 처리
                mimeMessageHelper.setText(emailRequest.getMessage());
            }

            mailSender.send(mimeMessage);
            return "이메일 전송 성공";

        } catch (MessagingException e) {
            log.error("이메일 전송 실패", e);
            return "이메일 전송 실패: " + e.getMessage();
        }
    }

    /**
     * Markdown 형식 문자열을 HTML 형식으로 변환
     */
    public static String convertMarkdownToHtml(String markdownStr) {
        MutableDataSet dataSet = new MutableDataSet();
        Parser parser = Parser.builder(dataSet).build();
        HtmlRenderer htmlRenderer = HtmlRenderer.builder(dataSet).build();
        return htmlRenderer.render(parser.parse(markdownStr));
    }
}

4. 등록 도구

마지막으로, 시작 클래스 또는 구성 클래스에서 우리가 작성한 툴 빈을 등록합니다

ToolCallbackProvider.

package com.sleekydz86.mcp;

import com.sleekydz86.mcp.tool.DateTool;
import com.sleekydz86.mcp.tool.EmailTool;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {

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

    /**
     * 사용자 정의 MCP 도구 등록
     * 이렇게 하면 ChatClient가 이 도구들의 존재를 인식할 수 있습니다
     */
    @Bean
    public ToolCallbackProvider registerMCPTools(DateTool dateTool, EmailTool emailTool) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(dateTool, emailTool)
                .build();
    }
}

5. 종합 시험

프로젝트를 시작하고 로그를 확인하여 도구가 제대로 로드되었는지 확인하십시오.

nged=true], tools=ToolCapabilities[listChanged=true]], Info: Implementation**[name=spring-ai-mcp-server-sse, version=1.0.0]** and Instructions null

] and Instructions null

테스트 시간 쿼리

이메일 전송 테스트

대화를 더욱 일관성 있게 만들어줍니다

실제로 우리는 작업을 완료하기 위해 여러 차례의 대화를 진행할 수 있습니다.
하지만 기본적으로 대화는 ChatClient상태를 저장하지 않습니다.
즉, 이전 문장에서 무슨 말이 오갔는지 기억하지 않습니다.

이 문제를 해결하기 위해 Chat Memory를 도입해야 합니다 .
ChatServiceImpl<context:>에 Chat Memory를 주입 하고
< context:> ChatMemory에서 설정하세요 .

모델이 설정되면 컨텍스트 메모리 기능을 갖추게 되어 여러 단계의 의도 확인을 원활하게 처리할 수 있습니다.

정리하며

이 글을 통해 우리는 Spring AI 애플리케이션의 기능에 있어 큰 도약을 이루었습니다.
단순한 "정보 수집기"에서 "작업 실행기"로 진화한 것입니다.

MCP 클라이언트를 사용하여 기존 파일 시스템 서비스를 손쉽게 통합했습니다.
저희는 Spring AI Tool을 사용하여 맞춤형 이메일 및 시간 관리 서비스를 개발했습니다 .
Chat Memory를 사용함으로써 AI에 기억 기능을 부여하여 더욱 자연스러운 상호 작용을 가능하게 했습니다.

이제 AI는 파일을 조작하고 이메일을 보낼 수 있습니다.
하지만 기업용 애플리케이션에서 가장 중요한 데이터는 대개 데이터베이스에 저장됩니다.
대규모 모델이 데이터베이스를 안전하고 정확하게 조회하고 조작할 수 있도록 하려면 어떻게 해야 할까요?
다음 글에서는 MCP를 활용한 대규모 모델-데이터베이스 상호작용 개발에 대해 포스팅 해보겠습니다 .

profile
그냥 코딩할래요 재미있어요

0개의 댓글