mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-28 16:20:56 +08:00
检查bot是否运行正常 / bot check (push) Waiting to run
CodeQL Code Security Analysis / Analyze (python) (push) Waiting to run
Sequential Lint and Type Check / pyright-call (push) Blocked by required conditions
Release Drafter / Update Release Draft (push) Waiting to run
Ruff Lint / Ruff Lint (push) Waiting to run
Force Sync to Aliyun / sync (push) Waiting to run
Sequential Lint and Type Check / ruff-call (push) Waiting to run
Update Version / update-version (push) Waiting to run
* ✨ feat(llm): 新增 Grok 适配器及 API 密钥重置指令 - 新增 Grok 适配器并支持原生推特检索工具 `x_search` - 优化 DeepSeek 适配器以迁移至 Responses 协议,增强多模态支持 - 在 LLM 管理插件中新增 `llm reset` 子命令以重置 Key 状态 - 增强 MCP 连接异常处理及流关闭容错能力 * 🚨 auto fix by pre-commit hooks --------- Co-authored-by: webjoin111 <455457521@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
114 lines
4.3 KiB
Python
114 lines
4.3 KiB
Python
from collections.abc import AsyncGenerator
|
||
from contextlib import asynccontextmanager
|
||
import json
|
||
from typing import Any
|
||
|
||
import anyio
|
||
from anyio import create_memory_object_stream, create_task_group
|
||
from mcp.shared.message import SessionMessage
|
||
from mcp.types import JSONRPCMessage
|
||
|
||
from zhenxun.services.ai.sandbox.protocols import SupportsStreamExecution
|
||
from zhenxun.services.ai.sandbox.registry import SandboxRegistry
|
||
from zhenxun.services.ai.utils.logger import log_sandbox as logger
|
||
from zhenxun.utils.pydantic_compat import model_dump_json, model_validate
|
||
|
||
from .base import BaseMcpProxyExtension
|
||
|
||
|
||
class UniversalMcpExtension(BaseMcpProxyExtension):
|
||
"""通用 MCP 代理扩展类,用于在沙箱内连接 MCP 服务"""
|
||
|
||
@property
|
||
def extension_name(self) -> str:
|
||
"""获取 MCP 代理扩展的唯一名称"""
|
||
return "universal_mcp"
|
||
|
||
@asynccontextmanager
|
||
async def connect_mcp(
|
||
self, command: str, args: list[str], env: dict[str, str] | None = None
|
||
) -> AsyncGenerator[tuple[Any, Any], None]:
|
||
"""启动沙箱内的 MCP 服务器,并建立与之进行 JSON-RPC 通信的双向内存流管道"""
|
||
if not isinstance(self.session, SupportsStreamExecution):
|
||
raise RuntimeError(
|
||
"当前沙箱驱动不支持流式后台进程执行 (SupportsStreamExecution),"
|
||
"无法启动原生 MCP 代理。"
|
||
)
|
||
|
||
logger.info(
|
||
"[UniversalMcpExtension] 正在沙箱内原生启动 MCP 服务器: "
|
||
f"{command} {' '.join(args)}"
|
||
)
|
||
|
||
cmd_list = [command, *args]
|
||
|
||
async with self.session.create_stream_process(
|
||
command=cmd_list, cwd=self.session.workspace_path, env=env
|
||
) as process_stream:
|
||
read_prod, read_cons = create_memory_object_stream(10)
|
||
write_prod, write_cons = create_memory_object_stream(10)
|
||
|
||
async def stream_reader():
|
||
buffer = b""
|
||
try:
|
||
while True:
|
||
msg = await process_stream.read()
|
||
if msg is None:
|
||
break
|
||
if msg.stream_type == 1:
|
||
buffer += msg.data
|
||
while b"\n" in buffer:
|
||
line, buffer = buffer.split(b"\n", 1)
|
||
if not line.strip():
|
||
continue
|
||
try:
|
||
msg_obj = model_validate(
|
||
JSONRPCMessage, json.loads(line)
|
||
)
|
||
await read_prod.send(
|
||
SessionMessage(message=msg_obj)
|
||
)
|
||
except Exception as exc:
|
||
await read_prod.send(exc)
|
||
except anyio.ClosedResourceError:
|
||
pass
|
||
except BaseException as e:
|
||
logger.debug(
|
||
f"🔇 [MCP Universal] 读流异常: {type(e).__name__}: {e}"
|
||
)
|
||
finally:
|
||
try:
|
||
await read_prod.aclose()
|
||
except Exception:
|
||
pass
|
||
|
||
async def stream_writer():
|
||
try:
|
||
async for msg in write_cons:
|
||
data = (
|
||
model_dump_json(
|
||
msg.message, by_alias=True, exclude_none=True
|
||
).encode("utf-8")
|
||
+ b"\n"
|
||
)
|
||
await process_stream.write(data)
|
||
except anyio.ClosedResourceError:
|
||
pass
|
||
except BaseException as e:
|
||
logger.debug(
|
||
f"🔇 [MCP Universal] 写流异常: {type(e).__name__}: {e}"
|
||
)
|
||
|
||
async with create_task_group() as tg:
|
||
tg.start_soon(stream_reader)
|
||
tg.start_soon(stream_writer)
|
||
yield read_cons, write_prod
|
||
tg.cancel_scope.cancel()
|
||
|
||
|
||
SandboxRegistry.register_extension(UniversalMcpExtension)
|
||
|
||
__all__ = [
|
||
"UniversalMcpExtension",
|
||
]
|