2025년 3월 28일 금요일

인공지능 AI 에이전트 표준 프로토콜 MCP 분석 및 사용하기

이 글은 앤트로픽(Anthropic. 클로드 개발사)의 인공지능 AI 에이전트 표준 프로토콜 MCP(Model Context Protocol) 사용하는 방법을 간략히 설명한다. 

MCP는 애플리케이션이 LLM에 컨텍스트를 제공하는 방식을 표준화한 개방형 프로토콜이다. USB-C 포트처럼, 다양한 도구와 데이터를 LLM에 연결하는 통합된 인터페이스 역할을 한다. LLM 기반 에이전트와 워크플로우 구축을 지원하며, 유연한 통합, 데이터 보호, 공급업체 간 전환성을 제공한다.  
애트로픽 MCP 소개

이 글은 주로 다음 문서를 참고하였다.

아키텍처 구조
MCP는 클라이언트-서버 구조로 구성되며, 로컬 및 원격 데이터를 안전하게 연결할 수 있는 아키텍처를 따른다.
MCP 구조

  • MCP 호스트는 MCP 프로토콜을 통해 서비스에 액세스할 수 있는 애플리케이션이다. Claude 데스크톱 앱, AI 에이전트/CLI, 커서 IDE 등이 이에 해당하며, LLM(로컬 또는 원격)을 활용하여 다양한 작업을 수행한다.  
  • MCP 클라이언트는 MCP 서버와 연결하기 위해 호스트 애플리케이션과 통합된 클라이언트이다.  
  • MCP 서버는 MCP 프로토콜을 통해 특정 기능을 노출하는 응용 프로그램 또는 프로그램이다. 서버는 Docker 컨테이너, JVM, Node.js(UV/UVX) 프로세스에서 실행될 수 있으며, MCP 커뮤니티에서 제공하는 사전 구축된 서버를 활용할 수도 있다.  
  • 로컬 데이터 소스는 로컬 시스템에 존재하는 데이터베이스 또는 파일 시스템이다.  
  • 원격 서비스는 웹 API를 통해 액세스할 수 있는 GitHub, Brave Search와 같은 외부 리소스이다.
MCP를 이용하면, 서버, 클랑이언트, 로컬에 있는 파일, 폴더, 앱에 접근해 이를 LLM(Large Language Model)으로 컨트롤할 수 있다. 

MCP는 전형적인 호스트-서버 프로토콜(TCP/IP와 유사)을 따른다. 서버의 실행 모드는  SSE(server sent event)와 stdio(표준입출력) 모드가 있다.

프로토콜은 JSON-RPC 포맷을 따르며, 예를 들어 도구 호출 시 다음과 같이 호스트-서버 간 정보를 주고 받는다. 
# 호출
{
  "jsonrpc": "2.0",
  "id": "call-1",
  "method": "callTool",
  "params": {
    "name": "create_record",
    "arguments": {
      "title": "New record",
      "content": "Record content"
    }
  }
}

# 응답
{
  "jsonrpc": "2.0",
  "id": "call-1",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Created New Record"
      }
    ]
  }
}

개발환경 설치
터미널에서 다음을 설치한다.
pip install mcp pydantic-ai fastmcp tavily-python 

에이전트 오케스트레이션 방법
작업을 수행할 수 있는 올바른 에이전트를 전달한다. 예를 들어, 사용자가 텍스트를 스페인어로 번역을 요청하는 경우 사용자 요청을 완료하려면 요청을 스페인어 에이전트로 라우팅해야 한다. 다음은 OpenAI를 이용한 오케스트레이션을 보여준다.

## OpenAI Agent Usage: Agents as tools
from agents import Agent, Runner
import asyncio

spanish_agent = Agent(
    name="Spanish agent",
    instructions="You translate the user's message to Spanish",
)

french_agent = Agent(
    name="French agent",
    instructions="You translate the user's message to French",
)

orchestrator_agent = Agent(
    name="orchestrator_agent",
    instructions=(
        "You are a translation agent. You use the tools given to you to translate."
        "If asked for multiple traconvert this articlenslations, you call the relevant tools."
    ),
    tools=[
        spanish_agent.as_tool(
            tool_name="translate_to_spanish",
            tool_description="Translate the user's message to Spanish",
        ),
        french_agent.as_tool(
            too## OpenAI Agent SDKl_name="translate_to_french",
            tool_description="Translate the user's message to French",
        ),
    ],
)

async def main():
    result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.")
    print(result.final_output)

도구 검색 및 호출 방법
MCP의 도구는 서버가 클라이언트 요청을 받아 실행 가능한 기능을 외부에 노출하고, LLM이 작업을 수행하는 데 활용할 수 있도록 설계된 요소이다. 도구는 클라이언트를 통해 검색 가능하며(`tools/list`), 작업 수행 요청은 `tools/call` 엔드포인트를 통해 전달된다. 

다음은 도구를 기술하는 메타데이터이다.
{
  name: string;          // Unique identifier for the tool
  description: string;  // Human-readable description
  inputSchema: {         // JSON Schema for the tool's parameters
    type: "object",
    properties: { ... }  // Tool-specific parameters
  }
}
 
도구는 단순한 계산부터 복잡한 API 연동까지 다양할 수 있고, 고유한 이름과 설명을 통해 식별되고 사용된다.  

도구는 다음과 같이 정의, 호출된다. 
app = Server("example-server")

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="calculate_sum",
            description="Add two numbers together",
            inputSchema={
                "type": "object",
                "properties": {
                    "a": {"type": "number"},
                    "b": {"type": "number"}
                },
                "required": ["a", "b"]
            }
        )
    ]

@app.call_tool()
async def call_tool(
    name: str,
    arguments: dict
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
    if name == "calculate_sum":
        a = arguments["a"]
        b = arguments["b"]
        result = a + b
        return [types.TextContent(type="text", text=str(result))]
    raise ValueError(f"Tool not found: {name}")

MCP 도구를 구현할 때는 다음과 같은 권장사항을 따르는 것이 좋다.  

도구는 명확한 이름과 설명을 갖추고, 매개변수는 JSON 스키마로 상세히 정의하며, 사용 예제를 포함해 모델이 이해하고 활용할 수 있도록 한다. 작업의 안정성과 신뢰성을 위해 오류 처리와 유효성 검사를 구현하고, 긴 작업은 진행 상황 보고를 포함하며, 원자성을 유지해야 한다.  
반환 값 구조는 문서화하고, 적절한 시간 초과와 리소스 사용 제한을 설정하며, 디버깅 및 모니터링을 위한 로깅 기능도 포함해야 한다.

간단한 MCP 서버 클라이언트 개발 실습
mcp_server.py란 이름으로 서버를 다음과 같이 코딩한다.
from mcp.server.fastmcp import FastMCP
import math

# instantiate an MCP server client
mcp = FastMCP("Hello World")

@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return int(a + b)

@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"

if __name__ == "__main__":
mcp.run(transport="stdio")

터미널에서 다음과 같이 MCP 서버 명령을 실행한다.
mcp dev mcp_server.py

만약, npm error code SELF_SIGNED_CERT_IN_CHAIN 에러가 발생한다면, 방화벽 때문이니 다른 네트웍에서 시도하거나, 해당 방화벽 옵션을 꺼야 한다. 서버가 제대로 실행되면, 다음 명령이 성공할 것이다.
curl -i http://localhost:5173/sse

서버가 실행되었다면, http://localhost:5173/#roots 링크에 접속한다. 

다음과 같이 mcp_client.py 클라이언트를 코딩한다.
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.sse import sse_client
from mcp.client.stdio import stdio_client

async def run():
    async with sse_client(url="http://localhost:5173/sse") as streams:
        async with ClientSession(*streams) as session:
            await session.initialize()
            tools = await session.get_tools()
            print(tools)

            result = await session.run("calculate power 2, 4?")
            print(result.data)

if __name__ == "__main__":
    import asyncio
    asyncio.run(run())

만약 stdio 방식을 사용하면, 다음과 같이 서버를 지정하고 stdio_client를 사용한다.
server_params = StdioServerParameters(
    command="python",  # Executable
    args=["F:\\projects\\mcp_agent\\mcp-server-client\\mcp_server.py"],  # Optional command line arguments
    env=None,  # Optional environment variables
)

...
    async with stdio_client(server_params) as streams:

pydantic_ai를 이용한 클라이언트 코드는 다음과 같다. 
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPServerHTTP
from dotenv import load_dotenv
import os, asyncio

load_dotenv()

server = MCPServerHTTP(url='http://localhost:5173/sse')  # Ensure the server URL is correct and supports SSE 
agent = Agent('openai:gpt-4o', mcp_servers=[server])  

result = agent.run_sync('calculate power 2, 4?')
print(result.data)


파이썬 MCP Claude 기반 실습
파이썬 MCP Claude 기반 실습을 위해 다음 명령을 터미널에서 실행한다. 참고로, 미리 Claude 앱이 컴퓨터에 설치되어 있어야 한다.
git clone https://github.com/modelcontextprotocol/python-sdk.git
cd python-sdk
pip install mcp

vscode로 다음 server.py파일을 코딩한다. 
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Demo")

# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b

# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
    """Get a personalized greeting"""
    return f"Hello, {name}!"

다음과 같이 Claude 서버에 설치한다. 
mcp install server.py

MCP 검사기로 테스트한다.
mcp dev server.py

Ollama MCP-CLI 기반 실습
이 장은 Ollama, mcp-cli 도구를 이용해 MCP 서버에 등록된 도구를 호출하는 방법을 간략히 실습한다. 
터미널에서 다음 명령을 실행한다.

mcp서버 실헹에 필요한 UV를 설치한다.
pip install uv
uv sync --reinstall

다음과 같이 Ollama를 실행한다.
ollama run llama3.2

새로운 터미널 창에서 다음 명령을 실행한다.
uv run mcp-cli chat --server filesystem --provider ollama --model llama3.2

레퍼런스

댓글 없음:

댓글 쓰기