Set Up MCP Monitoring

Monitor MCP server tool executions, prompt retrievals, resource access, and errors.

With Sentry's MCP Monitoring, you can track and debug MCP servers with full-stack context. You can monitor tool executions, prompt retrievals, resource access, and error rates alongside your other Sentry data, including logs, errors, and traces.

Before you begin, set up tracing.

Wrap each McpServer instance with wrapMcpServerWithSentry to automatically record MCP requests, tool calls, prompt retrievals, resource reads, and handler errors.

Import Sentry from your framework's SDK package, then wrap the MCP server instance before connecting it to a transport:

mcp-server.js
Copied
import * as Sentry from "___SDK_PACKAGE___";
import { McpServer } from "@modelcontextprotocol/server";

const server = Sentry.wrapMcpServerWithSentry(
  new McpServer({
    name: "my-mcp-server",
    version: "1.0.0",
  }),
);

Register tools, prompts, and resources on server as usual. The wrapper returns the same server instance.

Support for @modelcontextprotocol/server 2.x requires Sentry JavaScript SDK version 10.70.0 or newer. For @modelcontextprotocol/sdk 1.x, use Sentry JavaScript SDK version 9.46.0 or newer and import McpServer from @modelcontextprotocol/sdk/server/mcp.js. The Sentry wrapper is otherwise the same.

MCP inputs and outputs may contain sensitive data. Use recordInputs and recordOutputs to control collection for a specific server:

Copied
const server = Sentry.wrapMcpServerWithSentry(mcpServer, {
  recordInputs: false,
  recordOutputs: false,
});

These options override the corresponding dataCollection.genAI.inputs and dataCollection.genAI.outputs settings and require Sentry JavaScript SDK version 10.33.0 or newer.

Cloudflare MCP work can finish after the Worker returns an HTTP response, including work kept alive with waitUntil(). With the static trace lifecycle, Sentry snapshots the request transaction when the response is returned, so MCP spans that finish later may be missing.

Set traceLifecycle: "stream" so the SDK can send each sampled span when it finishes. This changes how spans are delivered; you still need to wrap the MCP server as shown above. Span streaming on Cloudflare requires @sentry/cloudflare version 10.49.0 or newer.

index.js
Copied
import * as Sentry from "@sentry/cloudflare";

const worker = {
  async fetch(request, env, ctx) {
    return handleMcpRequest(request, env, ctx);
  },
};

export default Sentry.withSentry(
  (env) => ({
    dsn: env.SENTRY_DSN,
    tracesSampleRate: 1.0,
    traceLifecycle: "stream",
  }),
  worker,
);

Stream mode sends span records instead of assembling one transaction event with embedded spans. beforeSendTransaction and ignoreTransactions don't apply to streamed spans. See Streamed Spans for the beforeSendSpan and ignoreSpans configuration.

If you use McpAgent, wrap the McpServer returned by its server getter, and wrap the Agent class separately with instrumentAgentWithSentry to preserve request and RPC context. Agent instrumentation, MCP server wrapping, and span streaming solve different parts of the setup; none replaces the others. See Agents SDK.

The setup above automatically instruments MCP servers built with the official MCP SDK (@modelcontextprotocol/sdk). If your server uses a different library or a custom implementation that wrapMcpServerWithSentry can't wrap, you can record the same spans manually.

You don't need this if you're already using wrapMcpServerWithSentry — it creates these spans for you. Otherwise, use Sentry.startSpan() to create the spans described below.

Describes MCP tool execution.

  • The span op (transaction mode) or the span's sentry.op attribute (stream mode) MUST be "mcp.server".
  • The span name SHOULD be "tools/call {mcp.tool.name}".
  • The mcp.tool.name attribute MUST be set to the tool's name. (e.g. "get_weather")
  • The mcp.method.name attribute SHOULD be set to "tools/call".
  • All Common Span Attributes SHOULD be set.

Additional attributes on the span:

Data AttributeTypeRequirement LevelDescriptionExample
mcp.tool.namestringrequiredThe name of the MCP tool being called."get_weather"
mcp.method.namestringrecommendedShould be set to "tools/call"."tools/call"
mcp.request.idstringoptionalThe unique identifier for the MCP request."req_123abc"
mcp.request.argument.*anyoptionalTool input arguments (requires send_default_pii=True)."San Francisco" for mcp.request.argument.city
mcp.tool.result.contentstringoptionalThe result/output content from the tool execution."The weather is sunny"
mcp.tool.result.content_countintoptionalThe number of items/keys in the tool result.5
mcp.tool.result.is_errorbooleanoptionalWhether the tool execution resulted in an error.True

Copied
// Example tool execution
const toolName = "get_weather";
const toolArguments = { city: "San Francisco" };

await Sentry.startSpan(
  {
    op: "mcp.server",
    name: `tools/call ${toolName}`,
  },
  async (span) => {
    // Set MCP-specific attributes
    span.setAttribute("mcp.tool.name", toolName);
    span.setAttribute("mcp.method.name", "tools/call");

    // Set request metadata
    span.setAttribute("mcp.request.id", "req_123abc");
    span.setAttribute("mcp.session.id", "session_xyz789");
    span.setAttribute("mcp.transport", "stdio"); // or "http", "sse" for HTTP/WebSocket/SSE
    span.setAttribute("network.transport", "pipe"); // or "tcp" for HTTP/SSE

    // Set tool arguments (optional, requires recordInputs: true)
    for (const [key, value] of Object.entries(toolArguments)) {
      span.setAttribute(`mcp.request.argument.${key}`, value);
    }

    // Execute the tool
    try {
      const result = executeTool(toolName, toolArguments);

      // Set result data
      span.setAttribute("mcp.tool.result.content", JSON.stringify(result));
      span.setAttribute("mcp.tool.result.is_error", false);

      // Set result content count if applicable
      if (
        Array.isArray(result) ||
        (typeof result === "object" && result !== null)
      ) {
        span.setAttribute(
          "mcp.tool.result.content_count",
          Array.isArray(result)
            ? result.length
            : Object.keys(result).length,
        );
      }
    } catch (error) {
      span.setAttribute("mcp.tool.result.is_error", true);
      throw error;
    }
  },
);

Describes MCP prompt retrieval.

  • The span op (transaction mode) or the span's sentry.op attribute (stream mode) MUST be "mcp.server".
  • The span name SHOULD be "prompts/get {mcp.prompt.name}".
  • The mcp.prompt.name attribute MUST be set to the prompt's name. (e.g. "code_review")
  • The mcp.method.name attribute SHOULD be set to "prompts/get".
  • All Common Span Attributes SHOULD be set.

Additional attributes on the span:

Data AttributeTypeRequirement LevelDescriptionExample
mcp.prompt.namestringrequiredThe name of the MCP prompt being retrieved."code_review"
mcp.method.namestringrecommendedShould be set to "prompts/get"."prompts/get"
mcp.request.idstringoptionalThe unique identifier for the MCP request."req_456def"
mcp.request.argument.*anyoptionalPrompt input arguments (requires send_default_pii=True)."python" for mcp.request.argument.language
mcp.prompt.result.message_contentstringoptionalThe message content from the prompt retrieval (requires send_default_pii=True)."Review the following code..."
mcp.prompt.result.message_rolestringoptionalThe role of the message (only for single-message prompts)."user", "assistant", "system"
mcp.prompt.result.message_countintoptionalThe number of messages in the prompt result.1, 3

Copied
// Example prompt retrieval
const promptName = "code_review";
const promptArguments = { language: "python" };

await Sentry.startSpan(
  {
    op: "mcp.server",
    name: `prompts/get ${promptName}`,
  },
  async (span) => {
    // Set MCP-specific attributes
    span.setAttribute("mcp.prompt.name", promptName);
    span.setAttribute("mcp.method.name", "prompts/get");

    // Set request metadata
    span.setAttribute("mcp.request.id", "req_456def");
    span.setAttribute("mcp.session.id", "session_xyz789");
    span.setAttribute("mcp.transport", "http");
    span.setAttribute("network.transport", "tcp");

    // Set prompt arguments (optional, requires recordInputs: true)
    for (const [key, value] of Object.entries(promptArguments)) {
      span.setAttribute(`mcp.request.argument.${key}`, value);
    }

    // Retrieve the prompt
    const promptResult = getPrompt(promptName, promptArguments);

    // Set result data
    const messages = promptResult.messages || [];
    span.setAttribute("mcp.prompt.result.message_count", messages.length);

    // For single-message prompts, set role and content
    if (messages.length === 1) {
      span.setAttribute(
        "mcp.prompt.result.message_role",
        messages[0].role,
      );
      // Content may contain sensitive data, only set if recordOutputs: true
      span.setAttribute(
        "mcp.prompt.result.message_content",
        JSON.stringify(messages[0].content),
      );
    }
  },
);

Describes MCP resource access.

  • The span op (transaction mode) or the span's sentry.op attribute (stream mode) MUST be "mcp.server".
  • The span name SHOULD be "resources/read {mcp.resource.uri}".
  • The mcp.resource.uri attribute MUST be set to the resource's URI. (e.g. "file:///path/to/resource")
  • The mcp.method.name attribute SHOULD be set to "resources/read".
  • All Common Span Attributes SHOULD be set.

Additional attributes on the span:

Data AttributeTypeRequirement LevelDescriptionExample
mcp.resource.uristringrequiredThe URI of the MCP resource being accessed."file:///path/to/resource"
mcp.method.namestringrecommendedShould be set to "resources/read""resources/read"
mcp.request.idstringoptionalThe unique identifier for the MCP request."req_789ghi"
mcp.resource.protocolstringoptionalThe protocol/scheme of the MCP resource URI."file", "http", "https"

Copied
// Example resource access
const resourceUri = "file:///path/to/resource.txt";

await Sentry.startSpan(
  {
    op: "mcp.server",
    name: `resources/read ${resourceUri}`,
  },
  async (span) => {
    // Set MCP-specific attributes
    span.setAttribute("mcp.resource.uri", resourceUri);
    span.setAttribute("mcp.method.name", "resources/read");

    // Set request metadata
    span.setAttribute("mcp.request.id", "req_789ghi");
    span.setAttribute("mcp.session.id", "session_xyz789");
    span.setAttribute("mcp.transport", "http");
    span.setAttribute("network.transport", "tcp");

    // Access the resource
    const resourceData = readResource(resourceUri);
  },
);

The following attributes are common across all MCP span types and SHOULD be set when available:

Data AttributeTypeRequirement LevelDescriptionExample
mcp.transportstringrecommendedThe transport method used for MCP communication."stdio", "sse", "http"
network.transportstringrecommendedThe network transport used."pipe", "tcp"
mcp.session.idstringrecommendedThe session identifier for the MCP connection."a1b2c3d4e5f6"
mcp.request.idstringoptionalThe unique identifier for the MCP request."req_123abc"
Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").