---
title: "MCP Monitoring"
description: "Monitor MCP server tool executions, prompt retrievals, resource access, and errors."
url: https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring/
---

# Set Up MCP Monitoring for Pi Durable

With Sentry's [MCP Monitoring](https://docs.sentry.io/product/mcp-servers.md), 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](https://docs.sentry.io/platforms/javascript/guides/pi-durable/tracing.md).

## [Instrument the MCP Server](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#instrument-the-mcp-server)

Starting with Sentry JavaScript SDK version `11.1.0`, the SDK instruments every `McpServer` instance automatically when tracing is enabled. It records MCP requests, tool calls, prompt retrievals, resource reads, and handler errors. Automatic instrumentation supports `@modelcontextprotocol/server` `>=2.0.0 <3` and `@modelcontextprotocol/sdk` `>=1.9.0 <2`.

Create your MCP server as usual. No Sentry-specific code is needed:

```javascript
import { McpServer } from "@modelcontextprotocol/server";

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

On SDK versions earlier than `11.1.0`, or if automatic instrumentation isn't set up for your app, wrap the MCP server instance before connecting it to a transport:

```javascript
import * as Sentry from "<sdk-package-name>";
import { McpServer } from "@modelcontextprotocol/server";

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

The wrapper returns the same server instance. Wrapping a server the SDK already instrumented doesn't instrument it twice.

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.

### [Configure Input and Output Recording](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#configure-input-and-output-recording)

By default, the SDK sends the inputs and outputs of your MCP tool calls, prompt retrievals, and resource reads. This gives you rich debugging context. MCP payloads can carry sensitive data, such as tokens, file paths, or private user content. Review what your server handles before you deploy to production.

The [`dataCollection.genAI`](https://docs.sentry.io/platforms/javascript/guides/pi-durable/configuration/options.md#dataCollection) option controls this recording globally for both MCP and AI instrumentation:

```javascript
Sentry.init({
  dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
  dataCollection: {
    genAI: { inputs: false, outputs: false },
  },
});
```

To control recording for a specific server, pass it to `wrapMcpServerWithSentry` with the `recordInputs` and `recordOutputs` options. These options override the `dataCollection.genAI` settings, and they also apply to a server the SDK has already instrumented automatically:

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

The `recordInputs` and `recordOutputs` options require Sentry JavaScript SDK version `10.33.0` or newer. In version 10, the SDK records inputs and outputs only when you set the `dataCollection` option or enable them on the wrapper. Starting with version 11, it records them by default.

## [Manual Instrumentation](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#manual-instrumentation)

The setup above instruments MCP servers built with the official [MCP SDK](https://github.com/modelcontextprotocol/typescript-sdk) (`@modelcontextprotocol/server` or `@modelcontextprotocol/sdk`). If your server uses a different library or a custom implementation that the SDK can't instrument, you can record the same spans manually.

You don't need this if your server is already instrumented, automatically or with `wrapMcpServerWithSentry`. The SDK creates these spans for you. Otherwise, use [Sentry.startSpan()](https://docs.sentry.io/platforms/javascript/tracing/instrumentation/custom-instrumentation.md#starting-a-span) to create the spans described below.

## [Spans](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#spans)

### [Tool Execution Span](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#tool-execution-span)

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](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#common-span-attributes) SHOULD be set.

Additional attributes on the span:

| Data Attribute                  | Type    | Requirement Level | Description                                              | Example                                           |
| ------------------------------- | ------- | ----------------- | -------------------------------------------------------- | ------------------------------------------------- |
| `mcp.tool.name`                 | string  | required          | The name of the MCP tool being called.                   | `"get_weather"`                                   |
| `mcp.method.name`               | string  | recommended       | Should be set to "tools/call".                           | `"tools/call"`                                    |
| `mcp.request.id`                | string  | optional          | The unique identifier for the MCP request.               | `"req_123abc"`                                    |
| `mcp.request.argument.*`        | any     | optional          | Tool input arguments (requires `send_default_pii=True`). | `"San Francisco"` for `mcp.request.argument.city` |
| `mcp.tool.result.content`       | string  | optional          | The result/output content from the tool execution.       | `"The weather is sunny"`                          |
| `mcp.tool.result.content_count` | int     | optional          | The number of items/keys in the tool result.             | `5`                                               |
| `mcp.tool.result.is_error`      | boolean | optional          | Whether the tool execution resulted in an error.         | `True`                                            |

#### [Example Tool Execution Span:](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#example-tool-execution-span)

```javascript
// 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;
    }
  },
);
```

### [Prompt Retrieval Span](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#prompt-retrieval-span)

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](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#common-span-attributes) SHOULD be set.

Additional attributes on the span:

| Data Attribute                      | Type   | Requirement Level | Description                                                                       | Example                                        |
| ----------------------------------- | ------ | ----------------- | --------------------------------------------------------------------------------- | ---------------------------------------------- |
| `mcp.prompt.name`                   | string | required          | The name of the MCP prompt being retrieved.                                       | `"code_review"`                                |
| `mcp.method.name`                   | string | recommended       | Should be set to "prompts/get".                                                   | `"prompts/get"`                                |
| `mcp.request.id`                    | string | optional          | The unique identifier for the MCP request.                                        | `"req_456def"`                                 |
| `mcp.request.argument.*`            | any    | optional          | Prompt input arguments (requires `send_default_pii=True`).                        | `"python"` for `mcp.request.argument.language` |
| `mcp.prompt.result.message_content` | string | optional          | The message content from the prompt retrieval (requires `send_default_pii=True`). | `"Review the following code..."`               |
| `mcp.prompt.result.message_role`    | string | optional          | The role of the message (only for single-message prompts).                        | `"user"`, `"assistant"`, `"system"`            |
| `mcp.prompt.result.message_count`   | int    | optional          | The number of messages in the prompt result.                                      | `1`, `3`                                       |

#### [Example Prompt Retrieval Span:](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#example-prompt-retrieval-span)

```javascript
// 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),
      );
    }
  },
);
```

### [Resource Read Span](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#resource-read-span)

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](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#common-span-attributes) SHOULD be set.

Additional attributes on the span:

| Data Attribute          | Type   | Requirement Level | Description                                  | Example                       |
| ----------------------- | ------ | ----------------- | -------------------------------------------- | ----------------------------- |
| `mcp.resource.uri`      | string | required          | The URI of the MCP resource being accessed.  | `"file:///path/to/resource"`  |
| `mcp.method.name`       | string | recommended       | Should be set to "resources/read"            | `"resources/read"`            |
| `mcp.request.id`        | string | optional          | The unique identifier for the MCP request.   | `"req_789ghi"`                |
| `mcp.resource.protocol` | string | optional          | The protocol/scheme of the MCP resource URI. | `"file"`, `"http"`, `"https"` |

#### [Example Resource Read Span:](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#example-resource-read-span)

```javascript
// 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);
  },
);
```

## [Common Span Attributes](https://docs.sentry.io/platforms/javascript/guides/pi-durable/mcp-monitoring.md#common-span-attributes)

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

| Data Attribute      | Type   | Requirement Level | Description                                      | Example                    |
| ------------------- | ------ | ----------------- | ------------------------------------------------ | -------------------------- |
| `mcp.transport`     | string | recommended       | The transport method used for MCP communication. | `"stdio"`, `"sse", "http"` |
| `network.transport` | string | recommended       | The network transport used.                      | `"pipe"`, `"tcp"`          |
| `mcp.session.id`    | string | recommended       | The session identifier for the MCP connection.   | `"a1b2c3d4e5f6"`           |
| `mcp.request.id`    | string | optional          | The unique identifier for the MCP request.       | `"req_123abc"`             |
