|
12 | 12 | ResourceLink, |
13 | 13 | TextContent, |
14 | 14 | TextResourceContents, |
| 15 | + ToolExecution, |
15 | 16 | ) |
16 | 17 | from pydantic import AnyUrl, BaseModel, Field, TypeAdapter |
17 | 18 | from typing_extensions import TypedDict |
@@ -1813,3 +1814,84 @@ def add(a: int, b: int) -> int: |
1813 | 1814 | assert tool.parameters is not None |
1814 | 1815 | assert "a" in tool.parameters["properties"] |
1815 | 1816 | assert "b" in tool.parameters["properties"] |
| 1817 | + |
| 1818 | + |
| 1819 | +class TestToolExecutionField: |
| 1820 | + """Tests for the execution field on the base Tool class.""" |
| 1821 | + |
| 1822 | + def test_tool_with_execution_field(self): |
| 1823 | + """Test that Tool can store and return execution metadata.""" |
| 1824 | + tool = Tool( |
| 1825 | + name="my_tool", |
| 1826 | + description="A tool with execution", |
| 1827 | + parameters={"type": "object", "properties": {}}, |
| 1828 | + execution=ToolExecution(taskSupport="optional"), |
| 1829 | + ) |
| 1830 | + |
| 1831 | + mcp_tool = tool.to_mcp_tool() |
| 1832 | + assert mcp_tool.execution is not None |
| 1833 | + assert mcp_tool.execution.taskSupport == "optional" |
| 1834 | + |
| 1835 | + def test_tool_without_execution_field(self): |
| 1836 | + """Test that Tool without execution returns None.""" |
| 1837 | + tool = Tool( |
| 1838 | + name="my_tool", |
| 1839 | + description="A tool without execution", |
| 1840 | + parameters={"type": "object", "properties": {}}, |
| 1841 | + ) |
| 1842 | + |
| 1843 | + mcp_tool = tool.to_mcp_tool() |
| 1844 | + assert mcp_tool.execution is None |
| 1845 | + |
| 1846 | + def test_execution_override_takes_precedence(self): |
| 1847 | + """Test that explicit override takes precedence over field value.""" |
| 1848 | + tool = Tool( |
| 1849 | + name="my_tool", |
| 1850 | + description="A tool", |
| 1851 | + parameters={"type": "object", "properties": {}}, |
| 1852 | + execution=ToolExecution(taskSupport="optional"), |
| 1853 | + ) |
| 1854 | + |
| 1855 | + override_execution = ToolExecution(taskSupport="required") |
| 1856 | + mcp_tool = tool.to_mcp_tool(execution=override_execution) |
| 1857 | + assert mcp_tool.execution is not None |
| 1858 | + assert mcp_tool.execution.taskSupport == "required" |
| 1859 | + |
| 1860 | + async def test_function_tool_task_config_still_works(self): |
| 1861 | + """FunctionTool should still derive execution from task_config.""" |
| 1862 | + |
| 1863 | + async def my_fn() -> str: |
| 1864 | + return "hello" |
| 1865 | + |
| 1866 | + tool = Tool.from_function(my_fn, task=True) |
| 1867 | + mcp_tool = tool.to_mcp_tool() |
| 1868 | + |
| 1869 | + # FunctionTool sets execution from task_config |
| 1870 | + assert mcp_tool.execution is not None |
| 1871 | + assert mcp_tool.execution.taskSupport == "optional" |
| 1872 | + |
| 1873 | + def test_tool_execution_required_mode(self): |
| 1874 | + """Test that Tool can store required execution mode.""" |
| 1875 | + tool = Tool( |
| 1876 | + name="my_tool", |
| 1877 | + description="A tool with required execution", |
| 1878 | + parameters={"type": "object", "properties": {}}, |
| 1879 | + execution=ToolExecution(taskSupport="required"), |
| 1880 | + ) |
| 1881 | + |
| 1882 | + mcp_tool = tool.to_mcp_tool() |
| 1883 | + assert mcp_tool.execution is not None |
| 1884 | + assert mcp_tool.execution.taskSupport == "required" |
| 1885 | + |
| 1886 | + def test_tool_execution_forbidden_mode(self): |
| 1887 | + """Test that Tool can store forbidden execution mode.""" |
| 1888 | + tool = Tool( |
| 1889 | + name="my_tool", |
| 1890 | + description="A tool with forbidden execution", |
| 1891 | + parameters={"type": "object", "properties": {}}, |
| 1892 | + execution=ToolExecution(taskSupport="forbidden"), |
| 1893 | + ) |
| 1894 | + |
| 1895 | + mcp_tool = tool.to_mcp_tool() |
| 1896 | + assert mcp_tool.execution is not None |
| 1897 | + assert mcp_tool.execution.taskSupport == "forbidden" |
0 commit comments