第28章 MCP开发常见问题排查

62 阅读7分钟

第28章 MCP开发常见问题排查

28.1 连接问题诊断(扩展版)

28.1.1 无法连接到MCP服务器

症状:客户端无法建立与MCP服务器的连接,超时或直接拒绝

排查步骤

graph TD
    A["连接失败"] --> B["检查服务器是否运行"]
    B -->|服务器未启动| C["启动服务器"]
    B -->|服务器已启动| D["检查网络配置"]
    
    D --> E["检查防火墙规则"]
    D --> F["检查端口监听"]
    
    E -->|防火墙阻止| G["配置防火墙规则"]
    E -->|防火墙允许| H["检查地址绑定"]
    
    F -->|端口未监听| I["检查服务器日志"]
    F -->|端口已监听| J["检查认证信息"]
    
    style A fill:#ff6b6b
    style C fill:#51cf66
    style G fill:#51cf66
    style I fill:#ffd43b

常见原因与解决方案

原因症状解决方案预防措施
服务器未启动连接被拒绝systemctl start mcp-server配置开机自启
端口被占用绑定端口失败lsof -i :8000 找到进程并kill使用动态端口分配
防火墙阻止连接超时sudo ufw allow 8000使用虚拟网络/VPN
地址绑定错误仅本地可访问改为 0.0.0.0:8000使用配置文件管理
DNS解析失败无法找到主机检查 /etc/hosts使用IP而非域名
认证失败拒绝连接检查API Key/证书自动化凭证管理
SSL/TLS错误TLS握手失败验证证书有效期自动化证书更新

高级诊断代码

"""
高级连接诊断与恢复
"""
import asyncio
import socket
import ssl
from typing import Dict, Any, Optional, Tuple, List
import logging
from datetime import datetime

logger = logging.getLogger(__name__)

class AdvancedConnectionDiagnostics:
    """高级连接诊断工具"""
    
    def __init__(self, timeout: int = 10):
        self.timeout = timeout
        self.connection_history: List[Dict] = []
    
    async def comprehensive_diagnostics(self, host: str, port: int, 
                                       use_ssl: bool = False) -> Dict[str, Any]:
        """完整的诊断流程"""
        report = {
            "host": host,
            "port": port,
            "timestamp": datetime.now().isoformat(),
            "checks": {},
            "recommendations": []
        }
        
        # 1. DNS检查
        dns_ok, dns_ip = await self._check_dns_async(host)
        report["checks"]["dns"] = {
            "status": "✓ Pass" if dns_ok else "✗ Fail",
            "detail": dns_ip,
            "latency_ms": 0
        }
        
        if not dns_ok:
            report["recommendations"].append("检查DNS配置或主机名是否正确")
            return report
        
        # 2. 端口开放性检查
        port_open, response_time = await self._check_port_async(host, port)
        report["checks"]["port"] = {
            "status": "✓ Open" if port_open else "✗ Closed",
            "latency_ms": response_time * 1000
        }
        
        if not port_open:
            report["recommendations"].append(f"端口{port}关闭,检查防火墙或服务是否正常运行")
            return report
        
        # 3. TLS/SSL检查(如果需要)
        if use_ssl:
            ssl_ok, ssl_error = await self._check_ssl_async(host, port)
            report["checks"]["ssl"] = {
                "status": "✓ Valid" if ssl_ok else "✗ Invalid",
                "detail": ssl_error or "SSL握手成功"
            }
            
            if not ssl_ok:
                report["recommendations"].append(f"SSL/TLS错误: {ssl_error}")
                return report
        
        # 4. 连接建立测试
        conn_ok, conn_error = await self._test_connection_async(host, port, use_ssl)
        report["checks"]["connection"] = {
            "status": "✓ Success" if conn_ok else "✗ Failed",
            "detail": conn_error or "连接建立成功"
        }
        
        # 5. 协议检查
        proto_ok, proto_info = await self._check_protocol_async(host, port)
        report["checks"]["protocol"] = {
            "status": "✓ Valid" if proto_ok else "✗ Invalid",
            "detail": proto_info
        }
        
        # 6. 认证检查
        auth_ok, auth_error = await self._check_authentication_async(host, port)
        report["checks"]["authentication"] = {
            "status": "✓ OK" if auth_ok else "✗ Failed",
            "detail": auth_error or "认证成功"
        }
        
        if not auth_ok:
            report["recommendations"].append(f"认证问题: {auth_error}")
        
        # 7. 性能指标
        report["performance"] = await self._measure_performance(host, port)
        
        return report
    
    async def _check_dns_async(self, hostname: str) -> Tuple[bool, str]:
        """异步DNS检查"""
        try:
            loop = asyncio.get_event_loop()
            ip = await asyncio.wait_for(
                loop.getaddrinfo(hostname, None),
                timeout=self.timeout
            )
            return True, ip[0][4][0]
        except Exception as e:
            return False, str(e)
    
    async def _check_port_async(self, host: str, port: int) -> Tuple[bool, float]:
        """异步端口检查"""
        import time
        start_time = time.time()
        try:
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port),
                timeout=self.timeout
            )
            elapsed = time.time() - start_time
            writer.close()
            await writer.wait_closed()
            return True, elapsed
        except Exception:
            return False, 0.0
    
    async def _check_ssl_async(self, host: str, port: int) -> Tuple[bool, Optional[str]]:
        """异步SSL/TLS检查"""
        try:
            context = ssl.create_default_context()
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port, ssl=context),
                timeout=self.timeout
            )
            writer.close()
            await writer.wait_closed()
            return True, None
        except ssl.SSLError as e:
            return False, f"SSL Error: {str(e)}"
        except Exception as e:
            return False, str(e)
    
    async def _test_connection_async(self, host: str, port: int, 
                                     use_ssl: bool) -> Tuple[bool, Optional[str]]:
        """测试实际连接"""
        try:
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port, ssl=use_ssl),
                timeout=self.timeout
            )
            # 尝试发送简单的HTTP请求
            writer.write(b"GET / HTTP/1.0\r\n\r\n")
            await writer.drain()
            
            response = await asyncio.wait_for(
                reader.read(1024),
                timeout=2
            )
            
            writer.close()
            await writer.wait_closed()
            
            return True, None
        except asyncio.TimeoutError:
            return False, "连接超时"
        except Exception as e:
            return False, str(e)
    
    async def _check_protocol_async(self, host: str, port: int) -> Tuple[bool, str]:
        """检查协议"""
        # 实际应该根据MCP协议检查
        try:
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port),
                timeout=self.timeout
            )
            writer.close()
            await writer.wait_closed()
            return True, "MCP protocol responsive"
        except Exception as e:
            return False, str(e)
    
    async def _check_authentication_async(self, host: str, port: int) -> Tuple[bool, Optional[str]]:
        """检查认证"""
        # 实际应该根据MCP认证机制检查
        try:
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port),
                timeout=self.timeout
            )
            writer.close()
            await writer.wait_closed()
            return True, None
        except Exception as e:
            return False, str(e)
    
    async def _measure_performance(self, host: str, port: int) -> Dict[str, Any]:
        """性能测量"""
        latencies = []
        for _ in range(5):
            try:
                start = asyncio.get_event_loop().time()
                reader, writer = await asyncio.wait_for(
                    asyncio.open_connection(host, port),
                    timeout=self.timeout
                )
                elapsed = asyncio.get_event_loop().time() - start
                latencies.append(elapsed * 1000)
                writer.close()
                await writer.wait_closed()
            except:
                pass
        
        if latencies:
            return {
                "min_ms": min(latencies),
                "max_ms": max(latencies),
                "avg_ms": sum(latencies) / len(latencies),
                "samples": len(latencies)
            }
        return {}

# 使用示例
async def main():
    diag = AdvancedConnectionDiagnostics()
    result = await diag.comprehensive_diagnostics("localhost", 8000, use_ssl=False)
    print(f"诊断结果: {result}")

asyncio.run(main())

28.1.2 连接不稳定,频繁断开(扩展)

实战案例:WireShark抓包分析

使用WireShark分析连接问题:

1. 启动抓包
   $ sudo wireshark

2. 过滤规则
   tcp.port == 8000 && (tcp.flags.syn==1 || tcp.flags.fin==1 || tcp.flags.rst==1)

3. 分析TCP状态图
   正常流程:SYN -> SYN-ACK -> ACK -> DATA -> FIN
   
   异常情况1:RST包
   -> 表示服务器强制关闭连接(可能原因:服务崩溃、超时、权限错误)
   
   异常情况2:多次重传
   -> 表示网络丢包或拥塞
   
   异常情况3:超长ACK时间
   -> 表示网络延迟高

自适应重连策略

"""
自适应重连与熔断机制
"""
import asyncio
import random
from dataclasses import dataclass
from typing import Callable, Any

@dataclass
class CircuitBreakerConfig:
    """熔断器配置"""
    failure_threshold: int = 5  # 失败次数阈值
    recovery_timeout: int = 60  # 恢复超时(秒)
    half_open_max_calls: int = 3  # 半开状态最多请求数

class AdaptiveConnectionManager:
    """自适应连接管理器"""
    
    def __init__(self, config: CircuitBreakerConfig):
        self.config = config
        self.failure_count = 0
        self.state = "closed"  # closed, open, half_open
        self.last_failure_time = None
    
    async def execute_with_circuit_breaker(self, 
                                          operation: Callable,
                                          *args, **kwargs) -> Any:
        """带熔断器的操作执行"""
        
        # 检查熔断器状态
        if self.state == "open":
            if self._should_attempt_recovery():
                self.state = "half_open"
                self.failure_count = 0
                logger.info("Circuit breaker transitioning to half_open state")
            else:
                raise Exception(f"Circuit breaker is open, retry after {self._get_retry_delay()}s")
        
        try:
            result = await operation(*args, **kwargs)
            
            # 操作成功,重置状态
            if self.state == "half_open":
                self.state = "closed"
                self.failure_count = 0
                logger.info("Circuit breaker closed after successful operation")
            
            return result
        
        except Exception as e:
            self.failure_count += 1
            self.last_failure_time = asyncio.get_event_loop().time()
            
            if self.failure_count >= self.config.failure_threshold:
                self.state = "open"
                logger.error(f"Circuit breaker opened after {self.failure_count} failures")
            
            raise
    
    def _should_attempt_recovery(self) -> bool:
        """判断是否应该尝试恢复"""
        if not self.last_failure_time:
            return False
        
        elapsed = asyncio.get_event_loop().time() - self.last_failure_time
        return elapsed >= self.config.recovery_timeout
    
    def _get_retry_delay(self) -> int:
        """获取重试延迟"""
        elapsed = asyncio.get_event_loop().time() - (self.last_failure_time or 0)
        return max(0, self.config.recovery_timeout - int(elapsed))

# 使用示例
async def unstable_operation():
    """可能不稳定的操作"""
    if random.random() < 0.3:
        raise Exception("Random failure")
    return "Success"

async def main():
    config = CircuitBreakerConfig()
    manager = AdaptiveConnectionManager(config)
    
    for i in range(20):
        try:
            result = await manager.execute_with_circuit_breaker(unstable_operation)
            print(f"Call {i+1}: {result}")
        except Exception as e:
            print(f"Call {i+1}: Failed - {e}")
            await asyncio.sleep(1)

28.2 工具调用失败排查(扩展版)

28.2.1 工具不可用或未找到

高级诊断与自修复

"""
工具自诊断与自修复系统
"""
import hashlib
from typing import Dict, List, Any
from datetime import datetime

class ToolDiagnosticsEngine:
    """工具诊断引擎"""
    
    def __init__(self, mcp_server):
        self.mcp_server = mcp_server
        self.tool_health_cache: Dict[str, Dict] = {}
        self.diagnostic_history: List[Dict] = []
    
    def comprehensive_tool_diagnostics(self, tool_name: str) -> Dict[str, Any]:
        """对工具进行全面诊断"""
        
        diagnostic = {
            "tool_name": tool_name,
            "timestamp": datetime.now().isoformat(),
            "status": "unknown",
            "checks": [],
            "issues": [],
            "recommendations": []
        }
        
        # 1. 注册检查
        check1 = self._check_registration(tool_name)
        diagnostic["checks"].append(check1)
        
        if not check1["passed"]:
            diagnostic["status"] = "not_registered"
            diagnostic["issues"].append("工具未注册")
            diagnostic["recommendations"].append("1. 检查工具注册代码 2. 查看初始化日志")
            return diagnostic
        
        # 2. Schema检查
        check2 = self._check_schema(tool_name)
        diagnostic["checks"].append(check2)
        
        if not check2["passed"]:
            diagnostic["issues"].append("Schema定义错误")
        
        # 3. 权限检查
        check3 = self._check_permissions(tool_name)
        diagnostic["checks"].append(check3)
        
        if not check3["passed"]:
            diagnostic["issues"].append("权限配置错误")
        
        # 4. 依赖检查
        check4 = self._check_dependencies(tool_name)
        diagnostic["checks"].append(check4)
        
        if not check4["passed"]:
            diagnostic["issues"].append("依赖项缺失或不可用")
        
        # 5. 性能检查
        check5 = self._check_performance(tool_name)
        diagnostic["checks"].append(check5)
        
        if check5["latency_ms"] > 5000:
            diagnostic["issues"].append("工具响应缓慢")
        
        # 确定整体状态
        if all(c["passed"] for c in diagnostic["checks"]):
            diagnostic["status"] = "healthy"
        elif any(not c["passed"] for c in diagnostic["checks"][:3]):
            diagnostic["status"] = "critical"
        else:
            diagnostic["status"] = "warning"
        
        # 记录诊断结果
        self.diagnostic_history.append(diagnostic)
        self.tool_health_cache[tool_name] = diagnostic
        
        return diagnostic
    
    def _check_registration(self, tool_name: str) -> Dict:
        """检查工具是否正确注册"""
        try:
            tool_list = self.mcp_server.list_tools()
            found = any(t.get("name") == tool_name for t in tool_list)
            
            return {
                "name": "registration",
                "passed": found,
                "details": f"找到工具" if found else f"未找到工具 {tool_name}"
            }
        except Exception as e:
            return {
                "name": "registration",
                "passed": False,
                "details": str(e)
            }
    
    def _check_schema(self, tool_name: str) -> Dict:
        """检查Schema定义"""
        try:
            tool = self.mcp_server.get_tool(tool_name)
            
            # 验证必需字段
            required_fields = ["name", "description", "inputSchema", "outputSchema"]
            missing = [f for f in required_fields if f not in tool]
            
            passed = len(missing) == 0
            
            return {
                "name": "schema",
                "passed": passed,
                "details": f"缺失字段: {missing}" if missing else "Schema完整"
            }
        except Exception as e:
            return {
                "name": "schema",
                "passed": False,
                "details": str(e)
            }
    
    def _check_permissions(self, tool_name: str) -> Dict:
        """检查权限配置"""
        try:
            tool = self.mcp_server.get_tool(tool_name)
            access_level = tool.get("access_level", "public")
            
            # 检查权限是否合理
            valid_levels = ["public", "internal", "restricted", "confidential"]
            passed = access_level in valid_levels
            
            return {
                "name": "permissions",
                "passed": passed,
                "details": f"权限等级: {access_level}"
            }
        except Exception as e:
            return {
                "name": "permissions",
                "passed": False,
                "details": str(e)
            }
    
    def _check_dependencies(self, tool_name: str) -> Dict:
        """检查工具依赖"""
        try:
            tool = self.mcp_server.get_tool(tool_name)
            dependencies = tool.get("dependencies", [])
            
            # 检查依赖是否可用
            all_tools = self.mcp_server.list_tools()
            available_tools = [t.get("name") for t in all_tools]
            
            missing_deps = [d for d in dependencies if d not in available_tools]
            passed = len(missing_deps) == 0
            
            return {
                "name": "dependencies",
                "passed": passed,
                "details": f"缺失依赖: {missing_deps}" if missing_deps else "所有依赖可用"
            }
        except Exception as e:
            return {
                "name": "dependencies",
                "passed": False,
                "details": str(e)
            }
    
    def _check_performance(self, tool_name: str) -> Dict:
        """检查性能"""
        import time
        
        try:
            start_time = time.time()
            # 执行工具(使用最小参数)
            result = self.mcp_server.execute_tool(tool_name, "test_user", {})
            elapsed = (time.time() - start_time) * 1000  # 转换为毫秒
            
            threshold = 5000  # 5秒
            passed = elapsed < threshold
            
            return {
                "name": "performance",
                "passed": passed,
                "latency_ms": elapsed,
                "details": f"响应时间: {elapsed:.0f}ms"
            }
        except Exception as e:
            return {
                "name": "performance",
                "passed": False,
                "latency_ms": 0,
                "details": str(e)
            }

# 使用示例
if __name__ == "__main__":
    engine = ToolDiagnosticsEngine(mcp_server)
    
    # 诊断特定工具
    result = engine.comprehensive_tool_diagnostics("query_customer")
    
    print(f"工具状态: {result['status']}")
    print(f"发现问题: {result['issues']}")
    print(f"建议: {result['recommendations']}")

28.3 资源访问问题(扩展)

大文件处理与流式传输

"""
大文件资源的流式读取与分块处理
"""
import asyncio
from typing import AsyncIterator
import logging

logger = logging.getLogger(__name__)

class StreamingResourceManager:
    """流式资源管理器"""
    
    def __init__(self, chunk_size: int = 1024 * 1024):  # 1MB chunks
        self.chunk_size = chunk_size
    
    async def stream_large_resource(self, resource_id: str) -> AsyncIterator[bytes]:
        """以流的方式读取大资源"""
        try:
            # 获取资源大小
            size = await self._get_resource_size(resource_id)
            logger.info(f"开始流式读取资源 {resource_id}, 大小: {size} bytes")
            
            # 分块读取
            offset = 0
            while offset < size:
                chunk = await self._read_resource_chunk(
                    resource_id, 
                    offset, 
                    self.chunk_size
                )
                
                if not chunk:
                    break
                
                yield chunk
                offset += len(chunk)
                
                # 定期报告进度
                progress_pct = (offset / size) * 100
                logger.debug(f"读取进度: {progress_pct:.1f}%")
        
        except Exception as e:
            logger.error(f"流式读取失败: {e}")
            raise
    
    async def _get_resource_size(self, resource_id: str) -> int:
        """获取资源大小"""
        # 实际实现应该调用MCP获取资源元数据
        return 10 * 1024 * 1024  # 假设10MB
    
    async def _read_resource_chunk(self, resource_id: str, 
                                   offset: int, size: int) -> bytes:
        """读取资源块"""
        # 实际实现应该调用MCP接口
        return b"chunk_data"
    
    async def upload_large_resource(self, resource_id: str, 
                                   file_path: str) -> bool:
        """上传大文件资源"""
        try:
            import os
            file_size = os.path.getsize(file_path)
            logger.info(f"开始上传资源 {resource_id}, 大小: {file_size} bytes")
            
            with open(file_path, 'rb') as f:
                offset = 0
                while True:
                    chunk = f.read(self.chunk_size)
                    if not chunk:
                        break
                    
                    await self._upload_chunk(resource_id, offset, chunk)
                    offset += len(chunk)
                    
                    progress_pct = (offset / file_size) * 100
                    logger.debug(f"上传进度: {progress_pct:.1f}%")
            
            logger.info(f"资源 {resource_id} 上传完成")
            return True
        
        except Exception as e:
            logger.error(f"上传失败: {e}")
            return False
    
    async def _upload_chunk(self, resource_id: str, offset: int, chunk: bytes):
        """上传资源块"""
        # 实际实现应该调用MCP接口
        pass

28.4 性能问题优化(扩展)

深度性能分析与优化建议

"""
自动化性能分析与优化推荐系统
"""
from collections import deque
import statistics
import logging
from typing import Dict, List, Any
from datetime import datetime

logger = logging.getLogger(__name__)

class PerformanceAdvisor:
    """性能顾问系统"""
    
    def __init__(self, sample_window: int = 100):
        self.sample_window = sample_window
        self.metrics_history: Dict[str, deque] = {}
    
    def record_operation(self, tool_name: str, duration_ms: float, 
                        success: bool = True):
        """记录操作"""
        if tool_name not in self.metrics_history:
            self.metrics_history[tool_name] = deque(maxlen=self.sample_window)
        
        self.metrics_history[tool_name].append({
            "duration": duration_ms,
            "success": success,
            "timestamp": datetime.now()
        })
    
    def get_performance_insights(self, tool_name: str) -> Dict[str, Any]:
        """获取性能洞察"""
        if tool_name not in self.metrics_history or not self.metrics_history[tool_name]:
            return {"status": "no_data"}
        
        metrics = list(self.metrics_history[tool_name])
        durations = [m["duration"] for m in metrics]
        
        insights = {
            "tool": tool_name,
            "sample_count": len(metrics),
            "success_rate": sum(1 for m in metrics if m["success"]) / len(metrics),
            "statistics": {
                "min": min(durations),
                "max": max(durations),
                "mean": statistics.mean(durations),
                "median": statistics.median(durations),
                "stdev": statistics.stdev(durations) if len(durations) > 1 else 0,
                "p95": sorted(durations)[int(len(durations) * 0.95)],
                "p99": sorted(durations)[int(len(durations) * 0.99)]
            }
        }
        
        # 分析趋势
        trends = self._analyze_trends(durations)
        insights["trends"] = trends
        
        # 生成建议
        recommendations = self._generate_recommendations(insights)
        insights["recommendations"] = recommendations
        
        return insights
    
    def _analyze_trends(self, durations: List[float]) -> Dict[str, Any]:
        """分析趋势"""
        if len(durations) < 10:
            return {"status": "insufficient_data"}
        
        recent = durations[-10:]
        older = durations[-20:-10]
        
        recent_avg = statistics.mean(recent)
        older_avg = statistics.mean(older)
        
        trend = "improving" if recent_avg < older_avg else "degrading"
        change_pct = abs((recent_avg - older_avg) / older_avg * 100)
        
        return {
            "trend": trend,
            "recent_avg_ms": recent_avg,
            "older_avg_ms": older_avg,
            "change_pct": change_pct
        }
    
    def _generate_recommendations(self, insights: Dict) -> List[str]:
        """生成优化建议"""
        recommendations = []
        stats = insights.get("statistics", {})
        
        # 基于P95的建议
        p95 = stats.get("p95", 0)
        if p95 > 5000:
            recommendations.append("P95响应时间 > 5s,建议进行深度性能分析")
            recommendations.append("- 检查数据库查询是否有N+1问题")
            recommendations.append("- 考虑添加缓存层")
            recommendations.append("- 分析网络延迟")
        
        # 基于P99的建议
        p99 = stats.get("p99", 0)
        if p99 > p95 * 2:
            recommendations.append(f"P99显著高于P95,存在离群值或极端情况")
            recommendations.append("- 查看最慢的请求日志")
            recommendations.append("- 检查是否有GC暂停或其他系统级事件")
        
        # 基于方差的建议
        stdev = stats.get("stdev", 0)
        if stdev > stats.get("mean", 1) * 0.5:
            recommendations.append("性能波动较大,可能有间歇性问题")
            recommendations.append("- 检查系统负载")
            recommendations.append("- 查看是否有资源竞争")
        
        # 基于成功率的建议
        if insights.get("success_rate", 1) < 0.95:
            recommendations.append(f"失败率 > 5%,需要可靠性改进")
            recommendations.append("- 添加重试逻辑")
            recommendations.append("- 改进错误处理")
        
        return recommendations if recommendations else ["性能表现良好"]

# 使用示例
advisor = PerformanceAdvisor()

# 记录一些操作
import random
for i in range(50):
    advisor.record_operation("query_customer", random.uniform(100, 1000))

# 获取洞察
insights = advisor.get_performance_insights("query_customer")
print(f"性能建议: {insights['recommendations']}")

28.5 安全问题处理(扩展)

漏洞检测与自动化修复

"""
自动化安全检测与修复系统
"""
import re
from enum import Enum
import logging
from typing import Dict, List

logger = logging.getLogger(__name__)

class VulnerabilityType(Enum):
    """漏洞类型"""
    SQL_INJECTION = "sql_injection"
    COMMAND_INJECTION = "command_injection"
    XSS = "xss"
    PATH_TRAVERSAL = "path_traversal"
    PRIVILEGE_ESCALATION = "privilege_escalation"
    SENSITIVE_DATA_EXPOSURE = "sensitive_data_exposure"

class SecurityScanner:
    """安全扫描器"""
    
    def scan_tool_inputs(self, tool_name: str, params: Dict) -> List[Dict]:
        """扫描工具输入是否存在安全问题"""
        
        vulnerabilities = []
        
        # 检查SQL注入
        sql_vulns = self._check_sql_injection(params)
        vulnerabilities.extend(sql_vulns)
        
        # 检查命令注入
        cmd_vulns = self._check_command_injection(params)
        vulnerabilities.extend(cmd_vulns)
        
        # 检查路径遍历
        path_vulns = self._check_path_traversal(params)
        vulnerabilities.extend(path_vulns)
        
        # 检查敏感数据
        data_vulns = self._check_sensitive_data(params)
        vulnerabilities.extend(data_vulns)
        
        return vulnerabilities
    
    def _check_sql_injection(self, params: Dict) -> List[Dict]:
        """检查SQL注入"""
        vulns = []
        
        sql_patterns = [
            r"('|\")\s*OR\s*('|\")",
            r"DROP\s+TABLE",
            r"DELETE\s+FROM",
            r"UNION\s+SELECT",
            r";\s*--"
        ]
        
        for key, value in params.items():
            if isinstance(value, str):
                for pattern in sql_patterns:
                    if re.search(pattern, value, re.IGNORECASE):
                        vulns.append({
                            "type": VulnerabilityType.SQL_INJECTION,
                            "parameter": key,
                            "pattern": pattern,
                            "severity": "HIGH"
                        })
        
        return vulns
    
    def _check_command_injection(self, params: Dict) -> List[Dict]:
        """检查命令注入"""
        vulns = []
        
        cmd_patterns = [
            r"[;&|`$()]",  # 命令分隔符
            r"<\(|>\(",    # 进程替换
        ]
        
        dangerous_params = ["cmd", "command", "exec", "shell"]
        
        for key, value in params.items():
            if key.lower() in dangerous_params and isinstance(value, str):
                for pattern in cmd_patterns:
                    if re.search(pattern, value):
                        vulns.append({
                            "type": VulnerabilityType.COMMAND_INJECTION,
                            "parameter": key,
                            "severity": "CRITICAL"
                        })
        
        return vulns
    
    def _check_path_traversal(self, params: Dict) -> List[Dict]:
        """检查路径遍历"""
        vulns = []
        
        path_patterns = [
            r"\.\./",           # 相对路径遍历
            r"\.\.",
            r"%2e%2e",          # URL编码的..
            r"\.\.\\",          # Windows路径
        ]
        
        path_params = ["path", "file", "dir", "directory", "filepath"]
        
        for key, value in params.items():
            if key.lower() in path_params and isinstance(value, str):
                for pattern in path_patterns:
                    if re.search(pattern, value, re.IGNORECASE):
                        vulns.append({
                            "type": VulnerabilityType.PATH_TRAVERSAL,
                            "parameter": key,
                            "severity": "HIGH"
                        })
        
        return vulns
    
    def _check_sensitive_data(self, params: Dict) -> List[Dict]:
        """检查敏感数据"""
        vulns = []
        
        sensitive_patterns = [
            (r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", "email"),
            (r"\b\d{3}-\d{2}-\d{4}\b", "ssn"),
            (r"\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b", "credit_card"),
            (r"\b(?:password|passwd|pwd)\s*[:=]\s*[^\s]+", "password"),
        ]
        
        for key, value in params.items():
            if isinstance(value, str):
                for pattern, data_type in sensitive_patterns:
                    if re.search(pattern, value, re.IGNORECASE):
                        vulns.append({
                            "type": VulnerabilityType.SENSITIVE_DATA_EXPOSURE,
                            "parameter": key,
                            "data_type": data_type,
                            "severity": "MEDIUM"
                        })
        
        return vulns

# 使用示例
scanner = SecurityScanner()

# 检查参数
params = {
    "user_id": "123' OR '1'='1",
    "cmd": "ls -la; cat /etc/passwd"
}

vulns = scanner.scan_tool_inputs("query_user", params)
for vuln in vulns:
    print(f"[{vuln['severity']}] {vuln['type'].value} in {vuln['parameter']}")

28.6 调试技巧与工具(扩展)

交互式调试CLI

"""
MCP交互式调试工具
"""
import cmd
import json
from typing import Dict, Any
import logging

logger = logging.getLogger(__name__)

class MCPDebuggerCLI(cmd.Cmd):
    """MCP调试器命令行界面"""
    
    intro = "欢迎使用 MCP 调试器。输入 help 获取帮助。"
    prompt = "(mcp-debug) "
    
    def __init__(self, mcp_server):
        super().__init__()
        self.mcp_server = mcp_server
        self.last_result = None
        self.variables: Dict[str, Any] = {}
    
    def do_list_tools(self, arg):
        """列出所有可用的工具"""
        tools = self.mcp_server.list_tools()
        for tool in tools:
            print(f"- {tool.get('name')}: {tool.get('description')}")
    
    def do_call_tool(self, arg):
        """调用工具: call_tool <tool_name> [params_json]"""
        parts = arg.split(maxsplit=1)
        if not parts:
            print("用法: call_tool <tool_name> [params_json]")
            return
        
        tool_name = parts[0]
        params_str = parts[1] if len(parts) > 1 else "{}"
        
        try:
            params = json.loads(params_str)
            result = self.mcp_server.execute_tool(tool_name, "debug_user", params)
            self.last_result = result
            print(f"结果: {json.dumps(result, indent=2, ensure_ascii=False)}")
        except json.JSONDecodeError:
            print(f"JSON解析错误: {params_str}")
        except Exception as e:
            print(f"执行错误: {e}")
    
    def do_diagnose(self, arg):
        """诊断工具: diagnose <tool_name>"""
        if not arg:
            print("用法: diagnose <tool_name>")
            return
        
        # 这里应该调用诊断引擎
        print(f"诊断工具 {arg}...")
        print("TODO: 运行诊断")
    
    def do_set_var(self, arg):
        """设置变量: set_var <name> <value_json>"""
        parts = arg.split(maxsplit=1)
        if len(parts) < 2:
            print("用法: set_var <name> <value_json>")
            return
        
        try:
            value = json.loads(parts[1])
            self.variables[parts[0]] = value
            print(f"变量 {parts[0]} = {value}")
        except json.JSONDecodeError:
            print(f"JSON解析错误")
    
    def do_get_var(self, arg):
        """获取变量: get_var <name>"""
        if arg in self.variables:
            print(f"{arg} = {self.variables[arg]}")
        else:
            print(f"变量 {arg} 未定义")
    
    def do_benchmark(self, arg):
        """性能基准测试: benchmark <tool_name> <iterations>"""
        parts = arg.split()
        if len(parts) < 2:
            print("用法: benchmark <tool_name> <iterations>")
            return
        
        tool_name = parts[0]
        iterations = int(parts[1])
        
        import time
        times = []
        for i in range(iterations):
            start = time.time()
            try:
                self.mcp_server.execute_tool(tool_name, "debug_user", {})
                elapsed = time.time() - start
                times.append(elapsed * 1000)
            except Exception as e:
                print(f"迭代 {i+1} 失败: {e}")
        
        if times:
            print(f"\n基准测试结果 ({iterations} 次):")
            print(f"  最小: {min(times):.2f}ms")
            print(f"  最大: {max(times):.2f}ms")
            print(f"  平均: {statistics.mean(times):.2f}ms")
            print(f"  P95:  {sorted(times)[int(len(times)*0.95)]:.2f}ms")

# 使用示例
if __name__ == "__main__":
    # 模拟MCP服务器
    class MockMCP:
        def __init__(self):
            self.tools = [
                {"name": "query_customer", "description": "查询客户信息"},
                {"name": "generate_report", "description": "生成报告"},
                {"name": "upload_file", "description": "上传文件"}
            ]
            self.tool_schemas = {
                "query_customer": {"inputSchema": {"type": "object", "properties": {"user_id": {"type": "string"}}}, "outputSchema": {"type": "object", "properties": {"name": {"type": "string"}, "age": {"type": "number"}}}, "access_level": "public"},
                "generate_report": {"inputSchema": {"type": "object", "properties": {"report_type": {"type": "string", "enum": ["daily", "weekly", "monthly"]}}}, "outputSchema": {"type": "object", "properties": {"report_url": {"type": "string"}}}, "access_level": "internal"},
                "upload_file": {"inputSchema": {"type": "object", "properties": {"file_path": {"type": "string"}}}, "outputSchema": {"type": "object", "properties": {"file_id": {"type": "string"}}}, "access_level": "restricted"}
            }
            self.resources = {}

        def list_tools(self):
            return self.tools

        def get_tool(self, name: str):
            return self.tool_schemas.get(name)

        def execute_tool(self, name: str, user: str, params: Dict):
            if name == "query_customer":
                if params.get("user_id") == "123' OR '1'='1":
                    raise Exception("SQL injection detected")
                return {"name": "John Doe", "age": 30}
            elif name == "generate_report":
                report_type = params.get("report_type")
                if report_type == "daily":
                    return {"report_url": "http://example.com/daily_report.pdf"}
                elif report_type == "weekly":
                    return {"report_url": "http://example.com/weekly_report.pdf"}
                else:
                    return {"report_url": "http://example.com/monthly_report.pdf"}
            elif name == "upload_file":
                file_path = params.get("file_path")
                if file_path == "malicious_file.txt":
                    raise Exception("Command injection detected")
                self.resources[f"file_{len(self.resources) + 1}"] = {"uri": f"file://{file_path}", "size": 1024}
                return {"file_id": f"file_{len(self.resources)}"}
            else:
                raise Exception(f"Tool not found: {name}")

        def get_resource(self, uri: str):
            return self.resources.get(uri.replace("file://", ""))

        def list_resources(self):
            return list(self.resources.values())

    mcp_server = MockMCP()
    debugger = MCPDebuggerCLI(mcp_server)
    debugger.cmdloop()

章节完成 ✅ (经过大幅扩展)

  • 原始字数:5,200字
  • 扩展后字数:约7,500+字
  • 代码行数:1,000+行(原始700+行)
  • 图表:1张(原始)+ 增强内容
  • 核心内容:连接诊断、工具诊断、性能分析、安全扫描、交互式调试