1. Description
The primary source for a component’s description is its module-level docstring.
# tools/my_tool.py
"""This is the description for 'my_tool'.
It can span multiple lines and will be used by FastMCP.
"""
# ... rest of the tool code
- This docstring is mandatory. If missing, the build will fail.
- The function docstring of the exported function is generally ignored if a module docstring is present. It might be used as a fallback if the module docstring is missing, but relying on this is not recommended.
2. Entry function (export
)
GolfMCP needs to know which function within your Python file is the main entry point for the component. This is primarily done by assigning the function to a module-level variable named export
.
# tools/calculator.py
"""A simple calculator tool."""
async def add_numbers(a: int, b: int) -> int:
return a + b
async def subtract_numbers(a: int, b: int) -> int:
return a - b
# 'add_numbers' is the designated tool function
export = add_numbers
If an export
variable is not found, GolfMCP build will fail.
3. Function signature requirements
The signature of your exported function must adhere to certain rules:
- Tools:
- Parameters must have type hints (e.g.,
param: str
, count: int
).
- The return value must have a type hint (e.g.,
-> str
, -> Dict[str, Any]
).
- Can be
async def
or def
.
- Resources:
- If defined as a function, it follows similar rules to tools.
- Resource functions typically do not take arguments unless they are part of a resource template (where arguments come from URI path parameters).
- The URI for the resource is defined by a module-level variable
resource_uri: str
.
- Example:
resource_uri = "data://config"
- Can also be a simple module-level constant (JSON-serializable).
- Prompts:
- The function should return either a
str
(which becomes a single user message) or a List[Dict]
.
- Parameters should have type hints.
Pydantic for Input/Output Schemas (Tools):
For tools, Pydantic BaseModel
is highly recommended for defining complex input argument structures and output shapes, enabling clear schemas and validation for FastMCP.
-
Input Parameters & Schema:
Tool inputs are always defined by the parameters of your exported tool function. These parameters must have type hints and descriptions.
- Direct Function Parameters: You define inputs directly in the tool function’s signature.
# tools/hello.py
"""Hello World tool example."""
from typing import Annotated
from pydantic import BaseModel, Field
class Output(BaseModel):
message: str
async def hello(
name: Annotated[str, Field(description="The name of the person to greet")] = "World",
greeting: Annotated[str, Field(description="The greeting phrase to use")] = "Hello"
) -> Output:
# 'name' and 'greeting' are direct input parameters.
# Golf's parser creates an inputSchema in the manifest based on these.
# FastMCP uses these type hints for runtime validation.
return Output(message=f"{greeting}, {name}!")
export = hello
-
Output Structure & Schema:
The structure of your tool’s output should be defined by its return type hint. It is strongly recommended to use a Pydantic BaseModel
for this return type to ensure a clear and validated output schema.
# tools/user_profile_tool.py
"""Tool to fetch user profile information."""
from pydantic import BaseModel
from typing import Optional
class Output(BaseModel): # Explicit Pydantic model for the output
user_id: int
username: str
email: Optional[str]
is_active: bool
async def fetch_user_profile(user_id: int) -> Output:
# FastMCP uses the 'Output' return type hint for its
# runtime behavior and schema exposure to clients.
return Output(
user_id=user_id,
username="example_user",
email="user@example.com",
is_active=True
)
export = fetch_user_profile
4. Parameter annotations (recommended)
While simple type hints are sufficient, using Annotated
with Pydantic Field
provides rich descriptions and validation that significantly improves how AI agents understand and use your tools.
Basic parameter annotations
from typing import Annotated
from pydantic import BaseModel, Field
async def hello(
name: Annotated[str, Field(description="The name of the person to greet")] = "World",
greeting: Annotated[str, Field(description="The greeting phrase to use")] = "Hello"
) -> Output:
"""Say hello to the given name."""
return Output(message=f"{greeting}, {name}!")
Parameter validation
Field annotations support various validation constraints:
from typing import Annotated, Optional
from pydantic import BaseModel, Field
class ChargeOutput(BaseModel):
success: bool
charge_id: str
message: str
async def charge_payment(
# Numeric constraints
amount: Annotated[float, Field(
description="Amount to charge in USD",
gt=0, # Must be greater than 0
le=10000 # Maximum charge limit
)],
# Pattern validation
card_token: Annotated[str, Field(
description="Tokenized payment card identifier",
pattern=r"^tok_[a-zA-Z0-9]+$" # Regex validation
)],
# String length constraints
description: Annotated[str, Field(
description="Optional payment description",
max_length=200,
min_length=0
)] = "",
# Optional parameters with constraints
customer_email: Annotated[Optional[str], Field(
description="Customer's email address",
pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$",
default=None
)] = None
) -> ChargeOutput:
"""Process a payment charge with validation."""
# Implementation here
pass
Tool annotations provide behavioral hints about your tools to MCP clients, following the official MCP specification. These hints help clients understand how to interact with your tools safely and efficiently.
Overview
Add an annotations
dictionary at the module level to specify behavioral hints:
"""Delete file tool."""
from typing import Annotated
from pydantic import BaseModel, Field
# Tool annotations - MCP behavioral hints
annotations = {
"readOnlyHint": False, # Tool modifies state
"destructiveHint": True, # Tool may delete/mutate data irreversibly
"idempotentHint": False, # Repeated calls have side effects
"openWorldHint": False # Tool operates on local state only
}
class Output(BaseModel):
success: bool
message: str
async def delete_file(
path: Annotated[str, Field(description="Path to file to delete")]
) -> Output:
"""Delete a file from the filesystem."""
return Output(success=True, message="File deleted")
export = delete_file
Supported annotation types
true
→ tool cannot change state (pure “GET-style” call)
Note: When true
, other hints are ignored by MCP clients.
true
→ tool may delete or irreversibly mutate data
Only meaningful when readOnlyHint
is false
.
true
→ repeated calls with identical arguments are safe and side-effect-free
Helps MCP clients add caching and deduplication.
true
→ behavior depends on external mutable state (e.g., live web, clock)
Signals lower reproducibility to MCP clients.
Common patterns
Read-only tool:
# Tool that only reads data
annotations = {
"readOnlyHint": True # Other hints ignored when this is true
}
Web search tool:
# Tool that searches the internet
annotations = {
"readOnlyHint": True, # Doesn't modify local state
"openWorldHint": True # Depends on external web services
}
Idempotent tool:
# Tool safe to retry
annotations = {
"readOnlyHint": False,
"destructiveHint": False,
"idempotentHint": True, # Safe to call multiple times
"openWorldHint": False
}
File operations tool:
# Tool that modifies files
annotations = {
"readOnlyHint": False,
"destructiveHint": True, # May delete files
"idempotentHint": False, # Multiple calls may have different effects
"openWorldHint": False # Works on local filesystem
}