ai
3 мин
27 октября 2026 г.
Источник: Dev.to AI Feed

readOnlyHint isn't a permission: blocking a destructive MCP tool by policy

DVARA
DVARA
RSS AI Ingest
readOnlyHint isn't a permission: blocking a destructive MCP tool by policy

MCP lets a tool describe itself. readOnlyHint: true says "I only read". destructiveHint: true says "I can delete or overwrite something". Clients can use those hints to decide when to ask a person before running a tool. Two things go wrong ...

MCP lets a tool describe itself. readOnlyHint: true says "I only read". destructiveHint: true says "I can delete or overwrite something". Clients can use those hints to decide when to ask a person before running a tool. Two things go wrong with that in practice. The hint is often missing. We looked at about 20,000 MCP tools for a report on MCP tool annotations. In Docker's MCP Catalog, 69% of tools that delete or overwrite something carry no hint at all. The hint can be wrong. One server in that catalogue marked 15 delete tools as read-only, "delete ALL integrations" among them. The tools never set a hint, and a framework default filled in readOnlyHint: true for every one. The vendor fixed it within a week; the catalogue still served the old version three months later. The MCP specification already tells clients what to do about this: For trust & safety and security, clients MUST consider tool annotations to be untrusted unless they come from trusted servers. So where should the decision about what an agent may do live? Not in the tool's description of itself. Here's what it looks like to put it somewhere you control instead, with a real run you can reproduce in about five minutes. The setup The DVARA MCP quickstart starts a gateway, its console and a small Spring AI MCP server with three tools: Tool What it does get_order_status Reads an order's status lookup_customer Returns a customer record, including an email, phone and card number delete_customer Deletes a customer record. Irreversible. Your agent talks to the gateway, not to the server. The gateway lists the server's tools to the agent as demo-tools__ and applies the workspace's rules to every call. git clone --depth 1 https://github.com/dvarahq/dvara-examples.git cd dvara-examples/docker-compose/mcp-quickstart ./start.sh You need Docker and JBang. No licence key and no AI provider account. The rule The quickstart loads one policy into the workspace. This is all of it: version: "1" rules: - id: deny-delete conditions: mcp_tool: denylist: [delete_customer] action: DENY deny_message: "delete_customer is irreversible and not allowed in this workspace" Notice what it doesn't depend on: the tool's annotations. If delete_customer claimed to be read-only, or said nothing at all, the rule would apply just the same. The call Ask the gateway to run the tool, the way any MCP client would. Replace with DEMO_API_KEY from the quickstart's .env: curl -s http://localhost:8080/mcp \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call", "params":{"name":"demo-tools__delete_customer","arguments":{"customerId":"C-42"}}}' The gateway refuses it before it reaches the server: {"jsonrpc":"2.0","id":1,"error":{"code":-32001,"message":"delete_customer is irreversible and not allowed in this workspace","data":{"dvara_code":"MCP_POLICY_DENIED","http_status":403,"trace_id":"3054f7e845e4437ea9ec3fb704fe9a0e"}}} The demo server writes a line to its log whenever delete_customer actually runs. After this call, there's no such line: the server never received it. The record Open the console at http://localhost:8090 and go to MCP → Tool Calls. The refused call is there with status 403, next to the allowed get_order_status and the lookup_customer call whose result had its email, phone and card number redacted. That record is the other half of the point. When someone asks what an agent did, or tried to do, the answer doesn't depend on each server's logging. Adapting it to your own tools Deny by name. Add the tools that should never run unattended to the denylist. Start with the ones whose names say delete, drop, revoke, transfer or send. Hold for a person instead of denying. Some actions should happen, just not without a human saying yes. DVARA can hold a tool call for approval; the native MCP endpoint docs cover how. Treat a missing hint as unknown. If a tool you haven't classified shows up, the safe default is to ask, not to assume it only reads. Pin your servers. Catalogues can serve an older version than upstream. Know which version you run. Annotations are still worth setting if you write MCP servers: they help every client that does read them. Just don't make them the thing that stands between an agent and an irreversible action. Data in this post comes from our report on MCP tool annotations, which has the full method. Tool data: Glama (glama.ai); Docker MCP Catalog data is MIT-licensed.

Хотите внедрить ИИ в ваш бренд?

Спроектируем и развернем автономных агентов и современный цифровой стек под ваши задачи.

Рассчитать проект