Skip to content

Commit 48242a9

Browse files
authored
feat(mcp): python manual instrumentation (#15348)
1 parent b1128e2 commit 48242a9

File tree

5 files changed

+233
-0
lines changed

5 files changed

+233
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
title: Instrument MCP Servers
3+
sidebar_order: 600
4+
description: "Learn how to manually instrument your code to use Sentry's MCP monitoring."
5+
---
6+
7+
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.
8+
9+
As a prerequisite to setting up MCP monitoring with Python, you'll need to first <PlatformLink to="/tracing/">set up tracing</PlatformLink>. Once this is done, the Python 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.
10+
11+
## Automatic Instrumentation
12+
13+
The Python SDK supports automatic instrumentation for MCP servers. We recommend adding the MCP integration to your Sentry configuration to automatically capture spans for MCP operations.
14+
15+
- <PlatformLink to="/integrations/mcp/">
16+
MCP (Model Context Protocol)
17+
</PlatformLink>
18+
19+
## Manual Instrumentation
20+
21+
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.
22+
23+
The [@sentry_sdk.trace()](/platforms/python/tracing/instrumentation/custom-instrumentation/#span-templates) decorator can also be used to create these spans.
24+
25+
## Spans
26+
27+
### Tool Execution Span
28+
29+
<Include name="tracing/mcp-module/tool-execution-span" />
30+
31+
#### Example Tool Execution Span:
32+
33+
```python
34+
import sentry_sdk
35+
import json
36+
37+
sentry_sdk.init(...)
38+
39+
# Example tool execution
40+
tool_name = "get_weather"
41+
tool_arguments = {"city": "San Francisco"}
42+
43+
with sentry_sdk.start_span(
44+
op="mcp.server",
45+
name=f"tools/call {tool_name}",
46+
) as span:
47+
# Set MCP-specific attributes
48+
span.set_data("mcp.tool.name", tool_name)
49+
span.set_data("mcp.method.name", "tools/call")
50+
51+
# Set request metadata
52+
span.set_data("mcp.request.id", "req_123abc")
53+
span.set_data("mcp.session.id", "session_xyz789")
54+
span.set_data("mcp.transport", "pipe") # or "tcp" for HTTP/WebSocket/SSE
55+
56+
# Set tool arguments (optional, if send_default_pii=True)
57+
for key, value in tool_arguments.items():
58+
span.set_data(f"mcp.request.argument.{key}", value)
59+
60+
# Execute the tool
61+
try:
62+
result = execute_tool(tool_name, tool_arguments)
63+
64+
# Set result data
65+
span.set_data("mcp.tool.result.content", json.dumps(result))
66+
span.set_data("mcp.tool.result.is_error", False)
67+
68+
# Set result content count if applicable
69+
if isinstance(result, (list, dict)):
70+
span.set_data("mcp.tool.result.content_count", len(result))
71+
except Exception as e:
72+
span.set_data("mcp.tool.result.is_error", True)
73+
raise
74+
```
75+
76+
### Prompt Retrieval Span
77+
78+
<Include name="tracing/mcp-module/prompt-retrieval-span" />
79+
80+
#### Example Prompt Retrieval Span:
81+
82+
```python
83+
import sentry_sdk
84+
import json
85+
86+
sentry_sdk.init(...)
87+
88+
# Example prompt retrieval
89+
prompt_name = "code_review"
90+
prompt_arguments = {"language": "python"}
91+
92+
with sentry_sdk.start_span(
93+
op="mcp.server",
94+
name=f"prompts/get {prompt_name}",
95+
) as span:
96+
# Set MCP-specific attributes
97+
span.set_data("mcp.prompt.name", prompt_name)
98+
span.set_data("mcp.method.name", "prompts/get")
99+
100+
# Set request metadata
101+
span.set_data("mcp.request.id", "req_456def")
102+
span.set_data("mcp.session.id", "session_xyz789")
103+
span.set_data("mcp.transport", "http")
104+
105+
# Set prompt arguments (optional, if send_default_pii=True)
106+
for key, value in prompt_arguments.items():
107+
span.set_data(f"mcp.request.argument.{key}", value)
108+
109+
# Retrieve the prompt
110+
prompt_result = get_prompt(prompt_name, prompt_arguments)
111+
112+
# Set result data
113+
messages = prompt_result.get("messages", [])
114+
span.set_data("mcp.prompt.result.message_count", len(messages))
115+
116+
# For single-message prompts, set role and content
117+
if len(messages) == 1:
118+
span.set_data("mcp.prompt.result.message_role", messages[0].get("role"))
119+
# Content is PII, only set if send_default_pii=True
120+
span.set_data(
121+
"mcp.prompt.result.message_content",
122+
json.dumps(messages[0].get("content"))
123+
)
124+
```
125+
126+
### Resource Read Span
127+
128+
<Include name="tracing/mcp-module/resource-read-span" />
129+
130+
#### Example Resource Read Span:
131+
132+
```python
133+
import sentry_sdk
134+
from urllib.parse import urlparse
135+
136+
sentry_sdk.init(...)
137+
138+
# Example resource access
139+
resource_uri = "file:///path/to/resource.txt"
140+
141+
with sentry_sdk.start_span(
142+
op="mcp.server",
143+
name=f"resources/read {resource_uri}",
144+
) as span:
145+
# Set MCP-specific attributes
146+
span.set_data("mcp.resource.uri", resource_uri)
147+
span.set_data("mcp.method.name", "resources/read")
148+
149+
# Extract and set protocol/scheme
150+
parsed_uri = urlparse(resource_uri)
151+
if parsed_uri.scheme:
152+
span.set_data("mcp.resource.protocol", parsed_uri.scheme)
153+
154+
# Set request metadata
155+
span.set_data("mcp.request.id", "req_789ghi")
156+
span.set_data("mcp.session.id", "session_xyz789")
157+
span.set_data("mcp.transport", "http")
158+
159+
# Access the resource
160+
resource_data = read_resource(resource_uri)
161+
```
162+
163+
## Common Span Attributes
164+
165+
<Include name="tracing/mcp-module/common-span-attributes" />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
The following attributes are common across all MCP span types and SHOULD be set when available:
2+
3+
| Data Attribute | Type | Requirement Level | Description | Example |
4+
| :------------------ | :----- | :---------------- | :----------------------------------------------- | :------------------------- |
5+
| `mcp.transport` | string | recommended | The transport method used for MCP communication. | `"stdio"`, `"sse", "http"` |
6+
| `network.transport` | string | recommended | The network transport used. | `"pipe"`, `"tcp"` |
7+
| `mcp.session.id` | string | recommended | The session identifier for the MCP connection. | `"a1b2c3d4e5f6"` |
8+
| `mcp.request.id` | string | optional | The unique identifier for the MCP request. | `"req_123abc"` |
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Describes MCP prompt retrieval.
2+
3+
The [@sentry_sdk.trace()](/platforms/python/tracing/instrumentation/custom-instrumentation/#span-templates) decorator can also be used to create this span.
4+
5+
- The span's `op` MUST be `"mcp.server"`.
6+
- The span `name` SHOULD be `"prompts/get {mcp.prompt.name}"`.
7+
- The `mcp.prompt.name` attribute MUST be set to the prompt's name. (e.g. `"code_review"`)
8+
- The `mcp.method.name` attribute SHOULD be set to `"prompts/get"`.
9+
- All [Common Span Attributes](#common-span-attributes) SHOULD be set.
10+
11+
Additional attributes on the span:
12+
13+
| Data Attribute | Type | Requirement Level | Description | Example |
14+
| :---------------------------------- | :----- | :---------------- | :-------------------------------------------------------------------------------- | :--------------------------------------------- |
15+
| `mcp.prompt.name` | string | required | The name of the MCP prompt being retrieved. | `"code_review"` |
16+
| `mcp.method.name` | string | recommended | Should be set to "prompts/get". | `"prompts/get"` |
17+
| `mcp.request.id` | string | optional | The unique identifier for the MCP request. | `"req_456def"` |
18+
| `mcp.request.argument.*` | any | optional | Prompt input arguments (requires `send_default_pii=True`). | `"python"` for `mcp.request.argument.language` |
19+
| `mcp.prompt.result.message_content` | string | optional | The message content from the prompt retrieval (requires `send_default_pii=True`). | `"Review the following code..."` |
20+
| `mcp.prompt.result.message_role` | string | optional | The role of the message (only for single-message prompts). | `"user"`, `"assistant"`, `"system"` |
21+
| `mcp.prompt.result.message_count` | int | optional | The number of messages in the prompt result. | `1`, `3` |
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Describes MCP resource access.
2+
3+
The [@sentry_sdk.trace()](/platforms/python/tracing/instrumentation/custom-instrumentation/#span-templates) decorator can also be used to create this span.
4+
5+
- The span's `op` MUST be `"mcp.server"`.
6+
- The span `name` SHOULD be `"resources/read {mcp.resource.uri}"`.
7+
- The `mcp.resource.uri` attribute MUST be set to the resource's URI. (e.g. `"file:///path/to/resource"`)
8+
- The `mcp.method.name` attribute SHOULD be set to `"resources/read"`.
9+
- All [Common Span Attributes](#common-span-attributes) SHOULD be set.
10+
11+
Additional attributes on the span:
12+
13+
| Data Attribute | Type | Requirement Level | Description | Example |
14+
| :---------------------- | :----- | :---------------- | :------------------------------------------- | :---------------------------- |
15+
| `mcp.resource.uri` | string | required | The URI of the MCP resource being accessed. | `"file:///path/to/resource"` |
16+
| `mcp.method.name` | string | recommended | Should be set to "resources/read" | `"resources/read"` |
17+
| `mcp.request.id` | string | optional | The unique identifier for the MCP request. | `"req_789ghi"` |
18+
| `mcp.resource.protocol` | string | optional | The protocol/scheme of the MCP resource URI. | `"file"`, `"http"`, `"https"` |
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Describes MCP tool execution.
2+
3+
The [@sentry_sdk.trace()](/platforms/python/tracing/instrumentation/custom-instrumentation/#span-templates) decorator can also be used to create this span.
4+
5+
- The span's `op` MUST be `"mcp.server"`.
6+
- The span `name` SHOULD be `"tools/call {mcp.tool.name}"`.
7+
- The `mcp.tool.name` attribute MUST be set to the tool's name. (e.g. `"get_weather"`)
8+
- The `mcp.method.name` attribute SHOULD be set to `"tools/call"`.
9+
- All [Common Span Attributes](#common-span-attributes) SHOULD be set.
10+
11+
Additional attributes on the span:
12+
13+
| Data Attribute | Type | Requirement Level | Description | Example |
14+
| :------------------------------ | :------ | :---------------- | :------------------------------------------------------- | :------------------------------------------------ |
15+
| `mcp.tool.name` | string | required | The name of the MCP tool being called. | `"get_weather"` |
16+
| `mcp.method.name` | string | recommended | Should be set to "tools/call". | `"tools/call"` |
17+
| `mcp.request.id` | string | optional | The unique identifier for the MCP request. | `"req_123abc"` |
18+
| `mcp.request.argument.*` | any | optional | Tool input arguments (requires `send_default_pii=True`). | `"San Francisco"` for `mcp.request.argument.city` |
19+
| `mcp.tool.result.content` | string | optional | The result/output content from the tool execution. | `"The weather is sunny"` |
20+
| `mcp.tool.result.content_count` | int | optional | The number of items/keys in the tool result. | `5` |
21+
| `mcp.tool.result.is_error` | boolean | optional | Whether the tool execution resulted in an error. | `True` |

0 commit comments

Comments
 (0)