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.
- {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/workflows/run-static.yml +12 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/PKG-INFO +15 -3
- {fastmcp-2.1.1 → fastmcp-2.1.2}/README.md +2 -1
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/getting-started/quickstart.mdx +2 -2
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/context.mdx +2 -2
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/fastmcp.mdx +6 -10
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/prompts.mdx +1 -1
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/tools.mdx +1 -1
- {fastmcp-2.1.1 → fastmcp-2.1.2}/pyproject.toml +25 -1
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/prompts/prompt.py +0 -8
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/prompts/prompt_manager.py +3 -1
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/resource.py +1 -9
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/resource_manager.py +5 -4
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/template.py +0 -8
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/tools/tool.py +0 -8
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/tools/tool_manager.py +4 -1
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/logging.py +14 -6
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/openapi.py +0 -87
- fastmcp-2.1.2/tests/server/test_openapi.py +719 -0
- fastmcp-2.1.2/tests/tools/__init__.py +0 -0
- fastmcp-2.1.2/tests/utilities/test_logging.py +30 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/uv.lock +2 -2
- fastmcp-2.1.1/tests/server/test_openapi.py +0 -355
- {fastmcp-2.1.1 → fastmcp-2.1.2}/.cursor/rules/core-mcp-objects.mdc +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/release.yml +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/workflows/publish.yml +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/.github/workflows/run-tests.yml +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/.gitignore +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/.pre-commit-config.yaml +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/LICENSE +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/Windows_Notes.md +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/assets/demo-inspector.png +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/clients/overview.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/clients/transports.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/docs.json +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/getting-started/installation.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/getting-started/welcome.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/composition.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/decorating-methods.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/fastapi.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/openapi.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/patterns/proxying.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/resources.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/servers/resources_backup.mdx +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/docs/style.css +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/complex_inputs.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/desktop.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/echo.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/memory.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/mount_example.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/readme-quickstart.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/sampling.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/screenshot.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/simple_echo.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/README.md +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/pyproject.toml +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/__main__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/hub.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/lights/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/lights/hue_utils.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/lights/server.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/py.typed +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/src/smart_home/settings.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/smart_home/uv.lock +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/examples/text_me.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/justfile +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/cli/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/cli/claude.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/cli/cli.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/base.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/client.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/roots.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/sampling.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/client/transports.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/exceptions.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/prompts/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/py.typed +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/resources/types.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/context.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/openapi.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/proxy.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/server/server.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/settings.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/tools/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/decorators.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/func_metadata.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/src/fastmcp/utilities/types.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/test_client.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/test_roots.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/client/test_sampling.py +0 -0
- /fastmcp-2.1.1/tests/prompts/__init__.py → /fastmcp-2.1.2/tests/conftest.py +0 -0
- {fastmcp-2.1.1/tests/resources → fastmcp-2.1.2/tests/prompts}/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/prompts/test_base.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/prompts/test_prompt_manager.py +0 -0
- {fastmcp-2.1.1/tests/server → fastmcp-2.1.2/tests/resources}/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_file_resources.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_function_resources.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_resource_manager.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_resource_template.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/resources/test_resources.py +0 -0
- {fastmcp-2.1.1/tests/tools → fastmcp-2.1.2/tests/server}/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_file_server.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_lifespan.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_mount.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_proxy.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_run_server.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_server.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_servers/fastmcp_server.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_servers/sse.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/server/test_servers/stdio.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/tools/test_tool_manager.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/__init__.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/conftest.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/test_openapi.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/test_openapi_advanced.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/openapi/test_openapi_fastapi.py +0 -0
- {fastmcp-2.1.1 → fastmcp-2.1.2}/tests/utilities/test_decorated_function.py +0 -0
- {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.
|
|
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
|
[](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
|
|
27
38
|
[](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
|
[](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
|
|
10
10
|
[](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](/
|
|
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](/
|
|
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 [
|
|
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](/
|
|
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.
|
|
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`
|
|
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](/
|
|
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](/
|
|
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](/
|
|
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](/
|
|
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 [
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
270
|
-
|
|
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 =
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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]:
|