Files
mcp-use__mcp-use/docs/python/server/tools.mdx
Pietro Zullo 8348c59a53 docs: reorganize, fix accuracy, and update for agent readability (#1502)
* docs: reorganize navigation and clean up content

- Remove 'Misc' group with misplaced blog posts
- Remove 'Libraries' redirect group from Home nav
- Simplify 'Dev Tools' group (flatten Inspector, CLI, Tunneling)
- Remove individual changelog versions from Python nav (keep summary)
- Remove changelog from TypeScript 'Get Started' group
- Remove tunneling duplicate from Python Development group
- Rewrite mcp101 as clean MCP concepts page (was pitch deck)
- Delete typescript/blog posts (not technical docs)
- Add redirects for deleted blog post URLs

* docs: fix content accuracy and remove sloppy language

- Fix broken python/server/mcp-apps link in home page
- Remove informal pitch-speak from python/server/index.mdx
- Fix grammar error in typescript/server/index.mdx
- Update typescript/client/logging.mdx: remove fake 'coming soon' API code
- Fix non-existent model name gpt-5.1 in typescript quickstart
- Fix mcp_use naming in development contributing guide

* docs: fix incorrect TypeScript server API calls in inspector docs

- Replace server.addTool() with server.tool() (correct mcp-use API)
- Add missing zod import to debugging-chatgpt-apps example

* docs: fix fake model names (gpt-5.1 → gpt-4o) in agent docs

* docs: update model names, fix APIs, and add agent visibility hints

- Replace all deprecated model names with latest: gpt-4.1, gpt-4.1-mini,
  claude-sonnet-4-6, claude-opus-4-7, gemini-2.5-flash across all docs
- Fix TypeScript server.tool() API in installation.mdx (old positional string form)
- Fix TypeScript agent.run() in quickstart.mdx (deprecated string form → object)
- Remove broken manufact.com/docs/llms.txt hidden hints from 5 pages
- Add <Visibility for="agents"> block to mcp101.mdx with install commands,
  key classes, and API signatures for agent-oriented navigation
- Add LangChain provider install note to Python quickstart (mcp-use agent
  requires a separate langchain-* provider package)
- Update model name cards in Python llm-integration.mdx

* docs: clean up remaining stale model names and improve accuracy

- Update supported model lists in Python and TypeScript llm-integration pages
- Fix cost optimization table to use current model IDs
- Update TypeScript agent overview supported providers list

* docs: add LangChain provider install to TypeScript quickstart agent section

The MCPAgent requires a separate @langchain/* provider package; this was
not mentioned before the code example that imports from @langchain/openai.

* docs: update to latest flagship model IDs (May 2026)

- OpenAI: gpt-4.1 → gpt-5.5, gpt-4.1-mini → gpt-5.5-mini
- Google: gemini-2.5-flash → gemini-3.1-flash, gemini-2.5-pro → gemini-3.1-pro
- Groq: llama-3.1-* → llama-4-scout-17b-16e-instruct
- Anthropic (claude-opus-4-7, claude-sonnet-4-6) already current, no change

* docs: replace all em dashes with hyphens

* docs: restore llms.txt hints using Visibility component

* docs: simplify TypeScript logging page to unsupported notice only

* docs: rewrite mcp101 to explain MCP protocol with mcp-use examples and doc links

* docs: remove goal.md

* docs: revert nav changes, keep only Misc group removal and blog redirects

* docs: reorder mcp101 to Server/App -> Client/Inspector -> Agent, restore title

* docs: add code examples for Resources and Prompts in mcp101

* docs: remove em dashes from mcp101

* docs: add MCPClient code examples to mcp101

* docs: add React client example to mcp101

* docs: add agent code examples to mcp101

* docs: expand MCP Apps section in mcp101 with example and links

* docs: restore Contacts, Socials, and Founders sections to mcp101

* docs: add context-setting intro paragraph to mcp101 for new readers

* docs: add MCP stores section to mcp101 intro with Claude, ChatGPT, Cursor

* docs: fix Claude store link

* docs: fix ChatGPT store link

* docs: fix Cursor store link

* docs: replace store Cards with linked image grid

* docs: add captions to store images

* docs: add Notifications to client primitives table in mcp101

* docs: add lead-in paragraph before resource cards at bottom of mcp101

* docs: move resource cards to end of page after Contacts/Socials/Founders

* docs: demote Contacts/Socials/Founders to bold text to remove from TOC

* docs: move Contacts/Socials/Founders to very end of mcp101

* docs: add Resources and Contacts section headers, link official MCP docs

* chore(docs): update site metadata, footer links, and rename mcp_use to mcp-use

* update docs metadata + images

* docs: enhance MCP section by highlighting the reach of MCP stores with 1 billion weekly active users

* api ref
2026-05-14 18:49:06 +02:00

303 lines
8.9 KiB
Plaintext

---
title: "Tools"
description: "Define and expose tools from your MCP server"
icon: "hammer"
---
Tools are the primary way your MCP server exposes functionality to clients. They represent actions that an AI agent can invoke to perform tasks.
## Example
```python
@server.tool(
name="get_weather",
title="Weather Information Provider",
description="Get current weather information for a location",
annotations=ToolAnnotations(
readOnlyHint=True,
openWorldHint=True,
),
)
async def get_weather(
location: Annotated[str, Field(description="City name or zip code")],
units: Annotated[str, Field(description="Temperature units", default="celsius")],
context: Context,
) -> str:
"""Get current weather information for a location."""
await context.info(f"Fetching weather for {location}")
return f"Weather in {location}: 22 {units}"
```
## Anatomy of a Tool
Here's the JSON-RPC response that the example above produces when a client calls `tools/list`:
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_weather",
"title": "Weather Information Provider",
"description": "Get current weather information for a location",
"annotations": {
"readOnlyHint": true,
"openWorldHint": true
},
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or zip code"
},
"units": {
"type": "string",
"description": "Temperature units",
"default": "celsius"
}
},
"required": ["location"]
}
}
]
}
}
```
Here's what maps to what:
| Python | MCP Schema | Notes |
|--------|-----------|-------|
| `name="get_weather"` | `"name"` | Falls back to function name if omitted |
| `title="Weather Information Provider"` | `"title"` | Human-readable display name |
| `description="Get current weather..."` | `"description"` | Falls back to docstring if omitted |
| `annotations=ToolAnnotations(...)` | `"annotations"` | Behavioral hints for clients |
| `location: str` | `"type": "string"` | Python type hint &rarr; JSON Schema type |
| `Field(description="City name...")` | property `"description"` | Argument-level description |
| `default="celsius"` | `"default": "celsius"` | Makes the parameter optional |
| No default on `location` | `"required": ["location"]` | Parameters without defaults are required |
| `context: Context` | *(not in schema)* | Automatically excluded from the schema |
<Tip>
Always use `Annotated[type, Field(description="...")]` for your tool parameters. Without it, LLMs calling your tool won't know what each argument means.
</Tip>
## Minimal Definition
At minimum, a tool only needs a function with type hints:
```python
@server.tool()
def greet(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
```
The function name becomes the tool name, the docstring becomes the description, and type hints define the input schema.
## Tool Options
The `@server.tool()` decorator accepts these options:
```python
@server.tool(
name="custom_name", # Override the function name
title="Custom Title", # Human-readable title
description="Custom desc", # Override the docstring
annotations=ToolAnnotations( # Behavioral hints for clients
destructiveHint=True,
readOnlyHint=False,
),
structured_output=True, # Return structured JSON output
)
def my_tool(param: str) -> str:
return param
```
### Tool Annotations
Tool annotations provide hints to clients about the tool's behavior:
| Annotation | Description |
|------------|-------------|
| `destructiveHint` | Tool may modify or delete data |
| `readOnlyHint` | Tool only reads data, no side effects |
| `idempotentHint` | Calling multiple times has same effect as once |
| `openWorldHint` | Tool interacts with external systems |
## Async Tools
Tools can be async for non-blocking I/O operations:
```python
import httpx
@server.tool()
async def fetch_url(
url: Annotated[str, Field(description="The URL to fetch content from")],
) -> str:
"""Fetch content from a URL."""
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
```
## Using Context
Access the MCP context for logging and progress reporting. Add a `Context` parameter - it's automatically excluded from the tool's input schema:
```python
from mcp_use.server import Context
@server.tool()
async def long_task(
items: Annotated[list[str], Field(description="Items to process")],
context: Context,
) -> str:
"""Process items with progress reporting."""
results = []
for i, item in enumerate(items):
await context.report_progress(i, len(items))
results.append(f"Processed: {item}")
return "\n".join(results)
```
## Parameter Types and JSON Schema
Every tool parameter becomes a property in the tool's `inputSchema`. mcp-use uses [Pydantic](https://docs.pydantic.dev/) to generate [JSON Schema 2020-12](https://json-schema.org/draft/2020-12/schema) from your Python type hints.
### Type mapping
| Python type | JSON Schema | Notes |
|-------------|-------------|-------|
| `str` | `{"type": "string"}` | |
| `int` | `{"type": "integer"}` | |
| `float` | `{"type": "number"}` | |
| `bool` | `{"type": "boolean"}` | |
| `list[str]` | `{"type": "array", "items": {"type": "string"}}` | Works with any item type |
| `dict[str, Any]` | `{"type": "object"}` | |
| `Literal["a", "b"]` | `{"type": "string", "enum": ["a", "b"]}` | Renders as a dropdown in the Inspector |
### Required, optional, and nullable
How you declare a parameter determines whether it appears in the `required` array:
```python
@server.tool()
def example(
# Required - no default, goes into "required"
name: Annotated[str, Field(description="User name")],
# Optional with default - NOT in "required", default shown in schema
limit: Annotated[int, Field(description="Max results")] = 10,
# Nullable - NOT in "required", type stays "string" (not anyOf)
company: Annotated[str | None, Field(description="Company filter")] = None,
) -> str: ...
```
This produces:
```json
{
"properties": {
"name": { "type": "string", "description": "User name" },
"limit": { "type": "integer", "description": "Max results", "default": 10 },
"company": { "type": "string", "description": "Company filter", "default": null }
},
"required": ["name"]
}
```
<Tip>
`Optional[str]` and `str | None` are identical at runtime and produce the same schema.
</Tip>
### Enum parameters with Literal
Use `Literal` to restrict a parameter to a fixed set of values. MCP clients like the Inspector render these as dropdown selects:
```python
from typing import Literal
@server.tool()
def create_event(
title: Annotated[str, Field(description="Event title")],
visibility: Annotated[
Literal["public", "private", "team"],
Field(description="Who can see this event"),
],
recurrence: Annotated[
Literal["none", "daily", "weekly", "monthly"],
Field(description="Recurrence pattern"),
] = "none",
) -> dict: ...
```
Produces `{"type": "string", "enum": ["public", "private", "team"]}` in the schema.
<Note>
mcp-use automatically simplifies Pydantic's nullable `anyOf` schemas. Without this,
`str | None` would produce `{"anyOf": [{"type": "string"}, {"type": "null"}]}` which
breaks description rendering in the MCP Inspector and fails validation in some clients
like Google's Gemini API. mcp-use collapses this into `{"type": "string", "default": null}`.
</Note>
## Complex Input Types
Use Pydantic models for complex inputs:
```python
from pydantic import BaseModel
class SearchQuery(BaseModel):
query: str = Field(description="The search query string")
max_results: int = Field(default=10, description="Maximum number of results to return")
include_metadata: bool = Field(default=False, description="Whether to include metadata")
@server.tool()
def search(params: SearchQuery) -> list[str]:
"""Search with complex parameters."""
return ["result1", "result2"]
```
## Return Types
Tools can return various types:
```python
# String
@server.tool()
def text_tool() -> str:
return "Hello"
# Dict (serialized to JSON)
@server.tool()
def json_tool() -> dict:
return {"key": "value", "count": 42}
# List
@server.tool()
def list_tool() -> list[str]:
return ["a", "b", "c"]
```
## Error Handling
Raise exceptions to indicate errors. The error message will be returned to the client:
```python
@server.tool()
def divide(
a: Annotated[float, Field(description="Dividend")],
b: Annotated[float, Field(description="Divisor")],
) -> float:
"""Divide two numbers."""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
```