Coverage for src/quickmcp/quick.py: 86%

44 statements  

« prev     ^ index     » next       coverage.py v7.10.4, created at 2025-08-20 20:56 +0200

1""" 

2QuickMCP - Super Simple API 

3The easiest way to create MCP servers. 

4""" 

5 

6from typing import Optional, Any, Callable 

7from pathlib import Path 

8 

9from .server import QuickMCPServer 

10from .factory import create_mcp_from_module as _create_from_module 

11from .factory import MCPFactory, FactoryConfig 

12 

13 

14def server(name: str = "quickmcp-server") -> QuickMCPServer: 

15 """ 

16 Create a new MCP server with zero configuration. 

17  

18 Example: 

19 from quickmcp.quick import server 

20  

21 app = server("my-app") 

22  

23 @app.tool() 

24 def hello(name: str) -> str: 

25 return f"Hello, {name}!" 

26  

27 app.run() 

28 """ 

29 return QuickMCPServer(name) 

30 

31 

32def from_file( 

33 file_path: str, 

34 name: Optional[str] = None, 

35 include_private: bool = False 

36) -> QuickMCPServer: 

37 """ 

38 Create an MCP server from any Python file instantly. 

39  

40 Example: 

41 from quickmcp.quick import from_file 

42  

43 # Turn any Python file into an MCP server 

44 app = from_file("my_utils.py") 

45 app.run() 

46  

47 Args: 

48 file_path: Path to Python file 

49 name: Optional server name (defaults to filename) 

50 include_private: Include functions starting with underscore 

51  

52 Returns: 

53 Ready-to-run MCP server 

54 """ 

55 # Use sensible defaults - no configuration needed 

56 config = FactoryConfig( 

57 check_dependencies=False, # Don't fail on missing optional deps 

58 warn_on_code_execution=False, # Don't warn, just work 

59 strict_type_conversion=False, # Be flexible 

60 cache_dependency_analysis=True, # Fast 

61 cache_type_hints=True, # Fast 

62 ) 

63 

64 if name is None: 

65 name = Path(file_path).stem.replace("_", "-") + "-mcp" 

66 

67 return _create_from_module( 

68 file_path, 

69 server_name=name, 

70 include_private=include_private, 

71 auto_run=False, 

72 config=config 

73 ) 

74 

75 

76def from_object(obj: Any, name: Optional[str] = None) -> QuickMCPServer: 

77 """ 

78 Create an MCP server from any Python object (module, class, or dict). 

79  

80 Example: 

81 from quickmcp.quick import from_object 

82 import math 

83  

84 # From a module 

85 app = from_object(math) 

86  

87 # From a class 

88 class MyTools: 

89 def add(self, a: int, b: int) -> int: 

90 return a + b 

91  

92 app = from_object(MyTools) 

93  

94 # From a dict of functions 

95 app = from_object({ 

96 "greet": lambda name: f"Hello, {name}!", 

97 "add": lambda a, b: a + b 

98 }) 

99  

100 app.run() 

101 """ 

102 from .factory import create_mcp_from_object as _create_from_object 

103 

104 # Use sensible defaults 

105 config = FactoryConfig( 

106 check_dependencies=False, 

107 warn_on_code_execution=False, 

108 strict_type_conversion=False, 

109 cache_dependency_analysis=True, 

110 cache_type_hints=True, 

111 ) 

112 

113 return _create_from_object(obj, server_name=name, config=config) 

114 

115 

116def run( 

117 func_or_file: Optional[Any] = None, 

118 name: str = "quickmcp-server", 

119 **kwargs 

120): 

121 """ 

122 The simplest way to run an MCP server. 

123  

124 Example: 

125 from quickmcp.quick import run 

126  

127 # Run current file as server 

128 run(__file__) 

129  

130 # Run with a function dict 

131 run({ 

132 "hello": lambda name: f"Hello, {name}!", 

133 "add": lambda a, b: a + b 

134 }) 

135  

136 # Run with decorators in current file 

137 from quickmcp.quick import run, tool 

138  

139 @tool 

140 def greet(name: str) -> str: 

141 return f"Hello, {name}!" 

142  

143 run() # Automatically finds decorated functions 

144 """ 

145 import sys 

146 import inspect 

147 

148 # If no argument, try to find decorated functions in caller's module 

149 if func_or_file is None: 

150 # Get the calling module 

151 frame = inspect.currentframe() 

152 if frame and frame.f_back: 

153 caller_module = inspect.getmodule(frame.f_back) 

154 if caller_module and hasattr(caller_module, '__file__'): 

155 func_or_file = caller_module.__file__ 

156 

157 # Determine what we're working with 

158 if func_or_file is None: 

159 # Create empty server 

160 app = server(name) 

161 elif isinstance(func_or_file, str): 

162 # It's a file path 

163 app = from_file(func_or_file, name=name) 

164 elif isinstance(func_or_file, dict): 

165 # It's a dictionary of functions 

166 app = from_object(func_or_file, name=name) 

167 elif callable(func_or_file): 

168 # It's a single function 

169 app = from_object({func_or_file.__name__: func_or_file}, name=name) 

170 elif hasattr(func_or_file, '__class__'): 

171 # It's an object/class 

172 app = from_object(func_or_file, name=name) 

173 else: 

174 # Create empty server 

175 app = server(name) 

176 

177 # Run the server 

178 transport = kwargs.get('transport', 'stdio') 

179 host = kwargs.get('host', 'localhost') 

180 port = kwargs.get('port', 8000) 

181 

182 app.run(transport=transport, host=host, port=port) 

183 

184 

185# Simple decorator for marking functions as tools 

186def tool(func: Optional[Callable] = None, *, name: Optional[str] = None, description: Optional[str] = None): 

187 """ 

188 Simple decorator to mark a function as an MCP tool. 

189  

190 Example: 

191 from quickmcp.quick import tool, run 

192  

193 @tool 

194 def hello(name: str) -> str: 

195 return f"Hello, {name}!" 

196  

197 @tool(description="Add two numbers") 

198 def add(a: int, b: int) -> int: 

199 return a + b 

200  

201 run() # Automatically creates server with these tools 

202 """ 

203 from .factory import mcp_tool 

204 return mcp_tool(func, name=name, description=description) 

205 

206 

207# Export the simplest API 

208__all__ = [ 

209 'server', 

210 'from_file', 

211 'from_object', 

212 'run', 

213 'tool', 

214]