Jeremiah Lowin commited on
Commit
21b0f29
·
1 Parent(s): b581701

remove empty parens from tool

Browse files
README.md CHANGED
@@ -31,7 +31,7 @@ from fastmcp import FastMCP
31
 
32
  mcp = FastMCP("Demo 🚀")
33
 
34
- @mcp.tool()
35
  def add(a: int, b: int) -> int:
36
  """Add two numbers"""
37
  return a + b
@@ -144,7 +144,7 @@ Learn more in the [**FastMCP Server Documentation**](https://gofastmcp.com/serve
144
  Tools allow LLMs to perform actions by executing your Python functions (sync or async). Ideal for computations, API calls, or side effects (like `POST`/`PUT`). FastMCP handles schema generation from type hints and docstrings. Tools can return various types, including text, JSON-serializable objects, and even images using the [`fastmcp.Image`](https://gofastmcp.com/servers/tools#return-values) helper.
145
 
146
  ```python
147
- @mcp.tool()
148
  def multiply(a: float, b: float) -> float:
149
  """Multiplies two numbers."""
150
  return a * b
@@ -201,7 +201,7 @@ from fastmcp import FastMCP, Context
201
 
202
  mcp = FastMCP("My MCP Server")
203
 
204
- @mcp.tool()
205
  async def process_data(uri: str, ctx: Context):
206
  # Log a message to the client
207
  await ctx.info(f"Processing {uri}...")
@@ -321,7 +321,7 @@ from fastmcp import FastMCP
321
 
322
  mcp = FastMCP("Demo 🚀")
323
 
324
- @mcp.tool()
325
  def hello(name: str) -> str:
326
  return f"Hello, {name}!"
327
 
 
31
 
32
  mcp = FastMCP("Demo 🚀")
33
 
34
+ @mcp.tool
35
  def add(a: int, b: int) -> int:
36
  """Add two numbers"""
37
  return a + b
 
144
  Tools allow LLMs to perform actions by executing your Python functions (sync or async). Ideal for computations, API calls, or side effects (like `POST`/`PUT`). FastMCP handles schema generation from type hints and docstrings. Tools can return various types, including text, JSON-serializable objects, and even images using the [`fastmcp.Image`](https://gofastmcp.com/servers/tools#return-values) helper.
145
 
146
  ```python
147
+ @mcp.tool
148
  def multiply(a: float, b: float) -> float:
149
  """Multiplies two numbers."""
150
  return a * b
 
201
 
202
  mcp = FastMCP("My MCP Server")
203
 
204
+ @mcp.tool
205
  async def process_data(uri: str, ctx: Context):
206
  # Log a message to the client
207
  await ctx.info(f"Processing {uri}...")
 
321
 
322
  mcp = FastMCP("Demo 🚀")
323
 
324
+ @mcp.tool
325
  def hello(name: str) -> str:
326
  return f"Hello, {name}!"
327
 
docs/clients/transports.mdx CHANGED
@@ -359,7 +359,7 @@ import asyncio
359
  # 1. Create your FastMCP server instance
360
  server = FastMCP(name="InMemoryServer")
361
 
362
- @server.tool()
363
  def ping():
364
  return "pong"
365
 
 
359
  # 1. Create your FastMCP server instance
360
  server = FastMCP(name="InMemoryServer")
361
 
362
+ @server.tool
363
  def ping():
364
  return "pong"
365
 
docs/deployment/asgi.mdx CHANGED
@@ -32,7 +32,7 @@ from fastmcp import FastMCP
32
 
33
  mcp = FastMCP("MyServer")
34
 
35
- @mcp.tool()
36
  def hello(name: str) -> str:
37
  return f"Hello, {name}!"
38
 
 
32
 
33
  mcp = FastMCP("MyServer")
34
 
35
+ @mcp.tool
36
  def hello(name: str) -> str:
37
  return f"Hello, {name}!"
38
 
docs/deployment/running-server.mdx CHANGED
@@ -22,7 +22,7 @@ from fastmcp import FastMCP
22
 
23
  mcp = FastMCP(name="MyServer")
24
 
25
- @mcp.tool()
26
  def hello(name: str) -> str:
27
  return f"Hello, {name}!"
28
 
@@ -244,7 +244,7 @@ import asyncio
244
 
245
  mcp = FastMCP(name="MyServer")
246
 
247
- @mcp.tool()
248
  def hello(name: str) -> str:
249
  return f"Hello, {name}!"
250
 
 
22
 
23
  mcp = FastMCP(name="MyServer")
24
 
25
+ @mcp.tool
26
  def hello(name: str) -> str:
27
  return f"Hello, {name}!"
28
 
 
244
 
245
  mcp = FastMCP(name="MyServer")
246
 
247
+ @mcp.tool
248
  def hello(name: str) -> str:
249
  return f"Hello, {name}!"
250
 
docs/getting-started/quickstart.mdx CHANGED
@@ -32,7 +32,7 @@ from fastmcp import FastMCP
32
 
33
  mcp = FastMCP("My MCP Server")
34
 
35
- @mcp.tool()
36
  def greet(name: str) -> str:
37
  return f"Hello, {name}!"
38
  ```
@@ -49,7 +49,7 @@ from fastmcp import FastMCP, Client
49
 
50
  mcp = FastMCP("My MCP Server")
51
 
52
- @mcp.tool()
53
  def greet(name: str) -> str:
54
  return f"Hello, {name}!"
55
 
@@ -76,7 +76,7 @@ from fastmcp import FastMCP
76
 
77
  mcp = FastMCP("My MCP Server")
78
 
79
- @mcp.tool()
80
  def greet(name: str) -> str:
81
  return f"Hello, {name}!"
82
 
 
32
 
33
  mcp = FastMCP("My MCP Server")
34
 
35
+ @mcp.tool
36
  def greet(name: str) -> str:
37
  return f"Hello, {name}!"
38
  ```
 
49
 
50
  mcp = FastMCP("My MCP Server")
51
 
52
+ @mcp.tool
53
  def greet(name: str) -> str:
54
  return f"Hello, {name}!"
55
 
 
76
 
77
  mcp = FastMCP("My MCP Server")
78
 
79
+ @mcp.tool
80
  def greet(name: str) -> str:
81
  return f"Hello, {name}!"
82
 
docs/getting-started/welcome.mdx CHANGED
@@ -14,7 +14,7 @@ from fastmcp import FastMCP
14
 
15
  mcp = FastMCP("Demo 🚀")
16
 
17
- @mcp.tool()
18
  def add(a: int, b: int) -> int:
19
  """Add two numbers"""
20
  return a + b
 
14
 
15
  mcp = FastMCP("Demo 🚀")
16
 
17
+ @mcp.tool
18
  def add(a: int, b: int) -> int:
19
  """Add two numbers"""
20
  return a + b
docs/integrations/anthropic.mdx CHANGED
@@ -28,7 +28,7 @@ from fastmcp import FastMCP
28
 
29
  mcp = FastMCP(name="Dice Roller")
30
 
31
- @mcp.tool()
32
  def roll_dice(n_dice: int) -> list[int]:
33
  """Roll `n_dice` 6-sided dice and return the results."""
34
  return [random.randint(1, 6) for _ in range(n_dice)]
@@ -171,7 +171,7 @@ auth = BearerAuthProvider(
171
 
172
  mcp = FastMCP(name="Dice Roller", auth=auth)
173
 
174
- @mcp.tool()
175
  def roll_dice(n_dice: int) -> list[int]:
176
  """Roll `n_dice` 6-sided dice and return the results."""
177
  return [random.randint(1, 6) for _ in range(n_dice)]
 
28
 
29
  mcp = FastMCP(name="Dice Roller")
30
 
31
+ @mcp.tool
32
  def roll_dice(n_dice: int) -> list[int]:
33
  """Roll `n_dice` 6-sided dice and return the results."""
34
  return [random.randint(1, 6) for _ in range(n_dice)]
 
171
 
172
  mcp = FastMCP(name="Dice Roller", auth=auth)
173
 
174
+ @mcp.tool
175
  def roll_dice(n_dice: int) -> list[int]:
176
  """Roll `n_dice` 6-sided dice and return the results."""
177
  return [random.randint(1, 6) for _ in range(n_dice)]
docs/integrations/claude-desktop.mdx CHANGED
@@ -31,7 +31,7 @@ from fastmcp import FastMCP
31
 
32
  mcp = FastMCP(name="Dice Roller")
33
 
34
- @mcp.tool()
35
  def roll_dice(n_dice: int) -> list[int]:
36
  """Roll `n_dice` 6-sided dice and return the results."""
37
  return [random.randint(1, 6) for _ in range(n_dice)]
 
31
 
32
  mcp = FastMCP(name="Dice Roller")
33
 
34
+ @mcp.tool
35
  def roll_dice(n_dice: int) -> list[int]:
36
  """Roll `n_dice` 6-sided dice and return the results."""
37
  return [random.randint(1, 6) for _ in range(n_dice)]
docs/integrations/gemini.mdx CHANGED
@@ -32,7 +32,7 @@ from fastmcp import FastMCP
32
 
33
  mcp = FastMCP(name="Dice Roller")
34
 
35
- @mcp.tool()
36
  def roll_dice(n_dice: int) -> list[int]:
37
  """Roll `n_dice` 6-sided dice and return the results."""
38
  return [random.randint(1, 6) for _ in range(n_dice)]
 
32
 
33
  mcp = FastMCP(name="Dice Roller")
34
 
35
+ @mcp.tool
36
  def roll_dice(n_dice: int) -> list[int]:
37
  """Roll `n_dice` 6-sided dice and return the results."""
38
  return [random.randint(1, 6) for _ in range(n_dice)]
docs/integrations/openai.mdx CHANGED
@@ -33,7 +33,7 @@ from fastmcp import FastMCP
33
 
34
  mcp = FastMCP(name="Dice Roller")
35
 
36
- @mcp.tool()
37
  def roll_dice(n_dice: int) -> list[int]:
38
  """Roll `n_dice` 6-sided dice and return the results."""
39
  return [random.randint(1, 6) for _ in range(n_dice)]
@@ -166,7 +166,7 @@ auth = BearerAuthProvider(
166
 
167
  mcp = FastMCP(name="Dice Roller", auth=auth)
168
 
169
- @mcp.tool()
170
  def roll_dice(n_dice: int) -> list[int]:
171
  """Roll `n_dice` 6-sided dice and return the results."""
172
  return [random.randint(1, 6) for _ in range(n_dice)]
 
33
 
34
  mcp = FastMCP(name="Dice Roller")
35
 
36
+ @mcp.tool
37
  def roll_dice(n_dice: int) -> list[int]:
38
  """Roll `n_dice` 6-sided dice and return the results."""
39
  return [random.randint(1, 6) for _ in range(n_dice)]
 
166
 
167
  mcp = FastMCP(name="Dice Roller", auth=auth)
168
 
169
+ @mcp.tool
170
  def roll_dice(n_dice: int) -> list[int]:
171
  """Roll `n_dice` 6-sided dice and return the results."""
172
  return [random.randint(1, 6) for _ in range(n_dice)]
docs/patterns/cli.mdx CHANGED
@@ -66,7 +66,7 @@ from fastmcp import FastMCP
66
 
67
  mcp = FastMCP("MyServer")
68
 
69
- @mcp.tool()
70
  def hello(name: str) -> str:
71
  return f"Hello, {name}!"
72
 
 
66
 
67
  mcp = FastMCP("MyServer")
68
 
69
+ @mcp.tool
70
  def hello(name: str) -> str:
71
  return f"Hello, {name}!"
72
 
docs/patterns/decorating-methods.mdx CHANGED
@@ -9,7 +9,7 @@ FastMCP's decorator system is designed to work with functions, but you may see u
9
 
10
  ## Why Are Methods Hard?
11
 
12
- When you apply a FastMCP decorator like `@tool`, `@resource()`, or `@prompt()` to a method, the decorator captures the function at decoration time. For instance methods and class methods, this poses a challenge because:
13
 
14
  1. For instance methods: The decorator gets the unbound method before any instance exists
15
  2. For class methods: The decorator gets the function before it's bound to the class
@@ -29,7 +29,7 @@ from fastmcp import FastMCP
29
  mcp = FastMCP()
30
 
31
  class MyClass:
32
- @mcp.tool()
33
  def my_method(self, x: int) -> int:
34
  return x * 2
35
 
@@ -53,7 +53,7 @@ from fastmcp import FastMCP
53
  mcp = FastMCP()
54
 
55
  class MyClass:
56
- @mcp.tool() # This won't work correctly
57
  def add(self, x, y):
58
  return x + y
59
  ```
@@ -100,19 +100,19 @@ mcp = FastMCP()
100
 
101
  class MyClass:
102
  @classmethod
103
- @mcp.tool() # This won't work but won't raise an error
104
  def from_string_v1(cls, s):
105
  return cls(s)
106
 
107
- @mcp.tool()
108
  @classmethod # This will raise a helpful ValueError
109
  def from_string_v2(cls, s):
110
  return cls(s)
111
  ```
112
  </Warning>
113
 
114
- - If `@classmethod` comes first, then `@mcp.tool()`: No error is raised, but it won't work correctly
115
- - If `@mcp.tool()` comes first, then `@classmethod`: FastMCP will detect this and raise a helpful `ValueError` with guidance
116
 
117
  <Check>
118
  **Do this instead**:
@@ -150,7 +150,7 @@ from fastmcp import FastMCP
150
  mcp = FastMCP()
151
 
152
  class MyClass:
153
- @mcp.tool()
154
  @staticmethod
155
  def utility(x, y):
156
  return x + y
 
9
 
10
  ## Why Are Methods Hard?
11
 
12
+ When you apply a FastMCP decorator like `@tool`, `@resource`, or `@prompt` to a method, the decorator captures the function at decoration time. For instance methods and class methods, this poses a challenge because:
13
 
14
  1. For instance methods: The decorator gets the unbound method before any instance exists
15
  2. For class methods: The decorator gets the function before it's bound to the class
 
29
  mcp = FastMCP()
30
 
31
  class MyClass:
32
+ @mcp.tool
33
  def my_method(self, x: int) -> int:
34
  return x * 2
35
 
 
53
  mcp = FastMCP()
54
 
55
  class MyClass:
56
+ @mcp.tool # This won't work correctly
57
  def add(self, x, y):
58
  return x + y
59
  ```
 
100
 
101
  class MyClass:
102
  @classmethod
103
+ @mcp.tool # This won't work but won't raise an error
104
  def from_string_v1(cls, s):
105
  return cls(s)
106
 
107
+ @mcp.tool
108
  @classmethod # This will raise a helpful ValueError
109
  def from_string_v2(cls, s):
110
  return cls(s)
111
  ```
112
  </Warning>
113
 
114
+ - If `@classmethod` comes first, then `@mcp.tool`: No error is raised, but it won't work correctly
115
+ - If `@mcp.tool` comes first, then `@classmethod`: FastMCP will detect this and raise a helpful `ValueError` with guidance
116
 
117
  <Check>
118
  **Do this instead**:
 
150
  mcp = FastMCP()
151
 
152
  class MyClass:
153
+ @mcp.tool
154
  @staticmethod
155
  def utility(x, y):
156
  return x + y
docs/patterns/http-requests.mdx CHANGED
@@ -25,7 +25,7 @@ from starlette.requests import Request
25
 
26
  mcp = FastMCP(name="HTTP Request Demo")
27
 
28
- @mcp.tool()
29
  async def user_agent_info() -> dict:
30
  """Return information about the user agent."""
31
  # Get the HTTP request
@@ -58,7 +58,7 @@ from fastmcp.server.dependencies import get_http_headers
58
 
59
  mcp = FastMCP(name="Headers Demo")
60
 
61
- @mcp.tool()
62
  async def safe_header_info() -> dict:
63
  """Safely get header information without raising errors."""
64
  # Get headers (returns empty dict if no request context)
 
25
 
26
  mcp = FastMCP(name="HTTP Request Demo")
27
 
28
+ @mcp.tool
29
  async def user_agent_info() -> dict:
30
  """Return information about the user agent."""
31
  # Get the HTTP request
 
58
 
59
  mcp = FastMCP(name="Headers Demo")
60
 
61
+ @mcp.tool
62
  async def safe_header_info() -> dict:
63
  """Safely get header information without raising errors."""
64
  # Get headers (returns empty dict if no request context)
docs/patterns/testing.mdx CHANGED
@@ -22,7 +22,7 @@ from fastmcp import FastMCP, Client
22
  def mcp_server():
23
  server = FastMCP("TestServer")
24
 
25
- @server.tool()
26
  def greet(name: str) -> str:
27
  return f"Hello, {name}!"
28
 
 
22
  def mcp_server():
23
  server = FastMCP("TestServer")
24
 
25
+ @server.tool
26
  def greet(name: str) -> str:
27
  return f"Hello, {name}!"
28
 
docs/servers/auth/bearer.mdx CHANGED
@@ -160,7 +160,7 @@ Once authenticated, your tools, resources, or prompts can access token informati
160
  from fastmcp import FastMCP, Context, ToolError
161
  from fastmcp.server.dependencies import get_access_token, AccessToken
162
 
163
- @mcp.tool()
164
  async def get_my_data(ctx: Context) -> dict:
165
  access_token: AccessToken = get_access_token()
166
 
 
160
  from fastmcp import FastMCP, Context, ToolError
161
  from fastmcp.server.dependencies import get_access_token, AccessToken
162
 
163
+ @mcp.tool
164
  async def get_my_data(ctx: Context) -> dict:
165
  access_token: AccessToken = get_access_token()
166
 
docs/servers/composition.mdx CHANGED
@@ -50,7 +50,7 @@ import asyncio
50
  # Define subservers
51
  weather_mcp = FastMCP(name="WeatherService")
52
 
53
- @weather_mcp.tool()
54
  def get_forecast(city: str) -> dict:
55
  """Get weather forecast."""
56
  return {"city": city, "forecast": "Sunny"}
@@ -102,7 +102,7 @@ from fastmcp import FastMCP, Client
102
  # Define subserver
103
  dynamic_mcp = FastMCP(name="DynamicService")
104
 
105
- @dynamic_mcp.tool()
106
  def initial_tool():
107
  """Initial tool demonstration."""
108
  return "Initial Tool Exists"
@@ -112,7 +112,7 @@ main_mcp = FastMCP(name="MainAppLive")
112
  main_mcp.mount("dynamic", dynamic_mcp)
113
 
114
  # Add a tool AFTER mounting - it will be accessible through main_mcp
115
- @dynamic_mcp.tool()
116
  def added_later():
117
  """Tool added after mounting."""
118
  return "Tool Added Dynamically!"
 
50
  # Define subservers
51
  weather_mcp = FastMCP(name="WeatherService")
52
 
53
+ @weather_mcp.tool
54
  def get_forecast(city: str) -> dict:
55
  """Get weather forecast."""
56
  return {"city": city, "forecast": "Sunny"}
 
102
  # Define subserver
103
  dynamic_mcp = FastMCP(name="DynamicService")
104
 
105
+ @dynamic_mcp.tool
106
  def initial_tool():
107
  """Initial tool demonstration."""
108
  return "Initial Tool Exists"
 
112
  main_mcp.mount("dynamic", dynamic_mcp)
113
 
114
  # Add a tool AFTER mounting - it will be accessible through main_mcp
115
+ @dynamic_mcp.tool
116
  def added_later():
117
  """Tool added after mounting."""
118
  return "Tool Added Dynamically!"
docs/servers/context.mdx CHANGED
@@ -41,7 +41,7 @@ from fastmcp import FastMCP, Context
41
 
42
  mcp = FastMCP(name="ContextDemo")
43
 
44
- @mcp.tool()
45
  async def process_file(file_uri: str, ctx: Context) -> str:
46
  """Processes a file, using context for logging and resource access."""
47
  # Context is available as the ctx parameter
@@ -99,7 +99,7 @@ async def process_data(data: list[float]) -> dict:
99
  ctx = get_context()
100
  await ctx.info(f"Processing {len(data)} data points")
101
 
102
- @mcp.tool()
103
  async def analyze_dataset(dataset_name: str) -> dict:
104
  # Call utility function that uses context internally
105
  data = load_data(dataset_name)
@@ -118,7 +118,7 @@ async def analyze_dataset(dataset_name: str) -> dict:
118
  Send log messages back to the MCP client. This is useful for debugging and providing visibility into function execution during a request.
119
 
120
  ```python
121
- @mcp.tool()
122
  async def analyze_data(data: list[float], ctx: Context) -> dict:
123
  """Analyze numerical data with logging."""
124
  await ctx.debug("Starting analysis of numerical data")
@@ -149,7 +149,7 @@ async def analyze_data(data: list[float], ctx: Context) -> dict:
149
  For long-running operations, notify the client about the progress. This allows clients to display progress indicators and provide a better user experience.
150
 
151
  ```python
152
- @mcp.tool()
153
  async def process_items(items: list[str], ctx: Context) -> dict:
154
  """Process a list of items with progress updates."""
155
  total = len(items)
@@ -182,7 +182,7 @@ Progress reporting requires the client to have sent a `progressToken` in the ini
182
  Read data from resources registered with your FastMCP server. This allows functions to access files, configuration, or dynamically generated content.
183
 
184
  ```python
185
- @mcp.tool()
186
  async def summarize_document(document_uri: str, ctx: Context) -> str:
187
  """Summarize a document by its resource URI."""
188
  # Read the document content
@@ -222,7 +222,7 @@ The returned content is typically accessed via `content_list[0].content` and can
222
  Request the client's LLM to generate text based on provided messages. This is useful when your function needs to leverage the LLM's capabilities to process data or generate responses.
223
 
224
  ```python
225
- @mcp.tool()
226
  async def analyze_sentiment(text: str, ctx: Context) -> dict:
227
  """Analyze the sentiment of a text using the client's LLM."""
228
  # Create a sampling prompt asking for sentiment analysis
@@ -258,7 +258,7 @@ async def analyze_sentiment(text: str, ctx: Context) -> dict:
258
  When providing a simple string, it's treated as a user message. For more complex scenarios, you can provide a list of messages with different roles.
259
 
260
  ```python
261
- @mcp.tool()
262
  async def generate_example(concept: str, ctx: Context) -> str:
263
  """Generate a Python code example for a given concept."""
264
  # Using a system prompt and a user message
@@ -280,7 +280,7 @@ See [Client Sampling](/clients/client#llm-sampling) for more details on how clie
280
  Access metadata about the current request and client.
281
 
282
  ```python
283
- @mcp.tool()
284
  async def request_info(ctx: Context) -> dict:
285
  """Return information about the current request."""
286
  return {
@@ -300,7 +300,7 @@ async def request_info(ctx: Context) -> dict:
300
  #### FastMCP Server and Sessions
301
 
302
  ```python
303
- @mcp.tool()
304
  async def advanced_tool(ctx: Context) -> str:
305
  """Demonstrate advanced context access."""
306
  # Access the FastMCP server instance
@@ -326,7 +326,7 @@ See the [HTTP Requests pattern](/patterns/http-requests) for more details.
326
  For web applications, you can access the underlying HTTP request:
327
 
328
  ```python
329
- @mcp.tool()
330
  async def handle_web_request(ctx: Context) -> dict:
331
  """Access HTTP request information from the Starlette request."""
332
  request = ctx.get_http_request()
 
41
 
42
  mcp = FastMCP(name="ContextDemo")
43
 
44
+ @mcp.tool
45
  async def process_file(file_uri: str, ctx: Context) -> str:
46
  """Processes a file, using context for logging and resource access."""
47
  # Context is available as the ctx parameter
 
99
  ctx = get_context()
100
  await ctx.info(f"Processing {len(data)} data points")
101
 
102
+ @mcp.tool
103
  async def analyze_dataset(dataset_name: str) -> dict:
104
  # Call utility function that uses context internally
105
  data = load_data(dataset_name)
 
118
  Send log messages back to the MCP client. This is useful for debugging and providing visibility into function execution during a request.
119
 
120
  ```python
121
+ @mcp.tool
122
  async def analyze_data(data: list[float], ctx: Context) -> dict:
123
  """Analyze numerical data with logging."""
124
  await ctx.debug("Starting analysis of numerical data")
 
149
  For long-running operations, notify the client about the progress. This allows clients to display progress indicators and provide a better user experience.
150
 
151
  ```python
152
+ @mcp.tool
153
  async def process_items(items: list[str], ctx: Context) -> dict:
154
  """Process a list of items with progress updates."""
155
  total = len(items)
 
182
  Read data from resources registered with your FastMCP server. This allows functions to access files, configuration, or dynamically generated content.
183
 
184
  ```python
185
+ @mcp.tool
186
  async def summarize_document(document_uri: str, ctx: Context) -> str:
187
  """Summarize a document by its resource URI."""
188
  # Read the document content
 
222
  Request the client's LLM to generate text based on provided messages. This is useful when your function needs to leverage the LLM's capabilities to process data or generate responses.
223
 
224
  ```python
225
+ @mcp.tool
226
  async def analyze_sentiment(text: str, ctx: Context) -> dict:
227
  """Analyze the sentiment of a text using the client's LLM."""
228
  # Create a sampling prompt asking for sentiment analysis
 
258
  When providing a simple string, it's treated as a user message. For more complex scenarios, you can provide a list of messages with different roles.
259
 
260
  ```python
261
+ @mcp.tool
262
  async def generate_example(concept: str, ctx: Context) -> str:
263
  """Generate a Python code example for a given concept."""
264
  # Using a system prompt and a user message
 
280
  Access metadata about the current request and client.
281
 
282
  ```python
283
+ @mcp.tool
284
  async def request_info(ctx: Context) -> dict:
285
  """Return information about the current request."""
286
  return {
 
300
  #### FastMCP Server and Sessions
301
 
302
  ```python
303
+ @mcp.tool
304
  async def advanced_tool(ctx: Context) -> str:
305
  """Demonstrate advanced context access."""
306
  # Access the FastMCP server instance
 
326
  For web applications, you can access the underlying HTTP request:
327
 
328
  ```python
329
+ @mcp.tool
330
  async def handle_web_request(ctx: Context) -> dict:
331
  """Access HTTP request information from the Starlette request."""
332
  request = ctx.get_http_request()
docs/servers/fastmcp.mdx CHANGED
@@ -47,7 +47,7 @@ FastMCP servers expose several types of components to the client:
47
  Tools are functions that the client can call to perform actions or access external systems.
48
 
49
  ```python
50
- @mcp.tool()
51
  def multiply(a: float, b: float) -> float:
52
  """Multiplies two numbers together."""
53
  return a * b
@@ -106,7 +106,7 @@ from fastmcp import FastMCP
106
 
107
  mcp = FastMCP(name="MyServer")
108
 
109
- @mcp.tool()
110
  def greet(name: str) -> str:
111
  """Greet a user by name."""
112
  return f"Hello, {name}!"
@@ -145,7 +145,7 @@ import asyncio
145
  main = FastMCP(name="Main")
146
  sub = FastMCP(name="Sub")
147
 
148
- @sub.tool()
149
  def hello():
150
  return "hi"
151
 
@@ -216,7 +216,7 @@ def yaml_serializer(data):
216
  # Create a server with the custom serializer
217
  mcp = FastMCP(name="MyServer", tool_serializer=yaml_serializer)
218
 
219
- @mcp.tool()
220
  def get_config():
221
  """Returns configuration in YAML format."""
222
  return {"api_key": "abc123", "debug": True, "rate_limit": 100}
 
47
  Tools are functions that the client can call to perform actions or access external systems.
48
 
49
  ```python
50
+ @mcp.tool
51
  def multiply(a: float, b: float) -> float:
52
  """Multiplies two numbers together."""
53
  return a * b
 
106
 
107
  mcp = FastMCP(name="MyServer")
108
 
109
+ @mcp.tool
110
  def greet(name: str) -> str:
111
  """Greet a user by name."""
112
  return f"Hello, {name}!"
 
145
  main = FastMCP(name="Main")
146
  sub = FastMCP(name="Sub")
147
 
148
+ @sub.tool
149
  def hello():
150
  return "hi"
151
 
 
216
  # Create a server with the custom serializer
217
  mcp = FastMCP(name="MyServer", tool_serializer=yaml_serializer)
218
 
219
+ @mcp.tool
220
  def get_config():
221
  """Returns configuration in YAML format."""
222
  return {"api_key": "abc123", "debug": True, "rate_limit": 100}
docs/servers/proxy.mdx CHANGED
@@ -90,7 +90,7 @@ from fastmcp import FastMCP
90
  # Original server
91
  original_server = FastMCP(name="Original")
92
 
93
- @original_server.tool()
94
  def tool_a() -> str:
95
  return "A"
96
 
 
90
  # Original server
91
  original_server = FastMCP(name="Original")
92
 
93
+ @original_server.tool
94
  def tool_a() -> str:
95
  return "A"
96
 
docs/servers/tools.mdx CHANGED
@@ -31,7 +31,7 @@ from fastmcp import FastMCP
31
 
32
  mcp = FastMCP(name="CalculatorServer")
33
 
34
- @mcp.tool()
35
  def add(a: int, b: int) -> int:
36
  """Adds two integer numbers together."""
37
  return a + b
@@ -61,7 +61,7 @@ Type annotations for parameters are essential for proper tool functionality. The
61
  Use standard Python type annotations for parameters:
62
 
63
  ```python
64
- @mcp.tool()
65
  def analyze_text(
66
  text: str,
67
  max_tokens: int = 100,
@@ -79,7 +79,7 @@ You can provide additional metadata about parameters using Pydantic's `Field` cl
79
  from typing import Annotated
80
  from pydantic import Field
81
 
82
- @mcp.tool()
83
  def process_image(
84
  image_url: Annotated[str, Field(description="URL of the image to process")],
85
  resize: Annotated[bool, Field(description="Whether to resize the image")] = False,
@@ -97,7 +97,7 @@ def process_image(
97
  You can also use the Field as a default value, though the Annotated approach is preferred:
98
 
99
  ```python
100
- @mcp.tool()
101
  def search_database(
102
  query: str = Field(description="Search query string"),
103
  limit: int = Field(10, description="Maximum number of results", ge=1, le=100)
@@ -137,7 +137,7 @@ For additional type annotations not listed here, see the [Parameter Types](#para
137
  FastMCP follows Python's standard function parameter conventions. Parameters without default values are required, while those with default values are optional.
138
 
139
  ```python
140
- @mcp.tool()
141
  def search_products(
142
  query: str, # Required - no default value
143
  max_results: int = 10, # Optional - has default value
@@ -197,14 +197,14 @@ FastMCP seamlessly supports both standard (`def`) and asynchronous (`async def`)
197
 
198
  ```python
199
  # Synchronous tool (suitable for CPU-bound or quick tasks)
200
- @mcp.tool()
201
  def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
202
  """Calculate the distance between two coordinates."""
203
  # Implementation...
204
  return 42.5
205
 
206
  # Asynchronous tool (ideal for I/O-bound operations)
207
- @mcp.tool()
208
  async def fetch_weather(city: str) -> dict:
209
  """Retrieve current weather conditions for a city."""
210
  # Use 'async def' for operations involving network calls, file I/O, etc.
@@ -244,7 +244,7 @@ except ImportError:
244
 
245
  mcp = FastMCP("Image Demo")
246
 
247
- @mcp.tool()
248
  def generate_image(width: int, height: int, color: str) -> Image:
249
  """Generates a solid color image."""
250
  # Create image using Pillow
@@ -258,7 +258,7 @@ def generate_image(width: int, height: int, color: str) -> Image:
258
  # Return using FastMCP's Image helper
259
  return Image(data=img_bytes, format="png")
260
 
261
- @mcp.tool()
262
  def do_nothing() -> None:
263
  """This tool performs an action but returns no data."""
264
  print("Performing a side effect...")
@@ -285,7 +285,7 @@ mcp = FastMCP(name="SecureServer", mask_error_details=True)
285
  from fastmcp import FastMCP
286
  from fastmcp.exceptions import ToolError
287
 
288
- @mcp.tool()
289
  def divide(a: float, b: float) -> float:
290
  """Divide a by b."""
291
 
@@ -351,7 +351,7 @@ from fastmcp import FastMCP, Context
351
 
352
  mcp = FastMCP(name="ContextDemo")
353
 
354
- @mcp.tool()
355
  async def process_data(data_uri: str, ctx: Context) -> dict:
356
  """Process data from a resource with progress reporting."""
357
  await ctx.info(f"Processing data from {data_uri}")
@@ -396,7 +396,7 @@ FastMCP supports **type coercion** when possible. This means that if a client se
396
  The most common parameter types are Python's built-in scalar types:
397
 
398
  ```python
399
- @mcp.tool()
400
  def process_values(
401
  name: str, # Text data
402
  count: int, # Integer numbers
@@ -416,7 +416,7 @@ FastMCP supports various date and time types from the `datetime` module:
416
  ```python
417
  from datetime import datetime, date, timedelta
418
 
419
- @mcp.tool()
420
  def process_date_time(
421
  event_date: date, # ISO format date string or date object
422
  event_time: datetime, # ISO format datetime string or datetime object
@@ -440,7 +440,7 @@ def process_date_time(
440
  FastMCP supports all standard Python collection types:
441
 
442
  ```python
443
- @mcp.tool()
444
  def analyze_data(
445
  values: list[float], # List of numbers
446
  properties: dict[str, str], # Dictionary with string keys and values
@@ -465,7 +465,7 @@ Collection types can be nested and combined to represent complex data structures
465
  For parameters that can accept multiple types or may be omitted:
466
 
467
  ```python
468
- @mcp.tool()
469
  def flexible_search(
470
  query: str | int, # Can be either string or integer
471
  filters: dict[str, str] | None = None, # Optional dictionary
@@ -488,7 +488,7 @@ Literals constrain parameters to a specific set of values:
488
  ```python
489
  from typing import Literal
490
 
491
- @mcp.tool()
492
  def sort_data(
493
  data: list[float],
494
  order: Literal["ascending", "descending"] = "ascending",
@@ -516,7 +516,7 @@ class Color(Enum):
516
  GREEN = "green"
517
  BLUE = "blue"
518
 
519
- @mcp.tool()
520
  def process_image(
521
  image_path: str,
522
  color_filter: Color = Color.RED
@@ -539,7 +539,7 @@ There are two approaches to handling binary data in tool parameters:
539
  #### Bytes
540
 
541
  ```python
542
- @mcp.tool()
543
  def process_binary(data: bytes):
544
  """Process binary data directly.
545
 
@@ -563,7 +563,7 @@ FastMCP does not automatically decode base64-encoded strings for bytes parameter
563
  from typing import Annotated
564
  from pydantic import Field
565
 
566
- @mcp.tool()
567
  def process_image_data(
568
  image_data: Annotated[str, Field(description="Base64-encoded image data")]
569
  ):
@@ -587,7 +587,7 @@ The `Path` type from the `pathlib` module can be used for file system paths:
587
  ```python
588
  from pathlib import Path
589
 
590
- @mcp.tool()
591
  def process_file(path: Path) -> str:
592
  """Process a file at the given path."""
593
  assert isinstance(path, Path) # Path is properly converted
@@ -603,7 +603,7 @@ The `UUID` type from the `uuid` module can be used for unique identifiers:
603
  ```python
604
  import uuid
605
 
606
- @mcp.tool()
607
  def process_item(
608
  item_id: uuid.UUID # String UUID or UUID object
609
  ) -> str:
@@ -628,7 +628,7 @@ class User(BaseModel):
628
  age: int | None = None
629
  is_active: bool = True
630
 
631
- @mcp.tool()
632
  def create_user(user: User):
633
  """Create a new user in the system."""
634
  # The input is automatically validated against the User model
@@ -657,7 +657,7 @@ Note that fields can be used *outside* Pydantic models to provide metadata and v
657
  from typing import Annotated
658
  from pydantic import Field
659
 
660
- @mcp.tool()
661
  def analyze_metrics(
662
  # Numbers with range constraints
663
  count: Annotated[int, Field(ge=0, le=100)], # 0 <= count <= 100
@@ -682,7 +682,7 @@ def analyze_metrics(
682
  You can also use `Field` as a default value, though the `Annotated` approach is preferred:
683
 
684
  ```python
685
- @mcp.tool()
686
  def validate_data(
687
  # Value constraints
688
  age: int = Field(ge=0, lt=120), # 0 <= age < 120
@@ -727,7 +727,7 @@ mcp = FastMCP(
727
  on_duplicate_tools="error"
728
  )
729
 
730
- @mcp.tool()
731
  def my_tool(): return "Version 1"
732
 
733
  # This will now raise a ValueError because 'my_tool' already exists
@@ -754,7 +754,7 @@ from fastmcp import FastMCP
754
 
755
  mcp = FastMCP(name="DynamicToolServer")
756
 
757
- @mcp.tool()
758
  def calculate_sum(a: int, b: int) -> int:
759
  """Add two numbers together."""
760
  return a + b
 
31
 
32
  mcp = FastMCP(name="CalculatorServer")
33
 
34
+ @mcp.tool
35
  def add(a: int, b: int) -> int:
36
  """Adds two integer numbers together."""
37
  return a + b
 
61
  Use standard Python type annotations for parameters:
62
 
63
  ```python
64
+ @mcp.tool
65
  def analyze_text(
66
  text: str,
67
  max_tokens: int = 100,
 
79
  from typing import Annotated
80
  from pydantic import Field
81
 
82
+ @mcp.tool
83
  def process_image(
84
  image_url: Annotated[str, Field(description="URL of the image to process")],
85
  resize: Annotated[bool, Field(description="Whether to resize the image")] = False,
 
97
  You can also use the Field as a default value, though the Annotated approach is preferred:
98
 
99
  ```python
100
+ @mcp.tool
101
  def search_database(
102
  query: str = Field(description="Search query string"),
103
  limit: int = Field(10, description="Maximum number of results", ge=1, le=100)
 
137
  FastMCP follows Python's standard function parameter conventions. Parameters without default values are required, while those with default values are optional.
138
 
139
  ```python
140
+ @mcp.tool
141
  def search_products(
142
  query: str, # Required - no default value
143
  max_results: int = 10, # Optional - has default value
 
197
 
198
  ```python
199
  # Synchronous tool (suitable for CPU-bound or quick tasks)
200
+ @mcp.tool
201
  def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
202
  """Calculate the distance between two coordinates."""
203
  # Implementation...
204
  return 42.5
205
 
206
  # Asynchronous tool (ideal for I/O-bound operations)
207
+ @mcp.tool
208
  async def fetch_weather(city: str) -> dict:
209
  """Retrieve current weather conditions for a city."""
210
  # Use 'async def' for operations involving network calls, file I/O, etc.
 
244
 
245
  mcp = FastMCP("Image Demo")
246
 
247
+ @mcp.tool
248
  def generate_image(width: int, height: int, color: str) -> Image:
249
  """Generates a solid color image."""
250
  # Create image using Pillow
 
258
  # Return using FastMCP's Image helper
259
  return Image(data=img_bytes, format="png")
260
 
261
+ @mcp.tool
262
  def do_nothing() -> None:
263
  """This tool performs an action but returns no data."""
264
  print("Performing a side effect...")
 
285
  from fastmcp import FastMCP
286
  from fastmcp.exceptions import ToolError
287
 
288
+ @mcp.tool
289
  def divide(a: float, b: float) -> float:
290
  """Divide a by b."""
291
 
 
351
 
352
  mcp = FastMCP(name="ContextDemo")
353
 
354
+ @mcp.tool
355
  async def process_data(data_uri: str, ctx: Context) -> dict:
356
  """Process data from a resource with progress reporting."""
357
  await ctx.info(f"Processing data from {data_uri}")
 
396
  The most common parameter types are Python's built-in scalar types:
397
 
398
  ```python
399
+ @mcp.tool
400
  def process_values(
401
  name: str, # Text data
402
  count: int, # Integer numbers
 
416
  ```python
417
  from datetime import datetime, date, timedelta
418
 
419
+ @mcp.tool
420
  def process_date_time(
421
  event_date: date, # ISO format date string or date object
422
  event_time: datetime, # ISO format datetime string or datetime object
 
440
  FastMCP supports all standard Python collection types:
441
 
442
  ```python
443
+ @mcp.tool
444
  def analyze_data(
445
  values: list[float], # List of numbers
446
  properties: dict[str, str], # Dictionary with string keys and values
 
465
  For parameters that can accept multiple types or may be omitted:
466
 
467
  ```python
468
+ @mcp.tool
469
  def flexible_search(
470
  query: str | int, # Can be either string or integer
471
  filters: dict[str, str] | None = None, # Optional dictionary
 
488
  ```python
489
  from typing import Literal
490
 
491
+ @mcp.tool
492
  def sort_data(
493
  data: list[float],
494
  order: Literal["ascending", "descending"] = "ascending",
 
516
  GREEN = "green"
517
  BLUE = "blue"
518
 
519
+ @mcp.tool
520
  def process_image(
521
  image_path: str,
522
  color_filter: Color = Color.RED
 
539
  #### Bytes
540
 
541
  ```python
542
+ @mcp.tool
543
  def process_binary(data: bytes):
544
  """Process binary data directly.
545
 
 
563
  from typing import Annotated
564
  from pydantic import Field
565
 
566
+ @mcp.tool
567
  def process_image_data(
568
  image_data: Annotated[str, Field(description="Base64-encoded image data")]
569
  ):
 
587
  ```python
588
  from pathlib import Path
589
 
590
+ @mcp.tool
591
  def process_file(path: Path) -> str:
592
  """Process a file at the given path."""
593
  assert isinstance(path, Path) # Path is properly converted
 
603
  ```python
604
  import uuid
605
 
606
+ @mcp.tool
607
  def process_item(
608
  item_id: uuid.UUID # String UUID or UUID object
609
  ) -> str:
 
628
  age: int | None = None
629
  is_active: bool = True
630
 
631
+ @mcp.tool
632
  def create_user(user: User):
633
  """Create a new user in the system."""
634
  # The input is automatically validated against the User model
 
657
  from typing import Annotated
658
  from pydantic import Field
659
 
660
+ @mcp.tool
661
  def analyze_metrics(
662
  # Numbers with range constraints
663
  count: Annotated[int, Field(ge=0, le=100)], # 0 <= count <= 100
 
682
  You can also use `Field` as a default value, though the `Annotated` approach is preferred:
683
 
684
  ```python
685
+ @mcp.tool
686
  def validate_data(
687
  # Value constraints
688
  age: int = Field(ge=0, lt=120), # 0 <= age < 120
 
727
  on_duplicate_tools="error"
728
  )
729
 
730
+ @mcp.tool
731
  def my_tool(): return "Version 1"
732
 
733
  # This will now raise a ValueError because 'my_tool' already exists
 
754
 
755
  mcp = FastMCP(name="DynamicToolServer")
756
 
757
+ @mcp.tool
758
  def calculate_sum(a: int, b: int) -> int:
759
  """Add two numbers together."""
760
  return a + b