Skip to content

Record tool error results on the client OpenTelemetry span - #3859

Closed
mshayeb wants to merge 1 commit into
modelcontextprotocol:mainfrom
mshayeb:fix/3810-client-span-tool-error
Closed

mshayeb wants to merge 1 commit into
modelcontextprotocol:mainfrom
mshayeb:fix/3810-client-span-tool-error

Conversation

@mshayeb

@mshayeb mshayeb commented Nov 14, 2026

Copy link
Copy Markdown

Fixes #3810

Records tool error results on the client OpenTelemetry span when CallToolResult.isError is True.

Motivation and Context

In MCP, application-level tool errors return a successful JSON-RPC response containing isError=True in the result payload rather than a JSON-RPC ErrorData protocol error. Previously, send_raw_request only checked if outcome was an ErrorData instance to record errors on the client OpenTelemetry span. Consequently, when a tool execution failed with CallToolResult.isError=True, the client span remained with StatusCode.UNSET and no error attributes, diverging from server spans which record StatusCode.ERROR and error.type="tool_error".

This change updates send_raw_request in src/mcp/shared/jsonrpc_dispatcher.py to check for method == "tools/call" with outcome.get("isError") is True. When encountered, the client span is marked with span.set_status(StatusCode.ERROR) and span.set_attribute("error.type", "tool_error"), adhering to OpenTelemetry GenAI MCP semantic conventions without setting rpc.response.status_code.

How Has This Been Tested?

Added unit test test_client_and_server_spans_agree_on_tool_error in tests/shared/test_otel.py:

  • Configured an MCPServer with an ok tool (returning "fine") and a fail tool (raising ToolError("upstream unavailable")).
  • Invoked both tools via Client(server, mode="legacy") and inspected spans captured via Logfire (CaptureLogfire).
  • Verified that the successful tool call leaves both client and server spans in StatusCode.UNSET with no error.type.
  • Verified that the failed tool call marks both client and server spans with StatusCode.ERROR and error.type="tool_error".
  • Verified that both spans share the same trace_id and that rpc.response.status_code is not set on the client span.

Breaking Changes

None. This change only updates OpenTelemetry client span status and attributes for failed tool calls.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I am assigned to the linked issue (or it is labeled help wanted, or I'm a maintainer)
  • I have disclosed any AI assistance and can explain the change in my own words
  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

  • Aligns client span behavior in shared/jsonrpc_dispatcher.py with server/_otel.py (following [v2] JSON-RPC error responses leave client OpenTelemetry spans UNSET #3174) under OpenTelemetry GenAI semantic conventions for MCP tool calls.
  • AI disclosure: AI tooling assisted with drafting/review; I have verified the implementation and test suite locally and take full ownership of the changes.

@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Nov 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #3810.

If a maintainer assigns you to #3810, this PR reopens on its own and there's nothing more you need to do here. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. What does help is engaging on the issue itself by confirming the repro, explaining why it matters for your use case, or describing the approach you'd take.

You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way.

CONTRIBUTING.md has the full reasoning, but in short:

  • We're a small team with very little capacity to review community PRs right now.
  • Many recent PRs are AI-generated with little human review, and reviewing one carefully still costs a maintainer as much time as it ever did. A well-described issue is usually more useful to us than the code.

Maintainers: reopen, remove missing-issue-link, or add bypass-issue-check to override.

@github-actions github-actions Bot closed this Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OTel client span stays Unset when the tool result has isError: true

1 participant