实验四:生产级GitHub克隆服务器 🐙
"最优秀的工具是那些你忘记在使用它们的工具,因为它们工作得如此自然。" - 让我们构建一个真正解决日常开发痛点的MCP服务器!
🎯 实验概述
这是我们MCP学习之旅的"毕业设计"!我们将构建一个真正实用的生产级MCP服务器,解决每个开发者都会遇到的问题:快速克隆GitHub仓库并在VS Code中打开。
现状痛点:
- 打开终端/命令提示符
- 导航到目标目录
- 运行
git clone
命令 - 在克隆的目录中打开VS Code
我们的解决方案: 一个智能命令搞定所有步骤! ⚡
📋 学习目标
完成这个实验后,你将能够:
- ✅ 构建企业级MCP服务器:为真实开发工作流创建生产就绪的解决方案
- ✅ 实现GitHub集成功能:通过MCP协议智能克隆仓库
- ✅ 掌握跨平台开发:编写兼容Windows/macOS/Linux的代码
- ✅ 集成GitHub Copilot代理:在AI助手中使用自定义MCP工具
- ✅ 应用生产最佳实践:错误处理、安全性和可维护性
⏱️ 时间分配
- 需求分析和设计:8分钟
- 核心功能实现:15分钟
- GitHub Copilot集成:7分钟
- 测试和优化:10分钟
🏗️ 项目架构设计
🎯 需求分析
我们要构建的GitHub克隆MCP服务器将提供以下功能:
功能模块 | 具体能力 | 技术实现 | 用户价值 |
---|---|---|---|
🔄 智能仓库克隆 | URL验证 + 自动克隆 | Git CLI + 参数验证 | 一键获取代码 |
📁 安全目录管理 | 冲突检测 + 路径验证 | 文件系统API | 防止意外覆盖 |
🚀 VS Code自动启动 | 跨平台应用启动 | 系统调用 | 无缝工作流切换 |
🛡️ 完善错误处理 | 网络 + 权限 + 路径异常 | 异常捕获体系 | 用户友好的体验 |
🔧 技术栈选择
# 核心技术栈
MCP_SDK = "1.9.3" # 最新MCP Python SDK
ASYNC_HTTP = "httpx" # 现代异步HTTP客户端
FILE_SYSTEM = "pathlib" # 现代路径操作
PROCESS_MGMT = "asyncio" # 异步进程管理
VALIDATION = "pydantic" # 数据验证(可选)
🚀 实现步骤详解
第一步:创建GitHub智能代理
让我们在AI Toolkit的Agent Builder中创建一个专门的GitHub管理代理:
📋 代理配置
- 代理名称:GitHubDeveloperAssistant
- 描述:专业的GitHub仓库管理和开发工具集成助手
- 模型选择:Claude 3.5 Sonnet(推荐用于代码分析)
- 工具集成:自定义MCP服务器
第二步:核心MCP服务器实现
创建文件 github_clone_mcp_server.py
:
#!/usr/bin/env python3
"""
GitHub Clone MCP Server - 生产级仓库克隆助手
版本: 1.0.0
作者: MCP Developer
描述: 智能化的GitHub仓库克隆和VS Code集成工具
"""
import asyncio
import json
import os
import subprocess
import sys
from pathlib import Path
from urllib.parse import urlparse
import platform
# MCP SDK导入
from mcp import Server
from mcp.types import Tool, TextContent, ImageContent
import httpx
class GitHubCloneMCPServer:
"""GitHub克隆MCP服务器主类"""
def __init__(self):
self.server = Server("github-clone-assistant")
self.workspace_root = Path.home() / "mcp-workspace"
self.workspace_root.mkdir(exist_ok=True)
self.setup_tools()
def setup_tools(self):
"""设置MCP工具"""
# 工具1: 智能仓库克隆
clone_tool = Tool(
name="intelligent_clone_repository",
description="智能克隆GitHub仓库,支持自动验证、目录管理和VS Code集成",
inputSchema={
"type": "object",
"properties": {
"repo_url": {
"type": "string",
"description": "GitHub仓库URL (支持HTTPS格式)",
"pattern": r"^https:\/\/github\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$"
},
"target_directory": {
"type": "string",
"description": "目标目录名(可选,自动从URL推断)",
},
"workspace_path": {
"type": "string",
"description": "工作区路径(可选,默认使用用户工作区)"
},
"branch": {
"type": "string",
"description": "指定克隆分支(可选,默认为main/master)"
},
"open_in_vscode": {
"type": "boolean",
"description": "克隆后是否在VS Code中打开项目",
"default": True
},
"shallow_clone": {
"type": "boolean",
"description": "是否使用浅克隆以提高速度",
"default": True
}
},
"required": ["repo_url"]
}
)
# 工具2: VS Code智能启动器
vscode_tool = Tool(
name="smart_open_in_vscode",
description="跨平台智能启动VS Code,支持多版本检测",
inputSchema={
"type": "object",
"properties": {
"project_path": {
"type": "string",
"description": "要打开的项目路径(绝对路径)"
},
"prefer_insiders": {
"type": "boolean",
"description": "优先使用VS Code Insiders版本",
"default": False
}
},
"required": ["project_path"]
}
)
# 工具3: 工作区管理器
workspace_tool = Tool(
name="manage_workspace",
description="管理本地开发工作区,支持列表、清理和组织功能",
inputSchema={
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["list", "clean", "analyze", "organize"],
"description": "执行的操作类型"
},
"filter_pattern": {
"type": "string",
"description": "过滤模式(用于清理和分析)"
}
},
"required": ["action"]
}
)
# 注册工具
self.server.add_tool(clone_tool, self.intelligent_clone_repository)
self.server.add_tool(vscode_tool, self.smart_open_in_vscode)
self.server.add_tool(workspace_tool, self.manage_workspace)
async def intelligent_clone_repository(self, arguments):
"""智能仓库克隆的核心实现"""
repo_url = arguments["repo_url"]
target_directory = arguments.get("target_directory")
workspace_path = arguments.get("workspace_path")
branch = arguments.get("branch")
open_in_vscode = arguments.get("open_in_vscode", True)
shallow_clone = arguments.get("shallow_clone", True)
try:
# 步骤1: 验证和解析仓库URL
repo_info = await self.validate_and_parse_repo_url(repo_url)
if not repo_info["valid"]:
return [TextContent(
type="text",
text=f"❌ 无效的GitHub仓库URL:{repo_info['error']}"
)]
# 步骤2: 确定工作路径
if workspace_path:
workspace = Path(workspace_path)
else:
workspace = self.workspace_root
workspace.mkdir(parents=True, exist_ok=True)
# 步骤3: 确定目标目录名
if not target_directory:
target_directory = repo_info["repo_name"]
clone_path = workspace / target_directory
# 步骤4: 检查目录冲突
if clone_path.exists():
return [TextContent(
type="text",
text=f"⚠️ 目录冲突!\n路径 `{clone_path}` 已存在。\n💡 建议:使用不同的target_directory参数或手动删除现有目录。"
)]
# 步骤5: 执行智能克隆
clone_result = await self.execute_smart_clone(
repo_url, clone_path, branch, shallow_clone
)
if not clone_result["success"]:
return [TextContent(
type="text",
text=f"❌ 克隆失败:{clone_result['error']}\n\n🔧 排查建议:\n- 检查网络连接\n- 验证仓库是否公开或有访问权限\n- 确认Git已正确安装"
)]
# 步骤6: 项目智能分析
project_analysis = await self.analyze_cloned_project(clone_path)
# 步骤7: 生成详细报告
success_report = await self.generate_clone_report(
repo_info, clone_path, project_analysis, clone_result
)
# 步骤8: VS Code集成
if open_in_vscode:
vscode_result = await self.smart_open_in_vscode({
"project_path": str(clone_path)
})
if isinstance(vscode_result, list) and vscode_result:
vscode_text = vscode_result[0].text
if "✅" in vscode_text:
success_report += "\n\n🚀 **VS Code集成成功!** 项目已自动打开。"
else:
success_report += f"\n\n⚠️ **VS Code启动问题:** {vscode_text}"
return [TextContent(type="text", text=success_report)]
except Exception as e:
error_msg = f"❌ **系统异常**\n\n错误详情:{str(e)}\n\n🔧 **调试建议:**\n- 检查系统环境配置\n- 验证MCP服务器权限\n- 查看详细错误日志"
return [TextContent(type="text", text=error_msg)]
async def validate_and_parse_repo_url(self, repo_url: str) -> dict:
"""验证并解析GitHub仓库URL"""
try:
# 移除.git后缀(如果存在)
clean_url = repo_url.rstrip('.git')
# 解析URL
parsed = urlparse(clean_url)
# 验证域名
if parsed.netloc != 'github.com':
return {
"valid": False,
"error": "只支持GitHub仓库 (github.com)"
}
# 解析路径
path_parts = parsed.path.strip('/').split('/')
if len(path_parts) != 2:
return {
"valid": False,
"error": "URL格式不正确,应为 https://github.com/用户名/仓库名"
}
owner, repo_name = path_parts
# 验证GitHub API(可选,检查仓库是否存在)
api_check = await self.verify_repo_exists(owner, repo_name)
return {
"valid": True,
"owner": owner,
"repo_name": repo_name,
"full_name": f"{owner}/{repo_name}",
"clean_url": clean_url,
"api_check": api_check
}
except Exception as e:
return {
"valid": False,
"error": f"URL解析失败:{str(e)}"
}
async def verify_repo_exists(self, owner: str, repo_name: str) -> dict:
"""通过GitHub API验证仓库是否存在(可选功能)"""
try:
api_url = f"https://api.github.com/repos/{owner}/{repo_name}"
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.get(api_url)
if response.status_code == 200:
repo_data = response.json()
return {
"exists": True,
"private": repo_data.get("private", False),
"description": repo_data.get("description", ""),
"language": repo_data.get("language", "Unknown"),
"stars": repo_data.get("stargazers_count", 0),
"forks": repo_data.get("forks_count", 0)
}
elif response.status_code == 404:
return {"exists": False, "reason": "仓库不存在或为私有仓库"}
else:
return {"exists": "unknown", "reason": f"API返回状态码:{response.status_code}"}
except Exception as e:
return {"exists": "unknown", "reason": f"API检查失败:{str(e)}"}
async def execute_smart_clone(self, repo_url: str, clone_path: Path, branch: str = None, shallow: bool = True) -> dict:
"""执行智能Git克隆操作"""
try:
# 构建git clone命令
cmd = ["git", "clone"]
# 浅克隆优化
if shallow:
cmd.extend(["--depth", "1"])
# 指定分支
if branch:
cmd.extend(["-b", branch])
# URL和目标路径
cmd.extend([repo_url, str(clone_path)])
# 执行克隆
process = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=clone_path.parent
)
stdout, stderr = await process.communicate()
# 解析结果
if process.returncode == 0:
return {
"success": True,
"output": stdout.decode('utf-8', errors='ignore'),
"path": str(clone_path),
"command": ' '.join(cmd)
}
else:
error_output = stderr.decode('utf-8', errors='ignore')
return {
"success": False,
"error": error_output,
"command": ' '.join(cmd),
"exit_code": process.returncode
}
except FileNotFoundError:
return {
"success": False,
"error": "Git未安装或未添加到系统PATH中",
"suggestion": "请安装Git:https://git-scm.com/downloads"
}
except Exception as e:
return {
"success": False,
"error": f"克隆过程异常:{str(e)}"
}
async def analyze_cloned_project(self, project_path: Path) -> dict:
"""智能分析克隆的项目"""
analysis = {
"project_type": "Unknown",
"language": "Unknown",
"frameworks": [],
"package_managers": [],
"has_tests": False,
"has_docs": False,
"config_files": [],
"file_count": 0,
"size_mb": 0,
"readme_exists": False,
"license_exists": False
}
try:
# 检测项目类型和主要语言
detection_rules = {
"package.json": ("JavaScript/TypeScript", "Node.js"),
"requirements.txt": ("Python", "pip"),
"pyproject.toml": ("Python", "Poetry/pip"),
"Cargo.toml": ("Rust", "Cargo"),
"pom.xml": ("Java", "Maven"),
"build.gradle": ("Java/Kotlin", "Gradle"),
"go.mod": ("Go", "Go Modules"),
"Gemfile": ("Ruby", "Bundler"),
"composer.json": ("PHP", "Composer")
}
for config_file, (language, pm) in detection_rules.items():
if (project_path / config_file).exists():
analysis["language"] = language
analysis["package_managers"].append(pm)
analysis["config_files"].append(config_file)
# 深度分析package.json
if config_file == "package.json":
await self.analyze_package_json(project_path, analysis)
# 检测常见配置文件
common_configs = [
"README.md", "README.rst", "README.txt",
"LICENSE", "LICENSE.md", "LICENSE.txt",
".gitignore", ".env.example", ".env",
"docker-compose.yml", "Dockerfile",
".github/workflows", ".gitlab-ci.yml"
]
for config in common_configs:
config_path = project_path / config
if config_path.exists():
analysis["config_files"].append(config)
if "README" in config:
analysis["readme_exists"] = True
if "LICENSE" in config:
analysis["license_exists"] = True
# 检测测试和文档目录
test_patterns = ["test", "tests", "__tests__", "spec", "specs"]
doc_patterns = ["docs", "doc", "documentation", "wiki"]
for item in project_path.iterdir():
if item.is_dir():
item_name_lower = item.name.lower()
if any(pattern in item_name_lower for pattern in test_patterns):
analysis["has_tests"] = True
if any(pattern in item_name_lower for pattern in doc_patterns):
analysis["has_docs"] = True
# 计算项目统计信息
total_size = 0
file_count = 0
for file_path in project_path.rglob('*'):
if file_path.is_file():
file_count += 1
try:
total_size += file_path.stat().st_size
except (OSError, IOError):
pass # 忽略无法访问的文件
analysis["file_count"] = file_count
analysis["size_mb"] = round(total_size / (1024 * 1024), 2)
# 推断项目类型
if analysis["language"] != "Unknown":
if "React" in analysis["frameworks"]:
analysis["project_type"] = "React应用"
elif "Vue" in analysis["frameworks"]:
analysis["project_type"] = "Vue应用"
elif "Angular" in analysis["frameworks"]:
analysis["project_type"] = "Angular应用"
elif "Express" in analysis["frameworks"]:
analysis["project_type"] = "Node.js后端"
elif "Django" in analysis["frameworks"]:
analysis["project_type"] = "Django应用"
elif "Flask" in analysis["frameworks"]:
analysis["project_type"] = "Flask应用"
else:
analysis["project_type"] = f"{analysis['language']}项目"
except Exception as e:
analysis["analysis_error"] = str(e)
return analysis
async def analyze_package_json(self, project_path: Path, analysis: dict):
"""深度分析package.json文件"""
try:
package_json_path = project_path / "package.json"
with open(package_json_path, 'r', encoding='utf-8') as f:
package_data = json.load(f)
# 分析依赖以推断框架
all_deps = {
**package_data.get("dependencies", {}),
**package_data.get("devDependencies", {})
}
framework_detection = {
"react": "React",
"vue": "Vue.js",
"@vue/cli": "Vue.js",
"angular": "Angular",
"@angular/core": "Angular",
"express": "Express.js",
"next": "Next.js",
"nuxt": "Nuxt.js",
"svelte": "Svelte",
"gatsby": "Gatsby"
}
for dep_name, framework_name in framework_detection.items():
if dep_name in all_deps:
analysis["frameworks"].append(framework_name)
# 检测构建工具
build_tools = {
"webpack": "Webpack",
"vite": "Vite",
"rollup": "Rollup",
"parcel": "Parcel"
}
for tool_name, tool_display in build_tools.items():
if tool_name in all_deps:
analysis["frameworks"].append(tool_display)
except Exception as e:
pass # 静默处理JSON解析错误
async def generate_clone_report(self, repo_info: dict, clone_path: Path, analysis: dict, clone_result: dict) -> str:
"""生成详细的克隆成功报告"""
# 基本信息部分
report = f"""🎉 **GitHub仓库克隆成功!**
📦 **仓库信息**
• 仓库:`{repo_info['full_name']}`
• 本地路径:`{clone_path}`
• 克隆命令:`{clone_result.get('command', 'N/A')}`"""
# API信息(如果可用)
if repo_info.get("api_check", {}).get("exists") == True:
api_info = repo_info["api_check"]
report += f"""
• 描述:{api_info.get('description', '无描述')}
• 主要语言:{api_info.get('language', 'Unknown')}
• ⭐ Stars:{api_info.get('stars', 0)} | 🔄 Forks:{api_info.get('forks', 0)}"""
# 项目分析部分
report += f"""
🔍 **项目分析**
• 项目类型:{analysis['project_type']}
• 主要语言:{analysis['language']}
• 包管理器:{', '.join(analysis['package_managers']) if analysis['package_managers'] else '无'}
• 框架/工具:{', '.join(analysis['frameworks']) if analysis['frameworks'] else '无'}
• 文件数量:{analysis['file_count']}
• 项目大小:{analysis['size_mb']} MB
📋 **项目特征**
{'✅ 包含README文档' if analysis['readme_exists'] else '❌ 缺少README文档'}
{'✅ 包含开源许可证' if analysis['license_exists'] else '❌ 未发现许可证'}
{'✅ 包含测试代码' if analysis['has_tests'] else '❌ 未检测到测试'}
{'✅ 包含项目文档' if analysis['has_docs'] else '❌ 缺少项目文档'}"""
# 配置文件列表
if analysis['config_files']:
report += f"""
📄 **配置文件**
{chr(10).join(f'• {file}' for file in analysis['config_files'])}"""
# 开发建议
suggestions = []
if analysis['language'] == 'JavaScript/TypeScript':
suggestions.append("💡 运行 `npm install` 或 `yarn install` 安装依赖")
if 'package.json' in analysis['config_files']:
suggestions.append("📋 查看 package.json 中的 scripts 了解可用命令")
elif analysis['language'] == 'Python':
suggestions.append("🐍 建议创建虚拟环境:`python -m venv venv`")
if 'requirements.txt' in analysis['config_files']:
suggestions.append("📦 安装依赖:`pip install -r requirements.txt`")
elif 'pyproject.toml' in analysis['config_files']:
suggestions.append("📦 使用Poetry安装:`poetry install`")
elif analysis['language'] == 'Java':
if 'Maven' in analysis['package_managers']:
suggestions.append("☕ 运行 `mvn install` 编译项目")
elif 'Gradle' in analysis['package_managers']:
suggestions.append("☕ 运行 `./gradlew build` 编译项目")
if not analysis['has_tests']:
suggestions.append("🧪 建议添加自动化测试提高代码质量")
if not analysis['readme_exists']:
suggestions.append("📝 建议添加README.md文档")
if suggestions:
report += f"""
💡 **开发建议**
{chr(10).join(suggestions)}"""
return report
async def smart_open_in_vscode(self, arguments):
"""智能VS Code启动器"""
project_path = arguments["project_path"]
prefer_insiders = arguments.get("prefer_insiders", False)
try:
# 验证路径存在
path_obj = Path(project_path)
if not path_obj.exists():
return [TextContent(
type="text",
text=f"❌ 路径不存在:{project_path}"
)]
# 检测操作系统
system = platform.system().lower()
# VS Code可执行文件检测
vscode_commands = []
if prefer_insiders:
if system == "windows":
vscode_commands = ["code-insiders", "code"]
elif system == "darwin": # macOS
vscode_commands = ["code-insiders", "code"]
else: # Linux
vscode_commands = ["code-insiders", "code"]
else:
if system == "windows":
vscode_commands = ["code", "code-insiders"]
elif system == "darwin": # macOS
vscode_commands = ["code", "code-insiders"]
else: # Linux
vscode_commands = ["code", "code-insiders"]
# 尝试启动VS Code
for cmd in vscode_commands:
try:
process = await asyncio.create_subprocess_exec(
cmd, str(project_path),
stdout=asyncio.subprocess.DEVNULL,
stderr=asyncio.subprocess.DEVNULL
)
# 等待进程启动
await asyncio.sleep(1)
if process.returncode is None: # 进程正在运行
return [TextContent(
type="text",
text=f"✅ 已使用 {cmd} 打开项目:{project_path}"
)]
except FileNotFoundError:
continue # 尝试下一个命令
except Exception as e:
continue # 尝试下一个命令
# 所有尝试都失败
return [TextContent(
type="text",
text=f"""❌ 无法启动VS Code
🔧 **解决方案:**
1. 确保VS Code已安装
2. 将VS Code添加到系统PATH中
3. 手动打开:{project_path}
💡 **下载链接:** https://code.visualstudio.com/"""
)]
except Exception as e:
return [TextContent(
type="text",
text=f"❌ VS Code启动异常:{str(e)}"
)]
async def manage_workspace(self, arguments):
"""工作区管理器"""
action = arguments["action"]
filter_pattern = arguments.get("filter_pattern", "*")
if action == "list":
return await self.list_workspace_projects()
elif action == "analyze":
return await self.analyze_workspace()
elif action == "clean":
return await self.clean_workspace(filter_pattern)
elif action == "organize":
return await self.organize_workspace()
else:
return [TextContent(
type="text",
text="❌ 不支持的操作类型"
)]
async def list_workspace_projects(self):
"""列出工作区项目"""
try:
projects = []
for item in self.workspace_root.iterdir():
if item.is_dir() and not item.name.startswith('.'):
# 快速项目类型检测
project_type = "Unknown"
main_language = "Unknown"
if (item / "package.json").exists():
project_type = "Node.js"
main_language = "JavaScript/TypeScript"
elif (item / "requirements.txt").exists() or (item / "pyproject.toml").exists():
project_type = "Python"
main_language = "Python"
elif (item / "pom.xml").exists():
project_type = "Java (Maven)"
main_language = "Java"
elif (item / "Cargo.toml").exists():
project_type = "Rust"
main_language = "Rust"
elif (item / "go.mod").exists():
project_type = "Go"
main_language = "Go"
# 计算项目大小
try:
total_size = sum(
f.stat().st_size for f in item.rglob('*')
if f.is_file()
)
size_mb = round(total_size / (1024 * 1024), 2)
except:
size_mb = 0
projects.append({
"name": item.name,
"type": project_type,
"language": main_language,
"size_mb": size_mb,
"path": str(item),
"last_modified": item.stat().st_mtime
})
if not projects:
return [TextContent(
type="text",
text=f"""📁 **工作区为空**
工作区路径:`{self.workspace_root}`
💡 使用 `intelligent_clone_repository` 克隆第一个项目吧!"""
)]
# 按最后修改时间排序
projects.sort(key=lambda x: x["last_modified"], reverse=True)
# 生成项目列表报告
report = f"""📁 **MCP工作区项目列表**
📍 工作区路径:`{self.workspace_root}`
📊 项目总数:{len(projects)}
"""
for i, project in enumerate(projects, 1):
report += f"""**{i}. {project['name']}**
• 类型:{project['type']}
• 语言:{project['language']}
• 大小:{project['size_mb']} MB
• 路径:`{project['path']}`
"""
return [TextContent(type="text", text=report.strip())]
except Exception as e:
return [TextContent(
type="text",
text=f"❌ 列出项目失败:{str(e)}"
)]
async def analyze_workspace(self):
"""分析工作区统计信息"""
try:
stats = {
"total_projects": 0,
"total_size_mb": 0,
"languages": {},
"project_types": {},
"largest_project": None,
"newest_project": None
}
newest_time = 0
largest_size = 0
for item in self.workspace_root.iterdir():
if item.is_dir() and not item.name.startswith('.'):
stats["total_projects"] += 1
# 项目大小
try:
total_size = sum(
f.stat().st_size for f in item.rglob('*')
if f.is_file()
)
size_mb = round(total_size / (1024 * 1024), 2)
stats["total_size_mb"] += size_mb
# 记录最大项目
if size_mb > largest_size:
largest_size = size_mb
stats["largest_project"] = {
"name": item.name,
"size_mb": size_mb
}
except:
size_mb = 0
# 最新项目
try:
mtime = item.stat().st_mtime
if mtime > newest_time:
newest_time = mtime
stats["newest_project"] = item.name
except:
pass
# 语言统计
if (item / "package.json").exists():
stats["languages"]["JavaScript/TypeScript"] = stats["languages"].get("JavaScript/TypeScript", 0) + 1
stats["project_types"]["Node.js"] = stats["project_types"].get("Node.js", 0) + 1
elif (item / "requirements.txt").exists() or (item / "pyproject.toml").exists():
stats["languages"]["Python"] = stats["languages"].get("Python", 0) + 1
stats["project_types"]["Python"] = stats["project_types"].get("Python", 0) + 1
elif (item / "pom.xml").exists():
stats["languages"]["Java"] = stats["languages"].get("Java", 0) + 1
stats["project_types"]["Java (Maven)"] = stats["project_types"].get("Java (Maven)", 0) + 1
else:
stats["languages"]["Unknown"] = stats["languages"].get("Unknown", 0) + 1
stats["project_types"]["Unknown"] = stats["project_types"].get("Unknown", 0) + 1
# 生成分析报告
report = f"""📊 **工作区分析报告**
📈 **总体统计**
• 项目总数:{stats['total_projects']}
• 占用空间:{round(stats['total_size_mb'], 2)} MB
• 平均项目大小:{round(stats['total_size_mb'] / max(stats['total_projects'], 1), 2)} MB
📝 **语言分布**
"""
for lang, count in sorted(stats["languages"].items(), key=lambda x: x[1], reverse=True):
percentage = round(count / stats["total_projects"] * 100, 1)
report += f"• {lang}:{count} 项目 ({percentage}%)\n"
report += f"""
🏗️ **项目类型分布**
"""
for ptype, count in sorted(stats["project_types"].items(), key=lambda x: x[1], reverse=True):
percentage = round(count / stats["total_projects"] * 100, 1)
report += f"• {ptype}:{count} 项目 ({percentage}%)\n"
if stats["largest_project"]:
report += f"""
🏆 **最大项目**:{stats['largest_project']['name']} ({stats['largest_project']['size_mb']} MB)"""
if stats["newest_project"]:
report += f"""
🆕 **最新项目**:{stats['newest_project']}"""
return [TextContent(type="text", text=report)]
except Exception as e:
return [TextContent(
type="text",
text=f"❌ 工作区分析失败:{str(e)}"
)]
async def run(self):
"""启动MCP服务器"""
print(f"🚀 GitHub Clone MCP服务器启动")
print(f"📁 工作区目录:{self.workspace_root}")
print(f"🔧 可用工具:intelligent_clone_repository, smart_open_in_vscode, manage_workspace")
await self.server.run()
# 主程序入口
async def main():
server = GitHubCloneMCPServer()
await server.run()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n👋 MCP服务器已停止")
except Exception as e:
print(f"❌ 服务器启动失败:{e}")
第三步:GitHub Copilot Agent Mode集成
现在让我们配置GitHub Copilot使用我们的自定义MCP服务器:
1. VS Code配置文件设置
在项目根目录创建 .vscode/settings.json
:
{
"github.copilot.chat.experimentalMcp": {
"mcpServers": {
"github-clone-assistant": {
"command": "python",
"args": ["github_clone_mcp_server.py"],
"description": "GitHub仓库智能克隆和VS Code集成助手",
"enabled": true
}
}
},
"python.defaultInterpreterPath": "./venv/bin/python"
}
2. Agent Builder配置
在AI Toolkit的Agent Builder中:
创建新代理:
- 名称:
GitHubDeveloperAssistant
- 模型:
Claude 3.5 Sonnet
- 描述:专业的GitHub开发工作流自动化助手
- 名称:
添加MCP服务器:
- 服务器类型:自定义MCP服务器
- 服务器路径:
./github_clone_mcp_server.py
- 传输方式:stdio
配置提示词模板:
你是一个专业的GitHub开发工作流助手,具备以下核心能力:
## 🎯 核心功能
- **智能仓库克隆**:快速克隆GitHub仓库并自动在VS Code中打开
- **项目分析**:深度分析项目结构、语言、框架和依赖
- **工作区管理**:组织和管理本地开发项目
- **开发建议**:基于项目特征提供个性化的开发建议
## 🔧 可用工具
1. `intelligent_clone_repository` - 智能克隆GitHub仓库
2. `smart_open_in_vscode` - 跨平台VS Code启动器
3. `manage_workspace` - 工作区项目管理
## 💡 使用示例
- "帮我克隆 https://github.com/microsoft/vscode 这个仓库"
- "列出我的所有项目"
- "分析我的工作区统计信息"
- "在VS Code中打开项目 /path/to/project"
始终提供详细、准确和有用的响应,并在适当时候给出开发建议。
第四步:实际使用和测试
在GitHub Copilot Chat中使用
打开GitHub Copilot Chat,尝试以下对话:
👤 User: 帮我克隆这个仓库:https://github.com/microsoft/vscode-ai-toolkit
🤖 Assistant: 我来帮你克隆Microsoft的VS Code AI Toolkit仓库!
[调用 intelligent_clone_repository 工具]
{
"repo_url": "https://github.com/microsoft/vscode-ai-toolkit",
"open_in_vscode": true,
"shallow_clone": true
}
🎉 GitHub仓库克隆成功!
📦 仓库信息
• 仓库:microsoft/vscode-ai-toolkit
• 本地路径:/Users/yourname/mcp-workspace/vscode-ai-toolkit
• 描述:AI Toolkit for Visual Studio Code
• 主要语言:TypeScript
• ⭐ Stars:1,245 | 🔄 Forks:156
🔍 项目分析
• 项目类型:TypeScript项目
• 主要语言:JavaScript/TypeScript
• 包管理器:npm
• 框架/工具:Webpack, ESLint
• 文件数量:847
• 项目大小:12.5 MB
📋 项目特征
✅ 包含README文档
✅ 包含开源许可证
✅ 包含测试代码
✅ 包含项目文档
💡 开发建议
• 运行 `npm install` 安装依赖
• 查看 package.json 中的 scripts 了解可用命令
• 🚀 已在VS Code中打开项目!
高级使用场景
👤 User: 我想要一个专门的工作区来存放AI相关的项目,帮我克隆几个知名的AI项目
🤖 Assistant: 很好的想法!我来帮你创建一个AI项目专用工作区,并克隆一些优秀的AI项目。
[依次调用 intelligent_clone_repository 工具克隆多个仓库]
1. Transformers (Hugging Face)
2. LangChain
3. OpenAI Cookbook
4. Stable Diffusion WebUI
所有项目已成功克隆到你的工作区!每个项目都已经过智能分析,你可以立即开始开发。需要我打开特定的项目吗?
🧪 测试和验证
功能测试清单
创建测试脚本 test_github_mcp_server.py
:
#!/usr/bin/env python3
"""
GitHub Clone MCP Server 测试套件
"""
import asyncio
import json
import tempfile
from pathlib import Path
from github_clone_mcp_server import GitHubCloneMCPServer
class MCPServerTester:
def __init__(self):
self.server = GitHubCloneMCPServer()
self.test_results = []
async def run_all_tests(self):
"""运行所有测试用例"""
print("🧪 开始MCP服务器功能测试...\n")
tests = [
("URL验证测试", self.test_url_validation),
("仓库克隆测试", self.test_repository_cloning),
("项目分析测试", self.test_project_analysis),
("工作区管理测试", self.test_workspace_management),
("VS Code集成测试", self.test_vscode_integration)
]
for test_name, test_func in tests:
print(f"🔍 {test_name}...")
try:
result = await test_func()
self.test_results.append((test_name, "✅", result))
print(f" ✅ {result}\n")
except Exception as e:
self.test_results.append((test_name, "❌", str(e)))
print(f" ❌ {str(e)}\n")
# 输出测试总结
self.print_test_summary()
async def test_url_validation(self):
"""测试URL验证功能"""
test_urls = [
("https://github.com/microsoft/vscode", True),
("https://github.com/user/repo.git", True),
("https://gitlab.com/user/repo", False),
("invalid-url", False)
]
for url, expected_valid in test_urls:
result = await self.server.validate_and_parse_repo_url(url)
if result["valid"] != expected_valid:
raise AssertionError(f"URL验证失败:{url}")
return "所有URL验证测试通过"
async def test_repository_cloning(self):
"""测试仓库克隆功能(使用小型测试仓库)"""
# 使用临时目录
with tempfile.TemporaryDirectory() as temp_dir:
self.server.workspace_root = Path(temp_dir)
# 克隆一个小型公开仓库
result = await self.server.intelligent_clone_repository({
"repo_url": "https://github.com/octocat/Hello-World",
"open_in_vscode": False,
"shallow_clone": True
})
if not result or "成功" not in result[0].text:
raise AssertionError("仓库克隆失败")
return "仓库克隆功能正常"
async def test_project_analysis(self):
"""测试项目分析功能"""
# 创建模拟项目结构
with tempfile.TemporaryDirectory() as temp_dir:
test_project = Path(temp_dir) / "test-project"
test_project.mkdir()
# 创建package.json
(test_project / "package.json").write_text(json.dumps({
"name": "test-project",
"dependencies": {"react": "^18.0.0"}
}))
# 创建其他文件
(test_project / "README.md").write_text("# Test Project")
(test_project / "LICENSE").write_text("MIT License")
analysis = await self.server.analyze_cloned_project(test_project)
if analysis["language"] != "JavaScript/TypeScript":
raise AssertionError("项目语言检测错误")
if "React" not in analysis["frameworks"]:
raise AssertionError("框架检测失败")
return "项目分析功能正常"
async def test_workspace_management(self):
"""测试工作区管理功能"""
with tempfile.TemporaryDirectory() as temp_dir:
self.server.workspace_root = Path(temp_dir)
# 创建测试项目
(Path(temp_dir) / "project1").mkdir()
(Path(temp_dir) / "project2").mkdir()
result = await self.server.manage_workspace({"action": "list"})
if not result or "project1" not in result[0].text:
raise AssertionError("工作区列表功能失败")
return "工作区管理功能正常"
async def test_vscode_integration(self):
"""测试VS Code集成功能"""
with tempfile.TemporaryDirectory() as temp_dir:
result = await self.server.smart_open_in_vscode({
"project_path": temp_dir
})
# 检查是否返回了合理的响应(可能成功或失败都是正常的)
if not result:
raise AssertionError("VS Code集成功能无响应")
return "VS Code集成功能响应正常"
def print_test_summary(self):
"""打印测试总结"""
passed = sum(1 for _, status, _ in self.test_results if status == "✅")
total = len(self.test_results)
print("=" * 60)
print(f"📊 测试总结:{passed}/{total} 通过")
print("=" * 60)
for test_name, status, result in self.test_results:
print(f"{status} {test_name}: {result}")
print(f"\n🎯 整体测试成功率:{passed/total*100:.1f}%")
async def main():
tester = MCPServerTester()
await tester.run_all_tests()
if __name__ == "__main__":
asyncio.run(main())
🎖️ 学习成果验证
💪 技能掌握检查清单
完成这个实验后,检验你是否掌握了以下技能:
🎯 基础技能 (必需掌握)
- [ ] 能够独立设计和实现自定义MCP服务器
- [ ] 理解并正确使用MCP Python SDK的最新API
- [ ] 实现跨平台兼容的系统集成功能
- [ ] 编写完善的错误处理和用户反馈机制
🚀 进阶技能 (熟练掌握)
- [ ] 能够集成GitHub API进行仓库验证
- [ ] 实现智能项目分析和自动化建议
- [ ] 构建生产级的异步处理流程
- [ ] 在GitHub Copilot中流畅使用自定义工具
🏆 专家技能 (精通掌握)
- [ ] 能够优化MCP服务器的性能和稳定性
- [ ] 设计可扩展的工具架构和插件系统
- [ ] 实现企业级的安全性和权限控制
- [ ] 构建完整的测试和监控体系
🎯 挑战任务
选择以下挑战来进一步提升你的技能:
🥉 初级挑战:功能扩展
为GitHub Clone服务器添加以下功能:
- 支持克隆指定的Git分支或标签
- 添加克隆进度显示
- 实现项目模板应用功能
🥈 中级挑战:工作流集成
构建一个完整的开发工作流自动化:
- 自动设置开发环境(安装依赖、配置工具)
- 集成代码质量检查工具
- 实现自动化的项目文档生成
🥇 高级挑战:企业级扩展
开发企业级的GitHub管理平台:
- 支持多Git平台(GitHub、GitLab、Bitbucket)
- 实现团队项目管理和权限控制
- 构建项目分析和报告仪表板
📚 深入学习资源
🔗 相关文档和教程
🎥 推荐视频教程
- "构建自定义MCP服务器" - MCP官方教程系列
- "GitHub API最佳实践" - GitHub开发者大会
- "Python异步编程深度解析" - PyCon技术分享
🌐 社区和支持
- MCP开发者社区:Discord
- GitHub讨论区:MCP Discussions
- Stack Overflow:标签
model-context-protocol
🎉 实验总结
恭喜你完成了MCP学习之旅的终极实验!🏆
通过这个实验,你不仅学会了如何构建生产级的MCP服务器,更重要的是掌握了AI原生开发的核心思维模式。你现在拥有的能力包括:
🌟 核心成就
- 架构设计能力:能够设计可扩展、可维护的MCP服务器架构
- 工程实践经验:掌握了错误处理、测试、文档等工程最佳实践
- AI集成专家:深度理解AI工具与传统开发工具的无缝集成
- 跨平台开发技能:编写兼容多种操作系统的高质量代码
- 用户体验意识:始终从用户角度思考工具的易用性和实用性
🚀 下一步建议
- 实际部署:将你的MCP服务器部署到真实的开发环境中
- 持续改进:根据使用反馈不断优化和扩展功能
- 社区贡献:将你的经验分享给MCP开发者社区
- 技术分享:撰写技术博客或制作教程视频
💡 最后的话
记住,最好的工具是那些能够真正解决问题、提高效率的工具。你构建的GitHub Clone MCP服务器虽然看起来简单,但它体现了优秀软件的核心特质:简单、可靠、实用。
现在,拿着你的"神兵利器",去征服更广阔的AI开发世界吧!🌟✨
🎯 准备好成为MCP生态系统的贡献者了吗?
你的下一个MCP服务器将改变什么样的开发工作流呢?我们期待看到你的创新!💫