diff --git a/nerve/tools/mcp/compiler.py b/nerve/tools/mcp/compiler.py index 1f5eb92..ba2d537 100644 --- a/nerve/tools/mcp/compiler.py +++ b/nerve/tools/mcp/compiler.py @@ -91,7 +91,11 @@ async def create_function_body(client: Client, mcp_tool: Tool) -> tuple[str, dic for name, arg_props in mcp_tool.inputSchema.get("properties", {}).items(): # print(name, arg_props) args_def, arg_type = _get_python_type(name, arg_props) - arg = {"name": name, "type": _stringify_type(arg_type), "description": arg_props.get("description", "")} + arg = { + "name": name, + "type": _stringify_type(arg_type), + "description": arg_props.get("description", "").replace('\\', '\\\\').replace('"', '\\"'), + } if args_def: type_defs.update(args_def) diff --git a/nerve/tools/mcp/compiler_test.py b/nerve/tools/mcp/compiler_test.py index f7d861d..3e9bb1f 100644 --- a/nerve/tools/mcp/compiler_test.py +++ b/nerve/tools/mcp/compiler_test.py @@ -200,3 +200,29 @@ async def process_data(user: Annotated[user_0, "User information"], settings: An '''.strip(), func_body, ) + + async def test_create_function_body_double_quotes_inside_description(self) -> None: + client = MagicMock(spec=Client) + + mock_tool = Tool( + name="test_state_tool", + description="A tool to test default string handling.", + inputSchema={ + "type": "object", + "properties": { + "state": { + "type": "string", + "description": "The state of the \"entity\".", + "default": "open", # This default value could be misinterpreted as the 'open' function. + }, + }, + }, + ) + + func_body, type_defs = await create_function_body(client, mock_tool) + + # Default value to be correctly quoted as a string literal: 'open' + self.assertIn( + """async def test_state_tool(state: Annotated[str, "The state of the \\\"entity\\\"."] = 'open') -> Any:""", + func_body, + )