MCP Logo

客户端开发

学习如何构建能与所有MCP服务器集成的客户端。

在本教程中,您将学习如何构建一个连接到MCP服务器的大语言模型驱动的聊天机器人客户端。建议您先完成服务器快速入门教程,了解构建第一个服务器的基础知识。

您可以在这里找到本教程的完整代码。

系统要求

在开始之前,请确保您的系统满足以下要求:

  • Mac或Windows电脑
  • 已安装最新版本的Python
  • 已安装最新版本的uv工具

环境配置

首先,使用uv创建一个新的Python项目:

# 创建项目目录
uv init mcp-client
cd mcp-client
 
# 创建虚拟环境
uv venv
 
# 激活虚拟环境
# Windows系统:
.venv\Scripts\activate
# Unix或MacOS系统:
source .venv/bin/activate
 
# 安装所需的包
uv add mcp anthropic python-dotenv
 
# 删除模板文件
rm main.py
 
# 创建主文件
touch client.py

设置API密钥

您需要从Anthropic控制台获取Anthropic API密钥。

创建一个.env文件来存储密钥:

# 创建.env文件
touch .env

将您的密钥添加到.env文件中:

ANTHROPIC_API_KEY=<在此处填入您的密钥>

.env添加到.gitignore文件中:

echo ".env" >> .gitignore

请确保妥善保管您的ANTHROPIC_API_KEY

创建客户端

基本客户端结构

首先,我们设置导入并创建基本的客户端类:

import asyncio
from typing import Optional
from contextlib import AsyncExitStack
 
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
 
from anthropic import Anthropic
from dotenv import load_dotenv
 
load_dotenv()  # 从.env加载环境变量
 
class MCPClient:
    def __init__(self):
        # 初始化会话和客户端对象
        self.session: Optional[ClientSession] = None
        self.exit_stack = AsyncExitStack()
        self.anthropic = Anthropic()
    # 方法将在此处添加

服务器连接管理

接下来,我们实现连接到MCP服务器的方法:

async def connect_to_server(self, server_script_path: str):
    """连接到MCP服务器
 
    参数:
        server_script_path: 服务器脚本的路径(.py或.js)
    """
    is_python = server_script_path.endswith('.py')
    is_js = server_script_path.endswith('.js')
    if not (is_python or is_js):
        raise ValueError("服务器脚本必须是.py或.js文件")
 
    command = "python" if is_python else "node"
    server_params = StdioServerParameters(
        command=command,
        args=[server_script_path],
        env=None
    )
 
    stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
    self.stdio, self.write = stdio_transport
    self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
 
    await self.session.initialize()
 
    # 列出可用工具
    response = await self.session.list_tools()
    tools = response.tools
    print("\n已连接到服务器,可用工具:", [tool.name for tool in tools])

查询处理逻辑

现在让我们添加处理用户查询和工具调用的核心功能:

async def process_query(self, query: str) -> str:
    """使用Claude模型和MCP服务器上的可用工具处理用户查询"""
    messages = [
        {
            "role": "user",
            "content": query
        }
    ]
 
    response = await self.session.list_tools()
    available_tools = [{
        "name": tool.name,
        "description": tool.description,
        "input_schema": tool.inputSchema
    } for tool in response.tools]
 
    # 向Claude API发送初始请求
    response = self.anthropic.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=messages,
        tools=available_tools
    )
 
    # 处理Claude响应和可能的工具调用
    final_text = []
 
    assistant_message_content = []
    for content in response.content:
        if content.type == 'text':
            # 处理纯文本回复
            final_text.append(content.text)
            assistant_message_content.append(content)
        elif content.type == 'tool_use':
            # 处理工具调用请求
            tool_name = content.name
            tool_args = content.input
 
            # 通过MCP服务器执行工具调用
            result = await self.session.call_tool(tool_name, tool_args)
            final_text.append(f"[调用工具: {tool_name},参数: {tool_args}]")
 
            # 更新对话历史
            assistant_message_content.append(content)
            messages.append({
                "role": "assistant",
                "content": assistant_message_content
            })
            messages.append({
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": content.id,
                        "content": result.content
                    }
                ]
            })
 
            # 将工具执行结果发送回Claude,获取进一步回复
            response = self.anthropic.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=1000,
                messages=messages,
                tools=available_tools
            )
 
            final_text.append(response.content[0].text)
 
    # 将所有回复合并为一个完整的响应
    return "\n".join(final_text)

交互式聊天界面

现在我们添加用户交互界面和资源清理功能:

async def chat_loop(self):
    """运行交互式聊天循环,处理用户输入并显示回复"""
    print("\nMCP客户端已启动!")
    print("输入您的问题或输入'quit'退出。")
 
    while True:
        try:
            query = input("\n问题: ").strip()
 
            if query.lower() == 'quit':
                break
 
            response = await self.process_query(query)
            print("\n" + response)
 
        except Exception as e:
            print(f"\n错误: {str(e)}")
 
async def cleanup(self):
    """清理资源,关闭连接"""
    await self.exit_stack.aclose()

主入口点

最后,我们添加程序入口点和主要执行逻辑:

async def main():
    if len(sys.argv) < 2:
        print("用法: python client.py <服务器脚本路径>")
        sys.exit(1)
 
    client = MCPClient()
    try:
        await client.connect_to_server(sys.argv[1])
        await client.chat_loop()
    finally:
        await client.cleanup()
 
if __name__ == "__main__":
    import sys
    asyncio.run(main())

您可以在这里找到完整的client.py文件。

核心组件详解

1. 客户端初始化

  • MCPClient类负责初始化会话管理和API客户端
  • 使用AsyncExitStack确保资源的正确管理和释放
  • 配置Anthropic客户端以与Claude大语言模型进行交互

2. 服务器连接

  • 支持连接Python和Node.js编写的MCP服务器
  • 验证服务器脚本类型并选择适当的执行命令
  • 建立与服务器的通信通道
  • 初始化会话并获取可用工具列表

3. 查询处理

  • 维护完整的对话上下文和历史记录
  • 处理Claude的文本响应和工具调用指令
  • 协调Claude模型和MCP工具之间的消息流
  • 将多个回复整合为连贯的用户响应

4. 交互式界面

  • 提供简洁直观的命令行交互界面
  • 处理用户输入并格式化显示响应
  • 包含完善的错误处理机制
  • 支持通过输入'quit'优雅退出程序

5. 资源管理

  • 确保所有资源得到妥善清理
  • 提供连接问题的错误处理机制
  • 实现程序的优雅关闭和资源释放

常见自定义点

  1. 工具处理定制

    • 可修改process_query()方法以支持特定类型的工具
    • 为不同工具的调用添加专门的错误处理逻辑
    • 实现针对特定工具结果的格式化处理
  2. 响应处理优化

    • 自定义不同工具结果的展示方式
    • 添加响应的过滤、转换或优化处理
    • 实现更详细的日志记录或调试信息输出
  3. 用户界面增强

    • 添加图形界面(GUI)或网页前端
    • 实现更丰富的控制台输出,如颜色、进度条等
    • 添加命令历史记录、自动补全或提示功能

运行客户端

要使用任何MCP服务器运行您的客户端,请执行以下命令:

uv run client.py path/to/server.py # 连接Python服务器
uv run client.py path/to/build/index.js # 连接Node服务器

如果您正在继续服务器快速入门中的天气教程,您的命令可能类似于:python client.py .../quickstart-resources/weather-server-python/weather.py

客户端启动后将:

  1. 自动连接到指定的MCP服务器
  2. 显示所有可用的工具列表
  3. 启动交互式聊天会话,您可以:
    • 输入自然语言问题和指令
    • 查看工具被调用的过程和参数
    • 获取由Claude生成的全面响应

如果您连接到服务器快速入门中的天气服务器,界面应该如下所示:

工作原理

当您向客户端提交查询时,以下流程将自动执行:

  1. 客户端首先从MCP服务器获取所有可用工具的列表及其描述
  2. 您的查询文本连同工具描述信息一起发送给Claude模型
  3. Claude分析您的问题并决定是否需要使用特定工具
  4. 如果需要使用工具,客户端将通过MCP服务器执行相应的工具调用
  5. 工具执行的结果被发送回Claude进行分析和整合
  6. Claude根据原始问题和工具结果生成自然语言响应
  7. 最终响应展示给您,包含工具调用的信息和结果解释

最佳实践

  1. 健壮的错误处理

    • 始终使用try-catch块包装工具调用,避免单个工具错误导致整个会话崩溃
    • 提供具体明确的错误信息,帮助快速定位问题
    • 优雅地处理网络连接问题和服务器异常
  2. 高效的资源管理

    • 利用AsyncExitStack确保所有资源在使用后得到正确释放
    • 会话结束后妥善关闭所有连接
    • 实现对服务器意外断开的检测和处理机制
  3. 安全最佳实践

    • 使用.env文件或环境变量安全存储API密钥和敏感信息
    • 对服务器返回的结果进行验证,避免盲目信任
    • 谨慎管理工具的权限和访问范围,遵循最小权限原则

常见错误信息及解决方法

错误信息可能原因解决方案
Connection refused服务器未启动或路径错误确认服务器路径正确且服务器能正常启动
Tool execution failed工具执行环境配置错误检查工具所需的环境变量是否正确设置
ANTHROPIC_API_KEY is not setAPI密钥未设置确认环境变量中包含有效的API密钥
NoSuchElementException处理空数据检查数据流和工具返回值是否正确

故障排除

服务器路径问题

  • 检查服务器脚本路径是否准确无误
  • 如果相对路径不起作用,尝试使用绝对路径
  • Windows用户注意:在路径中使用正斜杠(/)或成对的反斜杠(\)
  • 确认服务器文件具有正确的扩展名(Python脚本为.py,Node.js脚本为.js)

路径示例参考:

# 相对路径示例
uv run client.py ./server/weather.py
 
# 绝对路径示例
uv run client.py /Users/username/projects/mcp-server/weather.py
 
# Windows路径示例(两种格式均可)
uv run client.py C:/projects/mcp-server/weather.py
uv run client.py C:\\projects\\mcp-server\\weather.py

响应时间问题

  • 首次查询的响应可能需要等待约30秒
  • 这是正常现象,延迟来源于以下几个方面:
    • 服务器初始化和加载过程
    • Claude模型的处理时间
    • 工具执行和结果获取的时间
  • 后续对话通常响应更快
  • 请耐心等待初始响应,不要中断进程

常见错误及解决方法

如果您遇到以下错误:

  • FileNotFoundError:检查服务器脚本文件路径是否正确
  • Connection refused:确保服务器正在运行且端口未被占用
  • Tool execution failed:检查工具所需的环境变量是否已正确设置
  • Timeout error:可能需要在客户端配置中增加超时时间限制

下一步

在 GitHub 上编辑

最后更新于