MCP · CLIENT

Client 구현

💻 Python 비동기 코드 🔗 LangChain 통합 ⚠️ 에러 처리 패턴

MCP ClientSession으로 서버에 연결하고, Tool 탐색·호출, Resource 읽기를 구현합니다. LangChain/LangGraph와 통합하는 패턴도 다룹니다.

기본 클라이언트 — stdio 연결

로컬 MCP Server 프로세스를 stdio로 연결하는 가장 기본적인 패턴입니다.

pythonstdio 클라이언트 — 전체 흐름
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    # 서버 프로세스 설정
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"],
        env={"DB_URL": "sqlite:///app.db"}  # 서버 환경변수
    )

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 1. 연결 초기화 (initialize + initialized)
            await session.initialize()

            # 2. 사용 가능한 Tool 목록 조회
            tools_result = await session.list_tools()
            for tool in tools_result.tools:
                print(f"Tool: {tool.name} — {tool.description}")

            # 3. Tool 호출
            result = await session.call_tool(
                "web_search",
                arguments={"query": "MCP Python SDK", "max_results": 3}
            )
            if not result.isError:
                for content in result.content:
                    print(content.text)

            # 4. Resource 읽기
            resource = await session.read_resource("config://app/settings")
            for content in resource.contents:
                print(content.text)

asyncio.run(main())

SSE 클라이언트 — 원격 서버 연결

pythonHTTP+SSE 클라이언트
from mcp.client.sse import sse_client

async def connect_remote():
    async with sse_client("http://my-mcp-server:8080/sse") as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            # 이후 사용법은 stdio와 동일
            result = await session.call_tool("query_db", {"sql": "SELECT * FROM users LIMIT 10"})
            return result

LangChain 통합

langchain-mcp-adapters 패키지를 사용하면 MCP Tool을 LangChain Tool로 자동 변환해 Agent에 직접 연결할 수 있습니다.

bash패키지 설치
pip install langchain-mcp-adapters langgraph langchain-anthropic
pythonMCP → LangGraph Agent 통합
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from langchain_anthropic import ChatAnthropic

async def build_agent():
    # 여러 MCP Server를 한번에 연결
    client = MultiServerMCPClient({
        "search": {
            "command": "python",
            "args": ["search_server.py"],
            "transport": "stdio",
        },
        "database": {
            "url": "http://db-server:8080/sse",
            "transport": "sse",
        },
    })

    async with client as mcp:
        # MCP Tool → LangChain Tool 자동 변환
        tools = await mcp.get_tools()

        model = ChatAnthropic(model="claude-opus-4-7")
        agent = create_react_agent(model, tools)

        result = await agent.ainvoke({
            "messages": [{"role": "user", "content": "최근 가입 사용자 10명을 검색해서 알려줘"}]
        })
        return result

asyncio.run(build_agent())

강건한 에러 처리

python에러 처리 + 재시도 패턴
from mcp import McpError
from mcp.types import ErrorCode
import asyncio

async def safe_tool_call(
    session: ClientSession,
    tool_name: str,
    arguments: dict,
    retries: int = 3
) -> str | None:
    for attempt in range(retries):
        try:
            result = await session.call_tool(tool_name, arguments)

            if result.isError:
                # Tool 수준 에러 — 재시도하지 않음
                error_text = result.content[0].text if result.content else "Unknown error"
                print(f"Tool error: {error_text}")
                return None

            return result.content[0].text

        except McpError as e:
            if e.error.code == ErrorCode.INVALID_PARAMS:
                raise  # 잘못된 파라미터 — 재시도 무의미
            if attempt == retries - 1:
                raise
            await asyncio.sleep(2 ** attempt)  # 지수 백오프

        except Exception as e:
            print(f"Attempt {attempt+1} failed: {e}")
            if attempt == retries - 1:
                raise
            await asyncio.sleep(2 ** attempt)

ClientSession 주요 메서드

메서드반환 타입설명
initialize()InitializeResult서버와 핸드셰이크. 가장 먼저 호출
list_tools()ListToolsResult사용 가능한 Tool 목록. cursor로 페이지네이션
call_tool(name, args)CallToolResultTool 실행. isError 필드로 성공 여부 확인
list_resources()ListResourcesResultResource URI 목록
read_resource(uri)ReadResourceResult특정 URI의 Resource 내용 읽기
list_prompts()ListPromptsResultPrompt 템플릿 목록
get_prompt(name, args)GetPromptResultPrompt 렌더링 (인수 전달)
send_ping()EmptyResult연결 헬스체크