Coverage for src / mcp_server_langgraph / cli / add_tool.py: 100%

18 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-03 00:43 +0000

1""" 

2Tool creation command for MCP Server CLI. 

3 

4Generates tool files with boilerplate code. 

5""" 

6 

7from pathlib import Path 

8 

9TOOL_TEMPLATE = """\"\"\" 

10{name} Tool 

11 

12{description} 

13\"\"\" 

14 

15from typing import Any, Dict 

16from pydantic import BaseModel, Field 

17 

18 

19class {class_name}Input(BaseModel): 

20 \"\"\"Input schema for {name} tool.\"\"\" 

21 # TODO: Define input fields 

22 input_text: str = Field(description="Input for the tool") 

23 

24 

25class {class_name}Output(BaseModel): 

26 \"\"\"Output schema for {name} tool.\"\"\" 

27 result: str = Field(description="Result from the tool") 

28 success: bool = Field(description="Whether the operation succeeded") 

29 

30 

31def {function_name}(input_data: {class_name}Input) -> {class_name}Output: 

32 \"\"\" 

33 {description} 

34 

35 Args: 

36 input_data: Input parameters for the tool 

37 

38 Returns: 

39 Output with result and success status 

40 

41 Example: 

42 >>> result = {function_name}({class_name}Input(input_text="test")) 

43 >>> print(result.result) 

44 \"\"\" 

45 # TODO: Implement your tool logic here 

46 

47 try: 

48 # Your implementation here 

49 result = f"Processed: {{input_data.input_text}}" 

50 

51 return {class_name}Output( 

52 result=result, 

53 success=True 

54 ) 

55 except Exception as e: 

56 return {class_name}Output( 

57 result=f"Error: {{str(e)}}", 

58 success=False 

59 ) 

60 

61 

62# Tool metadata for MCP registration 

63TOOL_METADATA = {{ 

64 "name": "{name}", 

65 "description": "{description}", 

66 "input_schema": {class_name}Input.model_json_schema(), 

67 "output_schema": {class_name}Output.model_json_schema(), 

68}} 

69""" 

70 

71 

72def generate_tool(name: str, description: str) -> None: 

73 """ 

74 Generate a tool file with boilerplate code. 

75 

76 Args: 

77 name: Name of the tool (e.g., "web_scraper") 

78 description: What the tool does 

79 

80 Raises: 

81 FileExistsError: If tool file already exists 

82 """ 

83 # Create tools directory if it doesn't exist 

84 tools_dir = Path("src/tools") 

85 tools_dir.mkdir(parents=True, exist_ok=True) 

86 

87 # Generate file name 

88 tool_file = tools_dir / f"{name}_tool.py" 

89 

90 if tool_file.exists(): 

91 msg = f"Tool file already exists: {tool_file}" 

92 raise FileExistsError(msg) 

93 

94 # Generate class and function names 

95 class_name = "".join(word.capitalize() for word in name.split("_")) 

96 function_name = name.replace("-", "_") 

97 

98 # Fill template 

99 content = TOOL_TEMPLATE.format( 

100 name=name, 

101 description=description, 

102 class_name=class_name, 

103 function_name=function_name, 

104 ) 

105 

106 # Write file 

107 tool_file.write_text(content) 

108 

109 # Create test file 

110 tests_dir = Path("tests/tools") 

111 tests_dir.mkdir(parents=True, exist_ok=True) 

112 

113 test_file = tests_dir / f"test_{name}_tool.py" 

114 test_content = f'''""" 

115Tests for {name} tool. 

116""" 

117 

118import pytest 

119from src.tools.{name}_tool import {function_name}, {class_name}Input, {class_name}Output 

120 

121 

122def test_{function_name}_success(): 

123 """Test successful tool execution.""" 

124 input_data = {class_name}Input(input_text="test input") 

125 result = {function_name}(input_data) 

126 

127 assert isinstance(result, {class_name}Output) 

128 assert result.success is True 

129 assert result.result is not None 

130 

131 

132def test_{function_name}_with_empty_input(): 

133 """Test tool with empty input.""" 

134 input_data = {class_name}Input(input_text="") 

135 result = {function_name}(input_data) 

136 

137 assert isinstance(result, {class_name}Output) 

138 # TODO: Add assertions for expected behavior 

139 

140 

141@pytest.mark.parametrize("test_input,expected_success", [ 

142 ("valid input", True), 

143 ("another valid input", True), 

144]) 

145def test_{function_name}_parametrized(test_input: str, expected_success: bool): 

146 """Parametrized tests for {name} tool.""" 

147 input_data = {class_name}Input(input_text=test_input) 

148 result = {function_name}(input_data) 

149 

150 assert result.success == expected_success 

151''' 

152 

153 test_file.write_text(test_content)