Jeremiah Lowin commited on
Commit
db65850
·
1 Parent(s): e423e66

Update proxy behavior and conflict resolution

Browse files
src/fastmcp/server/proxy.py CHANGED
@@ -172,8 +172,11 @@ class FastMCPProxy(FastMCP):
172
  super().__init__(**kwargs)
173
  self.client = client
174
 
175
- async def get_tools(self) -> dict[str, Tool]:
176
- tools = await super().get_tools()
 
 
 
177
 
178
  async with self.client:
179
  try:
@@ -187,12 +190,17 @@ class FastMCPProxy(FastMCP):
187
  # don't overwrite tools defined in the server
188
  if tool.name not in tools:
189
  tool_proxy = await ProxyTool.from_client(self.client, tool)
190
- tools[tool_proxy.name] = tool_proxy
191
 
192
- return tools
193
 
194
- async def get_resources(self) -> dict[str, Resource]:
195
- resources = await super().get_resources()
 
 
 
 
 
196
 
197
  async with self.client:
198
  try:
@@ -204,16 +212,19 @@ class FastMCPProxy(FastMCP):
204
  raise e
205
  for resource in client_resources:
206
  # don't overwrite resources defined in the server
207
- if str(resource.uri) not in resources:
208
  resource_proxy = await ProxyResource.from_client(
209
  self.client, resource
210
  )
211
- resources[str(resource_proxy.uri)] = resource_proxy
212
 
213
- return resources
214
 
215
- async def get_resource_templates(self) -> dict[str, ResourceTemplate]:
216
- templates = await super().get_resource_templates()
 
 
 
217
 
218
  async with self.client:
219
  try:
@@ -231,10 +242,10 @@ class FastMCPProxy(FastMCP):
231
  )
232
  templates[template_proxy.uri_template] = template_proxy
233
 
234
- return templates
235
 
236
- async def get_prompts(self) -> dict[str, Prompt]:
237
- prompts = await super().get_prompts()
238
 
239
  async with self.client:
240
  try:
@@ -250,7 +261,7 @@ class FastMCPProxy(FastMCP):
250
  prompt_proxy = await ProxyPrompt.from_client(self.client, prompt)
251
  prompts[prompt_proxy.name] = prompt_proxy
252
 
253
- return prompts
254
 
255
  async def _call_tool(self, key: str, arguments: dict[str, Any]) -> list[MCPContent]:
256
  try:
 
172
  super().__init__(**kwargs)
173
  self.client = client
174
 
175
+ async def _list_tools(self, apply_middleware: bool = True) -> list[Tool]:
176
+ tools = {
177
+ tool.name: tool
178
+ for tool in await super()._list_tools(apply_middleware=apply_middleware)
179
+ }
180
 
181
  async with self.client:
182
  try:
 
190
  # don't overwrite tools defined in the server
191
  if tool.name not in tools:
192
  tool_proxy = await ProxyTool.from_client(self.client, tool)
193
+ tools[tool_proxy.key] = tool_proxy
194
 
195
+ return list(tools.values())
196
 
197
+ async def _list_resources(self, apply_middleware: bool = True) -> list[Resource]:
198
+ resources = {
199
+ resource.uri: resource
200
+ for resource in await super()._list_resources(
201
+ apply_middleware=apply_middleware
202
+ )
203
+ }
204
 
205
  async with self.client:
206
  try:
 
212
  raise e
213
  for resource in client_resources:
214
  # don't overwrite resources defined in the server
215
+ if resource.uri not in resources:
216
  resource_proxy = await ProxyResource.from_client(
217
  self.client, resource
218
  )
219
+ resources[resource_proxy.uri] = resource_proxy
220
 
221
+ return list(resources.values())
222
 
223
+ async def _list_resource_templates(self) -> list[ResourceTemplate]:
224
+ templates = {
225
+ template.uri_template: template
226
+ for template in await super()._list_resource_templates()
227
+ }
228
 
229
  async with self.client:
230
  try:
 
242
  )
243
  templates[template_proxy.uri_template] = template_proxy
244
 
245
+ return list(templates.values())
246
 
247
+ async def _list_prompts(self) -> list[Prompt]:
248
+ prompts = {prompt.name: prompt for prompt in await super()._list_prompts()}
249
 
250
  async with self.client:
251
  try:
 
261
  prompt_proxy = await ProxyPrompt.from_client(self.client, prompt)
262
  prompts[prompt_proxy.name] = prompt_proxy
263
 
264
+ return list(prompts.values())
265
 
266
  async def _call_tool(self, key: str, arguments: dict[str, Any]) -> list[MCPContent]:
267
  try:
src/fastmcp/server/server.py CHANGED
@@ -435,7 +435,7 @@ class FastMCP(Generic[LifespanResultT]):
435
 
436
  with fastmcp.server.context.Context(fastmcp=self):
437
  tools = await self._middleware_list_tools()
438
- return [tool.to_mcp_tool(name=tool.name) for tool in tools]
439
 
440
  async def _middleware_list_tools(self) -> list[Tool]:
441
  """
@@ -475,7 +475,7 @@ class FastMCP(Generic[LifespanResultT]):
475
  """
476
 
477
  if (tools := self._cache.get("tools")) is self._cache.NOT_FOUND:
478
- tools: list[Tool] = []
479
 
480
  # iterate such that new mounts overwrite older ones
481
  for mounted_server in self._mounted_servers:
@@ -490,17 +490,17 @@ class FastMCP(Generic[LifespanResultT]):
490
  if mounted_server.prefix:
491
  for tool in server_tools:
492
  tool = tool.with_key(f"{mounted_server.prefix}_{tool.key}")
493
- tools.append(tool)
494
  else:
495
- tools.extend(server_tools)
496
  except Exception as e:
497
  logger.warning(
498
  f"Failed to get tools from mounted server '{mounted_server.prefix}': {e}"
499
  )
500
  continue
501
- tools.extend(self._tool_manager.get_tools().values())
502
  self._cache.set("tools", tools)
503
- return tools
504
 
505
  async def _mcp_list_resources(self) -> list[MCPResource]:
506
  logger.debug("Handler called: list_resources")
@@ -549,7 +549,7 @@ class FastMCP(Generic[LifespanResultT]):
549
  """
550
 
551
  if (resources := self._cache.get("resources")) is self._cache.NOT_FOUND:
552
- resources: list[Resource] = []
553
 
554
  # iterate such that new mounts overwrite older ones
555
  for mounted_server in self._mounted_servers:
@@ -570,17 +570,19 @@ class FastMCP(Generic[LifespanResultT]):
570
  self.resource_prefix_format,
571
  )
572
  )
573
- resources.append(resource)
574
  else:
575
- resources.extend(server_resources)
 
 
576
  except Exception as e:
577
  logger.warning(
578
  f"Failed to get resources from mounted server '{mounted_server.prefix}': {e}"
579
  )
580
  continue
581
- resources.extend(self._resource_manager.get_resources().values())
582
  self._cache.set("resources", resources)
583
- return resources
584
 
585
  async def _mcp_list_resource_templates(self) -> list[MCPResourceTemplate]:
586
  logger.debug("Handler called: list_resource_templates")
@@ -634,7 +636,7 @@ class FastMCP(Generic[LifespanResultT]):
634
  if (
635
  templates := self._cache.get("resource_templates")
636
  ) is self._cache.NOT_FOUND:
637
- templates: list[ResourceTemplate] = []
638
 
639
  # iterate such that new mounts overwrite older ones
640
  for mounted_server in self._mounted_servers:
@@ -655,18 +657,20 @@ class FastMCP(Generic[LifespanResultT]):
655
  self.resource_prefix_format,
656
  )
657
  )
658
- templates.append(template)
659
  else:
660
- templates.extend(server_templates)
 
 
661
  except Exception as e:
662
  logger.warning(
663
  "Failed to get resource templates from mounted server "
664
  f"'{mounted_server.prefix}': {e}"
665
  )
666
  continue
667
- templates.extend(self._resource_manager.get_templates().values())
668
  self._cache.set("resource_templates", templates)
669
- return templates
670
 
671
  async def _mcp_list_prompts(self) -> list[MCPPrompt]:
672
  logger.debug("Handler called: list_prompts")
@@ -713,7 +717,7 @@ class FastMCP(Generic[LifespanResultT]):
713
  """
714
 
715
  if (prompts := self._cache.get("prompts")) is self._cache.NOT_FOUND:
716
- prompts: list[Prompt] = []
717
 
718
  # iterate such that new mounts overwrite older ones
719
  for mounted_server in self._mounted_servers:
@@ -730,17 +734,19 @@ class FastMCP(Generic[LifespanResultT]):
730
  prompt = prompt.with_key(
731
  f"{mounted_server.prefix}_{prompt.key}"
732
  )
733
- prompts.append(prompt)
734
  else:
735
- prompts.extend(server_prompts)
 
 
736
  except Exception as e:
737
  logger.warning(
738
  f"Failed to get prompts from mounted server '{mounted_server.prefix}': {e}"
739
  )
740
  continue
741
- prompts.extend(self._prompt_manager.get_prompts().values())
742
  self._cache.set("prompts", prompts)
743
- return prompts
744
 
745
  async def _mcp_call_tool(
746
  self, key: str, arguments: dict[str, Any]
 
435
 
436
  with fastmcp.server.context.Context(fastmcp=self):
437
  tools = await self._middleware_list_tools()
438
+ return [tool.to_mcp_tool(name=tool.key) for tool in tools]
439
 
440
  async def _middleware_list_tools(self) -> list[Tool]:
441
  """
 
475
  """
476
 
477
  if (tools := self._cache.get("tools")) is self._cache.NOT_FOUND:
478
+ tools: dict[str, Tool] = {}
479
 
480
  # iterate such that new mounts overwrite older ones
481
  for mounted_server in self._mounted_servers:
 
490
  if mounted_server.prefix:
491
  for tool in server_tools:
492
  tool = tool.with_key(f"{mounted_server.prefix}_{tool.key}")
493
+ tools[tool.key] = tool
494
  else:
495
+ tools.update({tool.key: tool for tool in server_tools})
496
  except Exception as e:
497
  logger.warning(
498
  f"Failed to get tools from mounted server '{mounted_server.prefix}': {e}"
499
  )
500
  continue
501
+ tools.update(self._tool_manager.get_tools())
502
  self._cache.set("tools", tools)
503
+ return list(tools.values())
504
 
505
  async def _mcp_list_resources(self) -> list[MCPResource]:
506
  logger.debug("Handler called: list_resources")
 
549
  """
550
 
551
  if (resources := self._cache.get("resources")) is self._cache.NOT_FOUND:
552
+ resources: dict[str, Resource] = {}
553
 
554
  # iterate such that new mounts overwrite older ones
555
  for mounted_server in self._mounted_servers:
 
570
  self.resource_prefix_format,
571
  )
572
  )
573
+ resources[resource.key] = resource
574
  else:
575
+ resources.update(
576
+ {resource.key: resource for resource in server_resources}
577
+ )
578
  except Exception as e:
579
  logger.warning(
580
  f"Failed to get resources from mounted server '{mounted_server.prefix}': {e}"
581
  )
582
  continue
583
+ resources.update(self._resource_manager.get_resources())
584
  self._cache.set("resources", resources)
585
+ return list(resources.values())
586
 
587
  async def _mcp_list_resource_templates(self) -> list[MCPResourceTemplate]:
588
  logger.debug("Handler called: list_resource_templates")
 
636
  if (
637
  templates := self._cache.get("resource_templates")
638
  ) is self._cache.NOT_FOUND:
639
+ templates: dict[str, ResourceTemplate] = {}
640
 
641
  # iterate such that new mounts overwrite older ones
642
  for mounted_server in self._mounted_servers:
 
657
  self.resource_prefix_format,
658
  )
659
  )
660
+ templates[template.key] = template
661
  else:
662
+ templates.update(
663
+ {template.key: template for template in server_templates}
664
+ )
665
  except Exception as e:
666
  logger.warning(
667
  "Failed to get resource templates from mounted server "
668
  f"'{mounted_server.prefix}': {e}"
669
  )
670
  continue
671
+ templates.update(self._resource_manager.get_templates())
672
  self._cache.set("resource_templates", templates)
673
+ return list(templates.values())
674
 
675
  async def _mcp_list_prompts(self) -> list[MCPPrompt]:
676
  logger.debug("Handler called: list_prompts")
 
717
  """
718
 
719
  if (prompts := self._cache.get("prompts")) is self._cache.NOT_FOUND:
720
+ prompts: dict[str, Prompt] = {}
721
 
722
  # iterate such that new mounts overwrite older ones
723
  for mounted_server in self._mounted_servers:
 
734
  prompt = prompt.with_key(
735
  f"{mounted_server.prefix}_{prompt.key}"
736
  )
737
+ prompts[prompt.key] = prompt
738
  else:
739
+ prompts.update(
740
+ {prompt.key: prompt for prompt in server_prompts}
741
+ )
742
  except Exception as e:
743
  logger.warning(
744
  f"Failed to get prompts from mounted server '{mounted_server.prefix}': {e}"
745
  )
746
  continue
747
+ prompts.update(self._prompt_manager.get_prompts())
748
  self._cache.set("prompts", prompts)
749
+ return list(prompts.values())
750
 
751
  async def _mcp_call_tool(
752
  self, key: str, arguments: dict[str, Any]