Files
zhenxun_bot/zhenxun/services/ai/sandbox/addons/mcp_proxy.py
T
33d6ea1335
检查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): 优化 DeepSeek 适配器 (#2160)
* ✨ 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>
2026-09-22 09:02:15 +08:00

114 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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",
]