Coverage for src / mcp_server_langgraph / tools / __init__.py: 77%

49 statements  

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

1""" 

2Tool catalog for LangGraph agent 

3 

4Provides a registry of tools that the agent can execute. 

5Tools are defined using LangChain's @tool decorator for automatic schema generation. 

6""" 

7 

8from typing import Any 

9 

10from langchain_core.tools import BaseTool 

11 

12from mcp_server_langgraph.core.config import Settings, settings 

13from mcp_server_langgraph.tools.calculator_tools import add, calculator, divide, multiply, subtract 

14from mcp_server_langgraph.tools.filesystem_tools import list_directory, read_file, search_files 

15from mcp_server_langgraph.tools.search_tools import search_knowledge_base, web_search 

16 

17 

18def get_all_tools(settings_override: Any | None = None) -> list[BaseTool]: 

19 """ 

20 Factory function to get all tools based on settings. 

21 

22 This allows runtime configuration of tools, enabling: 

23 - Multi-tenant deployments with different tool sets 

24 - Dynamic enable/disable of code execution 

25 - Testing with custom settings 

26 

27 Args: 

28 settings_override: Optional Settings instance. If None, uses global settings. 

29 

30 Returns: 

31 List of all available tools based on settings 

32 """ 

33 # Use override settings if provided, otherwise use global settings 

34 effective_settings: Settings = settings_override if settings_override is not None else settings 

35 

36 # Base tools (always available) 

37 tools: list[BaseTool] = [ 

38 # Calculator tools 

39 calculator, 

40 add, 

41 subtract, 

42 multiply, 

43 divide, 

44 # Search tools 

45 search_knowledge_base, 

46 web_search, 

47 # Filesystem tools (read-only for safety) 

48 read_file, 

49 list_directory, 

50 search_files, 

51 ] 

52 

53 # Code execution tools (conditional on configuration) 

54 if effective_settings.enable_code_execution: 54 ↛ 55line 54 didn't jump to line 55 because the condition on line 54 was never true

55 try: 

56 from mcp_server_langgraph.tools.code_execution_tools import execute_python 

57 

58 tools.append(execute_python) 

59 except ImportError: 

60 # Code execution dependencies not installed - silently skip 

61 pass 

62 

63 return tools 

64 

65 

66# Backward compatibility: ALL_TOOLS uses default settings 

67# NOTE: For multi-tenant or runtime configuration, use get_all_tools(settings) instead 

68ALL_TOOLS: list[BaseTool] = get_all_tools() 

69 

70# Tool groups for conditional loading 

71CALCULATOR_TOOLS = [calculator, add, subtract, multiply, divide] 

72SEARCH_TOOLS = [search_knowledge_base, web_search] 

73FILESYSTEM_TOOLS = [read_file, list_directory, search_files] 

74 

75 

76# Code execution tools group (conditionally loaded based on settings) 

77# Returns empty list if code execution is disabled 

78def _get_code_execution_tools() -> list[BaseTool]: 

79 """Get code execution tools if enabled in settings""" 

80 if settings.enable_code_execution: 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true

81 try: 

82 from mcp_server_langgraph.tools.code_execution_tools import execute_python 

83 

84 return [execute_python] 

85 except ImportError: 

86 pass 

87 return [] 

88 

89 

90CODE_EXECUTION_TOOLS = _get_code_execution_tools() 

91 

92 

93def get_tools(categories: list[str] | None = None, settings_override: Any | None = None) -> list[BaseTool]: 

94 """ 

95 Get tools by category. 

96 

97 Args: 

98 categories: List of categories to include (None = all tools) 

99 Options: "calculator", "search", "filesystem", "code_execution" 

100 settings_override: Optional Settings instance for runtime configuration 

101 

102 Returns: 

103 List of tools matching the categories 

104 """ 

105 all_tools = get_all_tools(settings_override) 

106 

107 if categories is None: 

108 return all_tools 

109 

110 tools: list[BaseTool] = [] 

111 category_map = { 

112 "calculator": CALCULATOR_TOOLS, 

113 "search": SEARCH_TOOLS, 

114 "filesystem": FILESYSTEM_TOOLS, 

115 } 

116 

117 for category in categories: 

118 if category in category_map: 

119 tools.extend(category_map[category]) 

120 elif category == "code_execution": 120 ↛ 122line 120 didn't jump to line 122 because the condition on line 120 was never true

121 # Dynamically include code execution tools if enabled 

122 code_tools = [t for t in all_tools if t.name == "execute_python"] 

123 tools.extend(code_tools) 

124 

125 return tools 

126 

127 

128def get_tool_by_name(name: str, settings_override: Any | None = None) -> BaseTool | None: 

129 """ 

130 Get a specific tool by name. 

131 

132 Args: 

133 name: Tool name 

134 settings_override: Optional Settings instance for runtime configuration 

135 

136 Returns: 

137 Tool instance or None if not found 

138 """ 

139 all_tools = get_all_tools(settings_override) 

140 for tool in all_tools: 

141 if tool.name == name: 

142 return tool 

143 return None 

144 

145 

146__all__ = [ 

147 "ALL_TOOLS", 

148 "CALCULATOR_TOOLS", 

149 "CODE_EXECUTION_TOOLS", 

150 "FILESYSTEM_TOOLS", 

151 "SEARCH_TOOLS", 

152 "add", 

153 "calculator", 

154 "divide", 

155 "get_all_tools", # Factory function for runtime tool configuration 

156 "get_tool_by_name", 

157 "get_tools", 

158 "list_directory", 

159 "multiply", 

160 "read_file", 

161 "search_files", 

162 "search_knowledge_base", 

163 "subtract", 

164 "web_search", 

165]