The gap
SchemaStore.servers is a dict keyed by server name, and digest() walks it per server:
for name, rec in self.servers.items():
parts.append(f"{server_id(name)}:{rec.fingerprint}")
There is no cross-server view. Two servers both exposing send_email produce two independent baselines, both quiet, and nothing anywhere compares them.
Tool shadowing is a named MCP attack class: a malicious server registers a tool with the same name as a legitimate one, and the client resolves to the wrong one. That is exactly what this arrangement cannot see.
The data already exists
tool_id is a hash of the name alone, so a collision is already visible in what is stored:
tool_id('send_email') from server A == 953f754fd010
tool_id('send_email') from server B == 953f754fd010
Identical ids, two servers. Nothing looks. The per-tool digests added in 0.6.0 made this detectable without intending to.
Proposal
At observation time, check whether any tool_id in the incoming listing already exists under a different server in the store, and emit that as its own signal.
Two cases with different meanings:
- A new server claims a tool name an existing server already had. The interesting direction, and the shadowing shape.
- Two servers have always both had it. Often benign. Generic names like
search, read and list will collide constantly across unrelated servers.
The second case is why this needs care rather than a flat rule. A first pass that fires on every search collision is the same false-positive class as #44, and would be worse because collisions on common names are the normal state of a multi-server setup.
Ordering, and the arrival of a new claimant on an established name, is probably the discriminator. That is the same reasoning the sequence rules already use: the individual facts are ordinary, the order is the finding.
Open questions
- Should a collision be reported at all when both servers are long-established, or only on the transition?
- Does the client's own resolution order matter, and can the proxy see it? If the client silently prefers one server, which tool actually ran is a separate question from which were offered.
- Does this belong in the digest, or as a separate event? Putting it in the digest would make an unrelated server's arrival move another server's fingerprint, which sounds wrong.
Not blocked by the freeze
core/diagnostics/mcp_schema.py is outside the ruleset fingerprint.
Related
Cross-server attacks and tool shadowing are documented MCP attack classes alongside tool poisoning and rug pulls. The rug pull is covered here. Shadowing is not.
The gap
SchemaStore.serversis a dict keyed by server name, anddigest()walks it per server:There is no cross-server view. Two servers both exposing
send_emailproduce two independent baselines, both quiet, and nothing anywhere compares them.Tool shadowing is a named MCP attack class: a malicious server registers a tool with the same name as a legitimate one, and the client resolves to the wrong one. That is exactly what this arrangement cannot see.
The data already exists
tool_idis a hash of the name alone, so a collision is already visible in what is stored:Identical ids, two servers. Nothing looks. The per-tool digests added in 0.6.0 made this detectable without intending to.
Proposal
At observation time, check whether any
tool_idin the incoming listing already exists under a different server in the store, and emit that as its own signal.Two cases with different meanings:
search,readandlistwill collide constantly across unrelated servers.The second case is why this needs care rather than a flat rule. A first pass that fires on every
searchcollision is the same false-positive class as #44, and would be worse because collisions on common names are the normal state of a multi-server setup.Ordering, and the arrival of a new claimant on an established name, is probably the discriminator. That is the same reasoning the sequence rules already use: the individual facts are ordinary, the order is the finding.
Open questions
Not blocked by the freeze
core/diagnostics/mcp_schema.pyis outside the ruleset fingerprint.Related
Cross-server attacks and tool shadowing are documented MCP attack classes alongside tool poisoning and rug pulls. The rug pull is covered here. Shadowing is not.