From fe1bd37fd267745f96b3fbd4f24a8d7b7b402cc9 Mon Sep 17 00:00:00 2001
From: Ogi <86684834+obostjancic@users.noreply.github.com>
Date: Mon, 17 Nov 2025 16:19:10 +0100
Subject: [PATCH 1/2] feat(js-mcp-module): manual instrumentation docs
---
.../tracing/instrumentation/mcp-module.mdx | 222 ++++++++++++++++++
1 file changed, 222 insertions(+)
create mode 100644 docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx
diff --git a/docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx b/docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx
new file mode 100644
index 0000000000000..2692dbd28d779
--- /dev/null
+++ b/docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx
@@ -0,0 +1,222 @@
+---
+title: Instrument MCP Servers
+sidebar_order: 600
+description: "Learn how to manually instrument your code to use Sentry's MCP monitoring."
+supported:
+ - javascript.node
+ - javascript.aws-lambda
+ - javascript.azure-functions
+ - javascript.connect
+ - javascript.express
+ - javascript.fastify
+ - javascript.gcp-functions
+ - javascript.hapi
+ - javascript.hono
+ - javascript.koa
+ - javascript.nestjs
+ - javascript.bun
+ - javascript.deno
+ - javascript.nextjs
+ - javascript.nuxt
+ - javascript.astro
+ - javascript.solidstart
+ - javascript.sveltekit
+ - javascript.remix
+ - javascript.cloudflare
+ - javascript.tanstackstart-react
+---
+
+With Sentry's [MCP monitoring](/product/insights/ai/mcp/), you can track and debug MCP servers with full-stack context. You'll be able to monitor tool executions, prompt retrievals, resource access, and error rates. MCP monitoring data will be fully connected to your other Sentry data like logs, errors, and traces.
+
+As a prerequisite to setting up MCP monitoring with JavaScript, you'll need to first set up tracing. Once this is done, the JavaScript SDK will automatically instrument MCP servers created with supported libraries. If that doesn't fit your use case, you can use custom instrumentation described below.
+
+## Automatic Instrumentation
+
+The JavaScript SDK supports automatic instrumentation for MCP servers. We recommend adding the MCP integration to your Sentry configuration to automatically capture spans for MCP operations.
+
+-
+ MCP (Model Context Protocol)
+
+
+## Manual Instrumentation
+
+For your MCP data to show up in Sentry, spans must be created with well-defined names and data attributes. See below for the different types of MCP operations you can instrument.
+
+The [Sentry.startSpan()](/platforms/javascript/tracing/instrumentation/custom-instrumentation/#starting-a-span) method can be used to create these spans.
+
+## Spans
+
+### Tool Execution Span
+
+
+
+#### Example Tool Execution Span:
+
+```javascript
+import * as Sentry from "@sentry/node";
+
+Sentry.init({
+ // ... your Sentry configuration
+});
+
+// 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", "pipe"); // or "tcp" for HTTP/WebSocket/SSE
+
+ // Set tool arguments (optional, if send_default_pii=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
+
+
+
+#### Example Prompt Retrieval Span:
+
+```javascript
+import * as Sentry from "@sentry/node";
+
+Sentry.init({
+ // ... your Sentry configuration
+});
+
+// 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");
+
+ // Set prompt arguments (optional, if send_default_pii=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 is PII, only set if send_default_pii=true
+ span.setAttribute(
+ "mcp.prompt.result.message_content",
+ JSON.stringify(messages[0].content)
+ );
+ }
+ }
+);
+```
+
+### Resource Read Span
+
+
+
+#### Example Resource Read Span:
+
+```javascript
+import * as Sentry from "@sentry/node";
+
+Sentry.init({
+ // ... your Sentry configuration
+});
+
+// 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");
+
+ // Extract and set protocol/scheme
+ try {
+ const parsedUri = new URL(resourceUri);
+ if (parsedUri.protocol) {
+ // Remove trailing colon from protocol
+ span.setAttribute(
+ "mcp.resource.protocol",
+ parsedUri.protocol.replace(":", "")
+ );
+ }
+ } catch (error) {
+ // If URL parsing fails, continue without protocol attribute
+ }
+
+ // Set request metadata
+ span.setAttribute("mcp.request.id", "req_789ghi");
+ span.setAttribute("mcp.session.id", "session_xyz789");
+ span.setAttribute("mcp.transport", "http");
+
+ // Access the resource
+ const resourceData = readResource(resourceUri);
+ }
+);
+```
+
+## Common Span Attributes
+
+
From 679a8e08f14d7c6523b82a8d86a4eefee3b3425c Mon Sep 17 00:00:00 2001
From: Ogi <86684834+obostjancic@users.noreply.github.com>
Date: Mon, 17 Nov 2025 16:23:16 +0100
Subject: [PATCH 2/2] align docs
---
.../tracing/instrumentation/mcp-module.mdx | 19 ++++---------------
.../custom-instrumentation/mcp-module.mdx | 11 ++++-------
2 files changed, 8 insertions(+), 22 deletions(-)
diff --git a/docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx b/docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx
index 2692dbd28d779..d3326f67067f6 100644
--- a/docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx
+++ b/docs/platforms/javascript/common/tracing/instrumentation/mcp-module.mdx
@@ -76,7 +76,8 @@ await Sentry.startSpan(
// Set request metadata
span.setAttribute("mcp.request.id", "req_123abc");
span.setAttribute("mcp.session.id", "session_xyz789");
- span.setAttribute("mcp.transport", "pipe"); // or "tcp" for HTTP/WebSocket/SSE
+ 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, if send_default_pii=true)
for (const [key, value] of Object.entries(toolArguments)) {
@@ -140,6 +141,7 @@ await Sentry.startSpan(
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, if send_default_pii=true)
for (const [key, value] of Object.entries(promptArguments)) {
@@ -192,24 +194,11 @@ await Sentry.startSpan(
span.setAttribute("mcp.resource.uri", resourceUri);
span.setAttribute("mcp.method.name", "resources/read");
- // Extract and set protocol/scheme
- try {
- const parsedUri = new URL(resourceUri);
- if (parsedUri.protocol) {
- // Remove trailing colon from protocol
- span.setAttribute(
- "mcp.resource.protocol",
- parsedUri.protocol.replace(":", "")
- );
- }
- } catch (error) {
- // If URL parsing fails, continue without protocol attribute
- }
-
// 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);
diff --git a/docs/platforms/python/tracing/instrumentation/custom-instrumentation/mcp-module.mdx b/docs/platforms/python/tracing/instrumentation/custom-instrumentation/mcp-module.mdx
index 77dbe18346676..3b681f34ad8bf 100644
--- a/docs/platforms/python/tracing/instrumentation/custom-instrumentation/mcp-module.mdx
+++ b/docs/platforms/python/tracing/instrumentation/custom-instrumentation/mcp-module.mdx
@@ -51,7 +51,8 @@ with sentry_sdk.start_span(
# Set request metadata
span.set_data("mcp.request.id", "req_123abc")
span.set_data("mcp.session.id", "session_xyz789")
- span.set_data("mcp.transport", "pipe") # or "tcp" for HTTP/WebSocket/SSE
+ span.set_data("mcp.transport", "stdio") # or "http", "sse" for HTTP/WebSocket/SSE
+ span.set_data("network.transport", "pipe") # or "tcp" for HTTP/SSE
# Set tool arguments (optional, if send_default_pii=True)
for key, value in tool_arguments.items():
@@ -101,6 +102,7 @@ with sentry_sdk.start_span(
span.set_data("mcp.request.id", "req_456def")
span.set_data("mcp.session.id", "session_xyz789")
span.set_data("mcp.transport", "http")
+ span.set_data("network.transport", "tcp")
# Set prompt arguments (optional, if send_default_pii=True)
for key, value in prompt_arguments.items():
@@ -131,7 +133,6 @@ with sentry_sdk.start_span(
```python
import sentry_sdk
-from urllib.parse import urlparse
sentry_sdk.init(...)
@@ -146,15 +147,11 @@ with sentry_sdk.start_span(
span.set_data("mcp.resource.uri", resource_uri)
span.set_data("mcp.method.name", "resources/read")
- # Extract and set protocol/scheme
- parsed_uri = urlparse(resource_uri)
- if parsed_uri.scheme:
- span.set_data("mcp.resource.protocol", parsed_uri.scheme)
-
# Set request metadata
span.set_data("mcp.request.id", "req_789ghi")
span.set_data("mcp.session.id", "session_xyz789")
span.set_data("mcp.transport", "http")
+ span.set_data("network.transport", "tcp")
# Access the resource
resource_data = read_resource(resource_uri)