Skip to content

MCP tool spans report span.status=ok even when the tool returns a JSON-RPC error #20083

@betegon

Description

@betegon

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/core (affects all SDKs using wrapMcpServerWithSentry)

SDK Version

latest

Framework Version

@modelcontextprotocol/sdk ^1.9.0

Link to Sentry event

No response

Reproduction Example/SDK Setup

import { wrapMcpServerWithSentry } from '@sentry/core';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { z } from 'zod';

const server = wrapMcpServerWithSentry(
  new McpServer({ name: 'my-server', version: '1.0.0' })
);

// Tool that always fails
server.tool(
  'always-error',
  { message: z.string() },
  async () => {
    throw new Error('something went wrong');
  },
);

const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await server.connect(transport);

Steps to Reproduce

  1. Set up wrapMcpServerWithSentry with an MCP server
  2. Register a tool that throws or otherwise causes the MCP SDK to return a JSON-RPC error response (i.e. a response with an error field: { jsonrpc: "2.0", id: 1, error: { code: -32000, message: "..." } })
  3. Call the tool via an MCP client
  4. Inspect the resulting tools/call span in Sentry

Expected Result

The span should have span.status = error (specifically internal_error), so that failure_rate() in the MCP insights dashboard correctly reflects that the tool failed.

Actual Result

The span always has span.status = ok, even when the tool returned an error. This causes failure_rate() to read 0% for tools that consistently fail.

Root cause: completeSpanWithResults in correlation.ts ends the span without inspecting whether the outgoing JSON-RPC response contains an error field. The error-capture path relies on getActiveSpan(), which returns null in this context (the span is stored in a correlation map and is not the active span), so the error status is never set.

Additional Context

This affects the MCP insights dashboard — tools that always return errors appear healthy because their spans all have span.status=ok.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugjavascriptPull requests that update javascript code
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions