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

이러한 장벽을 허물기 위해 MCP(모델 컨텍스트 프로토콜)를 도입해야 합니다 . MCP의 개념에 대해서는 이미 온라인에 많은 자료가 있으므로 여기서는 자세히 설명하지 않겠습니다. 더 자세한 내용은 MCP 문서를 참조하십시오.
이 글은 두 부분으로 나뉘는데, 첫 번째 부분은 실제 MCP 구현에 관한 내용입니다.
Spring AI는 spring-ai-mcp-client애플리케이션이 MCP 표준을 준수하는 모든 서버에 연결할 수 있는 기능을 제공합니다.
여기서는 AI가 로컬에서 파일을 생성하고 읽을 수 있도록 공식 파일 시스템 MCP 서버를 예로 들어 설명합니다.
필수 조건 : 파일 시스템 MCP 서비스는
Node.js를 기반으로 하므로 로컬 환경에 Node.js(버전 18 이상)가 설치되어
있는지 확인하십시오.
문서 에 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>
{
"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
ChatServiceMCP 클라이언트가 발견한 도구를 ChatClient초기화 과정에서 시스템 에 등록 해야 합니다 .
public ChatServiceImpl(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {
// tools는 설정된 모든 MCP 도구를 자동으로 주입합니다
this.chatClient = chatClientBuilder
.defaultToolCallbacks(tools)
.build();
}
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-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>
설정 에서 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. 정상적으로 시작되면 설정이 올바른 것입니다.

대형 모델 자체에는 "지금 몇 시인지"에 대한 개념이 없으므로, 이를 위한 도구를 제공해야 합니다.
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"))
);
}
}
이것은 매개변수 구조를 정의해야 하는 약간 더 복잡한 도구입니다.
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));
}
}
마지막으로, 시작 클래스 또는 구성 클래스에서 우리가 작성한 툴 빈을 등록합니다
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();
}
}
프로젝트를 시작하고 로그를 확인하여 도구가 제대로 로드되었는지 확인하십시오.
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를 활용한 대규모 모델-데이터베이스 상호작용 개발에 대해 포스팅 해보겠습니다 .