fastmcp 2.2.1__tar.gz → 2.2.2__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 (144) hide show
  1. {fastmcp-2.2.1 → fastmcp-2.2.2}/PKG-INFO +1 -1
  2. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/clients/client.mdx +9 -0
  3. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/docs.json +2 -1
  4. fastmcp-2.2.2/docs/patterns/contrib.mdx +42 -0
  5. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/cli/cli.py +1 -1
  6. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/client.py +14 -21
  7. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/transports.py +2 -1
  8. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/README.md +11 -1
  9. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/bulk_tool_caller.py +5 -1
  10. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/example.py +1 -1
  11. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/README.md +1 -1
  12. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/example.py +2 -2
  13. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/proxy.py +34 -4
  14. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/test_client.py +30 -0
  15. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/contrib/test_bulk_tool_caller.py +2 -2
  16. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/contrib/test_mcp_mixin.py +3 -3
  17. {fastmcp-2.2.1 → fastmcp-2.2.2}/.cursor/rules/core-mcp-objects.mdc +0 -0
  18. {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  19. {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  20. {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/ISSUE_TEMPLATE/enhancement.yml +0 -0
  21. {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/release.yml +0 -0
  22. {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/workflows/publish.yml +0 -0
  23. {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/workflows/run-static.yml +0 -0
  24. {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/workflows/run-tests.yml +0 -0
  25. {fastmcp-2.2.1 → fastmcp-2.2.2}/.gitignore +0 -0
  26. {fastmcp-2.2.1 → fastmcp-2.2.2}/.pre-commit-config.yaml +0 -0
  27. {fastmcp-2.2.1 → fastmcp-2.2.2}/LICENSE +0 -0
  28. {fastmcp-2.2.1 → fastmcp-2.2.2}/README.md +0 -0
  29. {fastmcp-2.2.1 → fastmcp-2.2.2}/Windows_Notes.md +0 -0
  30. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/assets/demo-inspector.png +0 -0
  31. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/clients/transports.mdx +0 -0
  32. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/getting-started/installation.mdx +0 -0
  33. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/getting-started/quickstart.mdx +0 -0
  34. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/getting-started/welcome.mdx +0 -0
  35. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/composition.mdx +0 -0
  36. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/decorating-methods.mdx +0 -0
  37. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/fastapi.mdx +0 -0
  38. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/openapi.mdx +0 -0
  39. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/proxy.mdx +0 -0
  40. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/context.mdx +0 -0
  41. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/fastmcp.mdx +0 -0
  42. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/prompts.mdx +0 -0
  43. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/resources.mdx +0 -0
  44. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/tools.mdx +0 -0
  45. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/snippets/version-badge.mdx +0 -0
  46. {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/style.css +0 -0
  47. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/complex_inputs.py +0 -0
  48. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/desktop.py +0 -0
  49. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/echo.py +0 -0
  50. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/memory.py +0 -0
  51. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/mount_example.py +0 -0
  52. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/readme-quickstart.py +0 -0
  53. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/sampling.py +0 -0
  54. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/screenshot.py +0 -0
  55. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/simple_echo.py +0 -0
  56. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/README.md +0 -0
  57. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/pyproject.toml +0 -0
  58. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/__init__.py +0 -0
  59. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/__main__.py +0 -0
  60. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/hub.py +0 -0
  61. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/__init__.py +0 -0
  62. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/hue_utils.py +0 -0
  63. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/server.py +0 -0
  64. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/py.typed +0 -0
  65. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/settings.py +0 -0
  66. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/uv.lock +0 -0
  67. {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/text_me.py +0 -0
  68. {fastmcp-2.2.1 → fastmcp-2.2.2}/justfile +0 -0
  69. {fastmcp-2.2.1 → fastmcp-2.2.2}/pyproject.toml +0 -0
  70. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/__init__.py +0 -0
  71. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/cli/__init__.py +0 -0
  72. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/cli/claude.py +0 -0
  73. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/__init__.py +0 -0
  74. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/base.py +0 -0
  75. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/roots.py +0 -0
  76. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/sampling.py +0 -0
  77. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/README.md +0 -0
  78. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/__init__.py +0 -0
  79. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/__init__.py +0 -0
  80. {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/mcp_mixin.py +0 -0
  81. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/exceptions.py +0 -0
  82. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/prompts/__init__.py +0 -0
  83. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/prompts/prompt.py +0 -0
  84. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/prompts/prompt_manager.py +0 -0
  85. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/py.typed +0 -0
  86. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/__init__.py +0 -0
  87. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/resource.py +0 -0
  88. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/resource_manager.py +0 -0
  89. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/template.py +0 -0
  90. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/types.py +0 -0
  91. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/__init__.py +0 -0
  92. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/context.py +0 -0
  93. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/openapi.py +0 -0
  94. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/server.py +0 -0
  95. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/settings.py +0 -0
  96. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/tools/__init__.py +0 -0
  97. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/tools/tool.py +0 -0
  98. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/tools/tool_manager.py +0 -0
  99. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/__init__.py +0 -0
  100. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/decorators.py +0 -0
  101. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/func_metadata.py +0 -0
  102. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/logging.py +0 -0
  103. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/openapi.py +0 -0
  104. {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/types.py +0 -0
  105. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/__init__.py +0 -0
  106. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/cli/test_run.py +0 -0
  107. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/__init__.py +0 -0
  108. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/test_roots.py +0 -0
  109. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/test_sampling.py +0 -0
  110. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/conftest.py +0 -0
  111. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/contrib/__init__.py +0 -0
  112. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/prompts/__init__.py +0 -0
  113. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/prompts/test_base.py +0 -0
  114. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/prompts/test_prompt_manager.py +0 -0
  115. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/__init__.py +0 -0
  116. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_file_resources.py +0 -0
  117. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_function_resources.py +0 -0
  118. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_resource_manager.py +0 -0
  119. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_resource_template.py +0 -0
  120. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_resources.py +0 -0
  121. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/__init__.py +0 -0
  122. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_file_server.py +0 -0
  123. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_import_server.py +0 -0
  124. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_lifespan.py +0 -0
  125. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_mount.py +0 -0
  126. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_openapi.py +0 -0
  127. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_proxy.py +0 -0
  128. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_run_server.py +0 -0
  129. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_server.py +0 -0
  130. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/test_servers/fastmcp_server.py +0 -0
  131. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/test_servers/sse.py +0 -0
  132. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/test_servers/stdio.py +0 -0
  133. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/tools/__init__.py +0 -0
  134. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/tools/test_tool_manager.py +0 -0
  135. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/__init__.py +0 -0
  136. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/__init__.py +0 -0
  137. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/conftest.py +0 -0
  138. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi.py +0 -0
  139. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi_advanced.py +0 -0
  140. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi_fastapi.py +0 -0
  141. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/test_decorated_function.py +0 -0
  142. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/test_func_metadata.py +0 -0
  143. {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/test_logging.py +0 -0
  144. {fastmcp-2.2.1 → fastmcp-2.2.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.2.1
3
+ Version: 2.2.2
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
@@ -226,6 +226,15 @@ MCP allows servers to make requests *back* to the client for certain capabilitie
226
226
  )
227
227
  ```
228
228
 
229
+ ### Utility Methods
230
+
231
+ * **`ping()`**: Sends a ping request to the server to verify connectivity.
232
+ ```python
233
+ async def check_connection():
234
+ async with client:
235
+ await client.ping()
236
+ print("Server is reachable")
237
+ ```
229
238
 
230
239
  ### Error Handling
231
240
 
@@ -61,7 +61,8 @@
61
61
  "patterns/composition",
62
62
  "patterns/decorating-methods",
63
63
  "patterns/openapi",
64
- "patterns/fastapi"
64
+ "patterns/fastapi",
65
+ "patterns/contrib"
65
66
  ]
66
67
  },
67
68
  {
@@ -0,0 +1,42 @@
1
+ ---
2
+ title: "Contrib Modules"
3
+ description: "Community-contributed modules extending FastMCP"
4
+ icon: "cubes"
5
+ ---
6
+
7
+
8
+ 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
+
10
+ Contrib modules provide additional features, integrations, or patterns that complement the core FastMCP library. They offer a way for the community to share useful extensions while keeping the core library focused and maintainable.
11
+
12
+ The available modules can be viewed in the [contrib directory](https://github.com/jlowin/fastmcp/tree/main/src/contrib).
13
+
14
+ ## Usage
15
+
16
+ To use a contrib module, import it from the `fastmcp.contrib` package:
17
+
18
+ ```python
19
+ from fastmcp.contrib import my_module
20
+ ```
21
+
22
+ ## Important Considerations
23
+
24
+ - **Stability**: Modules in `contrib` may have different testing requirements or stability guarantees compared to the core library.
25
+ - **Compatibility**: Changes to core FastMCP might break modules in `contrib` without explicit warnings in the main changelog.
26
+ - **Dependencies**: Contrib modules may have additional dependencies not required by the core library. These dependencies are typically documented in the module's README or separate requirements files.
27
+
28
+ ## Contributing
29
+
30
+ We welcome contributions to the `contrib` package! If you have a module that extends FastMCP in a useful way, consider contributing it:
31
+
32
+ 1. Create a new directory in `src/fastmcp/contrib/` for your module
33
+ 3. Add proper tests for your module in `tests/contrib/`
34
+ 2. Include comprehensive documentation in a README.md file, including usage and examples, as well as any additional dependencies or installation instructions
35
+ 5. Submit a pull request
36
+
37
+ The ideal contrib module:
38
+ - Solves a specific use case or integration need
39
+ - Follows FastMCP coding standards
40
+ - Includes thorough documentation and examples
41
+ - Has comprehensive tests
42
+ - Specifies any additional dependencies
@@ -186,7 +186,7 @@ def version(ctx: Context):
186
186
  "MCP version": importlib.metadata.version("mcp"),
187
187
  "Python version": platform.python_version(),
188
188
  "Platform": platform.platform(),
189
- "FastMCP root path": f"~/{Path(__file__).resolve().parents[3].relative_to(Path.home())}",
189
+ "FastMCP root path": Path(fastmcp.__file__).resolve().parents[1],
190
190
  }
191
191
 
192
192
  g = Table.grid(padding=(0, 1))
@@ -45,7 +45,8 @@ class Client:
45
45
  ):
46
46
  self.transport = infer_transport(transport)
47
47
  self._session: ClientSession | None = None
48
- self._session_cms: list[AbstractAsyncContextManager[ClientSession]] = []
48
+ self._session_cm: AbstractAsyncContextManager[ClientSession] | None = None
49
+ self._nesting_counter: int = 0
49
50
 
50
51
  self._session_kwargs: SessionKwargs = {
51
52
  "sampling_callback": None,
@@ -85,29 +86,21 @@ class Client:
85
86
  return self._session is not None
86
87
 
87
88
  async def __aenter__(self):
88
- if self.is_connected():
89
- # We're already connected, no need to add None to the session_cms list
90
- return self
91
-
92
- try:
93
- session_cm = self.transport.connect_session(**self._session_kwargs)
94
- self._session_cms.append(session_cm)
95
- self._session = await self._session_cms[-1].__aenter__()
96
- return self
97
- except Exception as e:
98
- # Ensure cleanup if __aenter__ fails partially
99
- self._session = None
100
- if self._session_cms:
101
- self._session_cms.pop()
102
- raise ConnectionError(
103
- f"Failed to connect using {self.transport}: {e}"
104
- ) from e
89
+ if self._nesting_counter == 0:
90
+ # create new session
91
+ self._session_cm = self.transport.connect_session(**self._session_kwargs)
92
+ self._session = await self._session_cm.__aenter__()
93
+
94
+ self._nesting_counter += 1
95
+ return self
105
96
 
106
97
  async def __aexit__(self, exc_type, exc_val, exc_tb):
107
- if self._session_cms:
108
- await self._session_cms[-1].__aexit__(exc_type, exc_val, exc_tb)
98
+ self._nesting_counter -= 1
99
+
100
+ if self._nesting_counter == 0 and self._session_cm is not None:
101
+ await self._session_cm.__aexit__(exc_type, exc_val, exc_tb)
102
+ self._session_cm = None
109
103
  self._session = None
110
- self._session_cms.pop()
111
104
 
112
105
  # --- MCP Client Methods ---
113
106
  async def ping(self) -> None:
@@ -3,6 +3,7 @@ import contextlib
3
3
  import datetime
4
4
  import os
5
5
  import shutil
6
+ import sys
6
7
  from collections.abc import AsyncIterator
7
8
  from pathlib import Path
8
9
  from typing import (
@@ -185,7 +186,7 @@ class PythonStdioTransport(StdioTransport):
185
186
  args: list[str] | None = None,
186
187
  env: dict[str, str] | None = None,
187
188
  cwd: str | None = None,
188
- python_cmd: str = "python",
189
+ python_cmd: str = sys.executable,
189
190
  ):
190
191
  """
191
192
  Initialize a Python transport.
@@ -6,4 +6,14 @@ This directory holds community-contributed modules for FastMCP. These modules ex
6
6
  * Modules in `contrib` may have different testing requirements or stability guarantees compared to the core library.
7
7
  * Changes to the core FastMCP library might break modules in `contrib` without explicit warnings in the main changelog.
8
8
 
9
- Use these modules at your own discretion. Contributions are welcome, but please include tests and documentation.
9
+ Use these modules at your own discretion. Contributions are welcome, but please include tests and documentation.
10
+
11
+ ## Usage
12
+
13
+ To use a contrib module, import it from the `fastmcp.contrib` package.
14
+
15
+ ```python
16
+ from fastmcp.contrib import my_module
17
+ ```
18
+
19
+ Note that the contrib modules may have different dependencies than the core library, which can be noted in their respective README's or even separate requirements / dependency files.
@@ -3,10 +3,14 @@ from typing import Any
3
3
  from mcp.types import CallToolResult
4
4
  from pydantic import BaseModel, Field
5
5
 
6
- from contrib.mcp_mixin.mcp_mixin import _DEFAULT_SEPARATOR_TOOL, MCPMixin, mcp_tool
7
6
  from fastmcp import FastMCP
8
7
  from fastmcp.client import Client
9
8
  from fastmcp.client.transports import FastMCPTransport
9
+ from fastmcp.contrib.mcp_mixin.mcp_mixin import (
10
+ _DEFAULT_SEPARATOR_TOOL,
11
+ MCPMixin,
12
+ mcp_tool,
13
+ )
10
14
 
11
15
 
12
16
  class CallToolRequest(BaseModel):
@@ -1,7 +1,7 @@
1
1
  """Sample code for FastMCP using MCPMixin."""
2
2
 
3
- from contrib.bulk_tool_caller import BulkToolCaller
4
3
  from fastmcp import FastMCP
4
+ from fastmcp.contrib.bulk_tool_caller import BulkToolCaller
5
5
 
6
6
  mcp = FastMCP()
7
7
 
@@ -10,7 +10,7 @@ Inherit from `MCPMixin` and use the decorators on the methods you want to regist
10
10
 
11
11
  ```python
12
12
  from fastmcp import FastMCP
13
- from contrib.mcp_mixin import MCPMixin, mcp_tool, mcp_resource
13
+ from fastmcp.contrib.mcp_mixin import MCPMixin, mcp_tool, mcp_resource
14
14
 
15
15
  class MyComponent(MCPMixin):
16
16
  @mcp_tool(name="my_tool", description="Does something cool.")
@@ -2,13 +2,13 @@
2
2
 
3
3
  import asyncio
4
4
 
5
- from contrib.mcp_mixin import (
5
+ from fastmcp import FastMCP
6
+ from fastmcp.contrib.mcp_mixin import (
6
7
  MCPMixin,
7
8
  mcp_prompt,
8
9
  mcp_resource,
9
10
  mcp_tool,
10
11
  )
11
- from fastmcp import FastMCP
12
12
 
13
13
  mcp = FastMCP()
14
14
 
@@ -3,7 +3,9 @@ from urllib.parse import quote
3
3
 
4
4
  import mcp.types
5
5
  from mcp.server.lowlevel.helper_types import ReadResourceContents
6
+ from mcp.shared.exceptions import McpError
6
7
  from mcp.types import (
8
+ METHOD_NOT_FOUND,
7
9
  BlobResourceContents,
8
10
  EmbeddedResource,
9
11
  GetPromptResult,
@@ -173,7 +175,14 @@ class FastMCPProxy(FastMCP):
173
175
  tools = await super().get_tools()
174
176
 
175
177
  async with self.client:
176
- for tool in await self.client.list_tools():
178
+ try:
179
+ client_tools = await self.client.list_tools()
180
+ except McpError as e:
181
+ if e.error.code == METHOD_NOT_FOUND:
182
+ client_tools = []
183
+ else:
184
+ raise e
185
+ for tool in client_tools:
177
186
  tool_proxy = await ProxyTool.from_client(self.client, tool)
178
187
  tools[tool_proxy.name] = tool_proxy
179
188
 
@@ -183,7 +192,14 @@ class FastMCPProxy(FastMCP):
183
192
  resources = await super().get_resources()
184
193
 
185
194
  async with self.client:
186
- for resource in await self.client.list_resources():
195
+ try:
196
+ client_resources = await self.client.list_resources()
197
+ except McpError as e:
198
+ if e.error.code == METHOD_NOT_FOUND:
199
+ client_resources = []
200
+ else:
201
+ raise e
202
+ for resource in client_resources:
187
203
  resource_proxy = await ProxyResource.from_client(self.client, resource)
188
204
  resources[str(resource_proxy.uri)] = resource_proxy
189
205
 
@@ -193,7 +209,14 @@ class FastMCPProxy(FastMCP):
193
209
  templates = await super().get_resource_templates()
194
210
 
195
211
  async with self.client:
196
- for template in await self.client.list_resource_templates():
212
+ try:
213
+ client_templates = await self.client.list_resource_templates()
214
+ except McpError as e:
215
+ if e.error.code == METHOD_NOT_FOUND:
216
+ client_templates = []
217
+ else:
218
+ raise e
219
+ for template in client_templates:
197
220
  template_proxy = await ProxyTemplate.from_client(self.client, template)
198
221
  templates[template_proxy.uri_template] = template_proxy
199
222
 
@@ -203,7 +226,14 @@ class FastMCPProxy(FastMCP):
203
226
  prompts = await super().get_prompts()
204
227
 
205
228
  async with self.client:
206
- for prompt in await self.client.list_prompts():
229
+ try:
230
+ client_prompts = await self.client.list_prompts()
231
+ except McpError as e:
232
+ if e.error.code == METHOD_NOT_FOUND:
233
+ client_prompts = []
234
+ else:
235
+ raise e
236
+ for prompt in client_prompts:
207
237
  prompt_proxy = await ProxyPrompt.from_client(self.client, prompt)
208
238
  prompts[prompt_proxy.name] = prompt_proxy
209
239
  return prompts
@@ -160,6 +160,36 @@ async def test_client_connection(fastmcp_server):
160
160
  assert not client.is_connected()
161
161
 
162
162
 
163
+ async def test_client_nested_context_manager(fastmcp_server):
164
+ """Test that the client connects and disconnects once in nested context manager."""
165
+
166
+ client = Client(fastmcp_server)
167
+
168
+ # Before connection
169
+ assert not client.is_connected()
170
+ assert client._session is None
171
+
172
+ # During connection
173
+ async with client:
174
+ assert client.is_connected()
175
+ assert client._session is not None
176
+ session = client._session
177
+
178
+ # Re-use the same session
179
+ async with client:
180
+ assert client.is_connected()
181
+ assert client._session is session
182
+
183
+ # Re-use the same session
184
+ async with client:
185
+ assert client.is_connected()
186
+ assert client._session is session
187
+
188
+ # After connection
189
+ assert not client.is_connected()
190
+ assert client._session is None
191
+
192
+
163
193
  async def test_resource_template(fastmcp_server):
164
194
  """Test using a resource template with InMemoryClient."""
165
195
  client = Client(transport=FastMCPTransport(fastmcp_server))
@@ -3,12 +3,12 @@ from typing import Any
3
3
  import pytest
4
4
  from mcp.types import EmbeddedResource, ImageContent, TextContent
5
5
 
6
- from contrib.bulk_tool_caller.bulk_tool_caller import (
6
+ from fastmcp import FastMCP
7
+ from fastmcp.contrib.bulk_tool_caller.bulk_tool_caller import (
7
8
  BulkToolCaller,
8
9
  CallToolRequest,
9
10
  CallToolRequestResult,
10
11
  )
11
- from fastmcp import FastMCP
12
12
 
13
13
  ContentType = TextContent | ImageContent | EmbeddedResource
14
14
 
@@ -2,18 +2,18 @@
2
2
 
3
3
  import pytest
4
4
 
5
- from contrib.mcp_mixin import (
5
+ from fastmcp import FastMCP
6
+ from fastmcp.contrib.mcp_mixin import (
6
7
  MCPMixin,
7
8
  mcp_prompt,
8
9
  mcp_resource,
9
10
  mcp_tool,
10
11
  )
11
- from contrib.mcp_mixin.mcp_mixin import (
12
+ from fastmcp.contrib.mcp_mixin.mcp_mixin import (
12
13
  _DEFAULT_SEPARATOR_PROMPT,
13
14
  _DEFAULT_SEPARATOR_RESOURCE,
14
15
  _DEFAULT_SEPARATOR_TOOL,
15
16
  )
16
- from fastmcp import FastMCP
17
17
 
18
18
 
19
19
  class TestMCPMixin:
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
File without changes