#5076·OpenViking

fix(mcp): mark whole-call tool failures with isError

Author: ehz0ahCreated Sep 15, 2026Updated Sep 17, 2026
Labelsagent-plugins

Issue Origin

Observed or reproduced in a real environment

Bug Description

Some OpenViking MCP tools convert a failed operation into ordinary result content. The wire result has isError: false, so a conforming client or agent can treat the failed operation as successful.

This is an interoperability problem, not a message-quality problem. The current error text is actionable and should remain available.

External evidence shows the same failure class in another MCP server: apache/superset#43358 reports a permission denial returned with isError: false, after which an agent treated the forbidden write as successful. The MCP tools specification requires API, validation, and business-logic failures to use a tool result with isError: true: https://modelcontextprotocol.io/specification/2026-07-28/server/tools#error-handling

Steps to Reproduce

  1. Use current upstream/main at 87ae94031b95d2f4e54b32d23f342167e069e1c2.
  2. Call the MCP list_watches tool while the watch scheduler is unavailable.
  3. Inspect the resulting CallToolResult.

Expected Behavior

The tool returns the existing actionable text and sets isError: true.

json
{
  "content": [{"type": "text", "text": "Error: Watch scheduler not running"}],
  "isError": true
}

Actual Behavior

The same failed operation is returned as a successful tool result.

json
{
  "content": [{"type": "text", "text": "Error: Watch scheduler not running"}],
  "structuredContent": {"result": "Error: Watch scheduler not running"},
  "isError": false
}

The same source pattern exists in selected add_resource, cancel_watch, and glob whole-call failure paths.

Minimal Reproducible Example

python
handler = mcp._mcp_server.request_handlers[CallToolRequest]
request = CallToolRequest(
    params=CallToolRequestParams(name="list_watches", arguments={})
)
result = (await handler(request)).root
assert result.isError is False

Error Logs

bash
No exception is logged. The failure is encoded as successful content.

OpenViking Version

upstream/main at 87ae94031b95d2f4e54b32d23f342167e069e1c2

Python Version

3.13

Operating System

macOS

Model Backend

Not applicable

Additional Context

A focused fix should preserve the current text, successful results, partial batch results, and HTTP or JSON-RPC authentication errors. It should not include grep, because PR #5029 already owns that failure path. The health tool should continue to return health state as data because the diagnostic call itself completed.

Searches of all OpenViking issue, discussion, PR, branch, and source states found no equivalent server-side change. PR #4724 concerns consumers reading camelCase isError; it does not change server emission.