번역 자료 / / 2025. 5. 22. 15:30

MCP 도구

원문: https://modelcontextprotocol.io/docs/concepts/tools

LLM이 서버를 통해 작업을 수행할 수 있게 하기

도구(Tools)는 Model Context Protocol(MCP)의 강력한 기본 요소로, 서버가 실행 가능한 기능을 클라이언트에 노출할 수 있게 합니다. 도구를 통해 LLM은 외부 시스템과 상호작용하고, 계산을 수행하며, 실제 세계에서 작업을 수행할 수 있습니다.

도구는 모델 제어(model-controlled)가 되도록 설계되었습니다. 이는 AI 모델이 자동으로 호출할 수 있도록(승인을 부여하는 사람이 포함된 상태에서) 서버에서 클라이언트로 도구가 노출된다는 의미입니다.

개요

MCP의 도구는 서버가 클라이언트에 의해 호출되고 LLM이 작업을 수행하는 데 사용할 수 있는 실행 가능한 함수를 노출할 수 있게 합니다. 도구의 주요 측면은 다음과 같습니다:

  • 검색: 클라이언트는 tools/list 엔드포인트를 통해 사용 가능한 도구를 나열할 수 있습니다
  • 호출: 도구는 tools/call 엔드포인트를 사용하여 호출되며, 서버는 요청된 작업을 수행하고 결과를 반환합니다
  • 유연성: 도구는 간단한 계산부터 복잡한 API 상호작용까지 다양할 수 있습니다

리소스와 마찬가지로, 도구는 고유한 이름으로 식별되며 사용 방법을 안내하기 위한 설명을 포함할 수 있습니다. 그러나 리소스와 달리, 도구는 상태를 수정하거나 외부 시스템과 상호작용할 수 있는 동적 작업을 나타냅니다.

도구 정의 구조

각 도구는 다음 구조로 정의됩니다:

{
  name: string;          // 도구의 고유 식별자
  description?: string;  // 사람이 읽을 수 있는 설명
  inputSchema: {         // 도구 매개변수의 JSON 스키마
    type: "object",
    properties: { ... }  // 도구별 매개변수
                },
  annotations?: {        // 도구 동작에 대한 선택적 힌트
    title?: string;      // 도구의 사람이 읽을 수 있는 제목
    readOnlyHint?: boolean;    // true인 경우 도구가 환경을 수정하지 않음
    destructiveHint?: boolean; // true인 경우 도구가 파괴적 업데이트를 수행할 수 있음
    idempotentHint?: boolean;  // true인 경우 동일한 인수로 반복 호출해도 추가 효과 없음
    openWorldHint?: boolean;   // true인 경우 도구가 외부 엔티티와 상호작용함
  }
}

도구 구현하기

다음은 MCP 서버에서 기본 도구를 구현하는 예시입니다:

  • TypeScript
  • Python
const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {}
  }
});

// 사용 가능한 도구 정의
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [{
      name: "calculate_sum",
      description: "두 숫자를 더합니다",
      inputSchema: {
        type: "object",
        properties: {
          a: { type: "number" },
          b: { type: "number" }
        },
        required: ["a", "b"]
      }
    }]
  };
});

// 도구 실행 처리
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "calculate_sum") {
    const { a, b } = request.params.arguments;
    return {
      content: [
        {
          type: "text",
          text: String(a + b)
        }
      ]
    };
  }
  throw new Error("도구를 찾을 수 없습니다");
});
app = Server("example-server")

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="calculate_sum",
            description="두 숫자를 더합니다",
            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"도구를 찾을 수 없습니다: {name}")

도구 패턴 예시

서버가 제공할 수 있는 도구 유형의 예시는 다음과 같습니다:

시스템 작업

로컬 시스템과 상호작용하는 도구:

{
  name: "execute_command",
  description: "셸 명령어 실행",
  inputSchema: {
    type: "object",
    properties: {
      command: { type: "string" },
      args: { type: "array", items: { type: "string" } }
    }
  }
}

API 통합

외부 API를 래핑하는 도구:

{
  name: "github_create_issue",
  description: "GitHub 이슈 생성",
  inputSchema: {
    type: "object",
    properties: {
      title: { type: "string" },
      body: { type: "string" },
      labels: { type: "array", items: { type: "string" } }
    }
  }
}

데이터 처리

데이터를 변환하거나 분석하는 도구:

{
  name: "analyze_csv",
  description: "CSV 파일 분석",
  inputSchema: {
    type: "object",
    properties: {
      filepath: { type: "string" },
      operations: {
        type: "array",
        items: {
          enum: ["sum", "average", "count"]
        }
      }
    }
  }
}

모범 사례

도구를 구현할 때:

  1. 명확하고 설명적인 이름과 설명 제공
  2. 매개변수에 대한 상세한 JSON 스키마 정의 사용
  3. 모델이 어떻게 사용해야 하는지 보여주기 위해 도구 설명에 예시 포함
  4. 적절한 오류 처리 및 검증 구현
  5. 긴 작업에는 진행 상황 보고 사용
  6. 도구 작업을 집중적이고 원자적으로 유지
  7. 예상되는 반환 값 구조 문서화
  8. 적절한 타임아웃 구현
  9. 리소스 집약적 작업에 대한 속도 제한 고려
  10. 디버깅 및 모니터링을 위한 도구 사용 로깅

보안 고려사항

도구를 노출할 때:

입력 검증

  • 모든 매개변수를 스키마에 대해 검증
  • 파일 경로 및 시스템 명령어 정제
  • URL 및 외부 식별자 검증
  • 매개변수 크기 및 범위 확인
  • 명령어 인젝션 방지

접근 제어

  • 필요한 경우 인증 구현
  • 적절한 권한 부여 확인 사용
  • 도구 사용 감사
  • 요청 속도 제한
  • 남용 모니터링

오류 처리

  • 내부 오류를 클라이언트에 노출하지 않음
  • 보안 관련 오류 로깅
  • 타임아웃 적절히 처리
  • 오류 후 리소스 정리
  • 반환 값 검증

도구 검색 및 업데이트

MCP는 동적 도구 검색을 지원합니다:

  1. 클라이언트는 언제든지 사용 가능한 도구를 나열할 수 있습니다
  2. 서버는 notifications/tools/list_changed를 사용하여 도구가 변경될 때 클라이언트에 알릴 수 있습니다
  3. 런타임 중에 도구를 추가하거나 제거할 수 있습니다
  4. 도구 정의를 업데이트할 수 있습니다(단, 이는 신중하게 수행해야 함)

오류 처리

도구 오류는 MCP 프로토콜 수준 오류가 아닌 결과 객체 내에서 보고되어야 합니다. 이를 통해 LLM이 오류를 보고 잠재적으로 처리할 수 있습니다. 도구가 오류를 발생시킬 때:

  1. 결과에서 isErrortrue로 설정
  2. content 배열에 오류 세부 정보 포함

다음은 도구에 대한 적절한 오류 처리 예시입니다:

  • TypeScript
  • Python
try {
  // 도구 작업
  const result = performOperation();
  return {
    content: [
      {
        type: "text",
        text: `작업 성공: ${result}`
      }
    ]
  };
} catch (error) {
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: `오류: ${error.message}`
      }
    ]
  };
}
try:
    # 도구 작업
    result = perform_operation()
    return types.CallToolResult(
        content=[
            types.TextContent(
                type="text",
                text=f"작업 성공: {result}"
            )
        ]
    )
except Exception as error:
    return types.CallToolResult(
        isError=True,
        content=[
            types.TextContent(
                type="text",
                text=f"오류: {str(error)}"
            )
        ]
    )

이 접근 방식을 통해 LLM은 오류가 발생했음을 확인하고 잠재적으로 수정 조치를 취하거나 사용자 개입을 요청할 수 있습니다.

도구 주석

도구 주석은 도구의 동작에 대한 추가 메타데이터를 제공하여 클라이언트가 도구를 표시하고 관리하는 방법을 이해하는 데 도움을 줍니다. 이러한 주석은 도구의 특성과 영향을 설명하는 힌트이지만, 보안 결정을 위해 의존해서는 안 됩니다.

도구 주석의 목적

도구 주석은 다음과 같은 여러 가지 주요 목적을 제공합니다:

  1. 모델 컨텍스트에 영향을 주지 않고 UX 관련 정보 제공
  2. 클라이언트가 도구를 적절하게 분류하고 표시하는 데 도움
  3. 도구의 잠재적 부작용에 대한 정보 전달
  4. 도구 승인을 위한 직관적인 인터페이스 개발 지원

사용 가능한 도구 주석

MCP 사양은 도구에 대해 다음과 같은 주석을 정의합니다:

주석 타입 기본값 설명
title string - UI 표시에 유용한 도구의 사람이 읽을 수 있는 제목
readOnlyHint boolean false true인 경우, 도구가 환경을 수정하지 않음을 나타냄
destructiveHint boolean true true인 경우, 도구가 파괴적 업데이트를 수행할 수 있음(readOnlyHint가 false일 때만 의미 있음)
idempotentHint boolean false true인 경우, 동일한 인수로 도구를 반복적으로 호출해도 추가 효과가 없음(readOnlyHint가 false일 때만 의미 있음)
openWorldHint boolean true true인 경우, 도구가 외부 엔티티의 "오픈 월드"와 상호작용할 수 있음

사용 예시

다음은 다양한 시나리오에 대한 주석이 있는 도구를 정의하는 방법입니다:

// 읽기 전용 검색 도구
{
  name: "web_search",
  description: "정보를 위해 웹 검색",
  inputSchema: {
    type: "object",
    properties: {
      query: { type: "string" }
    },
    required: ["query"]
  },
  annotations: {
    title: "웹 검색",
    readOnlyHint: true,
    openWorldHint: true
  }
}

// 파괴적인 파일 삭제 도구
{
  name: "delete_file",
  description: "파일 시스템에서 파일 삭제",
  inputSchema: {
    type: "object",
    properties: {
      path: { type: "string" }
    },
    required: ["path"]
  },
  annotations: {
    title: "파일 삭제",
    readOnlyHint: false,
    destructiveHint: true,
    idempotentHint: true,
    openWorldHint: false
  }
}

// 비파괴적 데이터베이스 레코드 생성 도구
{
  name: "create_record",
  description: "데이터베이스에 새 레코드 생성",
  inputSchema: {
    type: "object",
    properties: {
      table: { type: "string" },
      data: { type: "object" }
    },
    required: ["table", "data"]
  },
  annotations: {
    title: "데이터베이스 레코드 생성",
    readOnlyHint: false,
    destructiveHint: false,
    idempotentHint: false,
    openWorldHint: false
  }
}

서버 구현에 주석 통합하기

  • TypeScript
  • Python
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [{
      name: "calculate_sum",
      description: "두 숫자를 더합니다",
      inputSchema: {
        type: "object",
        properties: {
          a: { type: "number" },
          b: { type: "number" }
        },
        required: ["a", "b"]
      },
      annotations: {
        title: "합계 계산",
        readOnlyHint: true,
        openWorldHint: false
      }
    }]
  };
});
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("example-server")

@mcp.tool(
    annotations={
        "title": "합계 계산",
        "readOnlyHint": True,
        "openWorldHint": False
    }
)
async def calculate_sum(a: float, b: float) -> str:
    """두 숫자를 더합니다.

    Args:
        a: 더할 첫 번째 숫자
        b: 더할 두 번째 숫자
    """
    result = a + b
    return str(result)

도구 주석에 대한 모범 사례

  1. 부작용에 대해 정확하게 표시: 도구가 환경을 수정하는지 여부와 수정이 파괴적인지 여부를 명확하게 표시합니다.
  2. 설명적인 제목 사용: 도구의 목적을 명확하게 설명하는 사람 친화적인 제목을 제공합니다.
  3. 멱등성을 적절하게 표시: 동일한 인수로 반복 호출해도 추가 효과가 없는 경우에만 도구를 멱등적으로 표시합니다.
  4. 적절한 개방/폐쇄 세계 힌트 설정: 도구가 폐쇄 시스템(데이터베이스와 같은) 또는 개방 시스템(웹과 같은)과 상호작용하는지 표시합니다.
  5. 주석은 힌트임을 기억하세요: ToolAnnotations의 모든 속성은 힌트이며 도구 동작에 대한 충실한 설명을 제공한다고 보장할 수 없습니다. 클라이언트는 주석만을 기반으로 보안 중요 결정을 내려서는 안 됩니다.

도구 테스트

MCP 도구에 대한 종합적인 테스트 전략은 다음을 포함해야 합니다:

  • 기능 테스트: 도구가 유효한 입력으로 올바르게 실행되고 유효하지 않은 입력을 적절히 처리하는지 확인
  • 통합 테스트: 실제 및 모의 종속성을 모두 사용하여 외부 시스템과의 도구 상호작용 테스트
  • 보안 테스트: 인증, 권한 부여, 입력 정제 및 속도 제한 검증
  • 성능 테스트: 부하 하에서의 동작, 타임아웃 처리 및 리소스 정리 확인
  • 오류 처리: 도구가 MCP 프로토콜을 통해 오류를 적절히 보고하고 리소스를 정리하는지 확인
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유