Spec Registry
Help your agents use open-source better. Learn more.
Find usage specs for your project’s dependencies
- Author
- tessl
- Last updated
- Spec files
pypi-fastmcp
Describes: pypi/fastmcp
- Description
- The fast, Pythonic way to build MCP servers and clients with minimal boilerplate code.
- Author
- tessl
- Last updated
tools.md docs/
1# Tools System23Tools allow LLMs to perform actions by executing Python functions, with automatic schema generation and flexible return types. The FastMCP tools system handles function introspection, parameter validation, and result formatting automatically.45## Capabilities67### Tool Classes89Base classes for creating and managing tools with schema generation and execution.1011```python { .api }12class Tool:13def __init__(14self,15name: str,16description: str,17func: Callable,18schema: dict | None = None19):20"""21Base tool class.2223Parameters:24- name: Tool name for LLM reference25- description: Tool description for LLM understanding26- func: Python function to execute27- schema: Optional custom JSON schema (auto-generated if None)28"""2930class FunctionTool(Tool):31"""Tool implementation for function-based tools with automatic schema generation."""32```3334### Tool Manager3536Manages tool registration, retrieval, and execution within a FastMCP server.3738```python { .api }39class ToolManager:40def add_tool(self, tool: Tool) -> None:41"""42Add a tool to the manager.4344Parameters:45- tool: Tool instance to add46"""4748def remove_tool(self, name: str) -> None:49"""50Remove a tool by name.5152Parameters:53- name: Name of tool to remove54"""5556def get_tool(self, name: str) -> Tool | None:57"""58Get a tool by name.5960Parameters:61- name: Name of tool to retrieve6263Returns:64Tool instance or None if not found65"""6667def list_tools(self) -> list[Tool]:68"""69List all registered tools.7071Returns:72List of all tool instances73"""74```7576### Tool Transformations7778Functions for transforming and forwarding tool calls to other implementations.7980```python { .api }81def forward(82tool_name: str,83target_tool_name: str | None = None,84transform_args: Callable | None = None,85transform_result: Callable | None = None86) -> Callable:87"""88Forward tool calls to another tool with optional transformations.8990Parameters:91- tool_name: Name of tool to forward92- target_tool_name: Target tool name (defaults to same name)93- transform_args: Function to transform arguments94- transform_result: Function to transform result9596Returns:97Tool transformation function98"""99100def forward_raw(101tool_name: str,102target_function: Callable,103transform_args: Callable | None = None,104transform_result: Callable | None = None105) -> Callable:106"""107Forward tool calls to a raw function with optional transformations.108109Parameters:110- tool_name: Name of tool to forward111- target_function: Target function to call112- transform_args: Function to transform arguments113- transform_result: Function to transform result114115Returns:116Tool transformation function117"""118```119120## Usage Examples121122### Basic Tool Creation123124```python125from fastmcp import FastMCP126127mcp = FastMCP("Math Server")128129@mcp.tool130def add(a: int, b: int) -> int:131"""Add two numbers together."""132return a + b133134@mcp.tool135def divide(a: float, b: float) -> float:136"""Divide two numbers with error handling."""137if b == 0:138raise ValueError("Cannot divide by zero")139return a / b140141@mcp.tool142def factorial(n: int) -> int:143"""Calculate factorial of a number."""144if n < 0:145raise ValueError("Factorial not defined for negative numbers")146if n == 0 or n == 1:147return 1148result = 1149for i in range(2, n + 1):150result *= i151return result152```153154### Advanced Tool with Custom Types155156```python157from fastmcp import FastMCP158from fastmcp.utilities.types import Image, File159from typing import List, Dict, Optional160from dataclasses import dataclass161162mcp = FastMCP("Advanced Server")163164@dataclass165class ProcessingResult:166success: bool167message: str168data: Dict[str, any]169170@mcp.tool171def process_data(172items: List[str],173options: Optional[Dict[str, str]] = None,174batch_size: int = 10175) -> ProcessingResult:176"""177Process a list of data items with configurable options.178179Parameters:180- items: List of items to process181- options: Optional processing configuration182- batch_size: Number of items to process at once183184Returns:185ProcessingResult with success status and processed data186"""187if not items:188return ProcessingResult(189success=False,190message="No items provided",191data={}192)193194processed = []195for i in range(0, len(items), batch_size):196batch = items[i:i + batch_size]197processed.extend([item.upper() for item in batch])198199return ProcessingResult(200success=True,201message=f"Processed {len(items)} items",202data={"processed_items": processed}203)204205@mcp.tool206def create_chart(207data: Dict[str, List[float]],208title: str = "Chart"209) -> Image:210"""211Create a chart from data and return as image.212213Parameters:214- data: Dictionary with series names as keys and data points as values215- title: Chart title216217Returns:218Chart image as PNG219"""220# This would use matplotlib or similar to create a chart221import matplotlib.pyplot as plt222import io223224plt.figure(figsize=(10, 6))225for series_name, values in data.items():226plt.plot(values, label=series_name)227228plt.title(title)229plt.legend()230plt.grid(True)231232# Save to bytes233buffer = io.BytesIO()234plt.savefig(buffer, format='png')235buffer.seek(0)236237return Image(buffer.read(), mime_type="image/png")238```239240### Tool with Context Usage241242```python243from fastmcp import FastMCP, Context244import httpx245246mcp = FastMCP("API Server")247248@mcp.tool249async def fetch_and_analyze(250url: str,251analysis_prompt: str,252ctx: Context253) -> str:254"""255Fetch data from URL and analyze it using LLM.256257Parameters:258- url: URL to fetch data from259- analysis_prompt: Prompt for LLM analysis260- ctx: Execution context for capabilities261262Returns:263Analysis result from LLM264"""265# Log the operation266await ctx.info(f"Fetching data from {url}")267268# Fetch data using HTTP request269response = await ctx.http_request("GET", url)270271if response.status_code != 200:272await ctx.error(f"Failed to fetch data: {response.status_code}")273return f"Error: Unable to fetch data from {url}"274275data = response.text276await ctx.info(f"Fetched {len(data)} characters of data")277278# Use LLM sampling for analysis279messages = [280{281"role": "user",282"content": f"{analysis_prompt}\n\nData to analyze:\n{data[:1000]}..."283}284]285286# Report progress287await ctx.report_progress(50, 100)288289analysis = await ctx.sample(messages)290291await ctx.report_progress(100, 100)292293return analysis.text294295@mcp.tool296async def multi_step_process(297input_data: str,298ctx: Context299) -> Dict[str, str]:300"""301Perform multi-step processing with progress reporting.302303Parameters:304- input_data: Data to process305- ctx: Execution context306307Returns:308Dictionary with processing results309"""310results = {}311total_steps = 4312313# Step 1: Validation314await ctx.info("Step 1: Validating input")315await ctx.report_progress(1, total_steps)316if not input_data.strip():317raise ValueError("Input data cannot be empty")318results["validation"] = "passed"319320# Step 2: Processing321await ctx.info("Step 2: Processing data")322await ctx.report_progress(2, total_steps)323processed = input_data.upper().strip()324results["processed"] = processed325326# Step 3: Analysis327await ctx.info("Step 3: Analyzing results")328await ctx.report_progress(3, total_steps)329analysis = f"Data contains {len(processed)} characters"330results["analysis"] = analysis331332# Step 4: Completion333await ctx.info("Step 4: Finalizing results")334await ctx.report_progress(4, total_steps)335results["status"] = "complete"336337await ctx.info("Multi-step process completed successfully")338339return results340```341342### Tool Error Handling343344```python345from fastmcp import FastMCP346from fastmcp.exceptions import ToolError347348mcp = FastMCP("Robust Server")349350@mcp.tool351def safe_divide(a: float, b: float) -> float:352"""353Safely divide two numbers with proper error handling.354355Parameters:356- a: Dividend357- b: Divisor358359Returns:360Result of division361362Raises:363ToolError: If division by zero or invalid input364"""365try:366if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):367raise ToolError("Both arguments must be numbers")368369if b == 0:370raise ToolError("Division by zero is not allowed")371372result = a / b373374# Check for infinite or NaN results375if not isinstance(result, (int, float)) or result != result: # NaN check376raise ToolError("Division resulted in invalid number")377378return result379380except Exception as e:381if isinstance(e, ToolError):382raise383raise ToolError(f"Unexpected error during division: {str(e)}")384385@mcp.tool386def validate_and_process(data: Dict[str, any]) -> Dict[str, any]:387"""388Validate and process input data with comprehensive error handling.389390Parameters:391- data: Input data dictionary392393Returns:394Processed data dictionary395"""396if not isinstance(data, dict):397raise ToolError("Input must be a dictionary")398399required_fields = ["name", "value"]400missing_fields = [field for field in required_fields if field not in data]401402if missing_fields:403raise ToolError(f"Missing required fields: {', '.join(missing_fields)}")404405# Validate field types406if not isinstance(data["name"], str):407raise ToolError("Field 'name' must be a string")408409if not isinstance(data["value"], (int, float)):410raise ToolError("Field 'value' must be a number")411412# Process the data413return {414"processed_name": data["name"].strip().title(),415"processed_value": round(data["value"], 2),416"timestamp": "2024-01-01T00:00:00Z", # Would use real timestamp417"status": "processed"418}419```420421### Tool Transformations and Forwarding422423```python424from fastmcp import FastMCP425from fastmcp.tools import forward, forward_raw426427mcp = FastMCP("Transform Server")428429# Original tools430@mcp.tool431def original_add(a: int, b: int) -> int:432"""Add two integers."""433return a + b434435@mcp.tool436def original_multiply(x: float, y: float) -> float:437"""Multiply two floats."""438return x * y439440# Transform arguments: convert strings to numbers441def string_to_number_transform(args):442"""Transform string arguments to numbers."""443return {444key: float(value) if isinstance(value, str) and value.replace('.', '').isdigit() else value445for key, value in args.items()446}447448# Transform result: add metadata449def add_metadata_transform(result):450"""Add metadata to result."""451return {452"result": result,453"type": type(result).__name__,454"timestamp": "2024-01-01T00:00:00Z"455}456457# Apply transformations458mcp.add_tool_transformation(forward(459"string_add",460target_tool_name="original_add",461transform_args=string_to_number_transform,462transform_result=add_metadata_transform463))464465# Forward to external function466def external_power(base: float, exponent: float) -> float:467"""Calculate base raised to exponent."""468return base ** exponent469470mcp.add_tool_transformation(forward_raw(471"power",472target_function=external_power,473transform_result=lambda result: {"power_result": result}474))475```476477## Return Types478479Tools can return various types of data:480481```python482# Simple types483@mcp.tool484def get_string() -> str:485return "Hello, world!"486487@mcp.tool488def get_number() -> float:489return 42.5490491@mcp.tool492def get_boolean() -> bool:493return True494495# Complex types496@mcp.tool497def get_dict() -> Dict[str, any]:498return {"key": "value", "count": 10}499500@mcp.tool501def get_list() -> List[str]:502return ["item1", "item2", "item3"]503504# Media types505@mcp.tool506def get_image() -> Image:507# Return image data508with open("chart.png", "rb") as f:509return Image(f.read(), mime_type="image/png")510511@mcp.tool512def get_file() -> File:513# Return file data514return File(515data="file content",516name="output.txt",517mime_type="text/plain"518)519```