fastmcp 2.2.0__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 (145) hide show
  1. fastmcp-2.2.2/.github/ISSUE_TEMPLATE/bug.yml +63 -0
  2. fastmcp-2.2.2/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. fastmcp-2.2.2/.github/ISSUE_TEMPLATE/enhancement.yml +38 -0
  4. {fastmcp-2.2.0 → fastmcp-2.2.2}/.github/workflows/run-tests.yml +0 -1
  5. {fastmcp-2.2.0 → fastmcp-2.2.2}/.pre-commit-config.yaml +0 -1
  6. {fastmcp-2.2.0 → fastmcp-2.2.2}/PKG-INFO +9 -2
  7. {fastmcp-2.2.0 → fastmcp-2.2.2}/README.md +7 -0
  8. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/clients/client.mdx +9 -0
  9. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/docs.json +2 -1
  10. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/getting-started/quickstart.mdx +3 -3
  11. fastmcp-2.2.2/docs/patterns/contrib.mdx +42 -0
  12. {fastmcp-2.2.0 → fastmcp-2.2.2}/pyproject.toml +1 -1
  13. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/cli/cli.py +1 -1
  14. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/client/client.py +14 -21
  15. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/client/transports.py +2 -1
  16. fastmcp-2.2.2/src/fastmcp/contrib/README.md +19 -0
  17. fastmcp-2.2.2/src/fastmcp/contrib/bulk_tool_caller/README.md +35 -0
  18. fastmcp-2.2.2/src/fastmcp/contrib/bulk_tool_caller/__init__.py +3 -0
  19. fastmcp-2.2.2/src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py +135 -0
  20. fastmcp-2.2.2/src/fastmcp/contrib/bulk_tool_caller/example.py +17 -0
  21. fastmcp-2.2.2/src/fastmcp/contrib/mcp_mixin/README.md +39 -0
  22. fastmcp-2.2.2/src/fastmcp/contrib/mcp_mixin/__init__.py +8 -0
  23. fastmcp-2.2.2/src/fastmcp/contrib/mcp_mixin/example.py +52 -0
  24. fastmcp-2.2.2/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py +208 -0
  25. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/server/proxy.py +34 -4
  26. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/server/server.py +3 -1
  27. fastmcp-2.2.2/tests/cli/test_run.py +22 -0
  28. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/client/test_client.py +30 -0
  29. fastmcp-2.2.2/tests/contrib/__init__.py +1 -0
  30. fastmcp-2.2.2/tests/contrib/test_bulk_tool_caller.py +221 -0
  31. fastmcp-2.2.2/tests/contrib/test_mcp_mixin.py +255 -0
  32. {fastmcp-2.2.0 → fastmcp-2.2.2}/uv.lock +5 -16
  33. fastmcp-2.2.0/tests/cli/test_run.py +0 -77
  34. {fastmcp-2.2.0 → fastmcp-2.2.2}/.cursor/rules/core-mcp-objects.mdc +0 -0
  35. {fastmcp-2.2.0 → fastmcp-2.2.2}/.github/release.yml +0 -0
  36. {fastmcp-2.2.0 → fastmcp-2.2.2}/.github/workflows/publish.yml +0 -0
  37. {fastmcp-2.2.0 → fastmcp-2.2.2}/.github/workflows/run-static.yml +0 -0
  38. {fastmcp-2.2.0 → fastmcp-2.2.2}/.gitignore +0 -0
  39. {fastmcp-2.2.0 → fastmcp-2.2.2}/LICENSE +0 -0
  40. {fastmcp-2.2.0 → fastmcp-2.2.2}/Windows_Notes.md +0 -0
  41. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/assets/demo-inspector.png +0 -0
  42. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/clients/transports.mdx +0 -0
  43. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/getting-started/installation.mdx +0 -0
  44. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/getting-started/welcome.mdx +0 -0
  45. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/patterns/composition.mdx +0 -0
  46. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/patterns/decorating-methods.mdx +0 -0
  47. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/patterns/fastapi.mdx +0 -0
  48. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/patterns/openapi.mdx +0 -0
  49. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/patterns/proxy.mdx +0 -0
  50. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/servers/context.mdx +0 -0
  51. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/servers/fastmcp.mdx +0 -0
  52. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/servers/prompts.mdx +0 -0
  53. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/servers/resources.mdx +0 -0
  54. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/servers/tools.mdx +0 -0
  55. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/snippets/version-badge.mdx +0 -0
  56. {fastmcp-2.2.0 → fastmcp-2.2.2}/docs/style.css +0 -0
  57. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/complex_inputs.py +0 -0
  58. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/desktop.py +0 -0
  59. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/echo.py +0 -0
  60. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/memory.py +0 -0
  61. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/mount_example.py +0 -0
  62. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/readme-quickstart.py +0 -0
  63. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/sampling.py +0 -0
  64. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/screenshot.py +0 -0
  65. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/simple_echo.py +0 -0
  66. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/README.md +0 -0
  67. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/pyproject.toml +0 -0
  68. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/__init__.py +0 -0
  69. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/__main__.py +0 -0
  70. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/hub.py +0 -0
  71. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/__init__.py +0 -0
  72. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/hue_utils.py +0 -0
  73. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/lights/server.py +0 -0
  74. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/py.typed +0 -0
  75. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/src/smart_home/settings.py +0 -0
  76. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/smart_home/uv.lock +0 -0
  77. {fastmcp-2.2.0 → fastmcp-2.2.2}/examples/text_me.py +0 -0
  78. {fastmcp-2.2.0 → fastmcp-2.2.2}/justfile +0 -0
  79. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/__init__.py +0 -0
  80. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/cli/__init__.py +0 -0
  81. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/cli/claude.py +0 -0
  82. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/client/__init__.py +0 -0
  83. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/client/base.py +0 -0
  84. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/client/roots.py +0 -0
  85. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/client/sampling.py +0 -0
  86. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/exceptions.py +0 -0
  87. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/prompts/__init__.py +0 -0
  88. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/prompts/prompt.py +0 -0
  89. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/prompts/prompt_manager.py +0 -0
  90. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/py.typed +0 -0
  91. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/resources/__init__.py +0 -0
  92. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/resources/resource.py +0 -0
  93. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/resources/resource_manager.py +0 -0
  94. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/resources/template.py +0 -0
  95. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/resources/types.py +0 -0
  96. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/server/__init__.py +0 -0
  97. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/server/context.py +0 -0
  98. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/server/openapi.py +0 -0
  99. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/settings.py +0 -0
  100. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/tools/__init__.py +0 -0
  101. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/tools/tool.py +0 -0
  102. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/tools/tool_manager.py +0 -0
  103. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/utilities/__init__.py +0 -0
  104. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/utilities/decorators.py +0 -0
  105. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/utilities/func_metadata.py +0 -0
  106. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/utilities/logging.py +0 -0
  107. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/utilities/openapi.py +0 -0
  108. {fastmcp-2.2.0 → fastmcp-2.2.2}/src/fastmcp/utilities/types.py +0 -0
  109. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/__init__.py +0 -0
  110. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/client/__init__.py +0 -0
  111. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/client/test_roots.py +0 -0
  112. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/client/test_sampling.py +0 -0
  113. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/conftest.py +0 -0
  114. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/prompts/__init__.py +0 -0
  115. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/prompts/test_base.py +0 -0
  116. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/prompts/test_prompt_manager.py +0 -0
  117. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/resources/__init__.py +0 -0
  118. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/resources/test_file_resources.py +0 -0
  119. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/resources/test_function_resources.py +0 -0
  120. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/resources/test_resource_manager.py +0 -0
  121. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/resources/test_resource_template.py +0 -0
  122. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/resources/test_resources.py +0 -0
  123. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/__init__.py +0 -0
  124. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_file_server.py +0 -0
  125. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_import_server.py +0 -0
  126. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_lifespan.py +0 -0
  127. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_mount.py +0 -0
  128. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_openapi.py +0 -0
  129. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_proxy.py +0 -0
  130. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_run_server.py +0 -0
  131. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/server/test_server.py +0 -0
  132. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/test_servers/fastmcp_server.py +0 -0
  133. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/test_servers/sse.py +0 -0
  134. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/test_servers/stdio.py +0 -0
  135. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/tools/__init__.py +0 -0
  136. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/tools/test_tool_manager.py +0 -0
  137. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/__init__.py +0 -0
  138. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/openapi/__init__.py +0 -0
  139. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/openapi/conftest.py +0 -0
  140. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi.py +0 -0
  141. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi_advanced.py +0 -0
  142. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/openapi/test_openapi_fastapi.py +0 -0
  143. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/test_decorated_function.py +0 -0
  144. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/test_func_metadata.py +0 -0
  145. {fastmcp-2.2.0 → fastmcp-2.2.2}/tests/utilities/test_logging.py +0 -0
@@ -0,0 +1,63 @@
1
+ name: 🐛 Bug Report
2
+ description: Report a bug or unexpected behavior in FastMCP
3
+ labels: [bug, pending]
4
+
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: Thank you for contributing to FastMCP! 🙏
9
+
10
+ - type: textarea
11
+ id: description
12
+ attributes:
13
+ label: Description
14
+ description: |
15
+ Please explain what you're experiencing and what you would expect to happen instead.
16
+
17
+ Provide as much detail as possible to help us understand and solve your problem quickly.
18
+ validations:
19
+ required: true
20
+
21
+ - type: textarea
22
+ id: example
23
+ attributes:
24
+ label: Example Code
25
+ description: >
26
+ If applicable, please provide a self-contained,
27
+ [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
28
+ demonstrating the bug.
29
+
30
+ placeholder: |
31
+ from fastmcp import FastMCP
32
+
33
+ ...
34
+ render: Python
35
+
36
+ - type: textarea
37
+ id: version
38
+ attributes:
39
+ label: Version Information
40
+ description: |
41
+ Please provide information about your FastMCP version, MCP version, Python version, and OS.
42
+
43
+ To get this information, run the following command in your terminal and paste the output below:
44
+
45
+ ```bash
46
+ fastmcp version
47
+ ```
48
+
49
+ If there is other information that would be helpful, please include it as well.
50
+ render: Text
51
+ validations:
52
+ required: true
53
+
54
+ - type: textarea
55
+ id: additional_context
56
+ attributes:
57
+ label: Additional Context
58
+ description: |
59
+ Add any other context about the problem here. This could include:
60
+ - The full error message and traceback (if applicable)
61
+ - Information about your environment (e.g., virtual environment, installed packages)
62
+ - Steps to reproduce the issue
63
+ - Any recent changes in your code or setup that might be relevant
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: FastMCP Documentation
4
+ url: https://gofastmcp.com
5
+ about: Please review the documentation before opening an issue.
6
+ - name: MCP Python SDK
7
+ url: https://github.com/modelcontextprotocol/python-sdk/issues
8
+ about: Issues related to the low-level MCP Python SDK, including the FastMCP 1.0 module that is included in the `mcp` package, should be filed on the official MCP repository.
@@ -0,0 +1,38 @@
1
+ name: 💡 Enhancement Request
2
+ description: Suggest an idea or improvement for FastMCP
3
+ labels: [enhancement, pending]
4
+
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: Thank you for contributing to FastMCP! We value your ideas for improving the framework. 💡
9
+
10
+ - type: textarea
11
+ id: description
12
+ attributes:
13
+ label: Enhancement Description
14
+ description: |
15
+ Please describe the enhancement you'd like to see in FastMCP.
16
+
17
+ - What problem would this solve?
18
+ - How would this improve your workflow or experience with FastMCP?
19
+ - Are there any alternative solutions you've considered?
20
+ validations:
21
+ required: true
22
+
23
+ - type: textarea
24
+ id: use_case
25
+ attributes:
26
+ label: Use Case
27
+ description: |
28
+ Describe a specific use case or scenario where this enhancement would be beneficial.
29
+ If possible, provide an example of how you envision using this feature.
30
+
31
+ - type: textarea
32
+ id: example
33
+ attributes:
34
+ label: Proposed Implementation
35
+ description: >
36
+ If you have ideas about how this enhancement could be implemented,
37
+ please share them here. Code snippets, pseudocode, or general approaches are welcome.
38
+ render: Python
@@ -60,4 +60,3 @@ jobs:
60
60
 
61
61
  - name: Run tests
62
62
  run: uv run pytest -vv
63
- if: ${{ !(github.event.pull_request.head.repo.fork) }}
@@ -27,4 +27,3 @@ repos:
27
27
  hooks:
28
28
  - id: pyright-pretty
29
29
  files: ^src/|^tests/
30
- exclude: ^examples/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.2.0
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
@@ -17,11 +17,11 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
18
  Classifier: Typing :: Typed
19
19
  Requires-Python: >=3.10
20
- Requires-Dist: dotenv>=0.9.9
21
20
  Requires-Dist: exceptiongroup>=1.2.2
22
21
  Requires-Dist: httpx>=0.28.1
23
22
  Requires-Dist: mcp<2.0.0,>=1.6.0
24
23
  Requires-Dist: openapi-pydantic>=0.5.1
24
+ Requires-Dist: python-dotenv>=1.1.0
25
25
  Requires-Dist: rich>=13.9.4
26
26
  Requires-Dist: typer>=0.15.2
27
27
  Requires-Dist: websockets>=15.0.1
@@ -95,6 +95,7 @@ FastMCP handles the complex protocol details and server management, letting you
95
95
  - [Proxy Servers](#proxy-servers)
96
96
  - [Composing MCP Servers](#composing-mcp-servers)
97
97
  - [OpenAPI \& FastAPI Generation](#openapi--fastapi-generation)
98
+ - [Handling `stderr`](#handling-stderr)
98
99
  - [Running Your Server](#running-your-server)
99
100
  - [Development Mode (Recommended for Building \& Testing)](#development-mode-recommended-for-building--testing)
100
101
  - [Claude Desktop Integration (For Regular Use)](#claude-desktop-integration-for-regular-use)
@@ -695,6 +696,12 @@ mcp_server = FastMCP.from_openapi(openapi_spec, client=http_client)
695
696
  if __name__ == "__main__":
696
697
  mcp_server.run()
697
698
  ```
699
+
700
+ ### Handling `stderr`
701
+ The MCP spec allows for the server to write anything it wants to `stderr`, and it
702
+ doesn't specify the format in any way. FastMCP will forward the server's `stderr`
703
+ to the client's `stderr`.
704
+
698
705
  ## Running Your Server
699
706
 
700
707
  Choose the method that best suits your needs:
@@ -66,6 +66,7 @@ FastMCP handles the complex protocol details and server management, letting you
66
66
  - [Proxy Servers](#proxy-servers)
67
67
  - [Composing MCP Servers](#composing-mcp-servers)
68
68
  - [OpenAPI \& FastAPI Generation](#openapi--fastapi-generation)
69
+ - [Handling `stderr`](#handling-stderr)
69
70
  - [Running Your Server](#running-your-server)
70
71
  - [Development Mode (Recommended for Building \& Testing)](#development-mode-recommended-for-building--testing)
71
72
  - [Claude Desktop Integration (For Regular Use)](#claude-desktop-integration-for-regular-use)
@@ -666,6 +667,12 @@ mcp_server = FastMCP.from_openapi(openapi_spec, client=http_client)
666
667
  if __name__ == "__main__":
667
668
  mcp_server.run()
668
669
  ```
670
+
671
+ ### Handling `stderr`
672
+ The MCP spec allows for the server to write anything it wants to `stderr`, and it
673
+ doesn't specify the format in any way. FastMCP will forward the server's `stderr`
674
+ to the client's `stderr`.
675
+
669
676
  ## Running Your Server
670
677
 
671
678
  Choose the method that best suits your needs:
@@ -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
  {
@@ -32,7 +32,7 @@ from fastmcp import FastMCP
32
32
 
33
33
  mcp = FastMCP("My MCP Server")
34
34
 
35
- @mcp.tool
35
+ @mcp.tool()
36
36
  def greet(name: str) -> str:
37
37
  return f"Hello, {name}!"
38
38
  ```
@@ -48,7 +48,7 @@ from fastmcp import FastMCP, Client
48
48
 
49
49
  mcp = FastMCP("My MCP Server")
50
50
 
51
- @mcp.tool
51
+ @mcp.tool()
52
52
  def greet(name: str) -> str:
53
53
  return f"Hello, {name}!"
54
54
 
@@ -75,7 +75,7 @@ from fastmcp import FastMCP, Client
75
75
 
76
76
  mcp = FastMCP("My MCP Server")
77
77
 
78
- @mcp.tool
78
+ @mcp.tool()
79
79
  def greet(name: str) -> str:
80
80
  return f"Hello, {name}!"
81
81
 
@@ -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
@@ -4,7 +4,7 @@ dynamic = ["version"]
4
4
  description = "The fast, Pythonic way to build MCP servers."
5
5
  authors = [{ name = "Jeremiah Lowin" }]
6
6
  dependencies = [
7
- "dotenv>=0.9.9",
7
+ "python-dotenv>=1.1.0",
8
8
  "exceptiongroup>=1.2.2",
9
9
  "httpx>=0.28.1",
10
10
  "mcp>=1.6.0,<2.0.0",
@@ -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.
@@ -0,0 +1,19 @@
1
+ # FastMCP Contrib Modules
2
+
3
+ This directory holds community-contributed modules for FastMCP. These modules extend FastMCP's functionality but are not officially maintained by the core team.
4
+
5
+ **Guarantees:**
6
+ * Modules in `contrib` may have different testing requirements or stability guarantees compared to the core library.
7
+ * Changes to the core FastMCP library might break modules in `contrib` without explicit warnings in the main changelog.
8
+
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.
@@ -0,0 +1,35 @@
1
+ # Bulk Tool Caller
2
+
3
+ This module provides the `BulkToolCaller` class, which extends the `MCPMixin` to offer tools for performing multiple tool calls in a single request to a FastMCP server. This can be useful for optimizing interactions with the server by reducing the overhead of individual tool calls.
4
+
5
+ ## Usage
6
+
7
+ To use the `BulkToolCaller`, see the example [example.py](./example.py) file. The `BulkToolCaller` can be instantiated and then registered with a FastMCP server URL. It provides methods to call multiple tools in bulk, either different tools or the same tool with different arguments.
8
+
9
+
10
+ ## Provided Tools
11
+
12
+ The `BulkToolCaller` provides the following tools:
13
+
14
+ ### `call_tools_bulk`
15
+
16
+ Calls multiple different tools registered on the MCP server in a single request.
17
+
18
+ - **Arguments:**
19
+ - `tool_calls` (list of `CallToolRequest`): A list of objects, where each object specifies the `tool` name and `arguments` for an individual tool call.
20
+ - `continue_on_error` (bool, optional): If `True`, continue executing subsequent tool calls even if a previous one resulted in an error. Defaults to `True`.
21
+
22
+ - **Returns:**
23
+ A list of `CallToolRequestResult` objects, each containing the result (`isError`, `content`) and the original `tool` name and `arguments` for each call.
24
+
25
+ ### `call_tool_bulk`
26
+
27
+ Calls a single tool registered on the MCP server multiple times with different arguments in a single request.
28
+
29
+ - **Arguments:**
30
+ - `tool` (str): The name of the tool to call.
31
+ - `tool_arguments` (list of dict): A list of dictionaries, where each dictionary contains the arguments for an individual run of the tool.
32
+ - `continue_on_error` (bool, optional): If `True`, continue executing subsequent tool calls even if a previous one resulted in an error. Defaults to `True`.
33
+
34
+ - **Returns:**
35
+ A list of `CallToolRequestResult` objects, each containing the result (`isError`, `content`) and the original `tool` name and `arguments` for each call.
@@ -0,0 +1,3 @@
1
+ from .bulk_tool_caller import BulkToolCaller
2
+
3
+ __all__ = ["BulkToolCaller"]
@@ -0,0 +1,135 @@
1
+ from typing import Any
2
+
3
+ from mcp.types import CallToolResult
4
+ from pydantic import BaseModel, Field
5
+
6
+ from fastmcp import FastMCP
7
+ from fastmcp.client import Client
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
+ )
14
+
15
+
16
+ class CallToolRequest(BaseModel):
17
+ """A class to represent a request to call a tool with specific arguments."""
18
+
19
+ tool: str = Field(description="The name of the tool to call.")
20
+ arguments: dict[str, Any] = Field(
21
+ description="A dictionary containing the arguments for the tool call."
22
+ )
23
+
24
+
25
+ class CallToolRequestResult(CallToolResult):
26
+ """
27
+ A class to represent the result of a bulk tool call.
28
+ It extends CallToolResult to include information about the requested tool call.
29
+ """
30
+
31
+ tool: str = Field(description="The name of the tool that was called.")
32
+ arguments: dict[str, Any] = Field(
33
+ description="The arguments used for the tool call."
34
+ )
35
+
36
+ @classmethod
37
+ def from_call_tool_result(
38
+ cls, result: CallToolResult, tool: str, arguments: dict[str, Any]
39
+ ) -> "CallToolRequestResult":
40
+ """
41
+ Create a CallToolRequestResult from a CallToolResult.
42
+ """
43
+ return cls(
44
+ tool=tool,
45
+ arguments=arguments,
46
+ isError=result.isError,
47
+ content=result.content,
48
+ )
49
+
50
+
51
+ class BulkToolCaller(MCPMixin):
52
+ """
53
+ A class to provide a "bulk tool call" tool for a FastMCP server
54
+ """
55
+
56
+ def register_tools(
57
+ self,
58
+ mcp_server: "FastMCP",
59
+ prefix: str | None = None,
60
+ separator: str = _DEFAULT_SEPARATOR_TOOL,
61
+ ) -> None:
62
+ """
63
+ Register the tools provided by this class with the given MCP server.
64
+ """
65
+ self.connection = FastMCPTransport(mcp_server)
66
+
67
+ super().register_tools(mcp_server=mcp_server)
68
+
69
+ @mcp_tool()
70
+ async def call_tools_bulk(
71
+ self, tool_calls: list[CallToolRequest], continue_on_error: bool = True
72
+ ) -> list[CallToolRequestResult]:
73
+ """
74
+ Call multiple tools registered on this MCP server in a single request. Each call can
75
+ be for a different tool and can include different arguments. Useful for speeding up
76
+ what would otherwise take several individual tool calls.
77
+ """
78
+ results = []
79
+
80
+ for tool_call in tool_calls:
81
+ result = await self._call_tool(tool_call.tool, tool_call.arguments)
82
+
83
+ results.append(result)
84
+
85
+ if result.isError and not continue_on_error:
86
+ return results
87
+
88
+ return results
89
+
90
+ @mcp_tool()
91
+ async def call_tool_bulk(
92
+ self,
93
+ tool: str,
94
+ tool_arguments: list[dict[str, str | int | float | bool | None]],
95
+ continue_on_error: bool = True,
96
+ ) -> list[CallToolRequestResult]:
97
+ """
98
+ Call a single tool registered on this MCP server multiple times with a single request.
99
+ Each call can include different arguments. Useful for speeding up what would otherwise
100
+ take several individual tool calls.
101
+
102
+ Args:
103
+ tool: The name of the tool to call.
104
+ tool_arguments: A list of dictionaries, where each dictionary contains the arguments for an individual run of the tool.
105
+ """
106
+ results = []
107
+
108
+ for tool_call_arguments in tool_arguments:
109
+ result = await self._call_tool(tool, tool_call_arguments)
110
+
111
+ results.append(result)
112
+
113
+ if result.isError and not continue_on_error:
114
+ return results
115
+
116
+ return results
117
+
118
+ async def _call_tool(
119
+ self, tool: str, arguments: dict[str, Any]
120
+ ) -> CallToolRequestResult:
121
+ """
122
+ Helper method to call a tool with the provided arguments.
123
+ """
124
+
125
+ async with Client(self.connection) as client:
126
+ result = await client.call_tool(
127
+ name=tool, arguments=arguments, _return_raw_result=True
128
+ )
129
+
130
+ return CallToolRequestResult(
131
+ tool=tool,
132
+ arguments=arguments,
133
+ isError=result.isError,
134
+ content=result.content,
135
+ )
@@ -0,0 +1,17 @@
1
+ """Sample code for FastMCP using MCPMixin."""
2
+
3
+ from fastmcp import FastMCP
4
+ from fastmcp.contrib.bulk_tool_caller import BulkToolCaller
5
+
6
+ mcp = FastMCP()
7
+
8
+
9
+ @mcp.tool()
10
+ def echo_tool(text: str) -> str:
11
+ """Echo the input text"""
12
+ return text
13
+
14
+
15
+ bulk_tool_caller = BulkToolCaller()
16
+
17
+ bulk_tool_caller.register_tools(mcp)
@@ -0,0 +1,39 @@
1
+ # MCP Mixin
2
+
3
+ This module provides the `MCPMixin` base class and associated decorators (`@mcp_tool`, `@mcp_resource`, `@mcp_prompt`).
4
+
5
+ It allows developers to easily define classes whose methods can be registered as tools, resources, or prompts with a `FastMCP` server instance using the `register_all()`, `register_tools()`, `register_resources()`, or `register_prompts()` methods provided by the mixin.
6
+
7
+ ## Usage
8
+
9
+ Inherit from `MCPMixin` and use the decorators on the methods you want to register.
10
+
11
+ ```python
12
+ from fastmcp import FastMCP
13
+ from fastmcp.contrib.mcp_mixin import MCPMixin, mcp_tool, mcp_resource
14
+
15
+ class MyComponent(MCPMixin):
16
+ @mcp_tool(name="my_tool", description="Does something cool.")
17
+ def tool_method(self):
18
+ return "Tool executed!"
19
+
20
+ @mcp_resource(uri="component://data")
21
+ def resource_method(self):
22
+ return {"data": "some data"}
23
+
24
+ mcp_server = FastMCP()
25
+ component = MyComponent()
26
+
27
+ # Register all decorated methods with a prefix
28
+ # Useful if you will have multiple instantiated objects of the same class
29
+ # and want to avoid name collisions.
30
+ component.register_all(mcp_server, prefix="my_comp")
31
+
32
+ # Register without a prefix
33
+ # component.register_all(mcp_server)
34
+
35
+ # Now 'my_comp_my_tool' tool and 'my_comp+component://data' resource are registered (if prefix used)
36
+ # Or 'my_tool' and 'component://data' are registered (if no prefix used)
37
+ ```
38
+
39
+ The `prefix` argument in registration methods is optional. If omitted, methods are registered with their original decorated names/URIs. Individual separators (`tools_separator`, `resources_separator`, `prompts_separator`) can also be provided to `register_all` to change the separator for specific types.
@@ -0,0 +1,8 @@
1
+ from .mcp_mixin import MCPMixin, mcp_tool, mcp_resource, mcp_prompt
2
+
3
+ __all__ = [
4
+ "MCPMixin",
5
+ "mcp_tool",
6
+ "mcp_resource",
7
+ "mcp_prompt",
8
+ ]