fastmcp 2.1.1__tar.gz → 2.1.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 (127) hide show
  1. {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/workflows/run-static.yml +12 -0
  2. {fastmcp-2.1.1 → fastmcp-2.1.2}/PKG-INFO +15 -3
  3. {fastmcp-2.1.1 → fastmcp-2.1.2}/README.md +2 -1
  4. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/getting-started/quickstart.mdx +2 -2
  5. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/context.mdx +2 -2
  6. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/fastmcp.mdx +6 -10
  7. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/prompts.mdx +1 -1
  8. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/tools.mdx +1 -1
  9. {fastmcp-2.1.1 → fastmcp-2.1.2}/pyproject.toml +25 -1
  10. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/prompts/prompt.py +0 -8
  11. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/prompts/prompt_manager.py +3 -1
  12. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/resource.py +1 -9
  13. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/resource_manager.py +5 -4
  14. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/template.py +0 -8
  15. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/tools/tool.py +0 -8
  16. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/tools/tool_manager.py +4 -1
  17. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/logging.py +14 -6
  18. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/openapi.py +0 -87
  19. fastmcp-2.1.2/tests/server/test_openapi.py +719 -0
  20. fastmcp-2.1.2/tests/tools/__init__.py +0 -0
  21. fastmcp-2.1.2/tests/utilities/test_logging.py +30 -0
  22. {fastmcp-2.1.1 → fastmcp-2.1.2}/uv.lock +2 -2
  23. fastmcp-2.1.1/tests/server/test_openapi.py +0 -355
  24. {fastmcp-2.1.1 → fastmcp-2.1.2}/.cursor/rules/core-mcp-objects.mdc +0 -0
  25. {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/release.yml +0 -0
  26. {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/workflows/publish.yml +0 -0
  27. {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/workflows/run-tests.yml +0 -0
  28. {fastmcp-2.1.1 → fastmcp-2.1.2}/.gitignore +0 -0
  29. {fastmcp-2.1.1 → fastmcp-2.1.2}/.pre-commit-config.yaml +0 -0
  30. {fastmcp-2.1.1 → fastmcp-2.1.2}/LICENSE +0 -0
  31. {fastmcp-2.1.1 → fastmcp-2.1.2}/Windows_Notes.md +0 -0
  32. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/assets/demo-inspector.png +0 -0
  33. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/clients/overview.mdx +0 -0
  34. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/clients/transports.mdx +0 -0
  35. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/docs.json +0 -0
  36. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/getting-started/installation.mdx +0 -0
  37. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/getting-started/welcome.mdx +0 -0
  38. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/composition.mdx +0 -0
  39. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/decorating-methods.mdx +0 -0
  40. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/fastapi.mdx +0 -0
  41. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/openapi.mdx +0 -0
  42. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/proxying.mdx +0 -0
  43. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/resources.mdx +0 -0
  44. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/resources_backup.mdx +0 -0
  45. {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/style.css +0 -0
  46. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/complex_inputs.py +0 -0
  47. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/desktop.py +0 -0
  48. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/echo.py +0 -0
  49. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/memory.py +0 -0
  50. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/mount_example.py +0 -0
  51. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/readme-quickstart.py +0 -0
  52. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/sampling.py +0 -0
  53. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/screenshot.py +0 -0
  54. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/simple_echo.py +0 -0
  55. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/README.md +0 -0
  56. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/pyproject.toml +0 -0
  57. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/__init__.py +0 -0
  58. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/__main__.py +0 -0
  59. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/hub.py +0 -0
  60. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/lights/__init__.py +0 -0
  61. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/lights/hue_utils.py +0 -0
  62. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/lights/server.py +0 -0
  63. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/py.typed +0 -0
  64. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/settings.py +0 -0
  65. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/uv.lock +0 -0
  66. {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/text_me.py +0 -0
  67. {fastmcp-2.1.1 → fastmcp-2.1.2}/justfile +0 -0
  68. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/__init__.py +0 -0
  69. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/cli/__init__.py +0 -0
  70. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/cli/claude.py +0 -0
  71. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/cli/cli.py +0 -0
  72. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/__init__.py +0 -0
  73. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/base.py +0 -0
  74. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/client.py +0 -0
  75. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/roots.py +0 -0
  76. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/sampling.py +0 -0
  77. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/transports.py +0 -0
  78. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/exceptions.py +0 -0
  79. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/prompts/__init__.py +0 -0
  80. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/py.typed +0 -0
  81. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/__init__.py +0 -0
  82. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/types.py +0 -0
  83. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/__init__.py +0 -0
  84. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/context.py +0 -0
  85. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/openapi.py +0 -0
  86. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/proxy.py +0 -0
  87. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/server.py +0 -0
  88. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/settings.py +0 -0
  89. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/tools/__init__.py +0 -0
  90. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/__init__.py +0 -0
  91. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/decorators.py +0 -0
  92. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/func_metadata.py +0 -0
  93. {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/types.py +0 -0
  94. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/__init__.py +0 -0
  95. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/__init__.py +0 -0
  96. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/test_client.py +0 -0
  97. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/test_roots.py +0 -0
  98. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/test_sampling.py +0 -0
  99. /fastmcp-2.1.1/tests/prompts/__init__.py → /fastmcp-2.1.2/tests/conftest.py +0 -0
  100. {fastmcp-2.1.1/tests/resources → fastmcp-2.1.2/tests/prompts}/__init__.py +0 -0
  101. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/prompts/test_base.py +0 -0
  102. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/prompts/test_prompt_manager.py +0 -0
  103. {fastmcp-2.1.1/tests/server → fastmcp-2.1.2/tests/resources}/__init__.py +0 -0
  104. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_file_resources.py +0 -0
  105. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_function_resources.py +0 -0
  106. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_resource_manager.py +0 -0
  107. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_resource_template.py +0 -0
  108. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_resources.py +0 -0
  109. {fastmcp-2.1.1/tests/tools → fastmcp-2.1.2/tests/server}/__init__.py +0 -0
  110. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_file_server.py +0 -0
  111. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_lifespan.py +0 -0
  112. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_mount.py +0 -0
  113. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_proxy.py +0 -0
  114. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_run_server.py +0 -0
  115. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_server.py +0 -0
  116. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_servers/fastmcp_server.py +0 -0
  117. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_servers/sse.py +0 -0
  118. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_servers/stdio.py +0 -0
  119. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/tools/test_tool_manager.py +0 -0
  120. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/__init__.py +0 -0
  121. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/__init__.py +0 -0
  122. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/conftest.py +0 -0
  123. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/test_openapi.py +0 -0
  124. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/test_openapi_advanced.py +0 -0
  125. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/test_openapi_fastapi.py +0 -0
  126. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/test_decorated_function.py +0 -0
  127. {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/test_func_metadata.py +0 -0
@@ -8,7 +8,19 @@ env:
8
8
  on:
9
9
  push:
10
10
  branches: ["main"]
11
+ paths:
12
+ - "src/**"
13
+ - "tests/**"
14
+ - "uv.lock"
15
+ - "pyproject.toml"
16
+ - ".github/workflows/**"
11
17
  pull_request:
18
+ paths:
19
+ - "src/**"
20
+ - "tests/**"
21
+ - "uv.lock"
22
+ - "pyproject.toml"
23
+ - ".github/workflows/**"
12
24
  workflow_dispatch:
13
25
 
14
26
  permissions:
@@ -1,10 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: The fast, Pythonic way to build MCP servers.
5
+ Project-URL: Homepage, https://gofastmcp.com
6
+ Project-URL: Repository, https://github.com/jlowin/fastmcp
7
+ Project-URL: Documentation, https://gofastmcp.com
5
8
  Author: Jeremiah Lowin
6
- License: Apache-2.0
9
+ License-Expression: Apache-2.0
7
10
  License-File: LICENSE
11
+ Keywords: agent,fastmcp,llm,mcp,mcp client,mcp server,model context protocol
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Classifier: Typing :: Typed
8
19
  Requires-Python: >=3.10
9
20
  Requires-Dist: dotenv>=0.9.9
10
21
  Requires-Dist: fastapi>=0.115.12
@@ -26,6 +37,7 @@ Description-Content-Type: text/markdown
26
37
  [![Tests](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
27
38
  [![License](https://img.shields.io/github/license/jlowin/fastmcp.svg)](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
28
39
 
40
+ <a href="https://trendshift.io/repositories/13266" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13266" alt="jlowin%2Ffastmcp | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
29
41
  </div>
30
42
 
31
43
  The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a new, standardized way to provide context and tools to your LLMs, and FastMCP makes building MCP servers and clients simple and intuitive. Create tools, expose resources, define prompts, and connect components with clean, Pythonic code.
@@ -787,4 +799,4 @@ We use `ruff` via `pre-commit`.
787
799
 
788
800
  Please open an issue or discussion for questions or suggestions!
789
801
 
790
- </details>
802
+ </details>
@@ -9,6 +9,7 @@
9
9
  [![Tests](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
10
10
  [![License](https://img.shields.io/github/license/jlowin/fastmcp.svg)](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
11
11
 
12
+ <a href="https://trendshift.io/repositories/13266" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13266" alt="jlowin%2Ffastmcp | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
12
13
  </div>
13
14
 
14
15
  The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a new, standardized way to provide context and tools to your LLMs, and FastMCP makes building MCP servers and clients simple and intuitive. Create tools, expose resources, define prompts, and connect components with clean, Pythonic code.
@@ -770,4 +771,4 @@ We use `ruff` via `pre-commit`.
770
771
 
771
772
  Please open an issue or discussion for questions or suggestions!
772
773
 
773
- </details>
774
+ </details>
@@ -5,7 +5,7 @@ icon: rocket
5
5
 
6
6
  Welcome! This guide will help you quickly set up FastMCP and run your first MCP server.
7
7
 
8
- If you haven't already installed FastMCP, follow the [installation instructions](/docs/getting-started/installation).
8
+ If you haven't already installed FastMCP, follow the [installation instructions](/getting-started/installation).
9
9
 
10
10
  ## Creating a FastMCP Server
11
11
 
@@ -123,5 +123,5 @@ fastmcp run my_server.py:mcp
123
123
  Note that FastMCP *does not* require the `__main__` block in the server file, and will ignore it if it is present. Instead, it looks for the server object provided in the CLI command (here, `mcp`). If no server object is provided, `fastmcp run` will automatically search for servers called "mcp", "app", or "server" in the file.
124
124
 
125
125
  <Tip>
126
- We pointed our client at the server file, which is recognized as a Python MCP server and executed with `python my_server.py` by default. This exceutes the `__main__` block of the server file. There are other ways to run the server, which are described in the [server configuration](/docs/getting-started/configuration) guide.
126
+ We pointed our client at the server file, which is recognized as a Python MCP server and executed with `python my_server.py` by default. This exceutes the `__main__` block of the server file. There are other ways to run the server, which are described in the [server configuration](/servers/fastmcp#running-the-server) guide.
127
127
  </Tip>
@@ -5,7 +5,7 @@ description: Access MCP capabilities like logging, progress, and resources withi
5
5
  icon: rectangle-code
6
6
  ---
7
7
 
8
- When defining FastMCP [Tools](/server/tools), your functions might need to interact with the underlying MCP session or access server capabilities. FastMCP provides the `Context` object for this purpose.
8
+ When defining FastMCP [tools](/servers/tools), your functions might need to interact with the underlying MCP session or access server capabilities. FastMCP provides the `Context` object for this purpose.
9
9
 
10
10
  ## What Is Context?
11
11
 
@@ -227,7 +227,7 @@ async def generate_example(concept: str, ctx: Context) -> str:
227
227
  return f"```python\n{code_example}\n```"
228
228
  ```
229
229
 
230
- See [Client Sampling](/client/sampling) for more details on how clients handle these requests.
230
+ See [Client Sampling](/clients/overview#llm-sampling) for more details on how clients handle these requests.
231
231
 
232
232
  ### Request Information
233
233
 
@@ -28,9 +28,9 @@ The `FastMCP` constructor accepts several arguments:
28
28
 
29
29
  * `name`: (Optional) A human-readable name for your server. Defaults to "FastMCP".
30
30
  * `instructions`: (Optional) Description of how to interact with this server. These instructions help clients understand the server's purpose and available functionality.
31
- * `lifespan`: (Optional) An async context manager function for server startup and shutdown logic. See [Lifespan Management](/advanced/lifespan).
31
+ * `lifespan`: (Optional) An async context manager function for server startup and shutdown logic.
32
32
  * `tags`: (Optional) A set of strings to tag the server itself.
33
- * `**settings`: Keyword arguments corresponding to `ServerSettings` for configuration. See [Configuration](/advanced/configuration).
33
+ * `**settings`: Keyword arguments corresponding to additional `ServerSettings` configuration
34
34
 
35
35
  ## Components
36
36
 
@@ -47,7 +47,7 @@ def multiply(a: float, b: float) -> float:
47
47
  return a * b
48
48
  ```
49
49
 
50
- See [Tools](/server/tools) for detailed documentation.
50
+ See [Tools](/servers/tools) for detailed documentation.
51
51
 
52
52
  ### Resources
53
53
 
@@ -60,7 +60,7 @@ def get_config() -> dict:
60
60
  return {"theme": "dark", "version": "1.0"}
61
61
  ```
62
62
 
63
- See [Resources & Templates](/server/resources) for detailed documentation.
63
+ See [Resources & Templates](/servers/resources) for detailed documentation.
64
64
 
65
65
  ### Resource Templates
66
66
 
@@ -74,7 +74,7 @@ def get_user_profile(user_id: int) -> dict:
74
74
  return {"id": user_id, "name": f"User {user_id}", "status": "active"}
75
75
  ```
76
76
 
77
- See [Resources & Templates](/server/resources) for detailed documentation.
77
+ See [Resources & Templates](/servers/resources) for detailed documentation.
78
78
 
79
79
  ### Prompts
80
80
 
@@ -88,7 +88,7 @@ def analyze_data(data_points: list[float]) -> str:
88
88
  return f"Please analyze these data points: {formatted_data}"
89
89
  ```
90
90
 
91
- See [Prompts](/server/prompts) for detailed documentation.
91
+ See [Prompts](/servers/prompts) for detailed documentation.
92
92
 
93
93
  ## Running the Server
94
94
 
@@ -186,9 +186,6 @@ fastmcp run my_server.py:mcp --transport sse --host 127.0.0.1 --port 8888
186
186
 
187
187
  The CLI can dynamically find and run FastMCP server objects in your files, but including the `if __name__ == "__main__":` block ensures compatibility with all clients.
188
188
 
189
- <Tip>
190
- For more options, including how to set up your server's dependencies or use advanced configurations, see the [CLI Reference](/cli/overview).
191
- </Tip>
192
189
 
193
190
  ## Mounting Subservers
194
191
 
@@ -299,4 +296,3 @@ print(mcp.settings.on_duplicate_tools) # Output: DuplicateBehavior.ERROR
299
296
 
300
297
  All of these can be configured directly as parameters when creating the `FastMCP` instance.
301
298
 
302
- See the [Configuration](/advanced/configuration) page for more details.
@@ -195,7 +195,7 @@ Using the `ctx` parameter (based on its `Context` type hint), you can access:
195
195
  - **LLM Sampling:** `ctx.sample(...)`
196
196
  - **Request Info:** `ctx.request_id`, `ctx.client_id`
197
197
 
198
- Refer to the [Using Context](/server/context) page for more details on these capabilities.
198
+ Refer to the [Context documentation](/servers/context) for more details on these capabilities.
199
199
 
200
200
  ## Server Behavior
201
201
 
@@ -300,7 +300,7 @@ The Context object provides access to:
300
300
  - **LLM Sampling**: `ctx.sample(...)`
301
301
  - **Request Information**: `ctx.request_id`, `ctx.client_id`
302
302
 
303
- For full documentation on the Context object and all its capabilities, see the [Context Object](/server/context) page.
303
+ For full documentation on the Context object and all its capabilities, see the [Context documentation](/servers/context).
304
304
 
305
305
  ## Server Behavior
306
306
 
@@ -14,7 +14,26 @@ dependencies = [
14
14
  ]
15
15
  requires-python = ">=3.10"
16
16
  readme = "README.md"
17
- license = { text = "Apache-2.0" }
17
+ license = "Apache-2.0"
18
+
19
+ keywords = [
20
+ "mcp",
21
+ "mcp server",
22
+ "mcp client",
23
+ "model context protocol",
24
+ "fastmcp",
25
+ "llm",
26
+ "agent",
27
+ ]
28
+ classifiers = [
29
+ "Intended Audience :: Developers",
30
+ "License :: OSI Approved :: Apache Software License",
31
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Typing :: Typed",
36
+ ]
18
37
 
19
38
  [dependency-groups]
20
39
  dev = [
@@ -34,6 +53,11 @@ dev = [
34
53
  [project.scripts]
35
54
  fastmcp = "fastmcp.cli:app"
36
55
 
56
+ [project.urls]
57
+ Homepage = "https://gofastmcp.com"
58
+ Repository = "https://github.com/jlowin/fastmcp"
59
+ Documentation = "https://gofastmcp.com"
60
+
37
61
  [build-system]
38
62
  requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
39
63
  build-backend = "hatchling.build"
@@ -8,7 +8,6 @@ from typing import Annotated, Any, Literal
8
8
  import pydantic_core
9
9
  from mcp.types import EmbeddedResource, ImageContent, TextContent
10
10
  from pydantic import BaseModel, BeforeValidator, Field, TypeAdapter, validate_call
11
- from typing_extensions import Self
12
11
 
13
12
  from fastmcp.utilities.types import _convert_set_defaults
14
13
 
@@ -163,13 +162,6 @@ class Prompt(BaseModel):
163
162
  except Exception as e:
164
163
  raise ValueError(f"Error rendering prompt {self.name}: {e}")
165
164
 
166
- def copy(self, updates: dict[str, Any] | None = None) -> Self:
167
- """Copy the prompt with optional updates."""
168
- data = self.model_dump()
169
- if updates:
170
- data.update(updates)
171
- return type(self)(**data)
172
-
173
165
  def __eq__(self, other: object) -> bool:
174
166
  if not isinstance(other, Prompt):
175
167
  return False
@@ -1,5 +1,6 @@
1
1
  """Prompt management functionality."""
2
2
 
3
+ import copy
3
4
  from collections.abc import Awaitable, Callable
4
5
  from typing import Any
5
6
 
@@ -84,7 +85,8 @@ class PromptManager:
84
85
  # Create prefixed name
85
86
  prefixed_name = f"{prefix}{name}" if prefix else name
86
87
 
87
- new_prompt = prompt.copy(updates=dict(name=prefixed_name))
88
+ new_prompt = copy.copy(prompt)
89
+ new_prompt.name = prefixed_name
88
90
 
89
91
  # Store the prompt with the prefixed name
90
92
  self.add_prompt(new_prompt)
@@ -1,7 +1,7 @@
1
1
  """Base classes and interfaces for FastMCP resources."""
2
2
 
3
3
  import abc
4
- from typing import Annotated, Any
4
+ from typing import Annotated
5
5
 
6
6
  from pydantic import (
7
7
  AnyUrl,
@@ -13,7 +13,6 @@ from pydantic import (
13
13
  ValidationInfo,
14
14
  field_validator,
15
15
  )
16
- from typing_extensions import Self
17
16
 
18
17
  from fastmcp.utilities.types import _convert_set_defaults
19
18
 
@@ -54,13 +53,6 @@ class Resource(BaseModel, abc.ABC):
54
53
  """Read the resource content."""
55
54
  pass
56
55
 
57
- def copy(self, updates: dict[str, Any] | None = None) -> Self:
58
- """Copy the resource with optional updates."""
59
- data = self.model_dump()
60
- if updates:
61
- data.update(updates)
62
- return type(self)(**data)
63
-
64
56
  def __eq__(self, other: object) -> bool:
65
57
  if not isinstance(other, Resource):
66
58
  return False
@@ -1,5 +1,6 @@
1
1
  """Resource manager functionality."""
2
2
 
3
+ import copy
3
4
  import inspect
4
5
  import re
5
6
  from collections.abc import Callable
@@ -238,7 +239,8 @@ class ResourceManager:
238
239
  # Create prefixed URI and copy the resource with the new URI
239
240
  prefixed_uri = f"{prefix}{uri}" if prefix else uri
240
241
 
241
- new_resource = resource.copy(updates=dict(uri=prefixed_uri))
242
+ new_resource = copy.copy(resource)
243
+ new_resource.uri = AnyUrl(prefixed_uri)
242
244
 
243
245
  # Store directly in resources dictionary
244
246
  self.add_resource(new_resource)
@@ -266,9 +268,8 @@ class ResourceManager:
266
268
  f"{prefix}{uri_template}" if prefix else uri_template
267
269
  )
268
270
 
269
- new_template = template.copy(
270
- updates=dict(uri_template=prefixed_uri_template)
271
- )
271
+ new_template = copy.copy(template)
272
+ new_template.uri_template = prefixed_uri_template
272
273
 
273
274
  # Store directly in templates dictionary
274
275
  self.add_template(new_template)
@@ -8,7 +8,6 @@ from collections.abc import Callable
8
8
  from typing import Annotated, Any
9
9
 
10
10
  from pydantic import BaseModel, BeforeValidator, Field, TypeAdapter, validate_call
11
- from typing_extensions import Self
12
11
 
13
12
  from fastmcp.resources.types import FunctionResource, Resource
14
13
  from fastmcp.utilities.types import _convert_set_defaults
@@ -92,13 +91,6 @@ class ResourceTemplate(BaseModel):
92
91
  except Exception as e:
93
92
  raise ValueError(f"Error creating resource from template: {e}")
94
93
 
95
- def copy(self, updates: dict[str, Any] | None = None) -> Self:
96
- """Copy the resource template with optional updates."""
97
- data = self.model_dump()
98
- if updates:
99
- data.update(updates)
100
- return type(self)(**data)
101
-
102
94
  def __eq__(self, other: object) -> bool:
103
95
  if not isinstance(other, ResourceTemplate):
104
96
  return False
@@ -5,7 +5,6 @@ from collections.abc import Callable
5
5
  from typing import TYPE_CHECKING, Annotated, Any
6
6
 
7
7
  from pydantic import BaseModel, BeforeValidator, Field
8
- from typing_extensions import Self
9
8
 
10
9
  from fastmcp.exceptions import ToolError
11
10
  from fastmcp.utilities.func_metadata import FuncMetadata, func_metadata
@@ -102,13 +101,6 @@ class Tool(BaseModel):
102
101
  except Exception as e:
103
102
  raise ToolError(f"Error executing tool {self.name}: {e}") from e
104
103
 
105
- def copy(self, updates: dict[str, Any] | None = None) -> Self:
106
- """Copy the tool with optional updates."""
107
- data = self.model_dump()
108
- if updates:
109
- data.update(updates)
110
- return type(self)(**data)
111
-
112
104
  def __eq__(self, other: object) -> bool:
113
105
  if not isinstance(other, Tool):
114
106
  return False
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
+ import copy
3
4
  from collections.abc import Callable
4
5
  from typing import TYPE_CHECKING, Any
5
6
 
@@ -90,7 +91,9 @@ class ToolManager:
90
91
  for name, tool in tool_manager._tools.items():
91
92
  prefixed_name = f"{prefix}{name}" if prefix else name
92
93
 
93
- new_tool = tool.copy(updates=dict(name=prefixed_name))
94
+ new_tool = copy.copy(tool)
95
+ new_tool.name = prefixed_name
96
+
94
97
  # Store the copied tool
95
98
  self.add_tool(new_tool)
96
99
  logger.debug(f'Imported tool "{name}" as "{prefixed_name}"')
@@ -20,15 +20,23 @@ def get_logger(name: str) -> logging.Logger:
20
20
 
21
21
 
22
22
  def configure_logging(
23
- level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO",
23
+ level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | int = "INFO",
24
24
  ) -> None:
25
25
  """Configure logging for FastMCP.
26
26
 
27
27
  Args:
28
28
  level: the log level to use
29
29
  """
30
- logging.basicConfig(
31
- level=level,
32
- format="%(message)s",
33
- handlers=[RichHandler(console=Console(stderr=True), rich_tracebacks=True)],
34
- )
30
+ # Only configure the FastMCP logger namespace
31
+ handler = RichHandler(console=Console(stderr=True), rich_tracebacks=True)
32
+ formatter = logging.Formatter("%(message)s")
33
+ handler.setFormatter(formatter)
34
+
35
+ fastmcp_logger = logging.getLogger("FastMCP")
36
+ fastmcp_logger.setLevel(level)
37
+
38
+ # Remove any existing handlers to avoid duplicates on reconfiguration
39
+ for hdlr in fastmcp_logger.handlers[:]:
40
+ fastmcp_logger.removeHandler(hdlr)
41
+
42
+ fastmcp_logger.addHandler(handler)
@@ -150,93 +150,6 @@ def _resolve_ref(
150
150
  return item
151
151
 
152
152
 
153
- def _extract_schema_as_dict(
154
- schema_obj: Schema | Reference, openapi: OpenAPI
155
- ) -> JsonSchema:
156
- """Resolves a schema/reference and returns it as a dictionary."""
157
- resolved_schema = _resolve_ref(schema_obj, openapi)
158
- if isinstance(resolved_schema, Schema):
159
- # Using exclude_none=True might be better than exclude_unset sometimes
160
- return resolved_schema.model_dump(mode="json", by_alias=True, exclude_none=True)
161
- elif isinstance(resolved_schema, dict):
162
- logger.warning(
163
- "Resolved schema reference resulted in a dict, not a Schema model."
164
- )
165
- return resolved_schema
166
- else:
167
- ref_str = getattr(schema_obj, "ref", "unknown")
168
- logger.warning(
169
- f"Expected Schema after resolving ref '{ref_str}', got {type(resolved_schema)}. Returning empty dict."
170
- )
171
- return {}
172
-
173
-
174
- def _convert_to_parameter_location(param_in: str) -> ParameterLocation:
175
- """Convert string parameter location to our ParameterLocation type."""
176
- if param_in == "path":
177
- return "path"
178
- elif param_in == "query":
179
- return "query"
180
- elif param_in == "header":
181
- return "header"
182
- elif param_in == "cookie":
183
- return "cookie"
184
- else:
185
- logger.warning(f"Unknown parameter location: {param_in}, defaulting to 'query'")
186
- return "query"
187
-
188
-
189
- def _extract_responses(
190
- operation_responses: dict[str, Response | Reference] | None,
191
- openapi: OpenAPI,
192
- ) -> dict[str, ResponseInfo]:
193
- """Extracts and resolves response information for an operation."""
194
- extracted_responses: dict[str, ResponseInfo] = {}
195
- if not operation_responses:
196
- return extracted_responses
197
-
198
- for status_code, resp_or_ref in operation_responses.items():
199
- try:
200
- response = cast(Response, _resolve_ref(resp_or_ref, openapi))
201
- if not isinstance(response, Response):
202
- ref_str = getattr(resp_or_ref, "ref", "unknown")
203
- logger.warning(
204
- f"Expected Response after resolving ref '{ref_str}' for status code {status_code}, got {type(response)}. Skipping."
205
- )
206
- continue
207
-
208
- content_schemas: dict[str, JsonSchema] = {}
209
- if response.content:
210
- for media_type_str, media_type_obj in response.content.items():
211
- if (
212
- isinstance(media_type_obj, MediaType)
213
- and media_type_obj.media_type_schema
214
- ):
215
- try:
216
- schema_dict = _extract_schema_as_dict(
217
- media_type_obj.media_type_schema, openapi
218
- )
219
- content_schemas[media_type_str] = schema_dict
220
- except ValueError as schema_err:
221
- logger.error(
222
- f"Failed to extract schema for media type '{media_type_str}' in response {status_code}: {schema_err}"
223
- )
224
-
225
- resp_info = ResponseInfo(
226
- description=response.description, content_schema=content_schemas
227
- )
228
- extracted_responses[str(status_code)] = resp_info
229
-
230
- except (ValidationError, ValueError, AttributeError) as e:
231
- ref_name = getattr(resp_or_ref, "ref", "unknown")
232
- logger.error(
233
- f"Failed to extract response for status code {status_code} (ref: '{ref_name}'): {e}",
234
- exc_info=False,
235
- )
236
-
237
- return extracted_responses
238
-
239
-
240
153
  # --- Main Parsing Function ---
241
154
  # (No changes needed in the main loop logic, only in the helpers it calls)
242
155
  def parse_openapi_to_http_routes(openapi_dict: dict[str, Any]) -> list[HTTPRoute]: