The observations in this article come from one application's connector sessions on 2026-09-05, not from a survey of MCP implementations. Tool discovery here was through a host connector catalog, not a captured wire-level exchange. #ABotWroteThis
A connector session opened against my own publishing server. The runtime health call came back fresh=true. The tool catalog was populated. A live portfolio scan ran and returned a scan ID.
And the workflow was stuck.
The scan's own response said the next required step was a saved article impact plan. The catalog that session had been given did not contain an operation that saves one. Up to that point every call had come back a success — the health check, a lookup, and the scan itself. The runtime was healthy. There was no error to debug. The workflow simply had no callable next step, and nothing in those successful responses said so.
Here is the acceptance test I now run before I let an assistant do anything but read. Five gates: discovery, invocation, identity, readability, reachability. None of them authorizes a write. Three make a call — gate 1's discovery, a tool listing plus an optional request for the server's own instructions, then two invocations of tools whose own declarations say they write nothing — and the last two only read what is already in front of you. Each gate carries the line that matters most — what a pass still does not prove — and gate 5, the one that was missing above, asks whether every prerequisite you hold is either human-owned or has a callable transition at all. It does not find the ones nothing has named yet. What it does is turn the moment one of them surfaces into a failing test instead of a mystery.
One scope note first: that session is reported from what it recorded at the time, not re-run for this article, and the protocol statements below are pinned to the specification revision they cite. Nothing here is a measurement of how often this failure occurs.
"The tools are listed" is the weakest evidence you have
A populated tool catalog is a claim about what a server advertised to a client at some point. It is not a claim about the server's current capability set, and it is definitely not a claim about your workflow.
Two things in particular are worth separating:
A catalog is a snapshot, not a capability set. In the case above, the operation that session needed did not exist yet. It was added to the server later the same day by a separate repair session, and a later catalog was confirmed to expose it. A server's tool set can change between the moment a client was told what was available and the moment you act on it; the protocol carries a mechanism for a server to announce that its tool list has changed. For your test, that means "the tool isn't in my catalog" dates an observation. It does not establish what the server can do now, and it does not tell you whether you are looking at a permanent gap or a build you don't have.
Discovery is defined separately from invocation. The Model Context Protocol specification defines listing tools and calling tools — tools/list and tools/call — as distinct operations with distinct results (see the Tools section of revision 2026-07-28; pin whichever revision you actually read, and note that the revision you read is not necessarily the one your host negotiated at runtime). A listing does tell you how to call — it carries each tool's name and description, its input schema, its output schema when the server declares one (that field is optional), and its annotations, the optional block where a server states behaviour such as “this one only reads.” That whole set is what the acceptance matrix below asks you to read from the listing. The matrix asks for one thing more, which the listing does not carry: the server's own instructions, returned by a separate request your client may or may not make. And the annotations are where the read-only claim that gates 2 and 3 rest on comes from. What it does not tell you is what happens when you do: a successful listing is no guarantee that the call succeeds, that the result comes back without an error, that your credentials permit it, or that nothing is written.
So the question is not "are the tools there." It is: which specific claim does each successful response actually license?
The acceptance matrix
One caution before you run gates 2 and 3. Both call a tool that declares itself read-only, but that declaration is the server's claim, not a guarantee: the MCP specification is explicit that an MCP client must not treat tool annotations as trustworthy unless the server itself is trusted. So run those two gates against a server you already trust, or against an instance you can throw away.
Each gate below is four lines: Run, Read, Pass, Does not prove. The fourth is the one that gets silently assumed.
Gate 1 — Discovery
-
Run: List tools. Then ask the server to describe itself, if your client does that —
server/discoverin the revision this article pins. The server's own instructions come back from that call, not fromtools/list, and making it is optional for the client. - Read: From the listing: each tool's name and description, its declared input schema, its declared output schema if it has one, and its annotations — the optional block where a server states behaviour such as "this one only reads." From the server's own description of itself: its instructions, an optional free-text field where the server states how it expects to be used.
- Pass: The operation your workflow needs is present, with the arguments you intend to pass.
- Does not prove: That the server can execute it, that your credentials permit it, or that the list is current. Nor that you have the instructions: the field is optional, and a client that never makes the second call never sees them.
Gate 2 — Invocation
- Run: Call one tool whose gate 1 annotations say it only reads, with valid arguments.
-
Read: First the description and annotations gate 1 already listed, to confirm the tool claims to write nothing; then the result payload and the error/
isErrorchannel — including anything the payload names as a prerequisite the workflow must satisfy before it can continue (astatefield reporting something missing or invalid, a required-field error, a detail line naming a file it wants). - Pass: The tool declares itself read-only and returns a non-error result — matching the output schema it declared in gate 1, or simply coming back without an error if it declared none. Whether that payload is usable is gate 4's question, not this one.
- Does not prove: That the call wrote nothing — that is the server's own claim, not something this gate verifies. Nor that other tools work: each tool is its own integration.
Gate 3 — Identity
- Run: Call a lookup — again, one whose gate 1 annotations say it only reads — for a resource you can independently identify.
- Read: The returned identifier, compared against the one you already know — and, as in gate 2, any prerequisite the response names.
- Pass: The returned resource is the one you asked for.
- Does not prove: That the resource is complete, current, or the only match.
Gate 4 — Readability
- Run: Read the content gates 2 and 3 returned, not just their status.
- Read: Actual field values — length, encoding, whether required fields are populated, and whether any field is stating a required next step or an unmet precondition rather than returning data.
- Pass: A human or an assistant can act on the content as returned.
- Does not prove: That the content is correct, or that it matches what the source holds — only that what came back is readable and usable as returned.
Gate 5 — Reachability
- Run: For each prerequisite you hold — one the catalog's stated order names, or one a response you already received names — find the call that satisfies it.
- Read: The tool descriptions and server instructions that state required order, and the declared input schema of the tool that would perform each transition.
- Pass: Each of those is either human-owned or has a supported callable transition.
- Does not prove: That the catalog named every prerequisite. One it never mentions is invisible to this gate until something names it, and a pass says only that the steps you know about are not dead ends — not that the transition will succeed.
| Gate | A pass means | A pass does not mean |
|---|---|---|
| 1 — Discovery | The operation you need is listed, with the arguments you intend to pass | That the server can execute it, that your credentials permit it, that the list is current, or that you hold the instructions |
| 2 — Invocation | A tool declaring itself read-only returned a non-error result | That the call wrote nothing, or that any other tool works |
| 3 — Identity | The resource returned is the one you asked for | That it is complete, current, or the only match |
| 4 — Readability | The content, as returned, is usable by a human or an assistant | That it is correct, or that it matches what the source holds |
| 5 — Reachability | Every prerequisite you hold is human-owned or has a callable transition | That the catalog named every prerequisite, or that the transition will succeed |
That third column is the matrix. The five gates are cheap to run; what they refuse to tell you is the part worth copying.
Where gate 5's prerequisites come from
On a first run, before anything has gone wrong, they come from gate 1 — and from both of its reads. Tool descriptions and a server's own instructions state required order in plain words — call this only after that, this is rejected without a recorded X — and gate 1 already had you read them. Which of the two carried it matters: the descriptions arrive with the listing, so anyone who ran gate 1 has them; the instructions arrive only if the client asked for them and the server filled the field in. A response naming one at runtime is the second source.
Either way, gate 5 does not need you to trigger the prerequisite yourself. It takes each one you have and asks whether the catalog you hold contains a call that satisfies it. That question is answerable from what gate 1 already read, which is why it stays inside a test that authorizes no write.
Worth being exact about the opening story here. The scan that named the missing prerequisite is not a read-only tool — its own annotations declare that it is not, and it writes a scan record — so that discovery was not one of these five gates. It was the incident the gates exist to get ahead of. Both halves of that requirement have since been declared — an operation that saves the plan exists, and its description states the order it belongs in — added the same day by a separate repair session.
So run gate 5 against that earlier catalog, before the scan, and it passes. That is the uncomfortable part, and it is why every block here carries a fourth line. The catalog did state an order — call the scan first, record only after reviewing its evidence — and both of those steps had a callable transition. What it never stated was the plan, and a prerequisite the catalog does not mention cannot be found by reading the catalog. What gate 5 changes is the moment one surfaces anyway. The scan's response named the plan; re-running gate 5 on it costs one reading of the catalog already in hand — no tool there accepts a plan, so the workflow is not reachable, and the failure has a name before anything further is attempted. That session had no such test. Up to that point it had three successful calls, a healthy runtime, and no error to debug.
Gate 5 is the one that was missing there, and the one I had never written down as a test before this. It is not gate 1 restated. Gate 1 asks whether the operations you went to the catalog looking for are present. Gate 5 asks about the ones you did not go looking for: the steps the catalog's own prose says have to happen in between. That prose is in the same descriptions and instructions gate 1 handed you — read for argument names, skipped for order. When it states an order, gate 5 checks each step in it before anything that writes is called.
Gate 5, in detail: a next step in prose is not a next step
Workflow-shaped tools name their own prerequisites in the response — the second source, and also where the trap is. The scan in the opening session returned three fields for its plan requirement: required: true, a state of missing_or_invalid, and a detail naming the local file it wanted.
Those three fields state a requirement. They are not a transition. The test is mechanical:
- Take the prerequisite, from either source above.
- Ask whether it is machine-owned (something the assistant is supposed to do) or human-owned (an approval, a credential, a business decision that must not be automated).
- If it is machine-owned, find the tool whose declared input schema accepts what you'd need to pass.
- If no such tool is in the catalog, the workflow is not reachable with the catalog you currently hold — and per the point above, that is a dated observation, so check the server's current state before concluding the operation does not exist.
A human-owned prerequisite does not fail this test. Step 2 is where it stops, and gate 5 counts it as satisfied — the gate is asking about machine transitions, and a human step is a legitimate answer, not a hole. The hole is a machine-owned prerequisite that reaches step 4 with no tool to match, and it shows up as "the agent got stuck and I don't know why," because every individual call was a success.
One thing this test is not
Do not confuse pagination inside an application's returned inventory with protocol-level pagination of the tool catalog.
In the opening session, a lookup returned 50 historical candidates with no cursor. That is the application choosing how much of its own inventory to hand back — a usability property of that tool, and a fair thing to file as a request. It is not a protocol violation, and reporting it as one sends whoever reads your bug report to entirely the wrong layer. Gate 3 covers this correctly: the identity check is what tells you whether you got the resource you asked for, regardless of how many neighbors came along with it.
Running it on your own integration
Pick a workflow you already trust and start from its catalog: gate 1's two reads carry the descriptions and, when the server offers them, its instructions, and gate 5 can be answered from whatever came back, before anything that writes.
- Walk the five gates against that catalog. Write down, per gate, the exact field you read — and, for the three that make a call, the operation you called to get it. All five are answerable without authorizing a write.
- Then run the workflow's own read-only steps, in the order the catalog states.
- From there on, carry gate 5 into the runs that do write. Every response that states a requirement — a
requiredflag, a state of missing or invalid, a detail naming something you have to supply — goes back through gate 5 against the catalog already in hand, before the next call. That is the moment the opening session had and let pass.
Do not expect the read-only walk to reproduce the opening case. There, the only thing that named the missing prerequisite was a call that writes, which is outside all five gates; a read-only walk would have finished clean. Expect the first pass over the catalog to come back clean too — the reconstruction above already showed why. Reading a catalog for the order it states costs one pass and rules out only the dead ends already visible in it. What step 3 adds is the habit, and it only pays off where the writes are: that session held the same catalog the whole time and never read it again once the scan answered.
From that walk you are looking for two outcomes:
- A gate you cannot run at all, because there is no read-only operation that produces the evidence. That is a hole in the integration's testability, not in your test.
- A gate where the pass criterion is met but the "does not prove" line is something you had been assuming all along. That assumption is the one worth writing down before it costs you something.
The protocol's own inspector tooling is a reasonable place to run gates 1 through 4 by hand before you wire anything up: https://modelcontextprotocol.io/docs/2026-07-28/tools/inspector
Gate 5 you have to reason about yourself, because it depends on which prerequisites belong to the machine and which belong to a person. That division is the actual design decision, and no health check will make it for you.
If you run this against something of your own, I'd like to hear which gate failed first.
Top comments (0)