Tools
मशीनी अनुवाद
यह page अंग्रेज़ी documentation से अपने-आप अनुवादित किया गया है, और अंग्रेज़ी page ही प्रामाणिक version है। अगर कुछ गलत लगे, तो अनुवाद page बताता है कि इसकी सूचना कैसे दें।
tool ऐसा function है जिसे model call कर सकता है।
किसी सादे Python function पर @mcp.tool() लगाकर आप tool declare करते हैं। पूरा API बस इतना ही है।
आपका पहला tool
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str, limit: int) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r} (showing up to {limit})."
देखें आपने क्या लिखा। न कोई schema, न JSON, न protocol, बस एक function। SDK इससे तीन चीज़ें पढ़ता है:
- tool का नाम function का नाम है:
search_books। - model को जो description दिखता है वह docstring है:
Search the catalog by title or author. - model जो arguments pass कर सकता है वे type hints से आते हैं:
query: strऔरlimit: int।
Input schema
इन्हीं type hints से SDK एक JSON Schema बनाता है और tools/list के दौरान client को भेजता है:
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"title": "Limit", "type": "integer"}
},
"required": ["query", "limit"],
"title": "search_booksArguments"
}
दोनों arguments required में हैं क्योंकि किसी का भी default नहीं है। इसे आप थोड़ी ही देर में ठीक करेंगे। (title keys Pydantic की देन हैं; properties, उनके types और required ही असली contract हैं।)
Tip
यहाँ type hints documentation नहीं हैं। वे ही contract हैं। अगर कोई client "limit": "ten" भेजता है,
तो SDK उसे आपके function के चलने से पहले ही reject कर देता है।
model को क्या वापस मिलता है
tool को {"query": "dune", "limit": 5} के साथ call करें और result के दो हिस्से होते हैं:
result.content # [TextContent(text="Found 3 books matching 'dune' (showing up to 5).")]
result.structured_content # {'result': "Found 3 books matching 'dune' (showing up to 5)."}
content वह text है जो model पढ़ता है। structured_content client application के लिए typed data है। यह इसलिए मौजूद है क्योंकि आपने return type -> str declare किया।
structured_content की अभी चिंता न करें। अपने tools से असली Python objects लौटाएँ और सही चीज़ अपने-आप होती है; Structured Output page पूरा इसी बारे में है।
इसे आज़माएँ
server को MCP Inspector के साथ चलाएँ:
uv run mcp dev server.py
यह जो URL print करे उसे खोलें, Tools tab पर जाएँ, और search_books call करें।
Inspector एक form दिखाता है जिसमें एक required query text field और एक required limit number field है। यह form उसने आपके type hints से बनाया। बाकी हर MCP client भी यही करेगा।
Optional arguments
किसी parameter को default value दें और वह required नहीं रहता। बस इतना ही। यह सिर्फ़ Python है।
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str, limit: int = 10) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r} (showing up to {limit})."
schema भी साथ बदलता है:
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"default": 10, "title": "Limit", "type": "integer"}
},
"required": ["query"],
"title": "search_booksArguments"
}
limit required से बाहर हो गया और उसे "default": 10 मिल गया। जो client इसे छोड़ देता है उसे 10 मिलता है, ठीक वैसे ही जैसे Python में होता।
Field के साथ ज़्यादा विस्तृत schemas
type hints से काफ़ी काम चल जाता है, लेकिन कभी-कभी आप किसी argument की description देना चाहते हैं, या उस पर constraints लगाना।
type को Annotated में लपेटें और एक Pydantic Field जोड़ें:
from typing import Annotated, Literal
from pydantic import Field
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(
query: Annotated[str, Field(description="Title or author to search for.")],
limit: Annotated[int, Field(ge=1, le=50, description="Maximum number of results.")] = 10,
genre: Literal["fiction", "non-fiction", "poetry"] | None = None,
) -> str:
"""Search the catalog by title or author."""
where = f" in {genre}" if genre else ""
return f"Found 3 books matching {query!r}{where} (showing up to {limit})."
तीन नई चीज़ें, सब parameters पर:
Field(description=...): हर argument की अपनी description, जिसे model docstring के साथ पढ़ता है।Field(ge=1, le=50): संख्या की सीमाएँ। ये schema में"minimum": 1, "maximum": 50बनकर पहुँचती हैं।Literal["fiction", "non-fiction", "poetry"]: एक enum। model इन्हीं में से कोई एक चुन सकता है।
Check
constraints सजावट नहीं हैं। tool को limit=999 के साथ call करें और SDK
आपके function के चलने से पहले ही tool error के साथ जवाब देता है:
Input should be less than or equal to 50
यह error tool result के रूप में model के पास वापस जाता है, model इसे पढ़ता है और सही value के साथ
दोबारा कोशिश करता है। आपने एक बार le=50 लिखा और खुद को सुधारने वाले agents मुफ़्त में मिल गए।
Info
अगर आपने FastAPI या Pydantic इस्तेमाल किया है, तो यह सब आप पहले से जानते हैं। वही Field,
वही Annotated, वही validation। यहाँ MCP से जुड़ा कुछ नया सीखने को नहीं है।
parameter के रूप में model
जब कोई tool दो-तीन से ज़्यादा arguments लेता है, तो उन्हें एक Pydantic model में समेट लें:
from pydantic import BaseModel, Field
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
class Book(BaseModel):
title: str
author: str
year: int = Field(ge=1450, description="Year of first publication.")
@mcp.tool()
def add_book(book: Book) -> str:
"""Add a book to the catalog."""
return f"Added {book.title!r} by {book.author} ({book.year})."
Book schema tool के input schema के अंदर nested होता है (एक $defs reference के रूप में), model इसे JSON object के रूप में भरता है, और आपके function को एक असली Book instance मिलता है, पहले से validated, जिसमें .title, .author और .year attributes हैं।
आप इन्हें मिला-जुला सकते हैं: model parameters के साथ सादे parameters, nested models, models की lists। नीचे तक सब Pydantic ही है।
async def
अगर कोई tool I/O करता है (कोई API call करता है, file पढ़ता है, database से query करता है), तो उसे async def declare करें और उसके अंदर await करें। SDK उसे await करता है।
सादा def tool भी चलता है: SDK उसे एक thread में चलाता है ताकि वह server को कभी block न करे।
और कुछ configure करने को नहीं है।
नाम, titles और annotations
SDK जो कुछ भी अनुमान लगाता है, उसे आप decorator में override कर सकते हैं:
from mcp.server import MCPServer
from mcp.types import ToolAnnotations
mcp = MCPServer("Bookshop")
@mcp.tool(
title="Search the catalog",
annotations=ToolAnnotations(read_only_hint=True, open_world_hint=False),
)
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
titleUIs के लिए इंसानों के पढ़ने लायक नाम है। clientssearch_booksकी जगह "Search the catalog" दिखाते हैं।annotationsclient के लिए व्यवहार से जुड़े hints हैं:read_only_hint=True: यह tool कुछ नहीं बदलता।open_world_hint=False: यह चीज़ों के एक बंद set (इस catalog) पर काम करता है, खुले web पर नहीं।- बाकी दो,
destructive_hintऔरidempotent_hint, ऐसे tool के बारे में बताते हैं जो लिखता है: क्या वह कुछ delete कर सकता है, और क्या उसे दो बार call करना एक बार call करने जैसा ही है? spec दोनों को सिर्फ़ non-read-only tools के लिए define करता है, इसलिएsearch_booksपर ये कुछ नहीं कहते।
सलीकेदार client "क्या इसे चलाने से पहले मुझे user से पूछना होगा?" जैसी बातें इन्हीं से तय करता है। ये hints हैं, security नहीं। कभी इस भरोसे न रहें कि client इनका पालन करेगा।
Tip
अगर आप इन्हें function के नाम और docstring से नहीं निकालना चाहते, तो @mcp.tool() name= और description= भी
स्वीकार करता है। ज़्यादातर वक्त आप उन्हीं से निकालना चाहेंगे।
सारांश
- function पर
@mcp.tool()उसे tool बना देता है। नाम function से, description docstring से। - type hints ही input schema हैं। defaults arguments को optional बनाते हैं।
Annotated[..., Field(...)]descriptions और constraints जोड़ता है;Literalenums जोड़ता है।- structured "body" लेने का तरीका Pydantic model parameter है।
- गलत arguments आपके लिए reject कर दिए जाते हैं, ऐसे error के साथ जिसे model पढ़ सके और संभल सके।
- I/O के लिए
async def, बाकी सब के लिए सादाdef।
जो value आप return करते हैं उसका क्या होता है, यह Structured Output में है।