Skip to content

No structured way to signal a tool's refusal, distinct from success or a schema-validation error #282

Description

@AshrafAhmed9

Filing this from real friction hit building three apps for the WebMCP Challenge (one of them, Consequence, is built specifically around this issue).

The situation

execute() returns { content: [{ type: "text", text }] } regardless of whether the tool succeeded, was refused on purpose, or failed. There's no structured field distinguishing "I did the thing" from "I'm declining to do the thing, on purpose, and here's why."

We needed this because some of our tools enforce a real permission boundary: an agent is allowed to fill most fields on a form, but a few (attestations, signatures) can only be set by a human. When an agent calls our answer_question tool on one of those fields, the tool needs to refuse, clearly and on purpose, not silently no-op and not throw a generic error.

What we did instead

We return a normal-shaped success result whose text is a plain-language refusal message ("Refused: this field requires the human's own action"), and rely on the calling agent to read and respect that text. It works, but it's a convention we invented, not something the spec gives any structure to. An agent framework has no principled way to tell "the tool succeeded and this is the answer" apart from "the tool declined and this is why" without parsing prose.

Why this seems worth a spec-level answer

  • A refusal is a different kind of outcome than a normal result, and calling code (or a UI showing tool-call history, which several of our apps do) benefits from being able to render it differently without string-sniffing.
  • It's different from a schema-validation failure (which the runtime already rejects before execute even runs) — this is a tool voluntarily declining a well-formed, schema-valid call, because of business logic the schema can't express.
  • Multiple entries in the awesome-webmcp list implement permission/authorization patterns where this would apply (ours included), so it's probably a recurring need, not a one-off.

Happy to be pointed at existing discussion if this has already been considered and rejected — didn't find one searching open issues.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions