Python入门指南-AI番外-MCP完整教程:从零开始学会Model Context Protocol

1 阅读31分钟

image.png

Python入门指南-AI番外-MCP完整教程:从零开始学会Model Context Protocol

🎯 教程目标

欢迎来到MCP(Model Context Protocol)的世界!本教程将带你从零开始,学会什么是MCP,如何使用它,以及如何用Python创建自己的MCP应用。无论你是编程小白还是有一定基础的开发者,都能轻松掌握!

📚 目录

  1. MCP是什么?
  2. 为什么需要MCP?
  3. MCP的核心概念
  4. 环境准备
  5. 第一个MCP应用
  6. 与通义千问集成
  7. 实战项目:智能文件管理器
  8. 高级功能和最佳实践
  9. 常见问题解答
  10. 总结与展望

🤔 MCP是什么?

简单理解

想象一下,你有一个非常聪明的助手(比如通义千问这样的AI),但这个助手被困在一个房间里,无法直接接触外面的世界。MCP就像是为这个助手开了一扇窗户,让它可以:

  • 📁 读取和管理你的文件
  • 🌐 访问网络获取信息
  • 🗄️ 连接数据库查询数据
  • 🔧 使用各种工具和服务

技术定义

MCP(Model Context Protocol)是一个开放标准,它定义了AI模型如何与外部系统安全、可靠地交互。它就像是AI和外部世界之间的"翻译官"。

graph TD
    A[用户] --> B[AI助手<br/>通义千问]
    B --> C[MCP协议]
    C --> D[文件系统]
    C --> E[网络API]
    C --> F[数据库]
    C --> G[其他工具]
    
    D --> H[读写文件]
    E --> I[获取网络数据]
    F --> J[查询数据]
    G --> K[执行任务]
    
    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#f3e5f5
    style D fill:#e8f5e8
    style E fill:#e8f5e8
    style F fill:#e8f5e8
    style G fill:#e8f5e8

🔍 为什么需要MCP?

传统方式的问题

在MCP出现之前,让AI与外部系统交互就像让两个说不同语言的人交流一样困难:

graph LR
    A[AI模型] -.-> B[应用1]
    A -.-> C[应用2]
    A -.-> D[应用3]
    
    B --> E[自定义接口1]
    C --> F[自定义接口2]
    D --> G[自定义接口3]
    
    style A fill:#ffcdd2
    style B fill:#ffcdd2
    style C fill:#ffcdd2
    style D fill:#ffcdd2

问题:

  • 每个应用都需要定制开发
  • 安全性难以保证
  • 维护成本高
  • 无法复用

MCP的优势

MCP就像制定了一套通用的"外交协议":

graph LR
    A[AI模型] --> B[MCP标准接口]
    B --> C[应用1]
    B --> D[应用2]
    B --> E[应用3]
    
    style A fill:#c8e6c9
    style B fill:#fff9c4
    style C fill:#c8e6c9
    style D fill:#c8e6c9
    style E fill:#c8e6c9

优势:

  • 🔒 安全:统一的安全标准
  • 🔄 可复用:一次开发,处处使用
  • 📈 可扩展:轻松添加新功能
  • 🛡️ 稳定:标准化的接口

🏗️ MCP的核心概念

1. 服务器和客户端

sequenceDiagram
    participant C as MCP客户端<br/>(AI应用)
    participant S as MCP服务器<br/>(工具提供者)
    
    C->>S: 连接请求
    S->>C: 连接确认
    C->>S: 调用工具
    S->>C: 返回结果
    C->>S: 断开连接
  • MCP客户端:AI应用(如通义千问)
  • MCP服务器:提供具体功能的工具

2. 资源(Resources)

资源是MCP服务器可以提供的数据,比如:

  • 📄 文件内容
  • 🌐 网页数据
  • 🗄️ 数据库记录

3. 工具(Tools)

工具是MCP服务器可以执行的操作,比如:

  • ✍️ 写文件
  • 🔍 搜索
  • 📊 数据分析

4. 提示(Prompts)

提示是预定义的对话模板,帮助AI更好地理解任务。

🔧 环境准备

1. 安装Python

首先确保你的电脑上安装了Python 3.8或更高版本。

# 检查Python版本
python --version

# 如果版本过低,请从官网下载最新版本
# https://www.python.org/downloads/

2. 安装必要的包

# 创建虚拟环境(推荐)
python -m venv mcp_env

# 激活虚拟环境
# Windows:
mcp_env\Scripts\activate
# macOS/Linux:
source mcp_env/bin/activate

# 安装MCP相关包
pip install mcp
pip install dashscope  # 通义千问的SDK
pip install requests
pip install asyncio

3. 获取通义千问API密钥

  1. 访问 阿里云控制台
  2. 创建API Key
  3. 保存API Key(待会儿会用到)

🚀 第一个MCP应用

让我们创建一个简单的MCP服务器,它可以告诉我们当前的时间。

1. 创建基础MCP服务器

# file: simple_mcp_server.py

import asyncio
import json
from datetime import datetime
from mcp.server import Server
from mcp.types import TextContent, Tool

# 创建MCP服务器实例
server = Server("simple-time-server")

# 定义一个获取当前时间的工具
@server.list_tools()
async def list_tools():
    """列出所有可用的工具"""
    return [
        Tool(
            name="get_current_time",
            description="获取当前的日期和时间",
            inputSchema={
                "type": "object",
                "properties": {
                    "format": {
                        "type": "string",
                        "description": "时间格式,默认为'%Y-%m-%d %H:%M:%S'",
                        "default": "%Y-%m-%d %H:%M:%S"
                    }
                }
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    """执行工具调用"""
    if name == "get_current_time":
        # 获取格式参数,如果没有则使用默认格式
        time_format = arguments.get("format", "%Y-%m-%d %H:%M:%S")
        
        # 获取当前时间
        current_time = datetime.now().strftime(time_format)
        
        return [TextContent(
            type="text",
            text=f"当前时间是: {current_time}"
        )]
    
    raise ValueError(f"未知的工具: {name}")

# 启动服务器的函数
async def main():
    """启动MCP服务器"""
    print("🚀 启动简单时间服务器...")
    print("服务器正在监听连接...")
    
    # 这里我们使用标准输入输出作为传输方式
    # 在实际应用中,你可能会使用WebSocket或HTTP
    async with server.run_stdio():
        await asyncio.Event().wait()

if __name__ == "__main__":
    asyncio.run(main())

2. 测试MCP服务器

创建一个简单的测试客户端:

# file: test_mcp_client.py

import asyncio
import json
from mcp.client import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client

async def test_time_server():
    """测试时间服务器"""
    
    # 配置服务器参数
    server_params = StdioServerParameters(
        command="python",
        args=["simple_mcp_server.py"]
    )
    
    print("🔗 连接到时间服务器...")
    
    # 创建客户端会话
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初始化连接
            await session.initialize()
            
            print("✅ 连接成功!")
            
            # 获取可用工具列表
            tools = await session.list_tools()
            print(f"📋 可用工具: {[tool.name for tool in tools]}")
            
            # 调用获取时间的工具
            result = await session.call_tool("get_current_time", {})
            print(f"⏰ 结果: {result.content[0].text}")
            
            # 使用自定义格式
            result = await session.call_tool("get_current_time", {
                "format": "%Y年%m月%d日 %H时%M分%S秒"
            })
            print(f"⏰ 自定义格式: {result.content[0].text}")

if __name__ == "__main__":
    asyncio.run(test_time_server())

3. 运行测试

# 在终端中运行测试
python test_mcp_client.py

你应该看到类似的输出:

🔗 连接到时间服务器...
✅ 连接成功!
📋 可用工具: ['get_current_time']
⏰ 结果: 当前时间是: 2024-12-10 14:30:25
⏰ 自定义格式: 当前时间是: 20241210143025

🤖 与通义千问集成

现在让我们将MCP服务器与通义千问集成,创建一个真正智能的助手。 这里为什么使用通义千问呢?因为通义千问的模型可以处理各种任务,比如文本生成、图像生成、语音识别等,而MCP服务器则可以提供这些任务的接口。 且使我们国内的AI模型,可以更好的使用。

1. 创建通义千问客户端

# file: qianwen_mcp_client.py

import asyncio
import os
from dashscope import Generation
from mcp.client import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client

class QianWenMCPClient:
    def __init__(self, api_key: str):
        """初始化通义千问MCP客户端"""
        self.api_key = api_key
        os.environ['DASHSCOPE_API_KEY'] = api_key
        self.mcp_sessions = {}
    
    async def connect_mcp_server(self, server_name: str, server_params: StdioServerParameters):
        """连接到MCP服务器"""
        print(f"🔗 连接到 {server_name} 服务器...")
        
        try:
            # 创建服务器连接
            read, write = await stdio_client(server_params).__aenter__()
            session = ClientSession(read, write)
            await session.initialize()
            
            # 获取可用工具
            tools = await session.list_tools()
            
            self.mcp_sessions[server_name] = {
                'session': session,
                'tools': tools
            }
            
            print(f"✅ 成功连接到 {server_name}")
            print(f"📋 可用工具: {[tool.name for tool in tools]}")
            
        except Exception as e:
            print(f"❌ 连接 {server_name} 失败: {e}")
    
    async def call_mcp_tool(self, server_name: str, tool_name: str, arguments: dict = None):
        """调用MCP工具"""
        if server_name not in self.mcp_sessions:
            raise ValueError(f"未连接到服务器: {server_name}")
        
        session = self.mcp_sessions[server_name]['session']
        
        try:
            result = await session.call_tool(tool_name, arguments or {})
            return result.content[0].text if result.content else "无返回内容"
        except Exception as e:
            return f"调用工具失败: {e}"
    
    def chat_with_tools(self, user_message: str, available_tools: list = None):
        """与通义千问对话,支持工具调用"""
        
        # 构建系统提示
        system_prompt = """你是一个智能助手,可以调用外部工具来帮助用户。

可用工具:
- get_current_time: 获取当前时间

当用户询问时间相关信息时,你可以调用相应的工具。
请用自然、友好的语言回答用户的问题。"""
        
        # 调用通义千问
        try:
            response = Generation.call(
                model="qwen-turbo",
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_message}
                ],
                temperature=0.7
            )
            
            if response.status_code == 200:
                return response.output.text
            else:
                return f"调用通义千问失败: {response.message}"
                
        except Exception as e:
            return f"发生错误: {e}"
    
    async def intelligent_chat(self, user_message: str):
        """智能对话,自动判断是否需要调用工具"""
        
        # 简单的关键词检测,判断是否需要调用时间工具
        time_keywords = ['时间', '现在', '几点', '日期', '今天', '当前时间']
        need_time_tool = any(keyword in user_message for keyword in time_keywords)
        
        if need_time_tool and 'simple-time-server' in self.mcp_sessions:
            # 先获取时间信息
            time_info = await self.call_mcp_tool('simple-time-server', 'get_current_time')
            
            # 将时间信息加入上下文
            enhanced_message = f"用户问题: {user_message}\n当前时间信息: {time_info}"
            
            # 调用通义千问生成回答
            response = self.chat_with_tools(enhanced_message)
            return response
        else:
            # 直接调用通义千问
            return self.chat_with_tools(user_message)

# 使用示例
async def main():
    # 请替换为你的API Key
    API_KEY = "your_api_key_here"
    
    if API_KEY == "your_api_key_here":
        print("❌ 请先设置你的通义千问API Key")
        return
    
    # 创建客户端
    client = QianWenMCPClient(API_KEY)
    
    # 连接到时间服务器
    server_params = StdioServerParameters(
        command="python",
        args=["simple_mcp_server.py"]
    )
    
    await client.connect_mcp_server("simple-time-server", server_params)
    
    # 交互式对话
    print("\n🤖 智能助手已启动!输入 'quit' 退出")
    print("你可以询问时间相关问题,或者其他任何问题。")
    
    while True:
        user_input = input("\n👤 你: ")
        if user_input.lower() == 'quit':
            break
        
        response = await client.intelligent_chat(user_input)
        print(f"🤖 助手: {response}")

if __name__ == "__main__":
    asyncio.run(main())

2. 运行智能助手

# 设置API Key(请替换为你的真实API Key)
export DASHSCOPE_API_KEY="your_api_key_here"

# 运行智能助手
python qianwen_mcp_client.py

📁 实战项目:智能文件管理器

让我们创建一个更复杂的MCP应用——智能文件管理器,它可以帮助我们管理文件和文件夹。

1. 文件管理MCP服务器

# file: file_manager_server.py

import asyncio
import os
import json
import shutil
from pathlib import Path
from mcp.server import Server
from mcp.types import TextContent, Tool, Resource

class FileManagerServer:
    def __init__(self):
        self.server = Server("file-manager-server")
        self.setup_handlers()
    
    def setup_handlers(self):
        """设置处理器"""
        
        @self.server.list_tools()
        async def list_tools():
            """列出所有可用的工具"""
            return [
                Tool(
                    name="list_files",
                    description="列出指定目录中的文件和文件夹",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "directory": {
                                "type": "string",
                                "description": "要列出的目录路径,默认为当前目录"
                            },
                            "show_hidden": {
                                "type": "boolean",
                                "description": "是否显示隐藏文件",
                                "default": False
                            }
                        }
                    }
                ),
                Tool(
                    name="create_directory",
                    description="创建新的目录",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "path": {
                                "type": "string",
                                "description": "要创建的目录路径"
                            }
                        },
                        "required": ["path"]
                    }
                ),
                Tool(
                    name="read_file",
                    description="读取文件内容",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "file_path": {
                                "type": "string",
                                "description": "要读取的文件路径"
                            },
                            "encoding": {
                                "type": "string",
                                "description": "文件编码",
                                "default": "utf-8"
                            }
                        },
                        "required": ["file_path"]
                    }
                ),
                Tool(
                    name="write_file",
                    description="写入文件内容",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "file_path": {
                                "type": "string",
                                "description": "要写入的文件路径"
                            },
                            "content": {
                                "type": "string",
                                "description": "要写入的内容"
                            },
                            "encoding": {
                                "type": "string",
                                "description": "文件编码",
                                "default": "utf-8"
                            }
                        },
                        "required": ["file_path", "content"]
                    }
                ),
                Tool(
                    name="delete_file",
                    description="删除文件或目录",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "path": {
                                "type": "string",
                                "description": "要删除的文件或目录路径"
                            }
                        },
                        "required": ["path"]
                    }
                ),
                Tool(
                    name="get_file_info",
                    description="获取文件或目录的详细信息",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "path": {
                                "type": "string",
                                "description": "文件或目录路径"
                            }
                        },
                        "required": ["path"]
                    }
                ),
                Tool(
                    name="search_files",
                    description="搜索文件",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "directory": {
                                "type": "string",
                                "description": "搜索目录",
                                "default": "."
                            },
                            "pattern": {
                                "type": "string",
                                "description": "搜索模式(支持通配符)"
                            },
                            "recursive": {
                                "type": "boolean",
                                "description": "是否递归搜索",
                                "default": True
                            }
                        },
                        "required": ["pattern"]
                    }
                )
            ]
        
        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict):
            """执行工具调用"""
            try:
                if name == "list_files":
                    return await self.list_files(arguments)
                elif name == "create_directory":
                    return await self.create_directory(arguments)
                elif name == "read_file":
                    return await self.read_file(arguments)
                elif name == "write_file":
                    return await self.write_file(arguments)
                elif name == "delete_file":
                    return await self.delete_file(arguments)
                elif name == "get_file_info":
                    return await self.get_file_info(arguments)
                elif name == "search_files":
                    return await self.search_files(arguments)
                else:
                    raise ValueError(f"未知的工具: {name}")
            except Exception as e:
                return [TextContent(
                    type="text",
                    text=f"执行工具 {name} 时发生错误: {str(e)}"
                )]
    
    async def list_files(self, arguments: dict):
        """列出文件和文件夹"""
        directory = arguments.get("directory", ".")
        show_hidden = arguments.get("show_hidden", False)
        
        try:
            path = Path(directory)
            if not path.exists():
                return [TextContent(
                    type="text",
                    text=f"目录不存在: {directory}"
                )]
            
            if not path.is_dir():
                return [TextContent(
                    type="text",
                    text=f"路径不是目录: {directory}"
                )]
            
            items = []
            for item in path.iterdir():
                if not show_hidden and item.name.startswith('.'):
                    continue
                
                item_type = "📁" if item.is_dir() else "📄"
                size = ""
                if item.is_file():
                    size = f" ({self.format_size(item.stat().st_size)})"
                
                items.append(f"{item_type} {item.name}{size}")
            
            if not items:
                result = f"目录 {directory} 为空"
            else:
                result = f"目录 {directory} 内容:\n" + "\n".join(items)
            
            return [TextContent(type="text", text=result)]
            
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"列出目录失败: {str(e)}"
            )]
    
    async def create_directory(self, arguments: dict):
        """创建目录"""
        path = arguments["path"]
        
        try:
            Path(path).mkdir(parents=True, exist_ok=True)
            return [TextContent(
                type="text",
                text=f"成功创建目录: {path}"
            )]
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"创建目录失败: {str(e)}"
            )]
    
    async def read_file(self, arguments: dict):
        """读取文件"""
        file_path = arguments["file_path"]
        encoding = arguments.get("encoding", "utf-8")
        
        try:
            path = Path(file_path)
            if not path.exists():
                return [TextContent(
                    type="text",
                    text=f"文件不存在: {file_path}"
                )]
            
            if not path.is_file():
                return [TextContent(
                    type="text",
                    text=f"路径不是文件: {file_path}"
                )]
            
            with open(path, 'r', encoding=encoding) as f:
                content = f.read()
            
            return [TextContent(
                type="text",
                text=f"文件 {file_path} 的内容:\n{content}"
            )]
            
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"读取文件失败: {str(e)}"
            )]
    
    async def write_file(self, arguments: dict):
        """写入文件"""
        file_path = arguments["file_path"]
        content = arguments["content"]
        encoding = arguments.get("encoding", "utf-8")
        
        try:
            path = Path(file_path)
            # 确保父目录存在
            path.parent.mkdir(parents=True, exist_ok=True)
            
            with open(path, 'w', encoding=encoding) as f:
                f.write(content)
            
            return [TextContent(
                type="text",
                text=f"成功写入文件: {file_path}"
            )]
            
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"写入文件失败: {str(e)}"
            )]
    
    async def delete_file(self, arguments: dict):
        """删除文件或目录"""
        path = arguments["path"]
        
        try:
            path_obj = Path(path)
            if not path_obj.exists():
                return [TextContent(
                    type="text",
                    text=f"路径不存在: {path}"
                )]
            
            if path_obj.is_file():
                path_obj.unlink()
                return [TextContent(
                    type="text",
                    text=f"成功删除文件: {path}"
                )]
            elif path_obj.is_dir():
                shutil.rmtree(path_obj)
                return [TextContent(
                    type="text",
                    text=f"成功删除目录: {path}"
                )]
            
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"删除失败: {str(e)}"
            )]
    
    async def get_file_info(self, arguments: dict):
        """获取文件信息"""
        path = arguments["path"]
        
        try:
            path_obj = Path(path)
            if not path_obj.exists():
                return [TextContent(
                    type="text",
                    text=f"路径不存在: {path}"
                )]
            
            stat = path_obj.stat()
            info = [
                f"路径: {path}",
                f"类型: {'目录' if path_obj.is_dir() else '文件'}",
                f"大小: {self.format_size(stat.st_size)}",
                f"创建时间: {self.format_time(stat.st_ctime)}",
                f"修改时间: {self.format_time(stat.st_mtime)}",
                f"访问时间: {self.format_time(stat.st_atime)}"
            ]
            
            return [TextContent(
                type="text",
                text="\n".join(info)
            )]
            
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"获取文件信息失败: {str(e)}"
            )]
    
    async def search_files(self, arguments: dict):
        """搜索文件"""
        directory = arguments.get("directory", ".")
        pattern = arguments["pattern"]
        recursive = arguments.get("recursive", True)
        
        try:
            path = Path(directory)
            if not path.exists():
                return [TextContent(
                    type="text",
                    text=f"目录不存在: {directory}"
                )]
            
            if recursive:
                matches = list(path.rglob(pattern))
            else:
                matches = list(path.glob(pattern))
            
            if not matches:
                return [TextContent(
                    type="text",
                    text=f"未找到匹配 '{pattern}' 的文件"
                )]
            
            results = []
            for match in matches:
                match_type = "📁" if match.is_dir() else "📄"
                size = ""
                if match.is_file():
                    size = f" ({self.format_size(match.stat().st_size)})"
                results.append(f"{match_type} {match}{size}")
            
            return [TextContent(
                type="text",
                text=f"找到 {len(matches)} 个匹配项:\n" + "\n".join(results)
            )]
            
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"搜索失败: {str(e)}"
            )]
    
    def format_size(self, size: int) -> str:
        """格式化文件大小"""
        for unit in ['B', 'KB', 'MB', 'GB']:
            if size < 1024.0:
                return f"{size:.1f} {unit}"
            size /= 1024.0
        return f"{size:.1f} TB"
    
    def format_time(self, timestamp: float) -> str:
        """格式化时间戳"""
        import datetime
        return datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
    
    async def run(self):
        """运行服务器"""
        print("🚀 启动文件管理服务器...")
        print("服务器正在监听连接...")
        
        async with self.server.run_stdio():
            await asyncio.Event().wait()

# 启动服务器
if __name__ == "__main__":
    server = FileManagerServer()
    asyncio.run(server.run())

2. 增强的智能助手

现在让我们创建一个更智能的助手,它可以同时使用时间服务器和文件管理服务器:

# file: enhanced_assistant.py

import asyncio
import os
import re
from dashscope import Generation
from mcp.client import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client

class EnhancedAssistant:
    def __init__(self, api_key: str):
        """初始化增强助手"""
        self.api_key = api_key
        os.environ['DASHSCOPE_API_KEY'] = api_key
        self.mcp_sessions = {}
        self.conversation_history = []
    
    async def connect_servers(self):
        """连接到所有MCP服务器"""
        servers = {
            "time-server": {
                "command": "python",
                "args": ["simple_mcp_server.py"]
            },
            "file-manager": {
                "command": "python", 
                "args": ["file_manager_server.py"]
            }
        }
        
        for server_name, config in servers.items():
            try:
                server_params = StdioServerParameters(
                    command=config["command"],
                    args=config["args"]
                )
                
                print(f"🔗 连接到 {server_name}...")
                
                # 创建连接
                read, write = await stdio_client(server_params).__aenter__()
                session = ClientSession(read, write)
                await session.initialize()
                
                # 获取工具列表
                tools = await session.list_tools()
                
                self.mcp_sessions[server_name] = {
                    'session': session,
                    'tools': {tool.name: tool for tool in tools}
                }
                
                print(f"✅ 成功连接 {server_name}")
                print(f"📋 可用工具: {list(self.mcp_sessions[server_name]['tools'].keys())}")
                
            except Exception as e:
                print(f"❌ 连接 {server_name} 失败: {e}")
    
    async def call_tool(self, server_name: str, tool_name: str, arguments: dict = None):
        """调用MCP工具"""
        if server_name not in self.mcp_sessions:
            return f"❌ 未连接到服务器: {server_name}"
        
        session = self.mcp_sessions[server_name]['session']
        
        try:
            result = await session.call_tool(tool_name, arguments or {})
            return result.content[0].text if result.content else "无返回内容"
        except Exception as e:
            return f"❌ 调用工具失败: {e}"
    
    def analyze_user_intent(self, message: str):
        """分析用户意图,判断需要调用哪些工具"""
        intent = {
            'needs_time': False,
            'needs_file_ops': False,
            'file_operations': []
        }
        
        # 时间相关关键词
        time_keywords = ['时间', '现在', '几点', '日期', '今天', '当前时间']
        if any(keyword in message for keyword in time_keywords):
            intent['needs_time'] = True
        
        # 文件操作相关关键词
        file_keywords = {
            'list': ['列出', '查看', '显示', '目录', '文件夹', '文件'],
            'read': ['读取', '打开', '查看内容', '显示内容'],
            'write': ['写入', '创建', '保存', '新建'],
            'delete': ['删除', '移除'],
            'search': ['搜索', '查找', '寻找'],
            'info': ['信息', '详情', '属性']
        }
        
        for operation, keywords in file_keywords.items():
            if any(keyword in message for keyword in keywords):
                intent['needs_file_ops'] = True
                intent['file_operations'].append(operation)
        
        return intent
    
    def extract_file_params(self, message: str, operation: str):
        """从用户消息中提取文件操作参数"""
        params = {}
        
        # 提取文件路径(简单的正则匹配)
        path_patterns = [
            r'["\']([^"\']+)["\']',  # 引号中的路径
            r'(\S+\.\w+)',           # 带扩展名的文件
            r'(/\S+)',               # 绝对路径
            r'(\.{1,2}/\S+)',        # 相对路径
        ]
        
        for pattern in path_patterns:
            matches = re.findall(pattern, message)
            if matches:
                params['path'] = matches[0]
                break
        
        # 根据操作类型提取特定参数
        if operation == 'write':
            # 尝试提取要写入的内容
            content_patterns = [
                r'内容[是为]?["\']([^"\']+)["\']',
                r'写入["\']([^"\']+)["\']',
                r'内容:(.+)',
            ]
            
            for pattern in content_patterns:
                matches = re.findall(pattern, message)
                if matches:
                    params['content'] = matches[0]
                    break
        
        elif operation == 'search':
            # 提取搜索模式
            search_patterns = [
                r'搜索["\']([^"\']+)["\']',
                r'查找["\']([^"\']+)["\']',
                r'找["\']([^"\']+)["\']',
            ]
            
            for pattern in search_patterns:
                matches = re.findall(pattern, message)
                if matches:
                    params['pattern'] = matches[0]
                    break
        
        return params
    
    async def execute_file_operations(self, message: str, operations: list):
        """执行文件操作"""
        results = []
        
        for operation in operations:
            params = self.extract_file_params(message, operation)
            
            if operation == 'list':
                directory = params.get('path', '.')
                result = await self.call_tool('file-manager', 'list_files', {'directory': directory})
                results.append(f"📁 目录列表:\n{result}")
            
            elif operation == 'read':
                if 'path' in params:
                    result = await self.call_tool('file-manager', 'read_file', {'file_path': params['path']})
                    results.append(f"📄 文件内容:\n{result}")
                else:
                    results.append("❌ 请指定要读取的文件路径")
            
            elif operation == 'write':
                if 'path' in params and 'content' in params:
                    result = await self.call_tool('file-manager', 'write_file', {
                        'file_path': params['path'],
                        'content': params['content']
                    })
                    results.append(f"✍️ 写入结果:\n{result}")
                else:
                    results.append("❌ 请指定文件路径和内容")
            
            elif operation == 'delete':
                if 'path' in params:
                    result = await self.call_tool('file-manager', 'delete_file', {'path': params['path']})
                    results.append(f"🗑️ 删除结果:\n{result}")
                else:
                    results.append("❌ 请指定要删除的文件路径")
            
            elif operation == 'search':
                if 'pattern' in params:
                    result = await self.call_tool('file-manager', 'search_files', {'pattern': params['pattern']})
                    results.append(f"🔍 搜索结果:\n{result}")
                else:
                    results.append("❌ 请指定搜索模式")
            
            elif operation == 'info':
                if 'path' in params:
                    result = await self.call_tool('file-manager', 'get_file_info', {'path': params['path']})
                    results.append(f"ℹ️ 文件信息:\n{result}")
                else:
                    results.append("❌ 请指定文件路径")
        
        return "\n\n".join(results)
    
    async def smart_response(self, user_message: str):
        """智能响应用户消息"""
        # 分析用户意图
        intent = self.analyze_user_intent(user_message)
        
        # 收集上下文信息
        context_info = []
        
        # 获取时间信息
        if intent['needs_time']:
            time_info = await self.call_tool('time-server', 'get_current_time')
            context_info.append(f"当前时间: {time_info}")
        
        # 执行文件操作
        if intent['needs_file_ops']:
            file_results = await self.execute_file_operations(user_message, intent['file_operations'])
            context_info.append(f"文件操作结果: {file_results}")
        
        # 构建增强的消息
        if context_info:
            enhanced_message = f"""用户问题: {user_message}

相关信息:
{chr(10).join(context_info)}

请根据上述信息,用自然、友好的语言回答用户的问题。"""
        else:
            enhanced_message = user_message
        
        # 调用通义千问生成回答
        try:
            response = Generation.call(
                model="qwen-turbo",
                messages=[
                    {"role": "system", "content": """你是一个智能助手,可以帮助用户管理文件和获取信息。
请用自然、友好的语言回答用户的问题。如果有相关的操作结果,请整合到你的回答中。"""},
                    {"role": "user", "content": enhanced_message}
                ],
                temperature=0.7
            )
            
            if response.status_code == 200:
                return response.output.text
            else:
                return f"❌ 调用通义千问失败: {response.message}"
                
        except Exception as e:
            return f"❌ 发生错误: {e}"
    
    async def start_interactive_session(self):
        """启动交互式会话"""
        print("\n🤖 增强智能助手已启动!")
        print("我可以帮你:")
        print("  ⏰ 获取当前时间")
        print("  📁 管理文件和文件夹")
        print("  📄 读写文件内容")
        print("  🔍 搜索文件")
        print("  ℹ️ 获取文件信息")
        print("\n输入 'quit' 或 'exit' 退出")
        print("输入 'help' 获取帮助")
        
        while True:
            try:
                user_input = input("\n👤 你: ").strip()
                
                if user_input.lower() in ['quit', 'exit']:
                    print("👋 再见!")
                    break
                
                if user_input.lower() == 'help':
                    self.show_help()
                    continue
                
                if not user_input:
                    continue
                
                print("🤔 思考中...")
                response = await self.smart_response(user_input)
                print(f"🤖 助手: {response}")
                
            except KeyboardInterrupt:
                print("\n👋 再见!")
                break
            except Exception as e:
                print(f"❌ 发生错误: {e}")
    
    def show_help(self):
        """显示帮助信息"""
        help_text = """
📚 使用帮助:

⏰ 时间相关:
  - "现在几点了?"
  - "今天是什么日期?"

📁 文件操作:
  - "列出当前目录的文件"
  - "查看 /path/to/directory 目录"
  - "读取 example.txt 文件"
  - "创建文件 test.txt,内容是 'Hello World'"
  - "删除 old_file.txt"
  - "搜索 '*.py' 文件"
  - "获取 document.pdf 的信息"

💡 提示:
  - 文件路径可以用引号括起来
  - 支持相对路径和绝对路径
  - 可以同时询问多个问题
        """
        print(help_text)

# 主程序
async def main():
    # 请替换为你的API Key
    API_KEY = "your_api_key_here"
    
    if API_KEY == "your_api_key_here":
        print("❌ 请先设置你的通义千问API Key")
        print("在代码中找到 'your_api_key_here' 并替换为你的真实API Key")
        return
    
    # 创建助手
    assistant = EnhancedAssistant(API_KEY)
    
    # 连接到MCP服务器
    await assistant.connect_servers()
    
    # 启动交互式会话
    await assistant.start_interactive_session()

if __name__ == "__main__":
    asyncio.run(main())

3. 测试智能文件管理器

让我们创建一个测试脚本来验证我们的文件管理器:

# file: test_file_manager.py

import asyncio
import os
import tempfile
from pathlib import Path
from mcp.client import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client

async def test_file_manager():
    """测试文件管理器的所有功能"""
    
    # 创建临时测试目录
    with tempfile.TemporaryDirectory() as temp_dir:
        print(f"🧪 在临时目录中测试: {temp_dir}")
        
        # 连接到文件管理服务器
        server_params = StdioServerParameters(
            command="python",
            args=["file_manager_server.py"]
        )
        
        print("🔗 连接到文件管理服务器...")
        
        async with stdio_client(server_params) as (read, write):
            async with ClientSession(read, write) as session:
                await session.initialize()
                
                print("✅ 连接成功!")
                
                # 测试1: 列出目录
                print("\n📋 测试1: 列出目录")
                result = await session.call_tool("list_files", {"directory": temp_dir})
                print(f"结果: {result.content[0].text}")
                
                # 测试2: 创建目录
                print("\n📁 测试2: 创建目录")
                test_dir = os.path.join(temp_dir, "test_folder")
                result = await session.call_tool("create_directory", {"path": test_dir})
                print(f"结果: {result.content[0].text}")
                
                # 测试3: 写入文件
                print("\n✍️ 测试3: 写入文件")
                test_file = os.path.join(test_dir, "test.txt")
                result = await session.call_tool("write_file", {
                    "file_path": test_file,
                    "content": "这是一个测试文件!\n包含多行内容。"
                })
                print(f"结果: {result.content[0].text}")
                
                # 测试4: 读取文件
                print("\n📄 测试4: 读取文件")
                result = await session.call_tool("read_file", {"file_path": test_file})
                print(f"结果: {result.content[0].text}")
                
                # 测试5: 获取文件信息
                print("\nℹ️ 测试5: 获取文件信息")
                result = await session.call_tool("get_file_info", {"path": test_file})
                print(f"结果: {result.content[0].text}")
                
                # 测试6: 创建更多测试文件
                print("\n📝 测试6: 创建更多测试文件")
                for i in range(3):
                    file_path = os.path.join(test_dir, f"file_{i}.txt")
                    await session.call_tool("write_file", {
                        "file_path": file_path,
                        "content": f"这是第{i}个测试文件"
                    })
                    print(f"创建文件: {file_path}")
                
                # 测试7: 再次列出目录
                print("\n📋 测试7: 再次列出目录")
                result = await session.call_tool("list_files", {"directory": test_dir})
                print(f"结果: {result.content[0].text}")
                
                # 测试8: 搜索文件
                print("\n🔍 测试8: 搜索文件")
                result = await session.call_tool("search_files", {
                    "directory": temp_dir,
                    "pattern": "*.txt"
                })
                print(f"结果: {result.content[0].text}")
                
                # 测试9: 删除文件
                print("\n🗑️ 测试9: 删除文件")
                result = await session.call_tool("delete_file", {
                    "path": os.path.join(test_dir, "file_1.txt")
                })
                print(f"结果: {result.content[0].text}")
                
                # 测试10: 最终列出目录
                print("\n📋 测试10: 最终列出目录")
                result = await session.call_tool("list_files", {"directory": test_dir})
                print(f"结果: {result.content[0].text}")
                
                print("\n✅ 所有测试完成!")

if __name__ == "__main__":
    asyncio.run(test_file_manager())

4. 系统架构图

graph TB
    subgraph "用户界面层"
        A[用户输入] --> B[增强智能助手]
    end
    
    subgraph "AI处理层"
        B --> C[意图分析]
        C --> D[通义千问LLM]
        D --> E[响应生成]
    end
    
    subgraph "MCP协议层"
        B --> F[MCP客户端]
        F --> G[时间服务器]
        F --> H[文件管理服务器]
    end
    
    subgraph "工具执行层"
        G --> I[获取时间]
        H --> J[文件操作]
        J --> K[读取文件]
        J --> L[写入文件]
        J --> M[搜索文件]
        J --> N[删除文件]
        J --> O[获取信息]
    end
    
    subgraph "系统资源"
        I --> P[系统时间]
        K --> Q[文件系统]
        L --> Q
        M --> Q
        N --> Q
        O --> Q
    end
    
    E --> R[用户反馈]
    
    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#f3e5f5
    style D fill:#e8f5e8
    style F fill:#fff8e1
    style G fill:#fce4ec
    style H fill:#fce4ec

🎯 高级功能和最佳实践

1. 错误处理和日志记录

# file: advanced_mcp_server.py

import asyncio
import logging
from datetime import datetime
from mcp.server import Server
from mcp.types import TextContent, Tool

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('mcp_server.log'),
        logging.StreamHandler()
    ]
)

class AdvancedMCPServer:
    def __init__(self, server_name: str):
        self.server = Server(server_name)
        self.logger = logging.getLogger(server_name)
        self.request_count = 0
        self.setup_handlers()
    
    def setup_handlers(self):
        """设置处理器"""
        
        @self.server.list_tools()
        async def list_tools():
            """列出工具时记录日志"""
            self.logger.info("收到工具列表请求")
            return [
                Tool(
                    name="safe_operation",
                    description="安全的示例操作",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "data": {
                                "type": "string",
                                "description": "输入数据"
                            }
                        },
                        "required": ["data"]
                    }
                )
            ]
        
        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict):
            """带错误处理的工具调用"""
            self.request_count += 1
            request_id = f"req_{self.request_count}_{datetime.now().timestamp()}"
            
            self.logger.info(f"[{request_id}] 收到工具调用: {name}")
            
            try:
                # 验证工具名称
                if name not in ["safe_operation"]:
                    raise ValueError(f"未知工具: {name}")
                
                # 验证参数
                if not arguments or "data" not in arguments:
                    raise ValueError("缺少必需参数: data")
                
                # 执行操作
                result = await self.execute_safe_operation(arguments["data"])
                
                self.logger.info(f"[{request_id}] 工具调用成功")
                return [TextContent(type="text", text=result)]
                
            except Exception as e:
                self.logger.error(f"[{request_id}] 工具调用失败: {str(e)}")
                return [TextContent(
                    type="text",
                    text=f"操作失败: {str(e)}"
                )]
    
    async def execute_safe_operation(self, data: str) -> str:
        """安全的操作示例"""
        # 输入验证
        if not isinstance(data, str):
            raise ValueError("数据必须是字符串")
        
        if len(data) > 1000:
            raise ValueError("数据长度不能超过1000字符")
        
        # 模拟处理
        await asyncio.sleep(0.1)
        
        return f"处理完成: {data[:50]}{'...' if len(data) > 50 else ''}"
    
    async def run(self):
        """运行服务器"""
        self.logger.info("启动MCP服务器")
        
        try:
            async with self.server.run_stdio():
                await asyncio.Event().wait()
        except Exception as e:
            self.logger.error(f"服务器运行错误: {e}")
            raise

# 使用示例
if __name__ == "__main__":
    server = AdvancedMCPServer("advanced-server")
    asyncio.run(server.run())

2. 配置管理

# file: config_manager.py

import json
import os
from pathlib import Path
from typing import Dict, Any

class ConfigManager:
    """配置管理器"""
    
    def __init__(self, config_file: str = "mcp_config.json"):
        self.config_file = Path(config_file)
        self.config = self.load_config()
    
    def load_config(self) -> Dict[str, Any]:
        """加载配置文件"""
        if self.config_file.exists():
            try:
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except Exception as e:
                print(f"加载配置失败: {e}")
                return self.get_default_config()
        else:
            config = self.get_default_config()
            self.save_config(config)
            return config
    
    def get_default_config(self) -> Dict[str, Any]:
        """获取默认配置"""
        return {
            "api_keys": {
                "dashscope": "your_api_key_here"
            },
            "servers": {
                "time-server": {
                    "enabled": True,
                    "command": "python",
                    "args": ["simple_mcp_server.py"]
                },
                "file-manager": {
                    "enabled": True,
                    "command": "python",
                    "args": ["file_manager_server.py"]
                }
            },
            "security": {
                "max_file_size": 10485760,  # 10MB
                "allowed_extensions": [".txt", ".json", ".csv", ".md"],
                "restricted_paths": ["/etc", "/sys", "/proc"]
            },
            "logging": {
                "level": "INFO",
                "file": "mcp.log"
            }
        }
    
    def save_config(self, config: Dict[str, Any] = None):
        """保存配置文件"""
        if config is None:
            config = self.config
        
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, indent=2, ensure_ascii=False)
        except Exception as e:
            print(f"保存配置失败: {e}")
    
    def get(self, key: str, default: Any = None) -> Any:
        """获取配置值"""
        keys = key.split('.')
        value = self.config
        
        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return default
        
        return value
    
    def set(self, key: str, value: Any):
        """设置配置值"""
        keys = key.split('.')
        config = self.config
        
        for k in keys[:-1]:
            if k not in config:
                config[k] = {}
            config = config[k]
        
        config[keys[-1]] = value
        self.save_config()

3. 安全性增强

# file: security_utils.py

import os
import hashlib
from pathlib import Path
from typing import List, Optional

class SecurityManager:
    """安全管理器"""
    
    def __init__(self, config_manager):
        self.config = config_manager
        self.max_file_size = self.config.get("security.max_file_size", 10485760)
        self.allowed_extensions = self.config.get("security.allowed_extensions", [])
        self.restricted_paths = self.config.get("security.restricted_paths", [])
    
    def validate_file_path(self, file_path: str) -> bool:
        """验证文件路径是否安全"""
        try:
            # 规范化路径
            path = Path(file_path).resolve()
            # 检查是否在受限目录下
            for restricted in self.restricted_paths:
                if str(path).startswith(str(Path(restricted).resolve())):
                    return False
            # 检查文件扩展名
            if self.allowed_extensions:
                if path.suffix not in self.allowed_extensions:
                    return False
            return True
        except Exception as e:
            print(f"路径校验异常: {e}")
            return False

    def validate_file_size(self, file_path: str) -> bool:
        """校验文件大小是否超限"""
        try:
            path = Path(file_path)
            if not path.exists() or not path.is_file():
                return False
            if path.stat().st_size > self.max_file_size:
                return False
            return True
        except Exception as e:
            print(f"文件大小校验异常: {e}")
            return False

    def hash_file(self, file_path: str, algo: str = "sha256") -> Optional[str]:
        """计算文件哈希值"""
        try:
            h = hashlib.new(algo)
            with open(file_path, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    h.update(chunk)
            return h.hexdigest()
        except Exception as e:
            print(f"哈希计算失败: {e}")
            return None

    def is_safe_to_write(self, file_path: str) -> bool:
        """判断写入路径是否安全(不在受限目录、扩展名允许)"""
        return self.validate_file_path(file_path)

# 使用示例
if __name__ == "__main__":
    from config_manager import ConfigManager
    config = ConfigManager()
    sm = SecurityManager(config)
    test_path = "/tmp/test.txt"
    print("路径安全:", sm.validate_file_path(test_path))
    print("可写入:", sm.is_safe_to_write(test_path))



```python
# file: secure_mcp_server.py
import asyncio
import hashlib
import time
from pathlib import Path
from typing import Set, Dict, Any
from mcp.server import Server
from mcp.types import TextContent, Tool

class SecureMCPServer:
    """安全的MCP服务器"""
    
    def __init__(self, server_name: str, config_manager):
        self.server = Server(server_name)
        self.config = config_manager
        self.rate_limiter = RateLimiter()
        self.file_validator = FileValidator(config_manager)
        self.setup_handlers()
    
    def setup_handlers(self):
        """设置安全的处理器"""
        
        @self.server.list_tools()
        async def list_tools():
            return [
                Tool(
                    name="secure_file_read",
                    description="安全地读取文件",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "file_path": {
                                "type": "string",
                                "description": "文件路径"
                            }
                        },
                        "required": ["file_path"]
                    }
                )
            ]
        
        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict):
            """安全的工具调用"""
            try:
                # 速率限制检查
                if not self.rate_limiter.allow_request():
                    return [TextContent(
                        type="text",
                        text="请求过于频繁,请稍后再试"
                    )]
                
                if name == "secure_file_read":
                    return await self.secure_file_read(arguments)
                
                return [TextContent(
                    type="text",
                    text=f"未知工具: {name}"
                )]
                
            except Exception as e:
                return [TextContent(
                    type="text",
                    text=f"操作失败: {str(e)}"
                )]
    
    async def secure_file_read(self, arguments: dict):
        """安全地读取文件"""
        file_path = arguments.get("file_path")
        
        if not file_path:
            return [TextContent(
                type="text",
                text="缺少文件路径参数"
            )]
        
        # 安全验证
        if not self.file_validator.is_safe_path(file_path):
            return [TextContent(
                type="text",
                text="文件路径不安全或被禁止访问"
            )]
        
        if not self.file_validator.is_allowed_extension(file_path):
            return [TextContent(
                type="text",
                text="文件类型不被允许"
            )]
        
        try:
            path = Path(file_path)
            if not path.exists():
                return [TextContent(
                    type="text",
                    text="文件不存在"
                )]
            
            # 检查文件大小
            if path.stat().st_size > self.config.get("security.max_file_size", 1024*1024):
                return [TextContent(
                    type="text",
                    text="文件过大"
                )]
            
            with open(path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            return [TextContent(
                type="text",
                text=f"文件内容:\n{content}"
            )]
            
        except Exception as e:
            return [TextContent(
                type="text",
                text=f"读取文件失败: {str(e)}"
            )]

class RateLimiter:
    """速率限制器"""
    
    def __init__(self, max_requests: int = 60, window_seconds: int = 60):
        self.max_requests = max_requests
        self.window_seconds = window_seconds
        self.requests = []
    
    def allow_request(self) -> bool:
        """检查是否允许请求"""
        now = time.time()
        
        # 清理过期请求
        self.requests = [req_time for req_time in self.requests 
                        if now - req_time < self.window_seconds]
        
        if len(self.requests) >= self.max_requests:
            return False
        
        self.requests.append(now)
        return True

class FileValidator:
    """文件验证器"""
    
    def __init__(self, config_manager):
        self.config = config_manager
    
    def is_safe_path(self, path: str) -> bool:
        """检查路径是否安全"""
        try:
            # 规范化路径
            normalized = Path(path).resolve()
            
            # 检查是否在禁止访问的路径中
            restricted_paths = self.config.get("security.restricted_paths", [])
            for restricted in restricted_paths:
                if str(normalized).startswith(restricted):
                    return False
            
            # 检查路径遍历攻击
            if ".." in path or path.startswith("/"):
                return False
            
            return True
            
        except Exception:
            return False
    
    def is_allowed_extension(self, path: str) -> bool:
        """检查文件扩展名是否被允许"""
        allowed_extensions = self.config.get("security.allowed_extensions", [])
        
        if not allowed_extensions:
            return True
        
        extension = Path(path).suffix.lower()
        return extension in allowed_extensions

## 🔄 异步处理和性能优化

### 1. 异步任务队列

```python
# file: async_task_queue.py

import asyncio
from typing import Any, Callable, Dict, List
from dataclasses import dataclass
from enum import Enum

class TaskStatus(Enum):
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"

@dataclass
class Task:
    id: str
    func: Callable
    args: tuple
    kwargs: dict
    status: TaskStatus = TaskStatus.PENDING
    result: Any = None
    error: str = None
    created_at: float = None
    started_at: float = None
    completed_at: float = None

class AsyncTaskQueue:
    """异步任务队列"""
    
    def __init__(self, max_workers: int = 5):
        self.max_workers = max_workers
        self.tasks: Dict[str, Task] = {}
        self.queue = asyncio.Queue()
        self.workers = []
        self.running = False
    
    async def start(self):
        """启动任务队列"""
        if self.running:
            return
        
        self.running = True
        self.workers = [
            asyncio.create_task(self._worker(f"worker-{i}"))
            for i in range(self.max_workers)
        ]
        
        print(f"📋 任务队列已启动,工作线程数: {self.max_workers}")
    
    async def stop(self):
        """停止任务队列"""
        self.running = False
        
        # 等待所有工作线程完成
        for worker in self.workers:
            worker.cancel()
        
        await asyncio.gather(*self.workers, return_exceptions=True)
        print("📋 任务队列已停止")
    
    async def add_task(self, task_id: str, func: Callable, *args, **kwargs) -> str:
        """添加任务"""
        task = Task(
            id=task_id,
            func=func,
            args=args,
            kwargs=kwargs,
            created_at=asyncio.get_event_loop().time()
        )
        
        self.tasks[task_id] = task
        await self.queue.put(task)
        
        return task_id
    
    async def get_task_status(self, task_id: str) -> Dict[str, Any]:
        """获取任务状态"""
        task = self.tasks.get(task_id)
        if not task:
            return {"error": "任务不存在"}
        
        return {
            "id": task.id,
            "status": task.status.value,
            "result": task.result,
            "error": task.error,
            "created_at": task.created_at,
            "started_at": task.started_at,
            "completed_at": task.completed_at
        }
    
    async def _worker(self, worker_name: str):
        """工作线程"""
        while self.running:
            try:
                # 等待任务
                task = await asyncio.wait_for(self.queue.get(), timeout=1.0)
                
                print(f"🔄 {worker_name} 开始执行任务: {task.id}")
                
                # 更新任务状态
                task.status = TaskStatus.RUNNING
                task.started_at = asyncio.get_event_loop().time()
                
                try:
                    # 执行任务
                    if asyncio.iscoroutinefunction(task.func):
                        result = await task.func(*task.args, **task.kwargs)
                    else:
                        result = task.func(*task.args, **task.kwargs)
                    
                    # 任务完成
                    task.status = TaskStatus.COMPLETED
                    task.result = result
                    task.completed_at = asyncio.get_event_loop().time()
                    
                    print(f"✅ {worker_name} 完成任务: {task.id}")
                    
                except Exception as e:
                    # 任务失败
                    task.status = TaskStatus.FAILED
                    task.error = str(e)
                    task.completed_at = asyncio.get_event_loop().time()
                    
                    print(f"❌ {worker_name} 任务失败: {task.id}, 错误: {e}")
                
                finally:
                    self.queue.task_done()
                    
            except asyncio.TimeoutError:
                # 超时,继续等待
                continue
            except Exception as e:
                print(f"❌ {worker_name} 工作线程错误: {e}")

# 示例使用
async def example_long_task(data: str, delay: int = 2):
    """示例长时间运行的任务"""
    await asyncio.sleep(delay)
    return f"处理完成: {data}"

async def test_task_queue():
    """测试任务队列"""
    queue = AsyncTaskQueue(max_workers=3)
    await queue.start()
    
    try:
        # 添加一些任务
        task_ids = []
        for i in range(5):
            task_id = f"task-{i}"
            await queue.add_task(
                task_id,
                example_long_task,
                f"数据-{i}",
                delay=i+1
            )
            task_ids.append(task_id)
        
        # 等待所有任务完成
        while True:
            all_completed = True
            for task_id in task_ids:
                status = await queue.get_task_status(task_id)
                if status["status"] not in ["completed", "failed"]:
                    all_completed = False
                    break
            
            if all_completed:
                break
            
            await asyncio.sleep(0.5)
        
        # 显示结果
        for task_id in task_ids:
            status = await queue.get_task_status(task_id)
            print(f"任务 {task_id}: {status}")
            
    finally:
        await queue.stop()

if __name__ == "__main__":
    asyncio.run(test_task_queue())

4. 缓存机制

# file: cache_manager.py

import asyncio
import json
import time
from typing import Any, Dict, Optional
from dataclasses import dataclass, asdict

@dataclass
class CacheEntry:
    """缓存条目"""
    value: Any
    created_at: float
    expires_at: float
    hit_count: int = 0
    last_access: float = None

class CacheManager:
    """缓存管理器"""
    
    def __init__(self, default_ttl: int = 300, max_size: int = 1000):
        self.default_ttl = default_ttl  # 默认过期时间(秒)
        self.max_size = max_size        # 最大缓存大小
        self.cache: Dict[str, CacheEntry] = {}
        self.stats = {
            "hits": 0,
            "misses": 0,
            "evictions": 0
        }
    
    async def get(self, key: str) -> Optional[Any]:
        """获取缓存值"""
        now = time.time()
        
        if key not in self.cache:
            self.stats["misses"] += 1
            return None
        
        entry = self.cache[key]
        
        # 检查是否过期
        if now > entry.expires_at:
            del self.cache[key]
            self.stats["misses"] += 1
            return None
        
        # 更新访问信息
        entry.hit_count += 1
        entry.last_access = now
        self.stats["hits"] += 1
        
        return entry.value
    
    async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """设置缓存值"""
        if ttl is None:
            ttl = self.default_ttl
        
        now = time.time()
        
        # 检查缓存大小限制
        if len(self.cache) >= self.max_size and key not in self.cache:
            await self._evict_lru()
        
        # 创建缓存条目
        entry = CacheEntry(
            value=value,
            created_at=now,
            expires_at=now + ttl,
            last_access=now
        )
        
        self.cache[key] = entry
        return True
    
    async def delete(self, key: str) -> bool:
        """删除缓存值"""
        if key in self.cache:
            del self.cache[key]
            return True
        return False
    
    async def clear(self):
        """清空缓存"""
        self.cache.clear()
        self.stats = {"hits": 0, "misses": 0, "evictions": 0}
    
    async def _evict_lru(self):
        """驱逐最近最少使用的条目"""
        if not self.cache:
            return
        
        # 找到最少使用的条目
        lru_key = min(
            self.cache.keys(),
            key=lambda k: (self.cache[k].last_access or 0, self.cache[k].hit_count)
        )
        
        del self.cache[lru_key]
        self.stats["evictions"] += 1
    
    async def cleanup_expired(self):
        """清理过期条目"""
        now = time.time()
        expired_keys = [
            key for key, entry in self.cache.items()
            if now > entry.expires_at
        ]
        
        for key in expired_keys:
            del self.cache[key]
        
        return len(expired_keys)
    
    def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计信息"""
        total_requests = self.stats["hits"] + self.stats["misses"]
        hit_rate = (self.stats["hits"] / total_requests * 100) if total_requests > 0 else 0
        
        return {
            "cache_size": len(self.cache),
            "max_size": self.max_size,
            "hit_rate": f"{hit_rate:.2f}%",
            "stats": self.stats
        }

# 带缓存的MCP服务器示例
class CachedMCPServer:
    """带缓存的MCP服务器"""
    
    def __init__(self, server_name: str):
        self.server = Server(server_name)
        self.cache = CacheManager()
        self.setup_handlers()
    
    def setup_handlers(self):
        """设置带缓存的处理器"""
        
        @self.server.list_tools()
        async def list_tools():
            return [
                Tool(
                    name="cached_operation",
                    description="带缓存的操作",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "input": {
                                "type": "string",
                                "description": "输入数据"
                            },
                            "use_cache": {
                                "type": "boolean",
                                "description": "是否使用缓存",
                                "default": True
                            }
                        },
                        "required": ["input"]
                    }
                ),
                Tool(
                    name="cache_stats",
                    description="获取缓存统计信息",
                    inputSchema={
                        "type": "object",
                        "properties": {}
                    }
                )
            ]
        
        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict):
            """带缓存的工具调用"""
            try:
                if name == "cached_operation":
                    return await self.cached_operation(arguments)
                elif name == "cache_stats":
                    return await self.get_cache_stats()
                
                return [TextContent(
                    type="text",
                    text=f"未知工具: {name}"
                )]
                
            except Exception as e:
                return [TextContent(
                    type="text",
                    text=f"操作失败: {str(e)}"
                )]
    
    async def cached_operation(self, arguments: dict):
        """带缓存的操作"""
        input_data = arguments.get("input", "")
        use_cache = arguments.get("use_cache", True)
        
        # 生成缓存键
        cache_key = f"operation:{hash(input_data)}"
        
        # 尝试从缓存获取
        if use_cache:
            cached_result = await self.cache.get(cache_key)
            if cached_result is not None:
                return [TextContent(
                    type="text",
                    text=f"🎯 缓存命中: {cached_result}"
                )]
        
        # 执行实际操作(模拟耗时操作)
        await asyncio.sleep(1)
        result = f"处理结果: {input_data.upper()}"
        
        # 存入缓存
        if use_cache:
            await self.cache.set(cache_key, result)
        
        return [TextContent(
            type="text",
            text=f"💾 新计算: {result}"
        )]
    
    async def get_cache_stats(self):
        """获取缓存统计"""
        stats = self.cache.get_stats()
        return [TextContent(
            type="text",
            text=f"缓存统计:\n{json.dumps(stats, indent=2, ensure_ascii=False)}"
        )]

🌐 网络和API集成

1. HTTP客户端工具
# file: http_client_server.py

import asyncio
import aiohttp
import json
from typing import Dict, Any, Optional
from mcp.server import Server
from mcp.types import TextContent, Tool

class HttpClientServer:
    """HTTP客户端MCP服务器"""
    
    def __init__(self):
        self.server = Server("http-client-server")
        self.session: Optional[aiohttp.ClientSession] = None
        self.setup_handlers()
    
    async def _get_session(self) -> aiohttp.ClientSession:
        """获取HTTP会话"""
        if self.session is None or self.session.closed:
            self.session = aiohttp.ClientSession(
                timeout=aiohttp.ClientTimeout(total=30),
                headers={
                    'User-Agent': 'MCP-HTTP-Client/1.0'
                }
            )
        return self.session
    
    def setup_handlers(self):
        """设置HTTP处理器"""
        
        @self.server.list_tools()
        async def list_tools():
            return [
                Tool(
                    name="http_get",
                    description="发送HTTP GET请求",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "url": {
                                "type": "string",
                                "description": "请求URL"
                            },
                            "headers": {
                                "type": "object",
                                "description": "请求头",
                                "default": {}
                            },
                            "params": {
                                "type": "object",
                                "description": "查询参数",
                                "default": {}
                            }
                        },
                        "required": ["url"]
                    }
                ),
                Tool(
                    name="http_post",
                    description="发送HTTP POST请求",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "url": {
                                "type": "string",
                                "description": "请求URL"
                            },
                            "data": {
                                "type": ["object", "string"],
                                "description": "POST数据"
                            },
                            "headers": {
                                "type": "object",
                                "description": "请求头",
                                "default": {}
                            },
                            "json_data": {
                                "type": "boolean",
                                "description": "是否以JSON格式发送数据",
                                "default": True
                            }
                        },
                        "required": ["url", "data"]
                    }
                ),
                Tool(
                    name="api_call",
                    description="调用REST API",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "base_url": {
                                "type": "string",
                                "description": "API基础URL"
                            },
                            "endpoint": {
                                "type": "string",
                                "description": "API端点"
                            },
                            "method": {
                                "type": "string",
                                "description": "HTTP方法",
                                "enum": ["GET", "POST", "PUT", "DELETE"],
                                "default": "GET"
                            },
                            "auth_token": {
                                "type": "string",
                                "description": "认证令牌"
                            },
                            "data": {
                                "type": "object",
                                "description": "请求数据"
                            }
                        },
                        "required": ["base_url", "endpoint"]
                    }
                )
            ]
        
        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict):
            """执行HTTP工具调用"""
            try:
                if name == "http_get":
                    return await self.http_get(arguments)
                elif name == "http_post":
                    return await self.http_post(arguments)
                elif name == "api_call":
                    return await self.api_call(arguments)
                else:
                    return [TextContent(
                        type="text",
                        text=f"未知工具: {name}"
                    )]
                    
            except Exception as e:
                return [TextContent(
                    type="text",
                    text=f"HTTP请求失败: {str(e)}"
                )]
    
    async def http_get(self, arguments: dict):
        """执行HTTP GET请求"""
        url = arguments["url"]
        headers = arguments.get("headers", {})
        params = arguments.get("params", {})
        
        session = await self._get_session()
        
        async with session.get(url, headers=headers, params=params) as response:
            status = response.status
            content_type = response.headers.get('content-type', '')
            
            if 'application/json' in content_type:
                data = await response.json()
                result = json.dumps(data, indent=2, ensure_ascii=False)
            else:
                result = await response.text()
            
            return [TextContent(
                type="text",
                text=f"状态码: {status}\n内容类型: {content_type}\n\n响应内容:\n{result[:1000]}{'...' if len(result) > 1000 else ''}"
            )]
    
    async def http_post(self, arguments: dict):
        """执行HTTP POST请求"""
        url = arguments["url"]
        data = arguments["data"]
        headers = arguments.get("headers", {})
        json_data = arguments.get("json_data", True)
        
        session = await self._get_session()
        
        kwargs = {"headers": headers}
        
        if json_data:
            kwargs["json"] = data
        else:
            kwargs["data"] = data
        
        async with session.post(url, **kwargs) as response:
            status = response.status
            content_type = response.headers.get('content-type', '')
            
            if 'application/json' in content_type:
                result_data = await response.json()
                result = json.dumps(result_data, indent=2, ensure_ascii=False)
            else:
                result = await response.text()
            
            return [TextContent(
                type="text",
                text=f"状态码: {status}\n内容类型: {content_type}\n\n响应内容:\n{result[:1000]}{'...' if len(result) > 1000 else ''}"
            )]
    
    async def api_call(self, arguments: dict):
        """调用REST API"""
        base_url = arguments["base_url"].rstrip('/')
        endpoint = arguments["endpoint"].lstrip('/')
        method = arguments.get("method", "GET")
        auth_token = arguments.get("auth_token")
        data = arguments.get("data")
        
        url = f"{base_url}/{endpoint}"
        headers = {}
        
        if auth_token:
            headers["Authorization"] = f"Bearer {auth_token}"
        
        session = await self._get_session()
        
        kwargs = {"headers": headers}
        if data and method in ["POST", "PUT"]:
            kwargs["json"] = data
        
        async with session.request(method, url, **kwargs) as response:
            status = response.status
            
            try:
                result_data = await response.json()
                result = json.dumps(result_data, indent=2, ensure_ascii=False)
            except:
                result = await response.text()
            
            return [TextContent(
                type="text",
                text=f"API调用结果:\n状态码: {status}\n\n{result[:1000]}{'...' if len(result) > 1000 else ''}"
            )]
    
    async def run(self):
        """运行服务器"""
        print("🌐 启动HTTP客户端服务器...")
        
        try:
            async with self.server.run_stdio():
                await asyncio.Event().wait()
        finally:
            if self.session and not self.session.closed:
                await self.session.close()

if __name__ == "__main__":
    server = HttpClientServer()
    asyncio.run(server.run())

🎯 常见问题解答

Q1: 什么是MCP?它解决了什么问题?

A: MCP(Model Context Protocol)是一个标准化的协议,用于连接AI模型和外部工具。它解决了以下问题:

  1. 工具集成复杂度:提供统一的接口标准
  2. 安全性:通过标准化的协议确保安全交互
  3. 可扩展性:支持模块化的工具开发
  4. 互操作性:不同的AI模型可以使用相同的工具

Q2: MCP与传统API有什么区别?

A: 主要区别包括:

特性MCP传统API
标准化统一的协议标准各自定义标准
安全性内置安全机制需要自行实现
上下文管理自动管理上下文手动管理
工具发现自动发现机制手动配置
错误处理标准化错误处理各自实现

Q3: 如何选择合适的MCP服务器架构?

A: 考虑以下因素:

  1. 功能复杂度:简单功能用单一服务器,复杂功能用多服务器
  2. 性能要求:高性能需求使用异步架构
  3. 安全要求:敏感操作需要安全增强
  4. 扩展性:考虑未来扩展需求

Q4: MCP服务器的性能优化策略有哪些?

A: 主要优化策略:

  1. 异步处理:使用asyncio处理并发请求
  2. 缓存机制:缓存频繁访问的数据
  3. 连接池:重用数据库和HTTP连接
  4. 批处理:合并多个小请求
  5. 资源限制:限制并发数和内存使用

Q5: 如何确保MCP服务器的安全性?

A: 安全措施包括:

  1. 输入验证:严格验证所有输入参数
  2. 权限控制:实现细粒度的权限管理
  3. 速率限制:防止滥用和DoS攻击
  4. 路径安全:防止路径遍历攻击
  5. 日志记录:记录所有操作用于审计

总结与展望

MCP(Model Context Protocol)是一种标准化的协议,用于连接AI模型和外部工具。它解决了工具集成复杂度、安全性、可扩展性和互操作性等问题。MCP服务器架构可以根据功能复杂度和性能要求进行选择,并采用多种优化策略提高性能。同时,MCP服务器需要采取多种安全措施确保安全性。未来,MCP协议将继续发展,以支持更多的AI模型和工具集成。

如果看到这里,请给我点个赞吧!