클라이언트 개발자를 위한 가이드
이 튜토리얼에서는 MCP 서버에 연결되는 LLM 기반 챗봇 클라이언트를 구축하는 방법을 배우게 됩니다. 첫 번째 서버 구축의 기본 사항을 안내하는 서버 퀵스타트를 먼저 살펴보시면 도움이 됩니다.
이 튜토리얼의 전체 코드는 여기에서 찾을 수 있습니다.
시스템 요구사항
시작하기 전에 시스템이 다음 요구사항을 충족하는지 확인하세요:
- Mac 또는 Windows 컴퓨터
- 최신 Python 버전 설치
- 최신
uv버전 설치
환경 설정
먼저 uv로 새 Python 프로젝트를 생성하세요:
# 프로젝트 디렉토리 생성
uv init mcp-client
cd mcp-client
# 가상 환경 생성
uv venv
# 가상 환경 활성화
# Windows의 경우:
.venv\Scripts\activate
# Unix 또는 MacOS의 경우:
source .venv/bin/activate
# 필요한 패키지 설치
uv add mcp anthropic python-dotenv
# 보일러플레이트 파일 제거
rm hello.py
# 메인 파일 생성
touch client.pyAPI 키 설정
Anthropic Console에서 Anthropic API 키가 필요합니다.
저장할 .env 파일을 생성하세요:
# .env 파일 생성
touch .env.env 파일에 키를 추가하세요:
ANTHROPIC_API_KEY=<여기에 키 입력>.gitignore에 .env를 추가하세요:
echo ".env" >> .gitignore클라이언트 생성
기본 클라이언트 구조
먼저, 가져오기를 설정하고 기본 클라이언트 클래스를 생성해 보겠습니다.
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv() # .env에서 환경 변수 로드
class MCPClient:
def __init__(self):
# 세션 및 클라이언트 객체 초기화
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic()서버 연결 관리
다음으로, MCP 서버에 연결하는 메서드를 구현하겠습니다:
async def connect_to_server(self, server_script_path: str):
"""MCP 서버에 연결합니다
Args:
server_script_path: 서버 스크립트 경로 (.py 또는 .js)
"""
is_python = server_script_path.endswith('.py')
is_js = server_script_path.endswith('.js')
if not (is_python or is_js):
raise ValueError("서버 스크립트는 .py 또는 .js 파일이어야 합니다")
command = "python" if is_python else "node"
server_params = StdioServerParameters(
command=command,
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
await self.session.initialize()
# 사용 가능한 도구 목록
response = await self.session.list_tools()
tools = response.tools
print("\n서버에 연결되었습니다. 사용 가능한 도구:", [tool.name for tool in tools])쿼리 처리 로직
이제 쿼리 처리와 도구 호출 처리를 위한 핵심 기능을 추가해 보겠습니다:
async def process_query(self, query: str) -> str:
"""Claude와 사용 가능한 도구를 사용하여 쿼리를 처리합니다"""
messages = [
{
"role": "user",
"content": query
}
]
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# 초기 Claude API 호출
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# 응답 처리 및 도구 호출 처리
tool_results = []
final_text = []
assistant_message_content = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
assistant_message_content.append(content)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# 도구 호출 실행
result = await self.session.call_tool(tool_name, tool_args)
tool_results.append({"call": tool_name, "result": result})
final_text.append(f"[도구 {tool_name}를 인수 {tool_args}로 호출]")
assistant_message_content.append(content)
messages.append({
"role": "assistant",
"content": assistant_message_content
})
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content.id,
"content": result.content
}
]
})
# Claude에서 다음 응답 가져오기
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools
)
final_text.append(response.content[0].text)
return "\n".join(final_text)대화형 채팅 인터페이스
이제 채팅 루프와 정리 기능을 추가하겠습니다:
async def chat_loop(self):
"""대화형 채팅 루프를 실행합니다"""
print("\nMCP 클라이언트가 시작되었습니다!")
print("쿼리를 입력하거나 'quit'를 입력하여 종료하세요.")
while True:
try:
query = input("\n쿼리: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\n오류: {str(e)}")
async def cleanup(self):
"""리소스를 정리합니다"""
await self.exit_stack.aclose()메인 진입점
마지막으로, 메인 실행 로직을 추가하겠습니다:
async def main():
if len(sys.argv) < 2:
print("사용법: python client.py <서버_스크립트_경로>")
sys.exit(1)
client = MCPClient()
try:
await client.connect_to_server(sys.argv[1])
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())주요 구성 요소 설명
1. 클라이언트 초기화
MCPClient클래스는 세션 관리와 API 클라이언트로 초기화됩니다- 적절한 리소스 관리를 위해
AsyncExitStack을 사용합니다 - Claude 상호작용을 위해 Anthropic 클라이언트를 구성합니다
2. 서버 연결
- Python과 Node.js 서버를 모두 지원합니다
- 서버 스크립트 유형을 검증합니다
- 적절한 통신 채널을 설정합니다
- 세션을 초기화하고 사용 가능한 도구를 나열합니다
3. 쿼리 처리
- 대화 컨텍스트를 유지합니다
- Claude의 응답과 도구 호출을 처리합니다
- Claude와 도구 간의 메시지 흐름을 관리합니다
- 결과를 일관된 응답으로 결합합니다
4. 대화형 인터페이스
- 간단한 명령줄 인터페이스를 제공합니다
- 사용자 입력을 처리하고 응답을 표시합니다
- 기본 오류 처리를 포함합니다
- 정상적인 종료를 허용합니다
5. 리소스 관리
- 리소스의 적절한 정리
- 연결 문제에 대한 오류 처리
문제 해결
서버 경로 문제
- 서버 스크립트 경로가 올바른지 확인하세요
- 상대 경로가 작동하지 않으면 절대 경로를 사용하세요
- Windows 사용자의 경우 슬래시(/) 또는 이스케이프된 백슬래시(\)를 사용하세요
- 서버 파일이 올바른 확장자(.py는 Python, .js는 Node.js)를 가지는지 확인하세요
올바른 경로 사용 예시:
# 상대 경로
uv run client.py ./server/weather.py
# 절대 경로
uv run client.py /Users/username/projects/mcp-server/weather.py
# Windows 경로 (두 형식 모두 작동)
uv run client.py C:/projects/mcp-server/weather.py
uv run client.py C:\\projects\\mcp-server\\weather.py응답 시간
- 첫 번째 응답은 최대 30초까지 걸릴 수 있습니다
- 이는 정상이며 다음 과정에서 발생합니다:
- 서버 초기화
- Claude 쿼리 처리
- 도구 실행
- 후속 응답은 일반적으로 더 빠릅니다
- 초기 대기 중에는 프로세스를 중단하지 마세요
시스템 요구사항
시작하기 전에 시스템이 다음 요구사항을 충족하는지 확인하세요:
- Java 17 이상
- Maven 3.6 이상
- Spring Boot를 지원하는 IDE
프로젝트 생성
Maven으로 새 Spring Boot 프로젝트를 생성하세요:
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=ai-mcp-brave-chatbot \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=falsepom.xml을 업데이트하세요:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>ai-mcp-brave-chatbot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<properties>
<java.version>17</java.version>
<spring-ai.version>0.8.0</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>org.modelcontext</groupId>
<artifactId>mcp-spring-boot-starter</artifactId>
<version>0.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>애플리케이션 구성
application.yml을 생성하세요:
spring:
ai:
anthropic:
api-key: ${ANTHROPIC_API_KEY}
mcp:
servers:
filesystem:
command: npx
args:
- "-y"
- "@modelcontextprotocol/server-filesystem"
- "C:\\Users\\username\\Desktop"
- "C:\\Users\\username\\Downloads"애플리케이션 생성
메인 애플리케이션 클래스를 생성하세요:
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BraveChatApplication {
public static void main(String[] args) {
SpringApplication.run(BraveChatApplication.class, args);
}
}애플리케이션 실행
# 빌드 및 실행
./mvnw clean install
java -jar ./target/ai-mcp-brave-chatbot-0.0.1-SNAPSHOT.jar
# 대안
./mvnw spring-boot:runANTHROPIC_API_KEY 환경 변수를 설정하는 것을 잊지 마세요!작동 방식
애플리케이션은 여러 구성 요소를 통해 Spring AI를 MCP 서버와 통합합니다:
MCP 클라이언트 구성
- pom.xml의 필수 의존성:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
</dependency>- 애플리케이션 속성 (application.yml):
spring:
ai:
mcp:
client:
enabled: true
name: brave-search-client
version: 1.0.0
type: SYNC
request-timeout: 20s
stdio:
root-change-notification: true
servers-configuration: classpath:/mcp-servers-config.json
anthropic:
api-key: ${ANTHROPIC_API_KEY}- MCP 서버 구성 (
mcp-servers-config.json):
{
"mcpServers": {
"brave-search": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-brave-search"
],
"env": {
"BRAVE_API_KEY": "<BRAVE API 키를 여기에 입력>"
}
}
}
}채팅 구현
챗봇은 MCP 도구 통합이 있는 Spring AI의 ChatClient를 사용하여 구현됩니다:
var chatClient = chatClientBuilder
.defaultSystem("당신은 AI와 Java의 전문가인 유용한 어시스턴트입니다.")
.defaultTools((Object[]) mcpToolAdapter.toolCallbacks())
.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.build();주요 기능:
- 자연어 이해를 위해 Claude AI 모델 사용
- 도구 기능을 위해 MCP 통합
- InMemoryChatMemory를 사용하여 대화 기억 유지
- 대화형 명령줄 애플리케이션으로 실행
고급 구성
MCP 클라이언트는 추가 구성 옵션을 지원합니다:
McpSyncClientCustomizer또는McpAsyncClientCustomizer를 통한 클라이언트 사용자 정의- 여러 전송 유형을 가진 다중 클라이언트:
STDIO및SSE(서버 전송 이벤트) - Spring AI의 도구 실행 프레임워크와의 통합
- 자동 클라이언트 초기화 및 수명 주기 관리
WebFlux 기반 애플리케이션의 경우 대신 WebFlux 스타터를 사용할 수 있습니다:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId>
</dependency>이것은 유사한 기능을 제공하지만 WebFlux 기반 SSE 전송 구현을 사용하며, 프로덕션 배포에 권장됩니다.
다음 단계