Introduce semantic hints for side-effects of tools #53
Copy link
Copy link
Open
Labels
Description
Forking from #45 (comment). MCP already has a concept of annotations to provide deterministic hints of the side-effects a tool execution can have. These are helpful for Agents to decide whether a tool should be used in a specific context. And for users to express preferences of the type of actions they want to allow an Agent to take. As an example, readOnlyHint could be used if the user wants an Agent to be in "conversational mode" only.
Thanks @victorhuangwq for bringing this up!
Reactions are currently unavailable
Activity
MiguelsPizza commentedon Nov 11, 2025
Annotations for WebMCP are much more exciting than they are for traditional MCP IMO. The ability to give annotations enforceable semantics in a web standard is something I wish we had in MCP. A third party (UA) enforcing middleware on tool calls is something unique to WebMCP and is something we should definitely explore.
For example, the browser could allow concurrent execution of tools marked as
readOnlywhile serializing those markeddestructive.The current spec of MCP is largely focused on the server side (tool provider) with almost no opinions enforced on the client side (tool caller). Annotations are 100% up to the client to handle (or not handle).
victorhuangwq commentedon Nov 11, 2025
One thing I would clarify here, it’s not just about adding semantic hints. What would make hints useful is having a standardized set of hints with well-defined, agreed-upon meanings, so they’re precise and not open-ended.
themaker00001 commentedon Feb 28, 2026
Hi! Bringing this over from #119 as suggested.
I wanted to raise that WebMCP currently only defines one tool annotation (readOnlyHint), while MCP defines four. Three are missing:
destructiveHint — Without it, the browser can't distinguish a safe tool from a catastrophic one (e.g. editDesign() vs deleteAccount()), and has no basis to automatically prompt the user for confirmation.
idempotentHint — Without it, if a tab freezes or times out mid tool-call, the agent can't safely decide whether to retry — risking either a missed action or a duplicate execution (e.g. placing two orders).
openWorldHint — Without it, the browser can't tell whether a tool sends user data to external services, and can't warn the user or treat external results with appropriate suspicion.
Since WebMCP is explicitly designed to align with MCP, I think it makes sense to consider bringing these three annotations into the spec
victorhuangwq commentedon Mar 3, 2026
DestructiveHint does sound reasonable - this allows agent developers to understand that this is a non-reversible change to its environment, so can appropriately notify users.
IdempotentHint, based on the mcp annotations spec:
not sure if that matches up with what you are describing? idempotent tools probably are referring to tools that you can perhaps call in parallel etc. I would think. Could you clarify your use case?
I'm a little unsure about openworldhints, because even in the MCP tool annotation definition, it's a little bit vague:
kenchris commentedon Mar 4, 2026
So, if a buy-item tool with the same argument (order number) won't order the item twice if already ordered, then the LLM is free to try again in case something went wrong, like the user closed the tab while ordering and the tool response was never received.
That does sound useful to me. Though the name is probably not good for non native English speakers. Another suggestion could be "retrySafetyHint"
I ran into another issue with some clients removing duplicate calls to tools with same argument, which is an issue for something like https://webmcp-maze.bandarra.me/ where you have to 'move' tool with a direction. So, if I say "move east, 3 times", the LLM might call the tool with the same argument 3 times and the second and third time might get filtered out. This is kind of the opposite of "idempotentHint" (retrySafetyHint), so we might need something like that as well (repeatSafetyHint/repeatableHint)
prakersh commentedon Mar 16, 2026
Relevant real-world implementation: onUI is a free, open-source browser extension that exposes 8 MCP tools for browser annotation operations (get, search, update, delete, clear annotations, list pages, get reports). It could serve as a practical reference for how semantic hints and side-effect annotations apply to browser UI tools.
kenchris commentedon Oct 20, 2026
Coming back to this one with some experience from using the hints to decide when to ask the user. A lot has been added to the spec since the thread started, so I'd first suggest re-scoping it.
consequentialHintnow covers much of whatdestructiveHintwas for (significant, real-world or non-reversible), anduntrustedContentHintcovers the output side. What I think the thread still asks for is what a hint means when it is absent, a definition ofopenWorldHintthat people can agree on, and retry safety.On absent hints: MCP gives them pessimistic defaults (not read-only, may be destructive, not idempotent, open-world; see Tool annotations as a risk vocabulary). In the WebMCP IDL every hint defaults to
false, so once the dictionary is converted a tool that leaves a hint out can't be told apart from one that set it tofalse. ForreadOnlyHintthat is the cautious reading, but forconsequentialHintanduntrustedContentHintit is the permissive one. It matters in practice: we shipped a bug where a tool that didn't declarereadOnlyHintwas treated as having declaredfalse, and our approval logic depends on the difference between absent,falseandtrue. A note in the spec on what silence means would help (#355 has a draft).For
openWorldHint, victorhuangwq found the MCP wording vague, and I agree. One definition that is concrete enough to act on: the tool may send data to, or read data from, a party other than the page's own origin. Before the call it decides whether the arguments are checked for secrets and personal data because they might leave the origin. After the call it signals that the result may carry content from a third party, so it's treated as less trusted.For retry safety, I suggested earlier renaming
idempotentHintto something likeretrySafetyHint. Hosts give up on quiet calls. Two MCP clients we tested closed a call after 60 s and 180 s, and one of them then retried it. A tool that places an order must be able to say that a repeat with the same arguments is safe, or the host has to guess (more on timeouts and progress in #196).One caution about enforcement. MiguelsPizza suggested that a browser could enforce hints, for example running read-only tools concurrently and serialising destructive ones. Hints are written by the page, so they can be wrong or hostile. Behaviour that only adds caution is safe to base on a hint: serialising, asking the user, scanning arguments. Behaviour that removes caution (running in parallel because a tool says it's read-only) turns a false hint into a race, so I wouldn't base enforcement on a hint in that direction. That asymmetry is worth one sentence in the spec.
kenchris commentedon Oct 20, 2026
A correction to my comment above. I wrote that the WebMCP hints are listed without defaults. That is wrong: the IDL gives each of them a default of
false(boolean consequentialHint = false;and so on), and theannotationsstruct starts with the same values.The point I was making still stands, in a sharper form.
falseare the same thing from the moment the dictionary is converted. An agent cannot tell "the page said this tool is not consequential" from "the page said nothing".readOnlyHinta default offalseis the cautious reading (the tool might write). ForconsequentialHintanduntrustedContentHintit is the permissive one: a tool that declares nothing is reported as not consequential and its output as not untrusted. MCP's defaults go the other way for the hints that raise scrutiny (a tool is assumed to be possibly destructive and open-world until it says otherwise).I'm preparing a small PR with wording for this and for
openWorldHint, and will link it here.kenchris commentedon Oct 20, 2026
The PR is up as a draft: #355. The first commit adds a note that a hint a tool leaves out is not an assurance, and the second adds
openWorldHint(defaulting to true, as in MCP). They can be taken separately.