Jeremiah Lowin commited on
Commit
6d99710
·
unverified ·
2 Parent(s): c32211cbf961cc

Merge pull request #1131 from jlowin/fix-tool-transform-title-field

Browse files
src/fastmcp/tools/tool.py CHANGED
@@ -185,8 +185,9 @@ class Tool(FastMCPComponent):
185
  tool: Tool,
186
  transform_fn: Callable[..., Any] | None = None,
187
  name: str | None = None,
 
188
  transform_args: dict[str, ArgTransform] | None = None,
189
- description: str | None = None,
190
  tags: set[str] | None = None,
191
  annotations: ToolAnnotations | None = None,
192
  output_schema: dict[str, Any] | None | Literal[False] = None,
@@ -199,6 +200,7 @@ class Tool(FastMCPComponent):
199
  tool=tool,
200
  transform_fn=transform_fn,
201
  name=name,
 
202
  transform_args=transform_args,
203
  description=description,
204
  tags=tags,
 
185
  tool: Tool,
186
  transform_fn: Callable[..., Any] | None = None,
187
  name: str | None = None,
188
+ title: str | None | NotSetT = NotSet,
189
  transform_args: dict[str, ArgTransform] | None = None,
190
+ description: str | None | NotSetT = NotSet,
191
  tags: set[str] | None = None,
192
  annotations: ToolAnnotations | None = None,
193
  output_schema: dict[str, Any] | None | Literal[False] = None,
 
200
  tool=tool,
201
  transform_fn=transform_fn,
202
  name=name,
203
+ title=title,
204
  transform_args=transform_args,
205
  description=description,
206
  tags=tags,
src/fastmcp/tools/tool_transform.py CHANGED
@@ -325,7 +325,8 @@ class TransformedTool(Tool):
325
  cls,
326
  tool: Tool,
327
  name: str | None = None,
328
- description: str | None = None,
 
329
  tags: set[str] | None = None,
330
  transform_fn: Callable[..., Any] | None = None,
331
  transform_args: dict[str, ArgTransform] | None = None,
@@ -342,6 +343,7 @@ class TransformedTool(Tool):
342
  to call the parent tool. Functions with **kwargs receive transformed
343
  argument names.
344
  name: New name for the tool. Defaults to parent tool's name.
 
345
  transform_args: Optional transformations for parent tool arguments.
346
  Only specified arguments are transformed, others pass through unchanged:
347
  - Simple rename (str)
@@ -506,13 +508,18 @@ class TransformedTool(Tool):
506
  f"{', '.join(sorted(duplicates))}"
507
  )
508
 
509
- final_description = description if description is not None else tool.description
 
 
 
 
510
 
511
  transformed_tool = cls(
512
  fn=final_fn,
513
  forwarding_fn=forwarding_fn,
514
  parent_tool=tool,
515
- name=name or tool.name,
 
516
  description=final_description,
517
  parameters=final_schema,
518
  output_schema=final_output_schema,
 
325
  cls,
326
  tool: Tool,
327
  name: str | None = None,
328
+ title: str | None | NotSetT = NotSet,
329
+ description: str | None | NotSetT = NotSet,
330
  tags: set[str] | None = None,
331
  transform_fn: Callable[..., Any] | None = None,
332
  transform_args: dict[str, ArgTransform] | None = None,
 
343
  to call the parent tool. Functions with **kwargs receive transformed
344
  argument names.
345
  name: New name for the tool. Defaults to parent tool's name.
346
+ title: New title for the tool. Defaults to parent tool's title.
347
  transform_args: Optional transformations for parent tool arguments.
348
  Only specified arguments are transformed, others pass through unchanged:
349
  - Simple rename (str)
 
508
  f"{', '.join(sorted(duplicates))}"
509
  )
510
 
511
+ final_name = name or tool.name
512
+ final_description = (
513
+ description if not isinstance(description, NotSetT) else tool.description
514
+ )
515
+ final_title = title if not isinstance(title, NotSetT) else tool.title
516
 
517
  transformed_tool = cls(
518
  fn=final_fn,
519
  forwarding_fn=forwarding_fn,
520
  parent_tool=tool,
521
+ name=final_name,
522
+ title=final_title,
523
  description=final_description,
524
  parameters=final_schema,
525
  output_schema=final_output_schema,
tests/tools/test_tool_transform.py CHANGED
@@ -1316,3 +1316,88 @@ class TestTransformToolOutputSchema:
1316
  # Should use ToolResult content directly
1317
  assert result.content[0].text == "Direct: 6" # type: ignore[attr-defined]
1318
  assert result.structured_content == {"direct_value": 6, "doubled": 12}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1316
  # Should use ToolResult content directly
1317
  assert result.content[0].text == "Direct: 6" # type: ignore[attr-defined]
1318
  assert result.structured_content == {"direct_value": 6, "doubled": 12}
1319
+
1320
+
1321
+ @pytest.fixture
1322
+ def sample_tool():
1323
+ """Sample tool for testing transformations."""
1324
+
1325
+ def sample_func(x: int) -> str:
1326
+ return f"Result: {x}"
1327
+
1328
+ return Tool.from_function(
1329
+ sample_func,
1330
+ name="sample_tool",
1331
+ title="Original Tool Title",
1332
+ description="Original description",
1333
+ )
1334
+
1335
+
1336
+ @pytest.fixture
1337
+ def sample_tool_no_title():
1338
+ """Sample tool without title for testing."""
1339
+
1340
+ def sample_func(x: int) -> str:
1341
+ return f"Result: {x}"
1342
+
1343
+ return Tool.from_function(sample_func, name="no_title_tool")
1344
+
1345
+
1346
+ def test_transform_inherits_title(sample_tool):
1347
+ """Test that transformed tools inherit title when none specified."""
1348
+ transformed = Tool.from_tool(sample_tool)
1349
+ assert transformed.title == "Original Tool Title"
1350
+
1351
+
1352
+ def test_transform_overrides_title(sample_tool):
1353
+ """Test that transformed tools can override title."""
1354
+ transformed = Tool.from_tool(sample_tool, title="New Tool Title")
1355
+ assert transformed.title == "New Tool Title"
1356
+
1357
+
1358
+ def test_transform_sets_title_to_none(sample_tool):
1359
+ """Test that transformed tools can explicitly set title to None."""
1360
+ transformed = Tool.from_tool(sample_tool, title=None)
1361
+ assert transformed.title is None
1362
+
1363
+
1364
+ def test_transform_inherits_none_title(sample_tool_no_title):
1365
+ """Test that transformed tools inherit None title."""
1366
+ transformed = Tool.from_tool(sample_tool_no_title)
1367
+ assert transformed.title is None
1368
+
1369
+
1370
+ def test_transform_adds_title_to_none(sample_tool_no_title):
1371
+ """Test that transformed tools can add title when parent has None."""
1372
+ transformed = Tool.from_tool(sample_tool_no_title, title="Added Title")
1373
+ assert transformed.title == "Added Title"
1374
+
1375
+
1376
+ def test_transform_inherits_description(sample_tool):
1377
+ """Test that transformed tools inherit description when none specified."""
1378
+ transformed = Tool.from_tool(sample_tool)
1379
+ assert transformed.description == "Original description"
1380
+
1381
+
1382
+ def test_transform_overrides_description(sample_tool):
1383
+ """Test that transformed tools can override description."""
1384
+ transformed = Tool.from_tool(sample_tool, description="New description")
1385
+ assert transformed.description == "New description"
1386
+
1387
+
1388
+ def test_transform_sets_description_to_none(sample_tool):
1389
+ """Test that transformed tools can explicitly set description to None."""
1390
+ transformed = Tool.from_tool(sample_tool, description=None)
1391
+ assert transformed.description is None
1392
+
1393
+
1394
+ def test_transform_inherits_none_description(sample_tool_no_title):
1395
+ """Test that transformed tools inherit None description."""
1396
+ transformed = Tool.from_tool(sample_tool_no_title)
1397
+ assert transformed.description is None
1398
+
1399
+
1400
+ def test_transform_adds_description_to_none(sample_tool_no_title):
1401
+ """Test that transformed tools can add description when parent has None."""
1402
+ transformed = Tool.from_tool(sample_tool_no_title, description="Added description")
1403
+ assert transformed.description == "Added description"