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 서버 사용하기
  1. 설정 파일 편집 후 Claude Desktop 재시작
  2. 새 대화 시작
  3. 서버가 제공하는 도구를 자연어로 요청
  4. 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}")

모범 사례

클라이언트 개발 권장사항
  1. 연결 관리: try-finally로 확실한 종료 보장
  2. 에러 처리: 모든 도구 호출에 에러 처리 구현
  3. 타임아웃: 장시간 실행 작업에 타임아웃 설정
  4. 재시도: 일시적 오류 시 재시도 로직
  5. 로깅: 디버깅을 위한 상세 로깅

연결 풀링

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 조건을 문서화해 대응 시간을 줄이세요.