Skip to content

templates

Resource template functionality.

ResourceTemplate

Bases: BaseModel

A template for dynamically creating resources.

Source code in src/mcp/server/mcpserver/resources/templates.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
class ResourceTemplate(BaseModel):
    """A template for dynamically creating resources."""

    uri_template: str = Field(description="URI template with parameters (e.g. weather://{city}/current)")
    name: str = Field(description="Name of the resource")
    title: str | None = Field(description="Human-readable title of the resource", default=None)
    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")
    meta: dict[str, Any] | None = Field(default=None, description="Optional metadata for this 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")

    @classmethod
    def from_function(
        cls,
        fn: Callable[..., Any],
        uri_template: str,
        name: str | None = None,
        title: str | None = None,
        description: str | None = None,
        mime_type: str | None = None,
        icons: list[Icon] | None = None,
        annotations: Annotations | None = None,
        meta: dict[str, Any] | None = None,
        context_kwarg: str | None = None,
    ) -> ResourceTemplate:
        """Create a template from a function."""
        func_name = name or fn.__name__
        if func_name == "<lambda>":
            raise ValueError("You must provide a name for lambda functions")  # pragma: no cover

        # Find context parameter if it exists
        if context_kwarg is None:  # pragma: no branch
            context_kwarg = find_context_parameter(fn)

        # Get schema from func_metadata, excluding context parameter
        func_arg_metadata = func_metadata(
            fn,
            skip_names=[context_kwarg] if context_kwarg is not None else [],
        )
        parameters = func_arg_metadata.arg_model.model_json_schema()

        # ensure the arguments are properly cast
        fn = validate_call(fn)

        return cls(
            uri_template=uri_template,
            name=func_name,
            title=title,
            description=description or fn.__doc__ or "",
            mime_type=mime_type or "text/plain",
            icons=icons,
            annotations=annotations,
            meta=meta,
            fn=fn,
            parameters=parameters,
            context_kwarg=context_kwarg,
        )

    def matches(self, uri: str) -> dict[str, Any] | None:
        """Check if URI matches template and extract parameters.

        Extracted parameters are URL-decoded to handle percent-encoded characters.
        """
        # Convert template to regex pattern
        pattern = self.uri_template.replace("{", "(?P<").replace("}", ">[^/]+)")
        match = re.match(f"^{pattern}$", uri)
        if match:
            # URL-decode all extracted parameter values
            return {key: unquote(value) for key, value in match.groupdict().items()}
        return None

    async def create_resource(
        self,
        uri: str,
        params: dict[str, Any],
        context: Context[LifespanContextT, RequestT],
    ) -> Resource:
        """Create a resource from the template with the given parameters.

        Raises:
            ValueError: If creating the resource fails.
        """
        try:
            # Add context to params if needed
            params = inject_context(self.fn, params, context, self.context_kwarg)

            fn = self.fn
            if is_async_callable(fn):
                result = await fn(**params)
            else:
                result = await anyio.to_thread.run_sync(functools.partial(self.fn, **params))

            return FunctionResource(
                uri=uri,  # type: ignore
                name=self.name,
                title=self.title,
                description=self.description,
                mime_type=self.mime_type,
                icons=self.icons,
                annotations=self.annotations,
                meta=self.meta,
                fn=lambda: result,  # Capture result in closure
            )
        except Exception as e:
            raise ValueError(f"Error creating resource from template: {e}")

from_function classmethod

from_function(
    fn: Callable[..., Any],
    uri_template: str,
    name: str | None = None,
    title: str | None = None,
    description: str | None = None,
    mime_type: str | None = None,
    icons: list[Icon] | None = None,
    annotations: Annotations | None = None,
    meta: dict[str, Any] | None = None,
    context_kwarg: str | None = None,
) -> ResourceTemplate

Create a template from a function.

Source code in src/mcp/server/mcpserver/resources/templates.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@classmethod
def from_function(
    cls,
    fn: Callable[..., Any],
    uri_template: str,
    name: str | None = None,
    title: str | None = None,
    description: str | None = None,
    mime_type: str | None = None,
    icons: list[Icon] | None = None,
    annotations: Annotations | None = None,
    meta: dict[str, Any] | None = None,
    context_kwarg: str | None = None,
) -> ResourceTemplate:
    """Create a template from a function."""
    func_name = name or fn.__name__
    if func_name == "<lambda>":
        raise ValueError("You must provide a name for lambda functions")  # pragma: no cover

    # Find context parameter if it exists
    if context_kwarg is None:  # pragma: no branch
        context_kwarg = find_context_parameter(fn)

    # Get schema from func_metadata, excluding context parameter
    func_arg_metadata = func_metadata(
        fn,
        skip_names=[context_kwarg] if context_kwarg is not None else [],
    )
    parameters = func_arg_metadata.arg_model.model_json_schema()

    # ensure the arguments are properly cast
    fn = validate_call(fn)

    return cls(
        uri_template=uri_template,
        name=func_name,
        title=title,
        description=description or fn.__doc__ or "",
        mime_type=mime_type or "text/plain",
        icons=icons,
        annotations=annotations,
        meta=meta,
        fn=fn,
        parameters=parameters,
        context_kwarg=context_kwarg,
    )

matches

matches(uri: str) -> dict[str, Any] | None

Check if URI matches template and extract parameters.

Extracted parameters are URL-decoded to handle percent-encoded characters.

Source code in src/mcp/server/mcpserver/resources/templates.py
87
88
89
90
91
92
93
94
95
96
97
98
def matches(self, uri: str) -> dict[str, Any] | None:
    """Check if URI matches template and extract parameters.

    Extracted parameters are URL-decoded to handle percent-encoded characters.
    """
    # Convert template to regex pattern
    pattern = self.uri_template.replace("{", "(?P<").replace("}", ">[^/]+)")
    match = re.match(f"^{pattern}$", uri)
    if match:
        # URL-decode all extracted parameter values
        return {key: unquote(value) for key, value in match.groupdict().items()}
    return None

create_resource async

create_resource(
    uri: str,
    params: dict[str, Any],
    context: Context[LifespanContextT, RequestT],
) -> Resource

Create a resource from the template with the given parameters.

Raises:

Type Description
ValueError

If creating the resource fails.

Source code in src/mcp/server/mcpserver/resources/templates.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
async def create_resource(
    self,
    uri: str,
    params: dict[str, Any],
    context: Context[LifespanContextT, RequestT],
) -> Resource:
    """Create a resource from the template with the given parameters.

    Raises:
        ValueError: If creating the resource fails.
    """
    try:
        # Add context to params if needed
        params = inject_context(self.fn, params, context, self.context_kwarg)

        fn = self.fn
        if is_async_callable(fn):
            result = await fn(**params)
        else:
            result = await anyio.to_thread.run_sync(functools.partial(self.fn, **params))

        return FunctionResource(
            uri=uri,  # type: ignore
            name=self.name,
            title=self.title,
            description=self.description,
            mime_type=self.mime_type,
            icons=self.icons,
            annotations=self.annotations,
            meta=self.meta,
            fn=lambda: result,  # Capture result in closure
        )
    except Exception as e:
        raise ValueError(f"Error creating resource from template: {e}")