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
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-03 00:43 +0000
1"""
2Tool creation command for MCP Server CLI.
4Generates tool files with boilerplate code.
5"""
7from pathlib import Path
9TOOL_TEMPLATE = """\"\"\"
10{name} Tool
12{description}
13\"\"\"
15from typing import Any, Dict
16from pydantic import BaseModel, Field
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")
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")
31def {function_name}(input_data: {class_name}Input) -> {class_name}Output:
32 \"\"\"
33 {description}
35 Args:
36 input_data: Input parameters for the tool
38 Returns:
39 Output with result and success status
41 Example:
42 >>> result = {function_name}({class_name}Input(input_text="test"))
43 >>> print(result.result)
44 \"\"\"
45 # TODO: Implement your tool logic here
47 try:
48 # Your implementation here
49 result = f"Processed: {{input_data.input_text}}"
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 )
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"""
72def generate_tool(name: str, description: str) -> None:
73 """
74 Generate a tool file with boilerplate code.
76 Args:
77 name: Name of the tool (e.g., "web_scraper")
78 description: What the tool does
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)
87 # Generate file name
88 tool_file = tools_dir / f"{name}_tool.py"
90 if tool_file.exists():
91 msg = f"Tool file already exists: {tool_file}"
92 raise FileExistsError(msg)
94 # Generate class and function names
95 class_name = "".join(word.capitalize() for word in name.split("_"))
96 function_name = name.replace("-", "_")
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 )
106 # Write file
107 tool_file.write_text(content)
109 # Create test file
110 tests_dir = Path("tests/tools")
111 tests_dir.mkdir(parents=True, exist_ok=True)
113 test_file = tests_dir / f"test_{name}_tool.py"
114 test_content = f'''"""
115Tests for {name} tool.
116"""
118import pytest
119from src.tools.{name}_tool import {function_name}, {class_name}Input, {class_name}Output
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)
127 assert isinstance(result, {class_name}Output)
128 assert result.success is True
129 assert result.result is not None
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)
137 assert isinstance(result, {class_name}Output)
138 # TODO: Add assertions for expected behavior
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)
150 assert result.success == expected_success
151'''
153 test_file.write_text(test_content)