Skill: danielliao/mcdonalds-mcp-order-lite (version 1.0.0)

--- name: mcdonalds-mcp-order-lite description: Place McDonald's China delivery orders through the official MCP server at https://mcp.mcd.cn using a Bearer MCP token over Streamable HTTP / JSON-RPC. Use when the user wants to browse McDonald's deliverable addresses, query store menu items, inspect meal details, calculate price, create a delivery order, query order status, or check McDonald's coupons/points. Also use when wiring this MCP into a client like Cursor, Cherry Studio, Trae, or another agent. This lite package intentionally contains no embedded token and only the core reusable files. --- Use the official McDonald's China MCP toolchain, not scraped web APIs. ## Core workflow 1. Identify the delivery address. 2. Call `delivery-query-addresses` first. 3. Pick the matching address record and carry forward `addressId`, `storeCode`, and `beCode` from the same record. 4. Call `query-meals` for that store. 5. If needed, call `query-meal-detail` for a chosen meal code. 6. Call `calculate-price` before ordering. 7. Show the user the real payable amount and wait for confirmation. 8. Call `create-order` only after confirmation. 9. Return the `payH5Url` to the user. 10. After the user says payment is complete, call `query-order`. ## Important constraints - Use server URL `https://mcp.mcd.cn`. - Send `Authorization: Bearer <TOKEN>`. - MCP protocol version must be `2025-06-18` or earlier. - Prefer low request volume; the docs say 600 requests/minute/token. - Do not invent `storeCode`, `beCode`, or `addressId`. - For delivery ordering, always use values returned by `delivery-query-addresses` or `delivery-create-address`. - Always price-check with `calculate-price` before `create-order`. - `query-meal-detail` docs note that replacing default combo choices is not yet supported in current version. ## Tool map ### Delivery ordering - `delivery-query-addresses`: list saved deliverable addresses - `delivery-create-address`: add a new delivery address - `query-store-coupons`: coupons usable for the chosen store - `query-meals`: menu for the chosen store - `query-meal-detail`: inspect a meal / combo - `calculate-price`: final price check - `create-order`: create payable delivery order - `query-order`: query order status after creation/payment ### Other useful tools - `available-coupons`: claimable coupons - `auto-bind-coupons`: one-click claim current coupons - `query-my-coupons`: coupon wallet - `list-nutrition-foods`: nutrition list - `query-my-account`: points account - `mall-points-products`, `mall-product-detail`, `mall-create-order`: points mall - `campaign-calendar`: marketing calendar - `now-time-info`: server time ## Recommended user-facing flow When the user says “帮我点麦当劳” or similar: - Confirm budget / preferred item if missing. - Resolve the delivery address from saved addresses first. - Find candidate items from `query-meals`. - Use `calculate-price` for one or a few concrete combinations. - Present the cheapest / best-fit options with real payable totals. - Only place the order after explicit confirmation. - After order creation, send the payment link. - After payment, query `query-order` instead of assuming success. ## Files in this skill - Read `references/api.md` for the compact API cheat sheet and tested field names. - Use `scripts/mcd_rpc.py` for direct RPC calls to the remote MCP server when you need deterministic testing or CLI usage. - `client.py` contains a Python wrapper around the same remote MCP calls. - `nlp_processor.py` contains lightweight text parsing for order intent. - `tools.py` contains higher-level helper functions. ## Packaging notes Keep the skill folder lean. Do not add extra docs beyond what is needed for reuse. Remove transient files like `__pycache__` before packaging.

path: nlp_processor.py
content: """
自然语言处理模块
用于解析用户点餐意图和订单项
"""

import re
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from enum import Enum


class OrderIntent(Enum):
    """订单意图枚举"""
    VIEW_MENU = "view_menu"           # 查看菜单
    SEARCH_PRODUCT = "search_product" # 搜索产品
    CREATE_ORDER = "create_order"     # 创建订单
    FIND_STORE = "find_store"         # 查找门店
    CHECK_ORDER = "check_order"       # 查询订单
    CANCEL_ORDER = "cancel_order"     # 取消订单
    UNKNOWN = "unknown"               # 未知意图


@dataclass
class IntentResult:
    """意图识别结果"""
    intent: OrderIntent
    confidence: float
    entities: Dict[str, str]
    raw_text: str


@dataclass
class OrderItem:
    """订单项"""
    product_name: str
    quantity: int
    customizations: Dict[str, str]
    notes: Optional[str] = None


class McDonaldsNLPProcessor:
    """麦当劳自然语言处理器"""
    
    def __init__(self):
        # 意图关键词映射
        self.intent_keywords = {
            OrderIntent.VIEW_MENU: [
                '菜单', '有什么吃的', '看看吃的', '产品', '有什么',
                '看看菜单', '菜品', '食物', '吃的', '喝什么'
            ],
            OrderIntent.SEARCH_PRODUCT: [
                '找', '搜索', '想吃', '来一份', '想要', '点',
                '有没有', '什么', '哪个', '推荐'
            ],
            OrderIntent.CREATE_ORDER: [
                '点餐', '下单', '我要', '来一个', '订', '购买',
                '买', '要', '给我', '来份', '一份', '两份'
            ],
            OrderIntent.FIND_STORE: [
                '附近', '门店', '在哪里', '麦当劳', '地址',
                '位置', '哪家', '最近', '距离'
            ],
            OrderIntent.CHECK_ORDER: [
                '订单', '状态', '查一下', '查询', '查看',
                '进度', '到哪里了', '好了吗'
            ],
            OrderIntent.CANCEL_ORDER: [
                '取消', '不要了', '退单', '退了', '撤销',
                '不买了', '算了', '退订'
            ]
        }
        
        # 数量词映射
        self.quantity_map = {
            '一': 1, '两': 2, '二': 2, '三': 3, '四': 4, '五': 5,
            '六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
            '一个': 1, '两个': 2, '三个': 3, '四个': 4, '五个': 5,
            '一份': 1, '两份': 2, '三份': 3, '四份': 4, '五份': 5,
            '1': 1, '2': 2, '3': 3, '4': 4, '5': 5,
            '6': 6, '7': 7, '8': 8, '9': 9, '10': 10
        }
        
        # 产品类别关键词
        self.category_keywords = {
            '汉堡': ['汉堡', '堡', '巨无霸', '麦辣鸡腿堡', '安格斯'],
            '薯条': ['薯条', '薯', '薯格'],
            '饮料': ['饮料', '可乐', '雪碧', '果汁', '咖啡', '奶茶', '奶昔'],
            '甜品': ['甜品', '冰淇淋', '派', '麦旋风', '甜筒'],
            '早餐': ['早餐', '粥', '油条', '汉堡', '早餐堡'],
            '套餐': ['套餐', '组合', 'set', 'combo']
        }
        
        # 定制选项关键词
        self.customization_keywords = {
            'size': {
                '大': 'large', '中': 'medium', '小': 'small',
                '加大': 'extra_large', '标准': 'regular'
            },
            'ice': {
                '正常冰': 'normal', '少冰': 'less', '去冰': 'no',
                '热': 'hot', '温': 'warm'
            },
            'sugar': {
                '正常糖': 'normal', '少糖': 'less', '无糖': 'no',
                '半糖': 'half'
            },
            'spicy': {
                '辣': 'spicy', '微辣': 'mild', '不辣': 'no',
                '特辣': 'extra_spicy'
            }
        }
        
        # 麦当劳常见产品名称
        self.common_products = {
            '巨无霸': 'big_mac',
            '麦辣鸡腿堡': 'spicy_chicken_burger',
            '安格斯厚牛堡': 'angus_beef_burger',
            '板烧鸡腿堡': 'grilled_chicken_burger',
            '麦香鱼': 'filet_o_fish',
            '麦乐鸡': 'chicken_mcnuggets',
            '薯条': 'french_fries',
            '可乐': 'coke',
            '雪碧': 'sprite',
            '麦旋风': 'mcflurry',
            '甜筒': 'cone',
            '苹果派': 'apple_pie',
            '红豆派': 'red_bean_pie'
        }
    
    def recognize_intent(self, text: str) -> IntentResult:
        """
        识别用户意图
        
        Args:
            text: 用户输入文本
            
        Returns:
            意图识别结果
        """
        text_lower = text.lower()
        entities = self._extract_entities(text)
        
        # 计算每个意图的匹配分数
        intent_scores = {}
        for intent, keywords in self.intent_keywords.items():
            score = 0
            for keyword in keywords:
                if keyword in text_lower:
                    score += 1
            intent_scores[intent] = score
        
        # 找出最高分意图
        best_intent = max(intent_scores.items(), key=lambda x: x[1])
        
        # 计算置信度
        total_keywords = sum(len(keywords) for keywords in self.intent_keywords.values())
        confidence = best_intent[1] / max(len(self.intent_keywords.get(best_intent[0], [])), 1)
        
        # 如果分数为0，则认为是未知意图
        if best_intent[1] == 0:
            best_intent = (OrderIntent.UNKNOWN, 0)
            confidence = 0.0
        
        return IntentResult(
            intent=best_intent[0],
            confidence=min(confidence, 1.0),
            entities=entities,
            raw_text=text
        )
    
    def _extract_entities(self, text: str) -> Dict[str, str]:
        """提取实体信息"""
        entities = {}
        
        # 提取数量
        quantity_match = re.search(r'([一二三四五六七八九十两]个?|[0-9]+个?|一份?|两份?|三份?|四份?|五份?)', text)
        if quantity_match:
            quantity_str = quantity_match.group(1)
            entities['quantity'] = str(self.quantity_map.get(quantity_str, 1))
        
        # 提取产品名称
        for product_cn, product_en in self.common_products.items():
            if product_cn in text:
                entities['product'] = product_en
                entities['product_cn'] = product_cn
                break
        
        # 提取尺寸
        for size_cn, size_en in self.customization_keywords['size'].items():
            if size_cn in text:
                entities['size'] = size_en
                break
        
        # 提取冰量
        for ice_cn, ice_en in self.customization_keywords['ice'].items():
            if ice_cn in text:
                entities['ice'] = ice_en
                break
        
        # 提取糖量
        for sugar_cn, sugar_en in self.customization_keywords['sugar'].items():
            if sugar_cn in text:
                entities['sugar'] = sugar_en
                break
        
        # 提取辣度
        for spicy_cn, spicy_en in self.customization_keywords['spicy'].items():
            if spicy_cn in text:
                entities['spicy'] = spicy_en
                break
        
        # 提取位置信息（简单匹配）
        location_patterns = [
            r'在(.{2,10}?)附近',
            r'到(.{2,10}?)的',
            r'(.{2,10}?)的麦当劳'
        ]
        
        for pattern in location_patterns:
            match = re.search(pattern, text)
            if match:
                entities['location'] = match.group(1)
                break
        
        return entities
    
    def parse_order_items(self, text: str, menu: List[Dict] = None) -> List[OrderItem]:
        """
        解析订单项
        
        Args:
            text: 用户点餐描述
            menu: 菜单数据（可选，用于产品匹配）
            
        Returns:
            解析后的订单项列表
        """
        items = []
        
        # 分割多个订单项（简单实现）
        # 使用逗号、和、还有等分隔符
        separators = ['，', ',', '、', '和', '还有', '再加']
        
        for sep in separators:
            if sep in text:
                parts = text.split(sep)
                break
        else:
            parts = [text]
        
        for part in parts:
            part = part.strip()
            if not part:
                continue
                
            # 解析单个订单项
            item = self._parse_single_item(part, menu)
            if item:
                items.append(item)
        
        return items
    
    def _parse_single_item(self, text: str, menu: List[Dict] = None) -> Optional[OrderItem]:
        """解析单个订单项"""
        # 提取数量
        quantity = 1
        for q_str, q_num in self.quantity_map.items():
            if q_str in text:
                quantity = q_num
                # 从文本中移除数量词以便后续匹配
                text = text.replace(q_str, '')
                break
        
        # 提取产品名称
        product_name = None
        product_id = None
        
        # 首先尝试匹配常见产品
        for cn_name, en_id in self.common_products.items():
            if cn_name in text:
                product_name = cn_name
                product_id = en_id
                text = text.replace(cn_name, '')
                break
        
        # 如果没有匹配到常见产品，尝试从菜单中匹配
        if not product_name and menu:
            for product in menu:
                if product.get('name') and product['name'] in text:
                    product_name = product['name']
                    product_id = product.get('id')
                    break
        
        if not product_name:
            # 如果还是没找到，使用文本中的关键词
            for category, keywords in self.category_keywords.items():
                for keyword in keywords:
                    if keyword in text:
                        product_name = keyword
                        break
                if product_name:
                    break
        
        # 提取定制选项
        customizations = {}
        for option_type, options in self.customization_keywords.items():
            for cn_option, en_option in options.items():
                if cn_option in text:
                    customizations[option_type] = en_option
                    text = text.replace(cn_option, '')
        
        # 提取备注（剩余文本）
        notes = text.strip() if text.strip() else None
        
        if product_name:
            return OrderItem(
                product_name=product_name,
                quantity=quantity,
                customizations=customizations,
                notes=notes
            )
        
        return None
    
    def generate_order_summary(self, items: List[OrderItem], 
                             store_name: str = None) -> str:
        """
        生成订单摘要
        
        Args:
            items: 订单项列表
            store_name: 门店名称
            
        Returns:
            订单摘要文本
        """
        if not items:
            return "订单为空"
        
        summary_parts = []
        
        if store_name:
            summary_parts.append(f"📍 门店：{store_name}")
        
        summary_parts.append("🛒 订单内容：")
        
        for i, item in enumerate(items, 1):
            item_text = f"  {i}. {item.product_name} ×{item.quantity}"
            
            if item.customizations:
                custom_text = []
                for opt_type, opt_value in item.customizations.items():
                    # 将英文选项转回中文显示
                    for cn_opt, en_opt in self.customization_keywords.get(opt_type, {}).items():
                        if en_opt == opt_value:
                            custom_text.append(cn_opt)
                            break
                
                if custom_text:
                    item_text += f" ({'，'.join(custom_text)})"
            
            if item.notes:
                item_text += f" 📝 {item.notes}"
            
            summary_parts.append(item_text)
        
        total_quantity = sum(item.quantity for item in items)
        summary_parts.append(f"\n📊 总计：{len(items)}种产品，{total_quantity}件商品")
        
        return "\n".join(summary_parts)
    
    def suggest_response(self, intent_result: IntentResult) -> str:
        """
        根据意图生成建议回复
        
        Args:
            intent_result: 意图识别结果
            
        Returns:
            建议回复文本
        """
        intent = intent_result.intent
        entities = intent_result.entities
        
        responses = {
            OrderIntent.VIEW_MENU: "好的，我来为您查看麦当劳的菜单。您想查看哪个分类的产品呢？",
            OrderIntent.SEARCH_PRODUCT: "好的，我来帮您搜索产品。您想找什么产品呢？",
            OrderIntent.CREATE_ORDER: "好的，我来帮您点餐。请告诉我您想要什么？",
            OrderIntent.FIND_STORE: "好的，我来帮您查找附近的麦当劳门店。请告诉我您的位置？",
            OrderIntent.CHECK_ORDER: "好的，我来帮您查询订单状态。请提供订单号？",
            OrderIntent.CANCEL_ORDER: "好的，我来帮您取消订单。请提供订单号？",
            OrderIntent.UNKNOWN: "抱歉，我没有理解您的意思。您可以告诉我想要点餐、查看菜单还是查找门店？"
        }
        
        response = responses.get(intent, responses[OrderIntent.UNKNOWN])
        
        # 根据提取的实体丰富回复
        if intent == OrderIntent.CREATE_ORDER and 'product' in entities:
            product_cn = entities.get('product_cn', '')
            quantity = entities.get('quantity', '1')
            response = f"好的，您要{quantity}份{product_cn}是吗？还需要其他什么吗？"
        
        elif intent == OrderIntent.FIND_STORE and 'location' in entities:
            location = entities['location']
            response = f"好的，我来查找{location}附近的麦当劳门店。"
        
        return response


# 使用示例
if __name__ == "__main__":
    processor = McDonaldsNLPProcessor()
    
    test_cases = [
        "我想看看麦当劳有什么吃的",
        "来一个巨无霸套餐",
        "我要大可乐和薯条",
        "附近有没有麦当劳",
        "我的订单到哪里了",
        "取消刚才的订单"
    ]
    
    print("=== NLP处理器测试 ===")
    
    for test_text in test_cases:
        print(f"\n输入: {test_text}")
        
        # 识别意图
        intent_result = processor.recognize_intent(test_text)
        print(f"意图: {intent_result.intent.value} (置信度: {intent_result.confidence:.2f})")
        if intent_result.entities:
            print(f"实体: {intent_result.entities}")
        
        # 生成建议回复
        response = processor.suggest_response(intent_result)
        print(f"回复: {response}")
        
        # 如果是点餐意图，尝试解析订单项
        if intent_result.intent == OrderIntent.CREATE_ORDER:
            items = processor.parse_order_items(test_text)
            if items:
                print("解析的订单项:")
                for item in items:
                    print(f"  - {item.product_name} ×{item.quantity}")
                    if item.customizations:
                        print(f"    定制: {item.customizations}")

path: references/api.md
content: # McDonald's MCP quick reference

## Endpoint
- MCP server: `https://mcp.mcd.cn`
- Header: `Authorization: Bearer <TOKEN>`
- Protocol: JSON-RPC over Streamable HTTP
- MCP version: `2025-06-18`

## Initialize

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": {},
    "clientInfo": {"name": "client-name", "version": "0.1.0"}
  }
}
```

Then send:

```json
{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}
```

## Tested ordering flow

### 1) Saved addresses
Tool: `delivery-query-addresses`
Input: `{}`
Important output fields:
- `addressId`
- `contactName`
- `phone`
- `fullAddress`
- `storeCode`
- `storeName`
- `beCode`

### 2) Store menu
Tool: `query-meals`
Input:
```json
{"storeCode":"1400650","beCode":"140065002"}
```
Important output shape:
- `data.categories[]`
- `data.meals[code] = { name, currentPrice }`

### 3) Meal detail
Tool: `query-meal-detail`
Input:
```json
{"code":"9900003001","storeCode":"1400650","beCode":"140065002"}
```
Important note from docs: default combo replacement not supported yet.

### 4) Price check
Tool: `calculate-price`
Input:
```json
{
  "storeCode":"1400650",
  "beCode":"140065002",
  "items":[
    {"productCode":"901000","quantity":1},
    {"productCode":"904105","quantity":1}
  ]
}
```
Important output fields:
- `productOriginalPrice`
- `productPrice`
- `deliveryOriginalPrice`
- `deliveryPrice`
- `packingOriginalPrice`
- `packingPrice`
- `discount`
- `price`
- `productList[]`

All price fields are in 分 unless the server embeds formatted text in the markdown summary.

### 5) Create order
Tool: `create-order`
Input:
```json
{
  "addressId":"1076478540000118049160177611",
  "storeCode":"1400650",
  "beCode":"140065002",
  "items":[
    {"productCode":"901000","quantity":1},
    {"productCode":"904105","quantity":1}
  ]
}
```
Important output fields:
- `orderId`
- `payId`
- `payH5Url`
- `orderDetail.orderStatus`
- `orderDetail.realTotalAmount`
- `orderDetail.orderProductList[]`

### 6) Query order
Tool: `query-order`
Input:
```json
{"orderId":"1030750870000656448787414429"}
```
Important output fields:
- `orderStatus`
- `storeName`
- `orderProductList[]`
- `deliveryInfo.expectDeliveryTime`

## Useful tool names
- `delivery-query-addresses`
- `delivery-create-address`
- `query-store-coupons`
- `query-meals`
- `query-meal-detail`
- `calculate-price`
- `create-order`
- `query-order`
- `available-coupons`
- `auto-bind-coupons`
- `query-my-coupons`
- `query-my-account`
- `mall-points-products`
- `mall-product-detail`
- `mall-create-order`
- `list-nutrition-foods`
- `campaign-calendar`
- `now-time-info`

## Error expectations
- `401`: bad / missing token
- `429`: rate limit

## Practical tips
- Always use real payable totals from `calculate-price`, not menu prices.
- Delivery fee can make cheap items exceed the user’s budget.
- Some discounts appear automatically at pricing time.
- Order creation returns a payment URL; payment is not complete until the user pays.


path: client.py
content: """
麦当劳MCP API客户端
基于麦当劳MCP平台的智能点餐客户端
"""

import os
import requests
from typing import Dict, List, Optional, Any
import json
from datetime import datetime
from urllib.parse import urljoin


class McDonaldsMCPError(Exception):
    """麦当劳MCP异常基类"""
    pass


class AuthenticationError(McDonaldsMCPError):
    """认证错误"""
    pass


class MenuUnavailableError(McDonaldsMCPError):
    """菜单不可用"""
    pass


class OrderCreationError(McDonaldsMCPError):
    """订单创建失败"""
    pass


class StoreNotFoundError(McDonaldsMCPError):
    """门店未找到"""
    pass


class McDonaldsMCPClient:
    """麦当劳MCP API客户端"""
    
    def __init__(self, token: str = None, base_url: str = None):
        """
        初始化客户端
        
        Args:
            token: MCP Token，默认为环境变量 MCD_MCP_TOKEN
            base_url: MCP 服务地址，默认为环境变量 MCD_MCP_URL 或 https://mcp.mcd.cn
        """
        self.token = token or os.getenv('MCD_MCP_TOKEN', '')
        self.base_url = base_url or os.getenv('MCD_MCP_URL', 'https://mcp.mcd.cn')
        self.headers = {
            'Authorization': f'Bearer {self.token}',
            'Content-Type': 'application/json',
            'User-Agent': 'McDonaldsMCPClient/1.0.0'
        }
        self.session = requests.Session()
        self.session.headers.update(self.headers)
        
    def _make_request(self, method: str, endpoint: str, data: Dict = None, 
                     params: Dict = None) -> Dict:
        """
        发送API请求
        
        Args:
            method: HTTP方法 (GET, POST, PUT, DELETE)
            endpoint: API端点
            data: 请求体数据
            params: URL参数
            
        Returns:
            API响应数据
            
        Raises:
            AuthenticationError: Token无效或过期
            MenuUnavailableError: 菜单不可用
            OrderCreationError: 订单创建失败
            McDonaldsMCPError: 其他API错误
        """
        url = urljoin(self.base_url, endpoint)
        
        try:
            response = self.session.request(
                method=method,
                url=url,
                json=data,
                params=params,
                timeout=30
            )
            response.raise_for_status()
            return response.json()
            
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 401:
                raise AuthenticationError("Token无效或已过期，请检查MCP Token")
            elif e.response.status_code == 404:
                if 'menu' in endpoint:
                    raise MenuUnavailableError("菜单暂时不可用")
                elif 'store' in endpoint:
                    raise StoreNotFoundError("未找到指定门店")
                else:
                    raise McDonaldsMCPError(f"资源未找到: {endpoint}")
            elif e.response.status_code == 400:
                error_msg = "请求参数错误"
                try:
                    error_data = e.response.json()
                    error_msg = error_data.get('message', error_msg)
                except:
                    pass
                raise OrderCreationError(f"订单创建失败: {error_msg}")
            elif e.response.status_code == 429:
                raise McDonaldsMCPError("请求过于频繁，请稍后再试")
            else:
                raise McDonaldsMCPError(f"API错误 ({e.response.status_code}): {e}")
                
        except requests.exceptions.ConnectionError:
            raise McDonaldsMCPError("网络连接失败，请检查网络设置")
        except requests.exceptions.Timeout:
            raise McDonaldsMCPError("请求超时，请稍后再试")
        except json.JSONDecodeError:
            raise McDonaldsMCPError("API响应格式错误")
        except Exception as e:
            raise McDonaldsMCPError(f"未知错误: {str(e)}")
    
    # ========== 菜单相关接口 ==========
    
    def get_menu(self, store_id: Optional[str] = None, 
                language: str = 'zh-CN') -> Dict:
        """
        获取麦当劳菜单
        
        Args:
            store_id: 门店ID，可选
            language: 语言代码，默认zh-CN
            
        Returns:
            菜单数据，包含分类和产品信息
        """
        endpoint = "v1/menu"
        params = {'language': language}
        if store_id:
            params['store_id'] = store_id
            
        return self._make_request('GET', endpoint, params=params)
    
    def get_categories(self, store_id: Optional[str] = None) -> List[Dict]:
        """
        获取菜单分类
        
        Args:
            store_id: 门店ID，可选
            
        Returns:
            分类列表
        """
        menu = self.get_menu(store_id)
        return menu.get('categories', [])
    
    def get_products_by_category(self, category_id: str, 
                               store_id: Optional[str] = None) -> List[Dict]:
        """
        获取指定分类的产品
        
        Args:
            category_id: 分类ID
            store_id: 门店ID，可选
            
        Returns:
            产品列表
        """
        menu = self.get_menu(store_id)
        for category in menu.get('categories', []):
            if category.get('id') == category_id:
                return category.get('products', [])
        return []
    
    # ========== 产品搜索接口 ==========
    
    def search_products(self, keyword: str, category: Optional[str] = None,
                       store_id: Optional[str] = None) -> List[Dict]:
        """
        搜索产品
        
        Args:
            keyword: 搜索关键词
            category: 产品类别，可选
            store_id: 门店ID，可选
            
        Returns:
            匹配的产品列表
        """
        endpoint = "v1/products/search"
        data = {'keyword': keyword}
        if category:
            data['category'] = category
        if store_id:
            data['store_id'] = store_id
            
        return self._make_request('POST', endpoint, data=data)
    
    def get_product_details(self, product_id: str, 
                          store_id: Optional[str] = None) -> Dict:
        """
        获取产品详情
        
        Args:
            product_id: 产品ID
            store_id: 门店ID，可选
            
        Returns:
            产品详情信息
        """
        endpoint = f"v1/products/{product_id}"
        params = {}
        if store_id:
            params['store_id'] = store_id
            
        return self._make_request('GET', endpoint, params=params)
    
    # ========== 门店相关接口 ==========
    
    def find_stores(self, latitude: float, longitude: float, 
                   radius: int = 5000, limit: int = 20) -> List[Dict]:
        """
        查找附近门店
        
        Args:
            latitude: 纬度
            longitude: 经度
            radius: 搜索半径（米），默认5000
            limit: 返回结果数量，默认20
            
        Returns:
            门店列表，包含距离信息
        """
        endpoint = "v1/stores/nearby"
        data = {
            'latitude': latitude,
            'longitude': longitude,
            'radius': radius,
            'limit': limit
        }
        
        return self._make_request('POST', endpoint, data=data)
    
    def get_store_details(self, store_id: str) -> Dict:
        """
        获取门店详情
        
        Args:
            store_id: 门店ID
            
        Returns:
            门店详情信息
        """
        endpoint = f"v1/stores/{store_id}"
        return self._make_request('GET', endpoint)
    
    def get_store_hours(self, store_id: str) -> Dict:
        """
        获取门店营业时间
        
        Args:
            store_id: 门店ID
            
        Returns:
            营业时间信息
        """
        endpoint = f"v1/stores/{store_id}/hours"
        return self._make_request('GET', endpoint)
    
    # ========== 订单相关接口 ==========
    
    def create_order(self, items: List[Dict], store_id: str,
                    user_info: Dict = None, 
                    delivery_info: Dict = None,
                    coupon_code: Optional[str] = None,
                    payment_method: str = 'online') -> Dict:
        """
        创建订单
        
        Args:
            items: 订单项列表，每个项包含:
                - product_id: 产品ID
                - quantity: 数量
                - customizations: 定制选项（可选）
            store_id: 门店ID
            user_info: 用户信息，包含姓名、电话等
            delivery_info: 配送信息（如果需要配送）
            coupon_code: 优惠券代码，可选
            payment_method: 支付方式，默认online
            
        Returns:
            订单信息，包含订单ID、总价等
        """
        endpoint = "v1/orders"
        data = {
            'items': items,
            'store_id': store_id,
            'payment_method': payment_method
        }
        
        if user_info:
            data['user_info'] = user_info
        if delivery_info:
            data['delivery_info'] = delivery_info
        if coupon_code:
            data['coupon_code'] = coupon_code
            
        return self._make_request('POST', endpoint, data=data)
    
    def get_order_status(self, order_id: str) -> Dict:
        """
        获取订单状态
        
        Args:
            order_id: 订单ID
            
        Returns:
            订单状态信息
        """
        endpoint = f"v1/orders/{order_id}"
        return self._make_request('GET', endpoint)
    
    def cancel_order(self, order_id: str, reason: Optional[str] = None) -> Dict:
        """
        取消订单
        
        Args:
            order_id: 订单ID
            reason: 取消原因，可选
            
        Returns:
            取消结果
        """
        endpoint = f"v1/orders/{order_id}/cancel"
        data = {'reason': reason} if reason else {}
        return self._make_request('POST', endpoint, data=data)
    
    def get_order_history(self, user_id: Optional[str] = None,
                         limit: int = 20, offset: int = 0) -> List[Dict]:
        """
        获取订单历史
        
        Args:
            user_id: 用户ID，可选
            limit: 返回数量，默认20
            offset: 偏移量，默认0
            
        Returns:
            订单历史列表
        """
        endpoint = "v1/orders/history"
        params = {'limit': limit, 'offset': offset}
        if user_id:
            params['user_id'] = user_id
            
        return self._make_request('GET', endpoint, params=params)
    
    # ========== 优惠券相关接口 ==========
    
    def get_available_coupons(self, store_id: Optional[str] = None) -> List[Dict]:
        """
        获取可用优惠券
        
        Args:
            store_id: 门店ID，可选
            
        Returns:
            优惠券列表
        """
        endpoint = "v1/coupons/available"
        params = {}
        if store_id:
            params['store_id'] = store_id
            
        return self._make_request('GET', endpoint, params=params)
    
    def validate_coupon(self, coupon_code: str, 
                       store_id: Optional[str] = None) -> Dict:
        """
        验证优惠券
        
        Args:
            coupon_code: 优惠券代码
            store_id: 门店ID，可选
            
        Returns:
            验证结果和优惠信息
        """
        endpoint = "v1/coupons/validate"
        data = {'code': coupon_code}
        if store_id:
            data['store_id'] = store_id
            
        return self._make_request('POST', endpoint, data=data)
    
    # ========== 工具方法 ==========
    
    def format_price(self, price_cents: int) -> str:
        """格式化价格（分转元）"""
        return f"¥{price_cents / 100:.2f}"
    
    def calculate_order_total(self, items: List[Dict], 
                            coupon_discount: int = 0) -> Dict:
        """
        计算订单总价
        
        Args:
            items: 订单项列表
            coupon_discount: 优惠券折扣（分）
            
        Returns:
            价格摘要
        """
        subtotal = 0
        for item in items:
            price = item.get('price_cents', 0)
            quantity = item.get('quantity', 1)
            subtotal += price * quantity
        
        # 假设税费为10%
        tax = int(subtotal * 0.1)
        # 假设配送费为500分（5元）
        delivery_fee = 500
        
        total = subtotal + tax + delivery_fee - coupon_discount
        
        return {
            'subtotal': self.format_price(subtotal),
            'tax': self.format_price(tax),
            'delivery_fee': self.format_price(delivery_fee),
            'discount': self.format_price(coupon_discount),
            'total': self.format_price(total),
            'subtotal_cents': subtotal,
            'total_cents': total
        }
    
    def suggest_combo(self, main_product: Dict) -> List[Dict]:
        """
        推荐套餐组合
        
        Args:
            main_product: 主产品
            
        Returns:
            推荐的套餐组合
        """
        # 简单的套餐推荐逻辑
        combos = []
        
        # 如果是汉堡类，推荐搭配薯条和饮料
        if '汉堡' in main_product.get('name', '') or '堡' in main_product.get('name', ''):
            combos.append({
                'name': '经典套餐',
                'items': [
                    main_product,
                    {'name': '中薯条', 'price_cents': 1500},
                    {'name': '中可乐', 'price_cents': 1000}
                ],
                'total_saving': '节省 ¥5.00'
            })
        
        return combos
    
    def check_store_availability(self, store_id: str) -> bool:
        """
        检查门店是否可用
        
        Args:
            store_id: 门店ID
            
        Returns:
            是否可用
        """
        try:
            store_info = self.get_store_details(store_id)
            hours = self.get_store_hours(store_id)
            
            # 检查是否在营业时间内
            current_hour = datetime.now().hour
            opening_hour = hours.get('opening_hour', 7)
            closing_hour = hours.get('closing_hour', 23)
            
            return opening_hour <= current_hour < closing_hour
            
        except Exception:
            return False


# 示例使用
if __name__ == "__main__":
    # 初始化客户端
    client = McDonaldsMCPClient()
    
    print("=== 麦当劳MCP点餐客户端测试 ===")
    
    try:
        # 测试获取菜单
        print("1. 获取菜单...")
        menu = client.get_menu()
        categories = client.get_categories()
        print(f"  找到 {len(categories)} 个分类")
        
        # 测试搜索产品
        print("2. 搜索产品...")
        products = client.search_products("巨无霸")
        print(f"  找到 {len(products)} 个相关产品")
        
        # 测试查找门店
        print("3. 查找附近门店...")
        # 上海坐标
        stores = client.find_stores(latitude=31.2304, longitude=121.4737, radius=3000)
        print(f"  找到 {len(stores)} 家附近门店")
        
        if stores:
            store_id = stores[0]['id']
            print(f"  选择门店: {stores[0]['name']}")
            
            # 测试创建订单
            print("4. 创建测试订单...")
            test_items = [
                {
                    'product_id': 'big_mac',
                    'quantity': 1,
                    'customizations': {'size': 'large'}
                },
                {
                    'product_id': 'coke',
                    'quantity': 1,
                    'customizations': {'ice': 'normal'}
                }
            ]
            
            # 在实际使用中需要真实的用户信息
            # order = client.create_order(
            #     items=test_items,
            #     store_id=store_id,
            #     user_info={'name': '测试用户', 'phone': '13800138000'}
            # )
            # print(f"  订单创建成功: {order.get('order_id')}")
        
        print("✅ 所有测试通过！")
        
    except AuthenticationError as e:
        print(f"❌ 认证失败: {e}")
    except MenuUnavailableError as e:
        print(f"❌ 菜单不可用: {e}")
    except OrderCreationError as e:
        print(f"❌ 订单创建失败: {e}")
    except Exception as e:
        print(f"❌ 未知错误: {e}")

path: tools.py
content: """
OpenClaw工具集成
将麦当劳MCP点餐功能集成到OpenClaw中
"""

import os
import json
from typing import Dict, List, Optional, Any
from openclaw.skill import tool
from .client import McDonaldsMCPClient, McDonaldsMCPError
from .nlp_processor import McDonaldsNLPProcessor, OrderIntent, IntentResult

# 初始化客户端和NLP处理器
client = McDonaldsMCPClient()
nlp_processor = McDonaldsNLPProcessor()


@tool
async def mcdonalds_view_menu(store_id: str = None, language: str = "zh-CN") -> Dict:
    """
    查看麦当劳菜单
    
    Args:
        store_id: 门店ID（可选）
        language: 语言代码，默认zh-CN
        
    Returns:
        菜单数据
    """
    try:
        menu = client.get_menu(store_id, language)
        
        # 格式化返回结果
        categories = menu.get('categories', [])
        formatted_result = {
            'success': True,
            'store_id': store_id,
            'total_categories': len(categories),
            'categories': []
        }
        
        for category in categories[:10]:  # 限制返回数量
            category_info = {
                'id': category.get('id'),
                'name': category.get('name'),
                'description': category.get('description'),
                'product_count': len(category.get('products', []))
            }
            
            # 添加前几个产品作为示例
            sample_products = []
            for product in category.get('products', [])[:3]:
                sample_products.append({
                    'id': product.get('id'),
                    'name': product.get('name'),
                    'price': client.format_price(product.get('price_cents', 0)),
                    'description': product.get('description', '')[:50] + '...'
                })
            
            if sample_products:
                category_info['sample_products'] = sample_products
            
            formatted_result['categories'].append(category_info)
        
        return formatted_result
        
    except McDonaldsMCPError as e:
        return {
            'success': False,
            'error': str(e),
            'message': '获取菜单失败'
        }
    except Exception as e:
        return {
            'success': False,
            'error': str(e),
            'message': '未知错误'
        }


@tool
async def mcdonalds_search_product(keyword: str, category: str = None, 
                                  store_id: str = None) -> Dict:
    """
    搜索麦当劳产品
    
    Args:
        keyword: 搜索关键词
        category: 产品类别（可选）
        store_id: 门店ID（可选）
        
    Returns:
        搜索结果
    """
    try:
        products = client.search_products(keyword, category, store_id)
        
        formatted_result = {
            'success': True,
            'keyword': keyword,
            'total_results': len(products),
            'products': []
        }
        
        for product in products[:20]:  # 限制返回数量
            product_info = {
                'id': product.get('id'),
                'name': product.get('name'),
                'price': client.format_price(product.get('price_cents', 0)),
                'category': product.get('category'),
                'description': product.get('description', '')[:100],
                'available': product.get('available', True)
            }
            
            # 添加图片URL（如果有）
            if product.get('image_url'):
                product_info['image_url'] = product.get('image_url')
            
            formatted_result['products'].append(product_info)
        
        return formatted_result
        
    except McDonaldsMCPError as e:
        return {
            'success': False,
            'error': str(e),
            'message': '搜索产品失败'
        }


@tool
async def mcdonalds_find_stores(latitude: float, longitude: float, 
                               radius: int = 5000, limit: int = 10) -> Dict:
    """
    查找附近麦当劳门店
    
    Args:
        latitude: 纬度
        longitude: 经度
        radius: 搜索半径（米），默认5000
        limit: 返回结果数量，默认10
        
    Returns:
        门店列表
    """
    try:
        stores = client.find_stores(latitude, longitude, radius, limit)
        
        formatted_result = {
            'success': True,
            'location': {'latitude': latitude, 'longitude': longitude},
            'radius': radius,
            'total_stores': len(stores),
            'stores': []
        }
        
        for store in stores:
            store_info = {
                'id': store.get('id'),
                'name': store.get('name'),
                'address': store.get('address'),
                'distance': f"{store.get('distance', 0)}米",
                'phone': store.get('phone'),
                'opening_hours': store.get('opening_hours')
            }
            
            # 添加营业状态
            if client.check_store_availability(store.get('id')):
                store_info['status'] = '营业中'
            else:
                store_info['status'] = '已打烊'
            
            formatted_result['stores'].append(store_info)
        
        return formatted_result
        
    except McDonaldsMCPError as e:
        return {
            'success': False,
            'error': str(e),
            'message': '查找门店失败'
        }


@tool
async def mcdonalds_create_order(items: List[Dict], store_id: str,
                                user_name: str = None, user_phone: str = None,
                                delivery_address: str = None,
                                coupon_code: str = None) -> Dict:
    """
    创建麦当劳订单
    
    Args:
        items: 订单项列表，每个项包含:
            - product_id: 产品ID
            - quantity: 数量
            - customizations: 定制选项（可选）
        store_id: 门店ID
        user_name: 用户姓名
        user_phone: 用户电话
        delivery_address: 配送地址（可选）
        coupon_code: 优惠券代码（可选）
        
    Returns:
        订单信息
    """
    try:
        # 准备用户信息
        user_info = {}
        if user_name:
            user_info['name'] = user_name
        if user_phone:
            user_info['phone'] = user_phone
        
        # 准备配送信息
        delivery_info = None
        if delivery_address:
            delivery_info = {
                'address': delivery_address,
                'type': 'delivery'
            }
        
        # 创建订单
        order = client.create_order(
            items=items,
            store_id=store_id,
            user_info=user_info if user_info else None,
            delivery_info=delivery_info,
            coupon_code=coupon_code
        )
        
        # 计算价格摘要
        price_summary = client.calculate_order_total(
            items,
            coupon_discount=order.get('discount_cents', 0)
        )
        
        formatted_result = {
            'success': True,
            'order_id': order.get('order_id'),
            'store_id': store_id,
            'status': order.get('status', 'created'),
            'created_at': order.get('created_at'),
            'estimated_ready_time': order.get('estimated_ready_time'),
            'price_summary': price_summary,
            'message': '订单创建成功'
        }
        
        # 添加订单项详情
        formatted_result['items'] = []
        for item in items:
            formatted_result['items'].append({
                'product_id': item.get('product_id'),
                'quantity': item.get('quantity'),
                'customizations': item.get('customizations', {})
            })
        
        return formatted_result
        
    except McDonaldsMCPError as e:
        return {
            'success': False,
            'error': str(e),
            'message': '创建订单失败'
        }
    except Exception as e:
        return {
            'success': False,
            'error': str(e),
            'message': f'创建订单时发生错误: {str(e)}'
        }


@tool
async def mcdonalds_check_order(order_id: str) -> Dict:
    """
    查询订单状态
    
    Args:
        order_id: 订单ID
        
    Returns:
        订单状态信息
    """
    try:
        order_status = client.get_order_status(order_id)
        
        formatted_result = {
            'success': True,
            'order_id': order_id,
            'status': order_status.get('status'),
            'store_name': order_status.get('store_name'),
            'created_at': order_status.get('created_at'),
            'updated_at': order_status.get('updated_at'),
            'estimated_ready_time': order_status.get('estimated_ready_time'),
            'items_count': len(order_status.get('items', [])),
            'total_price': client.format_price(order_status.get('total_cents', 0))
        }
        
        # 添加状态描述
        status_descriptions = {
            'created': '订单已创建',
            'confirmed': '订单已确认',
            'preparing': '正在制作',
            'ready': '准备就绪',
            'delivering': '配送中',
            'delivered': '已送达',
            'cancelled': '已取消'
        }
        
        current_status = order_status.get('status')
        formatted_result['status_description'] = status_descriptions.get(
            current_status, '未知状态'
        )
        
        return formatted_result
        
    except McDonaldsMCPError as e:
        return {
            'success': False,
            'error': str(e),
            'message': '查询订单状态失败'
        }


@tool
async def mcdonalds_cancel_order(order_id: str, reason: str = None) -> Dict:
    """
    取消订单
    
    Args:
        order_id: 订单ID
        reason: 取消原因（可选）
        
    Returns:
        取消结果
    """
    try:
        result = client.cancel_order(order_id, reason)
        
        return {
            'success': True,
            'order_id': order_id,
            'cancelled': result.get('cancelled', False),
            'refund_amount': client.format_price(result.get('refund_cents', 0)),
            'message': result.get('message', '订单取消成功')
        }
        
    except McDonaldsMCPError as e:
        return {
            'success': False,
            'error': str(e),
            'message': '取消订单失败'
        }


@tool
async def mcdonalds_analyze_intent(user_input: str) -> Dict:
    """
    分析用户点餐意图
    
    Args:
        user_input: 用户输入文本
        
    Returns:
        意图分析结果
    """
    try:
        intent_result = nlp_processor.recognize_intent(user_input)
        
        formatted_result = {
            'success': True,
            'input': user_input,
            'intent': intent_result.intent.value,
            'confidence': intent_result.confidence,
            'entities': intent_result.entities,
            'suggested_response': nlp_processor.suggest_response(intent_result)
        }
        
        # 如果是点餐意图，尝试解析订单项
        if intent_result.intent == OrderIntent.CREATE_ORDER:
            # 获取菜单用于产品匹配
            menu = client.get_menu()
            all_products = []
            for category in menu.get('categories', []):
                all_products.extend(category.get('products', []))
            
            order_items = nlp_processor.parse_order_items(user_input, all_products)
            
            if order_items:
                formatted_result['parsed_items'] = []
                for item in order_items:
                    formatted_result['parsed_items'].append({
                        'product_name': item.product_name,
                        'quantity': item.quantity,
                        'customizations': item.customizations,
                        'notes': item.notes
                    })
                
                # 生成订单摘要
                formatted_result['order_summary'] = nlp_processor.generate_order_summary(order_items)
        
        return formatted_result
        
    except Exception as e:
        return {
            'success': False,
            'error': str(e),
            'message': '意图分析失败'
        }


@tool
async def mcdonalds_get_available_coupons(store_id: str = None) -> Dict:
    """
    获取可用优惠券
    
    Args:
        store_id: 门店ID（可选）
        
    Returns:
        优惠券列表
    """
    try:
        coupons = client.get_available_coupons(store_id)
        
        formatted_result = {
            'success': True,
            'total_coupons': len(coupons),
            'coupons': []
        }
        
        for coupon in coupons[:10]:  # 限制返回数量
            coupon_info = {
                'code': coupon.get('code'),
                'name': coupon.get('name'),
                'description': coupon.get('description'),
                'discount': coupon.get('discount_description'),
                'valid_until': coupon.get('valid_until'),
                'min_order_amount': client.format_price(coupon.get('min_order_cents', 0))
            }
            
            formatted_result['coupons'].append(coupon_info)
        
        return formatted_result
        
    except McDonaldsMCPError as e:
        return {
            'success': False,
            'error': str(e),
            'message': '获取优惠券失败'
        }


@tool
async def mcdonalds_suggest_combo(main_product: str) -> Dict:
    """
    推荐套餐组合
    
    Args:
        main_product: 主产品名称
        
    Returns:
        套餐推荐
    """
    try:
        # 搜索主产品
        products = client.search_products(main_product)
        
        if not products:
            return {
                'success': False,
                'message': f'未找到产品: {main_product}'
            }
        
        main_product_info = products[0]
        combos = client.suggest_combo(main_product_info)
        
        formatted_result = {
            'success': True,
            'main_product': main_product_info.get('name'),
            'total_combos': len(combos),
            'combos': []
        }
        
        for combo in combos:
            combo_info = {
                'name': combo.get('name'),
                'total_saving': combo.get('total_saving'),
                'items': []
            }
            
            for item in combo.get('items', []):
                combo_info['items'].append({
                    'name': item.get('name'),
                    'price': client.format_price(item.get('price_cents', 0))
                })
            
            formatted_result['combos'].append(combo_info)
        
        return formatted_result
        
    except Exception as e:
        return {
            'success': False,
            'error': str(e),
            'message': '推荐套餐失败'
        }


# 高级工具：智能点餐助手
@tool
async def mcdonalds_smart_order_assistant(user_input: str, 
                                         latitude: float = None,
                                         longitude: float = None) -> Dict:
    """
    智能点餐助手 - 一站式处理用户点餐需求
    
    Args:
        user_input: 用户输入文本
        latitude: 纬度（可选，用于查找门店）
        longitude: 经度（可选，用于查找门店）
        
    Returns:
        智能处理结果
    """
    try:
        # 分析用户意图
        intent_result = nlp_processor.recognize_intent(user_input)
        
        response = {
            'success': True,
            'input': user_input,
            'intent': intent_result.intent.value,
            'confidence': intent_result.confidence,
            'suggested_actions': [],
            'next_steps': []
        }
        
        # 根据意图提供不同的处理建议
        if intent_result.intent == OrderIntent.VIEW_MENU:
            response['suggested_actions'].append({
                'action': 'view_menu',
                'description': '查看麦当劳菜单',
                'tool': 'mcdonalds_view_menu'
            })
            response['next_steps'].append('请告诉我您想查看哪个门店的菜单（可选）')
            
        elif intent_result.intent == OrderIntent.SEARCH_PRODUCT:
            keyword = intent_result.entities.get('product_cn', '')
            if keyword:
                response['suggested_actions'].append({
                    'action': 'search_product',
                    'description': f'搜索产品: {keyword}',
                    'tool': 'mcdonalds_search_product',
                    'parameters': {'keyword': keyword}
                })
            else:
                response['next_steps'].append('请告诉我您想搜索什么产品？')
            
        elif intent_result.intent == OrderIntent.CREATE_ORDER:
            # 解析订单项
            menu = client.get_menu()
            all_products = []
            for category in menu.get('categories', []):
                all_products.extend(category.get('products', []))
            
            order_items = nlp_processor.parse_order_items(user_input, all_products)
            
            if order_items:
                response['parsed_order'] = {
                    'items': [],
                    'summary': nlp_processor.generate_order_summary(order_items)
                }
                
                for item in order_items:
                    response['parsed_order']['items'].append({
                        'product_name': item.product_name,
                        'quantity': item.quantity,
                        'customizations': item.customizations
                    })
                
                response['suggested_actions'].append({
                    'action': 'create_order',
                    'description': '创建订单',
                    'tool': 'mcdonalds_create_order',
                    'note': '需要提供门店ID和用户信息'
                })
                response['next_steps'].extend([
                    '请提供门店ID',
                    '请提供您的姓名和电话',
                    '是否需要配送？'
                ])
            else:
                response['next_steps'].append('请详细描述您想点的餐品')
            
        elif intent_result.intent == OrderIntent.FIND_STORE:
            if latitude and longitude:
                response['suggested_actions'].append({
                    'action': 'find_stores',
                    'description': '查找附近门店',
                    'tool': 'mcdonalds_find_stores',
                    'parameters': {
                        'latitude': latitude,
                        'longitude': longitude,
                        'radius': 3000
                    }
                })
            else:
                response['next_steps'].append('请提供您的位置信息（经纬度）或大致位置描述')
            
        elif intent_result.intent == OrderIntent.CHECK_ORDER:
            response['next_steps'].append('请提供您的订单号')
            
        elif intent_result.intent == OrderIntent.CANCEL_ORDER:
            response['next_steps'].append('请提供要取消的订单号')
        
        else:
            response['next_steps'] = [
                '我可以帮您：',
                '1. 查看麦当劳菜单',
                '2. 搜索特定产品',
                '3. 点餐下单',
                '4. 查找附近门店',
                '5. 查询订单状态',
                '6. 取消订单',
                '请告诉我您需要什么帮助？'
            ]
        
        return response
        
    except Exception as e:
        return {
            'success': False,
            'error': str(e),
            'message': '智能助手处理失败'
        }


# 示例配置
@tool
async def mcdonalds_get_config() -> Dict:
    """
    获取麦当劳MCP配置信息
    
    Returns:
        配置信息
    """
    return {
        'success': True,
        'config': {
            'api_base_url': client.base_url,
            'token_masked': client.token[:8] + '...' if client.token else '未设置',
            'available_tools': [
                'mcdonalds_view_menu',
                'mcdonalds_search_product',
                'mcdonalds_find_stores',
                'mcdonalds_create_order',
                'mcdonalds_check_order',
                'mcdonalds_cancel_order',
                'mcdonalds_analyze_intent',
                'mcdonalds_get_available_coupons',
                'mcdonalds_suggest_combo',
                'mcdonalds_smart_order_assistant'
            ],
            'example_queries': [
                '查看麦当劳菜单',
                '搜索巨无霸',
                '查找附近的麦当劳',
                '我要点一个巨无霸套餐',
                '我的订单状态',
                '取消订单123456'
            ]
        }
    }


# 测试函数
if __name__ == "__main__":
    import asyncio
    
    async def test_tools():
        print("=== 麦当劳MCP工具测试 ===")
        
        # 测试配置
        print("\n1. 测试配置获取:")
        config = await mcdonalds_get_config()
        print(f"   API地址: {config['config']['api_base_url']}")
        print(f"   可用工具: {len(config['config']['available_tools'])}个")
        
        # 测试意图分析
        print("\n2. 测试意图分析:")
        test_inputs = [
            "我想看看麦当劳有什么吃的",
            "来一个巨无霸",
            "附近有没有麦当劳"
        ]
        
        for test_input in test_inputs:
            result = await mcdonalds_analyze_intent(test_input)
            print(f"   '{test_input}' -> {result['intent']} (置信度: {result['confidence']:.2f})")
        
        # 测试菜单查看
        print("\n3. 测试菜单查看:")
        menu_result = await mcdonalds_view_menu()
        if menu_result['success']:
            print(f"   找到 {menu_result['total_categories']} 个分类")
            for category in menu_result['categories'][:3]:
                print(f"   - {category['name']} ({category['product_count']}个产品)")
        
        # 测试智能助手
        print("\n4. 测试智能助手:")
        assistant_result = await mcdonalds_smart_order_assistant("我要点巨无霸套餐")
        print(f"   意图: {assistant_result['intent']}")
        if 'parsed_order' in assistant_result:
            print(f"   解析的订单: {assistant_result['parsed_order']['summary']}")
        
        print("\n✅ 所有工具测试完成！")
    
    # 运行测试
    asyncio.run(test_tools())

path: scripts/mcd_rpc.py
content: #!/usr/bin/env python3
"""Minimal McDonald's MCP JSON-RPC client.

Examples:
  python3 scripts/mcd_rpc.py tools/list
  python3 scripts/mcd_rpc.py tools/call '{"name":"delivery-query-addresses","arguments":{}}'
"""

import json
import os
import sys
import urllib.request

URL = os.getenv("MCD_MCP_URL", "https://mcp.mcd.cn")
[REDACTED_SECRET]"MCD_MCP_TOKEN", "")


def rpc(method: str, params=None, req_id: int = 1):
    payload = {
        "jsonrpc": "2.0",
        "id": req_id,
        "method": method,
        "params": params or {},
    }
    req = urllib.request.Request(URL, data=json.dumps(payload).encode(), method="POST")
    req.add_header("Authorization", f"Bearer {TOKEN}")
    req.add_header("Content-Type", "application/json")
    req.add_header("Accept", "application/json, text/event-stream")
    with urllib.request.urlopen(req, timeout=60) as resp:
        return json.loads(resp.read().decode())


def notify_initialized():
    payload = {"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}}
    req = urllib.request.Request(URL, data=json.dumps(payload).encode(), method="POST")
    req.add_header("Authorization", f"Bearer {TOKEN}")
    req.add_header("Content-Type", "application/json")
    req.add_header("Accept", "application/json, text/event-stream")
    with urllib.request.urlopen(req, timeout=60):
        return None


def initialize():
    return rpc(
        "initialize",
        {
            "protocolVersion": "2025-06-18",
            "capabilities": {},
            "clientInfo": {"name": "mcd-rpc-script", "version": "0.1.0"},
        },
        1,
    )


def main():
    if not [REDACTED_SECRET]"Set MCD_MCP_TOKEN first", file=sys.stderr)
        sys.exit(2)
    if len(sys.argv) < 2:
        print(__doc__.strip())
        sys.exit(2)

    method = sys.argv[1]
    params = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {}

    init_res = initialize()
    notify_initialized()
    res = rpc(method, params, 2)
    print(json.dumps({"initialize": init_res, "result": res}, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)