fastmcp 2.14.4__py3-none-any.whl → 3.0.0b1__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/_vendor/__init__.py +1 -0
- fastmcp/_vendor/docket_di/README.md +7 -0
- fastmcp/_vendor/docket_di/__init__.py +163 -0
- fastmcp/cli/cli.py +112 -28
- fastmcp/cli/install/claude_code.py +1 -5
- fastmcp/cli/install/claude_desktop.py +1 -5
- fastmcp/cli/install/cursor.py +1 -5
- fastmcp/cli/install/gemini_cli.py +1 -5
- fastmcp/cli/install/mcp_json.py +1 -6
- fastmcp/cli/run.py +146 -5
- fastmcp/client/__init__.py +7 -9
- fastmcp/client/auth/oauth.py +18 -17
- fastmcp/client/client.py +100 -870
- fastmcp/client/elicitation.py +1 -1
- fastmcp/client/mixins/__init__.py +13 -0
- fastmcp/client/mixins/prompts.py +295 -0
- fastmcp/client/mixins/resources.py +325 -0
- fastmcp/client/mixins/task_management.py +157 -0
- fastmcp/client/mixins/tools.py +397 -0
- fastmcp/client/sampling/handlers/anthropic.py +2 -2
- fastmcp/client/sampling/handlers/openai.py +1 -1
- fastmcp/client/tasks.py +3 -3
- fastmcp/client/telemetry.py +47 -0
- fastmcp/client/transports/__init__.py +38 -0
- fastmcp/client/transports/base.py +82 -0
- fastmcp/client/transports/config.py +170 -0
- fastmcp/client/transports/http.py +145 -0
- fastmcp/client/transports/inference.py +154 -0
- fastmcp/client/transports/memory.py +90 -0
- fastmcp/client/transports/sse.py +89 -0
- fastmcp/client/transports/stdio.py +543 -0
- fastmcp/contrib/component_manager/README.md +4 -10
- fastmcp/contrib/component_manager/__init__.py +1 -2
- fastmcp/contrib/component_manager/component_manager.py +95 -160
- fastmcp/contrib/component_manager/example.py +1 -1
- fastmcp/contrib/mcp_mixin/example.py +4 -4
- fastmcp/contrib/mcp_mixin/mcp_mixin.py +11 -4
- fastmcp/decorators.py +41 -0
- fastmcp/dependencies.py +12 -1
- fastmcp/exceptions.py +4 -0
- fastmcp/experimental/server/openapi/__init__.py +18 -15
- fastmcp/mcp_config.py +13 -4
- fastmcp/prompts/__init__.py +6 -3
- fastmcp/prompts/function_prompt.py +465 -0
- fastmcp/prompts/prompt.py +321 -271
- fastmcp/resources/__init__.py +5 -3
- fastmcp/resources/function_resource.py +335 -0
- fastmcp/resources/resource.py +325 -115
- fastmcp/resources/template.py +215 -43
- fastmcp/resources/types.py +27 -12
- fastmcp/server/__init__.py +2 -2
- fastmcp/server/auth/__init__.py +14 -0
- fastmcp/server/auth/auth.py +30 -10
- fastmcp/server/auth/authorization.py +190 -0
- fastmcp/server/auth/oauth_proxy/__init__.py +14 -0
- fastmcp/server/auth/oauth_proxy/consent.py +361 -0
- fastmcp/server/auth/oauth_proxy/models.py +178 -0
- fastmcp/server/auth/{oauth_proxy.py → oauth_proxy/proxy.py} +24 -778
- fastmcp/server/auth/oauth_proxy/ui.py +277 -0
- fastmcp/server/auth/oidc_proxy.py +2 -2
- fastmcp/server/auth/providers/auth0.py +24 -94
- fastmcp/server/auth/providers/aws.py +26 -95
- fastmcp/server/auth/providers/azure.py +41 -129
- fastmcp/server/auth/providers/descope.py +18 -49
- fastmcp/server/auth/providers/discord.py +25 -86
- fastmcp/server/auth/providers/github.py +23 -87
- fastmcp/server/auth/providers/google.py +24 -87
- fastmcp/server/auth/providers/introspection.py +60 -79
- fastmcp/server/auth/providers/jwt.py +30 -67
- fastmcp/server/auth/providers/oci.py +47 -110
- fastmcp/server/auth/providers/scalekit.py +23 -61
- fastmcp/server/auth/providers/supabase.py +18 -47
- fastmcp/server/auth/providers/workos.py +34 -127
- fastmcp/server/context.py +372 -419
- fastmcp/server/dependencies.py +541 -251
- fastmcp/server/elicitation.py +20 -18
- fastmcp/server/event_store.py +3 -3
- fastmcp/server/http.py +16 -6
- fastmcp/server/lifespan.py +198 -0
- fastmcp/server/low_level.py +92 -2
- fastmcp/server/middleware/__init__.py +5 -1
- fastmcp/server/middleware/authorization.py +312 -0
- fastmcp/server/middleware/caching.py +101 -54
- fastmcp/server/middleware/middleware.py +6 -9
- fastmcp/server/middleware/ping.py +70 -0
- fastmcp/server/middleware/tool_injection.py +2 -2
- fastmcp/server/mixins/__init__.py +7 -0
- fastmcp/server/mixins/lifespan.py +217 -0
- fastmcp/server/mixins/mcp_operations.py +392 -0
- fastmcp/server/mixins/transport.py +342 -0
- fastmcp/server/openapi/__init__.py +41 -21
- fastmcp/server/openapi/components.py +16 -339
- fastmcp/server/openapi/routing.py +34 -118
- fastmcp/server/openapi/server.py +67 -392
- fastmcp/server/providers/__init__.py +71 -0
- fastmcp/server/providers/aggregate.py +261 -0
- fastmcp/server/providers/base.py +578 -0
- fastmcp/server/providers/fastmcp_provider.py +674 -0
- fastmcp/server/providers/filesystem.py +226 -0
- fastmcp/server/providers/filesystem_discovery.py +327 -0
- fastmcp/server/providers/local_provider/__init__.py +11 -0
- fastmcp/server/providers/local_provider/decorators/__init__.py +15 -0
- fastmcp/server/providers/local_provider/decorators/prompts.py +256 -0
- fastmcp/server/providers/local_provider/decorators/resources.py +240 -0
- fastmcp/server/providers/local_provider/decorators/tools.py +315 -0
- fastmcp/server/providers/local_provider/local_provider.py +465 -0
- fastmcp/server/providers/openapi/__init__.py +39 -0
- fastmcp/server/providers/openapi/components.py +332 -0
- fastmcp/server/providers/openapi/provider.py +405 -0
- fastmcp/server/providers/openapi/routing.py +109 -0
- fastmcp/server/providers/proxy.py +867 -0
- fastmcp/server/providers/skills/__init__.py +59 -0
- fastmcp/server/providers/skills/_common.py +101 -0
- fastmcp/server/providers/skills/claude_provider.py +44 -0
- fastmcp/server/providers/skills/directory_provider.py +153 -0
- fastmcp/server/providers/skills/skill_provider.py +432 -0
- fastmcp/server/providers/skills/vendor_providers.py +142 -0
- fastmcp/server/providers/wrapped_provider.py +140 -0
- fastmcp/server/proxy.py +34 -700
- fastmcp/server/sampling/run.py +341 -2
- fastmcp/server/sampling/sampling_tool.py +4 -3
- fastmcp/server/server.py +1214 -2171
- fastmcp/server/tasks/__init__.py +2 -1
- fastmcp/server/tasks/capabilities.py +13 -1
- fastmcp/server/tasks/config.py +66 -3
- fastmcp/server/tasks/handlers.py +65 -273
- fastmcp/server/tasks/keys.py +4 -6
- fastmcp/server/tasks/requests.py +474 -0
- fastmcp/server/tasks/routing.py +76 -0
- fastmcp/server/tasks/subscriptions.py +20 -11
- fastmcp/server/telemetry.py +131 -0
- fastmcp/server/transforms/__init__.py +244 -0
- fastmcp/server/transforms/namespace.py +193 -0
- fastmcp/server/transforms/prompts_as_tools.py +175 -0
- fastmcp/server/transforms/resources_as_tools.py +190 -0
- fastmcp/server/transforms/tool_transform.py +96 -0
- fastmcp/server/transforms/version_filter.py +124 -0
- fastmcp/server/transforms/visibility.py +526 -0
- fastmcp/settings.py +34 -96
- fastmcp/telemetry.py +122 -0
- fastmcp/tools/__init__.py +10 -3
- fastmcp/tools/function_parsing.py +201 -0
- fastmcp/tools/function_tool.py +467 -0
- fastmcp/tools/tool.py +215 -362
- fastmcp/tools/tool_transform.py +38 -21
- fastmcp/utilities/async_utils.py +69 -0
- fastmcp/utilities/components.py +152 -91
- fastmcp/utilities/inspect.py +8 -20
- fastmcp/utilities/json_schema.py +12 -5
- fastmcp/utilities/json_schema_type.py +17 -15
- fastmcp/utilities/lifespan.py +56 -0
- fastmcp/utilities/logging.py +12 -4
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
- fastmcp/utilities/openapi/parser.py +3 -3
- fastmcp/utilities/pagination.py +80 -0
- fastmcp/utilities/skills.py +253 -0
- fastmcp/utilities/tests.py +0 -16
- fastmcp/utilities/timeout.py +47 -0
- fastmcp/utilities/types.py +1 -1
- fastmcp/utilities/versions.py +285 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/METADATA +8 -5
- fastmcp-3.0.0b1.dist-info/RECORD +228 -0
- fastmcp/client/transports.py +0 -1170
- fastmcp/contrib/component_manager/component_service.py +0 -209
- fastmcp/prompts/prompt_manager.py +0 -117
- fastmcp/resources/resource_manager.py +0 -338
- fastmcp/server/tasks/converters.py +0 -206
- fastmcp/server/tasks/protocol.py +0 -359
- fastmcp/tools/tool_manager.py +0 -170
- fastmcp/utilities/mcp_config.py +0 -56
- fastmcp-2.14.4.dist-info/RECORD +0 -161
- /fastmcp/server/{openapi → providers/openapi}/README.md +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/WHEEL +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""OpenAPI component classes: Tool, Resource, and ResourceTemplate."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import warnings
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
from mcp.types import ToolAnnotations
|
|
13
|
+
from pydantic.networks import AnyUrl
|
|
14
|
+
|
|
15
|
+
import fastmcp
|
|
16
|
+
from fastmcp.resources import (
|
|
17
|
+
Resource,
|
|
18
|
+
ResourceContent,
|
|
19
|
+
ResourceResult,
|
|
20
|
+
ResourceTemplate,
|
|
21
|
+
)
|
|
22
|
+
from fastmcp.server.dependencies import get_http_headers
|
|
23
|
+
from fastmcp.server.tasks.config import TaskConfig
|
|
24
|
+
from fastmcp.tools.tool import Tool, ToolResult
|
|
25
|
+
from fastmcp.utilities.logging import get_logger
|
|
26
|
+
from fastmcp.utilities.openapi import HTTPRoute
|
|
27
|
+
from fastmcp.utilities.openapi.director import RequestDirector
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from fastmcp.server import Context
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"OpenAPIResource",
|
|
34
|
+
"OpenAPIResourceTemplate",
|
|
35
|
+
"OpenAPITool",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
logger = get_logger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _slugify(text: str) -> str:
|
|
42
|
+
"""Convert text to a URL-friendly slug format.
|
|
43
|
+
|
|
44
|
+
Only contains lowercase letters, uppercase letters, numbers, and underscores.
|
|
45
|
+
"""
|
|
46
|
+
if not text:
|
|
47
|
+
return ""
|
|
48
|
+
|
|
49
|
+
# Replace spaces and common separators with underscores
|
|
50
|
+
slug = re.sub(r"[\s\-\.]+", "_", text)
|
|
51
|
+
|
|
52
|
+
# Remove non-alphanumeric characters except underscores
|
|
53
|
+
slug = re.sub(r"[^a-zA-Z0-9_]", "", slug)
|
|
54
|
+
|
|
55
|
+
# Remove multiple consecutive underscores
|
|
56
|
+
slug = re.sub(r"_+", "_", slug)
|
|
57
|
+
|
|
58
|
+
# Remove leading/trailing underscores
|
|
59
|
+
slug = slug.strip("_")
|
|
60
|
+
|
|
61
|
+
return slug
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class OpenAPITool(Tool):
|
|
65
|
+
"""Tool implementation for OpenAPI endpoints."""
|
|
66
|
+
|
|
67
|
+
task_config: TaskConfig = TaskConfig(mode="forbidden")
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
client: httpx.AsyncClient,
|
|
72
|
+
route: HTTPRoute,
|
|
73
|
+
director: RequestDirector,
|
|
74
|
+
name: str,
|
|
75
|
+
description: str,
|
|
76
|
+
parameters: dict[str, Any],
|
|
77
|
+
output_schema: dict[str, Any] | None = None,
|
|
78
|
+
tags: set[str] | None = None,
|
|
79
|
+
timeout: float | None = None,
|
|
80
|
+
annotations: ToolAnnotations | None = None,
|
|
81
|
+
serializer: Callable[[Any], str] | None = None, # Deprecated
|
|
82
|
+
):
|
|
83
|
+
if serializer is not None and fastmcp.settings.deprecation_warnings:
|
|
84
|
+
warnings.warn(
|
|
85
|
+
"The `serializer` parameter is deprecated. "
|
|
86
|
+
"Return ToolResult from your tools for full control over serialization. "
|
|
87
|
+
"See https://gofastmcp.com/servers/tools#custom-serialization for migration examples.",
|
|
88
|
+
DeprecationWarning,
|
|
89
|
+
stacklevel=2,
|
|
90
|
+
)
|
|
91
|
+
super().__init__(
|
|
92
|
+
name=name,
|
|
93
|
+
description=description,
|
|
94
|
+
parameters=parameters,
|
|
95
|
+
output_schema=output_schema,
|
|
96
|
+
tags=tags or set(),
|
|
97
|
+
annotations=annotations,
|
|
98
|
+
serializer=serializer,
|
|
99
|
+
)
|
|
100
|
+
self._client = client
|
|
101
|
+
self._route = route
|
|
102
|
+
self._director = director
|
|
103
|
+
self._timeout = timeout
|
|
104
|
+
|
|
105
|
+
def __repr__(self) -> str:
|
|
106
|
+
return f"OpenAPITool(name={self.name!r}, method={self._route.method}, path={self._route.path})"
|
|
107
|
+
|
|
108
|
+
async def run(self, arguments: dict[str, Any]) -> ToolResult:
|
|
109
|
+
"""Execute the HTTP request using RequestDirector."""
|
|
110
|
+
try:
|
|
111
|
+
base_url = str(self._client.base_url) or "http://localhost"
|
|
112
|
+
|
|
113
|
+
# Build the request using RequestDirector
|
|
114
|
+
request = self._director.build(self._route, arguments, base_url)
|
|
115
|
+
|
|
116
|
+
# Add client headers (lowest precedence)
|
|
117
|
+
if self._client.headers:
|
|
118
|
+
for key, value in self._client.headers.items():
|
|
119
|
+
if key not in request.headers:
|
|
120
|
+
request.headers[key] = value
|
|
121
|
+
|
|
122
|
+
# Add MCP transport headers (highest precedence)
|
|
123
|
+
mcp_headers = get_http_headers()
|
|
124
|
+
if mcp_headers:
|
|
125
|
+
request.headers.update(mcp_headers)
|
|
126
|
+
|
|
127
|
+
logger.debug(f"run - sending request; headers: {request.headers}")
|
|
128
|
+
|
|
129
|
+
response = await self._client.send(request)
|
|
130
|
+
response.raise_for_status()
|
|
131
|
+
|
|
132
|
+
# Try to parse as JSON first
|
|
133
|
+
try:
|
|
134
|
+
result = response.json()
|
|
135
|
+
|
|
136
|
+
# Handle structured content based on output schema
|
|
137
|
+
if self.output_schema is not None:
|
|
138
|
+
if self.output_schema.get("x-fastmcp-wrap-result"):
|
|
139
|
+
structured_output = {"result": result}
|
|
140
|
+
else:
|
|
141
|
+
structured_output = result
|
|
142
|
+
elif not isinstance(result, dict):
|
|
143
|
+
structured_output = {"result": result}
|
|
144
|
+
else:
|
|
145
|
+
structured_output = result
|
|
146
|
+
|
|
147
|
+
return ToolResult(structured_content=structured_output)
|
|
148
|
+
except json.JSONDecodeError:
|
|
149
|
+
return ToolResult(content=response.text)
|
|
150
|
+
|
|
151
|
+
except httpx.HTTPStatusError as e:
|
|
152
|
+
error_message = (
|
|
153
|
+
f"HTTP error {e.response.status_code}: {e.response.reason_phrase}"
|
|
154
|
+
)
|
|
155
|
+
try:
|
|
156
|
+
error_data = e.response.json()
|
|
157
|
+
error_message += f" - {error_data}"
|
|
158
|
+
except (json.JSONDecodeError, ValueError):
|
|
159
|
+
if e.response.text:
|
|
160
|
+
error_message += f" - {e.response.text}"
|
|
161
|
+
raise ValueError(error_message) from e
|
|
162
|
+
|
|
163
|
+
except httpx.RequestError as e:
|
|
164
|
+
raise ValueError(f"Request error: {e!s}") from e
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class OpenAPIResource(Resource):
|
|
168
|
+
"""Resource implementation for OpenAPI endpoints."""
|
|
169
|
+
|
|
170
|
+
task_config: TaskConfig = TaskConfig(mode="forbidden")
|
|
171
|
+
|
|
172
|
+
def __init__(
|
|
173
|
+
self,
|
|
174
|
+
client: httpx.AsyncClient,
|
|
175
|
+
route: HTTPRoute,
|
|
176
|
+
director: RequestDirector,
|
|
177
|
+
uri: str,
|
|
178
|
+
name: str,
|
|
179
|
+
description: str,
|
|
180
|
+
mime_type: str = "application/json",
|
|
181
|
+
tags: set[str] | None = None,
|
|
182
|
+
timeout: float | None = None,
|
|
183
|
+
):
|
|
184
|
+
super().__init__(
|
|
185
|
+
uri=AnyUrl(uri),
|
|
186
|
+
name=name,
|
|
187
|
+
description=description,
|
|
188
|
+
mime_type=mime_type,
|
|
189
|
+
tags=tags or set(),
|
|
190
|
+
)
|
|
191
|
+
self._client = client
|
|
192
|
+
self._route = route
|
|
193
|
+
self._director = director
|
|
194
|
+
self._timeout = timeout
|
|
195
|
+
|
|
196
|
+
def __repr__(self) -> str:
|
|
197
|
+
return f"OpenAPIResource(name={self.name!r}, uri={self.uri!r}, path={self._route.path})"
|
|
198
|
+
|
|
199
|
+
async def read(self) -> ResourceResult:
|
|
200
|
+
"""Fetch the resource data by making an HTTP request."""
|
|
201
|
+
try:
|
|
202
|
+
path = self._route.path
|
|
203
|
+
resource_uri = str(self.uri)
|
|
204
|
+
|
|
205
|
+
# If this is a templated resource, extract path parameters from the URI
|
|
206
|
+
if "{" in path and "}" in path:
|
|
207
|
+
parts = resource_uri.split("/")
|
|
208
|
+
|
|
209
|
+
if len(parts) > 1:
|
|
210
|
+
path_params = {}
|
|
211
|
+
param_matches = re.findall(r"\{([^}]+)\}", path)
|
|
212
|
+
if param_matches:
|
|
213
|
+
param_matches.sort(reverse=True)
|
|
214
|
+
expected_param_count = len(parts) - 1
|
|
215
|
+
for i, param_name in enumerate(param_matches):
|
|
216
|
+
if i < expected_param_count:
|
|
217
|
+
param_value = parts[-1 - i]
|
|
218
|
+
path_params[param_name] = param_value
|
|
219
|
+
|
|
220
|
+
for param_name, param_value in path_params.items():
|
|
221
|
+
path = path.replace(f"{{{param_name}}}", str(param_value))
|
|
222
|
+
|
|
223
|
+
# Build headers with correct precedence
|
|
224
|
+
headers: dict[str, str] = {}
|
|
225
|
+
if self._client.headers:
|
|
226
|
+
headers.update(self._client.headers)
|
|
227
|
+
mcp_headers = get_http_headers()
|
|
228
|
+
if mcp_headers:
|
|
229
|
+
headers.update(mcp_headers)
|
|
230
|
+
|
|
231
|
+
response = await self._client.request(
|
|
232
|
+
method=self._route.method,
|
|
233
|
+
url=path,
|
|
234
|
+
headers=headers,
|
|
235
|
+
timeout=self._timeout,
|
|
236
|
+
)
|
|
237
|
+
response.raise_for_status()
|
|
238
|
+
|
|
239
|
+
content_type = response.headers.get("content-type", "").lower()
|
|
240
|
+
|
|
241
|
+
if "application/json" in content_type:
|
|
242
|
+
result = response.json()
|
|
243
|
+
return ResourceResult(
|
|
244
|
+
contents=[
|
|
245
|
+
ResourceContent(
|
|
246
|
+
content=json.dumps(result), mime_type="application/json"
|
|
247
|
+
)
|
|
248
|
+
]
|
|
249
|
+
)
|
|
250
|
+
elif any(ct in content_type for ct in ["text/", "application/xml"]):
|
|
251
|
+
return ResourceResult(
|
|
252
|
+
contents=[
|
|
253
|
+
ResourceContent(content=response.text, mime_type=self.mime_type)
|
|
254
|
+
]
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
return ResourceResult(
|
|
258
|
+
contents=[
|
|
259
|
+
ResourceContent(
|
|
260
|
+
content=response.content, mime_type=self.mime_type
|
|
261
|
+
)
|
|
262
|
+
]
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
except httpx.HTTPStatusError as e:
|
|
266
|
+
error_message = (
|
|
267
|
+
f"HTTP error {e.response.status_code}: {e.response.reason_phrase}"
|
|
268
|
+
)
|
|
269
|
+
try:
|
|
270
|
+
error_data = e.response.json()
|
|
271
|
+
error_message += f" - {error_data}"
|
|
272
|
+
except (json.JSONDecodeError, ValueError):
|
|
273
|
+
if e.response.text:
|
|
274
|
+
error_message += f" - {e.response.text}"
|
|
275
|
+
raise ValueError(error_message) from e
|
|
276
|
+
|
|
277
|
+
except httpx.RequestError as e:
|
|
278
|
+
raise ValueError(f"Request error: {e!s}") from e
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class OpenAPIResourceTemplate(ResourceTemplate):
|
|
282
|
+
"""Resource template implementation for OpenAPI endpoints."""
|
|
283
|
+
|
|
284
|
+
task_config: TaskConfig = TaskConfig(mode="forbidden")
|
|
285
|
+
|
|
286
|
+
def __init__(
|
|
287
|
+
self,
|
|
288
|
+
client: httpx.AsyncClient,
|
|
289
|
+
route: HTTPRoute,
|
|
290
|
+
director: RequestDirector,
|
|
291
|
+
uri_template: str,
|
|
292
|
+
name: str,
|
|
293
|
+
description: str,
|
|
294
|
+
parameters: dict[str, Any],
|
|
295
|
+
tags: set[str] | None = None,
|
|
296
|
+
timeout: float | None = None,
|
|
297
|
+
):
|
|
298
|
+
super().__init__(
|
|
299
|
+
uri_template=uri_template,
|
|
300
|
+
name=name,
|
|
301
|
+
description=description,
|
|
302
|
+
parameters=parameters,
|
|
303
|
+
tags=tags or set(),
|
|
304
|
+
)
|
|
305
|
+
self._client = client
|
|
306
|
+
self._route = route
|
|
307
|
+
self._director = director
|
|
308
|
+
self._timeout = timeout
|
|
309
|
+
|
|
310
|
+
def __repr__(self) -> str:
|
|
311
|
+
return f"OpenAPIResourceTemplate(name={self.name!r}, uri_template={self.uri_template!r}, path={self._route.path})"
|
|
312
|
+
|
|
313
|
+
async def create_resource(
|
|
314
|
+
self,
|
|
315
|
+
uri: str,
|
|
316
|
+
params: dict[str, Any],
|
|
317
|
+
context: Context | None = None,
|
|
318
|
+
) -> Resource:
|
|
319
|
+
"""Create a resource with the given parameters."""
|
|
320
|
+
uri_parts = [f"{key}={value}" for key, value in params.items()]
|
|
321
|
+
|
|
322
|
+
return OpenAPIResource(
|
|
323
|
+
client=self._client,
|
|
324
|
+
route=self._route,
|
|
325
|
+
director=self._director,
|
|
326
|
+
uri=uri,
|
|
327
|
+
name=f"{self.name}-{'-'.join(uri_parts)}",
|
|
328
|
+
description=self.description or f"Resource for {self._route.path}",
|
|
329
|
+
mime_type="application/json",
|
|
330
|
+
tags=set(self._route.tags or []),
|
|
331
|
+
timeout=self._timeout,
|
|
332
|
+
)
|