feat: mcp tools#2081
Conversation
详细概览本次变更为应用引入了MCP(模型上下文协议)服务集成,包括添加依赖、扩展应用上下文生命周期、创建MCP服务实现、以及新增UI组件来管理和监控MCP服务的启停与配置。 变更内容
时序图sequenceDiagram
actor User
participant UI as AiAssistantInterface
participant Ctx as ZContext
participant MCP as GuiMcpService
participant Op as Operation Layer
User->>UI: 点击启动按钮
UI->>Ctx: start_mcp_service()
Ctx->>MCP: start()
MCP->>Op: 初始化MCP服务器
Op-->>MCP: 服务器就绪
MCP-->>Ctx: 返回
Ctx-->>UI: 返回
UI->>Ctx: get_mcp_client_config_json()
Ctx->>MCP: build_client_config_json()
MCP-->>Ctx: 返回JSON配置
Ctx-->>UI: 返回JSON
UI->>UI: 刷新状态,显示JSON配置
UI-->>User: 显示成功提示
sequenceDiagram
participant MCP as GuiMcpService
participant Op as Operation Layer
participant Screen as 屏幕/游戏
participant OCR as OCR引擎
MCP->>Op: watch_zzz工具被调用
Op->>Screen: 捕获屏幕截图
Screen-->>Op: 返回截图
Op->>OCR: 执行OCR识别
OCR-->>Op: 返回可点击区域坐标
Op->>Op: 构建Base64图像+元数据载荷
Op-->>MCP: 返回截图数据
sequenceDiagram
participant MCP as GuiMcpService
participant Ctx as 应用上下文
participant Op as Operation Layer
participant Input as 输入系统
MCP->>Op: click_zzz工具被调用
Op->>Op: 验证控制器就绪
Op->>Input: 执行延迟等待
Input-->>Op: 延迟完成
Op->>Input: 模拟点击事件(x,y)
Input-->>Op: 返回点击结果及时序
Op-->>MCP: 返回成功状态
MCP->>Ctx: auto_battle_zzz被调用
Ctx->>Ctx: 切换自动战斗状态
Ctx->>Ctx: 启动后台监测循环
Ctx-->>MCP: 返回操作结果
代码审查工作量🎯 4 (复杂) | ⏱️ ~60 分钟 诗歌
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (5)
src/zzz_od/mcp/mcp_service.py (3)
36-37: 建议为ctx参数添加类型提示。根据编码规范,所有函数和方法应包含类型提示。
_McpBridgeOperation.__init__和GuiMcpService.__init__的ctx参数缺少类型提示。🔧 建议的修复
class _McpBridgeOperation(ZOperation): """ MCP工具桥接操作:复用 Operation 层封装(截图/坐标体系)。 """ - def __init__(self, ctx): + def __init__(self, ctx: "ZContext"): ZOperation.__init__(self, ctx=ctx, op_name="MCP工具桥接", need_check_game_win=False)class GuiMcpService: """在 GUI 进程内启动/停止的 MCP 服务。""" - def __init__(self, ctx): + def __init__(self, ctx: "ZContext"): self.ctx = ctx需要在文件顶部添加类型导入:
from typing import TYPE_CHECKING if TYPE_CHECKING: from zzz_od.context.zzz_context import ZContextAlso applies to: 47-48
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/zzz_od/mcp/mcp_service.py` around lines 36 - 37, Add type hints for the ctx parameter on _McpBridgeOperation.__init__ and GuiMcpService.__init__ by importing ZContext under a TYPE_CHECKING guard and annotating ctx: ZContext; specifically, add "from typing import TYPE_CHECKING" and the conditional import "if TYPE_CHECKING: from zzz_od.context.zzz_context import ZContext" at the top of the file and change the method signatures of _McpBridgeOperation.__init__ and GuiMcpService.__init__ to accept ctx: ZContext.
461-470:stop()方法中线程未能在超时内终止时缺少处理。如果
self._thread.join(timeout=5)超时后线程仍在运行,当前代码会将self._thread设为None,但实际线程可能仍在后台运行。建议添加日志警告或检查线程是否真正停止。🔧 建议的修复
if self._thread is not None: self._thread.join(timeout=5) + if self._thread.is_alive(): + log.warning("MCP服务线程未能在超时内停止") self._thread = None🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/zzz_od/mcp/mcp_service.py` around lines 461 - 470, 在 stop() 中 join 超时后没有检查线程状态并错误地把 self._thread 置为 None;在调用 self._thread.join(timeout=5) 之后用 self._thread.is_alive() 检查线程是否仍在运行,若仍存活则记录警告(例如 self._logger.warning 或 logging.warning),不要把 self._thread 设为 None(或仅在确认线程已停止后才清除),以便能正确反映后台线程状态并便于后续调试;保留对 self._stop_lock、self._stop_auto_battle_loop() 和 server.should_exit 的现有逻辑。
93-103:_port_max的静默修正可能导致混淆。当
_port_max < _port_start时,代码静默地将_port_max修正为_port_start。建议添加日志警告,让用户知道配置被调整了。🔧 建议的修复
def _find_available_port(self) -> int: if self._port_max < self._port_start: + log.warning( + "OD_MCP_PORT_MAX (%d) 小于 OD_MCP_PORT_START (%d),已自动调整", + self._port_max, self._port_start + ) self._port_max = self._port_start🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/zzz_od/mcp/mcp_service.py` around lines 93 - 103, The code in _find_available_port silently fixes _port_max when it's less than _port_start; update _find_available_port to log a warning before mutating _port_max so callers know the configuration was adjusted (check and use the service's logger instance, e.g., self._logger or self.logger), then set self._port_max = self._port_start and continue as before; keep the RuntimeError behavior unchanged if no port is found.src/zzz_od/gui/view/game_assistant/ai_assistant_interface.py (1)
14-14: 建议为parent参数添加类型提示。根据编码规范,所有函数和方法应包含类型提示。
🔧 建议的修复
- def __init__(self, ctx: ZContext, parent=None): + def __init__(self, ctx: ZContext, parent: QWidget | None = None):🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/zzz_od/gui/view/game_assistant/ai_assistant_interface.py` at line 14, The __init__ method for the AI assistant interface lacks a type hint for the parent parameter; update its signature to include a type (e.g., parent: Optional[Any] = None) and add the necessary typing imports (from typing import Optional, Any) so the signature becomes def __init__(self, ctx: ZContext, parent: Optional[Any] = None): and all references to parent remain unchanged; use Optional and Any to avoid forcing a specific GUI base class if unknown.src/zzz_od/context/zzz_context.py (1)
166-181:stop_auto_battle()是非阻塞的,可能存在竞态条件。根据
src/zzz_od/auto_battle/auto_battle_context.py:168-176,stop_auto_battle()内部调用auto_op.stop_running()只是设置一个停止事件,并不等待战斗循环实际退出。这意味着 MCP 服务可能在战斗循环仍在运行时就被停止了。建议考虑是否需要在停止 MCP 服务前等待战斗循环完全退出,或者确认当前行为是否符合预期。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/zzz_od/context/zzz_context.py` around lines 166 - 181, _stop_mcp_service currently calls auto_battle_context.stop_auto_battle() which is non-blocking (it calls auto_op.stop_running()), so MCP may be stopped while the battle loop is still running; fix by either making stop_auto_battle/blocking or by adding a short wait here: after calling auto_battle_context.stop_auto_battle() poll a provided status method (e.g., auto_battle_context.is_running() or a new auto_battle_context.wait_for_stop(timeout)) until the battle loop exits or a sensible timeout elapses, then proceed to call self._mcp_service.stop() and log/force-stop if the timeout is reached; update or add the status/wait method in auto_battle_context/auto_op if needed and reference _stop_mcp_service and auto_battle_context.stop_auto_battle / auto_op.stop_running in the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/zzz_od/context/zzz_context.py`:
- Around line 166-181: _stop_mcp_service currently calls
auto_battle_context.stop_auto_battle() which is non-blocking (it calls
auto_op.stop_running()), so MCP may be stopped while the battle loop is still
running; fix by either making stop_auto_battle/blocking or by adding a short
wait here: after calling auto_battle_context.stop_auto_battle() poll a provided
status method (e.g., auto_battle_context.is_running() or a new
auto_battle_context.wait_for_stop(timeout)) until the battle loop exits or a
sensible timeout elapses, then proceed to call self._mcp_service.stop() and
log/force-stop if the timeout is reached; update or add the status/wait method
in auto_battle_context/auto_op if needed and reference _stop_mcp_service and
auto_battle_context.stop_auto_battle / auto_op.stop_running in the change.
In `@src/zzz_od/gui/view/game_assistant/ai_assistant_interface.py`:
- Line 14: The __init__ method for the AI assistant interface lacks a type hint
for the parent parameter; update its signature to include a type (e.g., parent:
Optional[Any] = None) and add the necessary typing imports (from typing import
Optional, Any) so the signature becomes def __init__(self, ctx: ZContext,
parent: Optional[Any] = None): and all references to parent remain unchanged;
use Optional and Any to avoid forcing a specific GUI base class if unknown.
In `@src/zzz_od/mcp/mcp_service.py`:
- Around line 36-37: Add type hints for the ctx parameter on
_McpBridgeOperation.__init__ and GuiMcpService.__init__ by importing ZContext
under a TYPE_CHECKING guard and annotating ctx: ZContext; specifically, add
"from typing import TYPE_CHECKING" and the conditional import "if TYPE_CHECKING:
from zzz_od.context.zzz_context import ZContext" at the top of the file and
change the method signatures of _McpBridgeOperation.__init__ and
GuiMcpService.__init__ to accept ctx: ZContext.
- Around line 461-470: 在 stop() 中 join 超时后没有检查线程状态并错误地把 self._thread 置为 None;在调用
self._thread.join(timeout=5) 之后用 self._thread.is_alive() 检查线程是否仍在运行,若仍存活则记录警告(例如
self._logger.warning 或 logging.warning),不要把 self._thread 设为
None(或仅在确认线程已停止后才清除),以便能正确反映后台线程状态并便于后续调试;保留对
self._stop_lock、self._stop_auto_battle_loop() 和 server.should_exit 的现有逻辑。
- Around line 93-103: The code in _find_available_port silently fixes _port_max
when it's less than _port_start; update _find_available_port to log a warning
before mutating _port_max so callers know the configuration was adjusted (check
and use the service's logger instance, e.g., self._logger or self.logger), then
set self._port_max = self._port_start and continue as before; keep the
RuntimeError behavior unchanged if no port is found.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a0ebb07d-3db5-42bd-9666-0af42d5f2854
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (6)
pyproject.tomlsrc/zzz_od/context/zzz_context.pysrc/zzz_od/gui/view/game_assistant/ai_assistant_interface.pysrc/zzz_od/gui/view/game_assistant/game_assistant_interface.pysrc/zzz_od/mcp/__init__.pysrc/zzz_od/mcp/mcp_service.py
|
有演示吗? 指导如何启用 |
能让AI帮你打绝区零
Summary by CodeRabbit
发布说明