이제 MCP 서버가 작동하게 되었으니, 클라이언트 측을 구축할 차례입니다. 클라이언트는 우리 애플리케이션이 MCP 서버와 통신하고 그 기능에 접근할 수 있게 해주는 부분입니다.
클라이언트 아키텍처 이해하기
코드를 살펴보기 전에, MCP 프로젝트에 관한 중요한 점을 명확히 해두겠습니다. 일반적으로는 MCP 클라이언트나 MCP 서버 중 하나만 구현하게 됩니다 - 둘 다 구현하지는 않습니다. 이 프로젝트에서는 둘이 어떻게 함께 작동하는지 보여드리기 위해 둘 다 구축하고 있습니다.

MCP 클라이언트는 함께 작동하는 두 가지 주요 구성 요소로 이루어져 있습니다:

- MCP Client - 세션을 더 쉽게 사용할 수 있도록 우리가 만드는 커스텀 클래스
- Client Session - 서버에 대한 실제 연결 (MCP Python SDK의 일부)
클라이언트 세션은 저수준 통신을 처리하지만, 프로그램이 종료될 때 신중한 리소스 정리가 필요합니다. 그래서 우리는 이를 자체 클래스로 감싸서 - 그 정리 작업을 자동으로 관리합니다.
클라이언트가 우리 애플리케이션에 어떻게 들어맞는가
우리의 애플리케이션 흐름 다이어그램을 기억하시나요? 클라이언트는 두 가지 핵심 순간에서 중요한 역할을 합니다:

우리의 CLI 코드는 클라이언트를 다음과 같이 사용합니다:
- Claude에게 전송할 사용 가능한 도구 목록 가져오기
- Claude가 요청할 때 도구 실행하기
핵심 클라이언트 함수 구현하기
두 가지 필수 함수인 list_tools와 call_tool을 구현해 보겠습니다.
list_tools의 경우, 세션에 연결하여 사용 가능한 도구를 요청해야 합니다:
call_tool의 경우, 도구 이름과 입력 매개변수를 서버에 전달합니다:
이게 전부입니다! 세션이 복잡한 통신 세부 사항을 모두 우리를 위해 처리해 줍니다.
클라이언트 테스트하기
클라이언트 파일 하단에는 간단한 테스트 하니스가 포함되어 있습니다. 모든 것이 제대로 작동하는지 확인하기 위해 직접 실행할 수 있습니다:
이렇게 하면 MCP 서버에 연결하여 사용 가능한 도구를 출력합니다. 이름, 설명, 입력 스키마를 포함한 도구 정의를 보여주는 출력을 확인할 수 있을 것입니다.
중요한 스키마 차이점
여기 알아두어야 할 주의할 점이 있습니다: MCP 도구 정의는 Claude가 기대하는 것과 정확히 일치하지 않습니다. MCP 스펙은 도구 스키마에 대한 자체 형식을 가지고 있으며, 이는 Bedrock이 요구하는 것과 약간 다릅니다.
걱정하지 마세요 - 이 변환을 자동으로 처리하는 코드가 프로젝트에 이미 있습니다. core/bedrock.py의 to_bedrock_tools 함수는 MCP 도구 정의를 Claude가 이해하는 형식으로 변환합니다.
Claude로 테스트하기
이제 서버와 클라이언트가 모두 작동하므로, 전체 흐름을 테스트할 수 있습니다. 메인 애플리케이션을 실행하고 Claude에게 문서를 읽어달라고 요청해 보세요:
그런 다음 질문하세요: "report.pdf 문서의 내용은 무엇인가요?"
Claude는 다음과 같이 동작합니다:
- 클라이언트로부터 사용 가능한 도구 목록을 받습니다
- read_doc_contents 도구를 사용하기로 결정합니다
- 클라이언트가 MCP 서버에서 해당 도구를 실행합니다
- Claude가 문서 내용을 받아 응답합니다
클라이언트는 애플리케이션 코드와 MCP 서버 사이의 다리 역할을 하며, 서버 기능을 Claude 및 시스템의 다른 부분에 쉽게 노출할 수 있게 해줍니다.