feat: add resource annotations support to FastMCP (#1468)

This commit is contained in:
Fenn Bailey 2025-10-14 21:07:37 +11:00 • committed by GitHub
commit cd7253c593
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 185 additions and 5 deletions

View file

@ -13,7 +13,7 @@ from pydantic import (
field_validator,
)
from mcp.types import Icon
from mcp.types import Annotations, Icon
class Resource(BaseModel, abc.ABC):
@ -31,6 +31,7 @@ class Resource(BaseModel, abc.ABC):
pattern=r"^[a-zA-Z0-9]+/[a-zA-Z0-9\-+.]+$",
)
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this resource")
annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource")
@field_validator("name", mode="before")
@classmethod

View file

@ -10,7 +10,7 @@ from pydantic import AnyUrl
from mcp.server.fastmcp.resources.base import Resource
from mcp.server.fastmcp.resources.templates import ResourceTemplate
from mcp.server.fastmcp.utilities.logging import get_logger
from mcp.types import Icon
from mcp.types import Annotations, Icon
if TYPE_CHECKING:
from mcp.server.fastmcp.server import Context
@ -63,6 +63,7 @@ class ResourceManager:
description: str | None = None,
mime_type: str | None = None,
icons: list[Icon] | None = None,
annotations: Annotations | None = None,
) -> ResourceTemplate:
"""Add a template from a function."""
template = ResourceTemplate.from_function(
@ -73,6 +74,7 @@ class ResourceManager:
description=description,
mime_type=mime_type,
icons=icons,
annotations=annotations,
)
self._templates[template.uri_template] = template
return template

View file

@ -12,7 +12,7 @@ from pydantic import BaseModel, Field, validate_call
from mcp.server.fastmcp.resources.types import FunctionResource, Resource
from mcp.server.fastmcp.utilities.context_injection import find_context_parameter, inject_context
from mcp.server.fastmcp.utilities.func_metadata import func_metadata
from mcp.types import Icon
from mcp.types import Annotations, Icon
if TYPE_CHECKING:
from mcp.server.fastmcp.server import Context
@ -29,6 +29,7 @@ class ResourceTemplate(BaseModel):
description: str | None = Field(description="Description of what the resource does")
mime_type: str = Field(default="text/plain", description="MIME type of the resource content")
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for the resource template")
annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource template")
fn: Callable[..., Any] = Field(exclude=True)
parameters: dict[str, Any] = Field(description="JSON schema for function parameters")
context_kwarg: str | None = Field(None, description="Name of the kwarg that should receive context")
@ -43,6 +44,7 @@ class ResourceTemplate(BaseModel):
description: str | None = None,
mime_type: str | None = None,
icons: list[Icon] | None = None,
annotations: Annotations | None = None,
context_kwarg: str | None = None,
) -> ResourceTemplate:
"""Create a template from a function."""
@ -71,6 +73,7 @@ class ResourceTemplate(BaseModel):
description=description or fn.__doc__ or "",
mime_type=mime_type or "text/plain",
icons=icons,
annotations=annotations,
fn=fn,
parameters=parameters,
context_kwarg=context_kwarg,
@ -108,6 +111,7 @@ class ResourceTemplate(BaseModel):
description=self.description,
mime_type=self.mime_type,
icons=self.icons,
annotations=self.annotations,
fn=lambda: result, # Capture result in closure
)
except Exception as e:

View file

@ -14,7 +14,7 @@ import pydantic_core
from pydantic import AnyUrl, Field, ValidationInfo, validate_call
from mcp.server.fastmcp.resources.base import Resource
from mcp.types import Icon
from mcp.types import Annotations, Icon
class TextResource(Resource):
@ -82,6 +82,7 @@ class FunctionResource(Resource):
description: str | None = None,
mime_type: str | None = None,
icons: list[Icon] | None = None,
annotations: Annotations | None = None,
) -> "FunctionResource":
"""Create a FunctionResource from a function."""
func_name = name or fn.__name__
@ -99,6 +100,7 @@ class FunctionResource(Resource):
mime_type=mime_type or "text/plain",
fn=fn,
icons=icons,
annotations=annotations,
)

View file

@ -43,7 +43,7 @@ from mcp.server.streamable_http import EventStore
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
from mcp.server.transport_security import TransportSecuritySettings
from mcp.shared.context import LifespanContextT, RequestContext, RequestT
from mcp.types import AnyFunction, ContentBlock, GetPromptResult, Icon, ToolAnnotations
from mcp.types import Annotations, AnyFunction, ContentBlock, GetPromptResult, Icon, ToolAnnotations
from mcp.types import Prompt as MCPPrompt
from mcp.types import PromptArgument as MCPPromptArgument
from mcp.types import Resource as MCPResource
@ -322,6 +322,7 @@ class FastMCP(Generic[LifespanResultT]):
description=resource.description,
mimeType=resource.mime_type,
icons=resource.icons,
annotations=resource.annotations,
)
for resource in resources
]
@ -336,6 +337,7 @@ class FastMCP(Generic[LifespanResultT]):
description=template.description,
mimeType=template.mime_type,
icons=template.icons,
annotations=template.annotations,
)
for template in templates
]
@ -497,6 +499,7 @@ class FastMCP(Generic[LifespanResultT]):
description: str | None = None,
mime_type: str | None = None,
icons: list[Icon] | None = None,
annotations: Annotations | None = None,
) -> Callable[[AnyFunction], AnyFunction]:
"""Decorator to register a function as a resource.
@ -572,6 +575,7 @@ class FastMCP(Generic[LifespanResultT]):
description=description,
mime_type=mime_type,
icons=icons,
annotations=annotations,
)
else:
# Register as regular resource
@ -583,6 +587,7 @@ class FastMCP(Generic[LifespanResultT]):
description=description,
mime_type=mime_type,
icons=icons,
annotations=annotations,
)
self.add_resource(resource)
return fn