第13章 API与外部服务集成MCP应用

32 阅读9分钟

第13章 API与外部服务集成MCP应用

前言

API是连接内外部系统的桥梁。本章展示如何通过MCP为LLM提供与第三方服务(如电商平台、CRM、支付等)集成的能力。


13.1 案例1:电商平台订单管理系统

13.1.1 应用场景

graph TB
    A["电商订单管理"] --> B["订单查询"]
    A --> C["库存管理"]
    A --> D["支付处理"]
    A --> E["售后服务"]
    
    F["Claude"] --> F1["智能查询"]
    F --> F2["库存提示"]
    F --> F3["支付建议"]
    F --> F4["退货处理"]
    
    F1 --> B
    F2 --> C
    F3 --> D
    F4 --> E

13.1.2 电商API集成

from typing import Dict, List, Optional
from dataclasses import dataclass
from datetime import datetime
import aiohttp

@dataclass
class Order:
    """订单"""
    order_id: str
    customer_id: str
    status: str
    total_amount: float
    items: List[Dict]
    created_at: datetime
    updated_at: datetime


class EcommerceAPIClient:
    """电商API客户端"""
    
    def __init__(self, api_key: str, base_url: str):
        self.api_key = api_key
        self.base_url = base_url
        self.session = None
    
    async def connect(self):
        """建立连接"""
        self.session = aiohttp.ClientSession()
    
    async def disconnect(self):
        """断开连接"""
        if self.session:
            await self.session.close()
    
    async def search_orders(self, customer_id: str = None, 
                           status: str = None, limit: int = 10) -> List[Order]:
        """
        搜索订单
        
        Args:
            customer_id: 客户ID
            status: 订单状态
            limit: 返回数限制
            
        Returns:
            订单列表
        """
        params = {"limit": limit}
        if customer_id:
            params["customer_id"] = customer_id
        if status:
            params["status"] = status
        
        async with self.session.get(
            f"{self.base_url}/orders",
            params=params,
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            data = await resp.json()
            
            orders = []
            for item in data.get("orders", []):
                order = Order(
                    order_id=item["id"],
                    customer_id=item["customer_id"],
                    status=item["status"],
                    total_amount=item["total"],
                    items=item["items"],
                    created_at=datetime.fromisoformat(item["created_at"]),
                    updated_at=datetime.fromisoformat(item["updated_at"])
                )
                orders.append(order)
            
            return orders
    
    async def get_order_details(self, order_id: str) -> Dict:
        """获取订单详情"""
        async with self.session.get(
            f"{self.base_url}/orders/{order_id}",
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            return await resp.json()
    
    async def update_order_status(self, order_id: str, status: str) -> bool:
        """更新订单状态"""
        async with self.session.patch(
            f"{self.base_url}/orders/{order_id}",
            json={"status": status},
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            return resp.status == 200
    
    async def check_inventory(self, product_id: str) -> Dict:
        """
        检查库存
        
        Args:
            product_id: 产品ID
            
        Returns:
            库存信息
        """
        async with self.session.get(
            f"{self.base_url}/inventory/{product_id}",
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            return await resp.json()
    
    async def process_refund(self, order_id: str, reason: str) -> Dict:
        """
        处理退款
        
        Args:
            order_id: 订单ID
            reason: 退款原因
            
        Returns:
            退款结果
        """
        async with self.session.post(
            f"{self.base_url}/refunds",
            json={"order_id": order_id, "reason": reason},
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            return await resp.json()


class OrderManagementMCP:
    """订单管理MCP服务"""
    
    def __init__(self, api_client: EcommerceAPIClient):
        self.api = api_client
    
    async def find_order(self, order_id: str) -> Dict:
        """查找订单"""
        details = await self.api.get_order_details(order_id)
        
        return {
            "found": details.get("id") is not None,
            "order": {
                "id": details.get("id"),
                "status": details.get("status"),
                "total": details.get("total"),
                "items": details.get("items"),
                "customer": details.get("customer"),
                "created_at": details.get("created_at"),
                "updated_at": details.get("updated_at")
            } if details.get("id") else None
        }
    
    async def search_customer_orders(self, customer_id: str) -> Dict:
        """搜索客户订单"""
        orders = await self.api.search_orders(customer_id=customer_id, limit=20)
        
        # 按状态分组
        by_status = {}
        for order in orders:
            if order.status not in by_status:
                by_status[order.status] = []
            by_status[order.status].append({
                "id": order.order_id,
                "total": order.total_amount,
                "items_count": len(order.items)
            })
        
        return {
            "customer_id": customer_id,
            "total_orders": len(orders),
            "by_status": by_status,
            "recent_orders": [{
                "id": o.order_id,
                "total": o.total_amount,
                "status": o.status,
                "date": o.created_at.isoformat()
            } for o in orders[:5]]
        }
    
    async def handle_refund_request(self, order_id: str, reason: str) -> Dict:
        """处理退款请求"""
        result = await self.api.process_refund(order_id, reason)
        
        return {
            "success": result.get("success"),
            "refund_id": result.get("refund_id"),
            "amount": result.get("amount"),
            "status": result.get("status"),
            "message": result.get("message")
        }
    
    async def check_product_availability(self, product_id: str) -> Dict:
        """检查产品可用性"""
        inventory = await self.api.check_inventory(product_id)
        
        return {
            "product_id": product_id,
            "available": inventory.get("quantity", 0) > 0,
            "quantity": inventory.get("quantity"),
            "warehouse_locations": inventory.get("locations"),
            "next_restock_date": inventory.get("next_restock")
        }

13.2 工具定义与集成

class EcommerceMCPServer:
    """电商MCP服务器"""
    
    def __init__(self, order_manager: OrderManagementMCP):
        self.orders = order_manager
    
    def get_tools(self) -> List[Dict]:
        """获取工具定义"""
        return [
            {
                "name": "search_orders",
                "description": "Search for customer orders",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "customer_id": {
                            "type": "string",
                            "description": "Customer ID to search orders for"
                        },
                        "status": {
                            "type": "string",
                            "description": "Order status filter (pending/processing/shipped/delivered)",
                            "enum": ["pending", "processing", "shipped", "delivered"]
                        }
                    },
                    "required": ["customer_id"]
                }
            },
            {
                "name": "get_order_details",
                "description": "Get detailed information about an order",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "order_id": {
                            "type": "string",
                            "description": "Order ID"
                        }
                    },
                    "required": ["order_id"]
                }
            },
            {
                "name": "process_refund",
                "description": "Process a refund request",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "order_id": {
                            "type": "string",
                            "description": "Order ID"
                        },
                        "reason": {
                            "type": "string",
                            "description": "Refund reason"
                        }
                    },
                    "required": ["order_id", "reason"]
                }
            }
        ]
    
    async def call_tool(self, tool_name: str, arguments: Dict) -> str:
        """调用工具"""
        import json
        
        try:
            if tool_name == "search_orders":
                result = await self.orders.search_customer_orders(
                    arguments["customer_id"]
                )
            elif tool_name == "get_order_details":
                result = await self.orders.find_order(arguments["order_id"])
            elif tool_name == "process_refund":
                result = await self.orders.handle_refund_request(
                    arguments["order_id"],
                    arguments["reason"]
                )
            else:
                return json.dumps({"error": f"Unknown tool: {tool_name}"})
            
            return json.dumps(result, ensure_ascii=False)
        
        except Exception as e:
            return json.dumps({"error": str(e)})

13.3 CRM系统增强案例

class CRMAPIClient:
    """CRM API客户端"""
    
    def __init__(self, api_key: str, base_url: str):
        self.api_key = api_key
        self.base_url = base_url
        self.session = None
    
    async def connect(self):
        self.session = aiohttp.ClientSession()
    
    async def get_customer_profile(self, customer_id: str) -> Dict:
        """获取客户档案"""
        async with self.session.get(
            f"{self.base_url}/customers/{customer_id}",
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            return await resp.json()
    
    async def get_interaction_history(self, customer_id: str) -> List[Dict]:
        """获取交互历史"""
        async with self.session.get(
            f"{self.base_url}/customers/{customer_id}/interactions",
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            data = await resp.json()
            return data.get("interactions", [])
    
    async def create_opportunity(self, customer_id: str, opportunity_data: Dict) -> Dict:
        """创建商机"""
        async with self.session.post(
            f"{self.base_url}/opportunities",
            json={**opportunity_data, "customer_id": customer_id},
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            return await resp.json()
    
    async def update_customer_notes(self, customer_id: str, notes: str) -> bool:
        """更新客户备注"""
        async with self.session.patch(
            f"{self.base_url}/customers/{customer_id}",
            json={"notes": notes},
            headers={"Authorization": f"Bearer {self.api_key}"}
        ) as resp:
            return resp.status == 200

13.4 高级功能:限流与重试机制

from enum import Enum
import asyncio
from typing import Callable

class RetryStrategy:
    """重试策略"""
    
    def __init__(self, max_retries: int = 3, backoff_factor: float = 2.0):
        self.max_retries = max_retries
        self.backoff_factor = backoff_factor
    
    async def execute_with_retry(self, func: Callable, *args, **kwargs):
        """
        执行函数并进行重试
        
        Args:
            func: 要执行的函数
            *args: 位置参数
            **kwargs: 关键字参数
            
        Returns:
            函数执行结果
        """
        for attempt in range(self.max_retries):
            try:
                return await func(*args, **kwargs)
            except Exception as e:
                if attempt == self.max_retries - 1:
                    raise
                
                wait_time = self.backoff_factor ** attempt
                await asyncio.sleep(wait_time)


class RateLimiter:
    """速率限制器"""
    
    def __init__(self, requests_per_minute: int = 60):
        self.requests_per_minute = requests_per_minute
        self.request_times: List[float] = []
    
    async def wait_if_needed(self):
        """
        如果超过限制则等待
        """
        import time
        
        now = time.time()
        one_minute_ago = now - 60
        
        # 移除一分钟前的请求记录
        self.request_times = [t for t in self.request_times if t > one_minute_ago]
        
        if len(self.request_times) >= self.requests_per_minute:
            # 计算需要等待的时间
            oldest_request = self.request_times[0]
            wait_time = 60 - (now - oldest_request)
            if wait_time > 0:
                await asyncio.sleep(wait_time)
            self.request_times = []
        
        self.request_times.append(now)


class EnhancedEcommerceAPIClient(EcommerceAPIClient):
    """增强的电商API客户端"""
    
    def __init__(self, api_key: str, base_url: str, 
                 rate_limit: int = 100):
        super().__init__(api_key, base_url)
        self.retry_strategy = RetryStrategy()
        self.rate_limiter = RateLimiter(rate_limit)
        self.request_cache: Dict[str, Tuple[Dict, float]] = {}
        self.cache_ttl = 300  # 5分钟缓存
    
    async def search_orders_safe(self, customer_id: str = None, 
                                status: str = None) -> List[Order]:
        """
        安全的搜索订单(带重试和限流)
        """
        await self.rate_limiter.wait_if_needed()
        
        return await self.retry_strategy.execute_with_retry(
            self.search_orders,
            customer_id=customer_id,
            status=status
        )
    
    def _get_cache_key(self, method: str, *args) -> str:
        """生成缓存键"""
        import hashlib
        key = f"{method}_{' '.join(str(a) for a in args)}"
        return hashlib.md5(key.encode()).hexdigest()
    
    async def get_cached_result(self, method: str, *args, **kwargs):
        """获取缓存结果"""
        import time
        
        cache_key = self._get_cache_key(method, *args)
        
        if cache_key in self.request_cache:
            result, timestamp = self.request_cache[cache_key]
            if time.time() - timestamp < self.cache_ttl:
                return result
            else:
                del self.request_cache[cache_key]
        
        return None

13.5 完整的CRM系统集成

class EnhancedCRMAPIClient(CRMAPIClient):
    """增强的CRM客户端"""
    
    async def get_customer_analysis(self, customer_id: str) -> Dict:
        """
        获取客户综合分析
        
        Args:
            customer_id: 客户ID
            
        Returns:
            客户分析结果
        """
        # 获取基础信息
        profile = await self.get_customer_profile(customer_id)
        interactions = await self.get_interaction_history(customer_id)
        
        # 分析交互模式
        interaction_analysis = self._analyze_interactions(interactions)
        
        # 生成建议
        recommendations = self._generate_recommendations(profile, interaction_analysis)
        
        return {
            "customer_id": customer_id,
            "profile": profile,
            "interaction_analysis": interaction_analysis,
            "recommendations": recommendations,
            "health_score": self._calculate_health_score(profile, interaction_analysis)
        }
    
    def _analyze_interactions(self, interactions: List[Dict]) -> Dict:
        """分析交互"""
        if not interactions:
            return {"count": 0, "types": {}, "last_interaction": None}
        
        types = {}
        for interaction in interactions:
            itype = interaction.get("type", "unknown")
            types[itype] = types.get(itype, 0) + 1
        
        return {
            "count": len(interactions),
            "types": types,
            "last_interaction": interactions[-1].get("timestamp"),
            "engagement_level": "high" if len(interactions) > 5 else "medium" if len(interactions) > 2 else "low"
        }
    
    def _generate_recommendations(self, profile: Dict, 
                                 analysis: Dict) -> List[str]:
        """生成建议"""
        recommendations = []
        
        if analysis["engagement_level"] == "low":
            recommendations.append("Schedule follow-up call")
            recommendations.append("Send personalized offer")
        
        if profile.get("annual_value", 0) > 100000:
            recommendations.append("Assign dedicated account manager")
        
        return recommendations
    
    def _calculate_health_score(self, profile: Dict, 
                               analysis: Dict) -> float:
        """计算健康评分"""
        score = 50.0
        
        # 基于交互频率
        if analysis.get("engagement_level") == "high":
            score += 30
        elif analysis.get("engagement_level") == "medium":
            score += 15
        
        # 基于交易金额
        if profile.get("annual_value", 0) > 100000:
            score += 20
        
        return min(score, 100.0)

13.6 工作流程与业务场景

class OrderManagementWorkflow:
    """订单管理工作流"""
    
    def __init__(self, order_manager: OrderManagementMCP,
                 crm_client: EnhancedCRMAPIClient):
        self.orders = order_manager
        self.crm = crm_client
    
    async def handle_customer_inquiry(self, customer_id: str, 
                                     inquiry_type: str) -> Dict:
        """
        处理客户询问的完整工作流
        
        Args:
            customer_id: 客户ID
            inquiry_type: 询问类型
            
        Returns:
            处理结果
        """
        workflow_result = {
            "customer_id": customer_id,
            "steps": [],
            "resolution": None
        }
        
        # 第1步:获取客户订单
        workflow_result["steps"].append({
            "name": "Fetch Customer Orders",
            "status": "running"
        })
        
        orders = await self.orders.search_customer_orders(customer_id)
        workflow_result["steps"][-1]["status"] = "completed"
        workflow_result["steps"][-1]["data"] = orders
        
        # 第2步:获取客户分析
        workflow_result["steps"].append({
            "name": "Analyze Customer Profile",
            "status": "running"
        })
        
        analysis = await self.crm.get_customer_analysis(customer_id)
        workflow_result["steps"][-1]["status"] = "completed"
        workflow_result["steps"][-1]["data"] = analysis
        
        # 第3步:根据询问类型处理
        workflow_result["steps"].append({
            "name": f"Handle {inquiry_type}",
            "status": "running"
        })
        
        if inquiry_type == "refund":
            # 找最近的订单
            recent_order = orders.get("recent_orders", [{}])[0]
            resolution = await self.orders.handle_refund_request(
                recent_order.get("id"),
                "Customer request"
            )
        
        elif inquiry_type == "product_info":
            # 检查库存
            product_id = orders.get("recent_orders", [{}])[0].get("id")
            resolution = await self.orders.check_product_availability(product_id)
        
        else:
            resolution = {"status": "Unknown inquiry type"}
        
        workflow_result["steps"][-1]["status"] = "completed"
        workflow_result["resolution"] = resolution
        
        # 第4步:生成后续行动
        workflow_result["steps"].append({
            "name": "Generate Follow-up Actions",
            "status": "completed",
            "data": analysis.get("recommendations", [])
        })
        
        return workflow_result
    
    async def process_bulk_orders(self, customer_ids: List[str]) -> Dict:
        """
        批量处理订单
        
        Args:
            customer_ids: 客户ID列表
            
        Returns:
            批量处理结果
        """
        results = {
            "total": len(customer_ids),
            "successful": 0,
            "failed": 0,
            "details": []
        }
        
        for customer_id in customer_ids:
            try:
                workflow = await self.handle_customer_inquiry(
                    customer_id,
                    "status_check"
                )
                results["successful"] += 1
                results["details"].append({
                    "customer_id": customer_id,
                    "status": "completed"
                })
            except Exception as e:
                results["failed"] += 1
                results["details"].append({
                    "customer_id": customer_id,
                    "status": "failed",
                    "error": str(e)
                })
        
        return results

13.7 部署架构与安全

graph TB
    A["Claude Desktop"] -->|MCP| B["API集成服务器"]
    
    B --> C["请求管理"]
    C --> C1["限流器"]
    C --> C2["重试管理"]
    C --> C3["缓存层"]
    
    B --> D["API网关"]
    D --> E["电商API"]
    D --> F["CRM API"]
    D --> G["支付API"]
    
    B --> H["安全管理"]
    H --> H1["API密钥管理"]
    H --> H2["加密通道"]
    H --> H3["审计日志"]

13.7.1 安全实现

class APISecurityManager:
    """API安全管理"""
    
    def __init__(self):
        self.api_keys: Dict[str, Dict] = {}
        self.audit_log: List[Dict] = []
    
    def register_api_key(self, service: str, key: str, 
                        rotation_days: int = 90) -> Dict:
        """
        注册API密钥
        
        Args:
            service: 服务名称
            key: API密钥
            rotation_days: 轮换周期(天)
            
        Returns:
            密钥信息
        """
        from datetime import datetime, timedelta
        import hashlib
        
        key_hash = hashlib.sha256(key.encode()).hexdigest()
        
        key_info = {
            "service": service,
            "key_hash": key_hash,
            "created_at": datetime.now().isoformat(),
            "expires_at": (datetime.now() + timedelta(days=rotation_days)).isoformat(),
            "status": "active"
        }
        
        self.api_keys[service] = key_info
        
        self._log_audit("api_key_registered", {"service": service})
        
        return key_info
    
    def validate_api_key(self, service: str, key: str) -> bool:
        """验证API密钥"""
        import hashlib
        from datetime import datetime
        
        if service not in self.api_keys:
            return False
        
        key_info = self.api_keys[service]
        
        # 检查过期
        expires_at = datetime.fromisoformat(key_info["expires_at"])
        if datetime.now() > expires_at:
            self._log_audit("api_key_expired", {"service": service})
            return False
        
        # 验证哈希
        key_hash = hashlib.sha256(key.encode()).hexdigest()
        if key_hash != key_info["key_hash"]:
            self._log_audit("invalid_api_key", {"service": service})
            return False
        
        return True
    
    def _log_audit(self, action: str, details: Dict):
        """记录审计日志"""
        from datetime import datetime
        
        self.audit_log.append({
            "timestamp": datetime.now().isoformat(),
            "action": action,
            "details": details
        })

13.8 错误处理与恢复

class APIErrorHandler:
    """API错误处理"""
    
    @staticmethod
    def handle_error(error: Exception) -> Dict:
        """
        处理API错误
        
        Args:
            error: 错误对象
            
        Returns:
            错误信息
        """
        error_handlers = {
            "TimeoutError": APIErrorHandler._handle_timeout,
            "ConnectionError": APIErrorHandler._handle_connection,
            "ValueError": APIErrorHandler._handle_validation,
        }
        
        error_type = type(error).__name__
        handler = error_handlers.get(error_type, APIErrorHandler._handle_generic)
        
        return handler(error)
    
    @staticmethod
    def _handle_timeout(error: Exception) -> Dict:
        return {
            "error_type": "timeout",
            "message": "API request timed out",
            "suggestion": "Retry with exponential backoff",
            "retry_after_seconds": 30
        }
    
    @staticmethod
    def _handle_connection(error: Exception) -> Dict:
        return {
            "error_type": "connection",
            "message": "Failed to connect to API",
            "suggestion": "Check network connectivity and API endpoint",
            "retry_after_seconds": 60
        }
    
    @staticmethod
    def _handle_validation(error: Exception) -> Dict:
        return {
            "error_type": "validation",
            "message": "Invalid request parameters",
            "suggestion": "Review request schema and try again",
            "retry_after_seconds": 0
        }
    
    @staticmethod
    def _handle_generic(error: Exception) -> Dict:
        return {
            "error_type": "generic",
            "message": str(error),
            "suggestion": "Contact support if problem persists",
            "retry_after_seconds": 30
        }

13.9 完整集成示例

async def main():
    """完整使用示例"""
    
    # 初始化
    ecommerce_client = EnhancedEcommerceAPIClient(
        api_key="your_api_key",
        base_url="https://api.ecommerce.com",
        rate_limit=100
    )
    
    crm_client = EnhancedCRMAPIClient(
        api_key="your_crm_key",
        base_url="https://api.crm.com"
    )
    
    await ecommerce_client.connect()
    await crm_client.connect()
    
    # 初始化工作流
    order_manager = OrderManagementMCP(ecommerce_client)
    workflow = OrderManagementWorkflow(order_manager, crm_client)
    security = APISecurityManager()
    
    # 注册API密钥
    security.register_api_key("ecommerce", "your_api_key")
    security.register_api_key("crm", "your_crm_key")
    
    # 处理客户询问
    print("🔄 Processing customer inquiry...")
    result = await workflow.handle_customer_inquiry("CUST001", "refund")
    print(f"✅ Resolution: {result['resolution']}")
    
    # 批量处理
    print("🔄 Processing bulk orders...")
    bulk_result = await workflow.process_bulk_orders([
        "CUST001", "CUST002", "CUST003"
    ])
    print(f"✅ Successful: {bulk_result['successful']}/{bulk_result['total']}")
    
    # 清理
    await ecommerce_client.disconnect()
    await crm_client.disconnect()

本章总结

关键点说明
API集成通过HTTP/REST访问第三方服务
异步处理aiohttp进行高效的异步API调用
错误处理完善的异常处理和重试机制
数据转换将API响应转换为结构化数据
权限管理API密钥和认证
缓存策略热点数据缓存优化性能

常见问题

Q1: 如何处理API限流? A: 实施令牌桶算法或指数退避重试,遵守API速率限制。

Q2: 如何保护API密钥? A: 使用环境变量、密钥管理服务,不要硬编码密钥。

Q3: 如何处理API超时? A: 设置合理的超时时间,实施自动重试和降级方案。

Q4: 如何支持多个API版本? A: 通过适配器模式或版本路由支持多个API版本。

Q5: 如何监控API集成健康状态? A: 定期health check,记录API调用指标,设置告警。


下一章预告:第14章将讲述知识库与信息管理MCP应用