도구
도구 (Tools)
서버가 언어 모델이 호출할 수 있는 도구를 노출하는 방법을 설명하는 페이지예요. 도구는 모델이 데이터베이스 조회, API 호출, 계산 수행 같은 외부 시스템과 상호작용할 수 있게 해 주며, 각 도구는 이름과 스키마를 설명하는 메타데이터로 고유하게 식별돼요.
출처: 문서
본문
Model Context Protocol (MCP)은 서버가 언어 모델이 호출할 수 있는 도구를 노출할 수 있게 해 줘요. 도구는 모델이 데이터베이스 조회, API 호출, 계산 수행 같은 외부 시스템과 상호작용할 수 있게 해 줘요. 각 도구는 이름으로 고유하게 식별되며 스키마를 설명하는 메타데이터를 포함해요.
참고 (Note): 간결함을 위해 이 페이지의 요청 예시는
_meta요청 메타데이터(io.modelcontextprotocol/protocolVersion,io.modelcontextprotocol/clientInfo,io.modelcontextprotocol/clientCapabilities)를 생략해요. 모든 요청은 MUST 필수_meta필드를 포함해야 해요._meta를 참조하세요.
사용자 상호작용 모델 (User Interaction Model)
MCP의 도구는 **모델 통제(model-controlled)**로 설계됐어요. 즉 언어 모델이 컨텍스트 이해와 사용자 프롬프트에 기반해 도구를 자동으로 발견하고 호출할 수 있다는 뜻이에요.
하지만 구현자는 자신의 필요에 맞는 어떤 인터페이스 패턴으로든 도구를 노출할 자유가 있어요. 프로토콜 자체는 특정 사용자 상호작용 모델을 강제하지 않아요.
경고 (Warning): 신뢰·안전과 보안을 위해, 도구 호출을 거부할 수 있는 능력을 가진 사람(human)이 항상 루프 안에 있어야 SHOULD 해요.
애플리케이션은 SHOULD:
- AI 모델에 어떤 도구가 노출되고 있는지 명확히 하는 UI를 제공한다
- 도구가 호출될 때 명확한 시각적 표시를 넣는다
- 루프에 사람이 있도록 보장하기 위해 작업에 대한 확인 프롬프트를 사용자에게 제시한다
기능 (Capabilities)
도구를 지원하는 서버는 MUST tools 기능을 선언해야 해요:
{
"capabilities": {
"tools": {
"listChanged": true
}
}
}
listChanged는 사용 가능한 도구 목록이 바뀔 때 서버가 알림을 보낼지 여부를 나타내요.
tools 기능을 선언하는 서버는 MUST tools/list 요청에 요청한 클라이언트에게 현재 사용 가능한 도구 집합으로 응답해야 해요. 이 집합은 MAY 비어 있을 수 있고 MAY 시간이 지나며 바뀔 수 있지만(List Changed Notification 참조), 연결별로 또는 연결의 다른 요청의 부수 효과로 MUST NOT 달라지면 안 돼요. 이 집합은 MAY 요청에 제시된 권한 부여에 따라 달라질 수 있어요. 예를 들어 호출자의 부여된 범위가 허용하는 도구만 반환하는 것은, 자격 증명이 요청별 입력이지 연결 상태가 아니기 때문에 허용돼요.
서버는 SHOULD 결정적 순서로 도구를 반환해야 해요 (즉, 기본 도구 집합이 바뀌지 않았을 때 요청들 간에 같은 순서). 결정적 순서는 클라이언트가 도구 목록을 안정적으로 캐시할 수 있게 하고, 도구가 모델 컨텍스트에 포함될 때 LLM 프롬프트 캐시 히트율을 향상시켜요.
프로토콜 메시지 (Protocol Messages)
도구 나열하기 (Listing Tools)
사용 가능한 도구를 발견하려면 클라이언트는 tools/list 요청을 보내요. 이 작업은 pagination과 caching을 지원해요.
요청:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"cursor": "optional-cursor-value"
}
}
응답:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"tools": [
{
"name": "get_weather",
"title": "Weather Information Provider",
"description": "Get current weather information for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or zip code"
}
},
"required": ["location"]
},
"icons": [
{
"src": "https://example.com/weather-icon.png",
"mimeType": "image/png",
"sizes": ["48x48"]
}
]
}
],
"nextCursor": "next-page-cursor",
"ttlMs": 300000,
"cacheScope": "public"
}
}
도구 호출하기 (Calling Tools)
도구를 호출하려면 클라이언트는 tools/call 요청을 보내요:
요청:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"location": "New York"
}
}
}
응답:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
}
],
"isError": false
}
}
입력 필요 도구 결과 (Input Required Tool Results)
서버는 MAY 도구 호출이 완료되기 전에 추가 입력이 필요함을 나타내기 위해 tools/call에 InputRequiredResult로 응답할 수 있어요. 이는 다중 왕복 요청 메커니즘을 따라요.
입력 응답으로 요청을 재시도할 때 클라이언트는 요청 파라미터에 inputResponses와, 서버가 제공했다면 requestState를 포함해요:
입력 필요 응답 (Input Required Response):
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"resultType": "input_required",
"inputRequests": {
"github_login": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Please provide your GitHub username",
"requestedSchema": {
"type": "object",
"properties": {
"name": { "type": "string" }
},
"required": ["name"]
}
}
}
},
"requestState": "eyJsb2...rIn0..."
}
}
입력 응답으로 재시도 (Retry with Input Responses):
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"location": "New York"
},
"inputResponses": {
"github_login": {
"action": "accept",
"content": {
"name": "octocat"
}
}
},
"requestState": "eyJsb2...rIn0..."
}
}
JSON-RPC id는 MUST 초기 요청과 재시도 사이에 달라야 한다는 점을 기억하세요.
목록 변경 알림 (List Changed Notification)
사용 가능한 도구 목록이 바뀌면, listChanged 기능을 선언한 서버는 SHOULD toolsListChanged: true로 subscriptions/listen 스트림을 연 클라이언트에게 알림을 보내야 해요:
{
"jsonrpc": "2.0",
"method": "notifications/tools/list_changed"
}
메시지 흐름 (Message Flow)
sequenceDiagram
participant LLM
participant Client
participant Server
Note over Client,Server: Discovery
Client->>Server: tools/list
Server-->>Client: List of tools
Note over Client,LLM: Tool Selection
LLM->>Client: Select tool to use
Note over Client,Server: Invocation
Client->>Server: tools/call
Server-->>Client: Tool result
Client->>LLM: Process result
opt listChanged
Client->>Server: subscriptions/listen (toolsListChanged: true)
Server--)Client: notifications/subscriptions/acknowledged
Note over Client,Server: Updates
Server--)Client: notifications/tools/list_changed
Client->>Server: tools/list
Server-->>Client: Updated tools
end
데이터 타입 (Data Types)
Tool
도구 정의는 다음을 포함해요:
name: 도구의 고유 식별자title: 표시용 선택적 사람이 읽을 수 있는 도구 이름.description: 기능에 대한 사람이 읽을 수 있는 설명icons: 사용자 인터페이스에 표시하기 위한 선택적 아이콘 배열inputSchema: 기대되는 파라미터를 정의하는 JSON Schema- JSON Schema 사용 지침을 따른다
$schema필드가 없으면 기본값 2020-12- MUST 유효한 JSON Schema 객체여야 한다 (
null아님) - 파라미터가 없는 도구는 다음 유효한 접근 중 하나를 사용한다:
{ "type": "object", "additionalProperties": false }- 권장: 빈 객체만 명시적으로 수락{ "type": "object" }- 모든 객체 수락 (속성 포함)
- 속성은 MAY 파라미터 값을 HTTP 헤더로 노출하는
x-mcp-headerannotation을 포함할 수 있다
outputSchema: 기대되는 출력 구조를 정의하는 선택적 JSON Schema- JSON Schema 사용 지침을 따른다
$schema필드가 없으면 기본값 2020-12
annotations: 도구 동작을 설명하는 선택적 속성
경고 (Warning): 신뢰·안전과 보안을 위해, 클라이언트는 MUST 도구 annotation을 신뢰할 수 있는 서버에서 온 것이 아니라면 신뢰할 수 없는 것으로 간주해야 해요.
도구 이름 (Tool Names)
- 도구 이름은 SHOULD 길이가 1~128자(포함)여야 한다.
- 도구 이름은 SHOULD 대소문자 구분으로 간주되어야 한다.
- 다음만 SHOULD 허용되는 문자여야 한다: 대문자·소문자 ASCII 문자(A-Z, a-z), 숫자(0-9), 밑줄(_), 하이픈(-), 점(.)
- 도구 이름은 SHOULD NOT 공백, 쉼표 또는 기타 특수 문자를 포함해서는 안 된다.
- 도구 이름은 SHOULD 서버 내에서 고유해야 한다.
- 유효한 도구 이름 예시:
getUserDATA_EXPORT_v2admin.tools.list
참고 (Note): 도구 이름 고유성은 단일 서버로 한정된다. 여러 서버의 도구를 집계하는 클라이언트나 프록시는 MAY 명명 충돌(예: 두 서버가 각각
search도구 노출)을 만날 수 있고 SHOULD 도구 이름에 서버 식별자를 접두사로 붙이는 등 구분(disambiguation) 전략을 구현해야 한다.서버의
name(serverInfo에서)은 서버 간 고유하다고 보장되지 않으므로 SHOULD NOT 구분에 의존해서는 안 된다.
x-mcp-header
x-mcp-header 확장 속성은 Streamable HTTP transport를 사용할 때 특정 도구 파라미터를 HTTP 헤더로 미러링되도록 지정할 수 있게 해 줘요. 이는 네트워크 중간자(로드 밸런서, 프록시, WAF)가 요청 본문을 파싱하지 않고 파라미터 값에 기반해 요청을 라우팅·처리할 수 있게 해 줘요.
x-mcp-header 속성은 미러링할 속성의 JSON Schema 안에 직접 배치돼요. 그 값은 결과 Mcp-Param-{name} HTTP 헤더의 이름 부분을 지정해요.
x-mcp-header 값의 제약:
- MUST NOT 비어 있으면 안 된다
- MUST HTTP 필드 이름 토큰 구문을 일치해야 한다 (
1*tchar, RFC 9110 Section 5.1) - MUST NOT 캐리지 리턴(CR,
\r)이나 라인 피드(LF,\n)를 포함한 제어 문자를 포함해서는 안 된다 - MUST
inputSchema의 모든x-mcp-header값 사이에서 대소문자 구분 없이 고유해야 한다 - MUST 원시 타입(integer, string, boolean) 파라미터에만 적용되어야 한다.
number타입의 파라미터는 허용되지 않는다. 정수 값은 IEEE754 배정밀도 부동소수점으로 표현되는 정수의 안전 범위(−253+1 ~ 253−1) 안에 MUST 있어야 한다 - MUST Custom Headers from Tool Parameters에 정의된 대로 스키마 루트에서 정적으로 도달 가능한(statically reachable) 속성에만 적용되어야 한다. 이 페이지는 또한 호출 인자에서 헤더 값을 추출하는 방법을 정의한다
Streamable HTTP transport를 사용하는 클라이언트는 MUST 어떤 x-mcp-header 값이 이 제약을 위반하는 도구 정의를 거부해야 해요. 거부는 클라이언트가 MUST tools/list 결과에서 유효하지 않은 도구를 제외해야 함을 의미해요. 클라이언트는 SHOULD 도구 정의를 거부할 때 도구 이름과 거부 이유를 포함해 경고를 로깅해야 해요. 이는 단일 잘못된 도구 정의가 다른 유효한 도구가 사용되는 것을 막지 않도록 보장해요. 다른 트랜스포트(예: stdio)를 사용하는 클라이언트는 MAY x-mcp-header annotation을 완전히 무시할 수 있어요.
x-mcp-header가 있는 도구 정의 예시:
{
"name": "execute_sql",
"description": "Execute SQL on Google Cloud Spanner",
"inputSchema": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The region to execute the query in",
"x-mcp-header": "Region"
},
"query": {
"type": "string",
"description": "The SQL query to execute"
}
},
"required": ["region", "query"]
}
}
이 예시에서 도구가 "region": "us-west1"로 호출되면, 클라이언트는 HTTP 요청에 Mcp-Param-Region: us-west1 헤더를 추가해요.
경고 (Warning): 헤더 값은 네트워크 중간자에게 보이므로, 서버 개발자는 민감한 파라미터(비밀번호, API 키, 토큰, PII)를
x-mcp-header로 표시해서는 SHOULD NOT 안 된다.
도구 결과 (Tool Result)
도구 결과는 구조화된 (structured) 또는 비구조화된 (unstructured) 콘텐츠를 담을 수 있어요.
비구조화된 콘텐츠는 결과의 content 필드에 반환되며, 서로 다른 유형의 여러 콘텐츠 항목을 담을 수 있어요:
참고 (Note): 모든 콘텐츠 유형(텍스트, 이미지, 오디오, 리소스 링크, 임베디드 리소스)은 audience, priority, 수정 시간에 대한 메타데이터를 제공하는 선택적 annotations을 지원해요. 이는 리소스와 프롬프트에서 사용하는 것과 같은 annotation 형식이에요.
텍스트 콘텐츠 (Text Content)
{
"type": "text",
"text": "Tool result text"
}
이미지 콘텐츠 (Image Content)
{
"type": "image",
"data": "base64-encoded-data",
"mimeType": "image/png",
"annotations": {
"audience": ["user"],
"priority": 0.9
}
}
오디오 콘텐츠 (Audio Content)
{
"type": "audio",
"data": "base64-encoded-audio-data",
"mimeType": "audio/wav"
}
리소스 링크 (Resource Links)
도구는 MAY 추가 컨텍스트나 데이터를 제공하기 위해 리소스에 대한 링크를 반환할 수 있어요. 이 경우 도구는 클라이언트가 구독하거나 가져올 수 있는 URI를 반환해요:
{
"type": "resource_link",
"uri": "file:///project/src/main.rs",
"name": "main.rs",
"description": "Primary application entry point",
"mimeType": "text/x-rust"
}
리소스 링크는 클라이언트가 사용 방법을 이해하도록 돕기 위해 일반 리소스와 같은 Resource annotations을 지원해요.
정보 (Info): 도구가 반환한 리소스 링크는
resources/list요청 결과에 나타난다고 보장되지 않아요.
임베디드 리소스 (Embedded Resources)
리소스는 MAY 적절한 URI 스킴을 사용해 추가 컨텍스트나 데이터를 제공하도록 임베디드될 수 있어요. 임베디드 리소스를 사용하는 서버는 SHOULD resources 기능을 구현해야 해요:
{
"type": "resource",
"resource": {
"uri": "file:///project/src/main.rs",
"mimeType": "text/x-rust",
"text": "fn main() {\n println!(\"Hello world!\");\n}",
"annotations": {
"audience": ["user", "assistant"],
"priority": 0.7,
"lastModified": "2025-05-03T14:30:00Z"
}
}
}
임베디드 리소스는 클라이언트가 사용 방법을 이해하도록 돕기 위해 일반 리소스와 같은 Resource annotations을 지원해요.
구조화된 콘텐츠 (Structured Content)
구조화된 콘텐츠는 결과의 structuredContent 필드에 JSON 값으로 반환돼요. 이는 도구의 outputSchema가 정의되어 있다면 그것을 따르는 어떤 JSON 값(객체, 배열, 문자열, 숫자, 불리언, null)일 수 있어요.
하위 호환성을 위해, 구조화된 콘텐츠를 반환하는 도구는 SHOULD 직렬화된 JSON도 TextContent 블록으로 반환해야 해요.
참고 (Note):
structuredContent는 서버가 생성한 결과 데이터이며 LLM의 "structured outputs"(스키마 제약 모델 생성)와는 무관해요.
출력 스키마 (Output Schema)
도구는 구조화된 결과 검증을 위한 출력 스키마를 제공할 수도 있어요. 출력 스키마가 제공되면:
- 서버는 MUST 이 스키마를 따르는 구조화된 결과를 제공해야 한다.
- 클라이언트는 SHOULD 이 스키마에 대해 구조화된 결과를 검증해야 한다.
출력 스키마가 있는 도구 예시:
{
"name": "get_weather_data",
"title": "Weather Data Retriever",
"description": "Get current weather data for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or zip code"
}
},
"required": ["location"]
},
"outputSchema": {
"type": "object",
"properties": {
"temperature": {
"type": "number",
"description": "Temperature in celsius"
},
"conditions": {
"type": "string",
"description": "Weather conditions description"
},
"humidity": {
"type": "number",
"description": "Humidity percentage"
}
},
"required": ["temperature", "conditions", "humidity"]
}
}
이 도구에 대한 유효한 응답 예시:
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\", \"humidity\": 65}"
}
],
"structuredContent": {
"temperature": 22.5,
"conditions": "Partly cloudy",
"humidity": 65
}
}
}
배열 출력 스키마가 있는 도구 예시:
{
"name": "list_users",
"title": "User List",
"description": "Returns a list of all users",
"inputSchema": {
"type": "object",
"properties": {}
},
"outputSchema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["id", "name", "email"]
}
}
}
배열 출력이 있는 도구에 대한 유효한 응답 예시:
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "Found 2 users: Alice ([email protected]) and Bob ([email protected])."
}
],
"structuredContent": [
{ "id": "1", "name": "Alice", "email": "[email protected]" },
{ "id": "2", "name": "Bob", "email": "[email protected]" }
]
}
}
출력 스키마를 제공하면 클라이언트와 LLM이 구조화된 도구 출력을 올바르게 이해·처리하는 데 도움을 줘요:
- 응답의 엄격한 스키마 검증 가능
- 프로그래밍 언어와의 더 나은 통합을 위한 타입 정보 제공
- 클라이언트와 LLM이 반환된 데이터를 올바르게 파싱·활용하도록 안내
- 더 나은 문서화와 개발자 경험 지원
스키마 예시 (Schema Examples)
기본 2020-12 스키마가 있는 도구:
{
"name": "calculate_sum",
"description": "Add two numbers",
"inputSchema": {
"type": "object",
"properties": {
"a": { "type": "number" },
"b": { "type": "number" }
},
"required": ["a", "b"]
}
}
명시적 draft-07 스키마가 있는 도구:
{
"name": "calculate_sum",
"description": "Add two numbers",
"inputSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"a": { "type": "number" },
"b": { "type": "number" }
},
"required": ["a", "b"]
}
}
파라미터가 없는 도구:
{
"name": "get_current_time",
"description": "Returns the current server time",
"inputSchema": {
"type": "object",
"additionalProperties": false
}
}
상태를 유지하는 도구 (Stateful Tools)
참고 (Note): 이 섹션은 도구 설계를 위한 비규범적(non-normative) 안내예요. 프로토콜에는 상태 핸들이라는 개념이 없어요. 와이어 관점에서 핸들은 도구 결과의 평범한 문자열이자 이후 도구 호출의 평범한 인자일 뿐이에요.
MCP에는 프로토콜 수준 세션이 없으므로, 서버는 암시적 per-connection 상태에 의존해 한 도구 호출과 다음 호출을 연관시킬 수 없어요. 호출 전반에 걸쳐 상태를 유지해야 하는 서버(쇼핑 카트, 열린 브라우저 컨텍스트, 데이터베이스 트랜잭션)는 생성 도구에서 명시적 핸들을 반환하고, 이후 호출에서 그 핸들을 인자로 받아야 해요.
예를 들어 쇼핑 카트를 관리하는 서버는 다음을 노출할 수 있어요:
// → tools/call
{ "name": "create_basket", "arguments": {} }
// ← result
{
"content": [{ "type": "text", "text": "Created basket bsk_a1b2c3" }],
"structuredContent": { "basket_id": "bsk_a1b2c3" }
}
// → tools/call
{
"name": "add_item",
"arguments": { "basket_id": "bsk_a1b2c3", "sku": "..." }
}
모델은 basket_id를 계속 가져가는 책임이 있고, 서버는 그 키 아래에 카트 내용을 저장해 각 호출에서 조회해요.
핸들을 설계할 때 서버는 다음을 고려해야 해요:
- 권한 부여 (Authorization). 인증된 서버에서 핸들은 이름이지 능력(capability)이 아니에요. 서버는 매 호출에서 호출자의 권한 부여를 핸들에 대해 검증해야 해요. 인증되지 않은 서버에서 핸들은 필연적으로 bearer 토큰이므로, 충분한 엔트로피(예: UUIDv4)로 생성하고 제한된 수명을 주어야 해요.
- 불투명성 (Opacity). 내부 구조를 인코딩하는 핸들은 파싱이나 추측을 부르지만, 불투명한 식별자는 그렇지 않아요.
- 수명 (Lifetime). 핸들은 단일 연결보다 오래 살아남으므로, 서버의 보존 정책을 생성 도구의 description에 명시해야 해요 (예: "baskets expire after 24 hours of inactivity"), 그래야 모델이 상태 생성 여부를 결정할 때 볼 수 있어요.
- 만료 오류 (Expiry errors). 만료되었거나 알 수 없는 핸들에 대한 호출은 그 사실을 말하는 도구 실행 오류를 반환해야 해요. 그래야 모델이 새 핸들을 만들어 복구할 수 있어요.
오류 처리 (Error Handling)
도구는 두 가지 오류 보고 메커니즘을 사용해요:
-
프로토콜 오류 (Protocol Errors) 는 요청 구조 자체의 문제를 나타내며, 모델이 고치기 어려울 가능성이 높은 것들이에요:
- 알 수 없는 도구
- 잘못된 요청(CallToolRequest 스키마를 충족하지 못하는 요청)
- 서버 오류
이들은 표준 JSON-RPC 오류로 반환돼요:
{ "jsonrpc": "2.0", "id": 3, "error": { "code": -32602, "message": "Unknown tool: invalid_tool_name" } } -
도구 실행 오류 (Tool Execution Errors) 는 언어 모델이 스스로 고치고 조정된 파라미터로 재시도할 수 있는 실행 가능한 피드백을 담아요:
- API 실패
- 입력 검증 오류(예: 날짜가 잘못된 형식, 값이 범위를 벗어남)
- 비즈니스 로직 오류
이들은
isError: true로 도구 결과에 보고돼요:{ "jsonrpc": "2.0", "id": 4, "result": { "resultType": "complete", "content": [ { "type": "text", "text": "Invalid departure date: must be in the future. Current date is 08/08/2025." } ], "isError": true } }
클라이언트는 MAY 프로토콜 오류를 언어 모델에 제공할 수 있지만, 이는 성공적인 복구로 이어질 가능성이 낮아요. 클라이언트는 SHOULD 도구 실행 오류를 언어 모델에 제공해 자기 교정을 가능하게 해야 해요.
보안 고려 사항 (Security Considerations)
-
서버는 MUST:
- 모든 도구 입력을 검증한다
- 적절한 접근 통제를 구현한다
- 도구 호출에 비율 제한(rate limit)을 적용한다
- 도구 출력을 살균한다
-
클라이언트는 SHOULD:
- 민감한 작업에 사용자 확인을 요청한다
- 악의적이거나 우연한 데이터 유출을 피하려고 서버를 호출하기 전에 사용자에게 도구 입력을 보여준다
- LLM에 전달하기 전에 도구 결과를 검증한다
inputSchema와outputSchema에 대해 도구 입력·출력을 검증할 때$ref해석 요구사항을 따른다- 도구 호출에 시간 초과(timeouts)를 구현한다
- 감사 목적으로 도구 사용을 로깅한다