Spaces:
Running
Running
Jeremiah Lowin commited on
Commit ·
21b0f29
1
Parent(s): b581701
remove empty parens from tool
Browse files- README.md +4 -4
- docs/clients/transports.mdx +1 -1
- docs/deployment/asgi.mdx +1 -1
- docs/deployment/running-server.mdx +2 -2
- docs/getting-started/quickstart.mdx +3 -3
- docs/getting-started/welcome.mdx +1 -1
- docs/integrations/anthropic.mdx +2 -2
- docs/integrations/claude-desktop.mdx +1 -1
- docs/integrations/gemini.mdx +1 -1
- docs/integrations/openai.mdx +2 -2
- docs/patterns/cli.mdx +1 -1
- docs/patterns/decorating-methods.mdx +8 -8
- docs/patterns/http-requests.mdx +2 -2
- docs/patterns/testing.mdx +1 -1
- docs/servers/auth/bearer.mdx +1 -1
- docs/servers/composition.mdx +3 -3
- docs/servers/context.mdx +10 -10
- docs/servers/fastmcp.mdx +4 -4
- docs/servers/proxy.mdx +1 -1
- docs/servers/tools.mdx +26 -26
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
|
| 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
|
| 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
|
| 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
|
| 115 |
-
- If `@mcp.tool
|
| 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
|