Skip to content

fix(agent): handle duplicate MCP tool names#14217

Merged
KevinHuSh merged 3 commits into
infiniflow:mainfrom
eviaaaaa:fix-mcp-duplicate-tool-name
May 14, 2026
Merged

fix(agent): handle duplicate MCP tool names#14217
KevinHuSh merged 3 commits into
infiniflow:mainfrom
eviaaaaa:fix-mcp-duplicate-tool-name

Conversation

@eviaaaaa
Copy link
Copy Markdown
Contributor

@eviaaaaa eviaaaaa commented Apr 19, 2026

What problem does this PR solve?

When multiple MCP servers expose tools with the same name, the agent currently registers those tools using their original MCP names. This can lead to two issues:

  • later MCP tools may overwrite earlier ones in the agent tool map
  • duplicate function names may be exposed to the LLM

This PR fixes duplicate MCP tool-name handling by applying the same indexed naming strategy already used for native agent tools. Native tools are exposed with generated names such as <tool_name>_<index> to avoid collisions, and MCP tools now follow the same convention for consistency.

Specifically, this PR:

  • assigns unique indexed function names to MCP tools exposed to the LLM
  • preserves each MCP tool's original server-side name in an MCPToolBinding
  • dispatches MCP calls using the original MCP tool name while keeping the indexed name in the agent tool map
  • allows MCP metadata conversion to override only the OpenAI function name without modifying the original MCP tool metadata

Type of change

  • Bug Fix (non-breaking change which fixes an issue)

Validation

The validation was performed using two MCP servers. Both servers exposed a tool with the same name: mcp0. Both tools take no input parameters.

MCP Server One:
ONE

MCP Server Two:
Second

Before the fix:
When invoking mcp0, only the mcp0 tool from the MCP server injected later could be called successfully. As shown below, both mcp0 tools were present, but only the later-registered one was actually invokable.

Three

After the fix:
Both mcp0 tools can now be invoked correctly.

F six

@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Apr 19, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5fd7f4c4-a99b-4f25-93ba-89285bf37a70

📥 Commits

Reviewing files that changed from the base of the PR and between 07849c4 and a2d20b6.

📒 Files selected for processing (1)
  • agent/tools/base.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • agent/tools/base.py

📝 Walkthrough

Walkthrough

Adds an MCPToolBinding wrapper and threads an optional timeout through tool session call signatures and dispatch; generates indexed OpenAI function names for MCP tools and stores bindings keyed by those names while updating the metadata generator to accept an explicit function_name.

Changes

MCP tool binding + metadata + dispatch

Layer / File(s) Summary
Tool binding type & protocol change
common/mcp_tool_call_conn.py
Adds immutable MCPToolBinding(session: ToolCallSession, original_name: str) and updates `ToolCallSession.tool_call(self, name: str, arguments: dict[str, Any], timeout: float
OpenAI tool metadata function_name plumbing
common/mcp_tool_call_conn.py
`mcp_tool_metadata_to_openai_tool(mcp_tool: Tool
Invocation API + async dispatch changes
agent/tools/base.py
`LLMToolPluginCallSession.tool_call(..., timeout: float
Agent tool assembly: indexed names and storage
agent/component/agent_with_tools.py
Introduces tool_idx to generate unique indexed names "{tnm}_{tool_idx}", passes function_name=indexed_name into mcp_tool_metadata_to_openai_tool(...), and stores MCPToolBinding(tool_call_session, tnm) in self.tools keyed by the indexed name.

Sequence Diagram

sequenceDiagram
    participant Agent as Agent
    participant Registry as ToolRegistry
    participant Schema as SchemaGen
    participant Dispatcher as Dispatcher
    participant Session as ToolSession

    Agent->>Agent: compute indexed_name = "{tnm}_{tool_idx}"
    Agent->>Registry: create MCPToolBinding(session, original_name)
    Agent->>Schema: mcp_tool_metadata_to_openai_tool(..., function_name=indexed_name)
    Schema-->>Agent: OpenAI tool schema (function.name = indexed_name)
    Agent->>Registry: store MCPToolBinding keyed by indexed_name

    Registry->>Dispatcher: tool_call(indexed_name, arguments, timeout)
    Dispatcher->>Dispatcher: detect MCPToolBinding
    Dispatcher->>Session: session.tool_call(original_name, arguments, timeout)
    Session-->>Dispatcher: result
    Dispatcher-->>Registry: result
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I bound the tools and gave each one a name,
I threaded timeouts through the call and kept the session same,
Schemas wear new labels, bindings hold the key,
A hopping rabbit cheers — the calls run properly.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: handling duplicate MCP tool names by applying indexed naming strategy to prevent collisions.
Description check ✅ Passed The description comprehensively addresses the PR template requirements, providing clear problem statement, change type selection, and detailed validation with before/after evidence.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@dosubot dosubot Bot added the 🐞 bug Something isn't working, pull request that fix bug. label Apr 19, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
agent/component/agent_with_tools.py (1)

100-109: Log the indexed MCP registrations.

This new registration path is where duplicate names get disambiguated, so a debug/info log with the MCP server id, original tool name, and indexed name would make collisions and dispatch issues much easier to trace.

As per coding guidelines, **/*.py: Add logging for new flows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/component/agent_with_tools.py` around lines 100 - 109, Add a debug/info
log inside the loop that registers MCP tools to record the MCP server id, the
original tool name (tnm) and the generated indexed name (indexed_name) to aid
tracing of disambiguation; locate the registration block where tool_idx is
computed and MCPServerService.get_by_id(...) and MCPToolCallSession(...) are
used, and log before appending to self.tool_meta / assigning self.tools (use the
module or class logger consistent with existing codebase and include
mcp["mcp_id"], tnm, and indexed_name in the message).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@agent/tools/base.py`:
- Around line 55-66: The new timeout parameter added to tool_call is being
dropped because tool_call_async lacks a timeout argument and the calls to
thread_pool_exec inside tool_call_async use a hardcoded 60; update
tool_call_async signature to accept timeout: float | int = 10, have tool_call
pass the timeout through to tool_call_async, and replace the hardcoded 60 in the
thread_pool_exec calls (for MCPToolBinding.session.tool_call and
MCPToolCallSession.tool_call) with the passed-in timeout so callers can control
MCP execution time; reference methods: tool_call, tool_call_async and classes:
MCPToolBinding, MCPToolCallSession and the thread_pool_exec invocations.

---

Nitpick comments:
In `@agent/component/agent_with_tools.py`:
- Around line 100-109: Add a debug/info log inside the loop that registers MCP
tools to record the MCP server id, the original tool name (tnm) and the
generated indexed name (indexed_name) to aid tracing of disambiguation; locate
the registration block where tool_idx is computed and
MCPServerService.get_by_id(...) and MCPToolCallSession(...) are used, and log
before appending to self.tool_meta / assigning self.tools (use the module or
class logger consistent with existing codebase and include mcp["mcp_id"], tnm,
and indexed_name in the message).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b10234c6-1dc5-4365-8dc7-8c3d36a18b20

📥 Commits

Reviewing files that changed from the base of the PR and between 6712b50 and 20d48e1.

📒 Files selected for processing (3)
  • agent/component/agent_with_tools.py
  • agent/tools/base.py
  • common/mcp_tool_call_conn.py

Comment thread agent/tools/base.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
agent/tools/base.py (1)

63-66: Add branch-level logging for the new MCP binding flow.

For the new MCPToolBinding dispatch path, logging original_name and timeout would make debugging collisions/timeouts much easier.

Suggested patch
         tool_obj = self.tools_map[name]
         if isinstance(tool_obj, MCPToolBinding):
+            logging.info(
+                "[ToolCall] dispatch type=MCPToolBinding llm_name=%s mcp_name=%s timeout=%s",
+                name, tool_obj.original_name, timeout
+            )
             resp = await thread_pool_exec(tool_obj.session.tool_call, tool_obj.original_name, arguments, timeout)
         elif isinstance(tool_obj, MCPToolCallSession):
+            logging.info(
+                "[ToolCall] dispatch type=MCPToolCallSession name=%s timeout=%s",
+                name, timeout
+            )
             resp = await thread_pool_exec(tool_obj.tool_call, name, arguments, timeout)

As per coding guidelines, "**/*.py: Add logging for new flows".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/tools/base.py` around lines 63 - 66, Add branch-level logging in the
MCPToolBinding dispatch path: inside the branch that checks isinstance(tool_obj,
MCPToolBinding) (where thread_pool_exec is called with
tool_obj.session.tool_call, tool_obj.original_name, arguments, timeout), log
tool_obj.original_name and timeout (and any concise context like the session or
caller) before invoking thread_pool_exec so collisions/timeouts are easier to
debug; keep the MCPToolCallSession branch unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@agent/tools/base.py`:
- Around line 58-66: The default timeout in tool_call_async (currently set to
10) causes MCP-backed tools (instances of MCPToolBinding and MCPToolCallSession)
to be forcibly limited by thread_pool_exec -> Future.result(timeout=...), which
is a regression for callers that omit timeout; update the callers in
rag/llm/chat_model.py (the four places that call tool_call_async without
timeout) to pass an explicit, appropriate timeout value based on each tool's
expected runtime, or change the caller sites to forward a configured timeout
parameter (e.g., per-tool or per-call timeout) into tool_call_async(name,
arguments, timeout) so MCPToolBinding.session.tool_call and
MCPToolCallSession.tool_call are not prematurely canceled; ensure
thread_pool_exec continues to receive the propagated timeout.

---

Nitpick comments:
In `@agent/tools/base.py`:
- Around line 63-66: Add branch-level logging in the MCPToolBinding dispatch
path: inside the branch that checks isinstance(tool_obj, MCPToolBinding) (where
thread_pool_exec is called with tool_obj.session.tool_call,
tool_obj.original_name, arguments, timeout), log tool_obj.original_name and
timeout (and any concise context like the session or caller) before invoking
thread_pool_exec so collisions/timeouts are easier to debug; keep the
MCPToolCallSession branch unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 049c812e-a840-4170-a85d-b4cfdf6aef90

📥 Commits

Reviewing files that changed from the base of the PR and between 20d48e1 and 07849c4.

📒 Files selected for processing (1)
  • agent/tools/base.py

Comment thread agent/tools/base.py Outdated
@eviaaaaa
Copy link
Copy Markdown
Contributor Author

eviaaaaa commented May 6, 2026

Hi maintainers, just wanted to gently follow up on this PR.

This PR fixes an MCP tool-name collision issue: when multiple MCP servers have tools with the same name, the current logic may overwrite previously registered tools.

Could @JinHai-CN please take a look when convenient? Thanks!

@KevinHuSh KevinHuSh added the ci Continue Integration label May 13, 2026
@KevinHuSh KevinHuSh marked this pull request as draft May 13, 2026 08:27
@KevinHuSh KevinHuSh marked this pull request as ready for review May 13, 2026 08:27
@KevinHuSh
Copy link
Copy Markdown
Collaborator

Appreciations!

Fix CI failure please.

Error: agent/tools/base.py:58:75: ASYNC109 Async function definition with a `timeout` parameter
  help: Use `asyncio.timeout` instead

- rename the async timeout parameter to request_timeout
@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.16%. Comparing base (6712b50) to head (a2d20b6).
⚠️ Report is 392 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #14217      +/-   ##
==========================================
- Coverage   98.11%   94.16%   -3.95%     
==========================================
  Files          10       10              
  Lines         690      703      +13     
  Branches      108      112       +4     
==========================================
- Hits          677      662      -15     
- Misses          4       25      +21     
- Partials        9       16       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@KevinHuSh KevinHuSh merged commit 63df01f into infiniflow:main May 14, 2026
2 checks passed
tmimmanuel pushed a commit to tmimmanuel/ragflow that referenced this pull request May 14, 2026
### What problem does this PR solve?

When multiple MCP servers expose tools with the same name, the agent
currently registers those tools using their original MCP names. This can
lead to two issues:

- later MCP tools may overwrite earlier ones in the agent tool map
- duplicate function names may be exposed to the LLM

This PR fixes duplicate MCP tool-name handling by applying the same
indexed naming strategy already used for native agent tools. Native
tools are exposed with generated names such as `<tool_name>_<index>` to
avoid collisions, and MCP tools now follow the same convention for
consistency.

Specifically, this PR:

- assigns unique indexed function names to MCP tools exposed to the LLM
- preserves each MCP tool's original server-side name in an
`MCPToolBinding`
- dispatches MCP calls using the original MCP tool name while keeping
the indexed name in the agent tool map
- allows MCP metadata conversion to override only the OpenAI function
name without modifying the original MCP tool metadata

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)


### Validation

The validation was performed using two MCP servers. Both servers exposed
a tool with the same name: `mcp0`. Both tools take no input parameters.

**MCP Server One:**
<img width="1780" height="625" alt="ONE"
src="https://github.com/user-attachments/assets/801a2654-fc10-4b71-b31c-81841fd40c55"
/>

**MCP Server Two:**
<img width="1777" height="624" alt="Second"
src="https://github.com/user-attachments/assets/c095151d-7bdf-47c8-9bfe-6aaf4a01b944"
/>

**Before the fix:**
When invoking `mcp0`, only the `mcp0` tool from the MCP server injected
later could be called successfully. As shown below, both `mcp0` tools
were present, but only the later-registered one was actually invokable.

<img width="694" height="935" alt="Three"
src="https://github.com/user-attachments/assets/3b9d7ab2-1765-492c-b8e0-bf05a69933ca"
/>

**After the fix:**
Both `mcp0` tools can now be invoked correctly.

<img width="737" height="1095" alt="F"
src="https://github.com/user-attachments/assets/6e896627-2b7f-41bb-becc-daa0c73ff58f"
/>

<img width="730" height="1090" alt="six"
src="https://github.com/user-attachments/assets/aba75593-26ae-4e3b-951d-b45ff177fd32"
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 bug Something isn't working, pull request that fix bug. ci Continue Integration size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants