|
| 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" /> |
0 commit comments