Skip to content

MCP PR tools report gh/auth failures as successful results (isError unset) #3162

Description

@noQbot

Summary

The MCP list_prs, get_pr_impact, and triage_prs tools return their failure messages ("gh CLI not found or not authenticated", "PR #N not found") as ordinary text content with isError left unset (i.e. false). A client that checks the isError field to distinguish failure from success reads a genuine failure as a successful tool call.

Steps to reproduce

  1. Run the MCP server without gh installed / authenticated.
  2. Call list_prs (or get_pr_impact({"pr_number": 1})).
  3. Inspect the tool result. The content carries the error text, but the result has isError: false (or isError absent) — it looks like a success.

Expected behavior

A failed tool call — gh missing / not authenticated, or a PR that cannot be resolved — should return a result with isError: true, so clients can tell a real failure from a valid result. (Input-validation errors on other tools already return isError: true; the PR tools are inconsistent with that.)

Actual behavior

isError is never set. Under the hood graphify.prs.fetch_prs raises RuntimeError, and the tool handler turns it into an ordinary string:

  • _tool_list_prs / _tool_triage_prs (graphify/serve.py): except RuntimeError as e: return f"Error: {e}"
  • _tool_get_pr_impact: if pr_data is None: return f"PR #{number} not found …"

call_tool then wraps the string as a normal TextContent, and the transport returns CallToolResult(content=…) with no isError. So the MCP contract shows success for every one of these failures.

Root cause

The PR tool handlers return error strings instead of signalling an error, and the dispatch layer (call_tool / the HTTP _on_call_tool) never sets isError.

Fix

Introduce an error signal a handler raises (e.g. a ToolError) for the gh-missing / unresolved-PR cases; propagate it through call_tool so the mcp 1.x decorator wraps it as an error result, and have the 2.x _on_call_tool return CallToolResult(content=…, isError=True). Normal not-found text (missing node/community) and a PR that exists but touched no files stay isError: false.

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