mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 07:09:11 +02:00
Remove empty parens
This commit is contained in:
parent
699b457748
commit
d1549c3d35
37 changed files with 146 additions and 146 deletions
|
|
@ -31,7 +31,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP("Demo 🚀")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def multiply(a: float, b: float) -> float:
|
||||
"""Multiplies two numbers."""
|
||||
return a * b
|
||||
|
|
@ -201,7 +201,7 @@ from fastmcp import FastMCP, Context
|
|||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def process_data(uri: str, ctx: Context):
|
||||
# Log a message to the client
|
||||
await ctx.info(f"Processing {uri}...")
|
||||
|
|
@ -321,7 +321,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP("Demo 🚀")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP(name="MyServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
|
@ -244,7 +244,7 @@ import asyncio
|
|||
|
||||
mcp = FastMCP(name="MyServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
```
|
||||
|
|
@ -49,7 +49,7 @@ from fastmcp import FastMCP, Client
|
|||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP("Demo 🚀")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP(name="Dice Roller")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
"""Roll `n_dice` 6-sided dice and return the results."""
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
|
@ -170,7 +170,7 @@ auth = BearerAuthProvider(
|
|||
|
||||
mcp = FastMCP(name="Dice Roller", auth=auth)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
"""Roll `n_dice` 6-sided dice and return the results."""
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP(name="Dice Roller")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
"""Roll `n_dice` 6-sided dice and return the results."""
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP(name="Dice Roller")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
"""Roll `n_dice` 6-sided dice and return the results."""
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP(name="Dice Roller")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
"""Roll `n_dice` 6-sided dice and return the results."""
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
|
@ -165,7 +165,7 @@ auth = BearerAuthProvider(
|
|||
|
||||
mcp = FastMCP(name="Dice Roller", auth=auth)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
"""Roll `n_dice` 6-sided dice and return the results."""
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from starlette.requests import Request
|
|||
|
||||
mcp = FastMCP(name="HTTP Request Demo")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def user_agent_info() -> dict:
|
||||
"""Return information about the user agent."""
|
||||
# Get the HTTP request
|
||||
|
|
@ -58,7 +58,7 @@ from fastmcp.server.dependencies import get_http_headers
|
|||
|
||||
mcp = FastMCP(name="Headers Demo")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def safe_header_info() -> dict:
|
||||
"""Safely get header information without raising errors."""
|
||||
# Get headers (returns empty dict if no request context)
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ Once authenticated, your tools, resources, or prompts can access token informati
|
|||
from fastmcp import FastMCP, Context, ToolError
|
||||
from fastmcp.server.dependencies import get_access_token, AccessToken
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def get_my_data(ctx: Context) -> dict:
|
||||
access_token: AccessToken = get_access_token()
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ from fastmcp import FastMCP, Context
|
|||
|
||||
mcp = FastMCP(name="ContextDemo")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def process_file(file_uri: str, ctx: Context) -> str:
|
||||
"""Processes a file, using context for logging and resource access."""
|
||||
# Context is available as the ctx parameter
|
||||
|
|
@ -99,7 +99,7 @@ async def process_data(data: list[float]) -> dict:
|
|||
ctx = get_context()
|
||||
await ctx.info(f"Processing {len(data)} data points")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def analyze_dataset(dataset_name: str) -> dict:
|
||||
# Call utility function that uses context internally
|
||||
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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def analyze_data(data: list[float], ctx: Context) -> dict:
|
||||
"""Analyze numerical data with logging."""
|
||||
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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def process_items(items: list[str], ctx: Context) -> dict:
|
||||
"""Process a list of items with progress updates."""
|
||||
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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def summarize_document(document_uri: str, ctx: Context) -> str:
|
||||
"""Summarize a document by its resource URI."""
|
||||
# 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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def analyze_sentiment(text: str, ctx: Context) -> dict:
|
||||
"""Analyze the sentiment of a text using the client's LLM."""
|
||||
# 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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def generate_example(concept: str, ctx: Context) -> str:
|
||||
"""Generate a Python code example for a given concept."""
|
||||
# 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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def request_info(ctx: Context) -> dict:
|
||||
"""Return information about the current request."""
|
||||
return {
|
||||
|
|
@ -300,7 +300,7 @@ async def request_info(ctx: Context) -> dict:
|
|||
#### FastMCP Server and Sessions
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def advanced_tool(ctx: Context) -> str:
|
||||
"""Demonstrate advanced context access."""
|
||||
# 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:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def handle_web_request(ctx: Context) -> dict:
|
||||
"""Access HTTP request information from the Starlette request."""
|
||||
request = ctx.get_http_request()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def multiply(a: float, b: float) -> float:
|
||||
"""Multiplies two numbers together."""
|
||||
return a * b
|
||||
|
|
@ -106,7 +106,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP(name="MyServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
"""Greet a user by name."""
|
||||
return f"Hello, {name}!"
|
||||
|
|
@ -216,7 +216,7 @@ def yaml_serializer(data):
|
|||
# Create a server with the custom serializer
|
||||
mcp = FastMCP(name="MyServer", tool_serializer=yaml_serializer)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def get_config():
|
||||
"""Returns configuration in YAML format."""
|
||||
return {"api_key": "abc123", "debug": True, "rate_limit": 100}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ This allows LLMs to perform tasks like querying databases, calling APIs, making
|
|||
|
||||
### 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
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP(name="CalculatorServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Adds two integer numbers together."""
|
||||
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:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze_text(
|
||||
text: str,
|
||||
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 pydantic import Field
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_image(
|
||||
image_url: Annotated[str, Field(description="URL of the image to process")],
|
||||
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:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def search_database(
|
||||
query: str = Field(description="Search query string"),
|
||||
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.
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def search_products(
|
||||
query: str, # Required - no 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
|
||||
# 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:
|
||||
"""Calculate the distance between two coordinates."""
|
||||
# Implementation...
|
||||
return 42.5
|
||||
|
||||
# Asynchronous tool (ideal for I/O-bound operations)
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def fetch_weather(city: str) -> dict:
|
||||
"""Retrieve current weather conditions for a city."""
|
||||
# Use 'async def' for operations involving network calls, file I/O, etc.
|
||||
|
|
@ -244,7 +244,7 @@ except ImportError:
|
|||
|
||||
mcp = FastMCP("Image Demo")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def generate_image(width: int, height: int, color: str) -> Image:
|
||||
"""Generates a solid color image."""
|
||||
# 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 Image(data=img_bytes, format="png")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def do_nothing() -> None:
|
||||
"""This tool performs an action but returns no data."""
|
||||
print("Performing a side effect...")
|
||||
|
|
@ -285,7 +285,7 @@ mcp = FastMCP(name="SecureServer", mask_error_details=True)
|
|||
from fastmcp import FastMCP
|
||||
from fastmcp.exceptions import ToolError
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def divide(a: float, b: float) -> float:
|
||||
"""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)
|
||||
- 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
|
||||
@mcp.tool(
|
||||
|
|
@ -351,7 +351,7 @@ from fastmcp import FastMCP, Context
|
|||
|
||||
mcp = FastMCP(name="ContextDemo")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def process_data(data_uri: str, ctx: Context) -> dict:
|
||||
"""Process data from a resource with progress reporting."""
|
||||
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:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_values(
|
||||
name: str, # Text data
|
||||
count: int, # Integer numbers
|
||||
|
|
@ -416,7 +416,7 @@ FastMCP supports various date and time types from the `datetime` module:
|
|||
```python
|
||||
from datetime import datetime, date, timedelta
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_date_time(
|
||||
event_date: date, # ISO format date string or date 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:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze_data(
|
||||
values: list[float], # List of numbers
|
||||
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:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def flexible_search(
|
||||
query: str | int, # Can be either string or integer
|
||||
filters: dict[str, str] | None = None, # Optional dictionary
|
||||
|
|
@ -488,7 +488,7 @@ Literals constrain parameters to a specific set of values:
|
|||
```python
|
||||
from typing import Literal
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def sort_data(
|
||||
data: list[float],
|
||||
order: Literal["ascending", "descending"] = "ascending",
|
||||
|
|
@ -516,7 +516,7 @@ class Color(Enum):
|
|||
GREEN = "green"
|
||||
BLUE = "blue"
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_image(
|
||||
image_path: str,
|
||||
color_filter: Color = Color.RED
|
||||
|
|
@ -539,7 +539,7 @@ There are two approaches to handling binary data in tool parameters:
|
|||
#### Bytes
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_binary(data: bytes):
|
||||
"""Process binary data directly.
|
||||
|
||||
|
|
@ -563,7 +563,7 @@ FastMCP does not automatically decode base64-encoded strings for bytes parameter
|
|||
from typing import Annotated
|
||||
from pydantic import Field
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_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
|
||||
from pathlib import Path
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_file(path: Path) -> str:
|
||||
"""Process a file at the given path."""
|
||||
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
|
||||
import uuid
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_item(
|
||||
item_id: uuid.UUID # String UUID or UUID object
|
||||
) -> str:
|
||||
|
|
@ -628,7 +628,7 @@ class User(BaseModel):
|
|||
age: int | None = None
|
||||
is_active: bool = True
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def create_user(user: User):
|
||||
"""Create a new user in the system."""
|
||||
# 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 pydantic import Field
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze_metrics(
|
||||
# Numbers with range constraints
|
||||
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:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def validate_data(
|
||||
# Value constraints
|
||||
age: int = Field(ge=0, lt=120), # 0 <= age < 120
|
||||
|
|
@ -727,12 +727,12 @@ mcp = FastMCP(
|
|||
on_duplicate_tools="error"
|
||||
)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def my_tool(): return "Version 1"
|
||||
|
||||
# This will now raise a ValueError because 'my_tool' already exists
|
||||
# and on_duplicate_tools is set to "error".
|
||||
# @mcp.tool()
|
||||
# @mcp.tool
|
||||
# def my_tool(): return "Version 2"
|
||||
```
|
||||
|
||||
|
|
@ -754,7 +754,7 @@ from fastmcp import FastMCP
|
|||
|
||||
mcp = FastMCP(name="DynamicToolServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def calculate_sum(a: int, b: int) -> int:
|
||||
"""Add two numbers together."""
|
||||
return a + b
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class ShrimpTank(BaseModel):
|
|||
shrimp: list[Shrimp]
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def name_shrimp(
|
||||
tank: ShrimpTank,
|
||||
# You can use pydantic Field in function signatures for validation.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ if args.debug:
|
|||
mcp = FastMCP(server_name)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def get_status() -> dict[str, str | bool]:
|
||||
"""Get the current server configuration and status."""
|
||||
return {
|
||||
|
|
@ -34,7 +34,7 @@ def get_status() -> dict[str, str | bool]:
|
|||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def echo_message(message: str) -> str:
|
||||
"""Echo a message, with debug info if debug mode is enabled."""
|
||||
if args.debug:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ def get_greeting(name: str) -> str:
|
|||
return f"Hello, {name}!"
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from fastmcp import FastMCP
|
|||
mcp = FastMCP("Echo Server")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def echo_tool(text: str) -> str:
|
||||
"""Echo the input text"""
|
||||
return text
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ async def display_memory_tree(deps: Deps) -> str:
|
|||
return result
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def remember(
|
||||
contents: list[str] = Field(
|
||||
description="List of observations or memories to store"
|
||||
|
|
@ -294,7 +294,7 @@ async def remember(
|
|||
await deps.pool.close()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def read_profile() -> str:
|
||||
deps = Deps(openai=AsyncOpenAI(), pool=await get_db_pool())
|
||||
profile = await display_memory_tree(deps)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from fastmcp.client.sampling import RequestContext, SamplingMessage, SamplingPar
|
|||
mcp = FastMCP("Sampling Example")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def example_tool(prompt: str, context: Context) -> str:
|
||||
"""Sample a completion from the LLM."""
|
||||
response = await context.sample(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from fastmcp import FastMCP, Image
|
|||
mcp = FastMCP("Screenshot Demo", dependencies=["pyautogui", "Pillow"])
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def take_screenshot() -> Image:
|
||||
"""
|
||||
Take a screenshot of the user's screen and return it as an image. Use
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from fastmcp import FastMCP
|
|||
mcp = FastMCP("Echo Server")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def echo(text: str) -> str:
|
||||
"""Echo the input text"""
|
||||
return text
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from fastmcp.contrib.bulk_tool_caller import BulkToolCaller
|
|||
mcp = FastMCP()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def echo_tool(text: str) -> str:
|
||||
"""Echo the input text"""
|
||||
return text
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ def run_mcp_server(
|
|||
)
|
||||
)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ class TestErrorHandling:
|
|||
async def test_general_tool_exceptions_are_not_masked_by_default(self):
|
||||
mcp = FastMCP("TestServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def error_tool():
|
||||
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):
|
||||
mcp = FastMCP("TestServer", mask_error_details=True)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def error_tool():
|
||||
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):
|
||||
mcp = FastMCP("TestServer")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def custom_error_tool():
|
||||
raise ToolError("This is a test error (abc)")
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ class LogHandler:
|
|||
def fastmcp_server():
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def log(context: Context) -> None:
|
||||
await context.info(message="hello?")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def echo_log(
|
||||
message: str,
|
||||
context: Context,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ def clear_progress_messages():
|
|||
def fastmcp_server():
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def progress_tool(context: Context) -> int:
|
||||
for i in range(3):
|
||||
await context.report_progress(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from fastmcp import Client, Context, FastMCP
|
|||
def fastmcp_server():
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def list_roots(context: Context) -> list[str]:
|
||||
roots = await context.list_roots()
|
||||
return [str(r.uri) for r in roots]
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ from fastmcp.client.sampling import RequestContext, SamplingMessage, SamplingPar
|
|||
def fastmcp_server():
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def simple_sample(message: str, context: Context) -> str:
|
||||
result = await context.sample("Hello, world!")
|
||||
return cast(TextContent, result).text
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def sample_with_system_prompt(message: str, context: Context) -> str:
|
||||
result = await context.sample("Hello, world!", system_prompt="You love FastMCP")
|
||||
return cast(TextContent, result).text
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def sample_with_messages(message: str, context: Context) -> str:
|
||||
result = await context.sample(
|
||||
[
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class TestKeepAlive:
|
|||
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def pid() -> int:
|
||||
"""Gets PID of server"""
|
||||
return os.getpid()
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ def resources(mcp: FastMCP, test_dir: Path) -> FastMCP:
|
|||
|
||||
@pytest.fixture(autouse=True)
|
||||
def tools(mcp: FastMCP, test_dir: Path) -> FastMCP:
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def delete_file(path: str) -> bool:
|
||||
# ensure path is in test_dir
|
||||
if Path(path).resolve().parent != test_dir:
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class TestTools:
|
|||
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def fn(x: int) -> int:
|
||||
return x + 1
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ class TestToolDecorator:
|
|||
async def test_tool_decorator(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
|
|
@ -179,7 +179,7 @@ class TestToolDecorator:
|
|||
def __init__(self, x: int):
|
||||
self.x = x
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(self, y: int) -> int:
|
||||
return self.x + y
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ class TestToolDecorator:
|
|||
|
||||
class MyClass:
|
||||
@staticmethod
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ class TestToolDecorator:
|
|||
async def test_tool_decorator_async_function(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ class TestToolDecorator:
|
|||
"""Test that tools with annotated arguments work correctly."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(
|
||||
x: Annotated[int, Field(description="x is 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."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(
|
||||
x: int = Field(description="x is an int"),
|
||||
y: str = Field(description="y is not an int"),
|
||||
|
|
|
|||
|
|
@ -30,30 +30,30 @@ from fastmcp.utilities.types import Image
|
|||
def tool_server():
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def list_tool() -> list[str | int]:
|
||||
return ["x", 2]
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def error_tool() -> None:
|
||||
raise ValueError("Test error")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def image_tool(path: str) -> Image:
|
||||
return Image(path)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def mixed_content_tool() -> list[TextContent | ImageContent]:
|
||||
return [
|
||||
TextContent(type="text", text="Hello"),
|
||||
ImageContent(type="image", data="abc", mimeType="image/png"),
|
||||
]
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def mixed_list_fn(image_path: str) -> list:
|
||||
return [
|
||||
"text message",
|
||||
|
|
@ -100,7 +100,7 @@ class TestTools:
|
|||
mcp = FastMCP()
|
||||
client = Client(transport=FastMCPTransport(mcp))
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def error_tool():
|
||||
raise ValueError("Test error")
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ class TestToolReturnTypes:
|
|||
async def test_string(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def string_tool() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ class TestToolReturnTypes:
|
|||
async def test_bytes(self, tmp_path: Path):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def bytes_tool() -> bytes:
|
||||
return b"Hello, world!"
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ class TestToolReturnTypes:
|
|||
|
||||
test_uuid = uuid.uuid4()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def uuid_tool() -> uuid.UUID:
|
||||
return test_uuid
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ class TestToolReturnTypes:
|
|||
|
||||
test_path = Path("/tmp/test.txt")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def path_tool() -> Path:
|
||||
return test_path
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ class TestToolReturnTypes:
|
|||
|
||||
dt = datetime.datetime(2025, 4, 25, 1, 2, 3)
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def datetime_tool() -> datetime.datetime:
|
||||
return dt
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ class TestToolReturnTypes:
|
|||
async def test_image(self, tmp_path: Path):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def image_tool(path: str) -> Image:
|
||||
return Image(path)
|
||||
|
||||
|
|
@ -243,7 +243,7 @@ class TestToolParameters:
|
|||
async def test_parameter_descriptions_with_field_annotations(self):
|
||||
mcp = FastMCP("Test Server")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def greet(
|
||||
name: Annotated[str, Field(description="The name to greet")],
|
||||
title: Annotated[str, Field(description="Optional title", default="")],
|
||||
|
|
@ -268,7 +268,7 @@ class TestToolParameters:
|
|||
async def test_parameter_descriptions_with_field_defaults(self):
|
||||
mcp = FastMCP("Test Server")
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def greet(
|
||||
name: str = Field(description="The name to greet"),
|
||||
title: str = Field(description="Optional title", default=""),
|
||||
|
|
@ -293,7 +293,7 @@ class TestToolParameters:
|
|||
async def test_tool_with_bytes_input(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_image(image: bytes) -> Image:
|
||||
return Image(data=image)
|
||||
|
||||
|
|
@ -308,7 +308,7 @@ class TestToolParameters:
|
|||
async def test_tool_with_invalid_input(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def my_tool(x: int) -> int:
|
||||
return x + 1
|
||||
|
||||
|
|
@ -323,7 +323,7 @@ class TestToolParameters:
|
|||
"""Test string-to-int type coercion."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add_one(x: int) -> int:
|
||||
return x + 1
|
||||
|
||||
|
|
@ -336,7 +336,7 @@ class TestToolParameters:
|
|||
"""Test string-to-bool type coercion."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def toggle(flag: bool) -> bool:
|
||||
return not flag
|
||||
|
||||
|
|
@ -351,7 +351,7 @@ class TestToolParameters:
|
|||
async def test_annotated_field_validation(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: Annotated[int, Field(ge=1)]) -> None:
|
||||
pass
|
||||
|
||||
|
|
@ -362,7 +362,7 @@ class TestToolParameters:
|
|||
async def test_default_field_validation(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: int = Field(ge=1)) -> None:
|
||||
pass
|
||||
|
||||
|
|
@ -373,7 +373,7 @@ class TestToolParameters:
|
|||
async def test_default_field_is_still_required_if_no_default_specified(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: int = Field()) -> None:
|
||||
pass
|
||||
|
||||
|
|
@ -384,7 +384,7 @@ class TestToolParameters:
|
|||
async def test_literal_type_validation_error(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: Literal["a", "b"]) -> None:
|
||||
pass
|
||||
|
||||
|
|
@ -395,7 +395,7 @@ class TestToolParameters:
|
|||
async def test_literal_type_validation_success(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: Literal["a", "b"]) -> str:
|
||||
return x
|
||||
|
||||
|
|
@ -411,7 +411,7 @@ class TestToolParameters:
|
|||
GREEN = "green"
|
||||
BLUE = "blue"
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: MyEnum) -> str:
|
||||
return x.value
|
||||
|
||||
|
|
@ -427,7 +427,7 @@ class TestToolParameters:
|
|||
GREEN = "green"
|
||||
BLUE = "blue"
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: MyEnum) -> str:
|
||||
return x.value
|
||||
|
||||
|
|
@ -438,7 +438,7 @@ class TestToolParameters:
|
|||
async def test_union_type_validation(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def analyze(x: int | float) -> str:
|
||||
return str(x)
|
||||
|
||||
|
|
@ -455,7 +455,7 @@ class TestToolParameters:
|
|||
async def test_path_type(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_path(path: Path) -> str:
|
||||
assert isinstance(path, Path)
|
||||
return str(path)
|
||||
|
|
@ -470,7 +470,7 @@ class TestToolParameters:
|
|||
async def test_path_type_error(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_path(path: Path) -> str:
|
||||
return str(path)
|
||||
|
||||
|
|
@ -481,7 +481,7 @@ class TestToolParameters:
|
|||
async def test_uuid_type(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_uuid(x: uuid.UUID) -> str:
|
||||
assert isinstance(x, uuid.UUID)
|
||||
return str(x)
|
||||
|
|
@ -495,7 +495,7 @@ class TestToolParameters:
|
|||
async def test_uuid_type_error(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_uuid(x: uuid.UUID) -> str:
|
||||
return str(x)
|
||||
|
||||
|
|
@ -506,7 +506,7 @@ class TestToolParameters:
|
|||
async def test_datetime_type(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_datetime(x: datetime.datetime) -> str:
|
||||
return x.isoformat()
|
||||
|
||||
|
|
@ -519,7 +519,7 @@ class TestToolParameters:
|
|||
async def test_datetime_type_parse_string(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_datetime(x: datetime.datetime) -> str:
|
||||
return x.isoformat()
|
||||
|
||||
|
|
@ -532,7 +532,7 @@ class TestToolParameters:
|
|||
async def test_datetime_type_error(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_datetime(x: datetime.datetime) -> str:
|
||||
return x.isoformat()
|
||||
|
||||
|
|
@ -543,7 +543,7 @@ class TestToolParameters:
|
|||
async def test_date_type(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_date(x: datetime.date) -> str:
|
||||
return x.isoformat()
|
||||
|
||||
|
|
@ -554,7 +554,7 @@ class TestToolParameters:
|
|||
async def test_date_type_parse_string(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_date(x: datetime.date) -> str:
|
||||
return x.isoformat()
|
||||
|
||||
|
|
@ -565,7 +565,7 @@ class TestToolParameters:
|
|||
async def test_timedelta_type(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_timedelta(x: datetime.timedelta) -> str:
|
||||
return str(x)
|
||||
|
||||
|
|
@ -578,7 +578,7 @@ class TestToolParameters:
|
|||
async def test_timedelta_type_parse_int(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def send_timedelta(x: datetime.timedelta) -> str:
|
||||
return str(x)
|
||||
|
||||
|
|
@ -594,7 +594,7 @@ class TestToolContextInjection:
|
|||
"""Test that context parameters are properly detected."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def tool_with_context(x: int, ctx: Context) -> str:
|
||||
return f"Request {ctx.request_id}: {x}"
|
||||
|
||||
|
|
@ -607,7 +607,7 @@ class TestToolContextInjection:
|
|||
"""Test that context is properly injected into tool calls."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def tool_with_context(x: int, ctx: Context) -> str:
|
||||
assert isinstance(ctx, Context)
|
||||
assert ctx.request_id is not None
|
||||
|
|
@ -623,7 +623,7 @@ class TestToolContextInjection:
|
|||
"""Test that context works in async functions."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def async_tool(x: int, ctx: Context) -> str:
|
||||
assert ctx.request_id is not None
|
||||
return f"Async request {ctx.request_id}: {x}"
|
||||
|
|
@ -638,7 +638,7 @@ class TestToolContextInjection:
|
|||
"""Test that context is optional."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def no_context(x: int) -> int:
|
||||
return x * 2
|
||||
|
||||
|
|
@ -656,7 +656,7 @@ class TestToolContextInjection:
|
|||
def test_resource() -> str:
|
||||
return "resource data"
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
async def tool_with_resource(ctx: Context) -> str:
|
||||
r_iter = await ctx.read_resource("test://data")
|
||||
r_list = list(r_iter)
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ class TestLegacyToolJsonParsing:
|
|||
"""Test JSON string to collection type coercion."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_list(items: list[int]) -> int:
|
||||
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."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_list(items: list[int]) -> int:
|
||||
return sum(items)
|
||||
|
||||
|
|
@ -350,7 +350,7 @@ class TestLegacyToolJsonParsing:
|
|||
"""Test JSON string to dict type coercion."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_dict(data: dict[str, int]) -> int:
|
||||
return sum(data.values())
|
||||
|
||||
|
|
@ -365,7 +365,7 @@ class TestLegacyToolJsonParsing:
|
|||
"""Test JSON string to set type coercion."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_set(items: set[int]) -> int:
|
||||
assert isinstance(items, set)
|
||||
return sum(items)
|
||||
|
|
@ -378,7 +378,7 @@ class TestLegacyToolJsonParsing:
|
|||
"""Test JSON string to tuple type coercion."""
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def process_tuple(items: tuple[int, str]) -> int:
|
||||
assert isinstance(items, tuple)
|
||||
return items[0] + len(items[1])
|
||||
|
|
|
|||
|
|
@ -519,7 +519,7 @@ class TestCallTools:
|
|||
mcp = FastMCP(tool_serializer=custom_serializer)
|
||||
manager = mcp._tool_manager
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def get_data() -> dict:
|
||||
return {"key": "value", "number": 123}
|
||||
|
||||
|
|
@ -537,7 +537,7 @@ class TestCallTools:
|
|||
mcp = FastMCP(tool_serializer=custom_serializer)
|
||||
manager = mcp._tool_manager
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def get_data() -> list[dict]:
|
||||
return [
|
||||
{"key": "value", "number": 123},
|
||||
|
|
@ -561,7 +561,7 @@ class TestCallTools:
|
|||
mcp = FastMCP(tool_serializer=custom_serializer)
|
||||
manager = mcp._tool_manager
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def get_data() -> uuid.UUID:
|
||||
return uuid_result
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ async def test_multi_client(tmp_path: Path):
|
|||
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.tool()
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue