Remove empty parens

This commit is contained in:
Jeremiah Lowin 2025-06-04 15:37:55 -04:00
commit d1549c3d35
37 changed files with 146 additions and 146 deletions

View file

@ -31,7 +31,7 @@ from fastmcp import FastMCP
mcp = FastMCP("Demo 🚀") mcp = FastMCP("Demo 🚀")
@mcp.tool() @mcp.tool
def add(a: int, b: int) -> int: def add(a: int, b: int) -> int:
"""Add two numbers""" """Add two numbers"""
return a + b return a + b
@ -144,7 +144,7 @@ Learn more in the [**FastMCP Server Documentation**](https://gofastmcp.com/serve
Tools allow LLMs to perform actions by executing your Python functions (sync or async). Ideal for computations, API calls, or side effects (like `POST`/`PUT`). FastMCP handles schema generation from type hints and docstrings. Tools can return various types, including text, JSON-serializable objects, and even images using the [`fastmcp.Image`](https://gofastmcp.com/servers/tools#return-values) helper. Tools allow LLMs to perform actions by executing your Python functions (sync or async). Ideal for computations, API calls, or side effects (like `POST`/`PUT`). FastMCP handles schema generation from type hints and docstrings. Tools can return various types, including text, JSON-serializable objects, and even images using the [`fastmcp.Image`](https://gofastmcp.com/servers/tools#return-values) helper.
```python ```python
@mcp.tool() @mcp.tool
def multiply(a: float, b: float) -> float: def multiply(a: float, b: float) -> float:
"""Multiplies two numbers.""" """Multiplies two numbers."""
return a * b return a * b
@ -201,7 +201,7 @@ from fastmcp import FastMCP, Context
mcp = FastMCP("My MCP Server") mcp = FastMCP("My MCP Server")
@mcp.tool() @mcp.tool
async def process_data(uri: str, ctx: Context): async def process_data(uri: str, ctx: Context):
# Log a message to the client # Log a message to the client
await ctx.info(f"Processing {uri}...") await ctx.info(f"Processing {uri}...")
@ -321,7 +321,7 @@ from fastmcp import FastMCP
mcp = FastMCP("Demo 🚀") mcp = FastMCP("Demo 🚀")
@mcp.tool() @mcp.tool
def hello(name: str) -> str: def hello(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"

View file

@ -32,7 +32,7 @@ from fastmcp import FastMCP
mcp = FastMCP("MyServer") mcp = FastMCP("MyServer")
@mcp.tool() @mcp.tool
def hello(name: str) -> str: def hello(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"

View file

@ -22,7 +22,7 @@ from fastmcp import FastMCP
mcp = FastMCP(name="MyServer") mcp = FastMCP(name="MyServer")
@mcp.tool() @mcp.tool
def hello(name: str) -> str: def hello(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"
@ -244,7 +244,7 @@ import asyncio
mcp = FastMCP(name="MyServer") mcp = FastMCP(name="MyServer")
@mcp.tool() @mcp.tool
def hello(name: str) -> str: def hello(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"

View file

@ -32,7 +32,7 @@ from fastmcp import FastMCP
mcp = FastMCP("My MCP Server") mcp = FastMCP("My MCP Server")
@mcp.tool() @mcp.tool
def greet(name: str) -> str: def greet(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"
``` ```
@ -49,7 +49,7 @@ from fastmcp import FastMCP, Client
mcp = FastMCP("My MCP Server") mcp = FastMCP("My MCP Server")
@mcp.tool() @mcp.tool
def greet(name: str) -> str: def greet(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"
@ -76,7 +76,7 @@ from fastmcp import FastMCP
mcp = FastMCP("My MCP Server") mcp = FastMCP("My MCP Server")
@mcp.tool() @mcp.tool
def greet(name: str) -> str: def greet(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"

View file

@ -14,7 +14,7 @@ from fastmcp import FastMCP
mcp = FastMCP("Demo 🚀") mcp = FastMCP("Demo 🚀")
@mcp.tool() @mcp.tool
def add(a: int, b: int) -> int: def add(a: int, b: int) -> int:
"""Add two numbers""" """Add two numbers"""
return a + b return a + b

View file

@ -27,7 +27,7 @@ from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller") mcp = FastMCP(name="Dice Roller")
@mcp.tool() @mcp.tool
def roll_dice(n_dice: int) -> list[int]: def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results.""" """Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)] return [random.randint(1, 6) for _ in range(n_dice)]
@ -170,7 +170,7 @@ auth = BearerAuthProvider(
mcp = FastMCP(name="Dice Roller", auth=auth) mcp = FastMCP(name="Dice Roller", auth=auth)
@mcp.tool() @mcp.tool
def roll_dice(n_dice: int) -> list[int]: def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results.""" """Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)] return [random.randint(1, 6) for _ in range(n_dice)]

View file

@ -31,7 +31,7 @@ from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller") mcp = FastMCP(name="Dice Roller")
@mcp.tool() @mcp.tool
def roll_dice(n_dice: int) -> list[int]: def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results.""" """Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)] return [random.randint(1, 6) for _ in range(n_dice)]

View file

@ -31,7 +31,7 @@ from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller") mcp = FastMCP(name="Dice Roller")
@mcp.tool() @mcp.tool
def roll_dice(n_dice: int) -> list[int]: def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results.""" """Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)] return [random.randint(1, 6) for _ in range(n_dice)]

View file

@ -32,7 +32,7 @@ from fastmcp import FastMCP
mcp = FastMCP(name="Dice Roller") mcp = FastMCP(name="Dice Roller")
@mcp.tool() @mcp.tool
def roll_dice(n_dice: int) -> list[int]: def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results.""" """Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)] return [random.randint(1, 6) for _ in range(n_dice)]
@ -165,7 +165,7 @@ auth = BearerAuthProvider(
mcp = FastMCP(name="Dice Roller", auth=auth) mcp = FastMCP(name="Dice Roller", auth=auth)
@mcp.tool() @mcp.tool
def roll_dice(n_dice: int) -> list[int]: def roll_dice(n_dice: int) -> list[int]:
"""Roll `n_dice` 6-sided dice and return the results.""" """Roll `n_dice` 6-sided dice and return the results."""
return [random.randint(1, 6) for _ in range(n_dice)] return [random.randint(1, 6) for _ in range(n_dice)]

View file

@ -66,7 +66,7 @@ from fastmcp import FastMCP
mcp = FastMCP("MyServer") mcp = FastMCP("MyServer")
@mcp.tool() @mcp.tool
def hello(name: str) -> str: def hello(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"

View file

@ -25,7 +25,7 @@ from starlette.requests import Request
mcp = FastMCP(name="HTTP Request Demo") mcp = FastMCP(name="HTTP Request Demo")
@mcp.tool() @mcp.tool
async def user_agent_info() -> dict: async def user_agent_info() -> dict:
"""Return information about the user agent.""" """Return information about the user agent."""
# Get the HTTP request # Get the HTTP request
@ -58,7 +58,7 @@ from fastmcp.server.dependencies import get_http_headers
mcp = FastMCP(name="Headers Demo") mcp = FastMCP(name="Headers Demo")
@mcp.tool() @mcp.tool
async def safe_header_info() -> dict: async def safe_header_info() -> dict:
"""Safely get header information without raising errors.""" """Safely get header information without raising errors."""
# Get headers (returns empty dict if no request context) # Get headers (returns empty dict if no request context)

View file

@ -159,7 +159,7 @@ Once authenticated, your tools, resources, or prompts can access token informati
from fastmcp import FastMCP, Context, ToolError from fastmcp import FastMCP, Context, ToolError
from fastmcp.server.dependencies import get_access_token, AccessToken from fastmcp.server.dependencies import get_access_token, AccessToken
@mcp.tool() @mcp.tool
async def get_my_data(ctx: Context) -> dict: async def get_my_data(ctx: Context) -> dict:
access_token: AccessToken = get_access_token() access_token: AccessToken = get_access_token()

View file

@ -41,7 +41,7 @@ from fastmcp import FastMCP, Context
mcp = FastMCP(name="ContextDemo") mcp = FastMCP(name="ContextDemo")
@mcp.tool() @mcp.tool
async def process_file(file_uri: str, ctx: Context) -> str: async def process_file(file_uri: str, ctx: Context) -> str:
"""Processes a file, using context for logging and resource access.""" """Processes a file, using context for logging and resource access."""
# Context is available as the ctx parameter # Context is available as the ctx parameter
@ -99,7 +99,7 @@ async def process_data(data: list[float]) -> dict:
ctx = get_context() ctx = get_context()
await ctx.info(f"Processing {len(data)} data points") await ctx.info(f"Processing {len(data)} data points")
@mcp.tool() @mcp.tool
async def analyze_dataset(dataset_name: str) -> dict: async def analyze_dataset(dataset_name: str) -> dict:
# Call utility function that uses context internally # Call utility function that uses context internally
data = load_data(dataset_name) data = load_data(dataset_name)
@ -118,7 +118,7 @@ async def analyze_dataset(dataset_name: str) -> dict:
Send log messages back to the MCP client. This is useful for debugging and providing visibility into function execution during a request. Send log messages back to the MCP client. This is useful for debugging and providing visibility into function execution during a request.
```python ```python
@mcp.tool() @mcp.tool
async def analyze_data(data: list[float], ctx: Context) -> dict: async def analyze_data(data: list[float], ctx: Context) -> dict:
"""Analyze numerical data with logging.""" """Analyze numerical data with logging."""
await ctx.debug("Starting analysis of numerical data") await ctx.debug("Starting analysis of numerical data")
@ -149,7 +149,7 @@ async def analyze_data(data: list[float], ctx: Context) -> dict:
For long-running operations, notify the client about the progress. This allows clients to display progress indicators and provide a better user experience. For long-running operations, notify the client about the progress. This allows clients to display progress indicators and provide a better user experience.
```python ```python
@mcp.tool() @mcp.tool
async def process_items(items: list[str], ctx: Context) -> dict: async def process_items(items: list[str], ctx: Context) -> dict:
"""Process a list of items with progress updates.""" """Process a list of items with progress updates."""
total = len(items) total = len(items)
@ -182,7 +182,7 @@ Progress reporting requires the client to have sent a `progressToken` in the ini
Read data from resources registered with your FastMCP server. This allows functions to access files, configuration, or dynamically generated content. Read data from resources registered with your FastMCP server. This allows functions to access files, configuration, or dynamically generated content.
```python ```python
@mcp.tool() @mcp.tool
async def summarize_document(document_uri: str, ctx: Context) -> str: async def summarize_document(document_uri: str, ctx: Context) -> str:
"""Summarize a document by its resource URI.""" """Summarize a document by its resource URI."""
# Read the document content # Read the document content
@ -222,7 +222,7 @@ The returned content is typically accessed via `content_list[0].content` and can
Request the client's LLM to generate text based on provided messages. This is useful when your function needs to leverage the LLM's capabilities to process data or generate responses. Request the client's LLM to generate text based on provided messages. This is useful when your function needs to leverage the LLM's capabilities to process data or generate responses.
```python ```python
@mcp.tool() @mcp.tool
async def analyze_sentiment(text: str, ctx: Context) -> dict: async def analyze_sentiment(text: str, ctx: Context) -> dict:
"""Analyze the sentiment of a text using the client's LLM.""" """Analyze the sentiment of a text using the client's LLM."""
# Create a sampling prompt asking for sentiment analysis # Create a sampling prompt asking for sentiment analysis
@ -258,7 +258,7 @@ async def analyze_sentiment(text: str, ctx: Context) -> dict:
When providing a simple string, it's treated as a user message. For more complex scenarios, you can provide a list of messages with different roles. When providing a simple string, it's treated as a user message. For more complex scenarios, you can provide a list of messages with different roles.
```python ```python
@mcp.tool() @mcp.tool
async def generate_example(concept: str, ctx: Context) -> str: async def generate_example(concept: str, ctx: Context) -> str:
"""Generate a Python code example for a given concept.""" """Generate a Python code example for a given concept."""
# Using a system prompt and a user message # Using a system prompt and a user message
@ -280,7 +280,7 @@ See [Client Sampling](/clients/client#llm-sampling) for more details on how clie
Access metadata about the current request and client. Access metadata about the current request and client.
```python ```python
@mcp.tool() @mcp.tool
async def request_info(ctx: Context) -> dict: async def request_info(ctx: Context) -> dict:
"""Return information about the current request.""" """Return information about the current request."""
return { return {
@ -300,7 +300,7 @@ async def request_info(ctx: Context) -> dict:
#### FastMCP Server and Sessions #### FastMCP Server and Sessions
```python ```python
@mcp.tool() @mcp.tool
async def advanced_tool(ctx: Context) -> str: async def advanced_tool(ctx: Context) -> str:
"""Demonstrate advanced context access.""" """Demonstrate advanced context access."""
# Access the FastMCP server instance # Access the FastMCP server instance
@ -326,7 +326,7 @@ See the [HTTP Requests pattern](/patterns/http-requests) for more details.
For web applications, you can access the underlying HTTP request: For web applications, you can access the underlying HTTP request:
```python ```python
@mcp.tool() @mcp.tool
async def handle_web_request(ctx: Context) -> dict: async def handle_web_request(ctx: Context) -> dict:
"""Access HTTP request information from the Starlette request.""" """Access HTTP request information from the Starlette request."""
request = ctx.get_http_request() request = ctx.get_http_request()

View file

@ -47,7 +47,7 @@ FastMCP servers expose several types of components to the client:
Tools are functions that the client can call to perform actions or access external systems. Tools are functions that the client can call to perform actions or access external systems.
```python ```python
@mcp.tool() @mcp.tool
def multiply(a: float, b: float) -> float: def multiply(a: float, b: float) -> float:
"""Multiplies two numbers together.""" """Multiplies two numbers together."""
return a * b return a * b
@ -106,7 +106,7 @@ from fastmcp import FastMCP
mcp = FastMCP(name="MyServer") mcp = FastMCP(name="MyServer")
@mcp.tool() @mcp.tool
def greet(name: str) -> str: def greet(name: str) -> str:
"""Greet a user by name.""" """Greet a user by name."""
return f"Hello, {name}!" return f"Hello, {name}!"
@ -216,7 +216,7 @@ def yaml_serializer(data):
# Create a server with the custom serializer # Create a server with the custom serializer
mcp = FastMCP(name="MyServer", tool_serializer=yaml_serializer) mcp = FastMCP(name="MyServer", tool_serializer=yaml_serializer)
@mcp.tool() @mcp.tool
def get_config(): def get_config():
"""Returns configuration in YAML format.""" """Returns configuration in YAML format."""
return {"api_key": "abc123", "debug": True, "rate_limit": 100} return {"api_key": "abc123", "debug": True, "rate_limit": 100}

View file

@ -24,14 +24,14 @@ This allows LLMs to perform tasks like querying databases, calling APIs, making
### The `@tool` Decorator ### The `@tool` Decorator
Creating a tool is as simple as decorating a Python function with `@mcp.tool()`: Creating a tool is as simple as decorating a Python function with `@mcp.tool`:
```python ```python
from fastmcp import FastMCP from fastmcp import FastMCP
mcp = FastMCP(name="CalculatorServer") mcp = FastMCP(name="CalculatorServer")
@mcp.tool() @mcp.tool
def add(a: int, b: int) -> int: def add(a: int, b: int) -> int:
"""Adds two integer numbers together.""" """Adds two integer numbers together."""
return a + b return a + b
@ -61,7 +61,7 @@ Type annotations for parameters are essential for proper tool functionality. The
Use standard Python type annotations for parameters: Use standard Python type annotations for parameters:
```python ```python
@mcp.tool() @mcp.tool
def analyze_text( def analyze_text(
text: str, text: str,
max_tokens: int = 100, max_tokens: int = 100,
@ -79,7 +79,7 @@ You can provide additional metadata about parameters using Pydantic's `Field` cl
from typing import Annotated from typing import Annotated
from pydantic import Field from pydantic import Field
@mcp.tool() @mcp.tool
def process_image( def process_image(
image_url: Annotated[str, Field(description="URL of the image to process")], image_url: Annotated[str, Field(description="URL of the image to process")],
resize: Annotated[bool, Field(description="Whether to resize the image")] = False, resize: Annotated[bool, Field(description="Whether to resize the image")] = False,
@ -97,7 +97,7 @@ def process_image(
You can also use the Field as a default value, though the Annotated approach is preferred: You can also use the Field as a default value, though the Annotated approach is preferred:
```python ```python
@mcp.tool() @mcp.tool
def search_database( def search_database(
query: str = Field(description="Search query string"), query: str = Field(description="Search query string"),
limit: int = Field(10, description="Maximum number of results", ge=1, le=100) limit: int = Field(10, description="Maximum number of results", ge=1, le=100)
@ -137,7 +137,7 @@ For additional type annotations not listed here, see the [Parameter Types](#para
FastMCP follows Python's standard function parameter conventions. Parameters without default values are required, while those with default values are optional. FastMCP follows Python's standard function parameter conventions. Parameters without default values are required, while those with default values are optional.
```python ```python
@mcp.tool() @mcp.tool
def search_products( def search_products(
query: str, # Required - no default value query: str, # Required - no default value
max_results: int = 10, # Optional - has default value max_results: int = 10, # Optional - has default value
@ -197,14 +197,14 @@ FastMCP seamlessly supports both standard (`def`) and asynchronous (`async def`)
```python ```python
# Synchronous tool (suitable for CPU-bound or quick tasks) # Synchronous tool (suitable for CPU-bound or quick tasks)
@mcp.tool() @mcp.tool
def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float: def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
"""Calculate the distance between two coordinates.""" """Calculate the distance between two coordinates."""
# Implementation... # Implementation...
return 42.5 return 42.5
# Asynchronous tool (ideal for I/O-bound operations) # Asynchronous tool (ideal for I/O-bound operations)
@mcp.tool() @mcp.tool
async def fetch_weather(city: str) -> dict: async def fetch_weather(city: str) -> dict:
"""Retrieve current weather conditions for a city.""" """Retrieve current weather conditions for a city."""
# Use 'async def' for operations involving network calls, file I/O, etc. # Use 'async def' for operations involving network calls, file I/O, etc.
@ -244,7 +244,7 @@ except ImportError:
mcp = FastMCP("Image Demo") mcp = FastMCP("Image Demo")
@mcp.tool() @mcp.tool
def generate_image(width: int, height: int, color: str) -> Image: def generate_image(width: int, height: int, color: str) -> Image:
"""Generates a solid color image.""" """Generates a solid color image."""
# Create image using Pillow # Create image using Pillow
@ -258,7 +258,7 @@ def generate_image(width: int, height: int, color: str) -> Image:
# Return using FastMCP's Image helper # Return using FastMCP's Image helper
return Image(data=img_bytes, format="png") return Image(data=img_bytes, format="png")
@mcp.tool() @mcp.tool
def do_nothing() -> None: def do_nothing() -> None:
"""This tool performs an action but returns no data.""" """This tool performs an action but returns no data."""
print("Performing a side effect...") print("Performing a side effect...")
@ -285,7 +285,7 @@ mcp = FastMCP(name="SecureServer", mask_error_details=True)
from fastmcp import FastMCP from fastmcp import FastMCP
from fastmcp.exceptions import ToolError from fastmcp.exceptions import ToolError
@mcp.tool() @mcp.tool
def divide(a: float, b: float) -> float: def divide(a: float, b: float) -> float:
"""Divide a by b.""" """Divide a by b."""
@ -315,7 +315,7 @@ Annotations serve several purposes in client applications:
- Describing the safety profile of tools (destructive vs. non-destructive) - Describing the safety profile of tools (destructive vs. non-destructive)
- Signaling if tools interact with external systems - Signaling if tools interact with external systems
You can add annotations to a tool using the `annotations` parameter in the `@mcp.tool()` decorator: You can add annotations to a tool using the `annotations` parameter in the `@mcp.tool` decorator:
```python ```python
@mcp.tool( @mcp.tool(
@ -351,7 +351,7 @@ from fastmcp import FastMCP, Context
mcp = FastMCP(name="ContextDemo") mcp = FastMCP(name="ContextDemo")
@mcp.tool() @mcp.tool
async def process_data(data_uri: str, ctx: Context) -> dict: async def process_data(data_uri: str, ctx: Context) -> dict:
"""Process data from a resource with progress reporting.""" """Process data from a resource with progress reporting."""
await ctx.info(f"Processing data from {data_uri}") await ctx.info(f"Processing data from {data_uri}")
@ -396,7 +396,7 @@ FastMCP supports **type coercion** when possible. This means that if a client se
The most common parameter types are Python's built-in scalar types: The most common parameter types are Python's built-in scalar types:
```python ```python
@mcp.tool() @mcp.tool
def process_values( def process_values(
name: str, # Text data name: str, # Text data
count: int, # Integer numbers count: int, # Integer numbers
@ -416,7 +416,7 @@ FastMCP supports various date and time types from the `datetime` module:
```python ```python
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
@mcp.tool() @mcp.tool
def process_date_time( def process_date_time(
event_date: date, # ISO format date string or date object event_date: date, # ISO format date string or date object
event_time: datetime, # ISO format datetime string or datetime object event_time: datetime, # ISO format datetime string or datetime object
@ -440,7 +440,7 @@ def process_date_time(
FastMCP supports all standard Python collection types: FastMCP supports all standard Python collection types:
```python ```python
@mcp.tool() @mcp.tool
def analyze_data( def analyze_data(
values: list[float], # List of numbers values: list[float], # List of numbers
properties: dict[str, str], # Dictionary with string keys and values properties: dict[str, str], # Dictionary with string keys and values
@ -465,7 +465,7 @@ Collection types can be nested and combined to represent complex data structures
For parameters that can accept multiple types or may be omitted: For parameters that can accept multiple types or may be omitted:
```python ```python
@mcp.tool() @mcp.tool
def flexible_search( def flexible_search(
query: str | int, # Can be either string or integer query: str | int, # Can be either string or integer
filters: dict[str, str] | None = None, # Optional dictionary filters: dict[str, str] | None = None, # Optional dictionary
@ -488,7 +488,7 @@ Literals constrain parameters to a specific set of values:
```python ```python
from typing import Literal from typing import Literal
@mcp.tool() @mcp.tool
def sort_data( def sort_data(
data: list[float], data: list[float],
order: Literal["ascending", "descending"] = "ascending", order: Literal["ascending", "descending"] = "ascending",
@ -516,7 +516,7 @@ class Color(Enum):
GREEN = "green" GREEN = "green"
BLUE = "blue" BLUE = "blue"
@mcp.tool() @mcp.tool
def process_image( def process_image(
image_path: str, image_path: str,
color_filter: Color = Color.RED color_filter: Color = Color.RED
@ -539,7 +539,7 @@ There are two approaches to handling binary data in tool parameters:
#### Bytes #### Bytes
```python ```python
@mcp.tool() @mcp.tool
def process_binary(data: bytes): def process_binary(data: bytes):
"""Process binary data directly. """Process binary data directly.
@ -563,7 +563,7 @@ FastMCP does not automatically decode base64-encoded strings for bytes parameter
from typing import Annotated from typing import Annotated
from pydantic import Field from pydantic import Field
@mcp.tool() @mcp.tool
def process_image_data( def process_image_data(
image_data: Annotated[str, Field(description="Base64-encoded image data")] image_data: Annotated[str, Field(description="Base64-encoded image data")]
): ):
@ -587,7 +587,7 @@ The `Path` type from the `pathlib` module can be used for file system paths:
```python ```python
from pathlib import Path from pathlib import Path
@mcp.tool() @mcp.tool
def process_file(path: Path) -> str: def process_file(path: Path) -> str:
"""Process a file at the given path.""" """Process a file at the given path."""
assert isinstance(path, Path) # Path is properly converted assert isinstance(path, Path) # Path is properly converted
@ -603,7 +603,7 @@ The `UUID` type from the `uuid` module can be used for unique identifiers:
```python ```python
import uuid import uuid
@mcp.tool() @mcp.tool
def process_item( def process_item(
item_id: uuid.UUID # String UUID or UUID object item_id: uuid.UUID # String UUID or UUID object
) -> str: ) -> str:
@ -628,7 +628,7 @@ class User(BaseModel):
age: int | None = None age: int | None = None
is_active: bool = True is_active: bool = True
@mcp.tool() @mcp.tool
def create_user(user: User): def create_user(user: User):
"""Create a new user in the system.""" """Create a new user in the system."""
# The input is automatically validated against the User model # The input is automatically validated against the User model
@ -657,7 +657,7 @@ Note that fields can be used *outside* Pydantic models to provide metadata and v
from typing import Annotated from typing import Annotated
from pydantic import Field from pydantic import Field
@mcp.tool() @mcp.tool
def analyze_metrics( def analyze_metrics(
# Numbers with range constraints # Numbers with range constraints
count: Annotated[int, Field(ge=0, le=100)], # 0 <= count <= 100 count: Annotated[int, Field(ge=0, le=100)], # 0 <= count <= 100
@ -682,7 +682,7 @@ def analyze_metrics(
You can also use `Field` as a default value, though the `Annotated` approach is preferred: You can also use `Field` as a default value, though the `Annotated` approach is preferred:
```python ```python
@mcp.tool() @mcp.tool
def validate_data( def validate_data(
# Value constraints # Value constraints
age: int = Field(ge=0, lt=120), # 0 <= age < 120 age: int = Field(ge=0, lt=120), # 0 <= age < 120
@ -727,12 +727,12 @@ mcp = FastMCP(
on_duplicate_tools="error" on_duplicate_tools="error"
) )
@mcp.tool() @mcp.tool
def my_tool(): return "Version 1" def my_tool(): return "Version 1"
# This will now raise a ValueError because 'my_tool' already exists # This will now raise a ValueError because 'my_tool' already exists
# and on_duplicate_tools is set to "error". # and on_duplicate_tools is set to "error".
# @mcp.tool() # @mcp.tool
# def my_tool(): return "Version 2" # def my_tool(): return "Version 2"
``` ```
@ -754,7 +754,7 @@ from fastmcp import FastMCP
mcp = FastMCP(name="DynamicToolServer") mcp = FastMCP(name="DynamicToolServer")
@mcp.tool() @mcp.tool
def calculate_sum(a: int, b: int) -> int: def calculate_sum(a: int, b: int) -> int:
"""Add two numbers together.""" """Add two numbers together."""
return a + b return a + b

View file

@ -20,7 +20,7 @@ class ShrimpTank(BaseModel):
shrimp: list[Shrimp] shrimp: list[Shrimp]
@mcp.tool() @mcp.tool
def name_shrimp( def name_shrimp(
tank: ShrimpTank, tank: ShrimpTank,
# You can use pydantic Field in function signatures for validation. # You can use pydantic Field in function signatures for validation.

View file

@ -24,7 +24,7 @@ if args.debug:
mcp = FastMCP(server_name) mcp = FastMCP(server_name)
@mcp.tool() @mcp.tool
def get_status() -> dict[str, str | bool]: def get_status() -> dict[str, str | bool]:
"""Get the current server configuration and status.""" """Get the current server configuration and status."""
return { return {
@ -34,7 +34,7 @@ def get_status() -> dict[str, str | bool]:
} }
@mcp.tool() @mcp.tool
def echo_message(message: str) -> str: def echo_message(message: str) -> str:
"""Echo a message, with debug info if debug mode is enabled.""" """Echo a message, with debug info if debug mode is enabled."""
if args.debug: if args.debug:

View file

@ -26,7 +26,7 @@ def get_greeting(name: str) -> str:
return f"Hello, {name}!" return f"Hello, {name}!"
@mcp.tool() @mcp.tool
def add(a: int, b: int) -> int: def add(a: int, b: int) -> int:
"""Add two numbers""" """Add two numbers"""
return a + b return a + b

View file

@ -8,7 +8,7 @@ from fastmcp import FastMCP
mcp = FastMCP("Echo Server") mcp = FastMCP("Echo Server")
@mcp.tool() @mcp.tool
def echo_tool(text: str) -> str: def echo_tool(text: str) -> str:
"""Echo the input text""" """Echo the input text"""
return text return text

View file

@ -279,7 +279,7 @@ async def display_memory_tree(deps: Deps) -> str:
return result return result
@mcp.tool() @mcp.tool
async def remember( async def remember(
contents: list[str] = Field( contents: list[str] = Field(
description="List of observations or memories to store" description="List of observations or memories to store"
@ -294,7 +294,7 @@ async def remember(
await deps.pool.close() await deps.pool.close()
@mcp.tool() @mcp.tool
async def read_profile() -> str: async def read_profile() -> str:
deps = Deps(openai=AsyncOpenAI(), pool=await get_db_pool()) deps = Deps(openai=AsyncOpenAI(), pool=await get_db_pool())
profile = await display_memory_tree(deps) profile = await display_memory_tree(deps)

View file

@ -15,7 +15,7 @@ from fastmcp.client.sampling import RequestContext, SamplingMessage, SamplingPar
mcp = FastMCP("Sampling Example") mcp = FastMCP("Sampling Example")
@mcp.tool() @mcp.tool
async def example_tool(prompt: str, context: Context) -> str: async def example_tool(prompt: str, context: Context) -> str:
"""Sample a completion from the LLM.""" """Sample a completion from the LLM."""
response = await context.sample( response = await context.sample(

View file

@ -12,7 +12,7 @@ from fastmcp import FastMCP, Image
mcp = FastMCP("Screenshot Demo", dependencies=["pyautogui", "Pillow"]) mcp = FastMCP("Screenshot Demo", dependencies=["pyautogui", "Pillow"])
@mcp.tool() @mcp.tool
def take_screenshot() -> Image: def take_screenshot() -> Image:
""" """
Take a screenshot of the user's screen and return it as an image. Use Take a screenshot of the user's screen and return it as an image. Use

View file

@ -8,7 +8,7 @@ from fastmcp import FastMCP
mcp = FastMCP("Echo Server") mcp = FastMCP("Echo Server")
@mcp.tool() @mcp.tool
def echo(text: str) -> str: def echo(text: str) -> str:
"""Echo the input text""" """Echo the input text"""
return text return text

View file

@ -6,7 +6,7 @@ from fastmcp.contrib.bulk_tool_caller import BulkToolCaller
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def echo_tool(text: str) -> str: def echo_tool(text: str) -> str:
"""Echo the input text""" """Echo the input text"""
return text return text

View file

@ -53,7 +53,7 @@ def run_mcp_server(
) )
) )
@mcp.tool() @mcp.tool
def add(a: int, b: int) -> int: def add(a: int, b: int) -> int:
return a + b return a + b

View file

@ -510,7 +510,7 @@ class TestErrorHandling:
async def test_general_tool_exceptions_are_not_masked_by_default(self): async def test_general_tool_exceptions_are_not_masked_by_default(self):
mcp = FastMCP("TestServer") mcp = FastMCP("TestServer")
@mcp.tool() @mcp.tool
def error_tool(): def error_tool():
raise ValueError("This is a test error (abc)") raise ValueError("This is a test error (abc)")
@ -525,7 +525,7 @@ class TestErrorHandling:
async def test_general_tool_exceptions_are_masked_when_enabled(self): async def test_general_tool_exceptions_are_masked_when_enabled(self):
mcp = FastMCP("TestServer", mask_error_details=True) mcp = FastMCP("TestServer", mask_error_details=True)
@mcp.tool() @mcp.tool
def error_tool(): def error_tool():
raise ValueError("This is a test error (abc)") raise ValueError("This is a test error (abc)")
@ -540,7 +540,7 @@ class TestErrorHandling:
async def test_specific_tool_errors_are_sent_to_client(self): async def test_specific_tool_errors_are_sent_to_client(self):
mcp = FastMCP("TestServer") mcp = FastMCP("TestServer")
@mcp.tool() @mcp.tool
def custom_error_tool(): def custom_error_tool():
raise ToolError("This is a test error (abc)") raise ToolError("This is a test error (abc)")

View file

@ -17,11 +17,11 @@ class LogHandler:
def fastmcp_server(): def fastmcp_server():
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
async def log(context: Context) -> None: async def log(context: Context) -> None:
await context.info(message="hello?") await context.info(message="hello?")
@mcp.tool() @mcp.tool
async def echo_log( async def echo_log(
message: str, message: str,
context: Context, context: Context,

View file

@ -16,7 +16,7 @@ def clear_progress_messages():
def fastmcp_server(): def fastmcp_server():
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
async def progress_tool(context: Context) -> int: async def progress_tool(context: Context) -> int:
for i in range(3): for i in range(3):
await context.report_progress( await context.report_progress(

View file

@ -9,7 +9,7 @@ from fastmcp import Client, Context, FastMCP
def fastmcp_server(): def fastmcp_server():
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
async def list_roots(context: Context) -> list[str]: async def list_roots(context: Context) -> list[str]:
roots = await context.list_roots() roots = await context.list_roots()
return [str(r.uri) for r in roots] return [str(r.uri) for r in roots]

View file

@ -11,17 +11,17 @@ from fastmcp.client.sampling import RequestContext, SamplingMessage, SamplingPar
def fastmcp_server(): def fastmcp_server():
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
async def simple_sample(message: str, context: Context) -> str: async def simple_sample(message: str, context: Context) -> str:
result = await context.sample("Hello, world!") result = await context.sample("Hello, world!")
return cast(TextContent, result).text return cast(TextContent, result).text
@mcp.tool() @mcp.tool
async def sample_with_system_prompt(message: str, context: Context) -> str: async def sample_with_system_prompt(message: str, context: Context) -> str:
result = await context.sample("Hello, world!", system_prompt="You love FastMCP") result = await context.sample("Hello, world!", system_prompt="You love FastMCP")
return cast(TextContent, result).text return cast(TextContent, result).text
@mcp.tool() @mcp.tool
async def sample_with_messages(message: str, context: Context) -> str: async def sample_with_messages(message: str, context: Context) -> str:
result = await context.sample( result = await context.sample(
[ [

View file

@ -17,7 +17,7 @@ class TestKeepAlive:
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def pid() -> int: def pid() -> int:
"""Gets PID of server""" """Gets PID of server"""
return os.getpid() return os.getpid()

View file

@ -62,7 +62,7 @@ def resources(mcp: FastMCP, test_dir: Path) -> FastMCP:
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def tools(mcp: FastMCP, test_dir: Path) -> FastMCP: def tools(mcp: FastMCP, test_dir: Path) -> FastMCP:
@mcp.tool() @mcp.tool
def delete_file(path: str) -> bool: def delete_file(path: str) -> bool:
# ensure path is in test_dir # ensure path is in test_dir
if Path(path).resolve().parent != test_dir: if Path(path).resolve().parent != test_dir:

View file

@ -57,7 +57,7 @@ class TestTools:
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def fn(x: int) -> int: def fn(x: int) -> int:
return x + 1 return x + 1
@ -126,7 +126,7 @@ class TestToolDecorator:
async def test_tool_decorator(self): async def test_tool_decorator(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def add(x: int, y: int) -> int: def add(x: int, y: int) -> int:
return x + y return x + y
@ -179,7 +179,7 @@ class TestToolDecorator:
def __init__(self, x: int): def __init__(self, x: int):
self.x = x self.x = x
@mcp.tool() @mcp.tool
def add(self, y: int) -> int: def add(self, y: int) -> int:
return self.x + y return self.x + y
@ -207,7 +207,7 @@ class TestToolDecorator:
class MyClass: class MyClass:
@staticmethod @staticmethod
@mcp.tool() @mcp.tool
def add(x: int, y: int) -> int: def add(x: int, y: int) -> int:
return x + y return x + y
@ -217,7 +217,7 @@ class TestToolDecorator:
async def test_tool_decorator_async_function(self): async def test_tool_decorator_async_function(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
async def add(x: int, y: int) -> int: async def add(x: int, y: int) -> int:
return x + y return x + y
@ -288,7 +288,7 @@ class TestToolDecorator:
"""Test that tools with annotated arguments work correctly.""" """Test that tools with annotated arguments work correctly."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def add( def add(
x: Annotated[int, Field(description="x is an int")], x: Annotated[int, Field(description="x is an int")],
y: Annotated[str, Field(description="y is not an int")], y: Annotated[str, Field(description="y is not an int")],
@ -303,7 +303,7 @@ class TestToolDecorator:
"""Test that tools with annotated arguments work correctly.""" """Test that tools with annotated arguments work correctly."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def add( def add(
x: int = Field(description="x is an int"), x: int = Field(description="x is an int"),
y: str = Field(description="y is not an int"), y: str = Field(description="y is not an int"),

View file

@ -30,30 +30,30 @@ from fastmcp.utilities.types import Image
def tool_server(): def tool_server():
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def add(x: int, y: int) -> int: def add(x: int, y: int) -> int:
return x + y return x + y
@mcp.tool() @mcp.tool
def list_tool() -> list[str | int]: def list_tool() -> list[str | int]:
return ["x", 2] return ["x", 2]
@mcp.tool() @mcp.tool
def error_tool() -> None: def error_tool() -> None:
raise ValueError("Test error") raise ValueError("Test error")
@mcp.tool() @mcp.tool
def image_tool(path: str) -> Image: def image_tool(path: str) -> Image:
return Image(path) return Image(path)
@mcp.tool() @mcp.tool
def mixed_content_tool() -> list[TextContent | ImageContent]: def mixed_content_tool() -> list[TextContent | ImageContent]:
return [ return [
TextContent(type="text", text="Hello"), TextContent(type="text", text="Hello"),
ImageContent(type="image", data="abc", mimeType="image/png"), ImageContent(type="image", data="abc", mimeType="image/png"),
] ]
@mcp.tool() @mcp.tool
def mixed_list_fn(image_path: str) -> list: def mixed_list_fn(image_path: str) -> list:
return [ return [
"text message", "text message",
@ -100,7 +100,7 @@ class TestTools:
mcp = FastMCP() mcp = FastMCP()
client = Client(transport=FastMCPTransport(mcp)) client = Client(transport=FastMCPTransport(mcp))
@mcp.tool() @mcp.tool
def error_tool(): def error_tool():
raise ValueError("Test error") raise ValueError("Test error")
@ -119,7 +119,7 @@ class TestToolReturnTypes:
async def test_string(self): async def test_string(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def string_tool() -> str: def string_tool() -> str:
return "Hello, world!" return "Hello, world!"
@ -130,7 +130,7 @@ class TestToolReturnTypes:
async def test_bytes(self, tmp_path: Path): async def test_bytes(self, tmp_path: Path):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def bytes_tool() -> bytes: def bytes_tool() -> bytes:
return b"Hello, world!" return b"Hello, world!"
@ -143,7 +143,7 @@ class TestToolReturnTypes:
test_uuid = uuid.uuid4() test_uuid = uuid.uuid4()
@mcp.tool() @mcp.tool
def uuid_tool() -> uuid.UUID: def uuid_tool() -> uuid.UUID:
return test_uuid return test_uuid
@ -156,7 +156,7 @@ class TestToolReturnTypes:
test_path = Path("/tmp/test.txt") test_path = Path("/tmp/test.txt")
@mcp.tool() @mcp.tool
def path_tool() -> Path: def path_tool() -> Path:
return test_path return test_path
@ -169,7 +169,7 @@ class TestToolReturnTypes:
dt = datetime.datetime(2025, 4, 25, 1, 2, 3) dt = datetime.datetime(2025, 4, 25, 1, 2, 3)
@mcp.tool() @mcp.tool
def datetime_tool() -> datetime.datetime: def datetime_tool() -> datetime.datetime:
return dt return dt
@ -180,7 +180,7 @@ class TestToolReturnTypes:
async def test_image(self, tmp_path: Path): async def test_image(self, tmp_path: Path):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def image_tool(path: str) -> Image: def image_tool(path: str) -> Image:
return Image(path) return Image(path)
@ -243,7 +243,7 @@ class TestToolParameters:
async def test_parameter_descriptions_with_field_annotations(self): async def test_parameter_descriptions_with_field_annotations(self):
mcp = FastMCP("Test Server") mcp = FastMCP("Test Server")
@mcp.tool() @mcp.tool
def greet( def greet(
name: Annotated[str, Field(description="The name to greet")], name: Annotated[str, Field(description="The name to greet")],
title: Annotated[str, Field(description="Optional title", default="")], title: Annotated[str, Field(description="Optional title", default="")],
@ -268,7 +268,7 @@ class TestToolParameters:
async def test_parameter_descriptions_with_field_defaults(self): async def test_parameter_descriptions_with_field_defaults(self):
mcp = FastMCP("Test Server") mcp = FastMCP("Test Server")
@mcp.tool() @mcp.tool
def greet( def greet(
name: str = Field(description="The name to greet"), name: str = Field(description="The name to greet"),
title: str = Field(description="Optional title", default=""), title: str = Field(description="Optional title", default=""),
@ -293,7 +293,7 @@ class TestToolParameters:
async def test_tool_with_bytes_input(self): async def test_tool_with_bytes_input(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def process_image(image: bytes) -> Image: def process_image(image: bytes) -> Image:
return Image(data=image) return Image(data=image)
@ -308,7 +308,7 @@ class TestToolParameters:
async def test_tool_with_invalid_input(self): async def test_tool_with_invalid_input(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def my_tool(x: int) -> int: def my_tool(x: int) -> int:
return x + 1 return x + 1
@ -323,7 +323,7 @@ class TestToolParameters:
"""Test string-to-int type coercion.""" """Test string-to-int type coercion."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def add_one(x: int) -> int: def add_one(x: int) -> int:
return x + 1 return x + 1
@ -336,7 +336,7 @@ class TestToolParameters:
"""Test string-to-bool type coercion.""" """Test string-to-bool type coercion."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def toggle(flag: bool) -> bool: def toggle(flag: bool) -> bool:
return not flag return not flag
@ -351,7 +351,7 @@ class TestToolParameters:
async def test_annotated_field_validation(self): async def test_annotated_field_validation(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def analyze(x: Annotated[int, Field(ge=1)]) -> None: def analyze(x: Annotated[int, Field(ge=1)]) -> None:
pass pass
@ -362,7 +362,7 @@ class TestToolParameters:
async def test_default_field_validation(self): async def test_default_field_validation(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def analyze(x: int = Field(ge=1)) -> None: def analyze(x: int = Field(ge=1)) -> None:
pass pass
@ -373,7 +373,7 @@ class TestToolParameters:
async def test_default_field_is_still_required_if_no_default_specified(self): async def test_default_field_is_still_required_if_no_default_specified(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def analyze(x: int = Field()) -> None: def analyze(x: int = Field()) -> None:
pass pass
@ -384,7 +384,7 @@ class TestToolParameters:
async def test_literal_type_validation_error(self): async def test_literal_type_validation_error(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def analyze(x: Literal["a", "b"]) -> None: def analyze(x: Literal["a", "b"]) -> None:
pass pass
@ -395,7 +395,7 @@ class TestToolParameters:
async def test_literal_type_validation_success(self): async def test_literal_type_validation_success(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def analyze(x: Literal["a", "b"]) -> str: def analyze(x: Literal["a", "b"]) -> str:
return x return x
@ -411,7 +411,7 @@ class TestToolParameters:
GREEN = "green" GREEN = "green"
BLUE = "blue" BLUE = "blue"
@mcp.tool() @mcp.tool
def analyze(x: MyEnum) -> str: def analyze(x: MyEnum) -> str:
return x.value return x.value
@ -427,7 +427,7 @@ class TestToolParameters:
GREEN = "green" GREEN = "green"
BLUE = "blue" BLUE = "blue"
@mcp.tool() @mcp.tool
def analyze(x: MyEnum) -> str: def analyze(x: MyEnum) -> str:
return x.value return x.value
@ -438,7 +438,7 @@ class TestToolParameters:
async def test_union_type_validation(self): async def test_union_type_validation(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def analyze(x: int | float) -> str: def analyze(x: int | float) -> str:
return str(x) return str(x)
@ -455,7 +455,7 @@ class TestToolParameters:
async def test_path_type(self): async def test_path_type(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_path(path: Path) -> str: def send_path(path: Path) -> str:
assert isinstance(path, Path) assert isinstance(path, Path)
return str(path) return str(path)
@ -470,7 +470,7 @@ class TestToolParameters:
async def test_path_type_error(self): async def test_path_type_error(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_path(path: Path) -> str: def send_path(path: Path) -> str:
return str(path) return str(path)
@ -481,7 +481,7 @@ class TestToolParameters:
async def test_uuid_type(self): async def test_uuid_type(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_uuid(x: uuid.UUID) -> str: def send_uuid(x: uuid.UUID) -> str:
assert isinstance(x, uuid.UUID) assert isinstance(x, uuid.UUID)
return str(x) return str(x)
@ -495,7 +495,7 @@ class TestToolParameters:
async def test_uuid_type_error(self): async def test_uuid_type_error(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_uuid(x: uuid.UUID) -> str: def send_uuid(x: uuid.UUID) -> str:
return str(x) return str(x)
@ -506,7 +506,7 @@ class TestToolParameters:
async def test_datetime_type(self): async def test_datetime_type(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_datetime(x: datetime.datetime) -> str: def send_datetime(x: datetime.datetime) -> str:
return x.isoformat() return x.isoformat()
@ -519,7 +519,7 @@ class TestToolParameters:
async def test_datetime_type_parse_string(self): async def test_datetime_type_parse_string(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_datetime(x: datetime.datetime) -> str: def send_datetime(x: datetime.datetime) -> str:
return x.isoformat() return x.isoformat()
@ -532,7 +532,7 @@ class TestToolParameters:
async def test_datetime_type_error(self): async def test_datetime_type_error(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_datetime(x: datetime.datetime) -> str: def send_datetime(x: datetime.datetime) -> str:
return x.isoformat() return x.isoformat()
@ -543,7 +543,7 @@ class TestToolParameters:
async def test_date_type(self): async def test_date_type(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_date(x: datetime.date) -> str: def send_date(x: datetime.date) -> str:
return x.isoformat() return x.isoformat()
@ -554,7 +554,7 @@ class TestToolParameters:
async def test_date_type_parse_string(self): async def test_date_type_parse_string(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_date(x: datetime.date) -> str: def send_date(x: datetime.date) -> str:
return x.isoformat() return x.isoformat()
@ -565,7 +565,7 @@ class TestToolParameters:
async def test_timedelta_type(self): async def test_timedelta_type(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_timedelta(x: datetime.timedelta) -> str: def send_timedelta(x: datetime.timedelta) -> str:
return str(x) return str(x)
@ -578,7 +578,7 @@ class TestToolParameters:
async def test_timedelta_type_parse_int(self): async def test_timedelta_type_parse_int(self):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def send_timedelta(x: datetime.timedelta) -> str: def send_timedelta(x: datetime.timedelta) -> str:
return str(x) return str(x)
@ -594,7 +594,7 @@ class TestToolContextInjection:
"""Test that context parameters are properly detected.""" """Test that context parameters are properly detected."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def tool_with_context(x: int, ctx: Context) -> str: def tool_with_context(x: int, ctx: Context) -> str:
return f"Request {ctx.request_id}: {x}" return f"Request {ctx.request_id}: {x}"
@ -607,7 +607,7 @@ class TestToolContextInjection:
"""Test that context is properly injected into tool calls.""" """Test that context is properly injected into tool calls."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def tool_with_context(x: int, ctx: Context) -> str: def tool_with_context(x: int, ctx: Context) -> str:
assert isinstance(ctx, Context) assert isinstance(ctx, Context)
assert ctx.request_id is not None assert ctx.request_id is not None
@ -623,7 +623,7 @@ class TestToolContextInjection:
"""Test that context works in async functions.""" """Test that context works in async functions."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
async def async_tool(x: int, ctx: Context) -> str: async def async_tool(x: int, ctx: Context) -> str:
assert ctx.request_id is not None assert ctx.request_id is not None
return f"Async request {ctx.request_id}: {x}" return f"Async request {ctx.request_id}: {x}"
@ -638,7 +638,7 @@ class TestToolContextInjection:
"""Test that context is optional.""" """Test that context is optional."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def no_context(x: int) -> int: def no_context(x: int) -> int:
return x * 2 return x * 2
@ -656,7 +656,7 @@ class TestToolContextInjection:
def test_resource() -> str: def test_resource() -> str:
return "resource data" return "resource data"
@mcp.tool() @mcp.tool
async def tool_with_resource(ctx: Context) -> str: async def tool_with_resource(ctx: Context) -> str:
r_iter = await ctx.read_resource("test://data") r_iter = await ctx.read_resource("test://data")
r_list = list(r_iter) r_list = list(r_iter)

View file

@ -320,7 +320,7 @@ class TestLegacyToolJsonParsing:
"""Test JSON string to collection type coercion.""" """Test JSON string to collection type coercion."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def process_list(items: list[int]) -> int: def process_list(items: list[int]) -> int:
return sum(items) return sum(items)
@ -335,7 +335,7 @@ class TestLegacyToolJsonParsing:
"""Test that a list coercion error is raised if the input is not a valid list.""" """Test that a list coercion error is raised if the input is not a valid list."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def process_list(items: list[int]) -> int: def process_list(items: list[int]) -> int:
return sum(items) return sum(items)
@ -350,7 +350,7 @@ class TestLegacyToolJsonParsing:
"""Test JSON string to dict type coercion.""" """Test JSON string to dict type coercion."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def process_dict(data: dict[str, int]) -> int: def process_dict(data: dict[str, int]) -> int:
return sum(data.values()) return sum(data.values())
@ -365,7 +365,7 @@ class TestLegacyToolJsonParsing:
"""Test JSON string to set type coercion.""" """Test JSON string to set type coercion."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def process_set(items: set[int]) -> int: def process_set(items: set[int]) -> int:
assert isinstance(items, set) assert isinstance(items, set)
return sum(items) return sum(items)
@ -378,7 +378,7 @@ class TestLegacyToolJsonParsing:
"""Test JSON string to tuple type coercion.""" """Test JSON string to tuple type coercion."""
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def process_tuple(items: tuple[int, str]) -> int: def process_tuple(items: tuple[int, str]) -> int:
assert isinstance(items, tuple) assert isinstance(items, tuple)
return items[0] + len(items[1]) return items[0] + len(items[1])

View file

@ -519,7 +519,7 @@ class TestCallTools:
mcp = FastMCP(tool_serializer=custom_serializer) mcp = FastMCP(tool_serializer=custom_serializer)
manager = mcp._tool_manager manager = mcp._tool_manager
@mcp.tool() @mcp.tool
def get_data() -> dict: def get_data() -> dict:
return {"key": "value", "number": 123} return {"key": "value", "number": 123}
@ -537,7 +537,7 @@ class TestCallTools:
mcp = FastMCP(tool_serializer=custom_serializer) mcp = FastMCP(tool_serializer=custom_serializer)
manager = mcp._tool_manager manager = mcp._tool_manager
@mcp.tool() @mcp.tool
def get_data() -> list[dict]: def get_data() -> list[dict]:
return [ return [
{"key": "value", "number": 123}, {"key": "value", "number": 123},
@ -561,7 +561,7 @@ class TestCallTools:
mcp = FastMCP(tool_serializer=custom_serializer) mcp = FastMCP(tool_serializer=custom_serializer)
manager = mcp._tool_manager manager = mcp._tool_manager
@mcp.tool() @mcp.tool
def get_data() -> uuid.UUID: def get_data() -> uuid.UUID:
return uuid_result return uuid_result

View file

@ -102,7 +102,7 @@ async def test_multi_client(tmp_path: Path):
mcp = FastMCP() mcp = FastMCP()
@mcp.tool() @mcp.tool
def add(a: int, b: int) -> int: def add(a: int, b: int) -> int:
return a + b return a + b