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.
- {fastmcp-2.2.1 → fastmcp-2.2.2}/PKG-INFO +1 -1
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/clients/client.mdx +9 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/docs.json +2 -1
- fastmcp-2.2.2/docs/patterns/contrib.mdx +42 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/cli/cli.py +1 -1
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/client.py +14 -21
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/transports.py +2 -1
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/README.md +11 -1
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/bulk_tool_caller.py +5 -1
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/example.py +1 -1
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/README.md +1 -1
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/example.py +2 -2
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/proxy.py +34 -4
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/test_client.py +30 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/contrib/test_bulk_tool_caller.py +2 -2
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/contrib/test_mcp_mixin.py +3 -3
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.cursor/rules/core-mcp-objects.mdc +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/ISSUE_TEMPLATE/enhancement.yml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/release.yml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/workflows/publish.yml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/workflows/run-static.yml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.github/workflows/run-tests.yml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.gitignore +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/.pre-commit-config.yaml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/LICENSE +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/README.md +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/Windows_Notes.md +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/assets/demo-inspector.png +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/clients/transports.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/getting-started/installation.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/getting-started/quickstart.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/getting-started/welcome.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/composition.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/decorating-methods.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/fastapi.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/openapi.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/patterns/proxy.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/context.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/fastmcp.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/prompts.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/resources.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/servers/tools.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/snippets/version-badge.mdx +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/docs/style.css +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/complex_inputs.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/desktop.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/echo.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/memory.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/mount_example.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/readme-quickstart.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/sampling.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/screenshot.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/simple_echo.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/README.md +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/pyproject.toml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/__main__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/hub.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/hue_utils.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/server.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/py.typed +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/settings.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/smart_home/uv.lock +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/examples/text_me.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/justfile +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/pyproject.toml +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/cli/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/cli/claude.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/base.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/roots.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/client/sampling.py +0 -0
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/README.md +0 -0
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/__init__.py +0 -0
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/__init__.py +0 -0
- {fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/mcp_mixin/mcp_mixin.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/exceptions.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/prompts/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/prompts/prompt.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/prompts/prompt_manager.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/py.typed +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/resource.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/resource_manager.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/template.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/resources/types.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/context.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/openapi.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/server/server.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/settings.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/tools/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/tools/tool.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/tools/tool_manager.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/decorators.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/func_metadata.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/logging.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/openapi.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/src/fastmcp/utilities/types.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/cli/test_run.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/test_roots.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/client/test_sampling.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/conftest.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/contrib/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/prompts/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/prompts/test_base.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/prompts/test_prompt_manager.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_file_resources.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_function_resources.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_resource_manager.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_resource_template.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/resources/test_resources.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_file_server.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_import_server.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_lifespan.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_mount.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_openapi.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_proxy.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_run_server.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/server/test_server.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/test_servers/fastmcp_server.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/test_servers/sse.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/test_servers/stdio.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/tools/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/tools/test_tool_manager.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/__init__.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/conftest.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi_advanced.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi_fastapi.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/test_decorated_function.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/test_func_metadata.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/tests/utilities/test_logging.py +0 -0
- {fastmcp-2.2.1 → fastmcp-2.2.2}/uv.lock +0 -0
|
@@ -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
|
|
|
@@ -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":
|
|
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.
|
|
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.
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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 =
|
|
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.
|
{fastmcp-2.2.1/src → fastmcp-2.2.2/src/fastmcp}/contrib/bulk_tool_caller/bulk_tool_caller.py
RENAMED
|
@@ -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):
|
|
@@ -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.")
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|