Skip to content

MCP integration records tool calls that return isError as successful #7890

Open

Description

@4ktLuffy

Version

2.71.0 (master @ 6a4eb20)

Steps to Reproduce

Call an MCPServer (mcp 2.3.0) tool that raises ValueError, with MCPIntegration enabled.

Expected Result

The client gets isError: true and the tools/call span is marked as an error.

Actual Result

The client gets isError: true, but the span status stays ok, there is no error.type, and the MCP integration captures no error, so MCP tool error rates read 0%.

The mcp 2.x middleware never reads result["isError"] (mcp.py#L457-L501), and the mcp 1.x wrapper ignores a returned CallToolResult(isError=True) (mcp.py#L357-L395). Affected: raised errors on mcp 2.3.0 and fastmcp 4.0.11, and returned isError results on mcp 1.30.0.

I'd like to open a PR to fix this, with tests, if that's okay with you.

Activity

  1. moved this to Waiting for: Product Owner in GitHub Issues with 👀 3on Oct 6, 2026
  2. linear-code commented on Oct 6, 2026

    @linear-code
  3. alexander-alderman-webb commented on Oct 6, 2026

    @alexander-alderman-webb
    Collaborator

    Thanks for reporting!
    We'll be tackling these gaps in the coming months.

  4. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Oct 6, 2026
  5. 4ktLuffy commented on Oct 7, 2026

    @4ktLuffy
    Author

    Some numbers in case they help: with 25% of MCP tool calls returning isError, Sentry's span data shows a 0% failure rate for the Python server and 25% for the JS one. https://4ktluffy.github.io/mcp-incident-lab/

    The JS integration already handles this (correlation.js). Happy to port it to Python with tests if you're open to a PR.

  6. alexander-alderman-webb commented on Oct 13, 2026

    @alexander-alderman-webb
    Collaborator

    For graphql we don't set the span status to error when an error is reported because the response code is 200.
    This seems like a similar situation, so it's unclear that we want the span status to be error.

  7. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Oct 13, 2026
  8. 4ktLuffy commented on Oct 13, 2026

    @4ktLuffy
    Author

    Thanks Alex, that makes sense for GraphQL.

    Two things that might make MCP different:

    • The JS SDK already treats it as an error: for a tools/call result with isError=true it sets the span status to internal_error and error.type to "tool_error" (correlation.js). So today the same failing tool shows 25% failures from a JS MCP server and 0% from a Python one.
    • Even if the status should stay ok, SPANDATA.MCP_TOOL_RESULT_IS_ERROR is already defined in consts.py but never set. Setting it would let people count and alert on failed tool calls without changing the span status.

    Would a PR that only sets mcp.tool.result.is_error be useful as a first step? Happy to do it with tests, or to match whatever the JS side ends up doing.

  9. alexander-alderman-webb commented on Oct 13, 2026

    @alexander-alderman-webb
    Collaborator

    mcp.tool.result.is_error is not in OTel so let's not set it.
    Isn't this information already contained in the tool output content?

  10. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Oct 13, 2026
  11. 4ktLuffy commented on Oct 13, 2026

    @4ktLuffy
    Author

    Fair point on the custom attribute, I'll drop that idea.

    OTel does cover this case though, through error.type rather than a new attribute. The MCP conventions say that when the JSON-RPC call succeeds but the result carries an error, error.type SHOULD be set, and for a CallToolResult with isError=true it SHOULD be "tool_error":
    https://github.com/open-telemetry/semantic-conventions-genai/blob/4a39b6ef1363fab57bc40e18c82abb32cc89ebcd/docs/gen-ai/mcp.md#L187-L192

    That is also what the JS SDK does today.

    On the tool output: it is only recorded when prompts/outputs are allowed (send_default_pii or data_collection gen_ai.outputs), so with the defaults it is not on the span at all. And when it is, it is free text like "warehouse DB timeout", so it can't be counted or alerted on as a failure rate.

  12. alexander-alderman-webb commented on Oct 13, 2026

    @alexander-alderman-webb
    Collaborator

    error.type would be the OTel compliant way of doing things, so that's an argument in favor.

  13. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Oct 13, 2026
  14. 4ktLuffy commented on Oct 13, 2026

    @4ktLuffy
    Author

    Thanks! If it helps, I can open a PR that sets error.type="tool_error" when a tool call returns isError=true, with tests for both mcp 1.x and 2.x, and leaves the span status as it is. Just let me know.

  15. mvanhorn commented on Oct 13, 2026

    @mvanhorn

    The mcp 1.x handler wrapper and the mcp 2.x tool-call middleware only record a failure when the handler raises and that exception is caught. A normal return of a CallToolResult, or of a dict whose isError field is true, skips that path, so the span is finished without error.type.

    After the handler returns, both paths now use a helper that treats isError as true on either a dict or a result object.

    #8020

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions