fastmcp 2.2.10__py3-none-any.whl → 2.3.0__py3-none-any.whl
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/__init__.py +2 -1
- fastmcp/cli/cli.py +1 -1
- fastmcp/client/client.py +3 -2
- fastmcp/client/transports.py +45 -1
- fastmcp/prompts/prompt.py +10 -15
- fastmcp/prompts/prompt_manager.py +3 -10
- fastmcp/resources/resource.py +2 -7
- fastmcp/resources/resource_manager.py +2 -4
- fastmcp/resources/template.py +11 -24
- fastmcp/resources/types.py +15 -44
- fastmcp/server/__init__.py +1 -0
- fastmcp/server/context.py +50 -38
- fastmcp/server/dependencies.py +35 -0
- fastmcp/server/http.py +308 -0
- fastmcp/server/openapi.py +5 -16
- fastmcp/server/proxy.py +4 -13
- fastmcp/server/server.py +196 -271
- fastmcp/settings.py +7 -0
- fastmcp/tools/tool.py +14 -23
- fastmcp/tools/tool_manager.py +3 -9
- fastmcp/utilities/cache.py +26 -0
- fastmcp/utilities/tests.py +73 -1
- {fastmcp-2.2.10.dist-info → fastmcp-2.3.0.dist-info}/METADATA +2 -2
- {fastmcp-2.2.10.dist-info → fastmcp-2.3.0.dist-info}/RECORD +27 -25
- fastmcp/utilities/http.py +0 -44
- {fastmcp-2.2.10.dist-info → fastmcp-2.3.0.dist-info}/WHEEL +0 -0
- {fastmcp-2.2.10.dist-info → fastmcp-2.3.0.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.2.10.dist-info → fastmcp-2.3.0.dist-info}/licenses/LICENSE +0 -0
fastmcp/tools/tool.py
CHANGED
|
@@ -12,6 +12,7 @@ from pydantic import BaseModel, BeforeValidator, Field
|
|
|
12
12
|
|
|
13
13
|
import fastmcp
|
|
14
14
|
from fastmcp.exceptions import ToolError
|
|
15
|
+
from fastmcp.server.dependencies import get_context
|
|
15
16
|
from fastmcp.utilities.json_schema import prune_params
|
|
16
17
|
from fastmcp.utilities.logging import get_logger
|
|
17
18
|
from fastmcp.utilities.types import (
|
|
@@ -22,10 +23,7 @@ from fastmcp.utilities.types import (
|
|
|
22
23
|
)
|
|
23
24
|
|
|
24
25
|
if TYPE_CHECKING:
|
|
25
|
-
|
|
26
|
-
from mcp.shared.context import LifespanContextT
|
|
27
|
-
|
|
28
|
-
from fastmcp.server import Context
|
|
26
|
+
pass
|
|
29
27
|
|
|
30
28
|
logger = get_logger(__name__)
|
|
31
29
|
|
|
@@ -41,9 +39,6 @@ class Tool(BaseModel):
|
|
|
41
39
|
name: str = Field(description="Name of the tool")
|
|
42
40
|
description: str = Field(description="Description of what the tool does")
|
|
43
41
|
parameters: dict[str, Any] = Field(description="JSON schema for tool parameters")
|
|
44
|
-
context_kwarg: str | None = Field(
|
|
45
|
-
None, description="Name of the kwarg that should receive context"
|
|
46
|
-
)
|
|
47
42
|
tags: Annotated[set[str], BeforeValidator(_convert_set_defaults)] = Field(
|
|
48
43
|
default_factory=set, description="Tags for the tool"
|
|
49
44
|
)
|
|
@@ -60,13 +55,12 @@ class Tool(BaseModel):
|
|
|
60
55
|
fn: Callable[..., Any],
|
|
61
56
|
name: str | None = None,
|
|
62
57
|
description: str | None = None,
|
|
63
|
-
context_kwarg: str | None = None,
|
|
64
58
|
tags: set[str] | None = None,
|
|
65
59
|
annotations: ToolAnnotations | None = None,
|
|
66
60
|
serializer: Callable[[Any], str] | None = None,
|
|
67
61
|
) -> Tool:
|
|
68
62
|
"""Create a Tool from a function."""
|
|
69
|
-
from fastmcp import Context
|
|
63
|
+
from fastmcp.server.context import Context
|
|
70
64
|
|
|
71
65
|
# Reject functions with *args or **kwargs
|
|
72
66
|
sig = inspect.signature(fn)
|
|
@@ -86,8 +80,7 @@ class Tool(BaseModel):
|
|
|
86
80
|
type_adapter = get_cached_typeadapter(fn)
|
|
87
81
|
schema = type_adapter.json_schema()
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
context_kwarg = find_kwarg_by_type(fn, kwarg_type=Context)
|
|
83
|
+
context_kwarg = find_kwarg_by_type(fn, kwarg_type=Context)
|
|
91
84
|
if context_kwarg:
|
|
92
85
|
schema = prune_params(schema, params=[context_kwarg])
|
|
93
86
|
|
|
@@ -96,25 +89,23 @@ class Tool(BaseModel):
|
|
|
96
89
|
name=func_name,
|
|
97
90
|
description=func_doc,
|
|
98
91
|
parameters=schema,
|
|
99
|
-
context_kwarg=context_kwarg,
|
|
100
92
|
tags=tags or set(),
|
|
101
93
|
annotations=annotations,
|
|
102
94
|
serializer=serializer,
|
|
103
95
|
)
|
|
104
96
|
|
|
105
97
|
async def run(
|
|
106
|
-
self,
|
|
107
|
-
arguments: dict[str, Any],
|
|
108
|
-
context: Context[ServerSessionT, LifespanContextT] | None = None,
|
|
98
|
+
self, arguments: dict[str, Any]
|
|
109
99
|
) -> list[TextContent | ImageContent | EmbeddedResource]:
|
|
110
100
|
"""Run the tool with arguments."""
|
|
101
|
+
from fastmcp.server.context import Context
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
injected_args = (
|
|
114
|
-
{self.context_kwarg: context} if self.context_kwarg is not None else {}
|
|
115
|
-
)
|
|
103
|
+
arguments = arguments.copy()
|
|
116
104
|
|
|
117
|
-
|
|
105
|
+
try:
|
|
106
|
+
context_kwarg = find_kwarg_by_type(self.fn, kwarg_type=Context)
|
|
107
|
+
if context_kwarg and context_kwarg not in arguments:
|
|
108
|
+
arguments[context_kwarg] = get_context()
|
|
118
109
|
|
|
119
110
|
if fastmcp.settings.settings.tool_attempt_parse_json_args:
|
|
120
111
|
# Pre-parse data from JSON in order to handle cases like `["a", "b", "c"]`
|
|
@@ -125,7 +116,7 @@ class Tool(BaseModel):
|
|
|
125
116
|
# which can be pre-parsed here.
|
|
126
117
|
signature = inspect.signature(self.fn)
|
|
127
118
|
for param_name in self.parameters["properties"]:
|
|
128
|
-
arg =
|
|
119
|
+
arg = arguments.get(param_name, None)
|
|
129
120
|
# if not in signature, we won't have annotations, so skip logic
|
|
130
121
|
if param_name not in signature.parameters:
|
|
131
122
|
continue
|
|
@@ -140,13 +131,13 @@ class Tool(BaseModel):
|
|
|
140
131
|
):
|
|
141
132
|
continue
|
|
142
133
|
try:
|
|
143
|
-
|
|
134
|
+
arguments[param_name] = json.loads(arg)
|
|
144
135
|
|
|
145
136
|
except json.JSONDecodeError:
|
|
146
137
|
pass
|
|
147
138
|
|
|
148
139
|
type_adapter = get_cached_typeadapter(self.fn)
|
|
149
|
-
result = type_adapter.validate_python(
|
|
140
|
+
result = type_adapter.validate_python(arguments)
|
|
150
141
|
if inspect.isawaitable(result):
|
|
151
142
|
result = await result
|
|
152
143
|
|
fastmcp/tools/tool_manager.py
CHANGED
|
@@ -3,7 +3,6 @@ from __future__ import annotations as _annotations
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
|
-
from mcp.shared.context import LifespanContextT
|
|
7
6
|
from mcp.types import EmbeddedResource, ImageContent, TextContent, ToolAnnotations
|
|
8
7
|
|
|
9
8
|
from fastmcp.exceptions import NotFoundError
|
|
@@ -12,9 +11,7 @@ from fastmcp.tools.tool import Tool
|
|
|
12
11
|
from fastmcp.utilities.logging import get_logger
|
|
13
12
|
|
|
14
13
|
if TYPE_CHECKING:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
from fastmcp.server import Context
|
|
14
|
+
pass
|
|
18
15
|
|
|
19
16
|
logger = get_logger(__name__)
|
|
20
17
|
|
|
@@ -98,14 +95,11 @@ class ToolManager:
|
|
|
98
95
|
return tool
|
|
99
96
|
|
|
100
97
|
async def call_tool(
|
|
101
|
-
self,
|
|
102
|
-
key: str,
|
|
103
|
-
arguments: dict[str, Any],
|
|
104
|
-
context: Context[ServerSessionT, LifespanContextT] | None = None,
|
|
98
|
+
self, key: str, arguments: dict[str, Any]
|
|
105
99
|
) -> list[TextContent | ImageContent | EmbeddedResource]:
|
|
106
100
|
"""Call a tool by name with arguments."""
|
|
107
101
|
tool = self.get_tool(key)
|
|
108
102
|
if not tool:
|
|
109
103
|
raise NotFoundError(f"Unknown tool: {key}")
|
|
110
104
|
|
|
111
|
-
return await tool.run(arguments
|
|
105
|
+
return await tool.run(arguments)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
UTC = datetime.timezone.utc
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TimedCache:
|
|
8
|
+
NOT_FOUND = object()
|
|
9
|
+
|
|
10
|
+
def __init__(self, expiration: datetime.timedelta):
|
|
11
|
+
self.expiration = expiration
|
|
12
|
+
self.cache: dict[Any, tuple[Any, datetime.datetime]] = {}
|
|
13
|
+
|
|
14
|
+
def set(self, key: Any, value: Any) -> None:
|
|
15
|
+
expires = datetime.datetime.now(UTC) + self.expiration
|
|
16
|
+
self.cache[key] = (value, expires)
|
|
17
|
+
|
|
18
|
+
def get(self, key: Any) -> Any:
|
|
19
|
+
value = self.cache.get(key)
|
|
20
|
+
if value is not None and value[1] > datetime.datetime.now(UTC):
|
|
21
|
+
return value[0]
|
|
22
|
+
else:
|
|
23
|
+
return self.NOT_FOUND
|
|
24
|
+
|
|
25
|
+
def clear(self) -> None:
|
|
26
|
+
self.cache.clear()
|
fastmcp/utilities/tests.py
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import copy
|
|
4
|
+
import multiprocessing
|
|
5
|
+
import socket
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import Callable, Generator
|
|
2
8
|
from contextlib import contextmanager
|
|
3
|
-
from typing import Any
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
10
|
+
|
|
11
|
+
import uvicorn
|
|
4
12
|
|
|
5
13
|
from fastmcp.settings import settings
|
|
6
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from fastmcp.server.server import FastMCP
|
|
17
|
+
|
|
7
18
|
|
|
8
19
|
@contextmanager
|
|
9
20
|
def temporary_settings(**kwargs: Any):
|
|
@@ -39,3 +50,64 @@ def temporary_settings(**kwargs: Any):
|
|
|
39
50
|
for attr in kwargs:
|
|
40
51
|
if hasattr(settings, attr):
|
|
41
52
|
setattr(settings, attr, old_settings[attr])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _run_server(mcp_server: FastMCP, transport: Literal["sse"], port: int) -> None:
|
|
56
|
+
# Some Starlette apps are not pickleable, so we need to create them here based on the indicated transport
|
|
57
|
+
if transport == "sse":
|
|
58
|
+
app = mcp_server.sse_app()
|
|
59
|
+
else:
|
|
60
|
+
raise ValueError(f"Invalid transport: {transport}")
|
|
61
|
+
uvicorn_server = uvicorn.Server(
|
|
62
|
+
config=uvicorn.Config(
|
|
63
|
+
app=app,
|
|
64
|
+
host="127.0.0.1",
|
|
65
|
+
port=port,
|
|
66
|
+
log_level="error",
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
uvicorn_server.run()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@contextmanager
|
|
73
|
+
def run_server_in_process(
|
|
74
|
+
server_fn: Callable[[str, int], None],
|
|
75
|
+
) -> Generator[str, None, None]:
|
|
76
|
+
"""
|
|
77
|
+
Context manager that runs a Starlette app in a separate process and returns the
|
|
78
|
+
server URL. When the context manager is exited, the server process is killed.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
app: The Starlette app to run.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
The server URL.
|
|
85
|
+
"""
|
|
86
|
+
host = "127.0.0.1"
|
|
87
|
+
with socket.socket() as s:
|
|
88
|
+
s.bind((host, 0))
|
|
89
|
+
port = s.getsockname()[1]
|
|
90
|
+
|
|
91
|
+
proc = multiprocessing.Process(target=server_fn, args=(host, port), daemon=True)
|
|
92
|
+
proc.start()
|
|
93
|
+
|
|
94
|
+
# Wait for server to be running
|
|
95
|
+
max_attempts = 100
|
|
96
|
+
attempt = 0
|
|
97
|
+
while attempt < max_attempts and proc.is_alive():
|
|
98
|
+
try:
|
|
99
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
100
|
+
s.connect((host, port))
|
|
101
|
+
break
|
|
102
|
+
except ConnectionRefusedError:
|
|
103
|
+
time.sleep(0.01)
|
|
104
|
+
attempt += 1
|
|
105
|
+
else:
|
|
106
|
+
raise RuntimeError(f"Server failed to start after {max_attempts} attempts")
|
|
107
|
+
|
|
108
|
+
yield f"http://{host}:{port}"
|
|
109
|
+
|
|
110
|
+
proc.kill()
|
|
111
|
+
proc.join(timeout=2)
|
|
112
|
+
if proc.is_alive():
|
|
113
|
+
raise RuntimeError("Server process failed to terminate")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastmcp
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
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
|
|
@@ -19,7 +19,7 @@ Classifier: Typing :: Typed
|
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
20
|
Requires-Dist: exceptiongroup>=1.2.2
|
|
21
21
|
Requires-Dist: httpx>=0.28.1
|
|
22
|
-
Requires-Dist: mcp<2.0.0,>=1.
|
|
22
|
+
Requires-Dist: mcp<2.0.0,>=1.8.0
|
|
23
23
|
Requires-Dist: openapi-pydantic>=0.5.1
|
|
24
24
|
Requires-Dist: python-dotenv>=1.1.0
|
|
25
25
|
Requires-Dist: rich>=13.9.4
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
fastmcp/__init__.py,sha256=
|
|
1
|
+
fastmcp/__init__.py,sha256=yTAqLZORsPqbr7AE0ayw6zIYBeMlxQlI-3HE2WqbvHk,435
|
|
2
2
|
fastmcp/exceptions.py,sha256=QKVHbftoZp4YZQ2NxA-t1SjztqspFdX95YTFOAmr5EE,640
|
|
3
3
|
fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
fastmcp/settings.py,sha256=
|
|
4
|
+
fastmcp/settings.py,sha256=rDClnYEpYjEl8VsvvVrKp9oaE4YLfNQcMoZ41H_bDL0,2968
|
|
5
5
|
fastmcp/cli/__init__.py,sha256=Ii284TNoG5lxTP40ETMGhHEq3lQZWxu9m9JuU57kUpQ,87
|
|
6
6
|
fastmcp/cli/claude.py,sha256=IAlcZ4qZKBBj09jZUMEx7EANZE_IR3vcu7zOBJmMOuU,4567
|
|
7
|
-
fastmcp/cli/cli.py,sha256=
|
|
7
|
+
fastmcp/cli/cli.py,sha256=7s5RsV8D_tPs20EJcrCvyO-i69DmV60OiRGD21oEJSI,15728
|
|
8
8
|
fastmcp/client/__init__.py,sha256=BXO9NUhntZ5GnUACfaRCzDJ5IzxqFJs8qKG-CRMSco4,490
|
|
9
9
|
fastmcp/client/base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
fastmcp/client/client.py,sha256=
|
|
10
|
+
fastmcp/client/client.py,sha256=zfSLWSGqiBoADveKehsAL66CdyGGqmzVHR1q46zfdQY,15479
|
|
11
11
|
fastmcp/client/logging.py,sha256=Q8jYcZj4KA15Yiz3RP8tBXj8sd9IxL3VThF_Y0O4Upc,356
|
|
12
12
|
fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
|
|
13
13
|
fastmcp/client/sampling.py,sha256=UlDHxnd6k_HoU8RA3ob0g8-e6haJBc9u27N_v291QoI,1698
|
|
14
|
-
fastmcp/client/transports.py,sha256=
|
|
14
|
+
fastmcp/client/transports.py,sha256=rIOodm2_7tuwy2oMgobiSlZsU08Z2Iy0XJs0dhvprWE,18705
|
|
15
15
|
fastmcp/contrib/README.md,sha256=rKknYSI1T192UvSszqwwDlQ2eYQpxywrNTLoj177SYU,878
|
|
16
16
|
fastmcp/contrib/bulk_tool_caller/README.md,sha256=5aUUY1TSFKtz1pvTLSDqkUCkGkuqMfMZNsLeaNqEgAc,1960
|
|
17
17
|
fastmcp/contrib/bulk_tool_caller/__init__.py,sha256=xvGSSaUXTQrc31erBoi1Gh7BikgOliETDiYVTP3rLxY,75
|
|
@@ -22,31 +22,33 @@ fastmcp/contrib/mcp_mixin/__init__.py,sha256=aw9IQ1ssNjCgws4ZNt8bkdpossAAGVAwwjB
|
|
|
22
22
|
fastmcp/contrib/mcp_mixin/example.py,sha256=GnunkXmtG5hLLTUsM8aW5ZURU52Z8vI4tNLl-fK7Dg0,1228
|
|
23
23
|
fastmcp/contrib/mcp_mixin/mcp_mixin.py,sha256=cfIRbnSxsVzglTD-auyTE0izVQeHP7Oz18qzYoBZJgg,7899
|
|
24
24
|
fastmcp/prompts/__init__.py,sha256=An8uMBUh9Hrb7qqcn_5_Hent7IOeSh7EA2IUVsIrtHc,179
|
|
25
|
-
fastmcp/prompts/prompt.py,sha256=
|
|
26
|
-
fastmcp/prompts/prompt_manager.py,sha256=
|
|
25
|
+
fastmcp/prompts/prompt.py,sha256=psc-YiBRttbjETINaP9P9QV328yk96mDBsZgjOHVyKM,7777
|
|
26
|
+
fastmcp/prompts/prompt_manager.py,sha256=9VcioLE-AoUKe1e9SynNQME9SvWy0q1QAvO1ewIWVmI,3126
|
|
27
27
|
fastmcp/resources/__init__.py,sha256=t0x1j8lc74rjUKtXe9H5Gs4fpQt82K4NgBK6Y7A0xTg,467
|
|
28
|
-
fastmcp/resources/resource.py,sha256=
|
|
29
|
-
fastmcp/resources/resource_manager.py,sha256=
|
|
30
|
-
fastmcp/resources/template.py,sha256=
|
|
31
|
-
fastmcp/resources/types.py,sha256=
|
|
32
|
-
fastmcp/server/__init__.py,sha256=
|
|
33
|
-
fastmcp/server/context.py,sha256=
|
|
34
|
-
fastmcp/server/
|
|
35
|
-
fastmcp/server/
|
|
36
|
-
fastmcp/server/
|
|
28
|
+
fastmcp/resources/resource.py,sha256=Rx1My_fi1f-oqnQ9R_v7ejopAk4BJDfbB75-s4d31dM,2492
|
|
29
|
+
fastmcp/resources/resource_manager.py,sha256=_kJQMjb7U0NYPhWZEayDCgpMeYOnvtoCzi2sMZc71NA,9442
|
|
30
|
+
fastmcp/resources/template.py,sha256=X_x1wnmIWGykoKwhwGLZmvGysXIubVLHDrvSmbsu4kg,7306
|
|
31
|
+
fastmcp/resources/types.py,sha256=QPDeka_cM1hmvwW4FeFhqy6BEEi4MlwtpvhWUVWh5Fc,6459
|
|
32
|
+
fastmcp/server/__init__.py,sha256=bMD4aQD4yJqLz7-mudoNsyeV8UgQfRAg3PRwPvwTEds,119
|
|
33
|
+
fastmcp/server/context.py,sha256=ykitQygA7zT5prbFTLCuYlnAzuljf_9ErUT0FYBPv3E,8135
|
|
34
|
+
fastmcp/server/dependencies.py,sha256=1utkxFsV37HZcWBwI69JyngVN2ppGO_PEgxUlUHHy_Q,742
|
|
35
|
+
fastmcp/server/http.py,sha256=yFazRx3hKLKfthcGGkeL-8gQxV0r2oGpugsuOvgjBzw,10013
|
|
36
|
+
fastmcp/server/openapi.py,sha256=0nANnwHJ5VZInNyo2f9ErmO0K3igMv6bwyxf3G-BSls,23473
|
|
37
|
+
fastmcp/server/proxy.py,sha256=qcBD2wWMcXA4dhqppStVH4UhsyWm0cpPUuItLpO6H6A,9621
|
|
38
|
+
fastmcp/server/server.py,sha256=usocXySZvmrp6GOJzQQrh1lUiAyVMkRnJhk4NBhp0FQ,40827
|
|
37
39
|
fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
|
|
38
|
-
fastmcp/tools/tool.py,sha256=
|
|
39
|
-
fastmcp/tools/tool_manager.py,sha256=
|
|
40
|
+
fastmcp/tools/tool.py,sha256=lr9F90-A36Z6DT4LScJBW88uFq8xrSv00ivFxiERJe8,7786
|
|
41
|
+
fastmcp/tools/tool_manager.py,sha256=p2nHyLFgz28tbsLpWOurkbWRU2Z34_HcDohjrvwjI0E,3369
|
|
40
42
|
fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
|
|
43
|
+
fastmcp/utilities/cache.py,sha256=aV3oZ-ZhMgLSM9iAotlUlEy5jFvGXrVo0Y5Bj4PBtqY,707
|
|
41
44
|
fastmcp/utilities/decorators.py,sha256=AjhjsetQZF4YOPV5MTZmIxO21iFp_4fDIS3O2_KNCEg,2990
|
|
42
|
-
fastmcp/utilities/http.py,sha256=EzOe38e2oC0SjAnLmcf__1AjEtF19px8ZIVxPLt3Cr0,991
|
|
43
45
|
fastmcp/utilities/json_schema.py,sha256=mSakhP8bENxhLFMwHJSxJAFllNeByIBDjVohwlpac6w,2026
|
|
44
46
|
fastmcp/utilities/logging.py,sha256=zav8pnFxG_fvGJHUV2XpobmT9WVrmv1mlQBSCz-CPx4,1159
|
|
45
47
|
fastmcp/utilities/openapi.py,sha256=Er3G1MyFwiWVxZXicXtD2j-BvttHEDTi1dgkq1KiBQc,51073
|
|
46
|
-
fastmcp/utilities/tests.py,sha256=
|
|
48
|
+
fastmcp/utilities/tests.py,sha256=uUV-8CkhCe5zZJkxhgJXnxrjJ3Yq7cCMZN8xWKGuqdY,3181
|
|
47
49
|
fastmcp/utilities/types.py,sha256=6CcqAQ1QqCO2HGSFlPS6FO5JRWnacjCcO2-EhyEnZV0,4400
|
|
48
|
-
fastmcp-2.
|
|
49
|
-
fastmcp-2.
|
|
50
|
-
fastmcp-2.
|
|
51
|
-
fastmcp-2.
|
|
52
|
-
fastmcp-2.
|
|
50
|
+
fastmcp-2.3.0.dist-info/METADATA,sha256=v9jZ7vkBNKfVq-L4hoGpjOMrJfi-FUs4q3Cxiqxh-zU,16396
|
|
51
|
+
fastmcp-2.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
52
|
+
fastmcp-2.3.0.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
|
|
53
|
+
fastmcp-2.3.0.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
54
|
+
fastmcp-2.3.0.dist-info/RECORD,,
|
fastmcp/utilities/http.py
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from contextlib import (
|
|
4
|
-
asynccontextmanager,
|
|
5
|
-
)
|
|
6
|
-
from contextvars import ContextVar
|
|
7
|
-
|
|
8
|
-
from starlette.requests import Request
|
|
9
|
-
|
|
10
|
-
from fastmcp.utilities.logging import get_logger
|
|
11
|
-
|
|
12
|
-
logger = get_logger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
_current_starlette_request: ContextVar[Request | None] = ContextVar(
|
|
16
|
-
"starlette_request",
|
|
17
|
-
default=None,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@asynccontextmanager
|
|
22
|
-
async def starlette_request_context(request: Request):
|
|
23
|
-
token = _current_starlette_request.set(request)
|
|
24
|
-
try:
|
|
25
|
-
yield
|
|
26
|
-
finally:
|
|
27
|
-
_current_starlette_request.reset(token)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def get_current_starlette_request() -> Request | None:
|
|
31
|
-
return _current_starlette_request.get()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class RequestMiddleware:
|
|
35
|
-
"""
|
|
36
|
-
Middleware that stores each request in a ContextVar
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
def __init__(self, app):
|
|
40
|
-
self.app = app
|
|
41
|
-
|
|
42
|
-
async def __call__(self, scope, receive, send):
|
|
43
|
-
async with starlette_request_context(Request(scope)):
|
|
44
|
-
await self.app(scope, receive, send)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|