fastmcp 2.1.2__py3-none-any.whl → 2.2.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/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 +319 -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.0.dist-info}/METADATA +6 -5
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.0.dist-info}/RECORD +20 -20
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.0.dist-info}/WHEEL +0 -0
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.0.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.1.2.dist-info → fastmcp-2.2.0.dist-info}/licenses/LICENSE +0 -0
fastmcp/server/proxy.py
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
from typing import Any, cast
|
|
2
|
+
from urllib.parse import quote
|
|
2
3
|
|
|
3
4
|
import mcp.types
|
|
4
|
-
from mcp.
|
|
5
|
+
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
|
6
|
+
from mcp.types import (
|
|
7
|
+
BlobResourceContents,
|
|
8
|
+
EmbeddedResource,
|
|
9
|
+
GetPromptResult,
|
|
10
|
+
ImageContent,
|
|
11
|
+
TextContent,
|
|
12
|
+
TextResourceContents,
|
|
13
|
+
)
|
|
14
|
+
from pydantic.networks import AnyUrl
|
|
5
15
|
|
|
6
|
-
import fastmcp
|
|
7
16
|
from fastmcp.client import Client
|
|
17
|
+
from fastmcp.exceptions import NotFoundError
|
|
8
18
|
from fastmcp.prompts import Message, Prompt
|
|
9
19
|
from fastmcp.resources import Resource, ResourceTemplate
|
|
10
20
|
from fastmcp.server.context import Context
|
|
@@ -104,8 +114,14 @@ class ProxyTemplate(ResourceTemplate):
|
|
|
104
114
|
)
|
|
105
115
|
|
|
106
116
|
async def create_resource(self, uri: str, params: dict[str, Any]) -> ProxyResource:
|
|
117
|
+
# dont use the provided uri, because it may not be the same as the
|
|
118
|
+
# uri_template on the remote server.
|
|
119
|
+
# quote params to ensure they are valid for the uri_template
|
|
120
|
+
parameterized_uri = self.uri_template.format(
|
|
121
|
+
**{k: quote(v, safe="") for k, v in params.items()}
|
|
122
|
+
)
|
|
107
123
|
async with self._client:
|
|
108
|
-
result = await self._client.read_resource(
|
|
124
|
+
result = await self._client.read_resource(parameterized_uri)
|
|
109
125
|
|
|
110
126
|
if isinstance(result[0], TextResourceContents):
|
|
111
127
|
value = result[0].text
|
|
@@ -116,7 +132,7 @@ class ProxyTemplate(ResourceTemplate):
|
|
|
116
132
|
|
|
117
133
|
return ProxyResource(
|
|
118
134
|
client=self._client,
|
|
119
|
-
uri=
|
|
135
|
+
uri=parameterized_uri,
|
|
120
136
|
name=self.name,
|
|
121
137
|
description=self.description,
|
|
122
138
|
mime_type=result[0].mimeType,
|
|
@@ -145,79 +161,89 @@ class ProxyPrompt(Prompt):
|
|
|
145
161
|
async def render(self, arguments: dict[str, Any]) -> list[Message]:
|
|
146
162
|
async with self._client:
|
|
147
163
|
result = await self._client.get_prompt(self.name, arguments)
|
|
148
|
-
return [Message(role=m.role, content=m.content) for m in result
|
|
164
|
+
return [Message(role=m.role, content=m.content) for m in result]
|
|
149
165
|
|
|
150
166
|
|
|
151
167
|
class FastMCPProxy(FastMCP):
|
|
152
|
-
def __init__(self,
|
|
153
|
-
if not _async_constructor:
|
|
154
|
-
raise ValueError(
|
|
155
|
-
"FastMCPProxy() was initialied unexpectedly. Please use a constructor like `FastMCPProxy.from_client()` instead."
|
|
156
|
-
)
|
|
168
|
+
def __init__(self, client: "Client", **kwargs):
|
|
157
169
|
super().__init__(**kwargs)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
async def
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
170
|
+
self.client = client
|
|
171
|
+
|
|
172
|
+
async def get_tools(self) -> dict[str, Tool]:
|
|
173
|
+
tools = await super().get_tools()
|
|
174
|
+
|
|
175
|
+
async with self.client:
|
|
176
|
+
for tool in await self.client.list_tools():
|
|
177
|
+
tool_proxy = await ProxyTool.from_client(self.client, tool)
|
|
178
|
+
tools[tool_proxy.name] = tool_proxy
|
|
179
|
+
|
|
180
|
+
return tools
|
|
181
|
+
|
|
182
|
+
async def get_resources(self) -> dict[str, Resource]:
|
|
183
|
+
resources = await super().get_resources()
|
|
184
|
+
|
|
185
|
+
async with self.client:
|
|
186
|
+
for resource in await self.client.list_resources():
|
|
187
|
+
resource_proxy = await ProxyResource.from_client(self.client, resource)
|
|
188
|
+
resources[str(resource_proxy.uri)] = resource_proxy
|
|
189
|
+
|
|
190
|
+
return resources
|
|
191
|
+
|
|
192
|
+
async def get_resource_templates(self) -> dict[str, ResourceTemplate]:
|
|
193
|
+
templates = await super().get_resource_templates()
|
|
194
|
+
|
|
195
|
+
async with self.client:
|
|
196
|
+
for template in await self.client.list_resource_templates():
|
|
197
|
+
template_proxy = await ProxyTemplate.from_client(self.client, template)
|
|
198
|
+
templates[template_proxy.uri_template] = template_proxy
|
|
199
|
+
|
|
200
|
+
return templates
|
|
201
|
+
|
|
202
|
+
async def get_prompts(self) -> dict[str, Prompt]:
|
|
203
|
+
prompts = await super().get_prompts()
|
|
204
|
+
|
|
205
|
+
async with self.client:
|
|
206
|
+
for prompt in await self.client.list_prompts():
|
|
207
|
+
prompt_proxy = await ProxyPrompt.from_client(self.client, prompt)
|
|
208
|
+
prompts[prompt_proxy.name] = prompt_proxy
|
|
209
|
+
return prompts
|
|
210
|
+
|
|
211
|
+
async def _mcp_call_tool(
|
|
212
|
+
self, key: str, arguments: dict[str, Any]
|
|
213
|
+
) -> list[TextContent | ImageContent | EmbeddedResource]:
|
|
214
|
+
try:
|
|
215
|
+
result = await super()._mcp_call_tool(key, arguments)
|
|
216
|
+
return result
|
|
217
|
+
except NotFoundError:
|
|
218
|
+
async with self.client:
|
|
219
|
+
result = await self.client.call_tool(key, arguments)
|
|
220
|
+
return result
|
|
221
|
+
|
|
222
|
+
async def _mcp_read_resource(self, uri: AnyUrl | str) -> list[ReadResourceContents]:
|
|
223
|
+
try:
|
|
224
|
+
result = await super()._mcp_read_resource(uri)
|
|
225
|
+
return result
|
|
226
|
+
except NotFoundError:
|
|
227
|
+
async with self.client:
|
|
228
|
+
resource = await self.client.read_resource(uri)
|
|
229
|
+
if isinstance(resource[0], TextResourceContents):
|
|
230
|
+
content = resource[0].text
|
|
231
|
+
elif isinstance(resource[0], BlobResourceContents):
|
|
232
|
+
content = resource[0].blob
|
|
233
|
+
else:
|
|
234
|
+
raise ValueError(f"Unsupported content type: {type(resource[0])}")
|
|
235
|
+
|
|
236
|
+
return [
|
|
237
|
+
ReadResourceContents(content=content, mime_type=resource[0].mimeType)
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
async def _mcp_get_prompt(
|
|
241
|
+
self, name: str, arguments: dict[str, Any] | None = None
|
|
242
|
+
) -> GetPromptResult:
|
|
243
|
+
try:
|
|
244
|
+
result = await super()._mcp_get_prompt(name, arguments)
|
|
245
|
+
return result
|
|
246
|
+
except NotFoundError:
|
|
247
|
+
async with self.client:
|
|
248
|
+
result = await self.client.get_prompt(name, arguments)
|
|
249
|
+
return GetPromptResult(messages=result)
|