MCP 클라이언트
MCP 클라이언트는 MCP 서버와 통신하여 AI 모델에게 도구와 리소스를 제공합니다. Claude Desktop, Claude CLI, 그리고 커스텀 클라이언트를 설정하고 사용하는 방법을 학습합니다.
업데이트 안내: 모델/요금/버전/정책 등 시점에 민감한 정보는 변동될 수 있습니다.
최신 내용은 공식 문서를 확인하세요.
빠른 시작
- Claude Desktop: 설정 파일에 서버 추가
- Claude CLI:
mcp명령으로 서버 관리 - 커스텀 클라이언트: SDK로 직접 구현
Claude Desktop
설정
Claude Desktop은 MCP 서버를 설정 파일을 통해 관리합니다.
설정 파일 위치
| OS | 경로 |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
설정 파일 구조
JSON
{
"mcpServers": {
"filesystem": {
"command": "python",
"args": ["-m", "mcp_server_filesystem"]
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://user:password@localhost/mydb"
}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_your_token_here"
}
}
}
}
설정 예제
1. 파일시스템 서버 (Python)
JSON
{
"mcpServers": {
"filesystem": {
"command": "uv",
"args": [
"--directory",
"/path/to/mcp-server-filesystem",
"run",
"mcp-server-filesystem"
]
}
}
}
2. SQLite 서버
JSON
{
"mcpServers": {
"sqlite": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sqlite",
"/path/to/database.db"
]
}
}
}
3. 여러 서버 동시 사용
JSON
{
"mcpServers": {
"filesystem": {
"command": "python",
"args": ["-m", "mcp_server_filesystem"],
"env": {
"ALLOWED_DIRS": "/home/user/Documents,/home/user/Projects"
}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_your_token"
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://localhost/myapp"
}
},
"slack": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-slack"],
"env": {
"SLACK_BOT_TOKEN": "xoxb-your-token",
"SLACK_TEAM_ID": "T1234567"
}
}
}
}
사용 방법
Claude Desktop에서 MCP 서버 사용하기
- 설정 파일 편집 후 Claude Desktop 재시작
- 새 대화 시작
- 서버가 제공하는 도구를 자연어로 요청
- Claude가 자동으로 적절한 도구 선택 및 실행
예제 대화
대화
사용자: 내 프로젝트 디렉토리에 있는 모든 Python 파일을 찾아줘
Claude: [filesystem 서버의 search_files 도구 사용]
다음 Python 파일을 찾았습니다:
- /home/user/Projects/app.py
- /home/user/Projects/utils.py
- /home/user/Projects/tests/test_app.py
사용자: app.py 파일의 내용을 보여줘
Claude: [filesystem 서버의 read_file 도구 사용]
app.py 파일의 내용입니다:
[파일 내용 표시]
사용자: GitHub 저장소의 open 이슈를 확인해줘
Claude: [github 서버의 list_issues 도구 사용]
현재 open 상태인 이슈 목록입니다:
1. #42: 로그인 버그 수정
2. #43: 성능 개선 필요
...
문제 해결
| 문제 | 원인 | 해결 |
|---|---|---|
| 서버가 연결되지 않음 | 잘못된 명령어/경로 | 명령어와 경로 확인 |
| 환경 변수 인식 안 됨 | env 설정 누락 | env 섹션 추가 |
| 도구가 표시되지 않음 | 서버 초기화 실패 | 로그 확인 (개발자 도구) |
| 권한 오류 | 파일/디렉토리 접근 권한 | 서버 허용 목록 확인 |
보안 주의사항
- 설정 파일에 API 토큰/패스워드를 직접 저장하지 마세요
- 환경 변수나 시크릿 관리 도구 사용 권장
- 파일시스템 서버는 허용 디렉토리 제한 필수
Claude CLI
설치
Bash
# npm으로 설치
npm install -g @anthropic-ai/claude-cli
# 또는 Homebrew (macOS)
brew install claude-cli
설정
Bash
# API 키 설정
claude auth login
# 또는 환경 변수
export ANTHROPIC_API_KEY=sk-ant-your-key-here
MCP 서버 사용
서버 추가
Bash
# MCP 서버 추가
claude mcp add filesystem python -m mcp_server_filesystem
# 환경 변수와 함께 추가
claude mcp add github npx -y @modelcontextprotocol/server-github \
--env GITHUB_TOKEN=ghp_your_token
# 서버 목록 확인
claude mcp list
# 서버 제거
claude mcp remove filesystem
대화 시작
Bash
# MCP 서버와 함께 대화 시작
claude chat --mcp filesystem,github
# 또는 모든 서버 사용
claude chat --mcp all
# 일회성 질문
claude ask "프로젝트의 README.md 파일 내용을 보여줘" --mcp filesystem
CLI 사용 예제
Bash
# 파일 검색 및 분석
claude ask "src/ 디렉토리의 모든 TypeScript 파일을 분석하고 \
주요 함수 목록을 만들어줘" --mcp filesystem
# 데이터베이스 쿼리
claude ask "users 테이블에서 최근 7일간 가입한 사용자 수를 알려줘" \
--mcp postgres
# GitHub 이슈 생성
claude ask "버그 리포트: 로그인 시 세션 만료 문제, \
우선순위 높음으로 이슈 생성해줘" --mcp github
# 여러 서버 조합
claude chat --mcp filesystem,github,slack
커스텀 클라이언트 개발
Python 클라이언트
설치
Bash
pip install mcp
# 또는
uv add mcp
기본 클라이언트
Python
from mcp.client import Client
from mcp.client.stdio import StdioClientTransport
async def main():
# 클라이언트 생성
client = Client(
{"name": "my-client", "version": "1.0.0"},
{"capabilities": {"tools": {}}}
)
# 서버 연결 (stdio)
transport = StdioClientTransport({
"command": "python",
"args": ["-m", "mcp_server_filesystem"]
})
await client.connect(transport)
try:
# 도구 목록 조회
tools = await client.list_tools()
print("Available tools:")
for tool in tools:
print(f" - {tool['name']}: {tool['description']}")
# 도구 호출
result = await client.call_tool("read_file", {
"path": "/path/to/file.txt"
})
print("File content:", result)
finally:
await client.close()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
리소스 읽기
Python
# 리소스 목록
resources = await client.list_resources()
for resource in resources:
print(f"Resource: {resource['uri']}")
# 리소스 읽기
content = await client.read_resource("file:///path/to/doc.txt")
print(content)
프롬프트 사용
Python
# 프롬프트 목록
prompts = await client.list_prompts()
for prompt in prompts:
print(f"Prompt: {prompt['name']}")
# 프롬프트 가져오기
messages = await client.get_prompt("code-review", {
"language": "python",
"code": "def hello(): return 'world'"
})
print(messages)
TypeScript 클라이언트
설치
Bash
npm install @modelcontextprotocol/sdk
기본 클라이언트
TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
async function main() {
// 클라이언트 생성
const client = new Client(
{
name: "my-client",
version: "1.0.0"
},
{
capabilities: {
tools: {}
}
}
);
// 서버 연결
const transport = new StdioClientTransport({
command: "node",
args: ["dist/server.js"]
});
await client.connect(transport);
try {
// 도구 목록
const { tools } = await client.listTools();
console.log("Available tools:", tools);
// 도구 호출
const result = await client.callTool("read_file", {
path: "/path/to/file.txt"
});
console.log("Result:", result);
} finally {
await client.close();
}
}
main().catch(console.error);
Anthropic SDK와 통합
Python
import anthropic
from mcp.client import Client
from mcp.client.stdio import StdioClientTransport
async def chat_with_mcp():
# MCP 클라이언트 설정
mcp_client = Client(
{"name": "claude-client", "version": "1.0.0"},
{"capabilities": {"tools": {}}}
)
transport = StdioClientTransport({
"command": "python",
"args": ["-m", "mcp_server_filesystem"]
})
await mcp_client.connect(transport)
# MCP 도구를 Anthropic Tool 형식으로 변환
mcp_tools = await mcp_client.list_tools()
anthropic_tools = []
for tool in mcp_tools:
anthropic_tools.append({
"name": tool["name"],
"description": tool["description"],
"input_schema": tool["inputSchema"]
})
# Anthropic 클라이언트
client = anthropic.Anthropic()
# 대화 시작
messages = [{
"role": "user",
"content": "README.md 파일을 읽어줘"
}]
response = client.messages.create(
model="claude-4-5",
max_tokens=4096,
messages=messages,
tools=anthropic_tools
)
# Tool use 처리
while response.stop_reason == "tool_use":
tool_use = response.content[-1]
# MCP 서버에서 도구 실행
tool_result = await mcp_client.call_tool(
tool_use.name,
tool_use.input
)
# 결과를 메시지에 추가
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": tool_result
}]
})
# 다음 응답
response = client.messages.create(
model="claude-4-5",
max_tokens=4096,
messages=messages,
tools=anthropic_tools
)
print(response.content)
await mcp_client.close()
import asyncio
asyncio.run(chat_with_mcp())
클라이언트 Capabilities
Capability 선언
Python
client = Client(
{"name": "my-client", "version": "1.0.0"},
{
"capabilities": {
"tools": {}, # 도구 호출 지원
"resources": {
"subscribe": True # 리소스 변경 구독
},
"prompts": {}, # 프롬프트 사용
"sampling": {} # 샘플링 요청 수신
}
}
)
리소스 구독
Python
# 리소스 변경 구독
await client.subscribe_resource("file:///path/to/watched.txt")
# 리소스 변경 알림 수신
@client.on_resource_updated
async def handle_update(uri):
print(f"Resource updated: {uri}")
content = await client.read_resource(uri)
print("New content:", content)
# 구독 해제
await client.unsubscribe_resource("file:///path/to/watched.txt")
에러 처리
Python
from mcp.client import McpError
try:
result = await client.call_tool("read_file", {
"path": "/nonexistent.txt"
})
except McpError as e:
if e.code == -32001:
print("File not found")
elif e.code == -32002:
print("Permission denied")
else:
print(f"Error: {e.message}")
except Exception as e:
print(f"Unexpected error: {e}")
모범 사례
클라이언트 개발 권장사항
- 연결 관리: try-finally로 확실한 종료 보장
- 에러 처리: 모든 도구 호출에 에러 처리 구현
- 타임아웃: 장시간 실행 작업에 타임아웃 설정
- 재시도: 일시적 오류 시 재시도 로직
- 로깅: 디버깅을 위한 상세 로깅
연결 풀링
Python
class McpClientPool:
def __init__(self, server_config, pool_size=5):
self.server_config = server_config
self.pool_size = pool_size
self.clients = []
async def initialize(self):
for _ in range(self.pool_size):
client = await self._create_client()
self.clients.append(client)
async def _create_client(self):
client = Client(
{"name": "pooled-client", "version": "1.0.0"},
{"capabilities": {"tools": {}}}
)
transport = StdioClientTransport(self.server_config)
await client.connect(transport)
return client
async def get_client(self):
if self.clients:
return self.clients.pop()
return await self._create_client()
async def return_client(self, client):
if len(self.clients) < self.pool_size:
self.clients.append(client)
else:
await client.close()
다음 단계
핵심 정리
- MCP 클라이언트의 핵심 개념과 흐름을 정리합니다.
- Claude Desktop를 단계별로 이해합니다.
- 실전 적용 시 기준과 주의점을 확인합니다.
실무 팁
- 입력/출력 예시를 고정해 재현성을 확보하세요.
- MCP 클라이언트 범위를 작게 잡고 단계적으로 확장하세요.
- Claude Desktop 조건을 문서화해 대응 시간을 줄이세요.