Bare-name tool alias silently re-registers on a 3rd+ collision, only the 2nd logs a warning

Open Beginner friendly
#515 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
84/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
backend

Research direction

Start in src/mcphub/init.py at ToolUniverse.load_tools() and trace the bare-alias collision handling. Run the three-server reproduction from the issue to observe the alias table and warnings. Done means a name that collides across three or more servers is never silently restored as a bare alias, with behavior matching one of the expected outcomes.

Written by the indexing model from the issue text.

Description

stale
Background

ToolUniverse.load_tools() (src/mcphub/__init__.py) registers a bare
(unqualified) alias for each discovered tool name, so callers can do
tu.run("status") instead of tu.run("iot.status"). When a second server
exposes the same bare name, the collision is detected, a warning is logged,
and the alias is removed (self._alias.pop(t.name, None)) so the ambiguous
name stops resolving.

Removing the alias also erases all memory that the name was ever ambiguous.
If a third (or any later) server also happens to expose that same bare
name, the registration check sees the name is absent from self._alias again
and silently re-adds it, pointing at whichever server loaded last. No warning
fires on this second-and-later collision, only on the very first one. A
caller invoking the bare name afterward gets routed to one of 3+ legitimate
servers with no indication anything is ambiguous.

With the 6 MCP servers currently shipped (iot, utilities, fmsr, wo, tsfm,
vibration) I did not find a live 3-way name collision, so today this is a
latent defect rather than an observed production failure, but it would bite
silently the moment a third server (including a user-added one) reuses a
bare name already used by two others.

Steps to Reproduce

Ran against a real ToolUniverse instance and the real load_tools() code
path; only _connect and _worker.list are stubbed out so the repro doesn't
need three live stdio MCP server processes.

import logging, sys
sys.path.insert(0, "src")
from mcphub import ToolUniverse

logging.basicConfig(level=logging.WARNING)

class FakeTool:
    def __init__(self, name):
        self.name = name
        self.description = ""
        self.inputSchema = {}

class FakeListResult:
    def __init__(self, tools):
        self.tools = tools

FAKE_SERVERS = {
    "server1": [FakeTool("status")],
    "server2": [FakeTool("status")],
    "server3": [FakeTool("status")],
}

tu = ToolUniverse(servers={k: [] for k in FAKE_SERVERS})
tu._connect = lambda name: None                       # skip real stdio connect
tu._worker.list = lambda name: FakeListResult(FAKE_SERVERS[name])

count = tu.load_tools(servers=list(FAKE_SERVERS))
print("tools registered:", count)
print("alias table:", tu._alias)
tu.close()

Run with:

python -m pip install mcp
python repro.py
Observed
WARNING:mcphub:Ambiguous tool name 'status'; use 'server2.status'.
tools registered: 3
alias table: {'status': 'server3.status'}

Only one warning is logged (for the 2nd collision, server2 vs server1). The
3rd collision (server3) silently re-registers the bare alias with no warning
at all, and status now resolves to server3.status as if it had never been
ambiguous.

Expected

Either no bare alias for status after any collision, or a warning on every
collision (2nd, 3rd, ...), not just the first.

Environment
  • Repo HEAD: 248204c4cce9f9e3c3f3234e466f6694a080f6f7
  • Python 3.12.14, python:3.12-slim Docker image
  • mcp client package (latest from PyPI at repro time)

Reproduced end to end in a clean container against current main. Happy to
send a PR if useful; the fix looks like replacing the pop-then-forget pattern
with a separate set of names known to be ambiguous that's never cleared, and
warning on every collision.

Dominant language
Python
Stars
2.3k
Forks
325
Avg merge
16h 33m
Merged PRs (30d)
14

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from IBM/AssetOpsBench

All issues in IBM/AssetOpsBench

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.