AgentScope扩展开发:自定义MCP客户端深度指南
【免费下载链接】agentscope 项目地址: https://gitcode.com/GitHub_Trending/ag/agentscope
概述
Model Context Protocol(MCP,模型上下文协议)是AgentScope框架的核心扩展机制,允许开发者将外部工具和服务无缝集成到智能体系统中。本文将深入探讨如何自定义MCP客户端,从基础概念到高级实现技巧。
MCP客户端架构解析
AgentScope提供了完整的MCP客户端架构,支持多种传输协议和连接模式:
基础MCP客户端实现
1. 无状态HTTP客户端
from agentscope.mcp import HttpStatelessClient
import os
# 创建基础无状态客户端
weather_client = HttpStatelessClient(
name="weather_service",
transport="sse", # 支持sse或streamable_http
url="https://api.weather.com/mcp",
headers={"Authorization": f"Bearer {os.environ['WEATHER_API_KEY']}"},
timeout=30
)
2. 有状态Stdio客户端
from agentscope.mcp import StdioStatefulClient
# 创建进程式有状态客户端
database_client = StdioStatefulClient(
name="database_tool",
command="python",
args=["-m", "database_mcp_server"],
env={"DATABASE_URL": os.environ.get("DATABASE_URL")},
cwd="/opt/mcp_servers",
encoding="utf-8"
)
自定义MCP客户端开发指南
继承基础类实现自定义客户端
from agentscope.mcp import StatefulClientBase
from mcp import ClientSession, StdioServerParameters
import aiohttp
from typing import Literal
class CustomWebSocketClient(StatefulClientBase):
"""自定义WebSocket MCP客户端"""
def __init__(
self,
name: str,
ws_url: str,
auth_token: str = None,
reconnect_attempts: int = 3
) -> None:
super().__init__(name=name)
self.ws_url = ws_url
self.auth_token = auth_token
self.reconnect_attempts = reconnect_attempts
self.ws_session = None
async def connect(self) -> None:
"""建立WebSocket连接"""
headers = {}
if self.auth_token:
headers["Authorization"] = f"Bearer {self.auth_token}"
for attempt in range(self.reconnect_attempts):
try:
self.ws_session = await aiohttp.ClientSession().ws_connect(
self.ws_url,
headers=headers
)
# 初始化MCP会话
read_stream = self.ws_session.receive()
write_stream = self.ws_session.send_str
self.session = ClientSession(read_stream, write_stream)
await self.session.initialize()
self.is_connected = True
return
except Exception as e:
if attempt == self.reconnect_attempts - 1:
raise e
await asyncio.sleep(2 ** attempt)
async def close(self) -> None:
"""关闭连接"""
if self.ws_session:
await self.ws_session.close()
await super().close()
实现自定义工具包装器
from agentscope.mcp import MCPToolFunction
from agentscope.tool import ToolResponse
from typing import Any, Callable
import asyncio
class RetryableMCPToolFunction(MCPToolFunction):
"""支持重试机制的MCP工具函数"""
def __init__(
self,
mcp_name: str,
tool: Any,
wrap_tool_result: bool,
max_retries: int = 3,
retry_delay: float = 1.0,
session: Any = None
):
super().__init__(mcp_name, tool, wrap_tool_result, session=session)
self.max_retries = max_retries
self.retry_delay = retry_delay
async def __call__(self, **kwargs: Any) -> Any:
for attempt in range(self.max_retries):
try:
result = await super().__call__(**kwargs)
return result
except Exception as e:
if attempt == self.max_retries - 1:
raise e
await asyncio.sleep(self.retry_delay * (2 ** attempt))
高级功能实现
1. 工具发现与缓存机制
from agentscope.mcp import StatefulClientBase
from typing import List, Dict
import mcp.types
from datetime import datetime, timedelta
class CachingMCPClient(StatefulClientBase):
"""带工具缓存功能的MCP客户端"""
def __init__(self, name: str, cache_ttl: int = 300):
super().__init__(name=name)
self.cache_ttl = cache_ttl
self._tools_cache = None
self._cache_timestamp = None
async def list_tools(self) -> List[mcp.types.Tool]:
"""带缓存的工具列表获取"""
current_time = datetime.now()
if (self._tools_cache and self._cache_timestamp and
current_time - self._cache_timestamp < timedelta(seconds=self.cache_ttl)):
return self._tools_cache
tools = await super().list_tools()
self._tools_cache = tools
self._cache_timestamp = current_time
return tools
async def get_tool_by_pattern(self, pattern: str) -> List[mcp.types.Tool]:
"""通过模式匹配查找工具"""
tools = await self.list_tools()
return [tool for tool in tools if pattern.lower() in tool.name.lower()]
2. 批量工具执行器
from typing import List, Dict, Any
import asyncio
from concurrent.futures import ThreadPoolExecutor
class BatchMCPExecutor:
"""MCP工具批量执行器"""
def __init__(self, max_workers: int = 10):
self.executor = ThreadPoolExecutor(max_workers=max_workers)
async def execute_batch(
self,
client: StatefulClientBase,
tool_name: str,
parameters_list: List[Dict[str, Any]]
) -> List[Any]:
"""批量执行相同工具的不同参数"""
async def execute_single(params: Dict[str, Any]):
tool_func = await client.get_callable_function(tool_name)
return await tool_func(**params)
# 使用线程池执行批量任务
loop = asyncio.get_event_loop()
tasks = [
loop.run_in_executor(self.executor, execute_single, params)
for params in parameters_list
]
return await asyncio.gather(*tasks, return_exceptions=True)
实战案例:自定义天气服务MCP客户端
from agentscope.mcp import HttpStatelessClient
from pydantic import BaseModel, Field
from typing import List, Optional
import aiohttp
import json
class WeatherData(BaseModel):
temperature: float = Field(description="当前温度")
humidity: int = Field(description="湿度百分比")
condition: str = Field(description="天气状况")
location: str = Field(description="地理位置")
class CustomWeatherClient(HttpStatelessClient):
"""自定义天气服务MCP客户端"""
def __init__(self, api_key: str):
super().__init__(
name="custom_weather",
transport="streamable_http",
url="https://api.weatherapi.com/v1/current.json",
headers={"Content-Type": "application/json"}
)
self.api_key = api_key
async def get_weather(self, location: str) -> WeatherData:
"""获取指定位置的天气数据"""
params = {
"key": self.api_key,
"q": location,
"aqi": "no"
}
async with aiohttp.ClientSession() as session:
async with session.get(self.url, params=params) as response:
data = await response.json()
return WeatherData(
temperature=data["current"]["temp_c"],
humidity=data["current"]["humidity"],
condition=data["current"]["condition"]["text"],
location=location
)
async def get_callable_function(self, func_name: str, wrap_tool_result: bool = True):
"""重写工具获取方法"""
if func_name == "get_weather":
return self.get_weather
return await super().get_callable_function(func_name, wrap_tool_result)
性能优化与最佳实践
连接池管理
from agentscope.mcp import HttpStatelessClient
from aiohttp import ClientSession, TCPConnector
from typing import Dict
class ConnectionPoolMCPClient(HttpStatelessClient):
"""带连接池的MCP客户端"""
_session_pool: Dict[str, ClientSession] = {}
def __init__(self, name: str, transport: str, url: str, headers: Dict = None):
super().__init__(name, transport, url, headers)
# 创建或复用连接池
pool_key = f"{transport}_{url}"
if pool_key not in self._session_pool:
connector = TCPConnector(limit=100, limit_per_host=10)
self._session_pool[pool_key] = ClientSession(connector=connector)
self.session = self._session_pool[pool_key]
@classmethod
async def close_all_sessions(cls):
"""关闭所有连接池会话"""
for session in cls._session_pool.values():
await session.close()
cls._session_pool.clear()
监控与指标收集
from prometheus_client import Counter, Histogram
import time
from typing import Callable, Any
# 定义监控指标
mcp_requests_total = Counter('mcp_requests_total', 'Total MCP requests', ['client', 'tool'])
mcp_request_duration = Histogram('mcp_request_duration_seconds', 'MCP request duration', ['client'])
def monitor_mcp_tool(func: Callable) -> Callable:
"""MCP工具监控装饰器"""
async def wrapper(*args, **kwargs):
client_name = args[0].name if args else 'unknown'
tool_name = kwargs.get('func_name', 'unknown')
start_time = time.time()
mcp_requests_total.labels(client=client_name, tool=tool_name).inc()
try:
result = await func(*args, **kwargs)
duration = time.time() - start_time
mcp_request_duration.labels(client=client_name).observe(duration)
return result
except Exception as e:
duration = time.time() - start_time
mcp_request_duration.labels(client=client_name).observe(duration)
raise e
return wrapper
# 应用监控装饰器
MCPClientBase.get_callable_function = monitor_mcp_tool(MCPClientBase.get_callable_function)
故障排除与调试技巧
常见问题解决方案
| 问题类型 | 症状 | 解决方案 |
|---|---|---|
| 连接超时 | 客户端无法建立连接 | 检查网络配置,增加超时时间 |
| 认证失败 | 401/403错误 | 验证API密钥和权限设置 |
| 协议不匹配 | 传输协议错误 | 确认服务端支持的传输类型 |
| 内存泄漏 | 连接数持续增长 | 实现连接池和资源清理 |
调试工具函数
from agentscope.mcp import MCPClientBase
import logging
from typing import Any
class DebuggableMCPClient(MCPClientBase):
"""可调试的MCP客户端"""
def __init__(self, name: str, debug: bool = False):
super().__init__(name)
self.debug = debug
self.logger = logging.getLogger(f"mcp.{name}")
async def get_callable_function(self, func_name: str, wrap_tool_result: bool = True):
if self.debug:
self.logger.debug(f"Requesting tool: {func_name}")
tool_func = await super().get_callable_function(func_name, wrap_tool_result)
if self.debug:
async def debug_wrapper(**kwargs):
self.logger.debug(f"Calling {func_name} with args: {kwargs}")
try:
result = await tool_func(**kwargs)
self.logger.debug(f"Tool {func_name} succeeded: {result}")
return result
except Exception as e:
self.logger.error(f"Tool {func_name} failed: {e}")
raise
return debug_wrapper
return tool_func
总结
自定义MCP客户端是AgentScope框架扩展的核心能力,通过本文的深入讲解,您应该能够:
- 理解MCP架构:掌握有状态和无状态客户端的区别与应用场景
- 实现自定义客户端:从基础继承到高级功能扩展
- 优化性能:通过连接池、缓存和监控提升系统效率
- 处理复杂场景:应对认证、重试、批量处理等实际需求
MCP客户端的自定义能力为AgentScope生态提供了无限可能,让您能够将任何外部服务无缝集成到智能体工作流中,构建更加强大和灵活的AI应用系统。
【免费下载链接】agentscope 项目地址: https://gitcode.com/GitHub_Trending/ag/agentscope
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



