Repository navigation
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
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/core/agent/mcp_client.py" line_range="674" />
<code_context>
+ raise RuntimeError(f"MCP server repeated tools/list cursor: {cursor}")
+ seen_cursors.add(cursor)
+ page = await self.session.list_tools(cursor=cursor)
+ tools.extend(page.tools)
+ cursor = page.nextCursor
+
</code_context>
<issue_to_address>
**Normalized tools overwrite each other**
When tools on different pages have distinct MCP names that normalize to the same provider-facing name, `MCPTool` maps both names to the same provider-facing name, and `ToolSet.add_tool()` overwrites one entry; `FunctionToolManager.get_func()` resolves the shared name to the later wrapper, so the model sees an ambiguous name and the earlier tool cannot be called.
Detect colliding provider-facing names and reject or disambiguate them before registering the tools.
Also at `astrbot/core/agent/mcp_client.py:675`.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and the agent can now discover and potentially invoke tools returned on later MCP pages, whose actions may affect external systems. Reverting stops future discovery, but it cannot undo side effects from calls already made.
Blocking findings: astrbot/core/agent/mcp_client.py:674
|
@sourcery-ai review |
There was a problem hiding this comment.
Hey - I've found 3 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="astrbot/core/agent/mcp_client.py" line_range="678-679" />
<code_context>
+ tools.extend(page.tools)
+ cursor = page.nextCursor
+
+ normalized_names: dict[str, str] = {}
+ for tool in tools:
+ # Match MCPTool's provider-facing name before publishing the cache.
+ normalized_name = re.sub(r"[^A-Za-z0-9_-]+", "_", tool.name)
</code_context>
<issue_to_address>
**Tools from different servers collide**
When two active MCP servers expose distinct tool names that normalize to the same provider-facing name, each `MCPClient` checks only its own list, so `FunctionToolManager` publishes colliding names into one tool set. The tool set keeps or resolves only one matching tool, making the other unavailable or routing a call to the wrong server.
Check normalized names across all active MCP clients before publishing tools to the shared provider-facing tool set.
</issue_to_address>
### Comment 2
<location path="astrbot/core/agent/mcp_client.py" line_range="683" />
<code_context>
+ # Match MCPTool's provider-facing name before publishing the cache.
+ normalized_name = re.sub(r"[^A-Za-z0-9_-]+", "_", tool.name)
+ previous_name = normalized_names.get(normalized_name)
+ if previous_name is not None and previous_name != tool.name:
+ raise ValueError(
+ f"MCP tool names {previous_name!r} and {tool.name!r} "
</code_context>
<issue_to_address>
**Duplicate tool definitions remain**
When an MCP server returns the same tool name more than once across its pages, the collision check in `list_tools_and_save()` allows repeated identical names, so duplicate wrappers are registered under one provider-facing name. The manager or tool set keeps one definition, hiding the other schema or behavior from callers.
Reject any already-seen normalized name, including when the original tool names are identical.
Also at `astrbot/core/agent/mcp_client.py:675`.
</issue_to_address>
### Comment 3
<location path="astrbot/core/agent/mcp_client.py" line_range="672" />
<code_context>
+ seen_cursors = set()
+ while cursor is not None:
+ if cursor in seen_cursors:
+ raise RuntimeError(f"MCP server repeated tools/list cursor: {cursor}")
+ seen_cursors.add(cursor)
+ page = await self.session.list_tools(cursor=cursor)
</code_context>
<issue_to_address>
**Cursor text forges log entries**
When a remote MCP server returns the same cursor containing CR/LF characters on consecutive pages during a dashboard connection test, `list_tools_and_save()` interpolates the server-controlled cursor verbatim into the `RuntimeError`; the dashboard connection-test path logs its traceback, allowing the server to inject forged lines into AstrBot’s logs.
Escape or sanitize the cursor before including it in exception messages that are logged.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 3 findings to address first, and tools returned on later pages will now be available to the agent and may be invoked, including tools that make changes outside this client. Reverting restores the old tool list, but it cannot undo side effects of calls already made.
Blocking findings: astrbot/core/agent/mcp_client.py:679, astrbot/core/agent/mcp_client.py:683, astrbot/core/agent/mcp_client.py:672
There was a problem hiding this comment.
Hi, this test is unnecessary, it can be removed.
There was a problem hiding this comment.
Removed as suggested. Thanks :)
Problem
When an MCP server paginates
tools/list, AstrBot reads only the first page. Tools on later pages are missing from the tool list and cannot be called by the model.Solution
Follow
nextCursoruntil all pages have been read, then replace the cached tool list. Use the SDK'sClientSession.list_tools(cursor=...)so request serialization and tool output schema caching stay with the SDK.Changes
MCPClient.list_tools_and_save(), preserving the first page's metadata.1.10.0in both dependency files.Testing
1.10.0.page_2, returningpage-two-ok.The standalone pagination test file has been removed as requested in review.
Summary by Sourcery
Support reliable MCP tool discovery and registration across paginated listings and conflicting provider-facing names.
Bug Fixes:
Enhancements:
Build:
Tests: