deepcode-hku 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. cli/__init__.py +18 -0
  2. cli/cli_app.py +296 -0
  3. cli/cli_interface.py +744 -0
  4. cli/cli_launcher.py +155 -0
  5. cli/main_cli.py +243 -0
  6. cli/workflows/__init__.py +11 -0
  7. cli/workflows/cli_workflow_adapter.py +336 -0
  8. deepcode.py +219 -0
  9. deepcode_hku-1.0.1.dist-info/METADATA +695 -0
  10. deepcode_hku-1.0.1.dist-info/RECORD +44 -0
  11. deepcode_hku-1.0.1.dist-info/WHEEL +5 -0
  12. deepcode_hku-1.0.1.dist-info/entry_points.txt +2 -0
  13. deepcode_hku-1.0.1.dist-info/licenses/LICENSE +21 -0
  14. deepcode_hku-1.0.1.dist-info/top_level.txt +6 -0
  15. tools/__init__.py +0 -0
  16. tools/code_implementation_server.py +1045 -0
  17. tools/code_indexer.py +1657 -0
  18. tools/code_reference_indexer.py +486 -0
  19. tools/command_executor.py +324 -0
  20. tools/git_command.py +356 -0
  21. tools/pdf_converter.py +640 -0
  22. tools/pdf_downloader.py +1370 -0
  23. tools/pdf_utils.py +52 -0
  24. ui/__init__.py +43 -0
  25. ui/app.py +13 -0
  26. ui/components.py +1450 -0
  27. ui/handlers.py +773 -0
  28. ui/layout.py +106 -0
  29. ui/streamlit_app.py +38 -0
  30. ui/styles.py +2116 -0
  31. utils/__init__.py +17 -0
  32. utils/cli_interface.py +459 -0
  33. utils/dialogue_logger.py +671 -0
  34. utils/file_processor.py +426 -0
  35. utils/simple_llm_logger.py +198 -0
  36. workflows/__init__.py +31 -0
  37. workflows/agent_orchestration_engine.py +1371 -0
  38. workflows/agents/__init__.py +13 -0
  39. workflows/agents/code_implementation_agent.py +1093 -0
  40. workflows/agents/memory_agent_concise.py +923 -0
  41. workflows/agents/memory_agent_concise_index.py +935 -0
  42. workflows/code_implementation_workflow.py +924 -0
  43. workflows/code_implementation_workflow_index.py +931 -0
  44. workflows/codebase_index_workflow.py +726 -0
@@ -0,0 +1,324 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Command Executor MCP Tool / 命令执行器 MCP 工具
4
+
5
+ 专门负责执行LLM生成的shell命令来创建文件树结构
6
+ Specialized in executing LLM-generated shell commands to create file tree structures
7
+ """
8
+
9
+ import subprocess
10
+ from pathlib import Path
11
+ from typing import List, Dict
12
+ from mcp.server.models import InitializationOptions
13
+ import mcp.types as types
14
+ from mcp.server import NotificationOptions, Server
15
+ import mcp.server.stdio
16
+
17
+ # 创建MCP服务器实例 / Create MCP server instance
18
+ app = Server("command-executor")
19
+
20
+
21
+ @app.list_tools()
22
+ async def handle_list_tools() -> list[types.Tool]:
23
+ """
24
+ 列出可用工具 / List available tools
25
+ """
26
+ return [
27
+ types.Tool(
28
+ name="execute_commands",
29
+ description="""
30
+ 执行shell命令列表来创建文件树结构
31
+ Execute shell command list to create file tree structure
32
+
33
+ Args:
34
+ commands: 要执行的shell命令列表(每行一个命令)
35
+ working_directory: 执行命令的工作目录
36
+
37
+ Returns:
38
+ 命令执行结果和详细报告
39
+ """,
40
+ inputSchema={
41
+ "type": "object",
42
+ "properties": {
43
+ "commands": {
44
+ "type": "string",
45
+ "title": "Commands",
46
+ "description": "要执行的shell命令列表,每行一个命令",
47
+ },
48
+ "working_directory": {
49
+ "type": "string",
50
+ "title": "Working Directory",
51
+ "description": "执行命令的工作目录",
52
+ },
53
+ },
54
+ "required": ["commands", "working_directory"],
55
+ },
56
+ ),
57
+ types.Tool(
58
+ name="execute_single_command",
59
+ description="""
60
+ 执行单个shell命令
61
+ Execute single shell command
62
+
63
+ Args:
64
+ command: 要执行的单个命令
65
+ working_directory: 执行命令的工作目录
66
+
67
+ Returns:
68
+ 命令执行结果
69
+ """,
70
+ inputSchema={
71
+ "type": "object",
72
+ "properties": {
73
+ "command": {
74
+ "type": "string",
75
+ "title": "Command",
76
+ "description": "要执行的单个shell命令",
77
+ },
78
+ "working_directory": {
79
+ "type": "string",
80
+ "title": "Working Directory",
81
+ "description": "执行命令的工作目录",
82
+ },
83
+ },
84
+ "required": ["command", "working_directory"],
85
+ },
86
+ ),
87
+ ]
88
+
89
+
90
+ @app.call_tool()
91
+ async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent]:
92
+ """
93
+ 处理工具调用 / Handle tool calls
94
+ """
95
+ try:
96
+ if name == "execute_commands":
97
+ return await execute_command_batch(
98
+ arguments.get("commands", ""), arguments.get("working_directory", ".")
99
+ )
100
+ elif name == "execute_single_command":
101
+ return await execute_single_command(
102
+ arguments.get("command", ""), arguments.get("working_directory", ".")
103
+ )
104
+ else:
105
+ raise ValueError(f"未知工具 / Unknown tool: {name}")
106
+
107
+ except Exception as e:
108
+ return [
109
+ types.TextContent(
110
+ type="text",
111
+ text=f"工具执行错误 / Error executing tool {name}: {str(e)}",
112
+ )
113
+ ]
114
+
115
+
116
+ async def execute_command_batch(
117
+ commands: str, working_directory: str
118
+ ) -> list[types.TextContent]:
119
+ """
120
+ 执行多个shell命令 / Execute multiple shell commands
121
+
122
+ Args:
123
+ commands: 命令列表,每行一个命令 / Command list, one command per line
124
+ working_directory: 工作目录 / Working directory
125
+
126
+ Returns:
127
+ 执行结果 / Execution results
128
+ """
129
+ try:
130
+ # 确保工作目录存在 / Ensure working directory exists
131
+ Path(working_directory).mkdir(parents=True, exist_ok=True)
132
+
133
+ # 分割命令行 / Split command lines
134
+ command_lines = [
135
+ cmd.strip() for cmd in commands.strip().split("\n") if cmd.strip()
136
+ ]
137
+
138
+ if not command_lines:
139
+ return [
140
+ types.TextContent(
141
+ type="text", text="没有提供有效命令 / No valid commands provided"
142
+ )
143
+ ]
144
+
145
+ results = []
146
+ stats = {"successful": 0, "failed": 0, "timeout": 0}
147
+
148
+ for i, command in enumerate(command_lines, 1):
149
+ try:
150
+ # 执行命令 / Execute command
151
+ result = subprocess.run(
152
+ command,
153
+ shell=True,
154
+ cwd=working_directory,
155
+ capture_output=True,
156
+ text=True,
157
+ timeout=30, # 30秒超时
158
+ )
159
+
160
+ if result.returncode == 0:
161
+ results.append(f"✅ Command {i}: {command}")
162
+ if result.stdout.strip():
163
+ results.append(f" 输出 / Output: {result.stdout.strip()}")
164
+ stats["successful"] += 1
165
+ else:
166
+ results.append(f"❌ Command {i}: {command}")
167
+ if result.stderr.strip():
168
+ results.append(f" 错误 / Error: {result.stderr.strip()}")
169
+ stats["failed"] += 1
170
+
171
+ except subprocess.TimeoutExpired:
172
+ results.append(f"⏱️ Command {i} 超时 / timeout: {command}")
173
+ stats["timeout"] += 1
174
+ except Exception as e:
175
+ results.append(f"💥 Command {i} 异常 / exception: {command} - {str(e)}")
176
+ stats["failed"] += 1
177
+
178
+ # 生成执行报告 / Generate execution report
179
+ summary = generate_execution_summary(working_directory, command_lines, stats)
180
+ final_result = summary + "\n" + "\n".join(results)
181
+
182
+ return [types.TextContent(type="text", text=final_result)]
183
+
184
+ except Exception as e:
185
+ return [
186
+ types.TextContent(
187
+ type="text",
188
+ text=f"批量命令执行失败 / Failed to execute command batch: {str(e)}",
189
+ )
190
+ ]
191
+
192
+
193
+ async def execute_single_command(
194
+ command: str, working_directory: str
195
+ ) -> list[types.TextContent]:
196
+ """
197
+ 执行单个shell命令 / Execute single shell command
198
+
199
+ Args:
200
+ command: 要执行的命令 / Command to execute
201
+ working_directory: 工作目录 / Working directory
202
+
203
+ Returns:
204
+ 执行结果 / Execution result
205
+ """
206
+ try:
207
+ # 确保工作目录存在 / Ensure working directory exists
208
+ Path(working_directory).mkdir(parents=True, exist_ok=True)
209
+
210
+ # 执行命令 / Execute command
211
+ result = subprocess.run(
212
+ command,
213
+ shell=True,
214
+ cwd=working_directory,
215
+ capture_output=True,
216
+ text=True,
217
+ timeout=30,
218
+ )
219
+
220
+ # 格式化输出 / Format output
221
+ output = format_single_command_result(command, working_directory, result)
222
+
223
+ return [types.TextContent(type="text", text=output)]
224
+
225
+ except subprocess.TimeoutExpired:
226
+ return [
227
+ types.TextContent(
228
+ type="text", text=f"⏱️ 命令超时 / Command timeout: {command}"
229
+ )
230
+ ]
231
+ except Exception as e:
232
+ return [
233
+ types.TextContent(
234
+ type="text", text=f"💥 命令执行错误 / Command execution error: {str(e)}"
235
+ )
236
+ ]
237
+
238
+
239
+ def generate_execution_summary(
240
+ working_directory: str, command_lines: List[str], stats: Dict[str, int]
241
+ ) -> str:
242
+ """
243
+ 生成执行总结 / Generate execution summary
244
+
245
+ Args:
246
+ working_directory: 工作目录 / Working directory
247
+ command_lines: 命令列表 / Command list
248
+ stats: 统计信息 / Statistics
249
+
250
+ Returns:
251
+ 格式化的总结 / Formatted summary
252
+ """
253
+ return f"""
254
+ 命令执行总结 / Command Execution Summary:
255
+ {'='*50}
256
+ 工作目录 / Working Directory: {working_directory}
257
+ 总命令数 / Total Commands: {len(command_lines)}
258
+ 成功 / Successful: {stats['successful']}
259
+ 失败 / Failed: {stats['failed']}
260
+ 超时 / Timeout: {stats['timeout']}
261
+
262
+ 详细结果 / Detailed Results:
263
+ {'-'*50}"""
264
+
265
+
266
+ def format_single_command_result(
267
+ command: str, working_directory: str, result: subprocess.CompletedProcess
268
+ ) -> str:
269
+ """
270
+ 格式化单命令执行结果 / Format single command execution result
271
+
272
+ Args:
273
+ command: 执行的命令 / Executed command
274
+ working_directory: 工作目录 / Working directory
275
+ result: 执行结果 / Execution result
276
+
277
+ Returns:
278
+ 格式化的结果 / Formatted result
279
+ """
280
+ output = f"""
281
+ 单命令执行 / Single Command Execution:
282
+ {'='*40}
283
+ 工作目录 / Working Directory: {working_directory}
284
+ 命令 / Command: {command}
285
+ 返回码 / Return Code: {result.returncode}
286
+
287
+ """
288
+
289
+ if result.returncode == 0:
290
+ output += "✅ 状态 / Status: SUCCESS / 成功\n"
291
+ if result.stdout.strip():
292
+ output += f"输出 / Output:\n{result.stdout.strip()}\n"
293
+ else:
294
+ output += "❌ 状态 / Status: FAILED / 失败\n"
295
+ if result.stderr.strip():
296
+ output += f"错误 / Error:\n{result.stderr.strip()}\n"
297
+
298
+ return output
299
+
300
+
301
+ async def main():
302
+ """
303
+ 运行MCP服务器 / Run MCP server
304
+ """
305
+ # 通过stdio运行服务器 / Run server via stdio
306
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
307
+ await app.run(
308
+ read_stream,
309
+ write_stream,
310
+ InitializationOptions(
311
+ server_name="command-executor",
312
+ server_version="1.0.0",
313
+ capabilities=app.get_capabilities(
314
+ notification_options=NotificationOptions(),
315
+ experimental_capabilities={},
316
+ ),
317
+ ),
318
+ )
319
+
320
+
321
+ if __name__ == "__main__":
322
+ import asyncio
323
+
324
+ asyncio.run(main())
tools/git_command.py ADDED
@@ -0,0 +1,356 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ GitHub Repository Downloader MCP Tool using FastMCP
4
+ """
5
+
6
+ import asyncio
7
+ import os
8
+ import re
9
+ from typing import Dict, List, Optional
10
+ from pathlib import Path
11
+
12
+ from mcp.server import FastMCP
13
+
14
+ # 创建 FastMCP 实例
15
+ mcp = FastMCP("github-downloader")
16
+
17
+
18
+ class GitHubURLExtractor:
19
+ """提取GitHub URL的工具类"""
20
+
21
+ @staticmethod
22
+ def extract_github_urls(text: str) -> List[str]:
23
+ """从文本中提取GitHub URLs"""
24
+ patterns = [
25
+ # 标准HTTPS URL
26
+ r"https?://github\.com/[\w\-\.]+/[\w\-\.]+(?:\.git)?",
27
+ # SSH URL
28
+ r"git@github\.com:[\w\-\.]+/[\w\-\.]+(?:\.git)?",
29
+ # 短格式 owner/repo - 更严格的匹配
30
+ r"(?<!\S)(?<!/)(?<!\.)([\w\-\.]+/[\w\-\.]+)(?!/)(?!\S)",
31
+ ]
32
+
33
+ urls = []
34
+ for pattern in patterns:
35
+ matches = re.findall(pattern, text, re.IGNORECASE)
36
+ for match in matches:
37
+ # 处理短格式
38
+ if isinstance(match, tuple):
39
+ match = match[0]
40
+
41
+ # 清理URL
42
+ if match.startswith("git@"):
43
+ url = match.replace("git@github.com:", "https://github.com/")
44
+ elif match.startswith("http"):
45
+ url = match
46
+ else:
47
+ # 处理短格式 (owner/repo) - 添加更多验证
48
+ if "/" in match and not any(
49
+ x in match for x in ["./", "../", "deepcode_lab", "tools"]
50
+ ):
51
+ parts = match.split("/")
52
+ if (
53
+ len(parts) == 2
54
+ and all(
55
+ part.replace("-", "").replace("_", "").isalnum()
56
+ for part in parts
57
+ )
58
+ and not any(part.startswith(".") for part in parts)
59
+ ):
60
+ url = f"https://github.com/{match}"
61
+ else:
62
+ continue
63
+ else:
64
+ continue
65
+
66
+ # 规范化 URL
67
+ url = url.rstrip(".git")
68
+ url = url.rstrip("/")
69
+
70
+ # 修复重复的 github.com
71
+ if "github.com/github.com/" in url:
72
+ url = url.replace("github.com/github.com/", "github.com/")
73
+
74
+ urls.append(url)
75
+
76
+ return list(set(urls)) # 去重
77
+
78
+ @staticmethod
79
+ def extract_target_path(text: str) -> Optional[str]:
80
+ """从文本中提取目标路径"""
81
+ # 路径指示词模式
82
+ patterns = [
83
+ r'(?:to|into|in|at)\s+(?:folder|directory|path)?\s*["\']?([^\s"\']+)["\']?',
84
+ r'(?:save|download|clone)\s+(?:to|into|at)\s+["\']?([^\s"\']+)["\']?',
85
+ # 中文支持
86
+ r'(?:到|在|保存到|下载到|克隆到)\s*["\']?([^\s"\']+)["\']?',
87
+ ]
88
+
89
+ for pattern in patterns:
90
+ match = re.search(pattern, text, re.IGNORECASE)
91
+ if match:
92
+ path = match.group(1).strip("。,,.")
93
+ # 过滤掉通用词
94
+ if path and path.lower() not in [
95
+ "here",
96
+ "there",
97
+ "current",
98
+ "local",
99
+ "这里",
100
+ "当前",
101
+ "本地",
102
+ ]:
103
+ return path
104
+
105
+ return None
106
+
107
+ @staticmethod
108
+ def infer_repo_name(url: str) -> str:
109
+ """从URL推断仓库名称"""
110
+ url = url.rstrip(".git")
111
+ if "github.com" in url:
112
+ parts = url.split("/")
113
+ if len(parts) >= 2:
114
+ return parts[-1]
115
+ return "repository"
116
+
117
+
118
+ async def check_git_installed() -> bool:
119
+ """检查Git是否安装"""
120
+ try:
121
+ proc = await asyncio.create_subprocess_exec(
122
+ "git",
123
+ "--version",
124
+ stdout=asyncio.subprocess.PIPE,
125
+ stderr=asyncio.subprocess.PIPE,
126
+ )
127
+ await proc.wait()
128
+ return proc.returncode == 0
129
+ except Exception:
130
+ return False
131
+
132
+
133
+ async def clone_repository(repo_url: str, target_path: str) -> Dict[str, any]:
134
+ """执行git clone命令"""
135
+ try:
136
+ proc = await asyncio.create_subprocess_exec(
137
+ "git",
138
+ "clone",
139
+ repo_url,
140
+ target_path,
141
+ stdout=asyncio.subprocess.PIPE,
142
+ stderr=asyncio.subprocess.PIPE,
143
+ )
144
+
145
+ stdout, stderr = await proc.communicate()
146
+
147
+ return {
148
+ "success": proc.returncode == 0,
149
+ "stdout": stdout.decode("utf-8", errors="replace"),
150
+ "stderr": stderr.decode("utf-8", errors="replace"),
151
+ "returncode": proc.returncode,
152
+ }
153
+ except Exception as e:
154
+ return {"success": False, "error": str(e)}
155
+
156
+
157
+ @mcp.tool()
158
+ async def download_github_repo(instruction: str) -> str:
159
+ """
160
+ Download GitHub repositories from natural language instructions.
161
+
162
+ Args:
163
+ instruction: Natural language text containing GitHub URLs and optional target paths
164
+
165
+ Returns:
166
+ Status message about the download operation
167
+
168
+ Examples:
169
+ - "Download https://github.com/openai/gpt-3"
170
+ - "Clone microsoft/vscode to my-projects folder"
171
+ - "Get https://github.com/facebook/react"
172
+ """
173
+ # 检查Git是否安装
174
+ if not await check_git_installed():
175
+ return "❌ Error: Git is not installed or not in system PATH"
176
+
177
+ extractor = GitHubURLExtractor()
178
+
179
+ # 提取GitHub URLs
180
+ urls = extractor.extract_github_urls(instruction)
181
+ if not urls:
182
+ return "❌ No GitHub URLs found in the instruction"
183
+
184
+ # 提取目标路径
185
+ target_path = extractor.extract_target_path(instruction)
186
+
187
+ # 下载仓库
188
+ results = []
189
+ for url in urls:
190
+ try:
191
+ # 准备目标路径
192
+ if target_path:
193
+ # 判断是否为绝对路径
194
+ if os.path.isabs(target_path):
195
+ # 如果是绝对路径,直接使用
196
+ final_path = target_path
197
+ # 如果目标路径是目录,添加仓库名
198
+ if os.path.basename(target_path) == "" or target_path.endswith("/"):
199
+ final_path = os.path.join(
200
+ target_path, extractor.infer_repo_name(url)
201
+ )
202
+ else:
203
+ # 如果是相对路径,保持相对路径
204
+ final_path = target_path
205
+ # 如果目标路径是目录,添加仓库名
206
+ if os.path.basename(target_path) == "" or target_path.endswith("/"):
207
+ final_path = os.path.join(
208
+ target_path, extractor.infer_repo_name(url)
209
+ )
210
+ else:
211
+ final_path = extractor.infer_repo_name(url)
212
+
213
+ # 如果是相对路径,确保使用相对路径格式
214
+ if not os.path.isabs(final_path):
215
+ final_path = os.path.normpath(final_path)
216
+ if final_path.startswith("/"):
217
+ final_path = final_path.lstrip("/")
218
+
219
+ # 确保父目录存在
220
+ parent_dir = os.path.dirname(final_path)
221
+ if parent_dir:
222
+ os.makedirs(parent_dir, exist_ok=True)
223
+
224
+ # 检查目标路径是否已存在
225
+ if os.path.exists(final_path):
226
+ results.append(
227
+ f"❌ Failed to download {url}: Target path already exists: {final_path}"
228
+ )
229
+ continue
230
+
231
+ # 执行克隆
232
+ result = await clone_repository(url, final_path)
233
+
234
+ if result["success"]:
235
+ msg = f"✅ Successfully downloaded: {url}\n"
236
+ msg += f" Location: {final_path}"
237
+ if result.get("stdout"):
238
+ msg += f"\n {result['stdout'].strip()}"
239
+ else:
240
+ msg = f"❌ Failed to download: {url}\n"
241
+ msg += f" Error: {result.get('error', result.get('stderr', 'Unknown error'))}"
242
+
243
+ except Exception as e:
244
+ msg = f"❌ Failed to download: {url}\n"
245
+ msg += f" Error: {str(e)}"
246
+
247
+ results.append(msg)
248
+
249
+ return "\n\n".join(results)
250
+
251
+
252
+ @mcp.tool()
253
+ async def parse_github_urls(text: str) -> str:
254
+ """
255
+ Extract GitHub URLs and target paths from text.
256
+
257
+ Args:
258
+ text: Text containing GitHub URLs
259
+
260
+ Returns:
261
+ Parsed GitHub URLs and target path information
262
+ """
263
+ extractor = GitHubURLExtractor()
264
+
265
+ urls = extractor.extract_github_urls(text)
266
+ target_path = extractor.extract_target_path(text)
267
+
268
+ content = "📝 Parsed information:\n\n"
269
+
270
+ if urls:
271
+ content += "GitHub URLs found:\n"
272
+ for url in urls:
273
+ content += f" • {url}\n"
274
+ else:
275
+ content += "No GitHub URLs found\n"
276
+
277
+ if target_path:
278
+ content += f"\nTarget path: {target_path}"
279
+ else:
280
+ content += "\nTarget path: Not specified (will use repository name)"
281
+
282
+ return content
283
+
284
+
285
+ @mcp.tool()
286
+ async def git_clone(
287
+ repo_url: str, target_path: Optional[str] = None, branch: Optional[str] = None
288
+ ) -> str:
289
+ """
290
+ Clone a specific GitHub repository.
291
+
292
+ Args:
293
+ repo_url: GitHub repository URL
294
+ target_path: Optional target directory path
295
+ branch: Optional branch name to clone
296
+
297
+ Returns:
298
+ Status message about the clone operation
299
+ """
300
+ # 检查Git是否安装
301
+ if not await check_git_installed():
302
+ return "❌ Error: Git is not installed or not in system PATH"
303
+
304
+ # 准备目标路径
305
+ if not target_path:
306
+ extractor = GitHubURLExtractor()
307
+ target_path = extractor.infer_repo_name(repo_url)
308
+
309
+ # 转换为绝对路径
310
+ if not os.path.isabs(target_path):
311
+ target_path = str(Path.cwd() / target_path)
312
+
313
+ # 检查目标路径
314
+ if os.path.exists(target_path):
315
+ return f"❌ Error: Target path already exists: {target_path}"
316
+
317
+ # 构建命令
318
+ cmd = ["git", "clone"]
319
+ if branch:
320
+ cmd.extend(["-b", branch])
321
+ cmd.extend([repo_url, target_path])
322
+
323
+ # 执行克隆
324
+ try:
325
+ proc = await asyncio.create_subprocess_exec(
326
+ *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
327
+ )
328
+
329
+ stdout, stderr = await proc.communicate()
330
+
331
+ if proc.returncode == 0:
332
+ result = "✅ Successfully cloned repository\n"
333
+ result += f"Repository: {repo_url}\n"
334
+ result += f"Location: {target_path}"
335
+ if branch:
336
+ result += f"\nBranch: {branch}"
337
+ return result
338
+ else:
339
+ return f"❌ Clone failed\nError: {stderr.decode('utf-8', errors='replace')}"
340
+
341
+ except Exception as e:
342
+ return f"❌ Clone failed\nError: {str(e)}"
343
+
344
+
345
+ # 主程序入口
346
+ if __name__ == "__main__":
347
+ print("🚀 GitHub Repository Downloader MCP Tool")
348
+ print("📝 Starting server with FastMCP...")
349
+ print("\nAvailable tools:")
350
+ print(" • download_github_repo - Download repos from natural language")
351
+ print(" • parse_github_urls - Extract GitHub URLs from text")
352
+ print(" • git_clone - Clone a specific repository")
353
+ print("")
354
+
355
+ # 运行服务器
356
+ mcp.run()