Fix bug with duplicate behavior == ignore

This commit is contained in:
Jeremiah Lowin 2025-04-15 09:33:23 -04:00
commit 6e309e1354
12 changed files with 333 additions and 116 deletions

View file

@ -15,8 +15,19 @@ logger = get_logger(__name__)
class PromptManager:
"""Manages FastMCP prompts."""
def __init__(self, duplicate_behavior: DuplicateBehavior = DuplicateBehavior.WARN):
def __init__(self, duplicate_behavior: DuplicateBehavior | None = None):
self._prompts: dict[str, Prompt] = {}
# Default to "warn" if None is provided
if duplicate_behavior is None:
duplicate_behavior = "warn"
if duplicate_behavior not in DuplicateBehavior.__args__:
raise ValueError(
f"Invalid duplicate_behavior: {duplicate_behavior}. "
f"Must be one of: {', '.join(DuplicateBehavior.__args__)}"
)
self.duplicate_behavior = duplicate_behavior
def get_prompt(self, name: str) -> Prompt | None:
@ -44,17 +55,17 @@ class PromptManager:
# Check for duplicates
existing = self._prompts.get(prompt.name)
if existing:
if self.duplicate_behavior == DuplicateBehavior.WARN:
if self.duplicate_behavior == "warn":
logger.warning(f"Prompt already exists: {prompt.name}")
self._prompts[prompt.name] = prompt
elif self.duplicate_behavior == DuplicateBehavior.REPLACE:
elif self.duplicate_behavior == "replace":
self._prompts[prompt.name] = prompt
elif self.duplicate_behavior == DuplicateBehavior.ERROR:
elif self.duplicate_behavior == "error":
raise ValueError(f"Prompt already exists: {prompt.name}")
elif self.duplicate_behavior == DuplicateBehavior.IGNORE:
pass
self._prompts[prompt.name] = prompt
elif self.duplicate_behavior == "ignore":
return existing
else:
self._prompts[prompt.name] = prompt
return prompt
async def render_prompt(

View file

@ -19,9 +19,20 @@ logger = get_logger(__name__)
class ResourceManager:
"""Manages FastMCP resources."""
def __init__(self, duplicate_behavior: DuplicateBehavior = DuplicateBehavior.WARN):
def __init__(self, duplicate_behavior: DuplicateBehavior | None = None):
self._resources: dict[str, Resource] = {}
self._templates: dict[str, ResourceTemplate] = {}
# Default to "warn" if None is provided
if duplicate_behavior is None:
duplicate_behavior = "warn"
if duplicate_behavior not in DuplicateBehavior.__args__:
raise ValueError(
f"Invalid duplicate_behavior: {duplicate_behavior}. "
f"Must be one of: {', '.join(DuplicateBehavior.__args__)}"
)
self.duplicate_behavior = duplicate_behavior
def add_resource_or_template_from_fn(
@ -114,16 +125,17 @@ class ResourceManager:
)
existing = self._resources.get(str(resource.uri))
if existing:
if self.duplicate_behavior == DuplicateBehavior.WARN:
if self.duplicate_behavior == "warn":
logger.warning(f"Resource already exists: {resource.uri}")
self._resources[str(resource.uri)] = resource
elif self.duplicate_behavior == DuplicateBehavior.REPLACE:
elif self.duplicate_behavior == "replace":
self._resources[str(resource.uri)] = resource
elif self.duplicate_behavior == DuplicateBehavior.ERROR:
elif self.duplicate_behavior == "error":
raise ValueError(f"Resource already exists: {resource.uri}")
elif self.duplicate_behavior == DuplicateBehavior.IGNORE:
pass
self._resources[str(resource.uri)] = resource
elif self.duplicate_behavior == "ignore":
return existing
else:
self._resources[str(resource.uri)] = resource
return resource
def add_template_from_fn(
@ -167,16 +179,17 @@ class ResourceManager:
)
existing = self._templates.get(str(template.uri_template))
if existing:
if self.duplicate_behavior == DuplicateBehavior.WARN:
if self.duplicate_behavior == "warn":
logger.warning(f"Resource already exists: {template.uri_template}")
self._templates[str(template.uri_template)] = template
elif self.duplicate_behavior == DuplicateBehavior.REPLACE:
elif self.duplicate_behavior == "replace":
self._templates[str(template.uri_template)] = template
elif self.duplicate_behavior == DuplicateBehavior.ERROR:
elif self.duplicate_behavior == "error":
raise ValueError(f"Resource already exists: {template.uri_template}")
elif self.duplicate_behavior == DuplicateBehavior.IGNORE:
pass
self._templates[template.uri_template] = template
elif self.duplicate_behavior == "ignore":
return existing
else:
self._templates[template.uri_template] = template
return template
async def get_resource(self, uri: AnyUrl | str) -> Resource | None:

View file

@ -1,6 +1,5 @@
from __future__ import annotations as _annotations
from enum import Enum
from typing import TYPE_CHECKING, Literal
from pydantic import Field
@ -11,12 +10,7 @@ if TYPE_CHECKING:
LOG_LEVEL = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
class DuplicateBehavior(Enum):
WARN = "warn"
ERROR = "error"
REPLACE = "replace"
IGNORE = "ignore"
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
class Settings(BaseSettings):
@ -55,13 +49,13 @@ class ServerSettings(BaseSettings):
debug: bool = False
# resource settings
on_duplicate_resources: DuplicateBehavior = DuplicateBehavior.WARN
on_duplicate_resources: DuplicateBehavior = "warn"
# tool settings
on_duplicate_tools: DuplicateBehavior = DuplicateBehavior.WARN
on_duplicate_tools: DuplicateBehavior = "warn"
# prompt settings
on_duplicate_prompts: DuplicateBehavior = DuplicateBehavior.WARN
on_duplicate_prompts: DuplicateBehavior = "warn"
dependencies: list[str] = Field(
default_factory=list,

View file

@ -21,8 +21,19 @@ logger = get_logger(__name__)
class ToolManager:
"""Manages FastMCP tools."""
def __init__(self, duplicate_behavior: DuplicateBehavior = DuplicateBehavior.WARN):
def __init__(self, duplicate_behavior: DuplicateBehavior | None = None):
self._tools: dict[str, Tool] = {}
# Default to "warn" if None is provided
if duplicate_behavior is None:
duplicate_behavior = "warn"
if duplicate_behavior not in DuplicateBehavior.__args__:
raise ValueError(
f"Invalid duplicate_behavior: {duplicate_behavior}. "
f"Must be one of: {', '.join(DuplicateBehavior.__args__)}"
)
self.duplicate_behavior = duplicate_behavior
def get_tool(self, name: str) -> Tool | None:
@ -57,16 +68,17 @@ class ToolManager:
name = name or tool.name
existing = self._tools.get(name)
if existing:
if self.duplicate_behavior == DuplicateBehavior.WARN:
if self.duplicate_behavior == "warn":
logger.warning(f"Tool already exists: {name}")
self._tools[name] = tool
elif self.duplicate_behavior == DuplicateBehavior.REPLACE:
elif self.duplicate_behavior == "replace":
self._tools[name] = tool
elif self.duplicate_behavior == DuplicateBehavior.ERROR:
elif self.duplicate_behavior == "error":
raise ValueError(f"Tool already exists: {name}")
elif self.duplicate_behavior == DuplicateBehavior.IGNORE:
pass
self._tools[name] = tool
elif self.duplicate_behavior == "ignore":
return existing
else:
self._tools[name] = tool
return tool
async def call_tool(