fastmcp 2.2.2__tar.gz → 2.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. {fastmcp-2.2.2 → fastmcp-2.2.3}/PKG-INFO +19 -19
  2. {fastmcp-2.2.2 → fastmcp-2.2.3}/README.md +18 -18
  3. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/clients/transports.mdx +4 -0
  4. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/patterns/contrib.mdx +3 -0
  5. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/patterns/proxy.mdx +11 -9
  6. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/servers/fastmcp.mdx +6 -0
  7. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/servers/prompts.mdx +4 -0
  8. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/servers/resources.mdx +65 -3
  9. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/servers/tools.mdx +10 -3
  10. fastmcp-2.2.3/docs/snippets/version-badge.mdx +13 -0
  11. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/style.css +15 -18
  12. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/resources/template.py +5 -2
  13. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/tools/tool.py +6 -1
  14. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/resources/test_resource_template.py +69 -2
  15. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/tools/test_tool_manager.py +14 -20
  16. fastmcp-2.2.2/docs/snippets/version-badge.mdx +0 -8
  17. {fastmcp-2.2.2 → fastmcp-2.2.3}/.cursor/rules/core-mcp-objects.mdc +0 -0
  18. {fastmcp-2.2.2 → fastmcp-2.2.3}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  19. {fastmcp-2.2.2 → fastmcp-2.2.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  20. {fastmcp-2.2.2 → fastmcp-2.2.3}/.github/ISSUE_TEMPLATE/enhancement.yml +0 -0
  21. {fastmcp-2.2.2 → fastmcp-2.2.3}/.github/release.yml +0 -0
  22. {fastmcp-2.2.2 → fastmcp-2.2.3}/.github/workflows/publish.yml +0 -0
  23. {fastmcp-2.2.2 → fastmcp-2.2.3}/.github/workflows/run-static.yml +0 -0
  24. {fastmcp-2.2.2 → fastmcp-2.2.3}/.github/workflows/run-tests.yml +0 -0
  25. {fastmcp-2.2.2 → fastmcp-2.2.3}/.gitignore +0 -0
  26. {fastmcp-2.2.2 → fastmcp-2.2.3}/.pre-commit-config.yaml +0 -0
  27. {fastmcp-2.2.2 → fastmcp-2.2.3}/LICENSE +0 -0
  28. {fastmcp-2.2.2 → fastmcp-2.2.3}/Windows_Notes.md +0 -0
  29. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/assets/demo-inspector.png +0 -0
  30. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/clients/client.mdx +0 -0
  31. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/docs.json +0 -0
  32. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/getting-started/installation.mdx +0 -0
  33. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/getting-started/quickstart.mdx +0 -0
  34. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/getting-started/welcome.mdx +0 -0
  35. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/patterns/composition.mdx +0 -0
  36. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/patterns/decorating-methods.mdx +0 -0
  37. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/patterns/fastapi.mdx +0 -0
  38. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/patterns/openapi.mdx +0 -0
  39. {fastmcp-2.2.2 → fastmcp-2.2.3}/docs/servers/context.mdx +0 -0
  40. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/complex_inputs.py +0 -0
  41. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/desktop.py +0 -0
  42. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/echo.py +0 -0
  43. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/memory.py +0 -0
  44. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/mount_example.py +0 -0
  45. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/readme-quickstart.py +0 -0
  46. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/sampling.py +0 -0
  47. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/screenshot.py +0 -0
  48. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/simple_echo.py +0 -0
  49. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/README.md +0 -0
  50. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/pyproject.toml +0 -0
  51. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/__init__.py +0 -0
  52. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/__main__.py +0 -0
  53. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/hub.py +0 -0
  54. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/lights/__init__.py +0 -0
  55. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/lights/hue_utils.py +0 -0
  56. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/lights/server.py +0 -0
  57. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/py.typed +0 -0
  58. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/src/smart_home/settings.py +0 -0
  59. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/smart_home/uv.lock +0 -0
  60. {fastmcp-2.2.2 → fastmcp-2.2.3}/examples/text_me.py +0 -0
  61. {fastmcp-2.2.2 → fastmcp-2.2.3}/justfile +0 -0
  62. {fastmcp-2.2.2 → fastmcp-2.2.3}/pyproject.toml +0 -0
  63. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/__init__.py +0 -0
  64. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/cli/__init__.py +0 -0
  65. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/cli/claude.py +0 -0
  66. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/cli/cli.py +0 -0
  67. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/client/__init__.py +0 -0
  68. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/client/base.py +0 -0
  69. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/client/client.py +0 -0
  70. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/client/roots.py +0 -0
  71. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/client/sampling.py +0 -0
  72. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/client/transports.py +0 -0
  73. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/README.md +0 -0
  74. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/bulk_tool_caller/README.md +0 -0
  75. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/bulk_tool_caller/__init__.py +0 -0
  76. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py +0 -0
  77. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/bulk_tool_caller/example.py +0 -0
  78. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/mcp_mixin/README.md +0 -0
  79. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/mcp_mixin/__init__.py +0 -0
  80. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/mcp_mixin/example.py +0 -0
  81. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py +0 -0
  82. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/exceptions.py +0 -0
  83. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/prompts/__init__.py +0 -0
  84. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/prompts/prompt.py +0 -0
  85. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/prompts/prompt_manager.py +0 -0
  86. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/py.typed +0 -0
  87. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/resources/__init__.py +0 -0
  88. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/resources/resource.py +0 -0
  89. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/resources/resource_manager.py +0 -0
  90. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/resources/types.py +0 -0
  91. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/server/__init__.py +0 -0
  92. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/server/context.py +0 -0
  93. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/server/openapi.py +0 -0
  94. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/server/proxy.py +0 -0
  95. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/server/server.py +0 -0
  96. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/settings.py +0 -0
  97. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/tools/__init__.py +0 -0
  98. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/tools/tool_manager.py +0 -0
  99. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/utilities/__init__.py +0 -0
  100. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/utilities/decorators.py +0 -0
  101. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/utilities/func_metadata.py +0 -0
  102. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/utilities/logging.py +0 -0
  103. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/utilities/openapi.py +0 -0
  104. {fastmcp-2.2.2 → fastmcp-2.2.3}/src/fastmcp/utilities/types.py +0 -0
  105. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/__init__.py +0 -0
  106. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/cli/test_run.py +0 -0
  107. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/client/__init__.py +0 -0
  108. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/client/test_client.py +0 -0
  109. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/client/test_roots.py +0 -0
  110. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/client/test_sampling.py +0 -0
  111. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/conftest.py +0 -0
  112. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/contrib/__init__.py +0 -0
  113. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/contrib/test_bulk_tool_caller.py +0 -0
  114. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/contrib/test_mcp_mixin.py +0 -0
  115. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/prompts/__init__.py +0 -0
  116. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/prompts/test_base.py +0 -0
  117. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/prompts/test_prompt_manager.py +0 -0
  118. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/resources/__init__.py +0 -0
  119. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/resources/test_file_resources.py +0 -0
  120. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/resources/test_function_resources.py +0 -0
  121. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/resources/test_resource_manager.py +0 -0
  122. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/resources/test_resources.py +0 -0
  123. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/__init__.py +0 -0
  124. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_file_server.py +0 -0
  125. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_import_server.py +0 -0
  126. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_lifespan.py +0 -0
  127. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_mount.py +0 -0
  128. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_openapi.py +0 -0
  129. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_proxy.py +0 -0
  130. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_run_server.py +0 -0
  131. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/server/test_server.py +0 -0
  132. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/test_servers/fastmcp_server.py +0 -0
  133. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/test_servers/sse.py +0 -0
  134. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/test_servers/stdio.py +0 -0
  135. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/tools/__init__.py +0 -0
  136. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/__init__.py +0 -0
  137. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/openapi/__init__.py +0 -0
  138. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/openapi/conftest.py +0 -0
  139. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/openapi/test_openapi.py +0 -0
  140. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/openapi/test_openapi_advanced.py +0 -0
  141. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/openapi/test_openapi_fastapi.py +0 -0
  142. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/test_decorated_function.py +0 -0
  143. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/test_func_metadata.py +0 -0
  144. {fastmcp-2.2.2 → fastmcp-2.2.3}/tests/utilities/test_logging.py +0 -0
  145. {fastmcp-2.2.2 → fastmcp-2.2.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: The fast, Pythonic way to build MCP servers.
5
5
  Project-URL: Homepage, https://gofastmcp.com
6
6
  Project-URL: Repository, https://github.com/jlowin/fastmcp
@@ -361,32 +361,32 @@ The `Context` object provides:
361
361
 
362
362
  ### Images
363
363
 
364
- Easily handle image input and output using the `fastmcp.Image` helper class.
364
+ Easily handle image outputs using the `fastmcp.Image` helper class.
365
+
366
+ <Tip>
367
+ The below code requires the `pillow` library to be installed.
368
+ </Tip>
365
369
 
366
370
  ```python
367
- from fastmcp import FastMCP, Image
368
- from PIL import Image as PILImage
369
- import io
371
+ from mcp.server.fastmcp import FastMCP, Image
372
+ from io import BytesIO
373
+ try:
374
+ from PIL import Image as PILImage
375
+ except ImportError:
376
+ raise ImportError("Please install the `pillow` library to run this example.")
370
377
 
371
- mcp = FastMCP("Image Demo")
378
+ mcp = FastMCP("My App")
372
379
 
373
380
  @mcp.tool()
374
- def create_thumbnail(image_data: Image) -> Image:
375
- """Creates a 100x100 thumbnail from the provided image."""
376
- img = PILImage.open(io.BytesIO(image_data.data)) # Assumes image_data received as Image with bytes
377
- img.thumbnail((100, 100))
378
- buffer = io.BytesIO()
381
+ def create_thumbnail(image_path: str) -> Image:
382
+ """Create a thumbnail from an image"""
383
+ img = PILImage.open(image_path)
384
+ img.thumbnail((100, 100))
385
+ buffer = BytesIO()
379
386
  img.save(buffer, format="PNG")
380
- # Return a new Image object with the thumbnail data
381
387
  return Image(data=buffer.getvalue(), format="png")
382
-
383
- @mcp.tool()
384
- def load_image_from_disk(path: str) -> Image:
385
- """Loads an image from the specified path."""
386
- # Handles reading file and detecting format based on extension
387
- return Image(path=path)
388
388
  ```
389
- FastMCP handles the conversion to/from the base64-encoded format required by the MCP protocol.
389
+ Return the `Image` helper class from your tool to send an image to the client. The `Image` helper class handles the conversion to/from the base64-encoded format required by the MCP protocol. It works with either a path to an image file, or a bytes object.
390
390
 
391
391
 
392
392
  ### MCP Clients
@@ -332,32 +332,32 @@ The `Context` object provides:
332
332
 
333
333
  ### Images
334
334
 
335
- Easily handle image input and output using the `fastmcp.Image` helper class.
335
+ Easily handle image outputs using the `fastmcp.Image` helper class.
336
+
337
+ <Tip>
338
+ The below code requires the `pillow` library to be installed.
339
+ </Tip>
336
340
 
337
341
  ```python
338
- from fastmcp import FastMCP, Image
339
- from PIL import Image as PILImage
340
- import io
342
+ from mcp.server.fastmcp import FastMCP, Image
343
+ from io import BytesIO
344
+ try:
345
+ from PIL import Image as PILImage
346
+ except ImportError:
347
+ raise ImportError("Please install the `pillow` library to run this example.")
341
348
 
342
- mcp = FastMCP("Image Demo")
349
+ mcp = FastMCP("My App")
343
350
 
344
351
  @mcp.tool()
345
- def create_thumbnail(image_data: Image) -> Image:
346
- """Creates a 100x100 thumbnail from the provided image."""
347
- img = PILImage.open(io.BytesIO(image_data.data)) # Assumes image_data received as Image with bytes
348
- img.thumbnail((100, 100))
349
- buffer = io.BytesIO()
352
+ def create_thumbnail(image_path: str) -> Image:
353
+ """Create a thumbnail from an image"""
354
+ img = PILImage.open(image_path)
355
+ img.thumbnail((100, 100))
356
+ buffer = BytesIO()
350
357
  img.save(buffer, format="PNG")
351
- # Return a new Image object with the thumbnail data
352
358
  return Image(data=buffer.getvalue(), format="png")
353
-
354
- @mcp.tool()
355
- def load_image_from_disk(path: str) -> Image:
356
- """Loads an image from the specified path."""
357
- # Handles reading file and detecting format based on extension
358
- return Image(path=path)
359
359
  ```
360
- FastMCP handles the conversion to/from the base64-encoded format required by the MCP protocol.
360
+ Return the `Image` helper class from your tool to send an image to the client. The `Image` helper class handles the conversion to/from the base64-encoded format required by the MCP protocol. It works with either a path to an image file, or a bytes object.
361
361
 
362
362
 
363
363
  ### MCP Clients
@@ -5,6 +5,10 @@ description: Understand the different ways FastMCP Clients can connect to server
5
5
  icon: link
6
6
  ---
7
7
 
8
+ import { VersionBadge } from "/snippets/version-badge.mdx"
9
+
10
+ <VersionBadge version="2.0.0" />
11
+
8
12
  The FastMCP `Client` relies on a `ClientTransport` object to handle the specifics of connecting to and communicating with an MCP server. FastMCP provides several built-in transport implementations for common connection methods.
9
13
 
10
14
  While the `Client` often infers the correct transport automatically (see [Client Overview](/clients/client#transport-inference)), you can also instantiate transports explicitly for more control.
@@ -4,6 +4,9 @@ description: "Community-contributed modules extending FastMCP"
4
4
  icon: "cubes"
5
5
  ---
6
6
 
7
+ import { VersionBadge } from "/snippets/version-badge.mdx"
8
+
9
+ <VersionBadge version="2.2.1" />
7
10
 
8
11
  FastMCP includes a `contrib` package that holds community-contributed modules. These modules extend FastMCP's functionality but aren't officially maintained by the core team.
9
12
 
@@ -14,18 +14,20 @@ FastMCP provides a powerful proxying capability that allows one FastMCP server i
14
14
 
15
15
  Proxying means setting up a FastMCP server that doesn't implement its own tools or resources directly. Instead, when it receives a request (like `tools/call` or `resources/read`), it forwards that request to a *backend* MCP server, receives the response, and then relays that response back to the original client.
16
16
 
17
+
17
18
  ```mermaid
18
19
  sequenceDiagram
19
- participant Client
20
- participant ProxyServer as FastMCP Proxy Server
21
- participant BackendServer as Backend MCP Server
22
-
23
- Client->>ProxyServer: Request (e.g., stdio)
24
- ProxyServer->>BackendServer: Request (e.g., sse)
25
- BackendServer-->>ProxyServer: Response (e.g., sse)
26
- ProxyServer-->>Client: Response (e.g., stdio)
20
+ participant ClientApp as Your Client (e.g., Claude Desktop)
21
+ participant FastMCPProxy as FastMCP Proxy Server
22
+ participant BackendServer as Backend MCP Server (e.g., remote SSE)
23
+
24
+ ClientApp->>FastMCPProxy: MCP Request (e.g. stdio)
25
+ Note over FastMCPProxy, BackendServer: Proxy forwards the request
26
+ FastMCPProxy->>BackendServer: MCP Request (e.g. sse)
27
+ BackendServer-->>FastMCPProxy: MCP Response (e.g. sse)
28
+ Note over ClientApp, FastMCPProxy: Proxy relays the response
29
+ FastMCPProxy-->>ClientApp: MCP Response (e.g. stdio)
27
30
  ```
28
-
29
31
  ### Use Cases
30
32
 
31
33
  - **Transport Bridging**: Expose a server running on one transport (e.g., a remote SSE server) via a different transport (e.g., local Stdio for Claude Desktop).
@@ -5,6 +5,8 @@ description: Learn about the core FastMCP server class and how to run it.
5
5
  icon: server
6
6
  ---
7
7
 
8
+ import { VersionBadge } from "/snippets/version-badge.mdx"
9
+
8
10
  The central piece of a FastMCP application is the `FastMCP` server class. This class acts as the main container for your application's tools, resources, and prompts, and manages communication with MCP clients.
9
11
 
10
12
  ## Creating a Server
@@ -225,6 +227,8 @@ The CLI can dynamically find and run FastMCP server objects in your files, but i
225
227
 
226
228
  ## Composing Servers
227
229
 
230
+ <VersionBadge version="2.2.0" />
231
+
228
232
  FastMCP supports composing multiple servers together using `import_server` (static copy) and `mount` (live link). This allows you to organize large applications into modular components or reuse existing servers.
229
233
 
230
234
  See the [Server Composition](/patterns/composition) guide for full details, best practices, and examples.
@@ -246,6 +250,8 @@ main.mount("sub", sub)
246
250
 
247
251
  ## Proxying Servers
248
252
 
253
+ <VersionBadge version="2.0.0" />
254
+
249
255
  FastMCP can act as a proxy for any MCP server (local or remote) using `FastMCP.from_client`, letting you bridge transports or add a frontend to existing servers. For example, you can expose a remote SSE server locally via stdio, or vice versa.
250
256
 
251
257
  See the [Proxying Servers](/patterns/proxy) guide for details and advanced usage.
@@ -5,6 +5,8 @@ description: Create reusable, parameterized prompt templates for MCP clients.
5
5
  icon: message-lines
6
6
  ---
7
7
 
8
+ import { VersionBadge } from "/snippets/version-badge.mdx"
9
+
8
10
  Prompts are reusable message templates that help LLMs generate structured, purposeful responses. FastMCP simplifies defining these templates, primarily using the `@mcp.prompt` decorator.
9
11
 
10
12
  ## What Are Prompts?
@@ -201,6 +203,8 @@ Refer to the [Context documentation](/servers/context) for more details on these
201
203
 
202
204
  ### Duplicate Prompts
203
205
 
206
+ <VersionBadge version="2.1.0" />
207
+
204
208
  You can configure how the FastMCP server handles attempts to register multiple prompts with the same name. Use the `on_duplicate_prompts` setting during `FastMCP` initialization.
205
209
 
206
210
  ```python
@@ -5,6 +5,8 @@ description: Expose data sources and dynamic content generators to your MCP clie
5
5
  icon: database
6
6
  ---
7
7
 
8
+ import { VersionBadge } from "/snippets/version-badge.mdx"
9
+
8
10
  Resources represent data or files that an MCP client can read, and resource templates extend this concept by allowing clients to request dynamically generated resources based on parameters passed in the URI.
9
11
 
10
12
  FastMCP simplifies defining both static and dynamic resources, primarily using the `@mcp.resource` decorator.
@@ -183,6 +185,8 @@ Use these when the content is static or sourced directly from a file/URL, bypass
183
185
 
184
186
  #### Custom Resource Keys
185
187
 
188
+ <VersionBadge version="2.2.0" />
189
+
186
190
  When adding resources directly with `mcp.add_resource()`, you can optionally provide a custom storage key:
187
191
 
188
192
  ```python
@@ -201,6 +205,10 @@ Note that this parameter is only available when using `add_resource()` directly
201
205
 
202
206
  Resource Templates allow clients to request resources whose content depends on parameters embedded in the URI. Define a template using the **same `@mcp.resource` decorator**, but include `{parameter_name}` placeholders in the URI string and add corresponding arguments to your function signature.
203
207
 
208
+ Resource templates generate a new resource for each unique set of parameters, which means that resources can be dynamically created on-demand. For example, if the resource template `"user://profile/{name}"` is registered, MCP clients could request `"user://profile/ford"` or `"user://profile/marvin"` to retrieve either of those two user profiles as resources, without having to register each resource individually.
209
+
210
+ Here is a complete example that shows how to define two resource templates:
211
+
204
212
  ```python
205
213
  from fastmcp import FastMCP
206
214
 
@@ -233,11 +241,61 @@ def get_repo_info(owner: str, repo: str) -> dict:
233
241
  }
234
242
  ```
235
243
 
236
- With these templates defined, clients can request:
244
+ With these two templates defined, clients can request a variety of resources:
237
245
  - `weather://london/current` → Returns weather for London
238
- - `repos://fastmcp/docs/info` → Returns info about the fastmcp/docs repository
246
+ - `weather://paris/current` → Returns weather for Paris
247
+ - `repos://jlowin/fastmcp/info` → Returns info about the jlowin/fastmcp repository
248
+ - `repos://prefecthq/prefect/info` → Returns info about the prefecthq/prefect repository
249
+
250
+ ### Wildcard Parameters
251
+
252
+ <VersionBadge version="2.2.3" />
239
253
 
240
- ### Parameters and Default Values
254
+ Resource templates support wildcard parameters that can match multiple path segments. While standard parameters (`{param}`) only match a single path segment and don't cross "/" boundaries, wildcard parameters (`{param*}`) can capture multiple segments including slashes. Wildcards capture all subsequent path segments *up until* the defined part of the URI template (whether literal or another parameter). This allows you to have multiple wildcard parameters in a single URI template.
255
+
256
+ ```python
257
+ from fastmcp import FastMCP
258
+
259
+ mcp = FastMCP(name="DataServer")
260
+
261
+ # Standard parameter only matches one segment
262
+ @mcp.resource("files://{filename}")
263
+ def get_file(filename: str) -> str:
264
+ """Retrieves a file by name."""
265
+ # Will only match files://<single-segment>
266
+ return f"File content for: {filename}"
267
+
268
+ # Wildcard parameter can match multiple segments
269
+ @mcp.resource("path://{filepath*}")
270
+ def get_path_content(filepath: str) -> str:
271
+ """Retrieves content at a specific path."""
272
+ # Can match path://docs/server/resources.mdx
273
+ return f"Content at path: {filepath}"
274
+
275
+ # Mixing standard and wildcard parameters
276
+ @mcp.resource("repo://{owner}/{path*}/template.py")
277
+ def get_template_file(owner: str, path: str) -> dict:
278
+ """Retrieves a file from a specific repository and path, but
279
+ only if the resource ends with `template.py`"""
280
+ # Can match repo://jlowin/fastmcp/src/resources/template.py
281
+ return {
282
+ "owner": owner,
283
+ "path": path + "/template.py",
284
+ "content": f"File at {path}/template.py in {owner}'s repository"
285
+ }
286
+ ```
287
+
288
+ Wildcard parameters are useful when:
289
+
290
+ - Working with file paths or hierarchical data
291
+ - Creating APIs that need to capture variable-length path segments
292
+ - Building URL-like patterns similar to REST APIs
293
+
294
+ Note that like regular parameters, each wildcard parameter must still be a named parameter in your function signature, and all required function parameters must appear in the URI template.
295
+
296
+ ### Default Values
297
+
298
+ <VersionBadge version="2.2.0" />
241
299
 
242
300
  When creating resource templates, FastMCP enforces two rules for the relationship between URI template parameters and function parameters:
243
301
 
@@ -315,6 +373,8 @@ Templates provide a powerful way to expose parameterized data access points foll
315
373
 
316
374
  ### Custom Template Keys
317
375
 
376
+ <VersionBadge version="2.2.0" />
377
+
318
378
  Similar to resources, you can provide custom keys when directly adding templates:
319
379
 
320
380
  ```python
@@ -337,6 +397,8 @@ This allows accessing the same template implementation through different URI pat
337
397
 
338
398
  ### Duplicate Resources
339
399
 
400
+ <VersionBadge version="2.1.0" />
401
+
340
402
  You can configure how the FastMCP server handles attempts to register multiple resources or templates with the same URI. Use the `on_duplicate_resources` setting during `FastMCP` initialization.
341
403
 
342
404
  ```python
@@ -209,13 +209,18 @@ FastMCP automatically converts the value returned by your function into the appr
209
209
  - **`str`**: Sent as `TextContent`.
210
210
  - **`dict`, `list`, Pydantic `BaseModel`**: Serialized to a JSON string and sent as `TextContent`.
211
211
  - **`bytes`**: Base64 encoded and sent as `BlobResourceContents` (often within an `EmbeddedResource`).
212
- - **`fastmcp.utilities.types.Image`**: A helper class to easily return image data. Sent as `ImageContent`.
212
+ - **`fastmcp.Image`**: A helper class for easily returning image data. Sent as `ImageContent`.
213
213
  - **`None`**: Results in an empty response (no content is sent back to the client).
214
214
 
215
215
  ```python
216
- from fastmcp.utilities.types import Image
217
- from PIL import Image as PILImage
216
+ from fastmcp import FastMCP, Image
218
217
  import io
218
+ try:
219
+ from PIL import Image as PILImage
220
+ except ImportError:
221
+ raise ImportError("Please install the `pillow` library to run this example.")
222
+
223
+ mcp = FastMCP("Image Demo")
219
224
 
220
225
  @mcp.tool()
221
226
  def generate_image(width: int, height: int, color: str) -> Image:
@@ -306,6 +311,8 @@ For full documentation on the Context object and all its capabilities, see the [
306
311
 
307
312
  ### Duplicate Tools
308
313
 
314
+ <VersionBadge version="2.1.0" />
315
+
309
316
  You can control how the FastMCP server behaves if you try to register multiple tools with the same name. This is configured using the `on_duplicate_tools` argument when creating the `FastMCP` instance.
310
317
 
311
318
  ```python
@@ -0,0 +1,13 @@
1
+ export const VersionBadge = ({ version }) => {
2
+ return (
3
+ <code className="version-badge-container">
4
+ <div className="version-badge">
5
+ <span className="version-badge-label">New in version:</span>&nbsp;
6
+ <span className="version-badge-version">{version}</span>
7
+ </div>
8
+ </code>
9
+
10
+
11
+
12
+ );
13
+ };
@@ -14,16 +14,18 @@ h6 code:not(pre code) {
14
14
 
15
15
  /* Version badge -- display a badge with the current version of the documentation */
16
16
  .version-badge {
17
- display: inline-flex;
17
+ display: inline-block;
18
18
  align-items: center;
19
19
  gap: 0.3em;
20
- padding: 0.32em 1em;
21
- font-size: 0.92em;
22
- font-weight: 600;
23
- letter-spacing: 0.01em;
24
- color: #7417e5;
25
- background: #f3e8ff;
26
- border: 1.5px solid #c084fc;
20
+ padding: 0.2em 0.8em;
21
+ font-size: 1.1em;
22
+ font-weight: 400;
23
+
24
+ font-family: "Inter", sans-serif;
25
+ letter-spacing: 0.025em;
26
+ color: #ff5400;
27
+ background: #ffeee6;
28
+ border: 1px solid rgb(255, 84, 0, 0.5);
27
29
  border-radius: 6px;
28
30
  box-shadow: none;
29
31
  vertical-align: middle;
@@ -31,6 +33,11 @@ h6 code:not(pre code) {
31
33
  transition: box-shadow 0.2s, transform 0.15s;
32
34
  }
33
35
 
36
+ .version-badge-container {
37
+ margin: 0;
38
+ padding: 0;
39
+ }
40
+
34
41
  .version-badge:hover {
35
42
  box-shadow: 0 2px 8px 0 rgba(160, 132, 252, 0.1);
36
43
  transform: translateY(-1px) scale(1.03);
@@ -41,13 +48,3 @@ h6 code:not(pre code) {
41
48
  background: #312e81;
42
49
  border: 1.5px solid #a78bfa;
43
50
  }
44
-
45
- .badge-emoji {
46
- font-size: 1.15em;
47
- line-height: 1;
48
- text-shadow: 0 1px 2px #fff, 0 0px 2px #c084fc;
49
- }
50
-
51
- .dark .badge-emoji {
52
- text-shadow: 0 1px 2px #312e81, 0 0px 2px #a78bfa;
53
- }
@@ -24,13 +24,16 @@ from fastmcp.utilities.types import _convert_set_defaults
24
24
 
25
25
 
26
26
  def build_regex(template: str) -> re.Pattern:
27
- # Escape all non-brace characters, then restore {var} placeholders
28
27
  parts = re.split(r"(\{[^}]+\})", template)
29
28
  pattern = ""
30
29
  for part in parts:
31
30
  if part.startswith("{") and part.endswith("}"):
32
31
  name = part[1:-1]
33
- pattern += f"(?P<{name}>[^/]+)"
32
+ if name.endswith("*"):
33
+ name = name[:-1]
34
+ pattern += f"(?P<{name}>.+)"
35
+ else:
36
+ pattern += f"(?P<{name}>[^/]+)"
34
37
  else:
35
38
  pattern += re.escape(part)
36
39
  return re.compile(f"^{pattern}$")
@@ -76,7 +76,12 @@ class Tool(BaseModel):
76
76
  fn_callable,
77
77
  skip_names=[context_kwarg] if context_kwarg is not None else [],
78
78
  )
79
- parameters = func_arg_metadata.arg_model.model_json_schema()
79
+ try:
80
+ parameters = func_arg_metadata.arg_model.model_json_schema()
81
+ except Exception as e:
82
+ raise TypeError(
83
+ f'Unable to parse parameters for function "{fn.__name__}": {e}'
84
+ ) from e
80
85
 
81
86
  return cls(
82
87
  fn=fn_callable,
@@ -301,6 +301,23 @@ class TestResourceTemplate:
301
301
  class TestMatchUriTemplate:
302
302
  """Test match_uri_template function."""
303
303
 
304
+ @pytest.mark.parametrize(
305
+ "uri, expected_params",
306
+ [
307
+ ("test://a/b", None),
308
+ ("test://a/b/c", None),
309
+ ("test://a/x/b", {"x": "x"}),
310
+ ("test://a/x/y/b", None),
311
+ ],
312
+ )
313
+ def test_match_uri_template_single_param(
314
+ self, uri: str, expected_params: dict[str, str]
315
+ ):
316
+ """Test that match_uri_template uses the slash delimiter."""
317
+ uri_template = "test://a/{x}/b"
318
+ result = match_uri_template(uri=uri, uri_template=uri_template)
319
+ assert result == expected_params
320
+
304
321
  @pytest.mark.parametrize(
305
322
  "uri, expected_params",
306
323
  [
@@ -361,7 +378,7 @@ class TestMatchUriTemplate:
361
378
  ("other+prefix+test://foo/test/123", None),
362
379
  ],
363
380
  )
364
- def test_match_prefixed_uri_template(
381
+ def test_match_uri_template_with_prefix(
365
382
  self, uri: str, expected_params: dict[str, str] | None
366
383
  ):
367
384
  """Test matching URIs against a template with a prefix."""
@@ -369,10 +386,60 @@ class TestMatchUriTemplate:
369
386
  result = match_uri_template(uri=uri, uri_template=uri_template)
370
387
  assert result == expected_params
371
388
 
372
- def test_quoted_params(self):
389
+ def test_match_uri_template_quoted_params(self):
373
390
  uri_template = "user://{name}/{email}"
374
391
  quoted_name = quote("John Doe", safe="")
375
392
  quoted_email = quote("john@example.com", safe="")
376
393
  uri = f"user://{quoted_name}/{quoted_email}"
377
394
  result = match_uri_template(uri=uri, uri_template=uri_template)
378
395
  assert result == {"name": "John Doe", "email": "john@example.com"}
396
+
397
+ @pytest.mark.parametrize(
398
+ "uri, expected_params",
399
+ [
400
+ ("test://a/b", None),
401
+ ("test://a/b/c", None),
402
+ ("test://a/x/b", {"x": "x"}),
403
+ ("test://a/x/y/b", {"x": "x/y"}),
404
+ ("bad-prefix://a/x/y/b", None),
405
+ ("test://a/x/y/z", None),
406
+ ],
407
+ )
408
+ def test_match_uri_template_wildcard_param(
409
+ self, uri: str, expected_params: dict[str, str]
410
+ ):
411
+ """Test that match_uri_template uses the slash delimiter."""
412
+ uri_template = "test://a/{x*}/b"
413
+ result = match_uri_template(uri=uri, uri_template=uri_template)
414
+ assert result == expected_params
415
+
416
+ @pytest.mark.parametrize(
417
+ "uri, expected_params",
418
+ [
419
+ ("test://a/x/y/b/c/d", {"x": "x/y", "y": "c/d"}),
420
+ ("bad-prefix://a/x/y/b/c/d", None),
421
+ ("test://a/x/y/c/d", None),
422
+ ("test://a/x/b/y", {"x": "x", "y": "y"}),
423
+ ],
424
+ )
425
+ def test_match_uri_template_multiple_wildcard_params(
426
+ self, uri: str, expected_params: dict[str, str]
427
+ ):
428
+ """Test that match_uri_template uses the slash delimiter."""
429
+ uri_template = "test://a/{x*}/b/{y*}"
430
+ result = match_uri_template(uri=uri, uri_template=uri_template)
431
+ assert result == expected_params
432
+
433
+ def test_match_uri_template_wildcard_and_literal_param(self):
434
+ """Test that match_uri_template uses the slash delimiter."""
435
+ uri = "test://a/x/y/b"
436
+ uri_template = "test://a/{x*}/{y}"
437
+ result = match_uri_template(uri=uri, uri_template=uri_template)
438
+ assert result == {"x": "x/y", "y": "b"}
439
+
440
+ def test_match_consecutive_params(self):
441
+ """Test that consecutive parameters without a / are not matched."""
442
+ uri = "test://a/x/y"
443
+ uri_template = "test://a/{x}{y}"
444
+ result = match_uri_template(uri=uri, uri_template=uri_template)
445
+ assert result is None
@@ -2,8 +2,10 @@ import json
2
2
  import logging
3
3
 
4
4
  import pytest
5
+ from mcp.types import ImageContent, TextContent
5
6
  from pydantic import BaseModel
6
7
 
8
+ from fastmcp import Context, FastMCP, Image
7
9
  from fastmcp.exceptions import NotFoundError, ToolError
8
10
  from fastmcp.tools import ToolManager
9
11
  from fastmcp.tools.tool import Tool
@@ -68,6 +70,18 @@ class TestAddTools:
68
70
  assert "age" in tool.parameters["$defs"]["UserInput"]["properties"]
69
71
  assert "flag" in tool.parameters["properties"]
70
72
 
73
+ async def test_tool_with_image_return(self):
74
+ def image_tool(data: bytes) -> Image:
75
+ return Image(data=data)
76
+
77
+ manager = ToolManager()
78
+ manager.add_tool_from_fn(image_tool)
79
+
80
+ tool = manager.get_tool("image_tool")
81
+ result = await tool.run({"data": "test.png"})
82
+ assert tool.parameters["properties"]["data"]["type"] == "string"
83
+ assert isinstance(result[0], ImageContent)
84
+
71
85
  def test_add_invalid_tool(self):
72
86
  manager = ToolManager()
73
87
  with pytest.raises(AttributeError):
@@ -263,7 +277,6 @@ class TestCallTools:
263
277
  result = await manager.call_tool("double", {"n": 5})
264
278
  assert isinstance(result, list)
265
279
  assert len(result) == 1
266
- from mcp.types import TextContent
267
280
 
268
281
  assert isinstance(result[0], TextContent)
269
282
  assert result[0].text == "10"
@@ -279,7 +292,6 @@ class TestCallTools:
279
292
  result = await manager.call_tool("add", {"a": 1})
280
293
  assert isinstance(result, list)
281
294
  assert len(result) == 1
282
- from mcp.types import TextContent
283
295
 
284
296
  assert isinstance(result[0], TextContent)
285
297
  assert result[0].text == "2"
@@ -307,7 +319,6 @@ class TestCallTools:
307
319
  manager = ToolManager()
308
320
  manager.add_tool_from_fn(sum_vals)
309
321
  # Try both with plain list and with JSON list
310
- from mcp.types import TextContent
311
322
 
312
323
  result = await manager.call_tool("sum_vals", {"vals": "[1, 2, 3]"})
313
324
  assert isinstance(result, list)
@@ -329,7 +340,6 @@ class TestCallTools:
329
340
 
330
341
  manager = ToolManager()
331
342
  manager.add_tool_from_fn(concat_strs)
332
- from mcp.types import TextContent
333
343
 
334
344
  # Try both with plain python object and with JSON list
335
345
  result = await manager.call_tool("concat_strs", {"vals": ["a", "b", "c"]})
@@ -357,10 +367,6 @@ class TestCallTools:
357
367
  assert result[0].text == '"a"'
358
368
 
359
369
  async def test_call_tool_with_complex_model(self):
360
- from mcp.types import TextContent
361
-
362
- from fastmcp import Context
363
-
364
370
  class MyShrimpTank(BaseModel):
365
371
  class Shrimp(BaseModel):
366
372
  name: str
@@ -397,8 +403,6 @@ class TestCallTools:
397
403
 
398
404
  class TestToolSchema:
399
405
  async def test_context_arg_excluded_from_schema(self):
400
- from fastmcp import Context
401
-
402
406
  def something(a: int, ctx: Context) -> int:
403
407
  return a
404
408
 
@@ -415,7 +419,6 @@ class TestContextHandling:
415
419
  def test_context_parameter_detection(self):
416
420
  """Test that context parameters are properly detected in
417
421
  Tool.from_function()."""
418
- from fastmcp import Context
419
422
 
420
423
  def tool_with_context(x: int, ctx: Context) -> str:
421
424
  return str(x)
@@ -432,9 +435,6 @@ class TestContextHandling:
432
435
 
433
436
  async def test_context_injection(self):
434
437
  """Test that context is properly injected during tool execution."""
435
- from mcp.types import TextContent
436
-
437
- from fastmcp import Context, FastMCP
438
438
 
439
439
  def tool_with_context(x: int, ctx: Context) -> str:
440
440
  assert isinstance(ctx, Context)
@@ -453,9 +453,6 @@ class TestContextHandling:
453
453
 
454
454
  async def test_context_injection_async(self):
455
455
  """Test that context is properly injected in async tools."""
456
- from mcp.types import TextContent
457
-
458
- from fastmcp import Context, FastMCP
459
456
 
460
457
  async def async_tool(x: int, ctx: Context) -> str:
461
458
  assert isinstance(ctx, Context)
@@ -476,8 +473,6 @@ class TestContextHandling:
476
473
  """Test that context is optional when calling tools."""
477
474
  from mcp.types import TextContent
478
475
 
479
- from fastmcp import Context
480
-
481
476
  def tool_with_context(x: int, ctx: Context | None = None) -> str:
482
477
  return str(x)
483
478
 
@@ -492,7 +487,6 @@ class TestContextHandling:
492
487
 
493
488
  async def test_context_error_handling(self):
494
489
  """Test error handling when context injection fails."""
495
- from fastmcp import Context, FastMCP
496
490
 
497
491
  def tool_with_context(x: int, ctx: Context) -> str:
498
492
  raise ValueError("Test error")
@@ -1,8 +0,0 @@
1
- export const VersionBadge = ({ version }) => {
2
- return (
3
- <span className="version-badge">
4
- <span className="badge-emoji" aria-hidden="true" style={{ marginRight: '0.3em', verticalAlign: 'middle' }}>✨</span>
5
- New in version {version}
6
- </span>
7
- );
8
- };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes