도구
도구(tools)는 Model Context Protocol(MCP)에서 매우 중요한 기본 요소(primitive)로, 서버가 실행 가능한 기능을 클라이언트에 노출할 수 있게 합니다. 도구를 통해 LLM은 외부 시스템과 상호작용하고, 계산을 수행하며, 실제 세계에서 작업을 실행할 수 있습니다.
개요
MCP의 도구는 서버가 실행 가능한 함수를 노출하도록 하며, 클라이언트가 이를 호출하고 LLM이 작업을 수행하는 데 사용할 수 있습니다. 주요 특징은 다음과 같습니다:
- 탐색(Discovery): 클라이언트는
tools/list엔드포인트로 사용 가능한 도구를 나열할 수 있습니다. - 호출(Invocation):
tools/call엔드포인트로 도구를 호출하면, 서버가 요청된 작업을 수행하고 결과를 반환합니다. - 유연성(Flexibility): 간단한 계산부터 복잡한 외부 API 호출까지 폭넓게 구현할 수 있습니다.
리소스(resources)처럼 도구도 고유한 이름으로 식별되며, 사용을 돕는 설명을 포함할 수 있습니다. 다만 리소스와 달리 도구는 상태를 변경하거나 외부 시스템과 상호작용할 수 있는 동적 작업을 나타냅니다.
도구 정의 구조
각 도구는 다음 구조로 정의됩니다:
{
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
}
}도구 구현
MCP 서버에서 기본 도구를 구현하는 예시는 다음과 같습니다:
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "calculate_sum",
description: "Add two numbers together",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" }
},
required: ["a", "b"]
}
}]
};
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "calculate_sum") {
const { a, b } = request.params.arguments;
return {
toolResult: a + b
};
}
throw new Error("Tool not found");
});
```
@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}")
```
도구 패턴 예시
서버가 제공할 수 있는 도구 유형 예시는 다음과 같습니다:
시스템 작업
로컬 시스템과 상호작용하는 도구:
{
name: "execute_command",
description: "Run a shell command",
inputSchema: {
type: "object",
properties: {
command: { type: "string" },
args: { type: "array", items: { type: "string" } }
}
}
}API 통합
외부 API를 래핑하는 도구:
{
name: "github_create_issue",
description: "Create a GitHub issue",
inputSchema: {
type: "object",
properties: {
title: { type: "string" },
body: { type: "string" },
labels: { type: "array", items: { type: "string" } }
}
}
}데이터 처리
데이터를 변환하거나 분석하는 도구:
{
name: "analyze_csv",
description: "Analyze a CSV file",
inputSchema: {
type: "object",
properties: {
filepath: { type: "string" },
operations: {
type: "array",
items: {
enum: ["sum", "average", "count"]
}
}
}
}
}모범 사례
도구를 구현할 때는:
- 명확하고 설명적인 이름/설명을 제공합니다.
- 파라미터는 상세한 JSON Schema로 정의합니다.
- 모델이 어떻게 사용해야 하는지 알 수 있도록 설명에 예시를 포함합니다.
- 적절한 오류 처리와 입력 검증을 구현합니다.
- 시간이 오래 걸리는 작업은 progress 리포팅을 사용합니다.
- 도구 작업은 작고 원자적(atomic)으로 유지합니다.
- 기대하는 반환값 구조를 문서화합니다.
- 적절한 타임아웃을 구현합니다.
- 리소스 집약 작업은 레이트 리밋을 고려합니다.
- 디버깅/모니터링을 위해 도구 사용을 로깅합니다.
보안 고려사항
도구를 노출할 때는:
입력 검증
- 모든 파라미터를 스키마에 따라 검증합니다.
- 파일 경로와 시스템 커맨드를 정규화/무해화(sanitize)합니다.
- URL과 외부 식별자를 검증합니다.
- 파라미터 크기와 범위를 검사합니다.
- 커맨드 인젝션을 방지합니다.
접근 제어
- 필요 시 인증(authentication)을 구현합니다.
- 적절한 인가(authorization) 검사를 적용합니다.
- 도구 사용을 감사(audit)합니다.
- 요청에 레이트 리밋을 적용합니다.
- 남용 여부를 모니터링합니다.
오류 처리
- 내부 오류를 클라이언트에 그대로 노출하지 않습니다.
- 보안 관련 오류는 로깅합니다.
- 타임아웃을 적절히 처리합니다.
- 오류 발생 시 리소스를 정리(cleanup)합니다.
- 반환값을 검증합니다.
도구 탐색 및 업데이트
MCP는 동적인 도구 탐색을 지원합니다:
- 클라이언트는 언제든지 사용 가능한 도구 목록을 조회할 수 있습니다.
- 서버는
notifications/tools/list_changed로 도구 변경을 클라이언트에 알릴 수 있습니다. - 런타임 중에도 도구를 추가/제거할 수 있습니다.
- 도구 정의를 업데이트할 수 있지만(주의 필요), 호환성과 모델 동작에 영향을 줄 수 있습니다.
오류 처리
도구 오류는 MCP 프로토콜 레벨 오류로 올리는 대신, 결과(result) 객체 안에서 보고해야 합니다. 이렇게 하면 LLM이 오류를 인지하고, 수정 조치(또는 사람에게 도움 요청)를 할 수 있습니다. 도구에서 오류가 발생하면:
- 결과에
isError: true를 설정합니다. content배열에 오류 세부 정보를 포함합니다.
도구의 올바른 오류 처리 예시는 다음과 같습니다:
이 접근은 LLM이 오류 발생 사실을 확인하고, 문제를 바로잡거나 사람의 개입을 요청할 수 있게 합니다.
도구 테스트
MCP 도구를 위한 종합 테스트 전략은 다음을 포함해야 합니다:
- 기능 테스트: 유효 입력에서 올바르게 실행되는지, 잘못된 입력을 적절히 처리하는지 검증
- 통합 테스트: 실제/목(mock) 의존성을 사용해 외부 시스템과의 상호작용을 검증
- 보안 테스트: 인증/인가, 입력 sanitize, 레이트 리밋 검증
- 성능 테스트: 부하 상황에서의 동작, 타임아웃 처리, 리소스 정리 확인
- 오류 처리: MCP 프로토콜을 통해 오류를 올바르게 보고하고 리소스를 정리하는지 확인