MCP tool search relevance floor, from first principles
A search for a Cal.com tool returned a Notion one. The mcp tool search relevance floor is the rule that stops it: a result must cover half the query's weighted mass.
The search path is the normal path, and the mcp tool search relevance floor is what keeps it useful
Two things are true about MCP itself, and one thing is specific to how 450+ implements it. Keep them separate, because conflating them is where most misreadings of this piece would come from. The mcp tool search relevance floor described below is the piece that stops a search from returning something confidently wrong when the user asked for a tool the model does not actually have.
What the Model Context Protocol spec actually defines: a server exposes tools via tools/list and a client invokes one via tools/call. That's the whole contract. The spec has no opinion on search, ranking, or how many tools a server may list.
What 450+ does on top of that: past a configured threshold of 30 tools, the connected half of the catalog collapses behind two meta-tools, search_tools and execute_tool, both of which are ordinary entries returned by tools/list like any other tool. search_tools takes a query and returns a ranked subset; execute_tool takes a tool name and arguments and forwards to tools/call under the hood. This is an application-layer design choice, not a protocol requirement. A different MCP gateway could pick 10 as its threshold, or skip search entirely and paginate, or rerank differently. Nothing here should be read as "how MCP works"; it's how one gateway's tool-surface virtualization works.
The threshold counts catalog operations and connected tools together, and 450+'s control-plane catalog alone runs to several dozen operations, so a single connected app is normally enough to trip it. That makes the relevance floor a property of nearly every account on this system rather than a tuning knob reserved for large ones.
Two things stay fixed when the collapse happens. Control-plane operations remain listed individually, and search_tools never returns one. It only ranks over the third-party connector surface. And execute_tool is purely a naming indirection: it resolves to the same tool name and arguments and falls through the identical authorization gates as a direct tools/call, with no separate execution path and no elevated privilege. So the only thing search changes is which third-party tools the model gets to see. Whatever the ranker hands back is the model's entire picture of what the connected accounts can do.
The query that returned a Notion tool for a Cal.com question
A user with only Notion connected called search_tools with list_all_cal_com_schedules, a guess at a tool name from Cal.com. The top result was list_all_notion_users.
Ranking is purely lexical over three fields (tool name, description, connector label) with fixed weights: exact name-token match 5, either-direction name-token prefix match 3, connector-label match 2, description-token match 1. In that query, list and all scored 5 each against list_all_notion_users. cal, com and schedules scored nothing, because nothing in a Notion workspace's tool set contains them. The generic half of the query carried the result and the specific half was silently discarded.
A ranking function with no floor always returns something: it sorts a list and hands back the head of it. When the query names a product the user has not connected, the head of the list is a tool from a different product with a plausible verb in its name. That is worse than an empty result, because a model does not treat a weak match as a weak match. It calls it.
The relevance floor, stated precisely
A tool must account for at least half of the query's own IDF-weighted mass to be returned at all.
Each query token is weighted by inverse document frequency, measured over the set of tools the caller can actually see: not the full catalog, the post-restriction pool. list appears in nearly every tool name in any pool, so its IDF weight is low. schedules, cal and com appear in almost nothing a Notion-only user can reach, so their IDF weight is high. This weight is computed per-query against the current pool, not a stored constant.
Run the incident through it. list_all_notion_users covers only the two cheapest tokens and none of the expensive ones, under half of the query's total IDF mass. It never clears the floor. The caller gets an empty result, and the model reports that no matching tool exists, which is the correct answer: the user has no Cal.com connection, so there is no schedule to list.
Half is the point at which a candidate matched more of the query's informative content than it missed. Below that line, calling it an answer to the question can't be justified, regardless of its raw score.
Why the floor is a fraction of query mass, not a score cutoff
| Approach | Mechanism | Fails because |
|---|---|---|
| Absolute score cutoff (e.g., "return nothing scoring under 6") | Fixed threshold on raw weighted score | Not comparable across queries: a 6-token query accumulates more raw mass than a 1-token query; a query that happens to name the connector picks up +2 on every candidate. Tune for long queries and short ones return empty; tune for short queries and long ones pass junk. Drifts as the pool grows with each new connector. |
| Embedding / cosine similarity rerank | Vector similarity over tool name + description embeddings | Still always returns a ranked list, and a top-k over embeddings has the identical no-floor failure mode, just with better-sounding prose around the wrong answer. Also actively wrong on this failure case: "enumerate records from a SaaS product" is close in embedding space whether the product is Notion or Cal.com, so semantic similarity confuses cross-product tools worse than lexical matching does. Adds index-maintenance cost (rebuild on every connector edit, fork, or new connection) and inference latency inside the tool-call budget, and makes results harder to explain after the fact. |
| IDF-mass floor (relative, per-query) | Candidate must cover at least 50% of the query's own IDF-weighted token mass | Normalizes by construction: the same 50% rule applies to a one-word search and a six-token guessed tool name, and holds after the tenth connector is added, because both the numerator (candidate coverage) and denominator (query mass) are recomputed against the live pool on every call. |
A reranker, however good, is an upgrade to the scoring function underneath. It does not remove the need for a floor, because any function that produces a total order over candidates will hand back a top result even when every candidate is irrelevant. The floor is a separate mechanism: a threshold on whether to return anything at all, evaluated after ranking, not a way of ranking.
Other MCP gateways may reasonably choose a different threshold than 50%, or combine lexical floor with semantic fallback (for example an embedding-based rerank), or expose a confidence score instead of hard-cutting to empty. The claim here is narrower than "this is the only correct design." It's that any floor must be relative to the query's own token-mass distribution, not a fixed score, or it breaks under the two-sided pressure of short exact-name queries and long guessed queries sharing one endpoint.
Two adjacent bugs in the same ranker
The floor is one of three corrections in 450+'s ranker; the other two come from the same root cause: tokens that look like signal and are not.
Description stop-words. set, connection, frozen and more are excluded from description-token scoring, because they are scaffolding present on the description of every merged or frozen tool, regardless of what the tool does. Before this fix, a query containing "connection" gave every merged tool an identical description-match score, ties broke on name order, and the model was handed an arbitrary account's tool. Account labels and frozen field values stay scorable on purpose, because those are terms a caller might legitimately be searching for.
Tie-break by codepoint, never locale collation. Advertised tool names use exactly the character ranges that locale-aware string comparison reorders inconsistently across environments, and the ranked list is sliced to a return limit before it reaches the model. A locale-dependent tie-break would silently decide which tools the model sees at all, depending on server locale, which is not a decision a collation table should be making. Codepoint ordering is deterministic and identical everywhere the ranker runs.
What the floor costs you
Lexical matching has no synonyms. Search "calendar" against a connector whose tools all say "schedule," and the only path to a match is through the description or connector label, if those happen to contain the word. Add a floor and that near-miss stops being a weak result and becomes no result. This is a real, measurable precision/recall trade-off, not a hidden win, and it means genuine synonym-based queries will sometimes return empty where a synonym-aware system would have found the tool.
The justification is that the two failure modes are not symmetric in cost. An empty result is visible: the caller can rephrase, and the model reports it found nothing. A wrong result is invisible until it's already been executed against a live third-party account, and the failure surfaces only after the side effect. Given a choice between a system that sometimes says "no match" for a real synonym and one that sometimes executes the wrong tool against a live account, the floor trades recall for that specific reduction in silent wrong-tool execution. Whether that trade is right for a given deployment depends on how reversible the connected tools' side effects are; for read-only tools the cost of the floor is closer to pure precision gain with little downside.
Restrictions shrink the pool before ranking sees it
Ranking quality is partly a governance outcome, not just an algorithm outcome. Tools withheld by a restriction are excluded from both the tool count (relevant to the 30-tool collapse threshold) and from the search pool entirely. They were never handed to that caller, so they can't be ranked, matched, or leaked as a near-miss. The resolver enforces at every stage: browse, connect, advertise and execute, plus a final check on the fully substituted outbound URL.
The practical effect: a member whose role reaches two connectors searches a small, clean pool where the IDF weights on any query are sharply discriminating, so few candidates share tokens and the floor rarely has to arbitrate close calls. A member with everything connected searches a pool where far more tools compete on generic verbs like list, get, create, so IDF weights compress and the floor does more work. Restrictions function as a security control first, but they are also, mechanically, a precision control on search: a smaller authorized pool is an easier pool to search correctly.
One timing detail worth pinning down: when you change a restriction, search results reflect it about two minutes. removing or suspending a member revokes every live grant in the same transaction as the membership change
When none of this applies to you
If you're building your own MCP server with eight tools and you list them all via tools/list, you never enter search mode. The model reads the whole list on every request, there is no ranker in the path, and a relevance floor solves a problem you don't have. The 30-tool threshold, the IDF-mass floor, and the stop-word list are all irrelevant to a small, fixed, hand-written tool list. Stay there as long as the tool count lets you.
The floor starts to matter when two conditions hold together:
- the pool crosses whatever collapse threshold the gateway enforces (30, in 450+'s case), so a ranker is in the path at all, and
- the caller is a model composing a tool name from memory or inference rather than a human selecting from a visible, complete list.
Both conditions are met the moment a real company connects more than one or two SaaS accounts behind a single endpoint rather than a URL per user, which is why this is an endpoint-level guarantee rather than a per-integration configuration.
For what 450+'s endpoint serves once several accounts sit behind it, see the product overview and a single connector's tool surface such as Asana. For the team-by-team view of who searches for what, the use-cases index is the better starting point.