Skip to content

Introduce semantic hints for side-effects of tools #53

Open

Description

@khushalsagar

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!

Activity

  1. MiguelsPizza commented on Nov 11, 2025

    @MiguelsPizza
    Contributor

    MCP annotation spec for reference

    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 readOnly while serializing those marked destructive.

    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).

  2. victorhuangwq commented on Nov 11, 2025

    @victorhuangwq
    Contributor

    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.

  3. themaker00001 commented on Feb 28, 2026

    @themaker00001

    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

  4. victorhuangwq commented on Mar 3, 2026

    @victorhuangwq
    Contributor

    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:

    idempotentHint?: boolean
    If true, calling the tool repeatedly with the same arguments will have no additional effect on the its environment.

    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:

    openWorldHint?: boolean
    If true, this tool may interact with an “open world” of external entities. If false, the tool’s domain of interaction is closed. For example, the world of a web search tool is open, whereas that of a memory tool is not.

  5. kenchris commented on Mar 4, 2026

    @kenchris

    idempotentHint?: boolean
    If true, calling the tool repeatedly with the same arguments will have no additional effect on the its environment.

    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)

  6. prakersh commented on Mar 16, 2026

    @prakersh

    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.

  7. kenchris commented on Oct 20, 2026

    @kenchris

    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.

    consequentialHint now covers much of what destructiveHint was for (significant, real-world or non-reversible), and untrustedContentHint covers the output side. What I think the thread still asks for is what a hint means when it is absent, a definition of openWorldHint that 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 to false. For readOnlyHint that is the cautious reading, but for consequentialHint and untrustedContentHint it is the permissive one. It matters in practice: we shipped a bug where a tool that didn't declare readOnlyHint was treated as having declared false, and our approval logic depends on the difference between absent, false and true. 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 idempotentHint to something like retrySafetyHint. 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.

  8. kenchris commented on Oct 20, 2026

    @kenchris

    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 the annotations struct starts with the same values.

    The point I was making still stands, in a sharper form.

    • Because of the dictionary defaults, an absent hint and an explicit false are 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".
    • For readOnlyHint a default of false is the cautious reading (the tool might write). For consequentialHint and untrustedContentHint it 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.

  9. kenchris commented on Oct 20, 2026

    @kenchris

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions