fastmcp 2.1.2__py3-none-any.whl → 2.2.1__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/cli/cli.py +32 -0
- fastmcp/client/client.py +16 -15
- fastmcp/client/transports.py +28 -7
- fastmcp/exceptions.py +8 -0
- fastmcp/prompts/prompt.py +20 -1
- fastmcp/prompts/prompt_manager.py +37 -47
- fastmcp/resources/resource.py +20 -1
- fastmcp/resources/resource_manager.py +83 -116
- fastmcp/resources/template.py +81 -8
- fastmcp/server/openapi.py +10 -16
- fastmcp/server/proxy.py +102 -76
- fastmcp/server/server.py +321 -256
- fastmcp/settings.py +8 -11
- fastmcp/tools/tool.py +67 -7
- fastmcp/tools/tool_manager.py +46 -47
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.1.dist-info}/METADATA +14 -6
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.1.dist-info}/RECORD +20 -20
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.1.dist-info}/WHEEL +0 -0
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
"""Resource manager functionality."""
|
|
2
2
|
|
|
3
|
-
import copy
|
|
4
3
|
import inspect
|
|
5
|
-
import re
|
|
6
4
|
from collections.abc import Callable
|
|
7
5
|
from typing import Any
|
|
8
6
|
|
|
9
7
|
from pydantic import AnyUrl
|
|
10
8
|
|
|
11
|
-
from fastmcp.exceptions import
|
|
12
|
-
from fastmcp.resources import FunctionResource
|
|
13
|
-
from fastmcp.resources.
|
|
9
|
+
from fastmcp.exceptions import NotFoundError
|
|
10
|
+
from fastmcp.resources import FunctionResource
|
|
11
|
+
from fastmcp.resources.resource import Resource
|
|
12
|
+
from fastmcp.resources.template import (
|
|
13
|
+
ResourceTemplate,
|
|
14
|
+
match_uri_template,
|
|
15
|
+
)
|
|
14
16
|
from fastmcp.settings import DuplicateBehavior
|
|
15
17
|
from fastmcp.utilities.logging import get_logger
|
|
16
18
|
|
|
@@ -20,9 +22,20 @@ logger = get_logger(__name__)
|
|
|
20
22
|
class ResourceManager:
|
|
21
23
|
"""Manages FastMCP resources."""
|
|
22
24
|
|
|
23
|
-
def __init__(self, duplicate_behavior: DuplicateBehavior =
|
|
25
|
+
def __init__(self, duplicate_behavior: DuplicateBehavior | None = None):
|
|
24
26
|
self._resources: dict[str, Resource] = {}
|
|
25
27
|
self._templates: dict[str, ResourceTemplate] = {}
|
|
28
|
+
|
|
29
|
+
# Default to "warn" if None is provided
|
|
30
|
+
if duplicate_behavior is None:
|
|
31
|
+
duplicate_behavior = "warn"
|
|
32
|
+
|
|
33
|
+
if duplicate_behavior not in DuplicateBehavior.__args__:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"Invalid duplicate_behavior: {duplicate_behavior}. "
|
|
36
|
+
f"Must be one of: {', '.join(DuplicateBehavior.__args__)}"
|
|
37
|
+
)
|
|
38
|
+
|
|
26
39
|
self.duplicate_behavior = duplicate_behavior
|
|
27
40
|
|
|
28
41
|
def add_resource_or_template_from_fn(
|
|
@@ -52,7 +65,7 @@ class ResourceManager:
|
|
|
52
65
|
has_uri_params = "{" in uri and "}" in uri
|
|
53
66
|
has_func_params = bool(inspect.signature(fn).parameters)
|
|
54
67
|
|
|
55
|
-
if has_uri_params
|
|
68
|
+
if has_uri_params or has_func_params:
|
|
56
69
|
return self.add_template_from_fn(
|
|
57
70
|
fn, uri, name, description, mime_type, tags
|
|
58
71
|
)
|
|
@@ -99,32 +112,35 @@ class ResourceManager:
|
|
|
99
112
|
)
|
|
100
113
|
return self.add_resource(resource)
|
|
101
114
|
|
|
102
|
-
def add_resource(self, resource: Resource) -> Resource:
|
|
115
|
+
def add_resource(self, resource: Resource, key: str | None = None) -> Resource:
|
|
103
116
|
"""Add a resource to the manager.
|
|
104
117
|
|
|
105
118
|
Args:
|
|
106
119
|
resource: A Resource instance to add
|
|
120
|
+
key: Optional URI to use as the storage key (if different from resource.uri)
|
|
107
121
|
"""
|
|
122
|
+
storage_key = key or str(resource.uri)
|
|
108
123
|
logger.debug(
|
|
109
124
|
"Adding resource",
|
|
110
125
|
extra={
|
|
111
126
|
"uri": resource.uri,
|
|
127
|
+
"storage_key": storage_key,
|
|
112
128
|
"type": type(resource).__name__,
|
|
113
129
|
"resource_name": resource.name,
|
|
114
130
|
},
|
|
115
131
|
)
|
|
116
|
-
existing = self._resources.get(
|
|
132
|
+
existing = self._resources.get(storage_key)
|
|
117
133
|
if existing:
|
|
118
|
-
if self.duplicate_behavior ==
|
|
119
|
-
logger.warning(f"Resource already exists: {
|
|
120
|
-
self._resources[
|
|
121
|
-
elif self.duplicate_behavior ==
|
|
122
|
-
self._resources[
|
|
123
|
-
elif self.duplicate_behavior ==
|
|
124
|
-
raise ValueError(f"Resource already exists: {
|
|
125
|
-
elif self.duplicate_behavior ==
|
|
126
|
-
|
|
127
|
-
self._resources[
|
|
134
|
+
if self.duplicate_behavior == "warn":
|
|
135
|
+
logger.warning(f"Resource already exists: {storage_key}")
|
|
136
|
+
self._resources[storage_key] = resource
|
|
137
|
+
elif self.duplicate_behavior == "replace":
|
|
138
|
+
self._resources[storage_key] = resource
|
|
139
|
+
elif self.duplicate_behavior == "error":
|
|
140
|
+
raise ValueError(f"Resource already exists: {storage_key}")
|
|
141
|
+
elif self.duplicate_behavior == "ignore":
|
|
142
|
+
return existing
|
|
143
|
+
self._resources[storage_key] = resource
|
|
128
144
|
return resource
|
|
129
145
|
|
|
130
146
|
def add_template_from_fn(
|
|
@@ -138,16 +154,6 @@ class ResourceManager:
|
|
|
138
154
|
) -> ResourceTemplate:
|
|
139
155
|
"""Create a template from a function."""
|
|
140
156
|
|
|
141
|
-
# Validate that URI params match function params
|
|
142
|
-
uri_params = set(re.findall(r"{(\w+)}", uri_template))
|
|
143
|
-
func_params = set(inspect.signature(fn).parameters.keys())
|
|
144
|
-
|
|
145
|
-
if uri_params != func_params:
|
|
146
|
-
raise ValueError(
|
|
147
|
-
f"Mismatch between URI parameters {uri_params} "
|
|
148
|
-
f"and function parameters {func_params}"
|
|
149
|
-
)
|
|
150
|
-
|
|
151
157
|
template = ResourceTemplate.from_function(
|
|
152
158
|
fn,
|
|
153
159
|
uri_template=uri_template,
|
|
@@ -158,40 +164,60 @@ class ResourceManager:
|
|
|
158
164
|
)
|
|
159
165
|
return self.add_template(template)
|
|
160
166
|
|
|
161
|
-
def add_template(
|
|
167
|
+
def add_template(
|
|
168
|
+
self, template: ResourceTemplate, key: str | None = None
|
|
169
|
+
) -> ResourceTemplate:
|
|
162
170
|
"""Add a template to the manager.
|
|
163
171
|
|
|
164
172
|
Args:
|
|
165
173
|
template: A ResourceTemplate instance to add
|
|
174
|
+
key: Optional URI template to use as the storage key (if different from template.uri_template)
|
|
166
175
|
|
|
167
176
|
Returns:
|
|
168
177
|
The added template. If a template with the same URI already exists,
|
|
169
178
|
returns the existing template.
|
|
170
179
|
"""
|
|
180
|
+
uri_template_str = str(template.uri_template)
|
|
181
|
+
storage_key = key or uri_template_str
|
|
171
182
|
logger.debug(
|
|
172
|
-
"Adding
|
|
183
|
+
"Adding template",
|
|
173
184
|
extra={
|
|
174
|
-
"
|
|
185
|
+
"uri_template": uri_template_str,
|
|
186
|
+
"storage_key": storage_key,
|
|
175
187
|
"type": type(template).__name__,
|
|
176
|
-
"
|
|
188
|
+
"template_name": template.name,
|
|
177
189
|
},
|
|
178
190
|
)
|
|
179
|
-
existing = self._templates.get(
|
|
191
|
+
existing = self._templates.get(storage_key)
|
|
180
192
|
if existing:
|
|
181
|
-
if self.duplicate_behavior ==
|
|
182
|
-
logger.warning(f"
|
|
183
|
-
self._templates[
|
|
184
|
-
elif self.duplicate_behavior ==
|
|
185
|
-
self._templates[
|
|
186
|
-
elif self.duplicate_behavior ==
|
|
187
|
-
raise ValueError(f"
|
|
188
|
-
elif self.duplicate_behavior ==
|
|
189
|
-
|
|
190
|
-
self._templates[
|
|
193
|
+
if self.duplicate_behavior == "warn":
|
|
194
|
+
logger.warning(f"Template already exists: {storage_key}")
|
|
195
|
+
self._templates[storage_key] = template
|
|
196
|
+
elif self.duplicate_behavior == "replace":
|
|
197
|
+
self._templates[storage_key] = template
|
|
198
|
+
elif self.duplicate_behavior == "error":
|
|
199
|
+
raise ValueError(f"Template already exists: {storage_key}")
|
|
200
|
+
elif self.duplicate_behavior == "ignore":
|
|
201
|
+
return existing
|
|
202
|
+
self._templates[storage_key] = template
|
|
191
203
|
return template
|
|
192
204
|
|
|
193
|
-
|
|
194
|
-
"""
|
|
205
|
+
def has_resource(self, uri: AnyUrl | str) -> bool:
|
|
206
|
+
"""Check if a resource exists."""
|
|
207
|
+
uri_str = str(uri)
|
|
208
|
+
if uri_str in self._resources:
|
|
209
|
+
return True
|
|
210
|
+
for template_key in self._templates.keys():
|
|
211
|
+
if match_uri_template(uri_str, template_key):
|
|
212
|
+
return True
|
|
213
|
+
return False
|
|
214
|
+
|
|
215
|
+
async def get_resource(self, uri: AnyUrl | str) -> Resource:
|
|
216
|
+
"""Get resource by URI, checking concrete resources first, then templates.
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
NotFoundError: If no resource or template matching the URI is found.
|
|
220
|
+
"""
|
|
195
221
|
uri_str = str(uri)
|
|
196
222
|
logger.debug("Getting resource", extra={"uri": uri_str})
|
|
197
223
|
|
|
@@ -199,80 +225,21 @@ class ResourceManager:
|
|
|
199
225
|
if resource := self._resources.get(uri_str):
|
|
200
226
|
return resource
|
|
201
227
|
|
|
202
|
-
# Then check templates
|
|
203
|
-
for template in self._templates.
|
|
204
|
-
|
|
228
|
+
# Then check templates - use the utility function to match against storage keys
|
|
229
|
+
for storage_key, template in self._templates.items():
|
|
230
|
+
# Try to match against the storage key (which might be a custom key)
|
|
231
|
+
if params := match_uri_template(uri_str, storage_key):
|
|
205
232
|
try:
|
|
206
233
|
return await template.create_resource(uri_str, params)
|
|
207
234
|
except Exception as e:
|
|
208
235
|
raise ValueError(f"Error creating resource from template: {e}")
|
|
209
236
|
|
|
210
|
-
raise
|
|
211
|
-
|
|
212
|
-
def list_resources(self) -> list[Resource]:
|
|
213
|
-
"""List all registered resources."""
|
|
214
|
-
logger.debug("Listing resources", extra={"count": len(self._resources)})
|
|
215
|
-
return list(self._resources.values())
|
|
216
|
-
|
|
217
|
-
def list_templates(self) -> list[ResourceTemplate]:
|
|
218
|
-
"""List all registered templates."""
|
|
219
|
-
logger.debug("Listing templates", extra={"count": len(self._templates)})
|
|
220
|
-
return list(self._templates.values())
|
|
237
|
+
raise NotFoundError(f"Unknown resource: {uri_str}")
|
|
221
238
|
|
|
222
|
-
def
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
"""Import resources from another resource manager.
|
|
239
|
+
def get_resources(self) -> dict[str, Resource]:
|
|
240
|
+
"""Get all registered resources, keyed by URI."""
|
|
241
|
+
return self._resources
|
|
226
242
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
the original URI is used.
|
|
231
|
-
|
|
232
|
-
Args:
|
|
233
|
-
manager: The ResourceManager to import from
|
|
234
|
-
prefix: A prefix to apply to the resource URIs, including the delimiter.
|
|
235
|
-
For example, "app+" would result in URIs like "app+data://users".
|
|
236
|
-
If None, the original URI is used.
|
|
237
|
-
"""
|
|
238
|
-
for uri, resource in manager._resources.items():
|
|
239
|
-
# Create prefixed URI and copy the resource with the new URI
|
|
240
|
-
prefixed_uri = f"{prefix}{uri}" if prefix else uri
|
|
241
|
-
|
|
242
|
-
new_resource = copy.copy(resource)
|
|
243
|
-
new_resource.uri = AnyUrl(prefixed_uri)
|
|
244
|
-
|
|
245
|
-
# Store directly in resources dictionary
|
|
246
|
-
self.add_resource(new_resource)
|
|
247
|
-
logger.debug(f'Imported resource "{uri}" as "{prefixed_uri}"')
|
|
248
|
-
|
|
249
|
-
def import_templates(
|
|
250
|
-
self, manager: "ResourceManager", prefix: str | None = None
|
|
251
|
-
) -> None:
|
|
252
|
-
"""Import resource templates from another resource manager.
|
|
253
|
-
|
|
254
|
-
Templates are imported with a prefixed URI template if a prefix is provided.
|
|
255
|
-
For example, if a template has URI template "data://users/{id}" and you import
|
|
256
|
-
it with prefix "app+", the imported template will have URI template
|
|
257
|
-
"app+data://users/{id}". If no prefix is provided, the original URI template is used.
|
|
258
|
-
|
|
259
|
-
Args:
|
|
260
|
-
manager: The ResourceManager to import templates from
|
|
261
|
-
prefix: A prefix to apply to the template URIs, including the delimiter.
|
|
262
|
-
For example, "app+" would result in URI templates like "app+data://users/{id}".
|
|
263
|
-
If None, the original URI template is used.
|
|
264
|
-
"""
|
|
265
|
-
for uri_template, template in manager._templates.items():
|
|
266
|
-
# Create prefixed URI template and copy the template with the new URI template
|
|
267
|
-
prefixed_uri_template = (
|
|
268
|
-
f"{prefix}{uri_template}" if prefix else uri_template
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
new_template = copy.copy(template)
|
|
272
|
-
new_template.uri_template = prefixed_uri_template
|
|
273
|
-
|
|
274
|
-
# Store directly in templates dictionary
|
|
275
|
-
self.add_template(new_template)
|
|
276
|
-
logger.debug(
|
|
277
|
-
f'Imported template "{uri_template}" as "{prefixed_uri_template}"'
|
|
278
|
-
)
|
|
243
|
+
def get_templates(self) -> dict[str, ResourceTemplate]:
|
|
244
|
+
"""Get all registered templates, keyed by URI template."""
|
|
245
|
+
return self._templates
|
fastmcp/resources/template.py
CHANGED
|
@@ -6,13 +6,49 @@ import inspect
|
|
|
6
6
|
import re
|
|
7
7
|
from collections.abc import Callable
|
|
8
8
|
from typing import Annotated, Any
|
|
9
|
+
from urllib.parse import unquote
|
|
9
10
|
|
|
10
|
-
from
|
|
11
|
+
from mcp.types import ResourceTemplate as MCPResourceTemplate
|
|
12
|
+
from pydantic import (
|
|
13
|
+
AnyUrl,
|
|
14
|
+
BaseModel,
|
|
15
|
+
BeforeValidator,
|
|
16
|
+
Field,
|
|
17
|
+
TypeAdapter,
|
|
18
|
+
field_validator,
|
|
19
|
+
validate_call,
|
|
20
|
+
)
|
|
11
21
|
|
|
12
22
|
from fastmcp.resources.types import FunctionResource, Resource
|
|
13
23
|
from fastmcp.utilities.types import _convert_set_defaults
|
|
14
24
|
|
|
15
25
|
|
|
26
|
+
def build_regex(template: str) -> re.Pattern:
|
|
27
|
+
# Escape all non-brace characters, then restore {var} placeholders
|
|
28
|
+
parts = re.split(r"(\{[^}]+\})", template)
|
|
29
|
+
pattern = ""
|
|
30
|
+
for part in parts:
|
|
31
|
+
if part.startswith("{") and part.endswith("}"):
|
|
32
|
+
name = part[1:-1]
|
|
33
|
+
pattern += f"(?P<{name}>[^/]+)"
|
|
34
|
+
else:
|
|
35
|
+
pattern += re.escape(part)
|
|
36
|
+
return re.compile(f"^{pattern}$")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None:
|
|
40
|
+
regex = build_regex(uri_template)
|
|
41
|
+
match = regex.match(uri)
|
|
42
|
+
if match:
|
|
43
|
+
return {k: unquote(v) for k, v in match.groupdict().items()}
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MyModel(BaseModel):
|
|
48
|
+
key: str
|
|
49
|
+
value: int
|
|
50
|
+
|
|
51
|
+
|
|
16
52
|
class ResourceTemplate(BaseModel):
|
|
17
53
|
"""A template for dynamically creating resources."""
|
|
18
54
|
|
|
@@ -32,6 +68,14 @@ class ResourceTemplate(BaseModel):
|
|
|
32
68
|
description="JSON schema for function parameters"
|
|
33
69
|
)
|
|
34
70
|
|
|
71
|
+
@field_validator("mime_type", mode="before")
|
|
72
|
+
@classmethod
|
|
73
|
+
def set_default_mime_type(cls, mime_type: str | None) -> str:
|
|
74
|
+
"""Set default MIME type if not provided."""
|
|
75
|
+
if mime_type:
|
|
76
|
+
return mime_type
|
|
77
|
+
return "text/plain"
|
|
78
|
+
|
|
35
79
|
@classmethod
|
|
36
80
|
def from_function(
|
|
37
81
|
cls,
|
|
@@ -47,6 +91,30 @@ class ResourceTemplate(BaseModel):
|
|
|
47
91
|
if func_name == "<lambda>":
|
|
48
92
|
raise ValueError("You must provide a name for lambda functions")
|
|
49
93
|
|
|
94
|
+
# Validate that URI params match function params
|
|
95
|
+
uri_params = set(re.findall(r"{(\w+)}", uri_template))
|
|
96
|
+
if not uri_params:
|
|
97
|
+
raise ValueError("URI template must contain at least one parameter")
|
|
98
|
+
|
|
99
|
+
func_params = set(inspect.signature(fn).parameters.keys())
|
|
100
|
+
|
|
101
|
+
# get the parameters that are required
|
|
102
|
+
required_params = {
|
|
103
|
+
p
|
|
104
|
+
for p in func_params
|
|
105
|
+
if inspect.signature(fn).parameters[p].default is inspect.Parameter.empty
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if not required_params.issubset(uri_params):
|
|
109
|
+
raise ValueError(
|
|
110
|
+
f"URI parameters {uri_params} must be a subset of the required function arguments: {required_params}"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if not uri_params.issubset(func_params):
|
|
114
|
+
raise ValueError(
|
|
115
|
+
f"URI parameters {uri_params} must be a subset of the function arguments: {func_params}"
|
|
116
|
+
)
|
|
117
|
+
|
|
50
118
|
# Get schema from TypeAdapter - will fail if function isn't properly typed
|
|
51
119
|
parameters = TypeAdapter(fn).json_schema()
|
|
52
120
|
|
|
@@ -65,12 +133,7 @@ class ResourceTemplate(BaseModel):
|
|
|
65
133
|
|
|
66
134
|
def matches(self, uri: str) -> dict[str, Any] | None:
|
|
67
135
|
"""Check if URI matches template and extract parameters."""
|
|
68
|
-
|
|
69
|
-
pattern = self.uri_template.replace("{", "(?P<").replace("}", ">[^/]+)")
|
|
70
|
-
match = re.match(f"^{pattern}$", uri)
|
|
71
|
-
if match:
|
|
72
|
-
return match.groupdict()
|
|
73
|
-
return None
|
|
136
|
+
return match_uri_template(uri, self.uri_template)
|
|
74
137
|
|
|
75
138
|
async def create_resource(self, uri: str, params: dict[str, Any]) -> Resource:
|
|
76
139
|
"""Create a resource from the template with the given parameters."""
|
|
@@ -81,7 +144,7 @@ class ResourceTemplate(BaseModel):
|
|
|
81
144
|
result = await result
|
|
82
145
|
|
|
83
146
|
return FunctionResource(
|
|
84
|
-
uri=uri, #
|
|
147
|
+
uri=AnyUrl(uri), # Explicitly convert to AnyUrl
|
|
85
148
|
name=self.name,
|
|
86
149
|
description=self.description,
|
|
87
150
|
mime_type=self.mime_type,
|
|
@@ -95,3 +158,13 @@ class ResourceTemplate(BaseModel):
|
|
|
95
158
|
if not isinstance(other, ResourceTemplate):
|
|
96
159
|
return False
|
|
97
160
|
return self.model_dump() == other.model_dump()
|
|
161
|
+
|
|
162
|
+
def to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate:
|
|
163
|
+
"""Convert the resource template to an MCPResourceTemplate."""
|
|
164
|
+
kwargs = {
|
|
165
|
+
"uriTemplate": self.uri_template,
|
|
166
|
+
"name": self.name,
|
|
167
|
+
"description": self.description,
|
|
168
|
+
"mimeType": self.mime_type,
|
|
169
|
+
}
|
|
170
|
+
return MCPResourceTemplate(**kwargs | overrides)
|
fastmcp/server/openapi.py
CHANGED
|
@@ -8,6 +8,7 @@ from re import Pattern
|
|
|
8
8
|
from typing import Any, Literal
|
|
9
9
|
|
|
10
10
|
import httpx
|
|
11
|
+
from mcp.types import TextContent
|
|
11
12
|
from pydantic.networks import AnyUrl
|
|
12
13
|
|
|
13
14
|
from fastmcp.resources import Resource, ResourceTemplate
|
|
@@ -613,25 +614,18 @@ class FastMCPOpenAPI(FastMCP):
|
|
|
613
614
|
f"Registered TEMPLATE: {uri_template_str} ({route.method} {route.path}) with tags: {route.tags}"
|
|
614
615
|
)
|
|
615
616
|
|
|
616
|
-
async def
|
|
617
|
-
"""Override the call_tool method to return the raw result without converting to content.
|
|
617
|
+
async def _mcp_call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
618
|
+
"""Override the call_tool method to return the raw result without converting to content."""
|
|
618
619
|
|
|
619
|
-
For testing purposes, if specific tools are called, we convert the result to the expected object.
|
|
620
|
-
"""
|
|
621
620
|
context = self.get_context()
|
|
622
621
|
result = await self._tool_manager.call_tool(name, arguments, context=context)
|
|
623
622
|
|
|
624
|
-
# For
|
|
625
|
-
if
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
if isinstance(result, dict):
|
|
632
|
-
return User(**result)
|
|
633
|
-
except ImportError:
|
|
634
|
-
# If User class not found, just return the raw result
|
|
635
|
-
pass
|
|
623
|
+
# For other tools, ensure the response is wrapped in TextContent
|
|
624
|
+
if isinstance(result, dict | str):
|
|
625
|
+
if isinstance(result, dict):
|
|
626
|
+
result_text = json.dumps(result)
|
|
627
|
+
else:
|
|
628
|
+
result_text = result
|
|
629
|
+
return [TextContent(text=result_text, type="text")]
|
|
636
630
|
|
|
637
631
|
return result
|