Explore · experiment
All MCP Servers Failed the same Rule on My MCP Scanner!
Published 05 Jun, 2026 · experiment
An MCP scanner I'm building found no tool safety hints (read-only, destructive, open-world) on any of 16 public MCP servers. Without them, an agent client can't tell a read from a delete, so every call looks equally risky. What I found, what it doesn't prove, and what to check.
- Question
- Do public MCP servers declare the tool safety hints (read-only, destructive, open-world) that agent clients need to decide when to ask a human?
- Activity
- active
I’ve been building an MCP scanner, a tool that connects to a Model Context Protocol server, lists what it exposes, and checks it against a set of rules. MCP is the open standard most AI agents now use to plug into tools and data: a server offers tools like read_file or delete_record, and the agent calls them. On May 21, 2026, I pointed the scanner at 16 public MCP servers. I expected a mixed report card.
Instead, every server got flagged by the same rule: not one tool on any of the 16 servers carried the safety hints that let an agent client tell a harmless read from a destructive write.
Here’s why that matters. Those hints (MCP calls them tool annotations) are how a server tells the agent client which tools only read, which can destroy, and which reach out to the wider internet. The client can use that to decide when to stop and ask a human. Without them, the team approving agent actions gets the same prompt for “list files” as for “delete table”. In teams I’ve worked with, that leads to one of two outcomes: people click through prompts without reading, or someone sets the server to “always allow”.
I want to be direct about what this run does and doesn’t show, because one result doesn’t add up. More on that below.
What a tool annotation actually says
A tool annotation is a small set of optional true/false fields a server attaches to each tool. They were added to the spec in the 2025-03-26 revision. The first version of the spec, 2024-11-05, didn’t have them. Here are the four hints and what the ToolAnnotations definition in the spec’s schema reference says a client should assume when a hint is missing:
| Hint | What it tells the client | Assumed if missing |
|---|---|---|
readOnlyHint | The tool doesn’t change anything | false: it may change things |
destructiveHint | A change may delete or overwrite data | true: assume destructive |
idempotentHint | Repeating the call has no extra effect | false |
openWorldHint | The tool reaches external systems | true: assume it does |
Look at the right-hand column. The schema already chose the conservative default: a tool with no hints should be treated as a destructive tool that talks to the outside world. That’s the correct call. It also means that on a server with no hints, a spec-following client sees every tool as the worst case.
So the gap isn’t that unannotated tools get waved through automatically. The gap is that when everything looks dangerous, nothing can be told apart, and a human-approval step that can’t tell things apart turns into a rubber stamp.
One more line, from the spec’s Tools page, shapes everything else here: clients must treat annotations as untrusted unless they come from a trusted server. A hint is the server describing itself. It helps with the approval experience, but it isn’t a security boundary.
The data: 16 servers, 16 flags
The rule is simple. It passes a server if every tool declares at least one annotation with a valid boolean value. One scan, one date, all public servers:
| Server | What it does |
|---|---|
| browsermcp | Browser automation |
| calculator | Arithmetic |
| chrome-devtools | Browser debugging |
| context7-mcp | Documentation retrieval |
| datetime-server | Date and time |
| deepwiki-mcp | Knowledge access |
| everything-server | MCP reference/test server |
| filesystem | File operations |
| markdown-mcp | Document tools |
| memory-server | Persistence |
| onetrust-mcp | API bridge |
| playwright-mcp | Browser automation |
| puppeteer-mcp | Web scraping |
| sequential-thinking | Reasoning scaffold |
| sqlite-mcp | Database operations |
| youtube-transcript | Video transcripts |
Some of these hardly matter. Nobody needs an approval gate for add on a calculator. Others matter a lot: in the scan, a SQLite server’s query tools and its delete path looked the same to a connecting client, and so did a browser server’s navigation and interaction tools.
What the result doesn’t prove yet
The filesystem result doesn’t match that server’s own documentation. According to the official filesystem server’s README, its tools are mapped to read-only, idempotent and destructive hints. So on that server, either the version I scanned differed from the README, or my scanner isn’t reading the annotations object where the server puts it. I suspect the scanner. I’m rechecking how it reads the annotations object before the next run.
Until then, the honest reading is “my scanner found no hints on 16 servers”. It is not “no public MCP server declares hints”. At least one probably does. I’m not calling any of these servers defective: the hints are optional in the spec, and most of these servers were written before the hints existed or while they were brand new.
What I’m confident of doesn’t depend on the exact count. Two things:
- You can’t assume the hints exist. Across the servers I looked at, they are at best patchy, and nothing in my experience of building these servers warns you when they’re missing.
- Checking for them is harder than it looks. If my own scanner can misread where they live, a quick manual check can too.
Why missing hints weaken oversight
The recent incidents that worry me aren’t about annotations. They’re about what happens when nothing structural separates a safe action from a destructive one.
In July 2025, SaaStr founder Jason Lemkin publicly reported that Replit’s AI coding agent deleted his production database during what he had declared a code freeze, as The Register covered at the time. That wasn’t an MCP incident. The lesson I take from it is that a freeze which lives in the conversation isn’t a control. A destructive action needs a gate the agent can’t talk its way past, and that’s exactly the gate destructive-tool hints are meant to feed.
The other side of the argument came a couple of months later. Koi Security reported, and Snyk wrote up, a postmark-mcp package on npm that had been quietly copying outgoing email to an attacker’s address. A malicious server like that can label its send_email tool however it likes. That’s why the spec calls hints untrusted, and it’s why I treat annotations as a way to reduce noise in approvals, not as a control.
Here’s how I’d sort it:
- Annotations from a server you’ve vetted can decide which tools skip the prompt.
- Annotations from a server you haven’t vetted get ignored. Every tool asks.
- No annotations at all means every tool asks, and that noise is what pushes teams toward “always allow”.
Does any framework require this?
None that I found names MCP tool annotations. That includes the OWASP Top 10 for Agentic Applications, where agents misusing connected tools falls under ASI02, Tool Misuse and Exploitation.
The nearest legal hook is human oversight. For high-risk AI systems, Article 14 of the EU AI Act requires the system to be designed so that people can effectively oversee it while it’s in use. The Act doesn’t mention tool metadata. My view is that an approval step that treats a directory listing and a table drop the same way is hard to call effective oversight. Whether your agent workflows count as high-risk systems is a question for your own legal review, not this post.
How I’d write the hints
The TypeScript SDK’s README shows hints going in their own annotations object in the tool’s config, not mixed in with the tool’s other options. If I owned a server, a destructive tool would look like this:
server.registerTool(
"delete_record",
{
description: "Delete a database record by id",
inputSchema: { id: z.string() },
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
},
async ({ id }) => { /* ... */ }
);
Set the hints explicitly even when they match the defaults. A reviewer reading destructiveHint: true knows someone made that decision. A missing field could just as easily mean nobody thought about it.
Still working this out
I’m still building my model for how an agent client should weigh a hint it’s told not to trust. Signed server manifests, a registry vouching for them, pinning hints at the point of approval: each has trade-offs I haven’t settled. If you’re working on this too, I’d like to compare notes. And if I’ve got a server’s status wrong, tell me. I’d rather be corrected than confident.
What to do with this
Good (this week): List the MCP servers your agents connect to. For each one, pull the tool list the server returns (not its README) and check whether each tool has an annotations object. Write down which servers have none.
Better (this quarter): Set your agent clients so that any tool without hints, or with hints from a server you haven’t vetted, requires a human to approve it. Then measure how often people approve without changing anything, and how many servers end up on “always allow”. Those two numbers tell you whether your approval step is oversight or just friction. Where the line should sit depends on your business.
Best (this half): Make annotations part of MCP server intake, right next to where the package comes from and who maintains it. Keep approval and override logs as your oversight evidence. When a server you depend on has no hints, send the maintainer a pull request. Adding them is a small change, and the whole ecosystem gains from it.
I’ll re-run the same servers in about 90 days, once the scanner’s annotations lookup is fixed, and I’ll report whether anything moved.
Scan run May 21, 2026, against 16 public MCP servers using an MCP scanner I’m building. The rule checks tools/list output for at least one boolean annotation per tool. Single run: the check is deterministic, so repeating it adds nothing. The filesystem result is under recheck. Spec behavior as of the 2025-06-18 revision.
David O’Neil is a CISO who builds the tools he writes about, including scanners for the AI tooling supply chain his teams depend on.
ai agents · agent governance · supply chain