์๋ ํ์ธ์ dev_writer์ ๋๋ค.
์ด๋ฒ ์๊ฐ์๋ Spring AI ๊ณต์ ๋ฌธ์ ์ค MCP์ ๋ํด ๋ฒ์ญํ ๋ด์ฉ์ ์ ๋ฌํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
Model Context Protocol (MCP)
๋ชจ๋ธ ์ปจํ ์คํธ ํ๋กํ ์ฝ(Model Context Protocol, MCP)์ AI ๋ชจ๋ธ์ด ์ธ๋ถ ๋๊ตฌ ๋ฐ ๋ฆฌ์์ค์ ๊ตฌ์กฐํ๋ ๋ฐฉ์์ผ๋ก ์ํธ์์ฉํ ์ ์๋๋ก ํด์ฃผ๋ ํ์คํ๋ ํ๋กํ ์ฝ์ ๋๋ค. ๋ค์ํ ํ๊ฒฝ์์ ์ ์ฐ์ฑ์ ์ ๊ณตํ๊ธฐ ์ํด ์ฌ๋ฌ ์ ์ก ๋ฉ์ปค๋์ฆ์ ์ง์ํฉ๋๋ค.
MCP Java SDK๋ ๋ชจ๋ธ ์ปจํ ์คํธ ํ๋กํ ์ฝ์ Java ๊ตฌํ์ฒด๋ก, ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ํต์ ๋ฐฉ์์ ํตํด AI ๋ชจ๋ธ๊ณผ ๋๊ตฌ ๊ฐ์ ํ์คํ๋ ์ํธ์์ฉ์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
Spring AI MCP๋ MCP Java SDK์ Spring Boot ํตํฉ ๊ธฐ๋ฅ์ ํ์ฅํ ๊ฒ์ผ๋ก, ํด๋ผ์ด์ธํธ ๋ฐ ์๋ฒ ์คํํฐ๋ฅผ ๋ชจ๋ ์ ๊ณตํฉ๋๋ค. Spring Initializer๋ฅผ ์ฌ์ฉํด MCP ์ง์์ด ํฌํจ๋ AI ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ํ๊ฒ ๋ถํธ์คํธ๋ฉํ ์ ์์ต๋๋ค.
์ฐธ๊ณ
MCP Java SDK 0.8.0์ ์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ โ ๏ธ MCP Java SDK 0.8.0 ๋ฒ์ ์์๋ ์ธ์ ๊ธฐ๋ฐ ์ํคํ ์ฒ ๋ฑ ์ฌ๋ฌ ๊ฐ์ง ์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ์ด ๋์ ๋์์ต๋๋ค. Java SDK 0.7.0์์ ์ ๊ทธ๋ ์ด๋ํ๋ ๊ฒฝ์ฐ, ์์ธํ ๋ด์ฉ์ ๋ง์ด๊ทธ๋ ์ด์ ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํ์๊ธฐ ๋ฐ๋๋๋ค.
MCP Java SDK ์ํคํ ์ฒ
ํ
์ด ์น์ ์ MCP Java SDK ์ํคํ ์ฒ์ ๋ํ ๊ฐ์๋ฅผ ์ ๊ณตํฉ๋๋ค. Spring AI MCP ํตํฉ์ ๋ํ ๋ด์ฉ์ Spring AI MCP Boot Starters ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
Java MCP ๊ตฌํ์ 3 ๊ณ์ธต ์ํคํ ์ฒ๋ฅผ ๋ฐ๋ฆ ๋๋ค:
- ํด๋ผ์ด์ธํธ/์๋ฒ ๊ณ์ธต (Client/Server Layer): McpClient๋ ํด๋ผ์ด์ธํธ ์ธก ์์ ์ ์ฒ๋ฆฌํ๊ณ , McpServer๋ ์๋ฒ ์ธก ํ๋กํ ์ฝ ์์ ์ ๊ด๋ฆฌํฉ๋๋ค. ๋ ๊ตฌ์ฑ ์์ ๋ชจ๋ ํต์ ๊ด๋ฆฌ๋ฅผ ์ํด McpSession์ ์ฌ์ฉํฉ๋๋ค.
- ์ธ์ ๊ณ์ธต (Session Layer - McpSession): DefaultMcpSession ๊ตฌํ์ฒด๋ฅผ ํตํด ํต์ ํจํด๊ณผ ์ํ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
- ์ ์ก ๊ณ์ธต (Transport Layer - McpTransport): JSON-RPC ๋ฉ์์ง์ ์ง๋ ฌํ ๋ฐ ์ญ์ง๋ ฌํ๋ฅผ ์ฒ๋ฆฌํ๋ฉฐ, ๋ค์ํ ์ ์ก ๋ฐฉ์ ๊ตฌํ์ ์ง์ํฉ๋๋ค.
MCP ํด๋ผ์ด์ธํธ
MCP ํด๋ผ์ด์ธํธ(MCP Client)๋ ๋ชจ๋ธ ์ปจํ ์คํธ ํ๋กํ ์ฝ(MCP) ์ํคํ ์ฒ์ ํต์ฌ ๊ตฌ์ฑ ์์๋ก, MCP ์๋ฒ์์ ์ฐ๊ฒฐ์ ์ค์ ํ๊ณ ๊ด๋ฆฌํ๋ ์ญํ ์ ํฉ๋๋ค. ์ด ๊ตฌ์ฑ ์์๋ ํ๋กํ ์ฝ์ ํด๋ผ์ด์ธํธ ์ธก์ ๊ตฌํํ๋ฉฐ ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ๋ด๋นํฉ๋๋ค:
- ์๋ฒ์์ ํ๋กํ ์ฝ ๋ฒ์ ํ์์ ํตํด ํธํ์ฑ ๋ณด์ฅ
- ์ฌ์ฉ ๊ฐ๋ฅํ ๊ธฐ๋ฅ์ ํ์ธํ๊ธฐ ์ํ ๊ธฐ๋ฅ ํ์(Capability Negotiation)
- ๋ฉ์์ง ์ ์ก ๋ฐ JSON-RPC ํต์ ์ฒ๋ฆฌ
- ํด(๋๊ตฌ) ๊ฒ์ ๋ฐ ์คํ
- ๋ฆฌ์์ค ์ ๊ทผ ๋ฐ ๊ด๋ฆฌ
- ํ๋กฌํํธ ์์คํ
๊ณผ์ ์ํธ์์ฉ ์ ํ์ ๊ธฐ๋ฅ:
- ๋ฃจํธ(Roots) ๊ด๋ฆฌ
- ์ํ๋ง(Sampling) ๊ธฐ๋ฅ ์ง์
- ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ง์
- ์ง์ ์ ์ก ๋ฐฉ์:
- ํ๋ก์ธ์ค ๊ธฐ๋ฐ ํต์ ์ ์ํ Stdio ๊ธฐ๋ฐ ์ ์ก ๋ฐฉ์
- Java HttpClient ๊ธฐ๋ฐ SSE ํด๋ผ์ด์ธํธ ์ ์ก
- WebFlux SSE ํด๋ผ์ด์ธํธ ์ ์ก – ๋ฆฌ์กํฐ๋ธ HTTP ์คํธ๋ฆฌ๋ฐ์ฉ
MCP ์๋ฒ
MCP ์๋ฒ(MCP Server)๋ ๋ชจ๋ธ ์ปจํ ์คํธ ํ๋กํ ์ฝ(MCP) ์ํคํ ์ฒ์์ ๊ธฐ์ด์ ์ธ ๊ตฌ์ฑ ์์๋ก, ํด๋ผ์ด์ธํธ์๊ฒ ๋๊ตฌ, ๋ฆฌ์์ค, ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด ๊ตฌ์ฑ ์์๋ ํ๋กํ ์ฝ์ ์๋ฒ ์ธก์ ๊ตฌํํ๋ฉฐ ๋ค์๊ณผ ๊ฐ์ ์ญํ ์ ์ํํฉ๋๋ค:
- ์๋ฒ ์ธก ํ๋กํ ์ฝ ์์
์ฒ๋ฆฌ
- ํด(๋๊ตฌ)์ ๋ ธ์ถ ๋ฐ ๊ฒ์ ์ง์
- URI ๊ธฐ๋ฐ ์ ๊ทผ์ ํตํ ๋ฆฌ์์ค ๊ด๋ฆฌ
- ํ๋กฌํํธ ํ ํ๋ฆฟ ์ ๊ณต ๋ฐ ์ฒ๋ฆฌ
- ํด๋ผ์ด์ธํธ์์ ๊ธฐ๋ฅ ํ์(Capability Negotiation)
- ๊ตฌ์กฐํ๋ ๋ก๊น ๋ฐ ์๋ฆผ(Notification) ์ง์
- ๋์ ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ ๊ด๋ฆฌ
- ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ API ์ง์
- ์ง์ ์ ์ก ๋ฐฉ์:
- Stdio ๊ธฐ๋ฐ ์ ์ก ๋ฐฉ์ – ํ๋ก์ธ์ค ๊ธฐ๋ฐ ํต์ ์ฉ
- Servlet ๊ธฐ๋ฐ SSE ์๋ฒ ์ ์ก
- WebFlux SSE ์๋ฒ ์ ์ก – ๋ฆฌ์กํฐ๋ธ HTTP ์คํธ๋ฆฌ๋ฐ์ฉ
- WebMVC SSE ์๋ฒ ์ ์ก – ์๋ธ๋ฆฟ ๊ธฐ๋ฐ HTTP ์คํธ๋ฆฌ๋ฐ์ฉ
์ ์์ค MCP ํด๋ผ์ด์ธํธ/์๋ฒ API๋ฅผ ์ด์ฉํ ์์ธ ๊ตฌํ ๊ฐ์ด๋๋ MCP Java SDK ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๋ณด๋ค ๊ฐํธํ ์ค์ ์ ์ํ๋ค๋ฉด, ์๋์ ์ค๋ช ๋ Spring Boot ๊ธฐ๋ฐ MCP Boot Starters๋ฅผ ์ฌ์ฉํ์ธ์.
Spring AI MCP ํตํฉ
Spring AI๋ ๋ค์๊ณผ ๊ฐ์ Spring Boot ์คํํฐ๋ฅผ ํตํด MCP ํตํฉ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
ํด๋ผ์ด์ธํธ ์คํํฐ (Client Starters)
- spring-ai-starter-mcp-client
- STDIO ๋ฐ HTTP ๊ธฐ๋ฐ SSE ์ง์์ ์ ๊ณตํ๋ ํต์ฌ ํด๋ผ์ด์ธํธ ์คํํฐ
- spring-ai-starter-mcp-client-webflux
- WebFlux ๊ธฐ๋ฐ SSE ์ ์ก ๊ตฌํ
์๋ฒ ์คํํฐ (Server Starters)
- spring-ai-starter-mcp-server STDIO ์ ์ก์ ์ง์ํ๋ ํต์ฌ ์๋ฒ ์คํํฐ
- spring-ai-starter-mcp-server-webmvc Spring MVC ๊ธฐ๋ฐ SSE ์ ์ก ๊ตฌํ
- spring-ai-starter-mcp-server-webflux WebFlux ๊ธฐ๋ฐ SSE ์ ์ก ๊ตฌํ
์ถ๊ฐ ์๋ฃ
- MCP ํด๋ผ์ด์ธํธ Boot ์คํํฐ ๋ฌธ์
- MCP ์๋ฒ Boot ์คํํฐ ๋ฌธ์
- MCP ์ ํธ๋ฆฌํฐ ๋ฌธ์
- Model Context Protocol ๋ช ์ธ์
MCP ํด๋ผ์ด์ธํธ Boot ์คํํฐ
Spring AI MCP (Model Context Protocol) ํด๋ผ์ด์ธํธ Boot ์คํํฐ๋ Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์์ MCP ํด๋ผ์ด์ธํธ ๊ธฐ๋ฅ์ ์๋์ผ๋ก ๊ตฌ์ฑํด ์ฃผ๋ ๋ชจ๋์ ๋๋ค. ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ํด๋ผ์ด์ธํธ ๊ตฌํ์ ๋ชจ๋ ์ง์ํ๋ฉฐ, ๋ค์ํ ์ ์ก ๋ฐฉ์ ์ต์ ์ ์ ๊ณตํฉ๋๋ค.
MCP ํด๋ผ์ด์ธํธ Boot ์คํํฐ๋ ๋ค์์ ์ ๊ณตํฉ๋๋ค:
- ๋ณต์์ ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ๊ด๋ฆฌ
- (ํ์ฑํ๋ ๊ฒฝ์ฐ) ์๋ ํด๋ผ์ด์ธํธ ์ด๊ธฐํ
- ์ด๋ฆ ๊ธฐ๋ฐ ์ ์ก ๋ฐฉ์(multiple named transports) ์ง์
- Spring AI ํด ์คํ ํ๋ ์์ํฌ์ ํตํฉ
- ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ ์ข ๋ฃ ์ ์์ ์๋ ์ ๋ฆฌ๋ฅผ ํฌํจํ ์ ์ ํ ์๋ช ์ฃผ๊ธฐ ๊ด๋ฆฌ
- ์ปค์คํฐ๋ง์ด์ (Customizer)๋ฅผ ํตํ ํด๋ผ์ด์ธํธ ์์ฑ ๋ฐฉ์์ ์ฌ์ฉ์ ์ ์ ๊ฐ๋ฅ
์คํํฐ
์ฐธ๊ณ
์คํ๋ง AI ์๋ ๊ตฌ์ฑ, ์คํํฐ ๋ชจ๋์ ์ํฐํฉํธ ์ด๋ฆ์ ํฐ ๋ณํ๊ฐ ์์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ ์ ๊ทธ๋ ์ด๋ ๋ ธํธ๋ฅผ ์ฐธ์กฐํ์ธ์.
ํ์ค MCP ํด๋ผ์ด์ธํธ
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
Webflux ํด๋ผ์ด์ธํธ
WebFlux ์คํํฐ๋ ํ์ค ์คํํฐ์ ์ ์ฌํ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง, WebFlux ๊ธฐ๋ฐ SSE ์ ์ก ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
์ค์ ์์ฑ (Configuration Properties)
๊ณตํต ์์ฑ (Common Properties)
๊ณตํต ์์ฑ์ ๋ค์๊ณผ ๊ฐ์ ์ ๋์ฌ๋ก ์์ํฉ๋๋ค: spring.ai.mcp.client
์์ฑ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
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 |
Stdio ์ ์ก ์ค์ ์์ฑ (Stdio Transport Properties)
Standard I/O ์ ์ก ๋ฐฉ์์ ๋ํ ์์ฑ์ spring.ai.mcp.client.stdio ์ ๋์ฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค:
์์ฑ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
servers-configuration | MCP ์๋ฒ ๊ตฌ์ฑ์ด ๋ด๊ธด JSON ํ์์ ๋ฆฌ์์ค | - |
connections | ์ด๋ฆ์ด ์ง์ ๋ stdio ์ฐ๊ฒฐ ์ค์ ์ Map | - |
connections.[name].command | MCP ์๋ฒ๋ฅผ ์คํํ ๋ช ๋ น์ด | - |
connections.[name].args | ๋ช ๋ น์ด์ ์ ๋ฌํ ์ธ์ ๋ชฉ๋ก | - |
connections.[name].env | ์๋ฒ ํ๋ก์ธ์ค ์คํ ์ ์ฌ์ฉํ ํ๊ฒฝ ๋ณ์(Map ํํ) | - |
์์ ๊ตฌ์ฑ:
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"
]
}
}
}
ํ์ฌ Claude Desktop ํ์์ STDIO ์ฐ๊ฒฐ ๋ฐฉ์๋ง ์ง์ํฉ๋๋ค.
SSE ์ ์ก ์ค์ ์์ฑ (SSE Transport Properties)
Server-Sent Events (SSE) ์ ์ก ๋ฐฉ์์ ๋ํ ์ค์ ์ spring.ai.mcp.client.sse ์ ๋์ฌ๋ก ์์ํฉ๋๋ค.
์์ฑ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
connections | ์ด๋ฆ์ด ์ง์ ๋ SSE ์ฐ๊ฒฐ ์ค์ ๋ค์ Map | - |
connections.[name].url | MCP ์๋ฒ์ SSE ํต์ ์ ์ํ ๊ธฐ๋ณธ URL ์๋ํฌ์ธํธ | - |
connections.[name].sse-endpoint | ์ฐ๊ฒฐ์ ์ฌ์ฉํ SSE ์๋ํฌ์ธํธ URL ์ ๋ฏธ์ฌ(suffix) | /sse |
๊ธฐ๋ฅ
๋๊ธฐ/๋น๋๊ธฐ ํด๋ผ์ด์ธํธ ์ ํ (Sync/Async Client Types)
์คํํฐ๋ ๋ ๊ฐ์ง ์ ํ์ ํด๋ผ์ด์ธํธ๋ฅผ ์ง์ํฉ๋๋ค:
- ๋๊ธฐ(Synchronous) ๊ธฐ๋ณธ ํด๋ผ์ด์ธํธ ์ ํ์ผ๋ก, ๋ธ๋กํน ์์ฒญ-์๋ต ํจํด์ ์ ํฉํฉ๋๋ค.
- ๋น๋๊ธฐ(Asynchronous) ๋ ผ๋ธ๋กํน ์ฒ๋ฆฌ๊ฐ ํ์ํ ๋ฆฌ์กํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ํฉํ๋ฉฐ, ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํฉ๋๋ค: spring.ai.mcp.client.type=ASYNC
ํด๋ผ์ด์ธํธ ์ปค์คํฐ๋ง์ด์ง (Client Customization)
Spring AI์ ์๋ ๊ตฌ์ฑ์ ์ฝ๋ฐฑ ์ธํฐํ์ด์ค(Customizer)๋ฅผ ํตํด MCP ํด๋ผ์ด์ธํธ ์ฌ์์ ์ ์ฐํ๊ฒ ์ปค์คํฐ๋ง์ด์ง ํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์๋ ๊ตฌ์ฑ์ ์ฝ๋ฐฑ ์ธํฐํ์ด์ค๋ฅผ ํตํด MCP ํด๋ผ์ด์ธํธ ์ฌ์์ ํญ๋๊ฒ ์ปค์คํฐ๋ง์ด์ง ํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์ด๋ฌํ ์ปค์คํฐ๋ง์ด์ ๋ฅผ ์ฌ์ฉํ๋ฉด MCP ํด๋ผ์ด์ธํธ์ ์์ฒญ ํ์์์, ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐฉ์, ๋ฉ์์ง ์ฒ๋ฆฌ ๋ฑ ๋ค์ํ ๋์์ ์ค์ ํ ์ ์์ต๋๋ค.
์ปค์คํฐ๋ง์ด์ง ํ์
๋ค์๊ณผ ๊ฐ์ ์ปค์คํฐ๋ง์ด์ง ์ต์ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
- ์์ฒญ ๊ตฌ์ฑ(Request Configuration) – ์ฌ์ฉ์ ์ง์ ์์ฒญ ํ์์์์ ์ค์ ํ ์ ์์ต๋๋ค.
- ์ปค์คํ ์ํ๋ง ํธ๋ค๋ฌ(Custom Sampling Handlers) – ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ๋ฅผ ํตํด LLM์ผ๋ก๋ถํฐ ์ํ๋ง(์์ฑ ๋๋ ์์ฑ)์ ์์ฒญํ ์ ์๋ ํ์คํ๋ ๋ฐฉ์์ ๋๋ค. ์ด ํ๋ฆ์ ์๋ฒ๊ฐ ๋ณ๋์ API ํค ์์ด๋ AI ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์๋๋ก ํ๋ฉด์, ํด๋ผ์ด์ธํธ๊ฐ ๋ชจ๋ธ ์ ๊ทผ, ์ ํ, ๊ถํ์ ๋ํ ์ ์ด๊ถ์ ์ ์งํ ์ ์๊ฒ ํด ์ค๋๋ค.
- ํ์ผ ์์คํ (๋ฃจํธ) ์ ๊ทผ(File system (Roots) Access) – ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ํ์ผ ์์คํ ๋ฃจํธ๋ฅผ ๋ ธ์ถํ ์ ์๋ ํ์คํ๋ ๋ฐฉ์์ ๋๋ค. ๋ฃจํธ(Roots)๋ ์๋ฒ๊ฐ ํ์ผ ์์คํ ๋ด์์ ์์ ํ ์ ์๋ ๋ฒ์๋ฅผ ์ ์ํ๋ฉฐ, ์๋ฒ๋ ์ง์ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฃจํธ ๋ชฉ๋ก์ ์์ฒญํ๊ณ , ํด๋น ๋ชฉ๋ก์ด ๋ณ๊ฒฝ๋์์ ๋ ์๋ฆผ์ ๋ฐ์ ์ ์์ต๋๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ(Event Handlers) – ํน์ ์๋ฒ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ํด๋ผ์ด์ธํธ ์ธก์์ ์๋ฆผ์ ๋ฐ์ ์ ์๋๋ก ํ๋ ํธ๋ค๋ฌ์
๋๋ค:
- ํด ๋ณ๊ฒฝ ์๋ฆผ – ์ฌ์ฉ ๊ฐ๋ฅํ ์๋ฒ ํด ๋ชฉ๋ก์ด ๋ณ๊ฒฝ๋์์ ๋
- ๋ฆฌ์์ค ๋ณ๊ฒฝ ์๋ฆผ – ์ฌ์ฉ ๊ฐ๋ฅํ ์๋ฒ ๋ฆฌ์์ค ๋ชฉ๋ก์ด ๋ณ๊ฒฝ๋์์ ๋
- ํ๋กฌํํธ ๋ณ๊ฒฝ ์๋ฆผ – ์ฌ์ฉ ๊ฐ๋ฅํ ์๋ฒ ํ๋กฌํํธ ๋ชฉ๋ก์ด ๋ณ๊ฒฝ๋์์ ๋
- ๋ก๊น ํธ๋ค๋ฌ(Logging Handlers) – ์๋ฒ๊ฐ ๊ตฌ์กฐํ๋ ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ํด๋ผ์ด์ธํธ๋ก ์ ์กํ ์ ์๋ ํ์คํ๋ ๋ฐฉ์์ ๋๋ค. ํด๋ผ์ด์ธํธ๋ ์ต์ ๋ก๊ทธ ์์ค์ ์ค์ ํ์ฌ ๋ก๊น ์ ์์ธ ์์ค์ ์ ์ดํ ์ ์์ต๋๋ค.
๋๊ธฐ ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ McpSyncClientCustomizer๋ฅผ, ๋น๋๊ธฐ ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ McpAsyncClientCustomizer๋ฅผ ๊ตฌํํ๋ฉด ๋ฉ๋๋ค. ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์์ ๋ฐ๋ผ ์ ํํ ์ ์์ต๋๋ค.
// ๋๊ธฐ ๋ฐฉ์
@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
@Override
public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {
// ์์ฒญ ํ์์์์ 30์ด๋ก ์ค์
spec.requestTimeout(Duration.ofSeconds(30));
// ์ด ํด๋ผ์ด์ธํธ๊ฐ ์ ๊ทผํ ์ ์๋ ํ์ผ ์์คํ
๋ฃจํธ ๊ฒฝ๋ก ์ค์
spec.roots(roots);
// ๋ฉ์์ง ์์ฑ ์์ฒญ์ ๋ํ ์ํ๋ง ํธ๋ค๋ฌ ์ค์
spec.sampling((CreateMessageRequest messageRequest) -> {
// ์ํ๋ง ์ฒ๋ฆฌ ๋ก์ง
CreateMessageResult result = ...
return result;
});
// ์๋ฒ ํด ๋ชฉ๋ก์ด ๋ณ๊ฒฝ๋์์ ๋ ์๋ฆผ์ ๋ฐ์ ์๋น์ ์ค์
spec.toolsChangeConsumer((List<McpSchema.Tool> tools) -> {
// ํด ๋ณ๊ฒฝ ์ฒ๋ฆฌ
});
// ์๋ฒ ๋ฆฌ์์ค ๋ชฉ๋ก์ด ๋ณ๊ฒฝ๋์์ ๋ ์๋ฆผ์ ๋ฐ์ ์๋น์ ์ค์
spec.resourcesChangeConsumer((List<McpSchema.Resource> resources) -> {
// ๋ฆฌ์์ค ๋ณ๊ฒฝ ์ฒ๋ฆฌ
});
// ์๋ฒ ํ๋กฌํํธ ๋ชฉ๋ก์ด ๋ณ๊ฒฝ๋์์ ๋ ์๋ฆผ์ ๋ฐ์ ์๋น์ ์ค์
spec.promptsChangeConsumer((List<McpSchema.Prompt> prompts) -> {
// ํ๋กฌํํธ ๋ณ๊ฒฝ ์ฒ๋ฆฌ
});
// ์๋ฒ์์ ์ ๋ฌ๋๋ ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์๋น์ ์ค์
spec.loggingConsumer((McpSchema.LoggingMessageNotification log) -> {
// ๋ก๊ทธ ๋ฉ์์ง ์ฒ๋ฆฌ
});
}
}
// ๋น๋๊ธฐ ๋ฐฉ์
import org.springframework.stereotype.Component;
import java.time.Duration;
@Component
public class CustomMcpAsyncClientCustomizer implements McpAsyncClientCustomizer {
@Override
public void customize(String serverConfigurationName, McpClient.AsyncSpec spec) {
// ๋น๋๊ธฐ MCP ํด๋ผ์ด์ธํธ์ ์์ฒญ ํ์์์์ 30์ด๋ก ์ค์
spec.requestTimeout(Duration.ofSeconds(30));
}
}
serverConfigurationName ํ๋ผ๋ฏธํฐ๋ ์ปค์คํฐ๋ง์ด์ ๊ฐ ์ ์ฉ๋๋ ์๋ฒ ๊ตฌ์ฑ์ ์ด๋ฆ์ด๋ฉฐ, ํด๋น ๊ตฌ์ฑ์ ๋ํด MCP ํด๋ผ์ด์ธํธ๊ฐ ์์ฑ๋ฉ๋๋ค. MCP ํด๋ผ์ด์ธํธ์ ์๋ ๊ตฌ์ฑ์ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ์์ ๋ฐ๊ฒฌ๋ ๋ชจ๋ ์ปค์คํฐ๋ง์ด์ ๋ฅผ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ์ ์ฉํฉ๋๋ค.
์ ์ก ๊ณ์ธต ์ง์
์๋ ๊ตฌ์ฑ์ ์ฌ๋ฌ ๊ฐ์ง ์ ์ก ๋ฐฉ์์ ์ง์ํฉ๋๋ค:
- ํ์ค ์ ์ถ๋ ฅ(Standard I/O, Stdio) (spring-ai-starter-mcp-client๋ฅผ ํตํด ํ์ฑํ๋จ)
- SSE over HTTP (spring-ai-starter-mcp-client๋ฅผ ํตํด ํ์ฑํ๋จ)
- SSE over WebFlux (spring-ai-starter-mcp-client-webflux๋ฅผ ํตํด ํ์ฑํ๋จ)
Spring AI์์ ํตํฉ
์คํํฐ๋ Spring AI์ ํด ์คํ ํ๋ ์์ํฌ์ ํตํฉ๋๋ ํด ์ฝ๋ฐฑ์ ๊ตฌ์ฑํ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด MCP ํด์ AI ์ํธ์์ฉ์ ์ผ๋ถ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด ํตํฉ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ผ๋ฉฐ, spring.ai.mcp.client.toolcallback.enabled=false ์์ฑ์ ์ค์ ํ์ฌ ๋นํ์ฑํํ ์ ์์ต๋๋ค.
์์ ์ ํ๋ฆฌ์ผ์ด์
- Brave Web Search Chatbot - Model Context Protocol์ ์ฌ์ฉํ์ฌ ์น ๊ฒ์ ์๋ฒ์ ์ํธ์์ฉํ๋ ์ฑ๋ด ์์
- Default MCP Client Starter - spring-ai-starter-mcp-client ๊ธฐ๋ณธ MCP ํด๋ผ์ด์ธํธ Boot ์คํํฐ ์ฌ์ฉ ์์
- WebFlux MCP Client Starter - spring-ai-starter-mcp-client-webflux WebFlux ๊ธฐ๋ฐ MCP ํด๋ผ์ด์ธํธ Boot ์คํํฐ ์ฌ์ฉ ์์
์ถ๊ฐ ์๋ฃ
MCP ์๋ฒ Boot ์คํํฐ
ํ์ค ์คํํฐ๋ STDIO(ํ๋ก์ธ์ค ๋ด) ๋ฐ/๋๋ SSE(์๊ฒฉ) ์ ์ก ๋ฐฉ์์ ํตํด ํ๋ ์ด์์ MCP ์๋ฒ์ ๋์์ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค. SSE ์ฐ๊ฒฐ์ HttpClient ๊ธฐ๋ฐ ์ ์ก ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๊ฐ MCP ์๋ฒ์ ๋ํ ์ฐ๊ฒฐ๋ง๋ค ์๋ก์ด MCP ํด๋ผ์ด์ธํธ ์ธ์คํด์ค๊ฐ ์์ฑ๋ฉ๋๋ค.
์ฌ์ฉ์๋ ๋๊ธฐ(SYNC) ๋๋ ๋น๋๊ธฐ(ASYNC) MCP ํด๋ผ์ด์ธํธ ์ค ํ๋๋ง ์ ํํ ์ ์์ผ๋ฉฐ, ๋๊ธฐ์ ๋น๋๊ธฐ๋ฅผ ํผ์ฉํ ์๋ ์์ต๋๋ค. ์ด์ ํ๊ฒฝ์์๋ spring-ai-starter-mcp-client-webflux๋ฅผ ์ฌ์ฉํ WebFlux ๊ธฐ๋ฐ SSE ์ฐ๊ฒฐ์ ๊ถ์ฅํฉ๋๋ค.
MCP ์๋ฒ ๋ถํธ ์คํํฐ๋ ๋ค์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
- MCP ์๋ฒ ๊ตฌ์ฑ ์์์ ์๋ ์ค์
- ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ์ด์ ๋ชจ๋ ๋ชจ๋์ ๋ํ ์ง์
- ๋ค์ํ ์ ์ก ๊ณ์ธต ์ต์ ์ง์
- ์ ์ฐํ ํด, ๋ฆฌ์์ค, ํ๋กฌํํธ ๋ช ์ธ ๊ตฌ์ฑ
- ๋ณ๊ฒฝ ์ฌํญ์ ๋ํ ์๋ฆผ ๊ธฐ๋ฅ
์คํํฐ
์ฐธ๊ณ
์คํ๋ง AI ์๋ ๊ตฌ์ฑ, ์คํํฐ ๋ชจ๋์ ์ํฐํฉํธ ์ด๋ฆ์ ํฐ ๋ณํ๊ฐ ์์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ ์ ๊ทธ๋ ์ด๋ ๋ ธํธ๋ฅผ ์ฐธ๊ณ ํ์๊ธฐ ๋ฐ๋๋๋ค.
์ ์ก ๋ฐฉ์ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์๋ ์คํํฐ ์ค ํ๋๋ฅผ ์ ํํ์ธ์:
ํ์ค MCP ์๋ฒ
STDIO ์๋ฒ ์ ์ก ๋ฐฉ์์ ์ฌ์ฉํ๋ ์ ์ฒด MCP ์๋ฒ ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
- ์ปค๋งจ๋๋ผ์ธ ๋ฐ ๋ฐ์คํฌํฑ ๋๊ตฌ์ ์ ํฉ
- ์ถ๊ฐ์ ์ธ ์น ๊ด๋ จ ์์กด์ฑ์ด ํ์ํ์ง ์์
์ด ์คํํฐ๋ McpServerAutoConfiguration ์๋ ๊ตฌ์ฑ์ ํ์ฑํํ๋ฉฐ, ์ด๋ ๋ค์ ์ญํ ์ ์ํํฉ๋๋ค:
- ๊ธฐ๋ณธ ์๋ฒ ๊ตฌ์ฑ ์์ ์ค์
- ํด, ๋ฆฌ์์ค, ํ๋กฌํํธ ๋ช ์ธ ์ฒ๋ฆฌ
- ์๋ฒ ๊ธฐ๋ฅ ๋ฐ ๋ณ๊ฒฝ ์๋ฆผ ๊ด๋ฆฌ
- ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ์๋ฒ ๊ตฌํ ๋ชจ๋ ์ ๊ณต
WebMVC ์๋ฒ ์ ์ก
Spring MVC ๊ธฐ๋ฐ์ SSE(Server-Sent Events) ์๋ฒ ์ ์ก ๋ฐฉ์์ ์ฌ์ฉํ๋ฉฐ, ์ ํ์ ์ผ๋ก STDIO ์ ์ก๋ ์ง์ํ๋ ์ ์ฒด MCP ์๋ฒ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
์ด ์คํํฐ๋ McpWebMvcServerAutoConfiguration ๋ฐ McpServerAutoConfiguration ์๋ ๊ตฌ์ฑ์ ํ์ฑํํ์ฌ ๋ค์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
- Spring MVC๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ HTTP ์ ์ก ์ง์ (WebMvcSseServerTransportProvider)
- ์๋์ผ๋ก ๊ตฌ์ฑ๋๋ SSE ์๋ํฌ์ธํธ
- ์ ํ์ STDIO ์ ์ก ์ง์ (spring.ai.mcp.server.stdio=true๋ก ์ค์ ์ ํ์ฑํ)
- spring-boot-starter-web ๋ฐ mcp-spring-webmvc ์์กด์ฑ ํฌํจ
WebFlux ์๋ฒ ์ ์ก
Spring WebFlux ๊ธฐ๋ฐ์ SSE(Server-Sent Events) ์๋ฒ ์ ์ก ๋ฐฉ์์ ์ฌ์ฉํ๋ฉฐ, ์ ํ์ ์ผ๋ก STDIO ์ ์ก๋ ์ง์ํ๋ ์ ์ฒด MCP ์๋ฒ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
์ด ์คํํฐ๋ McpWebFluxServerAutoConfiguration ๋ฐ McpServerAutoConfiguration ์๋ ๊ตฌ์ฑ์ ํ์ฑํํ์ฌ ๋ค์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
- Spring WebFlux๋ฅผ ํ์ฉํ ๋ฆฌ์กํฐ๋ธ ์ ์ก ์ง์ (WebFluxSseServerTransportProvider)
- ์๋์ผ๋ก ๊ตฌ์ฑ๋๋ ๋ฆฌ์กํฐ๋ธ SSE ์๋ํฌ์ธํธ
- ์ ํ์ STDIO ์ ์ก ์ง์ (spring.ai.mcp.server.stdio=true๋ก ์ค์ ์ ํ์ฑํ)
- spring-boot-starter-webflux ๋ฐ mcp-spring-webflux ์์กด์ฑ ํฌํจ
์ค์ ์์ฑ (Configuration Properties)
๋ชจ๋ ์์ฑ์ spring.ai.mcp.server ์ ๋์ฌ๊ฐ ๋ถ์ต๋๋ค:
์์ฑ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
enabled | MCP ์๋ฒ๋ฅผ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค. | true |
stdio | stdio ์ ์ก ๋ฐฉ์์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค. | false |
name | ์๋ฒ ์๋ณ์ ์ํ ์๋ฒ ์ด๋ฆ์ ๋๋ค. | mcp-server |
version | ์๋ฒ ๋ฒ์ ์ ๋๋ค. | 1.0.0 |
instructions | ํด๋ผ์ด์ธํธ๊ฐ ์ด ์๋ฒ์ ์ํธ์์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์ ํ์ ์๋ด์ ๋๋ค. | null |
type | ์๋ฒ ์ ํ์ ๋๋ค (SYNC ๋๋ ASYNC). | SYNC |
capabilities.resource | ๋ฆฌ์์ค ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค. | true |
capabilities.tool | ํด ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค. | true |
capabilities.prompt | ํ๋กฌํํธ ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค. | true |
capabilities.completion | completion ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค. | true |
resource-change-notification | ๋ฆฌ์์ค ๋ณ๊ฒฝ ์๋ฆผ์ ํ์ฑํํฉ๋๋ค. | true |
prompt-change-notification | ํ๋กฌํํธ ๋ณ๊ฒฝ ์๋ฆผ์ ํ์ฑํํฉ๋๋ค. | true |
tool-change-notification | ํด ๋ณ๊ฒฝ ์๋ฆผ์ ํ์ฑํํฉ๋๋ค. | true |
tool-response-mime-type | (์ ํ ์ฌํญ) ํด ์ด๋ฆ๋ณ ์๋ต MIME ํ์ ์ ์ง์ ํฉ๋๋ค.์: spring.ai.mcp.server.tool-response-mime-type.generateImage=image/png๋ generateImage() ํด ์ด๋ฆ์ image/png MIME ํ์ ์ ์ฐ๊ฒฐํฉ๋๋ค. | - |
sse-message-endpoint | ํด๋ผ์ด์ธํธ๊ฐ ๋ฉ์์ง๋ฅผ ์ ์กํ๋ ๋ฐ ์ฌ์ฉํ ์น ์ ์ก์ฉ SSE ๋ฉ์์ง ์๋ํฌ์ธํธ ๊ฒฝ๋ก์ ๋๋ค. | /mcp/message |
sse-endpoint | ์น ์ ์ก์ฉ SSE ์๋ํฌ์ธํธ ๊ฒฝ๋ก์ ๋๋ค. | /sse |
base-url | ์ ํ์ URL ์ ๋์ฌ์ ๋๋ค. ์: base-url=/api/v1๋ก ์ค์ ํ๋ฉด ํด๋ผ์ด์ธํธ๋ /api/v1/sse ๋ฐ /api/v1/mcp/message์ ์ ๊ทผํฉ๋๋ค. | - |
request-timeout |
์๋ฒ ์๋ต์ ๊ธฐ๋ค๋ฆฌ๋ ์๊ฐ์ ๋๋ค. ์ด ์๊ฐ ์ดํ์๋ ํด ํธ์ถ, ๋ฆฌ์์ค ์ ๊ทผ, ํ๋กฌํํธ ์์ ์ ํฌํจํ ๋ชจ๋ ์์ฒญ์ด ํ์์์๋ฉ๋๋ค. | 20์ด |
๋๊ธฐ/๋น๋๊ธฐ ์๋ฒ ์ ํ
- ๋๊ธฐ ์๋ฒ(Synchronous Server) – ๊ธฐ๋ณธ ์๋ฒ ์ ํ์ผ๋ก, McpSyncServer๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํ๋ฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋จ์ํ ์์ฒญ-์๋ต ํจํด์ ์ ํฉํ๋๋ก ์ค๊ณ๋์ด ์์ต๋๋ค. ์ด ์๋ฒ ์ ํ์ ํ์ฑํํ๋ ค๋ฉด ์ค์ ์์ spring.ai.mcp.server.type=SYNC๋ก ์ง์ ํ์ญ์์ค. ํ์ฑํ๋๋ฉด ๋๊ธฐ ํด ๋ช ์ธ ๊ตฌ์ฑ์ ์๋์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
- ๋น๋๊ธฐ ์๋ฒ(Asynchronous Server) – ๋น๋๊ธฐ ์๋ฒ ๊ตฌํ์ McpAsyncServer๋ฅผ ์ฌ์ฉํ๋ฉฐ, ๋ ผ๋ธ๋กํน ์์ ์ ์ต์ ํ๋์ด ์์ต๋๋ค. ์ด ์๋ฒ ์ ํ์ ํ์ฑํํ๋ ค๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ค์ ์์ spring.ai.mcp.server.type=ASYNC๋ก ์ง์ ํ์ญ์์ค. ํด๋น ์๋ฒ ์ ํ์ Project Reactor ์ง์์ ํฌํจํ ๋น๋๊ธฐ ํด ๋ช ์ธ ๊ตฌ์ฑ์ ์๋์ผ๋ก ์ค์ ํฉ๋๋ค.
์๋ฒ ๊ธฐ๋ฅ (Server Capabilities)
MCP ์๋ฒ๋ ๋ค ๊ฐ์ง ์ฃผ์ ๊ธฐ๋ฅ ์ ํ์ ์ง์ํ๋ฉฐ, ๊ฐ๊ฐ ๊ฐ๋ณ์ ์ผ๋ก ํ์ฑํํ๊ฑฐ๋ ๋นํ์ฑํํ ์ ์์ต๋๋ค:
- ํด(๋๊ตฌ) – spring.ai.mcp.server.capabilities.tool=true|false๋ก ํด ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค.
- ๋ฆฌ์์ค – spring.ai.mcp.server.capabilities.resource=true|false๋ก ๋ฆฌ์์ค ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค.
- ํ๋กฌํํธ – spring.ai.mcp.server.capabilities.prompt=true|false๋ก ํ๋กฌํํธ ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค.
- ์์ฑ(Completions) – spring.ai.mcp.server.capabilities.completion=true|false๋ก ์์ฑ ๊ธฐ๋ฅ์ ํ์ฑํ/๋นํ์ฑํํฉ๋๋ค.
๋ชจ๋ ๊ธฐ๋ฅ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ต๋๋ค. ๊ธฐ๋ฅ์ ๋นํ์ฑํํ๋ฉด ํด๋น ๊ธฐ๋ฅ์ด ์๋ฒ์ ๋ฑ๋ก๋์ง ์์ผ๋ฉฐ, ํด๋ผ์ด์ธํธ์ ๋ ธ์ถ๋์ง ์์ต๋๋ค.
์ ์ก ์ต์ (Transport Options)
MCP ์๋ฒ๋ ์ธ ๊ฐ์ง ์ ์ก ๋ฐฉ์์ ์ง์ํ๋ฉฐ, ๊ฐ ๋ฐฉ์์ ์ ์ฉ ์คํํฐ์ ํจ๊ป ์ฌ์ฉ๋ฉ๋๋ค:
- ํ์ค ์ ์ถ๋ ฅ (STDIO) – spring-ai-starter-mcp-server
- Spring MVC (Server-Sent Events) – spring-ai-starter-mcp-server-webmvc
- Spring WebFlux (Reactive SSE) – spring-ai-starter-mcp-server-webflux
๊ธฐ๋ฅ ๋ฐ ์ ๊ณต ํญ๋ชฉ (Features and Capabilities)
MCP ์๋ฒ ๋ถํธ ์คํํฐ๋ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ํด, ๋ฆฌ์์ค, ํ๋กฌํํธ๋ฅผ ์ ๊ณตํ ์ ์๋๋ก ํฉ๋๋ค. ์๋ฒ ์ ํ(SYNC ๋๋ ASYNC)์ ๋ฐ๋ผ, Spring ๋น์ผ๋ก ๋ฑ๋ก๋ ์ฌ์ฉ์ ์ ์ ๊ธฐ๋ฅ ํธ๋ค๋ฌ๋ฅผ ์๋์ผ๋ก ๋๊ธฐ/๋น๋๊ธฐ ๋ช ์ธ๋ก ๋ณํํฉ๋๋ค.
ํด (Tools)
์๋ฒ๊ฐ ์ธ์ด ๋ชจ๋ธ์ ์ํด ํธ์ถ๋ ์ ์๋ ํด์ ๋ ธ์ถํ ์ ์๋๋ก ํฉ๋๋ค. MCP ์๋ฒ ๋ถํธ ์คํํฐ๋ ๋ค์์ ์ ๊ณตํฉ๋๋ค:
- ํด ๋ณ๊ฒฝ ์๋ฆผ ์ง์
- ์๋ฒ ์ ํ(SYNC ๋๋ ASYNC)์ ๋ฐ๋ผ Spring AI ํด์ ๋๊ธฐ/๋น๋๊ธฐ ๋ช ์ธ๋ก ์๋ ๋ณํ
- Spring ๋น์ ํตํ ์๋ ํด ๋ช ์ธํ
@Bean
public ToolCallbackProvider myTools(...) {
List<ToolCallback> tools = ...
return ToolCallbackProvider.from(tools);
}
๋๋ ์ ์์ค API๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค:
@Bean
public List<McpServerFeatures.SyncToolSpecification> myTools(...) {
List<McpServerFeatures.SyncToolSpecification> tools = ...
return tools;
}
์๋ ๊ตฌ์ฑ์ ๋ค์์ผ๋ก๋ถํฐ ๋ชจ๋ ToolCallback์ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ๋ฑ๋กํฉ๋๋ค:
- ๊ฐ๋ณ ToolCallback ๋น
- ToolCallback ๋น ๋ชฉ๋ก
- ToolCallbackProvider ๋น
ํด์ ์ด๋ฆ ๊ธฐ์ค์ผ๋ก ์ค๋ณต ์ ๊ฑฐ๋๋ฉฐ, ๊ฐ ํด ์ด๋ฆ์ ์ฒซ ๋ฒ์งธ ํญ๋ชฉ์ด ์ฌ์ฉ๋ฉ๋๋ค.
ํด ์ปจํ ์คํธ ์ง์ (Tool Context Support)
ํด ํธ์ถ ์ ์ปจํ ์คํธ ์ ๋ณด๋ฅผ ์ ๋ฌํ ์ ์๋๋ก ToolContext๊ฐ ์ง์๋ฉ๋๋ค. ์ด ์ปจํ ์คํธ์๋ exchange ํค ์๋์ McpSyncServerExchange ์ธ์คํด์ค๊ฐ ํฌํจ๋์ด ์์ผ๋ฉฐ, McpToolUtils.getMcpExchange(toolContext)๋ฅผ ํตํด ์ ๊ทผํ ์ ์์ต๋๋ค.
exchange.loggingNotification(…โ) ๋ฐ exchange.createMessage(…โ)๋ฅผ ์ฌ์ฉํ๋ ์์ ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๋ฆฌ์์ค ๊ด๋ฆฌ
์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ๋ฆฌ์์ค๋ฅผ ๋ ธ์ถํ ์ ์๋๋ก ํ์คํ๋ ๋ฐฉ์์ ์ ๊ณตํฉ๋๋ค.
- ์ ์ ๋ฐ ๋์ ๋ฆฌ์์ค ๋ช ์ธ
- ์ ํ์ ๋ณ๊ฒฝ ์๋ฆผ
- ๋ฆฌ์์ค ํ ํ๋ฆฟ ์ง์
- ๋๊ธฐ/๋น๋๊ธฐ ๋ฆฌ์์ค ๋ช ์ธ ๊ฐ ์๋ ๋ณํ
- Spring ๋น์ ํตํ ์๋ ๋ฆฌ์์ค ๋ช ์ธํ
@Bean
public List<McpServerFeatures.SyncResourceSpecification> myResources(...) {
var systemInfoResource = new McpSchema.Resource(...);
var resourceSpecification = new McpServerFeatures.SyncResourceSpecification(systemInfoResource, (exchange, request) -> {
try {
var systemInfo = Map.of(...);
String jsonContent = new ObjectMapper().writeValueAsString(systemInfo);
return new McpSchema.ReadResourceResult(
List.of(new McpSchema.TextResourceContents(request.uri(), "application/json", jsonContent)));
}
catch (Exception e) {
throw new RuntimeException("Failed to generate system info", e);
}
});
return List.of(resourceSpecification);
}
ํ๋กฌํํธ ๊ด๋ฆฌ
์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ํ๋กฌํํธ ํ ํ๋ฆฟ์ ๋ ธ์ถํ ์ ์๋๋ก ํ์คํ๋ ๋ฐฉ์์ ์ ๊ณตํฉ๋๋ค.
- ๋ณ๊ฒฝ ์๋ฆผ ์ง์
- ํ ํ๋ฆฟ ๋ฒ์ ๊ด๋ฆฌ
- ๋๊ธฐ/๋น๋๊ธฐ ํ๋กฌํํธ ๋ช ์ธ ๊ฐ ์๋ ๋ณํ
- Spring ๋น์ ํตํ ์๋ ํ๋กฌํํธ ๋ช ์ธํ
@Bean
public List<McpServerFeatures.SyncPromptSpecification> myPrompts() {
var prompt = new McpSchema.Prompt("greeting", "A friendly greeting prompt",
List.of(new McpSchema.PromptArgument("name", "The name to greet", true)));
var promptSpecification = new McpServerFeatures.SyncPromptSpecification(prompt, (exchange, getPromptRequest) -> {
String nameArgument = (String) getPromptRequest.arguments().get("name");
if (nameArgument == null) { nameArgument = "friend"; }
var userMessage = new PromptMessage(Role.USER, new TextContent("Hello " + nameArgument + "! How can I assist you today?"));
return new GetPromptResult("A personalized greeting message", List.of(userMessage));
});
return List.of(promptSpecification);
}
์์ฑ(Completion) ๊ด๋ฆฌ
์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ํ ์คํธ ์์ฑ(completion) ๊ธฐ๋ฅ์ ๋ ธ์ถํ ์ ์๋๋ก ํ์คํ๋ ๋ฐฉ์์ ์ ๊ณตํฉ๋๋ค.
- ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ completion ๋ช ์ธ ์ง์
- Spring ๋น์ ํตํ ์๋ ๋ฑ๋ก
@Bean
public List<McpServerFeatures.SyncCompletionSpecification> myCompletions() {
// "code-completion"์ด๋ผ๋ ์ด๋ฆ์ ๋๊ธฐํ completion ๊ธฐ๋ฅ ์ ์
var completion = new McpServerFeatures.SyncCompletionSpecification(
"code-completion", // ๊ธฐ๋ฅ ์ด๋ฆ
"Provides code completion suggestions", // ๊ธฐ๋ฅ ์ค๋ช
(exchange, request) -> {
// ํ
์คํธ ์๋์์ฑ(completion) ๊ฒฐ๊ณผ ๋ฐํ ๊ตฌํ
return new McpSchema.CompletionResult(List.of(
new McpSchema.Completion("suggestion1", "First suggestion"),
new McpSchema.Completion("suggestion2", "Second suggestion")
));
}
);
return List.of(completion); // ์ด ๊ธฐ๋ฅ์ ๋น์ผ๋ก ๋ฑ๋ก
}
๋ฃจํธ ๋ณ๊ฒฝ ์๋น์ (Root Change Consumers)
๋ฃจํธ๊ฐ ๋ณ๊ฒฝ๋๋ฉด, listChanged๋ฅผ ์ง์ํ๋ ํด๋ผ์ด์ธํธ๋ ๋ฃจํธ ๋ณ๊ฒฝ ์๋ฆผ(Root Change notification)์ ์ ์กํฉ๋๋ค.
- ๋ฃจํธ ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ์ํ ๋ชจ๋ํฐ๋ง ์ง์
- ๋ฆฌ์กํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ๋น๋๊ธฐ ์๋น์(async consumer)๋ก์ ์๋ ๋ณํ
- Spring ๋น์ ํตํ ์ ํ์ ๋ฑ๋ก ๊ฐ๋ฅ
@Bean
public BiConsumer<McpSyncServerExchange, List<McpSchema.Root>> rootsChangeHandler() {
return (exchange, roots) -> {
logger.info("Registering root resources: {}", roots);
};
}
์ฌ์ฉ ์
ํ์ค STDIO ์๋ฒ ๊ตฌ์ฑ (Standard STDIO Server Configuration)
# spring-ai-starter-mcp-server ์ฌ์ฉ
spring:
ai:
mcp:
server:
name: stdio-mcp-server
version: 1.0.0
type: SYNC
WebMVC ์๋ฒ ๊ตฌ์ฑ
# spring-ai-starter-mcp-server-webmvc ์ฌ์ฉ
spring:
ai:
mcp:
server:
name: webmvc-mcp-server
version: 1.0.0
type: SYNC
instructions: "This server provides weather information tools and resources"
sse-message-endpoint: /mcp/messages
capabilities:
tool: true
resource: true
prompt: true
completion: true
WebFlux ์๋ฒ ๊ตฌ์ฑ
# spring-ai-starter-mcp-server-webflux ์ฌ์ฉ
spring:
ai:
mcp:
server:
name: webflux-mcp-server
version: 1.0.0
type: ASYNC # Recommended for reactive applications
instructions: "This reactive server provides weather information tools and resources"
sse-message-endpoint: /mcp/messages
capabilities:
tool: true
resource: true
prompt: true
completion: true
MCP ์๋ฒ๊ฐ ํฌํจ๋ Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์์ฑ
@Service
public class WeatherService {
@Tool(description = "Get weather information by city name")
public String getWeather(String cityName) {
// ๊ตฌํ
}
}
@SpringBootApplication
public class McpServerApplication {
private static final Logger logger = LoggerFactory.getLogger(McpServerApplication.class);
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
}
์๋ ๊ตฌ์ฑ์ ToolCallback๋ค์ MCP ํด๋ก ์๋ ๋ฑ๋กํฉ๋๋ค. ์ฌ๋ฌ ๊ฐ์ ๋น์ด ToolCallback์ ์์ฑํ ์ ์์ผ๋ฉฐ, ์๋ ๊ตฌ์ฑ์ ์ด๋ค์ ๋ณํฉํ์ฌ ์ฒ๋ฆฌํฉ๋๋ค.
์์ ์ ํ๋ฆฌ์ผ์ด์
- ๋ ์จ ์๋ฒ (WebFlux) – WebFlux ์ ์ก ๋ฐฉ์์ ์ฌ์ฉํ๋ Spring AI MCP ์๋ฒ ๋ถํธ ์คํํฐ
- ๋ ์จ ์๋ฒ (STDIO) – STDIO ์ ์ก ๋ฐฉ์์ ์ฌ์ฉํ๋ Spring AI MCP ์๋ฒ ๋ถํธ ์คํํฐ
- ๋ ์จ ์๋ฒ ์๋ ๊ตฌ์ฑ – ์๋ ๊ตฌ์ฑ์ ์ฌ์ฉํ์ง ์๊ณ Java SDK๋ฅผ ํตํด ์๋ฒ๋ฅผ ์๋์ผ๋ก ์ค์ ํ๋ Spring AI MCP ์๋ฒ ๋ถํธ ์คํํฐ
์ถ๊ฐ ์๋ฃ
MCP ์ ํธ๋ฆฌํฐ
MCP ์ ํธ๋ฆฌํฐ(MCP Utilities)๋ Spring AI ์ ํ๋ฆฌ์ผ์ด์ ์ Model Context Protocol(MCP)์ ํตํฉํ๊ธฐ ์ํ ๊ธฐ๋ฐ ์ง์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด ์ ํธ๋ฆฌํฐ๋ Spring AI์ ํด ์์คํ ๊ณผ MCP ์๋ฒ ๊ฐ์ ์ํํ ํต์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ฉฐ, ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ์์ ์ ๋ชจ๋ ์ง์ํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ MCP ํด๋ผ์ด์ธํธ/์๋ฒ ๊ตฌ์ฑ ๋ฐ ์ํธ์์ฉ์ ์ฌ์ฉ๋๋ฉฐ, ๋ณด๋ค ๊ฐํธํ ์ค์ ์ ์ํ ๊ฒฝ์ฐ Boot ์คํํฐ ์ฌ์ฉ์ ๊ถ์ฅํฉ๋๋ค.
ToolCallback ์ ํธ๋ฆฌํฐ
Tool Callback ์ด๋ํฐ
MCP ํด์ Spring AI์ ํด ์ธํฐํ์ด์ค์ ์ด๋ํ (์ฐ๊ฒฐ)ํ์ฌ, ๋๊ธฐ(synchronous) ๋ฐ ๋น๋๊ธฐ(asynchronous) ์คํ์ ๋ชจ๋ ์ง์ํฉ๋๋ค.
// ๋๊ธฐ ๋ฐฉ์
McpSyncClient mcpClient = // MCP ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ํ๋
Tool mcpTool = // MCP ํด ์ ์ ํ๋
ToolCallback callback = new SyncMcpToolCallback(mcpClient, mcpTool);
// Spring AI ์ธํฐํ์ด์ค๋ฅผ ํตํด MCP ํด ์ฌ์ฉ
ToolDefinition definition = callback.getToolDefinition();
String result = callback.call("{\"param\": \"value\"}");
// ๋น๋๊ธฐ ๋ฐฉ์
McpAsyncClient mcpClient = // MCP ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ํ๋
Tool mcpTool = // MCP ํด ์ ์ ํ๋
ToolCallback callback = new AsyncMcpToolCallback(mcpClient, mcpTool);
// Spring AI ์ธํฐํ์ด์ค๋ฅผ ํตํด MCP ํด ์ฌ์ฉ
ToolDefinition definition = callback.getToolDefinition();
String result = callback.call("{\"param\": \"value\"}");
Tool Callback ์ ๊ณต์
MCP ํด๋ผ์ด์ธํธ๋ฅผ ํตํด MCP ํด์ ํ์(discover)ํ๊ณ Spring AI์์ ์ฌ์ฉํ ์ ์๋๋ก ์ ๊ณตํฉ๋๋ค.
// ๋๊ธฐ ๋ฐฉ์
McpSyncClient mcpClient = // MCP ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ํ๋
ToolCallbackProvider provider = new SyncMcpToolCallbackProvider(mcpClient);
// ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ MCP ํด ํ๋
ToolCallback[] tools = provider.getToolCallbacks();
// ๋น๋๊ธฐ ๋ฐฉ์
McpAsyncClient mcpClient = // MCP ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ํ๋
ToolCallbackProvider provider = new AsyncMcpToolCallbackProvider(mcpClient);
// ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ MCP ํด ํ๋
ToolCallback[] tools = provider.getToolCallbacks();
์ฌ๋ฌ ํด๋ผ์ด์ธํธ๋ฅผ ์ํ ๊ฒฝ์ฐ:
List<McpAsyncClient> clients = // ํด๋ผ์ด์ธํธ ๋ชฉ๋ก ํ๋
Flux<ToolCallback> callbacks = AsyncMcpToolCallbackProvider.asyncToolCallbacks(clients);
McpToolUtils
ToolCallbacks๋ฅผ ToolSpecifications๋ก ๋ณํ
Spring AI ํด ์ฝ๋ฐฑ์ MCP ํด ๋ช ์ธ๋ก ๋ณํํ๊ธฐ:
// ๋๊ธฐ ๋ฐฉ์
List<ToolCallback> toolCallbacks = // ํด ์ฝ๋ฐฑ ๋ชฉ๋ก ํ๋
List<SyncToolSpecifications> syncToolSpecs = McpToolUtils.toSyncToolSpecifications(toolCallbacks);
// ๋น๋๊ธฐ ๋ฐฉ์
List<ToolCallback> toolCallbacks = // ํด ์ฝ๋ฐฑ ๋ชฉ๋ก ํ๋
List<AsyncToolSpecification> asyncToolSpecifications = McpToolUtils.toAsyncToolSpecifications(toolCallbacks);
๊ทธ๋ฐ ๋ค์ McpServer.SyncSpecification์ ์ฌ์ฉํ์ฌ ํด ๋ช ์ธ๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค: (๋น๋๊ธฐ๋ McpServer.AsyncSpecification)
// ๋๊ธฐ ๋ฐฉ์
McpServer.SyncSpecification syncSpec = ...
syncSpec.tools(syncToolSpecs);
// ๋น๋๊ธฐ ๋ฐฉ์
McpServer.AsyncSpecification asyncSpec = ...
asyncSpec.tools(asyncToolSpecifications);
MCP ํด๋ผ์ด์ธํธ์์ ToolCallback ๊ฐ์ ธ์ค๊ธฐ
// ๋๊ธฐ ๋ฐฉ์
List<McpSyncClient> syncClients = // ๋๊ธฐ ํด๋ผ์ด์ธํธ ๋ชฉ๋ก ํ๋
List<ToolCallback> syncCallbacks = McpToolUtils.getToolCallbacksFromSyncClients(syncClients);
// ๋น๋๊ธฐ ๋ฐฉ์
List<McpAsyncClient> asyncClients = // ๋น๋๊ธฐ ํด๋ผ์ด์ธํธ ๋ชฉ๋ก ํ๋
List<ToolCallback> asyncCallbacks = McpToolUtils.getToolCallbacksFromAsyncClients(asyncClients);
๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ์ง์ (Native Image Support)
McpHints ํด๋์ค๋ MCP ์คํค๋ง ํด๋์ค๋ค์ ์ํ GraalVM ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ํํธ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ํด๋์ค๋ ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ ๋ MCP ์คํค๋ง ํด๋์ค์ ํ์ํ ๋ชจ๋ ๋ฆฌํ๋ ์ ํํธ๋ฅผ ์๋์ผ๋ก ๋ฑ๋กํฉ๋๋ค.
Reference
'๐ ๊ณต์ ๋ฌธ์ ๋ฒ์ญ > Spring AI (2025 Renewal)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring AI 2025 Renewal #7] Tool Calling (4) | 2025.05.25 |
---|---|
[Spring AI 2025 Renewal #6] Chat Memory (0) | 2025.05.17 |
[Spring AI 2025 Renewal #5] Embedding Models & Audio Models & Moderation Models (0) | 2025.05.17 |
[Spring AI 2025 Renewal #4] Chat Models & Image Models (1) | 2025.04.12 |
[Spring AI 2025 Renewal #3] Advisors API (0) | 2025.04.06 |