Skip to content

🐛 Bug Report: MCP InstrumentedStreamWriter.send() assumes dict-shaped request.result and can fail for object/model results #4038

@2getsandesh

Description

@2getsandesh

Which component is this bug for?

Traceloop SDK

📜 Description

In opentelemetry-instrumentation-mcp, InstrumentedStreamWriter.send() assumes request.result is always a dict:

if "isError" in request.result:
    if request.result["isError"] is True:
        span.set_status(
            Status(
                StatusCode.ERROR,
                f"{request.result['content'][0]['text']}",
            )
        )

Impact

  • Response-stream error handling can fail at runtime
  • Error spans may not be set correctly
  • Behavior is inconsistent across MCP instrumentation paths

Possible errors

  • TypeError: argument of type 'X' is not iterable
  • TypeError: 'X' object is not subscriptable
  • KeyError: 'content'
  • IndexError: list index out of range

File

  • packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py

Method

  • InstrumentedStreamWriter.send()

Proposed fix
Normalize request.result access before reading fields:

  • use .get(...) for dicts
  • use getattr(...) for objects

👟 Reproduction steps

  1. Use MCP instrumentation with a response/result object that exposes fields via attributes rather than dict keys.
  2. Trigger a response where the MCP result represents an error.
  3. Exercise the InstrumentedStreamWriter.send() path.
  4. Observe failure or missing error handling around:
request.result["content"][0]["text"]

👍 Expected behavior

The stream writer should support both of these shapes:

dict-style

request.result["isError"]
request.result["content"][0]["text"]

object-style

request.result.isError
request.result.content[0].text

without crashing, and should always set the span status correctly for MCP error responses.

👎 Actual Behavior with Screenshots

The code currently assumes only dict-style access in InstrumentedStreamWriter.send().

This causes inconsistent handling and can break when request.result is not a dict.

🤖 Python Version

No response

📃 Provide any additional context for the Bug.

No response

👀 Have you spent some time to check if this bug has been raised before?

  • I checked and didn't find similar issue

Are you willing to submit PR?

None

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