fastmcp 2.3.2__tar.gz → 2.3.4__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 (180) hide show
  1. {fastmcp-2.3.2 → fastmcp-2.3.4}/.github/workflows/run-tests.yml +3 -11
  2. {fastmcp-2.3.2 → fastmcp-2.3.4}/PKG-INFO +2 -2
  3. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/clients/client.mdx +44 -1
  4. fastmcp-2.3.4/docs/servers/composition.mdx +212 -0
  5. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/servers/resources.mdx +38 -9
  6. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/servers/tools.mdx +34 -12
  7. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/complex_inputs.py +1 -1
  8. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/desktop.py +1 -1
  9. {fastmcp-2.3.2 → fastmcp-2.3.4}/pyproject.toml +1 -1
  10. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/client/__init__.py +2 -0
  11. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/client/client.py +84 -21
  12. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/client/transports.py +53 -28
  13. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/exceptions.py +2 -0
  14. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/prompts/prompt.py +12 -6
  15. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/resources/resource_manager.py +22 -1
  16. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/resources/template.py +21 -17
  17. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/resources/types.py +25 -27
  18. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/server/openapi.py +14 -1
  19. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/server/proxy.py +4 -4
  20. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/server/server.py +78 -56
  21. fastmcp-2.3.4/src/fastmcp/settings.py +131 -0
  22. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/tools/tool.py +45 -45
  23. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/tools/tool_manager.py +27 -2
  24. fastmcp-2.3.4/src/fastmcp/utilities/exceptions.py +49 -0
  25. fastmcp-2.3.4/src/fastmcp/utilities/json_schema.py +120 -0
  26. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/utilities/logging.py +11 -6
  27. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/utilities/openapi.py +122 -7
  28. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/utilities/tests.py +4 -2
  29. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/client/test_client.py +143 -4
  30. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/client/test_sse.py +65 -4
  31. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/client/test_streamable_http.py +49 -2
  32. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/contrib/test_bulk_tool_caller.py +1 -2
  33. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/resources/test_file_resources.py +3 -2
  34. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/resources/test_function_resources.py +1 -1
  35. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/resources/test_resource_manager.py +85 -1
  36. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/resources/test_resource_template.py +0 -15
  37. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_mount.py +15 -0
  38. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_openapi.py +121 -2
  39. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_proxy.py +4 -5
  40. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_server.py +22 -2
  41. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_server_interactions.py +29 -28
  42. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/test_deprecated.py +11 -0
  43. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/tools/test_tool.py +3 -3
  44. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/tools/test_tool_manager.py +85 -1
  45. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/openapi/test_openapi_fastapi.py +17 -0
  46. fastmcp-2.3.4/tests/utilities/test_json_schema.py +304 -0
  47. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/test_typeadapter.py +2 -2
  48. {fastmcp-2.3.2 → fastmcp-2.3.4}/uv.lock +4 -4
  49. fastmcp-2.3.2/docs/servers/composition.mdx +0 -340
  50. fastmcp-2.3.2/src/fastmcp/settings.py +0 -105
  51. fastmcp-2.3.2/src/fastmcp/utilities/json_schema.py +0 -59
  52. fastmcp-2.3.2/tests/utilities/test_json_schema.py +0 -110
  53. {fastmcp-2.3.2 → fastmcp-2.3.4}/.cursor/rules/core-mcp-objects.mdc +0 -0
  54. {fastmcp-2.3.2 → fastmcp-2.3.4}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  55. {fastmcp-2.3.2 → fastmcp-2.3.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  56. {fastmcp-2.3.2 → fastmcp-2.3.4}/.github/ISSUE_TEMPLATE/enhancement.yml +0 -0
  57. {fastmcp-2.3.2 → fastmcp-2.3.4}/.github/release.yml +0 -0
  58. {fastmcp-2.3.2 → fastmcp-2.3.4}/.github/workflows/publish.yml +0 -0
  59. {fastmcp-2.3.2 → fastmcp-2.3.4}/.github/workflows/run-static.yml +0 -0
  60. {fastmcp-2.3.2 → fastmcp-2.3.4}/.gitignore +0 -0
  61. {fastmcp-2.3.2 → fastmcp-2.3.4}/.pre-commit-config.yaml +0 -0
  62. {fastmcp-2.3.2 → fastmcp-2.3.4}/LICENSE +0 -0
  63. {fastmcp-2.3.2 → fastmcp-2.3.4}/README.md +0 -0
  64. {fastmcp-2.3.2 → fastmcp-2.3.4}/Windows_Notes.md +0 -0
  65. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/assets/demo-inspector.png +0 -0
  66. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/clients/transports.mdx +0 -0
  67. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/deployment/asgi.mdx +0 -0
  68. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/deployment/authentication.mdx +0 -0
  69. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/deployment/cli.mdx +0 -0
  70. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/deployment/running-server.mdx +0 -0
  71. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/docs.json +0 -0
  72. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/getting-started/installation.mdx +0 -0
  73. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/getting-started/quickstart.mdx +0 -0
  74. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/getting-started/welcome.mdx +0 -0
  75. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/patterns/contrib.mdx +0 -0
  76. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/patterns/decorating-methods.mdx +0 -0
  77. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/patterns/fastapi.mdx +0 -0
  78. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/patterns/http-requests.mdx +0 -0
  79. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/patterns/openapi.mdx +0 -0
  80. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/patterns/testing.mdx +0 -0
  81. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/servers/context.mdx +0 -0
  82. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/servers/fastmcp.mdx +0 -0
  83. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/servers/prompts.mdx +0 -0
  84. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/servers/proxy.mdx +0 -0
  85. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/snippets/version-badge.mdx +0 -0
  86. {fastmcp-2.3.2 → fastmcp-2.3.4}/docs/style.css +0 -0
  87. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/echo.py +0 -0
  88. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/memory.py +0 -0
  89. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/mount_example.py +0 -0
  90. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/sampling.py +0 -0
  91. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/screenshot.py +0 -0
  92. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/serializer.py +0 -0
  93. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/simple_echo.py +0 -0
  94. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/README.md +0 -0
  95. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/pyproject.toml +0 -0
  96. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/__init__.py +0 -0
  97. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/__main__.py +0 -0
  98. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/hub.py +0 -0
  99. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/lights/__init__.py +0 -0
  100. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/lights/hue_utils.py +0 -0
  101. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/lights/server.py +0 -0
  102. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/py.typed +0 -0
  103. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/src/smart_home/settings.py +0 -0
  104. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/smart_home/uv.lock +0 -0
  105. {fastmcp-2.3.2 → fastmcp-2.3.4}/examples/text_me.py +0 -0
  106. {fastmcp-2.3.2 → fastmcp-2.3.4}/justfile +0 -0
  107. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/__init__.py +0 -0
  108. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/cli/__init__.py +0 -0
  109. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/cli/claude.py +0 -0
  110. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/cli/cli.py +0 -0
  111. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/client/base.py +0 -0
  112. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/client/logging.py +0 -0
  113. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/client/roots.py +0 -0
  114. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/client/sampling.py +0 -0
  115. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/README.md +0 -0
  116. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/bulk_tool_caller/README.md +0 -0
  117. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/bulk_tool_caller/__init__.py +0 -0
  118. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py +0 -0
  119. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/bulk_tool_caller/example.py +0 -0
  120. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/mcp_mixin/README.md +0 -0
  121. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/mcp_mixin/__init__.py +0 -0
  122. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/mcp_mixin/example.py +0 -0
  123. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py +0 -0
  124. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/low_level/README.md +0 -0
  125. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/low_level/__init__.py +0 -0
  126. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/low_level/sse_server_transport.py +0 -0
  127. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/prompts/__init__.py +0 -0
  128. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/prompts/prompt_manager.py +0 -0
  129. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/py.typed +0 -0
  130. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/resources/__init__.py +0 -0
  131. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/resources/resource.py +0 -0
  132. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/server/__init__.py +0 -0
  133. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/server/context.py +0 -0
  134. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/server/dependencies.py +0 -0
  135. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/server/http.py +0 -0
  136. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/tools/__init__.py +0 -0
  137. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/utilities/__init__.py +0 -0
  138. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/utilities/cache.py +0 -0
  139. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/utilities/decorators.py +0 -0
  140. {fastmcp-2.3.2 → fastmcp-2.3.4}/src/fastmcp/utilities/types.py +0 -0
  141. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/__init__.py +0 -0
  142. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/cli/test_cli.py +0 -0
  143. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/cli/test_run.py +0 -0
  144. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/client/__init__.py +0 -0
  145. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/client/test_logs.py +0 -0
  146. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/client/test_roots.py +0 -0
  147. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/client/test_sampling.py +0 -0
  148. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/conftest.py +0 -0
  149. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/contrib/__init__.py +0 -0
  150. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/contrib/test_mcp_mixin.py +0 -0
  151. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/prompts/__init__.py +0 -0
  152. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/prompts/test_prompt.py +0 -0
  153. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/prompts/test_prompt_manager.py +0 -0
  154. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/resources/__init__.py +0 -0
  155. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/resources/test_resources.py +0 -0
  156. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/__init__.py +0 -0
  157. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_auth_integration.py +0 -0
  158. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_context.py +0 -0
  159. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_file_server.py +0 -0
  160. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_http_dependencies.py +0 -0
  161. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_http_middleware.py +0 -0
  162. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_import_server.py +0 -0
  163. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_lifespan.py +0 -0
  164. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_run_server.py +0 -0
  165. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/server/test_tool_annotations.py +0 -0
  166. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/test_examples.py +0 -0
  167. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/test_servers/fastmcp_server.py +0 -0
  168. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/test_servers/sse.py +0 -0
  169. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/test_servers/stdio.py +0 -0
  170. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/tools/__init__.py +0 -0
  171. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/__init__.py +0 -0
  172. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/openapi/__init__.py +0 -0
  173. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/openapi/conftest.py +0 -0
  174. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/openapi/test_openapi.py +0 -0
  175. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/openapi/test_openapi_advanced.py +0 -0
  176. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/test_cache.py +0 -0
  177. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/test_decorated_function.py +0 -0
  178. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/test_logging.py +0 -0
  179. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/test_tests.py +0 -0
  180. {fastmcp-2.3.2 → fastmcp-2.3.4}/tests/utilities/test_types.py +0 -0
@@ -45,18 +45,10 @@ jobs:
45
45
  with:
46
46
  enable-cache: true
47
47
  cache-dependency-glob: "uv.lock"
48
-
49
- - name: Set up Python ${{ matrix.python-version }}
50
- run: uv python install ${{ matrix.python-version }}
48
+ python-version: ${{ matrix.python-version }}
51
49
 
52
50
  - name: Install FastMCP
53
- run: uv sync --dev
54
-
55
- - name: Fix pyreadline on Windows
56
- if: matrix.os == 'windows-latest'
57
- run: |
58
- uv pip uninstall -y pyreadline
59
- uv pip install pyreadline3
51
+ run: uv sync --dev --locked
60
52
 
61
53
  - name: Run tests
62
- run: uv run --frozen pytest
54
+ run: uv run pytest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.3.2
3
+ Version: 2.3.4
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
@@ -19,7 +19,7 @@ Classifier: Typing :: Typed
19
19
  Requires-Python: >=3.10
20
20
  Requires-Dist: exceptiongroup>=1.2.2
21
21
  Requires-Dist: httpx>=0.28.1
22
- Requires-Dist: mcp<2.0.0,>=1.8.0
22
+ Requires-Dist: mcp<2.0.0,>=1.8.1
23
23
  Requires-Dist: openapi-pydantic>=0.5.1
24
24
  Requires-Dist: python-dotenv>=1.1.0
25
25
  Requires-Dist: rich>=13.9.4
@@ -115,14 +115,18 @@ The standard client methods return user-friendly representations that may change
115
115
  tools = await client.list_tools()
116
116
  # tools -> list[mcp.types.Tool]
117
117
  ```
118
- * **`call_tool(name: str, arguments: dict[str, Any] | None = None)`**: Executes a tool on the server.
118
+ * **`call_tool(name: str, arguments: dict[str, Any] | None = None, timeout: float | None = None)`**: Executes a tool on the server.
119
119
  ```python
120
120
  result = await client.call_tool("add", {"a": 5, "b": 3})
121
121
  # result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
122
122
  print(result[0].text) # Assuming TextContent, e.g., '8'
123
+
124
+ # With timeout (aborts if execution takes longer than 2 seconds)
125
+ result = await client.call_tool("long_running_task", {"param": "value"}, timeout=2.0)
123
126
  ```
124
127
  * Arguments are passed as a dictionary. FastMCP servers automatically handle JSON string parsing for complex types if needed.
125
128
  * Returns a list of content objects (usually `TextContent` or `ImageContent`).
129
+ * The optional `timeout` parameter limits the maximum execution time (in seconds) for this specific call, overriding any client-level timeout.
126
130
 
127
131
  #### Resource Operations
128
132
 
@@ -191,6 +195,45 @@ These methods are especially useful for debugging or when you need to access met
191
195
 
192
196
  MCP allows servers to interact with clients in order to provide additional capabilities. The `Client` constructor accepts additional configuration to handle these server requests.
193
197
 
198
+ #### Timeout Control
199
+
200
+ <VersionBadge version="2.3.4" />
201
+
202
+ You can control request timeouts at both the client level and individual request level:
203
+
204
+ ```python
205
+ from fastmcp import Client
206
+ from fastmcp.exceptions import McpError
207
+
208
+ # Client with a global 5-second timeout for all requests
209
+ client = Client(
210
+ my_mcp_server,
211
+ timeout=5.0 # Default timeout in seconds
212
+ )
213
+
214
+ async with client:
215
+ # This uses the global 5-second timeout
216
+ result1 = await client.call_tool("quick_task", {"param": "value"})
217
+
218
+ # This specifies a 10-second timeout for this specific call
219
+ result2 = await client.call_tool("slow_task", {"param": "value"}, timeout=10.0)
220
+
221
+ try:
222
+ # This will likely timeout
223
+ result3 = await client.call_tool("medium_task", {"param": "value"}, timeout=0.01)
224
+ except McpError as e:
225
+ # Handle timeout error
226
+ print(f"The task timed out: {e}")
227
+ ```
228
+
229
+ <Warning>
230
+ Timeout behavior varies between transport types:
231
+
232
+ - With **SSE** transport, the per-request (tool call) timeout **always** takes precedence, regardless of which is lower.
233
+ - With **HTTP** transport, the **lower** of the two timeouts (client or tool call) takes precedence.
234
+
235
+ For consistent behavior across all transports, we recommend explicitly setting timeouts at the individual tool call level when needed, rather than relying on client-level timeouts.
236
+ </Warning>
194
237
 
195
238
  #### LLM Sampling
196
239
 
@@ -0,0 +1,212 @@
1
+ ---
2
+ title: Server Composition
3
+ sidebarTitle: Composition
4
+ description: Combine multiple FastMCP servers into a single, larger application using mounting and importing.
5
+ icon: puzzle-piece
6
+ ---
7
+ import { VersionBadge } from '/snippets/version-badge.mdx'
8
+
9
+ <VersionBadge version="2.2.0" />
10
+
11
+ As your MCP applications grow, you might want to organize your tools, resources, and prompts into logical modules or reuse existing server components. FastMCP supports composition through two methods:
12
+
13
+ - **`import_server`**: For a one-time copy of components with prefixing (static composition).
14
+ - **`mount`**: For creating a live link where the main server delegates requests to the subserver (dynamic composition).
15
+
16
+ ## Why Compose Servers?
17
+
18
+ - **Modularity**: Break down large applications into smaller, focused servers (e.g., a `WeatherServer`, a `DatabaseServer`, a `CalendarServer`).
19
+ - **Reusability**: Create common utility servers (e.g., a `TextProcessingServer`) and mount them wherever needed.
20
+ - **Teamwork**: Different teams can work on separate FastMCP servers that are later combined.
21
+ - **Organization**: Keep related functionality grouped together logically.
22
+
23
+ ### Importing vs Mounting
24
+
25
+ The choice of importing or mounting depends on your use case and requirements.
26
+
27
+ | Feature | Importing | Mounting |
28
+ |---------|----------------|---------|
29
+ | **Method** | `FastMCP.import_server()` | `FastMCP.mount()` |
30
+ | **Composition Type** | One-time copy (static) | Live link (dynamic) |
31
+ | **Updates** | Changes to subserver NOT reflected | Changes to subserver immediately reflected |
32
+ | **Best For** | Bundling finalized components | Modular runtime composition |
33
+
34
+ ### Proxy Servers
35
+
36
+ FastMCP supports [MCP proxying](/patterns/proxy), which allows you to mirror a local or remote server in a local FastMCP instance. Proxies are fully compatible with both importing and mounting.
37
+
38
+ ## Importing (Static Composition)
39
+
40
+ The `import_server()` method copies all components (tools, resources, templates, prompts) from one `FastMCP` instance (the *subserver*) into another (the *main server*). A `prefix` is added to avoid naming conflicts.
41
+
42
+ ```python
43
+ from fastmcp import FastMCP
44
+ import asyncio
45
+
46
+ # Define subservers
47
+ weather_mcp = FastMCP(name="WeatherService")
48
+
49
+ @weather_mcp.tool()
50
+ def get_forecast(city: str) -> dict:
51
+ """Get weather forecast."""
52
+ return {"city": city, "forecast": "Sunny"}
53
+
54
+ @weather_mcp.resource("data://cities/supported")
55
+ def list_supported_cities() -> list[str]:
56
+ """List cities with weather support."""
57
+ return ["London", "Paris", "Tokyo"]
58
+
59
+ # Define main server
60
+ main_mcp = FastMCP(name="MainApp")
61
+
62
+ # Import subserver
63
+ async def setup():
64
+ await main_mcp.import_server("weather", weather_mcp)
65
+
66
+ # Result: main_mcp now contains prefixed components:
67
+ # - Tool: "weather_get_forecast"
68
+ # - Resource: "weather+data://cities/supported"
69
+
70
+ if __name__ == "__main__":
71
+ asyncio.run(setup())
72
+ main_mcp.run()
73
+ ```
74
+
75
+ ### How Importing Works
76
+
77
+ When you call `await main_mcp.import_server(prefix, subserver)`:
78
+
79
+ 1. **Tools**: All tools from `subserver` are added to `main_mcp` with names prefixed using `{prefix}_`.
80
+ - `subserver.tool(name="my_tool")` becomes `main_mcp.tool(name="{prefix}_my_tool")`.
81
+ 2. **Resources**: All resources are added with URIs prefixed using `{prefix}+`.
82
+ - `subserver.resource(uri="data://info")` becomes `main_mcp.resource(uri="{prefix}+data://info")`.
83
+ 3. **Resource Templates**: Templates are prefixed similarly to resources.
84
+ - `subserver.resource(uri="data://{id}")` becomes `main_mcp.resource(uri="{prefix}+data://{id}")`.
85
+ 4. **Prompts**: All prompts are added with names prefixed like tools.
86
+ - `subserver.prompt(name="my_prompt")` becomes `main_mcp.prompt(name="{prefix}_my_prompt")`.
87
+
88
+ Note that `import_server` performs a **one-time copy** of components. Changes made to the `subserver` *after* importing **will not** be reflected in `main_mcp`. The `subserver`'s `lifespan` context is also **not** executed by the main server.
89
+
90
+ ## Mounting (Live Linking)
91
+
92
+ The `mount()` method creates a **live link** between the `main_mcp` server and the `subserver`. Instead of copying components, requests for components matching the `prefix` are **delegated** to the `subserver` at runtime.
93
+
94
+ ```python
95
+ import asyncio
96
+ from fastmcp import FastMCP, Client
97
+
98
+ # Define subserver
99
+ dynamic_mcp = FastMCP(name="DynamicService")
100
+
101
+ @dynamic_mcp.tool()
102
+ def initial_tool():
103
+ """Initial tool demonstration."""
104
+ return "Initial Tool Exists"
105
+
106
+ # Mount subserver (synchronous operation)
107
+ main_mcp = FastMCP(name="MainAppLive")
108
+ main_mcp.mount("dynamic", dynamic_mcp)
109
+
110
+ # Add a tool AFTER mounting - it will be accessible through main_mcp
111
+ @dynamic_mcp.tool()
112
+ def added_later():
113
+ """Tool added after mounting."""
114
+ return "Tool Added Dynamically!"
115
+
116
+ # Testing access to mounted tools
117
+ async def test_dynamic_mount():
118
+ tools = await main_mcp.get_tools()
119
+ print("Available tools:", list(tools.keys()))
120
+ # Shows: ['dynamic_initial_tool', 'dynamic_added_later']
121
+
122
+ async with Client(main_mcp) as client:
123
+ result = await client.call_tool("dynamic_added_later")
124
+ print("Result:", result[0].text)
125
+ # Shows: "Tool Added Dynamically!"
126
+
127
+ if __name__ == "__main__":
128
+ asyncio.run(test_dynamic_mount())
129
+ ```
130
+
131
+ ### How Mounting Works
132
+
133
+ When mounting is configured:
134
+
135
+ 1. **Live Link**: The parent server establishes a connection to the mounted server.
136
+ 2. **Dynamic Updates**: Changes to the mounted server are immediately reflected when accessed through the parent.
137
+ 3. **Prefixed Access**: The parent server uses prefixes to route requests to the mounted server.
138
+ 4. **Delegation**: Requests for components matching the prefix are delegated to the mounted server at runtime.
139
+
140
+ The same prefixing rules apply as with `import_server` for naming tools, resources, templates, and prompts.
141
+
142
+ ### Direct vs. Proxy Mounting
143
+
144
+ <VersionBadge version="2.2.7" />
145
+
146
+ FastMCP supports two mounting modes:
147
+
148
+ 1. **Direct Mounting** (default): The parent server directly accesses the mounted server's objects in memory.
149
+ - No client lifecycle events occur on the mounted server
150
+ - The mounted server's lifespan context is not executed
151
+ - Communication is handled through direct method calls
152
+
153
+ 2. **Proxy Mounting**: The parent server treats the mounted server as a separate entity and communicates with it through a client interface.
154
+ - Full client lifecycle events occur on the mounted server
155
+ - The mounted server's lifespan is executed when a client connects
156
+ - Communication happens via an in-memory Client transport
157
+
158
+ ```python
159
+ # Direct mounting (default when no custom lifespan)
160
+ main_mcp.mount("api", api_server)
161
+
162
+ # Proxy mounting (preserves full client lifecycle)
163
+ main_mcp.mount("api", api_server, as_proxy=True)
164
+ ```
165
+
166
+ FastMCP automatically uses proxy mounting when the mounted server has a custom lifespan, but you can override this behavior with the `as_proxy` parameter.
167
+
168
+ #### Interaction with Proxy Servers
169
+
170
+ When using `FastMCP.from_client()` to create a proxy server, mounting that server will always use proxy mounting:
171
+
172
+ ```python
173
+ # Create a proxy for a remote server
174
+ remote_proxy = FastMCP.from_client(Client("http://example.com/mcp"))
175
+
176
+ # Mount the proxy (always uses proxy mounting)
177
+ main_server.mount("remote", remote_proxy)
178
+ ```
179
+
180
+ ## Customizing Separators
181
+
182
+ Both `import_server()` and `mount()` allow you to customize the separators used for prefixing components. The defaults are `_` for tools and prompts, and `+` for resources.
183
+
184
+ <CodeGroup>
185
+
186
+ ```python import_server
187
+ await main_mcp.import_server(
188
+ prefix="api",
189
+ app=some_subserver,
190
+ tool_separator="_", # Tool name becomes: "api_sub_tool_name"
191
+ resource_separator="+", # Resource URI becomes: "api+data://sub_resource"
192
+ prompt_separator="_" # Prompt name becomes: "api_sub_prompt_name"
193
+ )
194
+ ```
195
+
196
+ ```python mount
197
+ main_mcp.mount(
198
+ prefix="api",
199
+ app=some_subserver,
200
+ tool_separator="_", # Tool name becomes: "api_sub_tool_name"
201
+ resource_separator="+", # Resource URI becomes: "api+data://sub_resource"
202
+ prompt_separator="_" # Prompt name becomes: "api_sub_prompt_name"
203
+ )
204
+ ```
205
+ </CodeGroup>
206
+ <Warning>
207
+ Be cautious when choosing separators. Some MCP clients (like Claude Desktop) might have restrictions on characters allowed in tool names (e.g., `/` might not be supported). The defaults (`_` for names, `+` for URIs) are generally safe.
208
+ </Warning>
209
+
210
+ <Tip>
211
+ To "cleanly" import or mount a server, set the prefix and all separators to `""` (empty string). This is generally unecessary but could save a couple tokens at the risk of a name collision!
212
+ </Tip>
@@ -147,6 +147,7 @@ async def read_important_log() -> str:
147
147
  return "Log file not found."
148
148
  ```
149
149
 
150
+
150
151
  ### Resource Classes
151
152
 
152
153
  While `@mcp.resource` is ideal for dynamic content, you can directly register pre-defined resources (like static files or simple text) using `mcp.add_resource()` and concrete `Resource` subclasses.
@@ -403,17 +404,45 @@ In this stacked decorator pattern:
403
404
  - Each parameter defaults to `None` when not included in the URI
404
405
  - The function logic handles whichever parameter is provided
405
406
 
406
- **How Templates Work:**
407
+ Templates provide a powerful way to expose parameterized data access points following REST-like principles.
408
+
409
+ ## Error Handling
407
410
 
408
- 1. **Definition:** When FastMCP sees `{...}` placeholders in the `@resource` URI and matching function parameters, it registers a `ResourceTemplate`.
409
- 2. **Discovery:** Clients list templates via `resources/listResourceTemplates`.
410
- 3. **Request & Matching:** A client requests a specific URI, e.g., `weather://london/current`. FastMCP matches this to the `weather://{city}/current` template.
411
- 4. **Parameter Extraction:** It extracts the parameter value: `city="london"`.
412
- 5. **Type Conversion & Function Call:** It converts extracted values to the types hinted in the function and calls `get_weather(city="london")`.
413
- 6. **Default Values:** For any function parameters with default values not included in the URI template, FastMCP uses the default values.
414
- 7. **Response:** The function's return value is formatted (e.g., dict to JSON) and sent back as the resource content.
411
+ <VersionBadge version="2.3.4" />
415
412
 
416
- Templates provide a powerful way to expose parameterized data access points following REST-like principles.
413
+ If your resource function encounters an error, you can raise a standard Python exception (`ValueError`, `TypeError`, `FileNotFoundError`, custom exceptions, etc.) or a FastMCP `ResourceError`.
414
+
415
+ For security reasons, most exceptions are wrapped in a generic `ResourceError` before being sent to the client, with internal error details masked. However, if you raise a `ResourceError` directly, its contents **are** included in the response. This allows you to provide informative error messages to the client on an opt-in basis.
416
+
417
+ ```python
418
+ from fastmcp import FastMCP
419
+ from fastmcp.exceptions import ResourceError
420
+
421
+ mcp = FastMCP(name="DataServer")
422
+
423
+ @mcp.resource("resource://safe-error")
424
+ def fail_with_details() -> str:
425
+ """This resource provides detailed error information."""
426
+ # ResourceError contents are sent back to clients
427
+ raise ResourceError("Unable to retrieve data: file not found")
428
+
429
+ @mcp.resource("resource://masked-error")
430
+ def fail_with_masked_details() -> str:
431
+ """This resource masks internal error details."""
432
+ # Other exceptions are converted to ResourceError with generic message
433
+ raise ValueError("Sensitive internal file path: /etc/secrets.conf")
434
+
435
+ @mcp.resource("data://{id}")
436
+ def get_data_by_id(id: str) -> dict:
437
+ """Template resources also support the same error handling pattern."""
438
+ if id == "secure":
439
+ raise ValueError("Cannot access secure data")
440
+ elif id == "missing":
441
+ raise ResourceError("Data ID 'missing' not found in database")
442
+ return {"id": id, "value": "data"}
443
+ ```
444
+
445
+ This error handling pattern applies to both regular resources and resource templates.
417
446
 
418
447
  ## Server Behavior
419
448
 
@@ -248,27 +248,30 @@ def do_nothing() -> None:
248
248
 
249
249
  ### Error Handling
250
250
 
251
- If your tool encounters an error, simply raise a standard Python exception (`ValueError`, `TypeError`, `FileNotFoundError`, custom exceptions, etc.).
251
+ <VersionBadge version="2.3.4" />
252
+
253
+ If your tool encounters an error, you can raise a standard Python exception (`ValueError`, `TypeError`, `FileNotFoundError`, custom exceptions, etc.) or a FastMCP `ToolError`.
254
+
255
+ In all cases, the exception is logged and converted into an MCP error response to be sent back to the client LLM. For security reasons, the error message is **not** included in the response by default. However, if you raise a `ToolError`, the contents of the exception **are** included in the response. This allows you to provide informative error messages to the client LLM on an opt-in basis, which can help the LLM understand failures and react appropriately.
256
+
257
+ ```python {2, 10, 14}
258
+ from fastmcp import FastMCP
259
+ from fastmcp.exceptions import ToolError
252
260
 
253
- ```python
254
261
  @mcp.tool()
255
262
  def divide(a: float, b: float) -> float:
256
263
  """Divide a by b."""
257
- if b == 0:
258
- # Raise a standard exception
259
- raise ValueError("Division by zero is not allowed.")
264
+
265
+ # Python exceptions raise errors but the contents are not sent to clients
260
266
  if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
261
267
  raise TypeError("Both arguments must be numbers.")
268
+
269
+ if b == 0:
270
+ # ToolError contents are sent back to clients
271
+ raise ToolError("Division by zero is not allowed.")
262
272
  return a / b
263
273
  ```
264
274
 
265
- FastMCP automatically catches exceptions raised within your tool function:
266
- 1. It converts the exception into an MCP error response, typically including the exception type and message.
267
- 2. This error response is sent back to the client/LLM.
268
- 3. The LLM can then inform the user or potentially try the tool again with different arguments.
269
-
270
- Using informative exceptions helps the LLM understand failures and react appropriately.
271
-
272
275
  ### Annotations
273
276
 
274
277
  <VersionBadge version="2.2.7" />
@@ -709,6 +712,25 @@ The duplicate behavior options are:
709
712
  - `"replace"`: Silently replaces the existing tool with the new one.
710
713
  - `"ignore"`: Keeps the original tool and ignores the new registration attempt.
711
714
 
715
+ ### Removing Tools
716
+
717
+ <VersionBadge version="2.3.4" />
718
+
719
+ You can dynamically remove tools from a server using the `remove_tool` method:
720
+
721
+ ```python
722
+ from fastmcp import FastMCP
723
+
724
+ mcp = FastMCP(name="DynamicToolServer")
725
+
726
+ @mcp.tool()
727
+ def calculate_sum(a: int, b: int) -> int:
728
+ """Add two numbers together."""
729
+ return a + b
730
+
731
+ mcp.remove_tool("calculate_sum")
732
+ ```
733
+
712
734
  ### Legacy JSON Parsing
713
735
 
714
736
  <VersionBadge version="2.2.10" />
@@ -8,7 +8,7 @@ from typing import Annotated
8
8
 
9
9
  from pydantic import BaseModel, Field
10
10
 
11
- from fastmcp.server import FastMCP
11
+ from fastmcp import FastMCP
12
12
 
13
13
  mcp = FastMCP("Shrimp Tank")
14
14
 
@@ -6,7 +6,7 @@ A simple example that exposes the desktop directory as a resource.
6
6
 
7
7
  from pathlib import Path
8
8
 
9
- from fastmcp.server import FastMCP
9
+ from fastmcp import FastMCP
10
10
 
11
11
  # Create server
12
12
  mcp = FastMCP("Demo")
@@ -7,7 +7,7 @@ dependencies = [
7
7
  "python-dotenv>=1.1.0",
8
8
  "exceptiongroup>=1.2.2",
9
9
  "httpx>=0.28.1",
10
- "mcp>=1.8.0,<2.0.0",
10
+ "mcp>=1.8.1,<2.0.0",
11
11
  "openapi-pydantic>=0.5.1",
12
12
  "rich>=13.9.4",
13
13
  "typer>=0.15.2",
@@ -9,6 +9,7 @@ from .transports import (
9
9
  UvxStdioTransport,
10
10
  NpxStdioTransport,
11
11
  FastMCPTransport,
12
+ StreamableHttpTransport,
12
13
  )
13
14
 
14
15
  __all__ = [
@@ -22,4 +23,5 @@ __all__ = [
22
23
  "UvxStdioTransport",
23
24
  "NpxStdioTransport",
24
25
  "FastMCPTransport",
26
+ "StreamableHttpTransport",
25
27
  ]