fix: prevent crash on malformed MCP server config (#5666)#5673
fix: prevent crash on malformed MCP server config (#5666)#5673Clhikari wants to merge 6 commits intoAstrBotDevs:masterfrom
Conversation
Summary of ChangesHello, 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
Changelog
Activity
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
Hey - 我发现了两个问题,并留下了一些高层次的反馈:
add_mcp_server和update_mcp_server中mcpServers的提取/校验逻辑是重复的;建议把这部分抽取到一个小的辅助函数中,以保持行为一致,并简化后续修改。- 在
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>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的 Review。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The
mcpServersextraction/validation logic inadd_mcp_serverandupdate_mcp_serveris duplicated; consider extracting it into a small helper to keep behavior consistent and simplify future changes. - In
McpServersSection.vue#getServers, ifresponse.data.status === 'error'youreturnearly without resettingloadingGettingServers, 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>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": | |||
There was a problem hiding this comment.
issue (complexity): 建议把重复的 mcpServers 解析逻辑和 old_config 归一化逻辑抽取到辅助函数中,在保留所有校验的同时,减少重复代码和分散的类型检查。
你可以通过集中处理重复的 mcpServers 逻辑并把 old_config 归一化到辅助函数中,来降低新增的复杂度。这样既能保留所有现有的校验和行为,又能去除重复代码和分散的类型检查。
1. 将 mcpServers 解析提取为一个辅助函数
add_mcp_server 和 update_mcp_server 中 key == "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, NoneThen 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 = FalseThis 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] = valueThis removes the scattered isinstance(old_config, dict) checks while preserving the current fallback logic (non-dict -> default active, ignore other fields).
astrbot/dashboard/routes/tools.py
Outdated
| 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 |
…com/Clhikari/AstrBot into fix/mcp-server-invalid-config-crash
|
@sourcery-ai review .finally()无论.then()内是否return都会执行,loadingGettingServers肯定会重置 |
There was a problem hiding this comment.
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>请帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续评审。
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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
astrbot/dashboard/routes/tools.py
Outdated
| result = _extract_mcp_server_config( | ||
| server_data["mcpServers"] | ||
| ) | ||
| if isinstance(result, str): | ||
| return Response().error(result).__dict__ | ||
| server_config = result |
There was a problem hiding this comment.
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 是打算在顶层进行控制的,建议使用合并而不是覆盖,例如:
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.
|
@sourcery-ai 这是已有行为,不在本次修改范围内 |
Dt8333
left a comment
There was a problem hiding this comment.
有一些想法:
可否将解析配置JSON做成一个工具函数,并在Add/Update/Test中复用?
这个实现在创建MCP,连接失败时弹出错误信息,不会关闭窗口,但却实际上创建了MCP。这可能不太符合操作直觉(创建成功:关闭窗口;创建失败:报错,窗口不关闭,保留配置)。可能需要一些改进?
astrbot/dashboard/routes/tools.py
Outdated
| key_0 = next(iter(mcp_servers_value)) | ||
| extracted = mcp_servers_value[key_0] | ||
| if not isinstance(extracted, dict): | ||
| return ( |
谢谢你的建议,我这边按你的思路做了最小改动统一了mcpServers解析,并把新增流程改成先测试连接,再保存并启用,启用失败会回滚配置,避免报错但已创建 |
|
@sourcery-ai review |
There was a problem hiding this comment.
嗨——我发现了 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>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进之后的评审。
Original comment in English
Hey - I've found 1 issue, and left some high level feedback:
- In
test_mcp_connectionyou special‑caseValueErrorby 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_confighelper is used in multiple paths with nearly identical error flows; you might want to centralize the mapping from itsValueErrormessages 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@sourcery-ai review |
There was a problem hiding this comment.
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>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- In
test_mcp_connection, the logic that checksstr(e) == "mcpServers 配置不能为空"is brittle; consider using a dedicated exception type or error code from_extract_mcp_server_configinstead of matching on the human-readable message string. - The
_extract_mcp_server_confighelper 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@sourcery-ai review |
There was a problem hiding this comment.
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.帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进之后的代码审查。
Original comment in English
Hey - I've left some high level feedback:
- In
getServers, the early return whenresponse.data.status === 'error'will skip any existing logic in the.thenbody that resetsloadingGettingServerstofalse, so consider moving the loading flag reset into afinallyor 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.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Fixes #5666
用户添加 MCP 服务器时,如果配置 JSON 格式错误(如将
type、url等属性直接写在mcpServers下而非嵌套在服务器名称内),后端提取到的值是字符串而非字典,导致AttributeError: 'str' object has no attribute 'get',整个 MCP 服务器列表 API崩溃,所有已配置的服务器从前端消失。
Modifications / 改动点
后端
astrbot/dashboard/routes/tools.pyget_mcp_servers:遍历配置时跳过非 dict类型的条目,避免单条损坏数据导致整个列表不可用
add_mcp_server/update_mcp_server:校验从mcpServers提取的配置是否为dict,防止将无效数据写入配置文件;处理空
mcpServers对象避免 IndexErrorupdate_mcp_server:防御已损坏的旧配置数据前端
dashboard/src/components/extension/McpServersSection.vuegetServers:检查response.data.status,后端返回错误时显示提示而非静默清空列表saveServer:后端返回错误时显示红色错误提示,而非绿色成功提示This is NOT a breaking change. / 这不是一个破坏性变更。
Screenshots or Test Results / 运行截图或测试结果
Checklist / 检查清单
are new features added in the PR, I have discussed it with the authors through
issues/emails, etc.
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 areintroduced, OR if new dependencies are introduced, they have been added to the
appropriate locations in
requirements.txtandpyproject.toml.Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements:
Original summary in English
Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements:
Bug 修复:
mcpServers负载予以拒绝,而不是持久化无效数据。Original summary in English
Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements:
Original summary in English
Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements:
Bug 修复:
mcpServers负载进行拒绝,而不是持久化无效数据。Original summary in English
Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements:
Original summary in English
Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements:
Bug 修复:
mcpServers负载予以拒绝,而不是持久化无效数据。Original summary in English
Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements:
Original summary in English
Sourcery 总结
提高 MCP 服务器配置处理的健壮性,并在控制面板中清晰展示后端错误。
Bug 修复:
mcpServers结构并跳过非对象条目,防止在列出 MCP 服务器时发生崩溃。mcpServers载荷,而不是保存无效配置。功能增强:
Original summary in English
Summary by Sourcery
改进后端 API 和仪表板 UI 中 MCP 服务器配置处理和错误报告的健壮性。
错误修复:
mcpServers结构并跳过非字典条目,防止崩溃。mcpServers负载,而不是持久化无效配置。增强:
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:
Enhancements: