refactor(module11): restructure MCP client implementation and migrate to chapter 11
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import uuid
|
||||
from contextlib import AsyncExitStack
|
||||
from typing import Optional,Literal
|
||||
|
||||
from .transports.base import MCPTransport
|
||||
from .transports.http import HttpMCPTransport
|
||||
from .transports.stdio import StdioMCPTransport
|
||||
|
||||
|
||||
class MCPClient:
|
||||
|
||||
# 编辑器 _impl 必须满足MCPTransport协议
|
||||
_impl:MCPTransport
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
transport:Literal["stdio","http"]="stdio",
|
||||
command:str=None,
|
||||
args:list[str]=None,
|
||||
env:dict=None,
|
||||
url:str=None
|
||||
):
|
||||
"""
|
||||
MCP 客户端 - 支持 stdio 与 HTTP 两种传输方式
|
||||
|
||||
:param transport: 传输模式 "stdio" / "http"
|
||||
:param command: stdio 模式的命令 (如npx)
|
||||
:param args: stdio 模式的参数
|
||||
:param env: stdio 模式的环境变量
|
||||
:param url: http模式的端点 URL
|
||||
"""
|
||||
|
||||
if transport=="stdio":
|
||||
if command is None:
|
||||
raise ValueError("stdio 传输模式需要参数 'command'")
|
||||
self._impl = StdioMCPTransport(command=command,args=args or [],env=env)
|
||||
elif transport=="http":
|
||||
if url is None:
|
||||
raise ValueError("http 传输模式需要参数 'command'")
|
||||
self._impl = HttpMCPTransport(url=url)
|
||||
else:
|
||||
raise ValueError(f"不支持的传输模式: {transport}")
|
||||
|
||||
|
||||
|
||||
async def connect(self):
|
||||
""" 建立MCP连接(stdio或HTTP) """
|
||||
await self._impl.connect()
|
||||
|
||||
|
||||
async def list_tools(self):
|
||||
"""查询工具列表,为LLM建立上下文用"""
|
||||
return await self._impl.list_tools()
|
||||
|
||||
async def call_tool(self,name:str,args:dict):
|
||||
"""调用工具(工程化:加上防御性处理)"""
|
||||
return await self._impl.call_tool(name,args)
|
||||
|
||||
|
||||
async def cleanup(self):
|
||||
"""关闭MCP服务、会话和transport"""
|
||||
return await self._impl.cleanup()
|
||||
|
||||
|
||||
async def _http_request(self,method:str,params:dict=None):
|
||||
""" 发送 HTTP JSON-RPC 请求 """
|
||||
payload = {
|
||||
"jsonrpc":"2.0",
|
||||
"id":str(uuid.uuid4()),
|
||||
"method":method,
|
||||
}
|
||||
if params:
|
||||
payload["params"] = params
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json, text/event-stream"
|
||||
}
|
||||
if self.session_id:
|
||||
headers["Mcp-Session-Id"] = self.session_id
|
||||
|
||||
response = await self.http_client.post(
|
||||
self.url,
|
||||
json=payload,
|
||||
headers=headers
|
||||
)
|
||||
|
||||
if "Mcp-Session-Id" in response.headers:
|
||||
self.session_id = response.headers["Mcp-Session-Id"]
|
||||
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
Reference in New Issue
Block a user