Skip to content

feat: mcp tools#2081

Open
kawayiYokami wants to merge 2 commits into
OneDragon-Anything:mainfrom
kawayiYokami:feature/mcp-tools
Open

feat: mcp tools#2081
kawayiYokami wants to merge 2 commits into
OneDragon-Anything:mainfrom
kawayiYokami:feature/mcp-tools

Conversation

@kawayiYokami
Copy link
Copy Markdown
Contributor

@kawayiYokami kawayiYokami commented Mar 7, 2026

能让AI帮你打绝区零

Summary by CodeRabbit

发布说明

  • 新功能
    • 新增AI助手界面,支持启动和停止AI服务
    • 提供客户端配置查看与复制功能
    • 支持键盘快捷键快速启动/停止AI助手
    • AI服务支持屏幕截图、操作控制和自动战斗管理

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 7, 2026

详细概览

本次变更为应用引入了MCP(模型上下文协议)服务集成,包括添加依赖、扩展应用上下文生命周期、创建MCP服务实现、以及新增UI组件来管理和监控MCP服务的启停与配置。

变更内容

Cohort / File(s) 摘要
依赖管理
pyproject.toml
添加两个依赖:mcp==1.12.4typing_extensions>=4.15.0
应用上下文生命周期
src/zzz_od/context/zzz_context.py
新增MCP服务属性和生命周期管理方法,包括 _start_mcp_service_stop_mcp_servicestart_mcp_servicestop_mcp_serviceis_mcp_service_runningget_mcp_client_config_json;集成MCP服务关闭到应用关闭钩子。
UI组件
src/zzz_od/gui/view/game_assistant/ai_assistant_interface.py, src/zzz_od/gui/view/game_assistant/game_assistant_interface.py
新增 AiAssistantInterface 组件,提供MCP服务启停、JSON配置复制、日志显示及键盘快捷键支持;将该组件注册到游戏助手界面。
MCP服务实现
src/zzz_od/mcp/mcp_service.py
实现 GuiMcpService 类,提供三个工具:watch_zzz(屏幕截图+OCR)、click_zzz(点击自动化)、auto_battle_zzz(自动战斗控制);包含完整的启停逻辑、线程管理、错误处理及日志过滤。

时序图

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: 显示成功提示
Loading
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: 返回截图数据
Loading
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: 返回操作结果
Loading

代码审查工作量

🎯 4 (复杂) | ⏱️ ~60 分钟

诗歌

🐰 MCP的魔法在代码中舞动,
屏幕捕获与点击自动化奔腾,
UI按钮串起生命周期的链条,
自动战斗的幽灵在后台徘徊,
一只兔子为这融合的杰作欢呼!✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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 ZContext

Also 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-176stop_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

📥 Commits

Reviewing files that changed from the base of the PR and between e4bfc2a and 9db478b.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • pyproject.toml
  • src/zzz_od/context/zzz_context.py
  • src/zzz_od/gui/view/game_assistant/ai_assistant_interface.py
  • src/zzz_od/gui/view/game_assistant/game_assistant_interface.py
  • src/zzz_od/mcp/__init__.py
  • src/zzz_od/mcp/mcp_service.py

@idk500
Copy link
Copy Markdown
Collaborator

idk500 commented May 4, 2026

有演示吗? 指导如何启用

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants