Spec RegistrySpec Registry

Help your agents use open-source better. Learn more.

Find usage specs for your project’s dependencies

>

pypi-fastmcp

Describes: pypipypi/fastmcp

Description
The fast, Pythonic way to build MCP servers and clients with minimal boilerplate code.
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/pypi-fastmcp@2.12.0

tools.md docs/

1
# Tools System
2
3
Tools 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.
4
5
## Capabilities
6
7
### Tool Classes
8
9
Base classes for creating and managing tools with schema generation and execution.
10
11
```python { .api }
12
class Tool:
13
def __init__(
14
self,
15
name: str,
16
description: str,
17
func: Callable,
18
schema: dict | None = None
19
):
20
"""
21
Base tool class.
22
23
Parameters:
24
- name: Tool name for LLM reference
25
- description: Tool description for LLM understanding
26
- func: Python function to execute
27
- schema: Optional custom JSON schema (auto-generated if None)
28
"""
29
30
class FunctionTool(Tool):
31
"""Tool implementation for function-based tools with automatic schema generation."""
32
```
33
34
### Tool Manager
35
36
Manages tool registration, retrieval, and execution within a FastMCP server.
37
38
```python { .api }
39
class ToolManager:
40
def add_tool(self, tool: Tool) -> None:
41
"""
42
Add a tool to the manager.
43
44
Parameters:
45
- tool: Tool instance to add
46
"""
47
48
def remove_tool(self, name: str) -> None:
49
"""
50
Remove a tool by name.
51
52
Parameters:
53
- name: Name of tool to remove
54
"""
55
56
def get_tool(self, name: str) -> Tool | None:
57
"""
58
Get a tool by name.
59
60
Parameters:
61
- name: Name of tool to retrieve
62
63
Returns:
64
Tool instance or None if not found
65
"""
66
67
def list_tools(self) -> list[Tool]:
68
"""
69
List all registered tools.
70
71
Returns:
72
List of all tool instances
73
"""
74
```
75
76
### Tool Transformations
77
78
Functions for transforming and forwarding tool calls to other implementations.
79
80
```python { .api }
81
def forward(
82
tool_name: str,
83
target_tool_name: str | None = None,
84
transform_args: Callable | None = None,
85
transform_result: Callable | None = None
86
) -> Callable:
87
"""
88
Forward tool calls to another tool with optional transformations.
89
90
Parameters:
91
- tool_name: Name of tool to forward
92
- target_tool_name: Target tool name (defaults to same name)
93
- transform_args: Function to transform arguments
94
- transform_result: Function to transform result
95
96
Returns:
97
Tool transformation function
98
"""
99
100
def forward_raw(
101
tool_name: str,
102
target_function: Callable,
103
transform_args: Callable | None = None,
104
transform_result: Callable | None = None
105
) -> Callable:
106
"""
107
Forward tool calls to a raw function with optional transformations.
108
109
Parameters:
110
- tool_name: Name of tool to forward
111
- target_function: Target function to call
112
- transform_args: Function to transform arguments
113
- transform_result: Function to transform result
114
115
Returns:
116
Tool transformation function
117
"""
118
```
119
120
## Usage Examples
121
122
### Basic Tool Creation
123
124
```python
125
from fastmcp import FastMCP
126
127
mcp = FastMCP("Math Server")
128
129
@mcp.tool
130
def add(a: int, b: int) -> int:
131
"""Add two numbers together."""
132
return a + b
133
134
@mcp.tool
135
def divide(a: float, b: float) -> float:
136
"""Divide two numbers with error handling."""
137
if b == 0:
138
raise ValueError("Cannot divide by zero")
139
return a / b
140
141
@mcp.tool
142
def factorial(n: int) -> int:
143
"""Calculate factorial of a number."""
144
if n < 0:
145
raise ValueError("Factorial not defined for negative numbers")
146
if n == 0 or n == 1:
147
return 1
148
result = 1
149
for i in range(2, n + 1):
150
result *= i
151
return result
152
```
153
154
### Advanced Tool with Custom Types
155
156
```python
157
from fastmcp import FastMCP
158
from fastmcp.utilities.types import Image, File
159
from typing import List, Dict, Optional
160
from dataclasses import dataclass
161
162
mcp = FastMCP("Advanced Server")
163
164
@dataclass
165
class ProcessingResult:
166
success: bool
167
message: str
168
data: Dict[str, any]
169
170
@mcp.tool
171
def process_data(
172
items: List[str],
173
options: Optional[Dict[str, str]] = None,
174
batch_size: int = 10
175
) -> ProcessingResult:
176
"""
177
Process a list of data items with configurable options.
178
179
Parameters:
180
- items: List of items to process
181
- options: Optional processing configuration
182
- batch_size: Number of items to process at once
183
184
Returns:
185
ProcessingResult with success status and processed data
186
"""
187
if not items:
188
return ProcessingResult(
189
success=False,
190
message="No items provided",
191
data={}
192
)
193
194
processed = []
195
for i in range(0, len(items), batch_size):
196
batch = items[i:i + batch_size]
197
processed.extend([item.upper() for item in batch])
198
199
return ProcessingResult(
200
success=True,
201
message=f"Processed {len(items)} items",
202
data={"processed_items": processed}
203
)
204
205
@mcp.tool
206
def create_chart(
207
data: Dict[str, List[float]],
208
title: str = "Chart"
209
) -> Image:
210
"""
211
Create a chart from data and return as image.
212
213
Parameters:
214
- data: Dictionary with series names as keys and data points as values
215
- title: Chart title
216
217
Returns:
218
Chart image as PNG
219
"""
220
# This would use matplotlib or similar to create a chart
221
import matplotlib.pyplot as plt
222
import io
223
224
plt.figure(figsize=(10, 6))
225
for series_name, values in data.items():
226
plt.plot(values, label=series_name)
227
228
plt.title(title)
229
plt.legend()
230
plt.grid(True)
231
232
# Save to bytes
233
buffer = io.BytesIO()
234
plt.savefig(buffer, format='png')
235
buffer.seek(0)
236
237
return Image(buffer.read(), mime_type="image/png")
238
```
239
240
### Tool with Context Usage
241
242
```python
243
from fastmcp import FastMCP, Context
244
import httpx
245
246
mcp = FastMCP("API Server")
247
248
@mcp.tool
249
async def fetch_and_analyze(
250
url: str,
251
analysis_prompt: str,
252
ctx: Context
253
) -> str:
254
"""
255
Fetch data from URL and analyze it using LLM.
256
257
Parameters:
258
- url: URL to fetch data from
259
- analysis_prompt: Prompt for LLM analysis
260
- ctx: Execution context for capabilities
261
262
Returns:
263
Analysis result from LLM
264
"""
265
# Log the operation
266
await ctx.info(f"Fetching data from {url}")
267
268
# Fetch data using HTTP request
269
response = await ctx.http_request("GET", url)
270
271
if response.status_code != 200:
272
await ctx.error(f"Failed to fetch data: {response.status_code}")
273
return f"Error: Unable to fetch data from {url}"
274
275
data = response.text
276
await ctx.info(f"Fetched {len(data)} characters of data")
277
278
# Use LLM sampling for analysis
279
messages = [
280
{
281
"role": "user",
282
"content": f"{analysis_prompt}\n\nData to analyze:\n{data[:1000]}..."
283
}
284
]
285
286
# Report progress
287
await ctx.report_progress(50, 100)
288
289
analysis = await ctx.sample(messages)
290
291
await ctx.report_progress(100, 100)
292
293
return analysis.text
294
295
@mcp.tool
296
async def multi_step_process(
297
input_data: str,
298
ctx: Context
299
) -> Dict[str, str]:
300
"""
301
Perform multi-step processing with progress reporting.
302
303
Parameters:
304
- input_data: Data to process
305
- ctx: Execution context
306
307
Returns:
308
Dictionary with processing results
309
"""
310
results = {}
311
total_steps = 4
312
313
# Step 1: Validation
314
await ctx.info("Step 1: Validating input")
315
await ctx.report_progress(1, total_steps)
316
if not input_data.strip():
317
raise ValueError("Input data cannot be empty")
318
results["validation"] = "passed"
319
320
# Step 2: Processing
321
await ctx.info("Step 2: Processing data")
322
await ctx.report_progress(2, total_steps)
323
processed = input_data.upper().strip()
324
results["processed"] = processed
325
326
# Step 3: Analysis
327
await ctx.info("Step 3: Analyzing results")
328
await ctx.report_progress(3, total_steps)
329
analysis = f"Data contains {len(processed)} characters"
330
results["analysis"] = analysis
331
332
# Step 4: Completion
333
await ctx.info("Step 4: Finalizing results")
334
await ctx.report_progress(4, total_steps)
335
results["status"] = "complete"
336
337
await ctx.info("Multi-step process completed successfully")
338
339
return results
340
```
341
342
### Tool Error Handling
343
344
```python
345
from fastmcp import FastMCP
346
from fastmcp.exceptions import ToolError
347
348
mcp = FastMCP("Robust Server")
349
350
@mcp.tool
351
def safe_divide(a: float, b: float) -> float:
352
"""
353
Safely divide two numbers with proper error handling.
354
355
Parameters:
356
- a: Dividend
357
- b: Divisor
358
359
Returns:
360
Result of division
361
362
Raises:
363
ToolError: If division by zero or invalid input
364
"""
365
try:
366
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
367
raise ToolError("Both arguments must be numbers")
368
369
if b == 0:
370
raise ToolError("Division by zero is not allowed")
371
372
result = a / b
373
374
# Check for infinite or NaN results
375
if not isinstance(result, (int, float)) or result != result: # NaN check
376
raise ToolError("Division resulted in invalid number")
377
378
return result
379
380
except Exception as e:
381
if isinstance(e, ToolError):
382
raise
383
raise ToolError(f"Unexpected error during division: {str(e)}")
384
385
@mcp.tool
386
def validate_and_process(data: Dict[str, any]) -> Dict[str, any]:
387
"""
388
Validate and process input data with comprehensive error handling.
389
390
Parameters:
391
- data: Input data dictionary
392
393
Returns:
394
Processed data dictionary
395
"""
396
if not isinstance(data, dict):
397
raise ToolError("Input must be a dictionary")
398
399
required_fields = ["name", "value"]
400
missing_fields = [field for field in required_fields if field not in data]
401
402
if missing_fields:
403
raise ToolError(f"Missing required fields: {', '.join(missing_fields)}")
404
405
# Validate field types
406
if not isinstance(data["name"], str):
407
raise ToolError("Field 'name' must be a string")
408
409
if not isinstance(data["value"], (int, float)):
410
raise ToolError("Field 'value' must be a number")
411
412
# Process the data
413
return {
414
"processed_name": data["name"].strip().title(),
415
"processed_value": round(data["value"], 2),
416
"timestamp": "2024-01-01T00:00:00Z", # Would use real timestamp
417
"status": "processed"
418
}
419
```
420
421
### Tool Transformations and Forwarding
422
423
```python
424
from fastmcp import FastMCP
425
from fastmcp.tools import forward, forward_raw
426
427
mcp = FastMCP("Transform Server")
428
429
# Original tools
430
@mcp.tool
431
def original_add(a: int, b: int) -> int:
432
"""Add two integers."""
433
return a + b
434
435
@mcp.tool
436
def original_multiply(x: float, y: float) -> float:
437
"""Multiply two floats."""
438
return x * y
439
440
# Transform arguments: convert strings to numbers
441
def string_to_number_transform(args):
442
"""Transform string arguments to numbers."""
443
return {
444
key: float(value) if isinstance(value, str) and value.replace('.', '').isdigit() else value
445
for key, value in args.items()
446
}
447
448
# Transform result: add metadata
449
def add_metadata_transform(result):
450
"""Add metadata to result."""
451
return {
452
"result": result,
453
"type": type(result).__name__,
454
"timestamp": "2024-01-01T00:00:00Z"
455
}
456
457
# Apply transformations
458
mcp.add_tool_transformation(forward(
459
"string_add",
460
target_tool_name="original_add",
461
transform_args=string_to_number_transform,
462
transform_result=add_metadata_transform
463
))
464
465
# Forward to external function
466
def external_power(base: float, exponent: float) -> float:
467
"""Calculate base raised to exponent."""
468
return base ** exponent
469
470
mcp.add_tool_transformation(forward_raw(
471
"power",
472
target_function=external_power,
473
transform_result=lambda result: {"power_result": result}
474
))
475
```
476
477
## Return Types
478
479
Tools can return various types of data:
480
481
```python
482
# Simple types
483
@mcp.tool
484
def get_string() -> str:
485
return "Hello, world!"
486
487
@mcp.tool
488
def get_number() -> float:
489
return 42.5
490
491
@mcp.tool
492
def get_boolean() -> bool:
493
return True
494
495
# Complex types
496
@mcp.tool
497
def get_dict() -> Dict[str, any]:
498
return {"key": "value", "count": 10}
499
500
@mcp.tool
501
def get_list() -> List[str]:
502
return ["item1", "item2", "item3"]
503
504
# Media types
505
@mcp.tool
506
def get_image() -> Image:
507
# Return image data
508
with open("chart.png", "rb") as f:
509
return Image(f.read(), mime_type="image/png")
510
511
@mcp.tool
512
def get_file() -> File:
513
# Return file data
514
return File(
515
data="file content",
516
name="output.txt",
517
mime_type="text/plain"
518
)
519
```