Spaces:
Running
Running
Jeremiah Lowin Claude commited on
Commit ·
226fdf7
1
Parent(s): 8ca70c5
Add title field support to FastMCP components
Browse files- Add optional title field to FastMCPComponent base class
- Support title parameter in all component decorators (@tool , @resource , @prompt )
- Include title in MCP protocol serialization when provided
- Add get_display_name() method to prefer title over name for display
- Add comprehensive tests for title functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
src/fastmcp/prompts/prompt.py
CHANGED
|
@@ -100,12 +100,16 @@ class Prompt(FastMCPComponent, ABC):
|
|
| 100 |
"description": self.description,
|
| 101 |
"arguments": arguments,
|
| 102 |
}
|
|
|
|
|
|
|
|
|
|
| 103 |
return MCPPrompt(**kwargs | overrides)
|
| 104 |
|
| 105 |
@staticmethod
|
| 106 |
def from_function(
|
| 107 |
fn: Callable[..., PromptResult | Awaitable[PromptResult]],
|
| 108 |
name: str | None = None,
|
|
|
|
| 109 |
description: str | None = None,
|
| 110 |
tags: set[str] | None = None,
|
| 111 |
enabled: bool | None = None,
|
|
@@ -119,7 +123,12 @@ class Prompt(FastMCPComponent, ABC):
|
|
| 119 |
- A sequence of any of the above
|
| 120 |
"""
|
| 121 |
return FunctionPrompt.from_function(
|
| 122 |
-
fn=fn,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
)
|
| 124 |
|
| 125 |
@abstractmethod
|
|
@@ -141,6 +150,7 @@ class FunctionPrompt(Prompt):
|
|
| 141 |
cls,
|
| 142 |
fn: Callable[..., PromptResult | Awaitable[PromptResult]],
|
| 143 |
name: str | None = None,
|
|
|
|
| 144 |
description: str | None = None,
|
| 145 |
tags: set[str] | None = None,
|
| 146 |
enabled: bool | None = None,
|
|
@@ -232,6 +242,7 @@ class FunctionPrompt(Prompt):
|
|
| 232 |
|
| 233 |
return cls(
|
| 234 |
name=func_name,
|
|
|
|
| 235 |
description=description,
|
| 236 |
arguments=arguments,
|
| 237 |
tags=tags or set(),
|
|
|
|
| 100 |
"description": self.description,
|
| 101 |
"arguments": arguments,
|
| 102 |
}
|
| 103 |
+
# Add title field if provided
|
| 104 |
+
if self.title is not None:
|
| 105 |
+
kwargs["title"] = self.title
|
| 106 |
return MCPPrompt(**kwargs | overrides)
|
| 107 |
|
| 108 |
@staticmethod
|
| 109 |
def from_function(
|
| 110 |
fn: Callable[..., PromptResult | Awaitable[PromptResult]],
|
| 111 |
name: str | None = None,
|
| 112 |
+
title: str | None = None,
|
| 113 |
description: str | None = None,
|
| 114 |
tags: set[str] | None = None,
|
| 115 |
enabled: bool | None = None,
|
|
|
|
| 123 |
- A sequence of any of the above
|
| 124 |
"""
|
| 125 |
return FunctionPrompt.from_function(
|
| 126 |
+
fn=fn,
|
| 127 |
+
name=name,
|
| 128 |
+
title=title,
|
| 129 |
+
description=description,
|
| 130 |
+
tags=tags,
|
| 131 |
+
enabled=enabled,
|
| 132 |
)
|
| 133 |
|
| 134 |
@abstractmethod
|
|
|
|
| 150 |
cls,
|
| 151 |
fn: Callable[..., PromptResult | Awaitable[PromptResult]],
|
| 152 |
name: str | None = None,
|
| 153 |
+
title: str | None = None,
|
| 154 |
description: str | None = None,
|
| 155 |
tags: set[str] | None = None,
|
| 156 |
enabled: bool | None = None,
|
|
|
|
| 242 |
|
| 243 |
return cls(
|
| 244 |
name=func_name,
|
| 245 |
+
title=title,
|
| 246 |
description=description,
|
| 247 |
arguments=arguments,
|
| 248 |
tags=tags or set(),
|
src/fastmcp/resources/resource.py
CHANGED
|
@@ -65,6 +65,7 @@ class Resource(FastMCPComponent, abc.ABC):
|
|
| 65 |
fn: Callable[[], Any],
|
| 66 |
uri: str | AnyUrl,
|
| 67 |
name: str | None = None,
|
|
|
|
| 68 |
description: str | None = None,
|
| 69 |
mime_type: str | None = None,
|
| 70 |
tags: set[str] | None = None,
|
|
@@ -74,6 +75,7 @@ class Resource(FastMCPComponent, abc.ABC):
|
|
| 74 |
fn=fn,
|
| 75 |
uri=uri,
|
| 76 |
name=name,
|
|
|
|
| 77 |
description=description,
|
| 78 |
mime_type=mime_type,
|
| 79 |
tags=tags,
|
|
@@ -112,6 +114,9 @@ class Resource(FastMCPComponent, abc.ABC):
|
|
| 112 |
"description": self.description,
|
| 113 |
"mimeType": self.mime_type,
|
| 114 |
}
|
|
|
|
|
|
|
|
|
|
| 115 |
return MCPResource(**kwargs | overrides)
|
| 116 |
|
| 117 |
def __repr__(self) -> str:
|
|
@@ -149,6 +154,7 @@ class FunctionResource(Resource):
|
|
| 149 |
fn: Callable[[], Any],
|
| 150 |
uri: str | AnyUrl,
|
| 151 |
name: str | None = None,
|
|
|
|
| 152 |
description: str | None = None,
|
| 153 |
mime_type: str | None = None,
|
| 154 |
tags: set[str] | None = None,
|
|
@@ -161,6 +167,7 @@ class FunctionResource(Resource):
|
|
| 161 |
fn=fn,
|
| 162 |
uri=uri,
|
| 163 |
name=name or fn.__name__,
|
|
|
|
| 164 |
description=description or inspect.getdoc(fn),
|
| 165 |
mime_type=mime_type or "text/plain",
|
| 166 |
tags=tags or set(),
|
|
|
|
| 65 |
fn: Callable[[], Any],
|
| 66 |
uri: str | AnyUrl,
|
| 67 |
name: str | None = None,
|
| 68 |
+
title: str | None = None,
|
| 69 |
description: str | None = None,
|
| 70 |
mime_type: str | None = None,
|
| 71 |
tags: set[str] | None = None,
|
|
|
|
| 75 |
fn=fn,
|
| 76 |
uri=uri,
|
| 77 |
name=name,
|
| 78 |
+
title=title,
|
| 79 |
description=description,
|
| 80 |
mime_type=mime_type,
|
| 81 |
tags=tags,
|
|
|
|
| 114 |
"description": self.description,
|
| 115 |
"mimeType": self.mime_type,
|
| 116 |
}
|
| 117 |
+
# Add title field if provided
|
| 118 |
+
if self.title is not None:
|
| 119 |
+
kwargs["title"] = self.title
|
| 120 |
return MCPResource(**kwargs | overrides)
|
| 121 |
|
| 122 |
def __repr__(self) -> str:
|
|
|
|
| 154 |
fn: Callable[[], Any],
|
| 155 |
uri: str | AnyUrl,
|
| 156 |
name: str | None = None,
|
| 157 |
+
title: str | None = None,
|
| 158 |
description: str | None = None,
|
| 159 |
mime_type: str | None = None,
|
| 160 |
tags: set[str] | None = None,
|
|
|
|
| 167 |
fn=fn,
|
| 168 |
uri=uri,
|
| 169 |
name=name or fn.__name__,
|
| 170 |
+
title=title,
|
| 171 |
description=description or inspect.getdoc(fn),
|
| 172 |
mime_type=mime_type or "text/plain",
|
| 173 |
tags=tags or set(),
|
src/fastmcp/resources/template.py
CHANGED
|
@@ -86,6 +86,7 @@ class ResourceTemplate(FastMCPComponent):
|
|
| 86 |
fn: Callable[..., Any],
|
| 87 |
uri_template: str,
|
| 88 |
name: str | None = None,
|
|
|
|
| 89 |
description: str | None = None,
|
| 90 |
mime_type: str | None = None,
|
| 91 |
tags: set[str] | None = None,
|
|
@@ -95,6 +96,7 @@ class ResourceTemplate(FastMCPComponent):
|
|
| 95 |
fn=fn,
|
| 96 |
uri_template=uri_template,
|
| 97 |
name=name,
|
|
|
|
| 98 |
description=description,
|
| 99 |
mime_type=mime_type,
|
| 100 |
tags=tags,
|
|
@@ -145,6 +147,9 @@ class ResourceTemplate(FastMCPComponent):
|
|
| 145 |
"description": self.description,
|
| 146 |
"mimeType": self.mime_type,
|
| 147 |
}
|
|
|
|
|
|
|
|
|
|
| 148 |
return MCPResourceTemplate(**kwargs | overrides)
|
| 149 |
|
| 150 |
@classmethod
|
|
@@ -197,6 +202,7 @@ class FunctionResourceTemplate(ResourceTemplate):
|
|
| 197 |
fn: Callable[..., Any],
|
| 198 |
uri_template: str,
|
| 199 |
name: str | None = None,
|
|
|
|
| 200 |
description: str | None = None,
|
| 201 |
mime_type: str | None = None,
|
| 202 |
tags: set[str] | None = None,
|
|
@@ -278,6 +284,7 @@ class FunctionResourceTemplate(ResourceTemplate):
|
|
| 278 |
return cls(
|
| 279 |
uri_template=uri_template,
|
| 280 |
name=func_name,
|
|
|
|
| 281 |
description=description,
|
| 282 |
mime_type=mime_type or "text/plain",
|
| 283 |
fn=fn,
|
|
|
|
| 86 |
fn: Callable[..., Any],
|
| 87 |
uri_template: str,
|
| 88 |
name: str | None = None,
|
| 89 |
+
title: str | None = None,
|
| 90 |
description: str | None = None,
|
| 91 |
mime_type: str | None = None,
|
| 92 |
tags: set[str] | None = None,
|
|
|
|
| 96 |
fn=fn,
|
| 97 |
uri_template=uri_template,
|
| 98 |
name=name,
|
| 99 |
+
title=title,
|
| 100 |
description=description,
|
| 101 |
mime_type=mime_type,
|
| 102 |
tags=tags,
|
|
|
|
| 147 |
"description": self.description,
|
| 148 |
"mimeType": self.mime_type,
|
| 149 |
}
|
| 150 |
+
# Add title field if provided
|
| 151 |
+
if self.title is not None:
|
| 152 |
+
kwargs["title"] = self.title
|
| 153 |
return MCPResourceTemplate(**kwargs | overrides)
|
| 154 |
|
| 155 |
@classmethod
|
|
|
|
| 202 |
fn: Callable[..., Any],
|
| 203 |
uri_template: str,
|
| 204 |
name: str | None = None,
|
| 205 |
+
title: str | None = None,
|
| 206 |
description: str | None = None,
|
| 207 |
mime_type: str | None = None,
|
| 208 |
tags: set[str] | None = None,
|
|
|
|
| 284 |
return cls(
|
| 285 |
uri_template=uri_template,
|
| 286 |
name=func_name,
|
| 287 |
+
title=title,
|
| 288 |
description=description,
|
| 289 |
mime_type=mime_type or "text/plain",
|
| 290 |
fn=fn,
|
src/fastmcp/server/server.py
CHANGED
|
@@ -790,6 +790,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 790 |
name_or_fn: AnyFunction,
|
| 791 |
*,
|
| 792 |
name: str | None = None,
|
|
|
|
| 793 |
description: str | None = None,
|
| 794 |
tags: set[str] | None = None,
|
| 795 |
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
@@ -804,6 +805,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 804 |
name_or_fn: str | None = None,
|
| 805 |
*,
|
| 806 |
name: str | None = None,
|
|
|
|
| 807 |
description: str | None = None,
|
| 808 |
tags: set[str] | None = None,
|
| 809 |
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
@@ -817,6 +819,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 817 |
name_or_fn: str | AnyFunction | None = None,
|
| 818 |
*,
|
| 819 |
name: str | None = None,
|
|
|
|
| 820 |
description: str | None = None,
|
| 821 |
tags: set[str] | None = None,
|
| 822 |
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
@@ -897,6 +900,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 897 |
tool = Tool.from_function(
|
| 898 |
fn,
|
| 899 |
name=tool_name,
|
|
|
|
| 900 |
description=description,
|
| 901 |
tags=tags,
|
| 902 |
output_schema=output_schema,
|
|
@@ -928,6 +932,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 928 |
return partial(
|
| 929 |
self.tool,
|
| 930 |
name=tool_name,
|
|
|
|
| 931 |
description=description,
|
| 932 |
tags=tags,
|
| 933 |
output_schema=output_schema,
|
|
@@ -1016,6 +1021,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1016 |
uri: str,
|
| 1017 |
*,
|
| 1018 |
name: str | None = None,
|
|
|
|
| 1019 |
description: str | None = None,
|
| 1020 |
mime_type: str | None = None,
|
| 1021 |
tags: set[str] | None = None,
|
|
@@ -1107,6 +1113,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1107 |
fn=fn,
|
| 1108 |
uri_template=uri,
|
| 1109 |
name=name,
|
|
|
|
| 1110 |
description=description,
|
| 1111 |
mime_type=mime_type,
|
| 1112 |
tags=tags,
|
|
@@ -1119,6 +1126,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1119 |
fn=fn,
|
| 1120 |
uri=uri,
|
| 1121 |
name=name,
|
|
|
|
| 1122 |
description=description,
|
| 1123 |
mime_type=mime_type,
|
| 1124 |
tags=tags,
|
|
@@ -1158,6 +1166,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1158 |
name_or_fn: AnyFunction,
|
| 1159 |
*,
|
| 1160 |
name: str | None = None,
|
|
|
|
| 1161 |
description: str | None = None,
|
| 1162 |
tags: set[str] | None = None,
|
| 1163 |
enabled: bool | None = None,
|
|
@@ -1169,6 +1178,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1169 |
name_or_fn: str | None = None,
|
| 1170 |
*,
|
| 1171 |
name: str | None = None,
|
|
|
|
| 1172 |
description: str | None = None,
|
| 1173 |
tags: set[str] | None = None,
|
| 1174 |
enabled: bool | None = None,
|
|
@@ -1179,6 +1189,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1179 |
name_or_fn: str | AnyFunction | None = None,
|
| 1180 |
*,
|
| 1181 |
name: str | None = None,
|
|
|
|
| 1182 |
description: str | None = None,
|
| 1183 |
tags: set[str] | None = None,
|
| 1184 |
enabled: bool | None = None,
|
|
@@ -1275,6 +1286,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1275 |
prompt = Prompt.from_function(
|
| 1276 |
fn=fn,
|
| 1277 |
name=prompt_name,
|
|
|
|
| 1278 |
description=description,
|
| 1279 |
tags=tags,
|
| 1280 |
enabled=enabled,
|
|
@@ -1303,6 +1315,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 1303 |
return partial(
|
| 1304 |
self.prompt,
|
| 1305 |
name=prompt_name,
|
|
|
|
| 1306 |
description=description,
|
| 1307 |
tags=tags,
|
| 1308 |
enabled=enabled,
|
|
|
|
| 790 |
name_or_fn: AnyFunction,
|
| 791 |
*,
|
| 792 |
name: str | None = None,
|
| 793 |
+
title: str | None = None,
|
| 794 |
description: str | None = None,
|
| 795 |
tags: set[str] | None = None,
|
| 796 |
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
|
|
| 805 |
name_or_fn: str | None = None,
|
| 806 |
*,
|
| 807 |
name: str | None = None,
|
| 808 |
+
title: str | None = None,
|
| 809 |
description: str | None = None,
|
| 810 |
tags: set[str] | None = None,
|
| 811 |
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
|
|
| 819 |
name_or_fn: str | AnyFunction | None = None,
|
| 820 |
*,
|
| 821 |
name: str | None = None,
|
| 822 |
+
title: str | None = None,
|
| 823 |
description: str | None = None,
|
| 824 |
tags: set[str] | None = None,
|
| 825 |
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
|
|
| 900 |
tool = Tool.from_function(
|
| 901 |
fn,
|
| 902 |
name=tool_name,
|
| 903 |
+
title=title,
|
| 904 |
description=description,
|
| 905 |
tags=tags,
|
| 906 |
output_schema=output_schema,
|
|
|
|
| 932 |
return partial(
|
| 933 |
self.tool,
|
| 934 |
name=tool_name,
|
| 935 |
+
title=title,
|
| 936 |
description=description,
|
| 937 |
tags=tags,
|
| 938 |
output_schema=output_schema,
|
|
|
|
| 1021 |
uri: str,
|
| 1022 |
*,
|
| 1023 |
name: str | None = None,
|
| 1024 |
+
title: str | None = None,
|
| 1025 |
description: str | None = None,
|
| 1026 |
mime_type: str | None = None,
|
| 1027 |
tags: set[str] | None = None,
|
|
|
|
| 1113 |
fn=fn,
|
| 1114 |
uri_template=uri,
|
| 1115 |
name=name,
|
| 1116 |
+
title=title,
|
| 1117 |
description=description,
|
| 1118 |
mime_type=mime_type,
|
| 1119 |
tags=tags,
|
|
|
|
| 1126 |
fn=fn,
|
| 1127 |
uri=uri,
|
| 1128 |
name=name,
|
| 1129 |
+
title=title,
|
| 1130 |
description=description,
|
| 1131 |
mime_type=mime_type,
|
| 1132 |
tags=tags,
|
|
|
|
| 1166 |
name_or_fn: AnyFunction,
|
| 1167 |
*,
|
| 1168 |
name: str | None = None,
|
| 1169 |
+
title: str | None = None,
|
| 1170 |
description: str | None = None,
|
| 1171 |
tags: set[str] | None = None,
|
| 1172 |
enabled: bool | None = None,
|
|
|
|
| 1178 |
name_or_fn: str | None = None,
|
| 1179 |
*,
|
| 1180 |
name: str | None = None,
|
| 1181 |
+
title: str | None = None,
|
| 1182 |
description: str | None = None,
|
| 1183 |
tags: set[str] | None = None,
|
| 1184 |
enabled: bool | None = None,
|
|
|
|
| 1189 |
name_or_fn: str | AnyFunction | None = None,
|
| 1190 |
*,
|
| 1191 |
name: str | None = None,
|
| 1192 |
+
title: str | None = None,
|
| 1193 |
description: str | None = None,
|
| 1194 |
tags: set[str] | None = None,
|
| 1195 |
enabled: bool | None = None,
|
|
|
|
| 1286 |
prompt = Prompt.from_function(
|
| 1287 |
fn=fn,
|
| 1288 |
name=prompt_name,
|
| 1289 |
+
title=title,
|
| 1290 |
description=description,
|
| 1291 |
tags=tags,
|
| 1292 |
enabled=enabled,
|
|
|
|
| 1315 |
return partial(
|
| 1316 |
self.prompt,
|
| 1317 |
name=prompt_name,
|
| 1318 |
+
title=title,
|
| 1319 |
description=description,
|
| 1320 |
tags=tags,
|
| 1321 |
enabled=enabled,
|
src/fastmcp/tools/tool.py
CHANGED
|
@@ -142,12 +142,16 @@ class Tool(FastMCPComponent):
|
|
| 142 |
"outputSchema": self.output_schema,
|
| 143 |
"annotations": self.annotations,
|
| 144 |
}
|
|
|
|
|
|
|
|
|
|
| 145 |
return MCPTool(**kwargs | overrides)
|
| 146 |
|
| 147 |
@staticmethod
|
| 148 |
def from_function(
|
| 149 |
fn: Callable[..., Any],
|
| 150 |
name: str | None = None,
|
|
|
|
| 151 |
description: str | None = None,
|
| 152 |
tags: set[str] | None = None,
|
| 153 |
annotations: ToolAnnotations | None = None,
|
|
@@ -160,6 +164,7 @@ class Tool(FastMCPComponent):
|
|
| 160 |
return FunctionTool.from_function(
|
| 161 |
fn=fn,
|
| 162 |
name=name,
|
|
|
|
| 163 |
description=description,
|
| 164 |
tags=tags,
|
| 165 |
annotations=annotations,
|
|
@@ -219,6 +224,7 @@ class FunctionTool(Tool):
|
|
| 219 |
cls,
|
| 220 |
fn: Callable[..., Any],
|
| 221 |
name: str | None = None,
|
|
|
|
| 222 |
description: str | None = None,
|
| 223 |
tags: set[str] | None = None,
|
| 224 |
annotations: ToolAnnotations | None = None,
|
|
@@ -250,6 +256,7 @@ class FunctionTool(Tool):
|
|
| 250 |
return cls(
|
| 251 |
fn=parsed_fn.fn,
|
| 252 |
name=name or parsed_fn.name,
|
|
|
|
| 253 |
description=description or parsed_fn.description,
|
| 254 |
parameters=parsed_fn.input_schema,
|
| 255 |
output_schema=output_schema,
|
|
|
|
| 142 |
"outputSchema": self.output_schema,
|
| 143 |
"annotations": self.annotations,
|
| 144 |
}
|
| 145 |
+
# Add title field if provided
|
| 146 |
+
if self.title is not None:
|
| 147 |
+
kwargs["title"] = self.title
|
| 148 |
return MCPTool(**kwargs | overrides)
|
| 149 |
|
| 150 |
@staticmethod
|
| 151 |
def from_function(
|
| 152 |
fn: Callable[..., Any],
|
| 153 |
name: str | None = None,
|
| 154 |
+
title: str | None = None,
|
| 155 |
description: str | None = None,
|
| 156 |
tags: set[str] | None = None,
|
| 157 |
annotations: ToolAnnotations | None = None,
|
|
|
|
| 164 |
return FunctionTool.from_function(
|
| 165 |
fn=fn,
|
| 166 |
name=name,
|
| 167 |
+
title=title,
|
| 168 |
description=description,
|
| 169 |
tags=tags,
|
| 170 |
annotations=annotations,
|
|
|
|
| 224 |
cls,
|
| 225 |
fn: Callable[..., Any],
|
| 226 |
name: str | None = None,
|
| 227 |
+
title: str | None = None,
|
| 228 |
description: str | None = None,
|
| 229 |
tags: set[str] | None = None,
|
| 230 |
annotations: ToolAnnotations | None = None,
|
|
|
|
| 256 |
return cls(
|
| 257 |
fn=parsed_fn.fn,
|
| 258 |
name=name or parsed_fn.name,
|
| 259 |
+
title=title,
|
| 260 |
description=description or parsed_fn.description,
|
| 261 |
parameters=parsed_fn.input_schema,
|
| 262 |
output_schema=output_schema,
|
src/fastmcp/utilities/components.py
CHANGED
|
@@ -24,6 +24,10 @@ class FastMCPComponent(FastMCPBaseModel):
|
|
| 24 |
name: str = Field(
|
| 25 |
description="The name of the component.",
|
| 26 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
description: str | None = Field(
|
| 28 |
default=None,
|
| 29 |
description="The description of the component.",
|
|
@@ -64,7 +68,7 @@ class FastMCPComponent(FastMCPBaseModel):
|
|
| 64 |
return self.model_dump() == other.model_dump()
|
| 65 |
|
| 66 |
def __repr__(self) -> str:
|
| 67 |
-
return f"{self.__class__.__name__}(name={self.name!r}, description={self.description!r}, tags={self.tags}, enabled={self.enabled})"
|
| 68 |
|
| 69 |
def enable(self) -> None:
|
| 70 |
"""Enable the component."""
|
|
@@ -73,3 +77,7 @@ class FastMCPComponent(FastMCPBaseModel):
|
|
| 73 |
def disable(self) -> None:
|
| 74 |
"""Disable the component."""
|
| 75 |
self.enabled = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
name: str = Field(
|
| 25 |
description="The name of the component.",
|
| 26 |
)
|
| 27 |
+
title: str | None = Field(
|
| 28 |
+
default=None,
|
| 29 |
+
description="The title of the component for display purposes.",
|
| 30 |
+
)
|
| 31 |
description: str | None = Field(
|
| 32 |
default=None,
|
| 33 |
description="The description of the component.",
|
|
|
|
| 68 |
return self.model_dump() == other.model_dump()
|
| 69 |
|
| 70 |
def __repr__(self) -> str:
|
| 71 |
+
return f"{self.__class__.__name__}(name={self.name!r}, title={self.title!r}, description={self.description!r}, tags={self.tags}, enabled={self.enabled})"
|
| 72 |
|
| 73 |
def enable(self) -> None:
|
| 74 |
"""Enable the component."""
|
|
|
|
| 77 |
def disable(self) -> None:
|
| 78 |
"""Disable the component."""
|
| 79 |
self.enabled = False
|
| 80 |
+
|
| 81 |
+
def get_display_name(self) -> str:
|
| 82 |
+
"""Get the display name for this component, preferring title over name."""
|
| 83 |
+
return self.title if self.title is not None else self.name
|
tests/tools/test_tool.py
CHANGED
|
@@ -1225,3 +1225,50 @@ class TestAutomaticStructuredContent:
|
|
| 1225 |
assert result.data.name == "Bob"
|
| 1226 |
assert result.data.age == 25
|
| 1227 |
assert result.data.verified is True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1225 |
assert result.data.name == "Bob"
|
| 1226 |
assert result.data.age == 25
|
| 1227 |
assert result.data.verified is True
|
| 1228 |
+
|
| 1229 |
+
|
| 1230 |
+
class TestToolTitle:
|
| 1231 |
+
"""Tests for tool title functionality."""
|
| 1232 |
+
|
| 1233 |
+
def test_tool_with_title(self):
|
| 1234 |
+
"""Test that tools can have titles and they appear in MCP conversion."""
|
| 1235 |
+
|
| 1236 |
+
def calculate(x: int, y: int) -> int:
|
| 1237 |
+
"""Calculate the sum of two numbers."""
|
| 1238 |
+
return x + y
|
| 1239 |
+
|
| 1240 |
+
tool = Tool.from_function(
|
| 1241 |
+
calculate,
|
| 1242 |
+
name="calc",
|
| 1243 |
+
title="Advanced Calculator Tool",
|
| 1244 |
+
description="Custom description",
|
| 1245 |
+
)
|
| 1246 |
+
|
| 1247 |
+
assert tool.name == "calc"
|
| 1248 |
+
assert tool.title == "Advanced Calculator Tool"
|
| 1249 |
+
assert tool.description == "Custom description"
|
| 1250 |
+
assert tool.get_display_name() == "Advanced Calculator Tool"
|
| 1251 |
+
|
| 1252 |
+
# Test MCP conversion includes title
|
| 1253 |
+
mcp_tool = tool.to_mcp_tool()
|
| 1254 |
+
assert mcp_tool.name == "calc"
|
| 1255 |
+
assert (
|
| 1256 |
+
hasattr(mcp_tool, "title") and mcp_tool.title == "Advanced Calculator Tool"
|
| 1257 |
+
)
|
| 1258 |
+
|
| 1259 |
+
def test_tool_without_title(self):
|
| 1260 |
+
"""Test that tools without titles use name as display name."""
|
| 1261 |
+
|
| 1262 |
+
def multiply(a: int, b: int) -> int:
|
| 1263 |
+
return a * b
|
| 1264 |
+
|
| 1265 |
+
tool = Tool.from_function(multiply)
|
| 1266 |
+
|
| 1267 |
+
assert tool.name == "multiply"
|
| 1268 |
+
assert tool.title is None
|
| 1269 |
+
assert tool.get_display_name() == "multiply"
|
| 1270 |
+
|
| 1271 |
+
# Test MCP conversion doesn't include title when None
|
| 1272 |
+
mcp_tool = tool.to_mcp_tool()
|
| 1273 |
+
assert mcp_tool.name == "multiply"
|
| 1274 |
+
assert not hasattr(mcp_tool, "title") or mcp_tool.title is None
|