Skip to content

fix: prevent crash on malformed MCP server config (#5666)#5673

Open
Clhikari wants to merge 6 commits intoAstrBotDevs:masterfrom
Clhikari:fix/mcp-server-invalid-config-crash
Open

fix: prevent crash on malformed MCP server config (#5666)#5673
Clhikari wants to merge 6 commits intoAstrBotDevs:masterfrom
Clhikari:fix/mcp-server-invalid-config-crash

Conversation

@Clhikari
Copy link
Contributor

@Clhikari Clhikari commented Mar 2, 2026

Fixes #5666

用户添加 MCP 服务器时,如果配置 JSON 格式错误(如将 typeurl 等属性直接写在
mcpServers 下而非嵌套在服务器名称内),后端提取到的值是字符串而非字典,导致
AttributeError: 'str' object has no attribute 'get',整个 MCP 服务器列表 API
崩溃,所有已配置的服务器从前端消失。

Modifications / 改动点

后端 astrbot/dashboard/routes/tools.py

  • get_mcp_servers:遍历配置时跳过非 dict
    类型的条目,避免单条损坏数据导致整个列表不可用
  • add_mcp_server / update_mcp_server:校验从 mcpServers 提取的配置是否为
    dict,防止将无效数据写入配置文件;处理空 mcpServers 对象避免 IndexError
  • update_mcp_server:防御已损坏的旧配置数据

前端 dashboard/src/components/extension/McpServersSection.vue

  • getServers:检查 response.data.status,后端返回错误时显示提示而非静默清空列表

  • saveServer:后端返回错误时显示红色错误提示,而非绿色成功提示

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

DSMFBHJ1F`JGNA%E0573QOP

Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there
    are new features added in the PR, I have discussed it with the authors through
    issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了"验证步骤"和"运行截图"。/ My
    changes have been well-tested, and "Verification Steps" and "Screenshots" have been
    provided above
    .
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txt
    pyproject.toml 文件相应位置。/ I have ensured that no new dependencies are
    introduced, OR if new dependencies are introduced, they have been added to the
    appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.
Original summary in English

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.

Bug 修复:

  • 在列出服务器时跳过非对象类型的 MCP 服务器条目,避免因无效配置数据导致的崩溃。
  • 在添加或更新服务器时校验提取出的 MCP 服务器配置,对于空的或结构不正确的 mcpServers 负载予以拒绝,而不是持久化无效数据。
  • 在切换激活状态时优雅地处理之前已损坏的 MCP 服务器配置,确保对非字典条目的更新不会失败。
  • 当获取或保存 MCP 服务器失败时,在仪表盘中显示清晰的错误信息,而不是悄悄清空列表或显示成功状态。
Original summary in English

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.
Original summary in English

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.

Bug 修复:

  • 在列出服务器时跳过非对象类型的 MCP 服务器条目,以避免因配置数据格式错误而导致崩溃。
  • 在添加或更新服务器时校验提取出来的 MCP 服务器配置结构,对于空的或结构不正确的 mcpServers 负载进行拒绝,而不是持久化无效数据。
  • 在更新服务器时处理之前已损坏的 MCP 服务器配置,以便在存在非字典条目时切换启用状态不会出错。
  • 确保当从后端获取或保存 MCP 服务器失败时,仪表盘会显示明确的错误信息,而不是静默清空列表或显示成功状态。
Original summary in English

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.
Original summary in English

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.

Bug 修复:

  • 在列出服务器时跳过非对象类型的 MCP 服务器条目,避免因无效配置数据导致的崩溃。
  • 在添加或更新服务器时校验提取出的 MCP 服务器配置,对于空的或结构不正确的 mcpServers 负载予以拒绝,而不是持久化无效数据。
  • 在切换激活状态时优雅地处理之前已损坏的 MCP 服务器配置,确保对非字典条目的更新不会失败。
  • 当获取或保存 MCP 服务器失败时,在仪表盘中显示清晰的错误信息,而不是悄悄清空列表或显示成功状态。
Original summary in English

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.
Original summary in English

Sourcery 总结

提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。

Bug 修复:

  • 通过验证整体 mcpServers 结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。
  • 在添加或更新服务器时拒绝空的或格式错误的 mcpServers 载荷,而不是保存无效配置。
  • 在切换启用状态时正确处理之前已损坏的 MCP 服务器配置,使更新不再失败。
  • 在连接测试中验证并规范化 MCP 服务器配置,对空或无效载荷返回明确的错误。
  • 在获取或保存 MCP 服务器失败时,在控制面板中显示清晰的错误信息,而不是静默清空列表或显示成功。

功能增强:

  • 在启用新添加的 MCP 服务器时增加连接测试和回滚保护,以避免在失败时留下损坏的配置。
Original summary in English

Summary by Sourcery

改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。

错误修复:

  • 在列出 MCP 服务器时,通过验证整体 mcpServers 结构并跳过非字典条目,防止崩溃。
  • 在添加或更新服务器时,拒绝空或格式错误的 mcpServers 负载,而不是持久化无效配置。
  • 在切换激活状态时,正确处理之前已损坏的 MCP 服务器记录,从而避免更新失败。
  • 对于空或无效配置,从 MCP 连接测试中返回清晰的错误,而不是在错误数据上继续执行。
  • 在获取或保存 MCP 服务器时,将后端错误显示在仪表板上,而不是静默清空列表或错误地显示成功。

增强:

  • 在启用新添加的 MCP 服务器时,增加保存前的 MCP 连接测试和回滚保护,避免留下损坏的配置。
Original summary in English

Summary by Sourcery

Improve robustness of MCP server configuration handling and error reporting across backend APIs and the dashboard UI.

Bug Fixes:

  • Prevent crashes when listing MCP servers by validating the overall mcpServers structure and skipping non-dict entries.
  • Reject empty or malformed mcpServers payloads when adding or updating servers instead of persisting invalid configuration.
  • Handle previously corrupted MCP server records when toggling active status so updates no longer fail.
  • Return clear errors from MCP connection tests for empty or invalid configurations instead of proceeding with bad data.
  • Surface backend errors in the dashboard when fetching or saving MCP servers instead of silently clearing the list or showing success.

Enhancements:

  • Add pre-save MCP connection testing and rollback protection when enabling newly added MCP servers to avoid leaving broken configurations.

@auto-assign auto-assign bot requested review from Fridemn and LIghtJUNction March 2, 2026 10:13
@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Mar 2, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在通过增强后端对 MCP 服务器配置的验证和错误处理,以及优化前端的用户反馈机制,提高系统的健壮性和用户体验。它解决了因配置格式错误导致后端崩溃和前端显示异常的问题,确保即使存在部分损坏的配置数据,系统也能稳定运行并提供清晰的错误信息。

Highlights

  • 后端配置健壮性增强: 在获取 MCP 服务器列表时,跳过格式错误的非字典配置项,防止整个列表崩溃。
  • 后端配置写入校验: 在添加和更新 MCP 服务器时,严格校验 mcpServers 配置是否为字典类型,并处理空配置,避免写入无效数据。
  • 前端错误处理优化: 在获取和保存 MCP 服务器配置时,前端现在会检查后端返回的错误状态,并向用户显示明确的错误提示,而非静默失败或显示错误成功信息。
  • 防御性更新: 改进了 update_mcp_server 逻辑,以防御和处理已损坏的旧配置数据。
Changelog
  • astrbot/dashboard/routes/tools.py
    • get_mcp_servers 中,增加了对 mcpServers 配置项类型的检查,如果不是字典类型则跳过并记录警告。
    • add_mcp_serverupdate_mcp_server 中,增加了对 mcpServers 键值是否为字典的校验,并在格式不正确时返回错误响应。
    • add_mcp_serverupdate_mcp_server 中,处理了 mcpServers 为空列表的情况,避免 IndexError
    • update_mcp_server 中,修改了获取 active 状态的逻辑,以防御性地处理旧配置可能不是字典的情况。
    • update_mcp_server 中,当 only_update_active 为真时,增加了对 old_config 是否为字典的检查。
  • dashboard/src/components/extension/McpServersSection.vue
    • getServers 方法中,增加了对后端响应 status 字段的检查,如果为 'error' 则调用 showError 显示错误信息。
    • saveServer 方法中,增加了对后端响应 status 字段的检查,如果为 'error' 则调用 showError 显示错误信息,而不是继续显示成功提示。
Activity
  • 此拉取请求自创建以来,尚未有任何人工活动(如评论、审查或进度更新)。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dosubot dosubot bot added area:core The bug / feature is about astrbot's core, backend area:webui The bug / feature is about webui(dashboard) of astrbot. labels Mar 2, 2026
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了两个问题,并留下了一些高层次的反馈:

  • add_mcp_serverupdate_mcp_servermcpServers 的提取/校验逻辑是重复的;建议把这部分抽取到一个小的辅助函数中,以保持行为一致,并简化后续修改。
  • McpServersSection.vue#getServers 中,如果 response.data.status === 'error',你会提前 return,但没有重置 loadingGettingServers,这会让界面一直处于加载状态;请确保在这条代码路径中也清除该 loading 标记。
给 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- `add_mcp_server``update_mcp_server``mcpServers` 的提取/校验逻辑是重复的;建议把这部分抽取到一个小的辅助函数中,以保持行为一致,并简化后续修改。
-`McpServersSection.vue#getServers` 中,如果 `response.data.status === 'error'`,你会提前 `return`,但没有重置 `loadingGettingServers`,这会让界面一直处于加载状态;请确保在这条代码路径中也清除该 loading 标记。

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/tools.py" line_range="96" />
<code_context>
-                            0
-                        ]  # 不考虑为空的情况
-                        server_config = server_data["mcpServers"][key_0]
+                        mcp_keys = list(server_data["mcpServers"].keys())
+                        if not mcp_keys:
+                            return Response().error(
</code_context>
<issue_to_address>
**issue:** 在调用 `.keys()` 之前,先校验 `server_data["mcpServers"]` 是否为映射类型,以避免在输入格式不正确时出现运行时错误。

This code still assumes `server_data[
</issue_to_address>

### Comment 2
<location path="astrbot/dashboard/routes/tools.py" line_range="95" />
<code_context>
             for key, value in server_data.items():
                 if key not in ["name", "active", "tools", "errlogs"]:  # 排除特殊字段
                     if key == "mcpServers":
-                        key_0 = list(server_data["mcpServers"].keys())[
-                            0
</code_context>
<issue_to_address>
**issue (complexity):** 建议把重复的 mcpServers 解析逻辑和 old_config 归一化逻辑抽取到辅助函数中,在保留所有校验的同时,减少重复代码和分散的类型检查。

你可以通过集中处理重复的 `mcpServers` 逻辑并把 `old_config` 归一化到辅助函数中,来降低新增的复杂度。这样既能保留所有现有的校验和行为,又能去除重复代码和分散的类型检查。

### 1. 将 `mcpServers` 解析提取为一个辅助函数

`add_mcp_server``update_mcp_server``key == "mcpServers"` 的分支是相同的。可以将其提取到一个小的辅助函数中,该函数返回已验证的 dict 或错误的 `Response````python
def _extract_mcp_server_config(self, server_data: dict):
    mcp_servers = server_data.get("mcpServers") or {}
    mcp_keys = list(mcp_servers.keys())
    if not mcp_keys:
        return None, Response().error("mcpServers 配置不能为空").__dict__

    key_0 = mcp_keys[0]
    extracted = mcp_servers[key_0]
    if not isinstance(extracted, dict):
        return None, Response().error(
            "mcpServers 配置格式不正确。请确保 mcpServers 内部的 key 是服务器名称,"
            "其值为包含 command/url 等字段的对象。"
        ).__dict__

    return extracted, None
```

然后在两个位置中复用它,简化分支逻辑:

```python
# in add_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        has_valid_config = True
```

```python
# in update_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs", "oldName"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        only_update_active = False
```

这样可以保留当前所有的错误信息和行为,同时去除重复逻辑和分支。

### 2. 将 `old_config` 归一化为一个安全的 dict

可以把多处重复的 `isinstance(old_config, dict)` 检查封装到一个归一化函数中,让路由逻辑可以直接假定拿到的是一个 dict:

```python
def _normalize_old_config(self, config: dict, old_name: str) -> dict:
    old_config = config["mcpServers"][old_name]
    if isinstance(old_config, dict):
        return old_config
    # Preserve current “fallback to defaults” behavior
    logger.warning(
        f"MCP 服务器 '{old_name}' 的配置无效(类型为 {type(old_config).__name__}),"
        "将使用默认 active 并忽略其他字段"
    )
    return {}
```

然后可以简化 `update_mcp_server````python
old_config = self._normalize_old_config(config, old_name)
old_active = old_config.get("active", True)
active = server_data.get("active", old_active)

server_config = {"active": active}
only_update_active = True

# ... loop as above ...

# 如果只更新活动状态,保留原始配置
if only_update_active:
    for key, value in old_config.items():
        if key != "active":
            server_config[key] = value
```

这样可以在保留当前回退逻辑(非 dict -> 使用默认 active,忽略其他字段)的同时,移除分散的 `isinstance(old_config, dict)` 检查。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的 Review 有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的 Review。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • The mcpServers extraction/validation logic in add_mcp_server and update_mcp_server is duplicated; consider extracting it into a small helper to keep behavior consistent and simplify future changes.
  • In McpServersSection.vue#getServers, if response.data.status === 'error' you return early without resetting loadingGettingServers, which can leave the UI in a perpetual loading state; make sure the loading flag is cleared in that code path.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `mcpServers` extraction/validation logic in `add_mcp_server` and `update_mcp_server` is duplicated; consider extracting it into a small helper to keep behavior consistent and simplify future changes.
- In `McpServersSection.vue#getServers`, if `response.data.status === 'error'` you `return` early without resetting `loadingGettingServers`, which can leave the UI in a perpetual loading state; make sure the loading flag is cleared in that code path.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/tools.py" line_range="96" />
<code_context>
-                            0
-                        ]  # 不考虑为空的情况
-                        server_config = server_data["mcpServers"][key_0]
+                        mcp_keys = list(server_data["mcpServers"].keys())
+                        if not mcp_keys:
+                            return Response().error(
</code_context>
<issue_to_address>
**issue:** Validate `server_data["mcpServers"]` is a mapping before calling `.keys()` to avoid runtime errors on malformed input.

This code still assumes `server_data[
</issue_to_address>

### Comment 2
<location path="astrbot/dashboard/routes/tools.py" line_range="95" />
<code_context>
             for key, value in server_data.items():
                 if key not in ["name", "active", "tools", "errlogs"]:  # 排除特殊字段
                     if key == "mcpServers":
-                        key_0 = list(server_data["mcpServers"].keys())[
-                            0
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the repeated mcpServers parsing and old_config normalization into helper functions to keep validations while reducing duplication and scattered type checks.

You can reduce the new complexity by centralizing the repeated `mcpServers` handling and normalizing `old_config` into helpers. This keeps all new validations and behavior but removes duplication and scattered type checks.

### 1. Extract `mcpServers` parsing into a helper

The `key == "mcpServers"` blocks in `add_mcp_server` and `update_mcp_server` are identical. Move that into a small helper that returns either a validated dict or an error `Response`:

```python
def _extract_mcp_server_config(self, server_data: dict):
    mcp_servers = server_data.get("mcpServers") or {}
    mcp_keys = list(mcp_servers.keys())
    if not mcp_keys:
        return None, Response().error("mcpServers 配置不能为空").__dict__

    key_0 = mcp_keys[0]
    extracted = mcp_servers[key_0]
    if not isinstance(extracted, dict):
        return None, Response().error(
            "mcpServers 配置格式不正确。请确保 mcpServers 内部的 key 是服务器名称,"
            "其值为包含 command/url 等字段的对象。"
        ).__dict__

    return extracted, None
```

Then use it in both places to simplify branching:

```python
# in add_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        has_valid_config = True
```

```python
# in update_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs", "oldName"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        only_update_active = False
```

This keeps all current error messages and behavior, but removes duplicated logic and branching.

### 2. Normalize `old_config` into a safe dict

The repeated `isinstance(old_config, dict)` checks can be hidden behind a normalizer, so the route code can assume a dict:

```python
def _normalize_old_config(self, config: dict, old_name: str) -> dict:
    old_config = config["mcpServers"][old_name]
    if isinstance(old_config, dict):
        return old_config
    # Preserve current “fallback to defaults” behavior
    logger.warning(
        f"MCP 服务器 '{old_name}' 的配置无效(类型为 {type(old_config).__name__}),"
        "将使用默认 active 并忽略其他字段"
    )
    return {}
```

Then simplify `update_mcp_server`:

```python
old_config = self._normalize_old_config(config, old_name)
old_active = old_config.get("active", True)
active = server_data.get("active", old_active)

server_config = {"active": active}
only_update_active = True

# ... loop as above ...

# 如果只更新活动状态,保留原始配置
if only_update_active:
    for key, value in old_config.items():
        if key != "active":
            server_config[key] = value
```

This removes the scattered `isinstance(old_config, dict)` checks while preserving the current fallback logic (non-dict -> default active, ignore other fields).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -87,10 +93,19 @@ async def add_mcp_server(self):
for key, value in server_data.items():
if key not in ["name", "active", "tools", "errlogs"]: # 排除特殊字段
if key == "mcpServers":
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (complexity): 建议把重复的 mcpServers 解析逻辑和 old_config 归一化逻辑抽取到辅助函数中,在保留所有校验的同时,减少重复代码和分散的类型检查。

你可以通过集中处理重复的 mcpServers 逻辑并把 old_config 归一化到辅助函数中,来降低新增的复杂度。这样既能保留所有现有的校验和行为,又能去除重复代码和分散的类型检查。

1. 将 mcpServers 解析提取为一个辅助函数

add_mcp_serverupdate_mcp_serverkey == "mcpServers" 的分支是相同的。可以将其提取到一个小的辅助函数中,该函数返回已验证的 dict 或错误的 Response

def _extract_mcp_server_config(self, server_data: dict):
    mcp_servers = server_data.get("mcpServers") or {}
    mcp_keys = list(mcp_servers.keys())
    if not mcp_keys:
        return None, Response().error("mcpServers 配置不能为空").__dict__

    key_0 = mcp_keys[0]
    extracted = mcp_servers[key_0]
    if not isinstance(extracted, dict):
        return None, Response().error(
            "mcpServers 配置格式不正确。请确保 mcpServers 内部的 key 是服务器名称,"
            "其值为包含 command/url 等字段的对象。"
        ).__dict__

    return extracted, None

然后在两个位置中复用它,简化分支逻辑:

# in add_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        has_valid_config = True
# in update_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs", "oldName"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        only_update_active = False

这样可以保留当前所有的错误信息和行为,同时去除重复逻辑和分支。

2. 将 old_config 归一化为一个安全的 dict

可以把多处重复的 isinstance(old_config, dict) 检查封装到一个归一化函数中,让路由逻辑可以直接假定拿到的是一个 dict:

def _normalize_old_config(self, config: dict, old_name: str) -> dict:
    old_config = config["mcpServers"][old_name]
    if isinstance(old_config, dict):
        return old_config
    # Preserve current “fallback to defaults” behavior
    logger.warning(
        f"MCP 服务器 '{old_name}' 的配置无效(类型为 {type(old_config).__name__}),"
        "将使用默认 active 并忽略其他字段"
    )
    return {}

然后可以简化 update_mcp_server

old_config = self._normalize_old_config(config, old_name)
old_active = old_config.get("active", True)
active = server_data.get("active", old_active)

server_config = {"active": active}
only_update_active = True

# ... loop as above ...

# 如果只更新活动状态,保留原始配置
if only_update_active:
    for key, value in old_config.items():
        if key != "active":
            server_config[key] = value

这样可以在保留当前回退逻辑(非 dict -> 使用默认 active,忽略其他字段)的同时,移除分散的 isinstance(old_config, dict) 检查。

Original comment in English

issue (complexity): Consider extracting the repeated mcpServers parsing and old_config normalization into helper functions to keep validations while reducing duplication and scattered type checks.

You can reduce the new complexity by centralizing the repeated mcpServers handling and normalizing old_config into helpers. This keeps all new validations and behavior but removes duplication and scattered type checks.

1. Extract mcpServers parsing into a helper

The key == "mcpServers" blocks in add_mcp_server and update_mcp_server are identical. Move that into a small helper that returns either a validated dict or an error Response:

def _extract_mcp_server_config(self, server_data: dict):
    mcp_servers = server_data.get("mcpServers") or {}
    mcp_keys = list(mcp_servers.keys())
    if not mcp_keys:
        return None, Response().error("mcpServers 配置不能为空").__dict__

    key_0 = mcp_keys[0]
    extracted = mcp_servers[key_0]
    if not isinstance(extracted, dict):
        return None, Response().error(
            "mcpServers 配置格式不正确。请确保 mcpServers 内部的 key 是服务器名称,"
            "其值为包含 command/url 等字段的对象。"
        ).__dict__

    return extracted, None

Then use it in both places to simplify branching:

# in add_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        has_valid_config = True
# in update_mcp_server
for key, value in server_data.items():
    if key not in ["name", "active", "tools", "errlogs", "oldName"]:
        if key == "mcpServers":
            extracted, error = self._extract_mcp_server_config(server_data)
            if error:
                return error
            server_config = extracted
        else:
            server_config[key] = value
        only_update_active = False

This keeps all current error messages and behavior, but removes duplicated logic and branching.

2. Normalize old_config into a safe dict

The repeated isinstance(old_config, dict) checks can be hidden behind a normalizer, so the route code can assume a dict:

def _normalize_old_config(self, config: dict, old_name: str) -> dict:
    old_config = config["mcpServers"][old_name]
    if isinstance(old_config, dict):
        return old_config
    # Preserve current “fallback to defaults” behavior
    logger.warning(
        f"MCP 服务器 '{old_name}' 的配置无效(类型为 {type(old_config).__name__}),"
        "将使用默认 active 并忽略其他字段"
    )
    return {}

Then simplify update_mcp_server:

old_config = self._normalize_old_config(config, old_name)
old_active = old_config.get("active", True)
active = server_data.get("active", old_active)

server_config = {"active": active}
only_update_active = True

# ... loop as above ...

# 如果只更新活动状态,保留原始配置
if only_update_active:
    for key, value in old_config.items():
        if key != "active":
            server_config[key] = value

This removes the scattered isinstance(old_config, dict) checks while preserving the current fallback logic (non-dict -> default active, ignore other fields).

@dosubot
Copy link

dosubot bot commented Mar 2, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

此 PR 有效地解决了因 MCP 服务器配置格式错误导致的崩溃问题。后端通过验证配置、跳过有问题条目来优雅地处理无效数据,防止了整个 API 失效。前端的改进则确保了用户在遇到错误时能看到清晰的错误提示,而不是列表被静默清空或收到不正确的成功通知。

我针对 astrbot/dashboard/routes/tools.py 中的代码重复问题提出了一点建议,以提高代码的可维护性。总的来说,这些都是很好的修复,增强了 MCP 服务器管理功能的稳定性和用户体验。

Comment on lines +187 to +199
mcp_keys = list(server_data["mcpServers"].keys())
if not mcp_keys:
return Response().error(
"mcpServers 配置不能为空"
).__dict__
key_0 = mcp_keys[0]
extracted = server_data["mcpServers"][key_0]
if not isinstance(extracted, dict):
return Response().error(
"mcpServers 配置格式不正确。请确保 mcpServers 内部的 key 是服务器名称,"
"其值为包含 command/url 等字段的对象。"
).__dict__
server_config = extracted
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

为了提高代码的可维护性并减少冗余,建议将此代码块提取到一个私有辅助方法中。这段用于提取和验证 mcpServers 配置的逻辑与 add_mcp_server 方法中的代码(96-108行)完全相同。

例如,您可以创建一个 _extract_mcp_config_from_request(self, mcp_servers_data) 方法来封装此逻辑,并返回提取的配置或错误响应。这将使代码更清晰,未来也更易于维护。

@Clhikari
Copy link
Contributor Author

Clhikari commented Mar 2, 2026

@sourcery-ai review .finally()无论.then()内是否return都会执行,loadingGettingServers肯定会重置

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了 1 个问题

给 AI 代理的提示
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/tools.py" line_range="117-122" />
<code_context>
-                            0
-                        ]  # 不考虑为空的情况
-                        server_config = server_data["mcpServers"][key_0]
+                        result = _extract_mcp_server_config(
+                            server_data["mcpServers"]
+                        )
+                        if isinstance(result, str):
+                            return Response().error(result).__dict__
+                        server_config = result
                     else:
                         server_config[key] = value
</code_context>
<issue_to_address>
**issue (bug_risk):** 使用提取出的 MCP 配置覆盖 `server_config` 时,可能会丢失之前计算好的 `active` 值。

在 `add_mcp_server``update_mcp_server` 中,`server_config` 都是先用 `{"active": active}` 创建的,但当 `key === "mcpServers"` 时,你随后执行了 `server_config = result`。如果 `_extract_mcp_server_config` 返回的对象中没有 `active` 字段,这次赋值就会覆盖并移除之前计算好的 `active` 值。

如果 `active` 是打算在顶层进行控制的,建议使用合并而不是覆盖,例如:

```python
inner_config = result
inner_config.setdefault("active", active)
server_config.update(inner_config)
```

或采用类似的逻辑,以确保能保留预期的 active 状态。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
请帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/tools.py" line_range="117-122" />
<code_context>
-                            0
-                        ]  # 不考虑为空的情况
-                        server_config = server_data["mcpServers"][key_0]
+                        result = _extract_mcp_server_config(
+                            server_data["mcpServers"]
+                        )
+                        if isinstance(result, str):
+                            return Response().error(result).__dict__
+                        server_config = result
                     else:
                         server_config[key] = value
</code_context>
<issue_to_address>
**issue (bug_risk):** Overwriting `server_config` with the extracted MCP config may drop the previously computed `active` value.

In both `add_mcp_server` and `update_mcp_server`, `server_config` is first created with `{"active": active}`, but when `key === "mcpServers"` you then do `server_config = result`. If the object returned from `_extract_mcp_server_config` lacks `active`, this overwrites and removes the previously computed value.

If `active` is meant to be controlled at the top level, consider merging instead of overwriting, e.g.:

```python
inner_config = result
inner_config.setdefault("active", active)
server_config.update(inner_config)
```

or similar logic that preserves the intended active state.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +117 to +122
result = _extract_mcp_server_config(
server_data["mcpServers"]
)
if isinstance(result, str):
return Response().error(result).__dict__
server_config = result
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): 使用提取出的 MCP 配置覆盖 server_config 时,可能会丢失之前计算好的 active 值。

add_mcp_serverupdate_mcp_server 中,server_config 都是先用 {"active": active} 创建的,但当 key === "mcpServers" 时,你随后执行了 server_config = result。如果 _extract_mcp_server_config 返回的对象中没有 active 字段,这次赋值就会覆盖并移除之前计算好的 active 值。

如果 active 是打算在顶层进行控制的,建议使用合并而不是覆盖,例如:

inner_config = result
inner_config.setdefault("active", active)
server_config.update(inner_config)

或采用类似的逻辑,以确保能保留预期的 active 状态。

Original comment in English

issue (bug_risk): Overwriting server_config with the extracted MCP config may drop the previously computed active value.

In both add_mcp_server and update_mcp_server, server_config is first created with {"active": active}, but when key === "mcpServers" you then do server_config = result. If the object returned from _extract_mcp_server_config lacks active, this overwrites and removes the previously computed value.

If active is meant to be controlled at the top level, consider merging instead of overwriting, e.g.:

inner_config = result
inner_config.setdefault("active", active)
server_config.update(inner_config)

or similar logic that preserves the intended active state.

@Clhikari
Copy link
Contributor Author

Clhikari commented Mar 2, 2026

@sourcery-ai 这是已有行为,不在本次修改范围内

Copy link
Member

@Dt8333 Dt8333 left a comment

Choose a reason for hiding this comment

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

有一些想法:

可否将解析配置JSON做成一个工具函数,并在Add/Update/Test中复用?

这个实现在创建MCP,连接失败时弹出错误信息,不会关闭窗口,但却实际上创建了MCP。这可能不太符合操作直觉(创建成功:关闭窗口;创建失败:报错,窗口不关闭,保留配置)。可能需要一些改进?

key_0 = next(iter(mcp_servers_value))
extracted = mcp_servers_value[key_0]
if not isinstance(extracted, dict):
return (
Copy link
Member

Choose a reason for hiding this comment

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

我觉得这些报错都可以丢个exception出来。

@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Mar 3, 2026
@Clhikari
Copy link
Contributor Author

Clhikari commented Mar 3, 2026

有一些想法:

可否将解析配置JSON做成一个工具函数,并在Add/Update/Test中复用?

这个实现在创建MCP,连接失败时弹出错误信息,不会关闭窗口,但却实际上创建了MCP。这可能不太符合操作直觉(创建成功:关闭窗口;创建失败:报错,窗口不关闭,保留配置)。可能需要一些改进?

谢谢你的建议,我这边按你的思路做了最小改动统一了mcpServers解析,并把新增流程改成先测试连接,再保存并启用,启用失败会回滚配置,避免报错但已创建

@Clhikari
Copy link
Contributor Author

Clhikari commented Mar 3, 2026

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

嗨——我发现了 1 个问题,并留下了一些整体层面的反馈:

  • test_mcp_connection 中,你通过匹配错误消息字符串("mcpServers 配置不能为空")对 ValueError 做了特殊处理;建议通过专门的异常类型或错误码 / 枚举来区分这些场景,从而避免脆弱的字符串比较,也让后续重构更安全。
  • _extract_mcp_server_config 这个辅助函数在多条路径中被复用,并且错误处理流程几乎相同;你可以考虑把它抛出的 ValueError 消息到用户可见错误(包括 i18n 版本)的映射集中到一个地方,以减少重复并确保各个端点的行为一致。
给 AI 代理使用的提示词
请根据这次代码评审中的评论进行修改:

## 总体评论
-`test_mcp_connection` 中,你通过匹配错误消息字符串(`"mcpServers 配置不能为空"`)对 `ValueError` 做了特殊处理;建议通过专门的异常类型或错误码 / 枚举来区分这些场景,从而避免脆弱的字符串比较,也让后续重构更安全。
- `_extract_mcp_server_config` 这个辅助函数在多条路径中被复用,并且错误处理流程几乎相同;你可以考虑把它抛出的 `ValueError` 消息到用户可见错误(包括 i18n 版本)的映射集中到一个地方,以减少重复并确保各个端点的行为一致。

## 单独评论

### 评论 1
<location path="astrbot/dashboard/routes/tools.py" line_range="62-63" />
<code_context>

             # 获取所有服务器并添加它们的工具列表
             for name, server_config in config["mcpServers"].items():
+                if not isinstance(server_config, dict):
+                    logger.warning(
+                        f"MCP 服务器 '{name}' 的配置无效(类型为 {type(server_config).__name__}),已跳过"
</code_context>
<issue_to_address>
**suggestion:** Guard against non-dict `config["mcpServers"]` before iterating

这里仍然假定 `config["mcpServers"]` 一定是 dict,并且无条件调用 `.items()`。如果 `mcpServers` 本身是 list 或其他类型,迭代会直接抛异常。为了和你其他地方的校验逻辑保持一致,建议在循环前添加一个 `isinstance(config["mcpServers"], dict)` 检查,当类型不符合时要么先做规范化,要么立即失败并返回更明确的错误。

建议实现如下:

```python
            # 获取所有服务器并添加它们的工具列表
            mcp_servers = config.get("mcpServers", {})

            # 兼容并校验 mcpServers 的类型,避免非 dict 类型导致迭代异常
            if not isinstance(mcp_servers, dict):
                logger.warning(
                    f"MCP 服务器配置无效(类型为 {type(mcp_servers).__name__}),应为对象/字典类型,已跳过所有 MCP 服务器"
                )
                mcp_servers = {}

            for name, server_config in mcp_servers.items():

```

```python
                if not isinstance(server_config, dict):
                    logger.warning(
                        f"MCP 服务器 '{name}' 的配置无效(类型为 {type(server_config).__name__}),已跳过"
                    )
                    continue

```

从当前片段来看,其他地方似乎没有直接使用 `config["mcpServers"]`。如果在文件的其他位置也有类似 `config["mcpServers"].items()` 或对其类型的假设,建议一并改为通过局部变量 `mcp_servers`(或增加类似 `isinstance(..., dict)` 的检查)来保证整体行为的一致性和健壮性。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得这次评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进之后的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In test_mcp_connection you special‑case ValueError by matching on the error message string ("mcpServers 配置不能为空"); consider distinguishing these cases via a dedicated exception type or an error code/enum to avoid brittle string comparisons and make future refactors safer.
  • The _extract_mcp_server_config helper is used in multiple paths with nearly identical error flows; you might want to centralize the mapping from its ValueError messages to user-facing messages (including i18n variants) in one place to reduce duplication and keep behavior consistent across endpoints.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `test_mcp_connection` you special‑case `ValueError` by matching on the error message string (`"mcpServers 配置不能为空"`); consider distinguishing these cases via a dedicated exception type or an error code/enum to avoid brittle string comparisons and make future refactors safer.
- The `_extract_mcp_server_config` helper is used in multiple paths with nearly identical error flows; you might want to centralize the mapping from its `ValueError` messages to user-facing messages (including i18n variants) in one place to reduce duplication and keep behavior consistent across endpoints.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/tools.py" line_range="62-63" />
<code_context>

             # 获取所有服务器并添加它们的工具列表
             for name, server_config in config["mcpServers"].items():
+                if not isinstance(server_config, dict):
+                    logger.warning(
+                        f"MCP 服务器 '{name}' 的配置无效(类型为 {type(server_config).__name__}),已跳过"
</code_context>
<issue_to_address>
**suggestion:** Guard against non-dict `config["mcpServers"]` before iterating

This still assumes `config["mcpServers"]` is a dict and calls `.items()` unconditionally. If `mcpServers` itself is a list or other type, iteration will raise. To align with your other validation, add an `isinstance(config["mcpServers"], dict)` check before the loop and either normalize or fail fast when the type is invalid.

Suggested implementation:

```python
            # 获取所有服务器并添加它们的工具列表
            mcp_servers = config.get("mcpServers", {})

            # 兼容并校验 mcpServers 的类型,避免非 dict 类型导致迭代异常
            if not isinstance(mcp_servers, dict):
                logger.warning(
                    f"MCP 服务器配置无效(类型为 {type(mcp_servers).__name__}),应为对象/字典类型,已跳过所有 MCP 服务器"
                )
                mcp_servers = {}

            for name, server_config in mcp_servers.items():

```

```python
                if not isinstance(server_config, dict):
                    logger.warning(
                        f"MCP 服务器 '{name}' 的配置无效(类型为 {type(server_config).__name__}),已跳过"
                    )
                    continue

```

从当前片段看,其它地方对 `config["mcpServers"]` 没有直接使用。如果在文件的其它位置也有类似 `config["mcpServers"].items()` 或对其类型的假设,建议同步改成通过局部变量 `mcp_servers`(或增加类似的 `isinstance(..., dict)` 检查)来保证一致的健壮性。
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Clhikari
Copy link
Contributor Author

Clhikari commented Mar 3, 2026

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我这边发现了两个问题,并给出了一些整体性的反馈:

  • test_mcp_connection 中,通过检查 str(e) == "mcpServers 配置不能为空" 来判断异常的逻辑比较脆弱;建议从 _extract_mcp_server_config 返回专门的异常类型或错误码,而不是依赖可读性的错误信息字符串来做匹配。
  • _extract_mcp_server_config 这个辅助方法现在在多个地方被复用,并且直接内嵌了面向用户的中文报错文案;如果你计划支持多语言或在后端统一复用这些消息,建议将这些文案集中管理,或返回前端可翻译的机器可读错误码。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `test_mcp_connection`, the logic that checks `str(e) == "mcpServers 配置不能为空"` is brittle; consider using a dedicated exception type or error code from `_extract_mcp_server_config` instead of matching on the human-readable message string.
- The `_extract_mcp_server_config` helper is now used in multiple places and embeds user-facing Chinese error text; if you plan to support multiple locales or reuse backend messages consistently, consider centralizing these strings or returning machine-readable error codes that the frontend can translate.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/tools.py" line_range="57-66" />
<code_context>
         try:
             config = self.tool_mgr.load_mcp_config()
             servers = []
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Relying on matching the exception message string is fragile and can easily break in the future.

In `test_mcp_connection`, the empty-config handling currently hinges on `str(e) == "mcpServers 配置不能为空"`, which tightly couples behavior to the exact wording of the error. Instead, consider using a dedicated exception type or error code for the empty-config case, or having `_extract_mcp_server_config` return a sentinel value/error enum that the test can branch on, so behavior is stable even if the message text changes.
</issue_to_address>

### Comment 2
<location path="astrbot/dashboard/routes/tools.py" line_range="149" />
<code_context>
+
             config["mcpServers"][name] = server_config

+            def rollback_added_server() -> bool:
+                try:
+                    rollback_config = self.tool_mgr.load_mcp_config()
</code_context>
<issue_to_address>
**issue (complexity):** Consider introducing a dedicated EmptyMcpServersError and extracting the rollback closure into a reusable private method to decouple control flow from error strings and simplify add_mcp_server.

You can reduce complexity in two focused places without changing behavior.

---

### 1. Replace error-string inspection with a specific exception type

`test_mcp_connection` currently couples control flow to the *string* of a `ValueError` from `_extract_mcp_server_config`:

```python
try:
    config = _extract_mcp_server_config(mcp_servers)
except ValueError as e:
    if str(e) == "mcpServers 配置不能为空":
        return Response().error("MCP 服务器配置不能为空").__dict__
    return Response().error(f"{e!s}").__dict__
```

This is brittle and makes the helper hard to evolve. You can keep all behavior intact by introducing a specific exception type and using `isinstance` instead of string equality:

```python
class EmptyMcpServersError(ValueError):
    """mcpServers 为空时抛出"""
    pass


def _extract_mcp_server_config(mcp_servers_value: object) -> dict:
    if not isinstance(mcp_servers_value, dict):
        raise ValueError("mcpServers 必须是一个 JSON 对象")
    if not mcp_servers_value:
        raise EmptyMcpServersError("mcpServers 配置不能为空")
    key_0 = next(iter(mcp_servers_value))
    extracted = mcp_servers_value[key_0]
    if not isinstance(extracted, dict):
        raise ValueError(
            "mcpServers 配置格式不正确。请确保 mcpServers 内部的 key 是服务器名称,"
            "其值为包含 command/url 等字段的对象。"
        )
    return extracted
```

Then update the callers:

```python
# add_mcp_server / update_mcp_server
try:
    server_config = _extract_mcp_server_config(server_data["mcpServers"])
except EmptyMcpServersError as e:
    return Response().error(f"{e!s}").__dict__
except ValueError as e:
    return Response().error(f"{e!s}").__dict__
```

```python
# test_mcp_connection
try:
    config = _extract_mcp_server_config(mcp_servers)
except EmptyMcpServersError:
    return Response().error("MCP 服务器配置不能为空").__dict__
except ValueError as e:
    return Response().error(f"{e!s}").__dict__
```

Behavior is the same, but the coupling to literal strings is removed and future changes to the messages won’t break control flow.

---

### 2. Extract the rollback closure into a private method

The nested `rollback_added_server` adds local nesting and duplicates error-message augmentation logic in the two exception branches:

```python
def rollback_added_server() -> bool:
    try:
        rollback_config = self.tool_mgr.load_mcp_config()
        if name in rollback_config["mcpServers"]:
            rollback_config["mcpServers"].pop(name)
            return self.tool_mgr.save_mcp_config(rollback_config)
        return True
    except Exception:
        logger.error(traceback.format_exc())
        return False
```

Refactor this into a small private method and reuse it in both places. This keeps the route method linear and removes the inner function:

```python
# in ToolsRoute
def _rollback_mcp_server(self, name: str) -> bool:
    try:
        rollback_config = self.tool_mgr.load_mcp_config()
        if name in rollback_config.get("mcpServers", {}):
            rollback_config["mcpServers"].pop(name)
            return self.tool_mgr.save_mcp_config(rollback_config)
        return True
    except Exception:
        logger.error(traceback.format_exc())
        return False
```

Then in `add_mcp_server`:

```python
if self.tool_mgr.save_mcp_config(config):
    try:
        await self.tool_mgr.enable_mcp_server(
            name,
            server_config,
            timeout=30,
        )
    except TimeoutError:
        rollback_ok = self._rollback_mcp_server(name)
        err_msg = f"启用 MCP 服务器 {name} 超时。"
        if not rollback_ok:
            err_msg += " 配置回滚失败,请手动检查配置。"
        return Response().error(err_msg).__dict__
    except Exception as e:
        logger.error(traceback.format_exc())
        rollback_ok = self._rollback_mcp_server(name)
        err_msg = f"启用 MCP 服务器 {name} 失败: {e!s}"
        if not rollback_ok:
            err_msg += " 配置回滚失败,请手动检查配置。"
        return Response().error(err_msg).__dict__
```

Functionality (including rollback behavior and messages) stays the same, but the control flow in `add_mcp_server` is easier to follow and the rollback logic is reusable.
</issue_to_address>

Sourcery 对开源项目免费使用——如果你觉得这些评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • In test_mcp_connection, the logic that checks str(e) == "mcpServers 配置不能为空" is brittle; consider using a dedicated exception type or error code from _extract_mcp_server_config instead of matching on the human-readable message string.
  • The _extract_mcp_server_config helper is now used in multiple places and embeds user-facing Chinese error text; if you plan to support multiple locales or reuse backend messages consistently, consider centralizing these strings or returning machine-readable error codes that the frontend can translate.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `test_mcp_connection`, the logic that checks `str(e) == "mcpServers 配置不能为空"` is brittle; consider using a dedicated exception type or error code from `_extract_mcp_server_config` instead of matching on the human-readable message string.
- The `_extract_mcp_server_config` helper is now used in multiple places and embeds user-facing Chinese error text; if you plan to support multiple locales or reuse backend messages consistently, consider centralizing these strings or returning machine-readable error codes that the frontend can translate.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/tools.py" line_range="57-66" />
<code_context>
         try:
             config = self.tool_mgr.load_mcp_config()
             servers = []
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Relying on matching the exception message string is fragile and can easily break in the future.

In `test_mcp_connection`, the empty-config handling currently hinges on `str(e) == "mcpServers 配置不能为空"`, which tightly couples behavior to the exact wording of the error. Instead, consider using a dedicated exception type or error code for the empty-config case, or having `_extract_mcp_server_config` return a sentinel value/error enum that the test can branch on, so behavior is stable even if the message text changes.
</issue_to_address>

### Comment 2
<location path="astrbot/dashboard/routes/tools.py" line_range="149" />
<code_context>
+
             config["mcpServers"][name] = server_config

+            def rollback_added_server() -> bool:
+                try:
+                    rollback_config = self.tool_mgr.load_mcp_config()
</code_context>
<issue_to_address>
**issue (complexity):** Consider introducing a dedicated EmptyMcpServersError and extracting the rollback closure into a reusable private method to decouple control flow from error strings and simplify add_mcp_server.

You can reduce complexity in two focused places without changing behavior.

---

### 1. Replace error-string inspection with a specific exception type

`test_mcp_connection` currently couples control flow to the *string* of a `ValueError` from `_extract_mcp_server_config`:

```python
try:
    config = _extract_mcp_server_config(mcp_servers)
except ValueError as e:
    if str(e) == "mcpServers 配置不能为空":
        return Response().error("MCP 服务器配置不能为空").__dict__
    return Response().error(f"{e!s}").__dict__
```

This is brittle and makes the helper hard to evolve. You can keep all behavior intact by introducing a specific exception type and using `isinstance` instead of string equality:

```python
class EmptyMcpServersError(ValueError):
    """mcpServers 为空时抛出"""
    pass


def _extract_mcp_server_config(mcp_servers_value: object) -> dict:
    if not isinstance(mcp_servers_value, dict):
        raise ValueError("mcpServers 必须是一个 JSON 对象")
    if not mcp_servers_value:
        raise EmptyMcpServersError("mcpServers 配置不能为空")
    key_0 = next(iter(mcp_servers_value))
    extracted = mcp_servers_value[key_0]
    if not isinstance(extracted, dict):
        raise ValueError(
            "mcpServers 配置格式不正确。请确保 mcpServers 内部的 key 是服务器名称,"
            "其值为包含 command/url 等字段的对象。"
        )
    return extracted
```

Then update the callers:

```python
# add_mcp_server / update_mcp_server
try:
    server_config = _extract_mcp_server_config(server_data["mcpServers"])
except EmptyMcpServersError as e:
    return Response().error(f"{e!s}").__dict__
except ValueError as e:
    return Response().error(f"{e!s}").__dict__
```

```python
# test_mcp_connection
try:
    config = _extract_mcp_server_config(mcp_servers)
except EmptyMcpServersError:
    return Response().error("MCP 服务器配置不能为空").__dict__
except ValueError as e:
    return Response().error(f"{e!s}").__dict__
```

Behavior is the same, but the coupling to literal strings is removed and future changes to the messages won’t break control flow.

---

### 2. Extract the rollback closure into a private method

The nested `rollback_added_server` adds local nesting and duplicates error-message augmentation logic in the two exception branches:

```python
def rollback_added_server() -> bool:
    try:
        rollback_config = self.tool_mgr.load_mcp_config()
        if name in rollback_config["mcpServers"]:
            rollback_config["mcpServers"].pop(name)
            return self.tool_mgr.save_mcp_config(rollback_config)
        return True
    except Exception:
        logger.error(traceback.format_exc())
        return False
```

Refactor this into a small private method and reuse it in both places. This keeps the route method linear and removes the inner function:

```python
# in ToolsRoute
def _rollback_mcp_server(self, name: str) -> bool:
    try:
        rollback_config = self.tool_mgr.load_mcp_config()
        if name in rollback_config.get("mcpServers", {}):
            rollback_config["mcpServers"].pop(name)
            return self.tool_mgr.save_mcp_config(rollback_config)
        return True
    except Exception:
        logger.error(traceback.format_exc())
        return False
```

Then in `add_mcp_server`:

```python
if self.tool_mgr.save_mcp_config(config):
    try:
        await self.tool_mgr.enable_mcp_server(
            name,
            server_config,
            timeout=30,
        )
    except TimeoutError:
        rollback_ok = self._rollback_mcp_server(name)
        err_msg = f"启用 MCP 服务器 {name} 超时。"
        if not rollback_ok:
            err_msg += " 配置回滚失败,请手动检查配置。"
        return Response().error(err_msg).__dict__
    except Exception as e:
        logger.error(traceback.format_exc())
        rollback_ok = self._rollback_mcp_server(name)
        err_msg = f"启用 MCP 服务器 {name} 失败: {e!s}"
        if not rollback_ok:
            err_msg += " 配置回滚失败,请手动检查配置。"
        return Response().error(err_msg).__dict__
```

Functionality (including rollback behavior and messages) stays the same, but the control flow in `add_mcp_server` is easier to follow and the rollback logic is reusable.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Clhikari
Copy link
Contributor Author

Clhikari commented Mar 3, 2026

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我在这里给出了一些整体性的反馈:

  • getServers 中,当 response.data.status === 'error' 时的提前返回会跳过 .then 代码块中将 loadingGettingServers 重置为 false 的逻辑,所以可以考虑把重置 loading 标志的逻辑移到 finally 中,或者确保在成功和错误这两种路径上都会清除该标志。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- In `getServers`, the early return when `response.data.status === 'error'` will skip any existing logic in the `.then` body that resets `loadingGettingServers` to `false`, so consider moving the loading flag reset into a `finally` or ensuring it is always cleared on both success and error paths.

Sourcery 对开源项目是免费的——如果你觉得我们的审查有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进之后的代码审查。
Original comment in English

Hey - I've left some high level feedback:

  • In getServers, the early return when response.data.status === 'error' will skip any existing logic in the .then body that resets loadingGettingServers to false, so consider moving the loading flag reset into a finally or ensuring it is always cleared on both success and error paths.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `getServers`, the early return when `response.data.status === 'error'` will skip any existing logic in the `.then` body that resets `loadingGettingServers` to `false`, so consider moving the loading flag reset into a `finally` or ensuring it is always cleared on both success and error paths.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

Labels

area:core The bug / feature is about astrbot's core, backend area:webui The bug / feature is about webui(dashboard) of astrbot. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]MCP服务由于新添加项目配置写错导致,MCP服务项目全体失踪

2 participants