An MCP tool returns "Operation failed" for every error. The agent retries invalid input as often as it retries timeouts. What should the tool return?
Why B is the answer
A structured error with a category, a retryable flag and a readable description
The agent can only make a good recovery decision if it can tell transient failures from validation and permission errors. An empty result is worse still, because it looks like success.
In detail
- An agent can only recover as well as the information it receives. "Operation failed" hides whether retrying could ever work.
- Different failures need different responses: fix the input, wait and retry, or stop and ask for help.
- A structured error makes that choice easy. In the Messages API, the matching idea is a tool_result with is_error set to true and a clear message.
- Include what the agent should do next in the description, such as which field was invalid.
Use case
An illustrative scenario, not a real incident.
A payments tool returns "Operation failed" for an invalid card and for a network timeout alike. The agent retries the bad card five times. With a category and a retryable flag, it retries the timeout and asks the customer about the card.
See it in different ways
flowchart TD
A["Tool fails"] --> B["tool_result with is_error"]
B --> C{"Message says what?"}
C -->|"Bad input"| D["Fix the arguments"]
C -->|"Temporary"| E["Retry"]
C -->|"Not allowed"| F["Stop and explain"]flowchart TD
A["Tool fails"] --> B{"Error category"}
B -->|"Validation"| C["Fix the input, then call again"]
B -->|"Timeout"| D["Wait, then retry"]
B -->|"Permission"| E["Stop and explain"]flowchart TD
Q{"What kind of failure?"}
Q -->|"Transient"| A0["Retry"]
Q -->|"Validation"| A1["Fix the input"]
Q -->|"Permission"| A2["Stop and report"]Key point: Good errors let the agent choose between retrying, changing input and stopping.
Why the other options are wrong
- A. An empty result, so the agent treats the call as finished and moves on to the next step
- This is the worst option. An empty result looks like success, so the agent moves on with nothing.
- C. An HTTP 500 status with no message body, so the client library raises its own exception
- The status alone does not say what went wrong, so the agent still cannot tell a validation error from a temporary one.
- D. A longer free-text message that apologizes for the failure and asks the agent to try again
- A longer message without a category is still vague, and asking for a retry is wrong when the input is invalid.

