or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdclient.mdcontext.mdindex.mdprompts.mdresources.mdserver.mdtools.mdtransports.mdutilities.md

tools.mddocs/

0

# Tools System

1

2

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.

3

4

## Capabilities

5

6

### Tool Classes

7

8

Base classes for creating and managing tools with schema generation and execution.

9

10

```python { .api }

11

class Tool:

12

def __init__(

13

self,

14

name: str,

15

description: str,

16

func: Callable,

17

schema: dict | None = None

18

):

19

"""

20

Base tool class.

21

22

Parameters:

23

- name: Tool name for LLM reference

24

- description: Tool description for LLM understanding

25

- func: Python function to execute

26

- schema: Optional custom JSON schema (auto-generated if None)

27

"""

28

29

class FunctionTool(Tool):

30

"""Tool implementation for function-based tools with automatic schema generation."""

31

```

32

33

### Tool Manager

34

35

Manages tool registration, retrieval, and execution within a FastMCP server.

36

37

```python { .api }

38

class ToolManager:

39

def add_tool(self, tool: Tool) -> None:

40

"""

41

Add a tool to the manager.

42

43

Parameters:

44

- tool: Tool instance to add

45

"""

46

47

def remove_tool(self, name: str) -> None:

48

"""

49

Remove a tool by name.

50

51

Parameters:

52

- name: Name of tool to remove

53

"""

54

55

def get_tool(self, name: str) -> Tool | None:

56

"""

57

Get a tool by name.

58

59

Parameters:

60

- name: Name of tool to retrieve

61

62

Returns:

63

Tool instance or None if not found

64

"""

65

66

def list_tools(self) -> list[Tool]:

67

"""

68

List all registered tools.

69

70

Returns:

71

List of all tool instances

72

"""

73

```

74

75

### Tool Transformations

76

77

Functions for transforming and forwarding tool calls to other implementations.

78

79

```python { .api }

80

def forward(

81

tool_name: str,

82

target_tool_name: str | None = None,

83

transform_args: Callable | None = None,

84

transform_result: Callable | None = None

85

) -> Callable:

86

"""

87

Forward tool calls to another tool with optional transformations.

88

89

Parameters:

90

- tool_name: Name of tool to forward

91

- target_tool_name: Target tool name (defaults to same name)

92

- transform_args: Function to transform arguments

93

- transform_result: Function to transform result

94

95

Returns:

96

Tool transformation function

97

"""

98

99

def forward_raw(

100

tool_name: str,

101

target_function: Callable,

102

transform_args: Callable | None = None,

103

transform_result: Callable | None = None

104

) -> Callable:

105

"""

106

Forward tool calls to a raw function with optional transformations.

107

108

Parameters:

109

- tool_name: Name of tool to forward

110

- target_function: Target function to call

111

- transform_args: Function to transform arguments

112

- transform_result: Function to transform result

113

114

Returns:

115

Tool transformation function

116

"""

117

```

118

119

## Usage Examples

120

121

### Basic Tool Creation

122

123

```python

124

from fastmcp import FastMCP

125

126

mcp = FastMCP("Math Server")

127

128

@mcp.tool

129

def add(a: int, b: int) -> int:

130

"""Add two numbers together."""

131

return a + b

132

133

@mcp.tool

134

def divide(a: float, b: float) -> float:

135

"""Divide two numbers with error handling."""

136

if b == 0:

137

raise ValueError("Cannot divide by zero")

138

return a / b

139

140

@mcp.tool

141

def factorial(n: int) -> int:

142

"""Calculate factorial of a number."""

143

if n < 0:

144

raise ValueError("Factorial not defined for negative numbers")

145

if n == 0 or n == 1:

146

return 1

147

result = 1

148

for i in range(2, n + 1):

149

result *= i

150

return result

151

```

152

153

### Advanced Tool with Custom Types

154

155

```python

156

from fastmcp import FastMCP

157

from fastmcp.utilities.types import Image, File

158

from typing import List, Dict, Optional

159

from dataclasses import dataclass

160

161

mcp = FastMCP("Advanced Server")

162

163

@dataclass

164

class ProcessingResult:

165

success: bool

166

message: str

167

data: Dict[str, any]

168

169

@mcp.tool

170

def process_data(

171

items: List[str],

172

options: Optional[Dict[str, str]] = None,

173

batch_size: int = 10

174

) -> ProcessingResult:

175

"""

176

Process a list of data items with configurable options.

177

178

Parameters:

179

- items: List of items to process

180

- options: Optional processing configuration

181

- batch_size: Number of items to process at once

182

183

Returns:

184

ProcessingResult with success status and processed data

185

"""

186

if not items:

187

return ProcessingResult(

188

success=False,

189

message="No items provided",

190

data={}

191

)

192

193

processed = []

194

for i in range(0, len(items), batch_size):

195

batch = items[i:i + batch_size]

196

processed.extend([item.upper() for item in batch])

197

198

return ProcessingResult(

199

success=True,

200

message=f"Processed {len(items)} items",

201

data={"processed_items": processed}

202

)

203

204

@mcp.tool

205

def create_chart(

206

data: Dict[str, List[float]],

207

title: str = "Chart"

208

) -> Image:

209

"""

210

Create a chart from data and return as image.

211

212

Parameters:

213

- data: Dictionary with series names as keys and data points as values

214

- title: Chart title

215

216

Returns:

217

Chart image as PNG

218

"""

219

# This would use matplotlib or similar to create a chart

220

import matplotlib.pyplot as plt

221

import io

222

223

plt.figure(figsize=(10, 6))

224

for series_name, values in data.items():

225

plt.plot(values, label=series_name)

226

227

plt.title(title)

228

plt.legend()

229

plt.grid(True)

230

231

# Save to bytes

232

buffer = io.BytesIO()

233

plt.savefig(buffer, format='png')

234

buffer.seek(0)

235

236

return Image(buffer.read(), mime_type="image/png")

237

```

238

239

### Tool with Context Usage

240

241

```python

242

from fastmcp import FastMCP, Context

243

import httpx

244

245

mcp = FastMCP("API Server")

246

247

@mcp.tool

248

async def fetch_and_analyze(

249

url: str,

250

analysis_prompt: str,

251

ctx: Context

252

) -> str:

253

"""

254

Fetch data from URL and analyze it using LLM.

255

256

Parameters:

257

- url: URL to fetch data from

258

- analysis_prompt: Prompt for LLM analysis

259

- ctx: Execution context for capabilities

260

261

Returns:

262

Analysis result from LLM

263

"""

264

# Log the operation

265

await ctx.info(f"Fetching data from {url}")

266

267

# Fetch data using HTTP request

268

response = await ctx.http_request("GET", url)

269

270

if response.status_code != 200:

271

await ctx.error(f"Failed to fetch data: {response.status_code}")

272

return f"Error: Unable to fetch data from {url}"

273

274

data = response.text

275

await ctx.info(f"Fetched {len(data)} characters of data")

276

277

# Use LLM sampling for analysis

278

messages = [

279

{

280

"role": "user",

281

"content": f"{analysis_prompt}\n\nData to analyze:\n{data[:1000]}..."

282

}

283

]

284

285

# Report progress

286

await ctx.report_progress(50, 100)

287

288

analysis = await ctx.sample(messages)

289

290

await ctx.report_progress(100, 100)

291

292

return analysis.text

293

294

@mcp.tool

295

async def multi_step_process(

296

input_data: str,

297

ctx: Context

298

) -> Dict[str, str]:

299

"""

300

Perform multi-step processing with progress reporting.

301

302

Parameters:

303

- input_data: Data to process

304

- ctx: Execution context

305

306

Returns:

307

Dictionary with processing results

308

"""

309

results = {}

310

total_steps = 4

311

312

# Step 1: Validation

313

await ctx.info("Step 1: Validating input")

314

await ctx.report_progress(1, total_steps)

315

if not input_data.strip():

316

raise ValueError("Input data cannot be empty")

317

results["validation"] = "passed"

318

319

# Step 2: Processing

320

await ctx.info("Step 2: Processing data")

321

await ctx.report_progress(2, total_steps)

322

processed = input_data.upper().strip()

323

results["processed"] = processed

324

325

# Step 3: Analysis

326

await ctx.info("Step 3: Analyzing results")

327

await ctx.report_progress(3, total_steps)

328

analysis = f"Data contains {len(processed)} characters"

329

results["analysis"] = analysis

330

331

# Step 4: Completion

332

await ctx.info("Step 4: Finalizing results")

333

await ctx.report_progress(4, total_steps)

334

results["status"] = "complete"

335

336

await ctx.info("Multi-step process completed successfully")

337

338

return results

339

```

340

341

### Tool Error Handling

342

343

```python

344

from fastmcp import FastMCP

345

from fastmcp.exceptions import ToolError

346

347

mcp = FastMCP("Robust Server")

348

349

@mcp.tool

350

def safe_divide(a: float, b: float) -> float:

351

"""

352

Safely divide two numbers with proper error handling.

353

354

Parameters:

355

- a: Dividend

356

- b: Divisor

357

358

Returns:

359

Result of division

360

361

Raises:

362

ToolError: If division by zero or invalid input

363

"""

364

try:

365

if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):

366

raise ToolError("Both arguments must be numbers")

367

368

if b == 0:

369

raise ToolError("Division by zero is not allowed")

370

371

result = a / b

372

373

# Check for infinite or NaN results

374

if not isinstance(result, (int, float)) or result != result: # NaN check

375

raise ToolError("Division resulted in invalid number")

376

377

return result

378

379

except Exception as e:

380

if isinstance(e, ToolError):

381

raise

382

raise ToolError(f"Unexpected error during division: {str(e)}")

383

384

@mcp.tool

385

def validate_and_process(data: Dict[str, any]) -> Dict[str, any]:

386

"""

387

Validate and process input data with comprehensive error handling.

388

389

Parameters:

390

- data: Input data dictionary

391

392

Returns:

393

Processed data dictionary

394

"""

395

if not isinstance(data, dict):

396

raise ToolError("Input must be a dictionary")

397

398

required_fields = ["name", "value"]

399

missing_fields = [field for field in required_fields if field not in data]

400

401

if missing_fields:

402

raise ToolError(f"Missing required fields: {', '.join(missing_fields)}")

403

404

# Validate field types

405

if not isinstance(data["name"], str):

406

raise ToolError("Field 'name' must be a string")

407

408

if not isinstance(data["value"], (int, float)):

409

raise ToolError("Field 'value' must be a number")

410

411

# Process the data

412

return {

413

"processed_name": data["name"].strip().title(),

414

"processed_value": round(data["value"], 2),

415

"timestamp": "2024-01-01T00:00:00Z", # Would use real timestamp

416

"status": "processed"

417

}

418

```

419

420

### Tool Transformations and Forwarding

421

422

```python

423

from fastmcp import FastMCP

424

from fastmcp.tools import forward, forward_raw

425

426

mcp = FastMCP("Transform Server")

427

428

# Original tools

429

@mcp.tool

430

def original_add(a: int, b: int) -> int:

431

"""Add two integers."""

432

return a + b

433

434

@mcp.tool

435

def original_multiply(x: float, y: float) -> float:

436

"""Multiply two floats."""

437

return x * y

438

439

# Transform arguments: convert strings to numbers

440

def string_to_number_transform(args):

441

"""Transform string arguments to numbers."""

442

return {

443

key: float(value) if isinstance(value, str) and value.replace('.', '').isdigit() else value

444

for key, value in args.items()

445

}

446

447

# Transform result: add metadata

448

def add_metadata_transform(result):

449

"""Add metadata to result."""

450

return {

451

"result": result,

452

"type": type(result).__name__,

453

"timestamp": "2024-01-01T00:00:00Z"

454

}

455

456

# Apply transformations

457

mcp.add_tool_transformation(forward(

458

"string_add",

459

target_tool_name="original_add",

460

transform_args=string_to_number_transform,

461

transform_result=add_metadata_transform

462

))

463

464

# Forward to external function

465

def external_power(base: float, exponent: float) -> float:

466

"""Calculate base raised to exponent."""

467

return base ** exponent

468

469

mcp.add_tool_transformation(forward_raw(

470

"power",

471

target_function=external_power,

472

transform_result=lambda result: {"power_result": result}

473

))

474

```

475

476

## Return Types

477

478

Tools can return various types of data:

479

480

```python

481

# Simple types

482

@mcp.tool

483

def get_string() -> str:

484

return "Hello, world!"

485

486

@mcp.tool

487

def get_number() -> float:

488

return 42.5

489

490

@mcp.tool

491

def get_boolean() -> bool:

492

return True

493

494

# Complex types

495

@mcp.tool

496

def get_dict() -> Dict[str, any]:

497

return {"key": "value", "count": 10}

498

499

@mcp.tool

500

def get_list() -> List[str]:

501

return ["item1", "item2", "item3"]

502

503

# Media types

504

@mcp.tool

505

def get_image() -> Image:

506

# Return image data

507

with open("chart.png", "rb") as f:

508

return Image(f.read(), mime_type="image/png")

509

510

@mcp.tool

511

def get_file() -> File:

512

# Return file data

513

return File(

514

data="file content",

515

name="output.txt",

516

mime_type="text/plain"

517

)

518

```