تعریف tools (Defining tools)
عنوان اصلی: Defining tools
هدف یادگیری: تعریف یک tool: name، description، inputSchema با JSON Schema، و یک handler که content برمیگرداند.
مفاهیم کلیدی: tools/list، tools/call، inputSchema، outputSchema، tool annotations (readOnlyHint، destructiveHint، idempotentHint، openWorldHint)، انواع content (text/image/audio/resource_link/embedded resource)، isError.
ابزارها model-controlled هستند — LLM بر اساس قصد کاربر آنها را انتخاب میکند. تعریف یک tool شامل: یک name یکتا، title انسانی اختیاری، یک description که LLM برای انتخاب از آن استفاده میکند، یک inputSchema (JSON Schema)، و اختیاری یک outputSchema برای نتایج structured. سرورها باید capability tools را اعلام کنند و بهتر است اگر میخواهند tool را بهصورت dynamic اضافه/حذف کنند، listChanged: true را ست کنند.
نتیجه یک tool یک آرایه از content item است. انواع content: text، image (base64 + MIME)، audio، resource_link (مرجع URI)، و resource embedded (محتوای کامل inline). وقتی چیزی اشتباه میشود، server یک نتیجه عادی با isError: true و یک text item توضیحی برمیگرداند — این به LLM اجازه میدهد خطا را بخواند و واکنش نشان دهد. JSON-RPC error response (-32602 Invalid params، -32601 Method not found، -32603 Internal error) را برای خطاهای protocol-level مثل tool name ناشناس نگه دارید.
annotationها (readOnlyHint، destructiveHint، idempotentHint، openWorldHint) advisory هستند — clientها باید آنها را untrusted بدانند مگر اینکه خود server trusted باشد. وجود دارند تا host بتواند تصمیم بگیرد auto-approve کند، از کاربر تایید بخواهد، یا رد کند.
مثال عملی — Python (سه tool)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
@mcp.tool()
def get_weather(city: str, unit: str = "celsius") -> str:
"""Get weather for a city."""
return f"Weather in {city}: 22 degrees{unit[0].upper()}"
@mcp.tool()
async def long_running(items: list[str]) -> str:
"""Example async tool that does I/O without blocking the STDIO loop."""
return f"Processed {len(items)} items"
مثال عملی — TypeScript (tool با Zod typed)
import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import * as z from 'zod/v4';
const server = new McpServer({ name: 'greeting-server', version: '1.0.0' });
server.registerTool(
'greet',
{
description: 'Greet someone by name',
inputSchema: z.object({ name: z.string() }),
},
async ({ name }) => ({
content: [{ type: 'text', text: `Hello, ${name}!` }],
}),
);
await server.connect(new StdioServerTransport());
مثال عملی — JSON-RPC خام روی wire
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "get_weather", "arguments": { "city": "New York" } } }
دیاگرام معماری (متنی): یک swim-lane با LLM، Client، Server. LLM → Client: «select tool». Client → Server: tools/call. Server → Client: result content. Client → LLM: «tool result here, continue».
اشتباهات رایج
- برگرداندن traceback Python از handler یک tool بهجای content با
isError: true. LLM خطاهای protocol را نمیبیند. - نامگذاری بیش از حد عمومی tools (
run,query) که وقتی چند server وصلاند، مدل نمیتواند تشخیص دهد. domain را بهصورت prefix بیاورید (github_create_issue). - فراموش کردن
requiredدر JSON Schema — مدل هرچه بخواهد میفرستد و validation رد میشود. - اعتبارسنجی نکردن ورودی سمت server. annotationها فقط hint هستند؛ server همچنان باید enforce کند.