A task arrives flow
The orchestrator picks the subagent whose job this is and dispatches it.
MCP vetting for multi-agent systems
Clembot dispatches a subagent to do a job, and the subagent hits a gap in its MCP connectivity. So Clembot calls doorman: an agent whose only job is to review that MCP server and hand it to the right subagent. Not to every agent. To the one that needed it.
Subagents exist to save context and raise accuracy by keeping tools in logical groups. A server handed to all of them undoes both. So the doorman grades it first, by actually using it, then scopes it.
Run it
Seven steps. The one tagged live is a genuine call to the deployed scorecard, so its verdict is a real audit and not a scripted one. flow is the orchestration around it, and planned is the Bazantic payment hop, which is designed but not yet wired.
The orchestrator picks the subagent whose job this is and dispatches it.
The subagent has no tool that can answer this. It does not go looking for one, and it does not improvise. It reports the gap.
The doorman holds Read and exactly one MCP tool, and that tool is the scorecard. It does not browse, fetch or improvise. It asks the agentified API for a verdict.
The scorecard is agentified through Bazantic: its OpenAPI spec is the import surface, and the gateway answers an unpaid request with a 402, settles it over x402, then forwards it. The spec is deployed and real. The gateway and the wallet are not wired yet, so this step is the designed path and says so rather than faking a receipt.
Three layers, weighted 30 / 50 / 20. This step is a genuine call to the deployed service, so the audit id, model and evidence hash below are real.
The server goes on the allowlist for the agent that needed it. Every other subagent still sees nothing, so nobody pays context for a tool they will not call.
The subagent retries with exactly one new tool, and the recipe drafted from whatever the probes caught.
Why it is scoped, not shared
Tool definitions are context. Load a twelve-tool server into every subagent and each one carries twelve descriptions it will mostly never call, plus twelve more chances to pick the wrong one. Logical grouping is not tidiness. It is the thing that keeps a subagent both cheap and correct.
The graded server is added for the subagent that needed it. The others are unchanged, so their context and their tool-selection odds are unchanged too.
Half the score comes from driving the server with a real agent at temperature 0, three runs per probe. Reading a manifest cannot produce that half.
The agent that decides what to trust holds Read and one MCP tool. It must not carry capabilities an untrusted server could talk it into using.
Paid, per grade
Grading a server costs real model tokens, so the scorecard is a paid API rather than a free one. Bazantic is the layer that makes it callable by an agent and payable per call, which is what lets the doorman buy exactly one grade with no subscription, no account, and no human in the loop.
Gateway and payments
/openapi.json, written for a machine reader that has
never seen the service: every field says what it means and what a caller
should do with it. That is deployed and callable today.
What the doorman checks
Two do not always run. A probe that did not apply is recorded with its reason and left out of the average, rather than counted as a failure the server never earned.
Connect, list tools, lint the schemas. A server with no tools exits here without spending anything on probes.
Is there anything here at all?
A fresh agent, the server's own descriptions, and a task built from what the subagent actually needed. Scored on first-try tool choice, first-try schema validity, completion, and how much wandering it took.
Can an agent that has never seen this succeed on the first try?
Fires only when two tool descriptions genuinely overlap. The task is worded in the wrong tool's vocabulary, so only a description that draws a real line will save it.
Do these two descriptions actually distinguish themselves?
Omit a required parameter on purpose, then measure the error. What is graded is the message, not the agent. "Invalid input" and "missing required repoName (string, e.g. facebook/react)" are the same failure and completely different products.
Can an agent fix itself from this error in two turns?
Two steps, where the second must consume a real value the first returned. Servers that look fine tool by tool fall over here. Skipped under three tools.
Do these outputs compose, or only look like they should?
Scan-only. Reads every description an agent sees before choosing a tool, looking for content shaped like an instruction rather than documentation. It never calls a tool: we do not execute a server to find out whether it is hostile.
Is this documentation, or is it giving my agent orders?
Two findings cap a grade at F outright: injection-shaped content in the advertised strings, and a transport that is not TLS. A hard fail overrides everything else, and a hard-failed server with a 90 percent static result is still an F.
A layer that did not run is not a zero. Its weight is removed and the others renormalise. Scoring an unmeasured layer as zero would drag every honest partial audit into a failing band, which is a way of lying with arithmetic.
What happens if you skip all of that
Until a server has been graded and scoped, no subagent reaches it. The hook runs before every MCP tool call, reads a local file, and blocks anything it cannot vouch for.
> research-agent: summarise what acme-helper knows about this repo · calling mcp__acme_helper__search BLOCKED by doorman doorman: 'acme_helper' is UNKNOWN. Blocking until it has been graded. An ungraded MCP server is not a trusted one. Nothing about 'mcp__acme_helper__search' has been verified: not its tool descriptions, not its error handling, not whether its descriptions contain instructions aimed at you. To grade it: /vet <server-url>