01 - Tools
Learn how to create tools with different parameter types and how arcade_mcp_server discovers them automatically.
Running the Example
- Run (HTTP default):
uv run 01_tools.py
- Run (stdio for Claude Desktop):
uv run 01_tools.py stdio
Source Code
#!/usr/bin/env python3
"""
01_tools.py - Tool creation and parameter types
This example demonstrates:
1. How to create tools with @app.tool decorator in MCPApp
2. Different parameter types (simple, lists, TypedDict)
3. Direct Python execution for better control
To run:
uv run 01_tools.py # HTTP transport (default)
uv run 01_tools.py stdio # stdio transport for Claude Desktop
"""
import sys
from typing import Annotated
from arcade_mcp_server import MCPApp
from typing_extensions import TypedDict
# Create the MCP application
app = MCPApp(
name="tools_example",
version="1.0.0",
instructions="Example server demonstrating various tool parameter types",
)
# === SIMPLE TOOLS ===
@app.tool
def hello(name: Annotated[str, "Name to greet"]) -> Annotated[str, "Greeting message"]:
"""Say hello to someone."""
return f"Hello, {name}!"
@app.tool
def add(
a: Annotated[float, "First number"], b: Annotated[float, "Second number"]
) -> Annotated[float, "Sum of the numbers"]:
"""Add two numbers together."""
return a + b
# === TOOLS WITH LIST PARAMETERS ===
@app.tool
def calculate_average(
numbers: Annotated[list[float], "List of numbers to average"],
) -> Annotated[float, "Average of all numbers"]:
"""Calculate the average of a list of numbers."""
if not numbers:
return 0.0
return sum(numbers) / len(numbers)
@app.tool
def factorial(n: Annotated[int, "Non-negative integer"]) -> Annotated[int, "Factorial of n"]:
"""Calculate the factorial of a number."""
if n < 0:
raise ValueError("Factorial not defined for negative numbers")
if n == 0:
return 1
result = 1
for i in range(1, n + 1):
result *= i
return result
# === TOOLS WITH COMPLEX TYPES (TypedDict) ===
class PersonInfo(TypedDict):
name: str
age: int
email: str
is_active: bool
@app.tool
def create_user_profile(
person: Annotated[PersonInfo, "Person's information"],
) -> Annotated[str, "Formatted user profile"]:
"""Create a formatted user profile from person information."""
status = "Active" if person["is_active"] else "Inactive"
return f"""
User Profile:
- Name: {person["name"]}
- Age: {person["age"]}
- Email: {person["email"]}
- Status: {status}
""".strip()
class CalculationResult(TypedDict):
sum: float
average: float
min: float
max: float
count: int
@app.tool
def analyze_numbers(
values: Annotated[list[float], "List of numbers to analyze"],
) -> Annotated[CalculationResult, "Statistical analysis of the numbers"]:
"""Analyze a list of numbers and return statistics."""
if not values:
return {"sum": 0.0, "average": 0.0, "min": 0.0, "max": 0.0, "count": 0}
return {
"sum": sum(values),
"average": sum(values) / len(values),
"min": min(values),
"max": max(values),
"count": len(values),
}
if __name__ == "__main__":
# Check if stdio transport was requested
transport = "stdio" if len(sys.argv) > 1 and sys.argv[1] == "stdio" else "http"
print(f"Starting {app.name} v{app.version}")
print(f"Transport: {transport}")
# Run the server
app.run(transport=transport, host="127.0.0.1", port=8000)
Creating Tools
1. Simple Tools
Basic tools with simple parameter types:
@app.tool
def hello(name: Annotated[str, "Name to greet"]) -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
@app.tool
def add(
a: Annotated[float, "First number"],
b: Annotated[float, "Second number"]
) -> Annotated[float, "Sum of the numbers"]:
"""Add two numbers together."""
return a + b
2. List Parameters
Working with lists of values:
@app.tool
def calculate_average(
numbers: Annotated[list[float], "List of numbers to average"]
) -> Annotated[float, "Average of all numbers"]:
"""Calculate the average of a list of numbers."""
if not numbers:
return 0.0
return sum(numbers) / len(numbers)
3. Complex Types with TypedDict
Using TypedDict for structured input and output:
class PersonInfo(TypedDict):
name: str
age: int
email: str
is_active: bool
@tool
def create_user_profile(
person: Annotated[PersonInfo, "Person's information"]
) -> Annotated[str, "Formatted user profile"]:
"""Create a formatted user profile from person information."""
# Implementation here
Managing Tools in MCPApp
With the direct Python approach, you have full control over your tools:
1. Defining Tools Directily
Use @app.tool
to define tools directly on your MCPApp instance:
2. Importing Tools from Files
You can import tools from other files and add them explicitly:
from my_tools import calculate, process_data
# Add imported tools to the app
app.add_tool(calculate)
app.add_tool(process_data)
3. Project Organization
Example project structure:
my_project/
├── server.py # Main MCPApp
├── tools/
│ ├── math.py # Tools using @tool decorator
│ └── utils.py # More tools
└── pyproject.toml # Dependencies
This approach gives you explicit control over which tools are loaded and how they're organized.
Best Practices
Parameter Annotations
- Always use
Annotated
: Provide descriptions for all parameters - Clear descriptions: Help the AI understand what each parameter does
- Type hints: Use proper Python type hints for validation
Tool Design
- Single purpose: Each tool should do one thing well
- Error handling: Add validation and helpful error messages
- Return types: Always annotate return types with descriptions
Organization
- Group related tools: Use directories to organize by functionality
- Naming conventions: Use clear, descriptive names
- Documentation: Write clear docstrings for each tool
Key Concepts
- Explicit Control: Use
@app.tool
decorators andapp.add_tool()
for precise tool management - Type Safety: Full type annotation support with runtime validation
- TypedDict Support: Use TypedDict for complex structured data
- Import Flexibility: Import tools from your own files and external packages
- Direct Execution: Run servers directly with
uv run
for better development experience