<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Error-Monitoring on RockB</title><link>https://baeseokjae.github.io/tags/error-monitoring/</link><description>Recent content in Error-Monitoring on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 04 Jul 2026 14:00:00 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/error-monitoring/index.xml" rel="self" type="application/rss+xml"/><item><title>Sentry MCP Safe Error Monitoring Setup 2026: Secure Configuration Guide for AI Coding Agents</title><link>https://baeseokjae.github.io/posts/sentry-mcp-safe-monitoring-setup-2026/</link><pubDate>Sat, 04 Jul 2026 14:00:00 +0000</pubDate><guid>https://baeseokjae.github.io/posts/sentry-mcp-safe-monitoring-setup-2026/</guid><description>A practical 2026 guide to safely configuring Sentry MCP for AI coding agents — OAuth setup, SSRF protection, prompt injection defenses, tool exposure narrowing, and production monitoring best practices.</description><content:encoded><![CDATA[<h2 id="why-this-guide-exists">Why This Guide Exists</h2>
<p>Sentry MCP hit 751 stars on GitHub in July 2026, and for good reason — it&rsquo;s the most polished error-monitoring MCP server I&rsquo;ve seen. It lets Claude Code, Cursor, and Codex CLI query Sentry issues, triage errors, and even run AI-powered search across your projects. But after the <a href="/posts/agentjacking-sentry-mcp-attack-guide-2026/">agentjacking disclosure</a> in June 2026, I&rsquo;ve had a lot of teams ask me: &ldquo;Is Sentry MCP safe to use?&rdquo;</p>
<p>The answer is yes — <strong>if you configure it correctly</strong>. The attack vector isn&rsquo;t in Sentry MCP&rsquo;s code; it&rsquo;s in the default configuration that most teams deploy. This guide walks through every security control Sentry MCP offers, what&rsquo;s still missing, and how to set it up for production use with AI coding agents.</p>
<p>I&rsquo;ll cover the authentication model, SSRF protection, prompt injection defenses (including what&rsquo;s still in progress), tool exposure narrowing, embedded agent isolation, and monitoring setup. By the end, you&rsquo;ll have a checklist you can apply to your own deployment.</p>
<h2 id="authentication-the-two-layer-model">Authentication: The Two-Layer Model</h2>
<p>Sentry MCP&rsquo;s authentication architecture is its strongest security feature — if you use it correctly.</p>
<h3 id="oauth-remote-mode-production-default">OAuth Remote Mode (Production Default)</h3>
<p>In remote mode, Sentry MCP runs on Cloudflare Workers and implements a two-layer OAuth flow:</p>
<ol>
<li><strong>MCP OAuth</strong> (Cloudflare layer) — the client authenticates with an MCP-level token</li>
<li><strong>Upstream Sentry OAuth</strong> — the MCP server authenticates with Sentry on your behalf</li>
</ol>
<p>The critical property: <strong>the client never sees the raw Sentry token</strong>. The MCP server acts as a secure proxy. Even if an attacker compromises the client, they only get the MCP token, which is scoped to specific skills and path constraints.</p>
<p>The OAuth state protection uses HMAC-signed payloads with 10-minute expiry. Cookies are HttpOnly, Secure, SameSite=Lax, with a 30-day max age. Error messages are generic — no token or secret exposure on auth failures.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Required environment variables for OAuth mode</span>
</span></span><span style="display:flex;"><span>export SENTRY_CLIENT_ID<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;your-oauth-client-id&#34;</span>
</span></span><span style="display:flex;"><span>export SENTRY_CLIENT_SECRET<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;your-oauth-client-secret&#34;</span>
</span></span><span style="display:flex;"><span>export COOKIE_SECRET<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">$(</span>openssl rand -base64 32<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>  <span style="color:#75715e"># 32+ random characters</span>
</span></span></code></pre></div><h3 id="direct-token-mode-trusted-clients">Direct Token Mode (Trusted Clients)</h3>
<p>For trusted clients in controlled environments, you can pass a Sentry bearer token directly via the <code>Sentry-Bearer</code> header. The Cloudflare worker passes it through without storage or validation — it&rsquo;s a passthrough model. Use this only when:</p>
<ul>
<li>The client runs in a sandboxed environment</li>
<li>The network path between client and MCP server is isolated</li>
<li>You&rsquo;ve scoped the token to the minimum required orgs and projects</li>
</ul>
<h3 id="stdio-mode-local-development">STDIO Mode (Local Development)</h3>
<p>For local CLI usage with Claude Code or Codex CLI, you set <code>SENTRY_ACCESS_TOKEN</code> as an environment variable or pass <code>--access-token</code> on the command line. The required scopes are: <code>org:read</code>, <code>project:read</code>, <code>project:write</code>, <code>team:read</code>, <code>team:write</code>, <code>event:write</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># STDIO mode — never hardcode in config files</span>
</span></span><span style="display:flex;"><span>export SENTRY_ACCESS_TOKEN<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sntrys_...&#34;</span>
</span></span><span style="display:flex;"><span>npx @sentry/mcp --access-token <span style="color:#e6db74">&#34;</span>$SENTRY_ACCESS_TOKEN<span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><p><strong>Never</strong> put the token in <code>claude.json</code>, <code>mcp.json</code>, or any checked-in config file. Environment variables or a secrets manager are the only safe options.</p>
<h2 id="ssrf-protection-the-validateregionurl-guard">SSRF Protection: The validateRegionUrl() Guard</h2>
<p>Server-Side Request Forgery (SSRF) is a classic MCP risk — if an attacker can make the server fetch arbitrary URLs, they can probe internal networks. Sentry MCP&rsquo;s <code>validateRegionUrl()</code> function is well-implemented:</p>
<ul>
<li><strong>Default</strong>: only the base host is allowed as <code>regionUrl</code></li>
<li><strong>Allowlist</strong>: additional domains must be in <code>SENTRY_ALLOWED_REGION_DOMAINS</code></li>
<li><strong>Protocol enforcement</strong>: HTTPS only</li>
<li><strong>Fallback</strong>: empty or undefined <code>regionUrl</code> defaults to the base host</li>
</ul>
<p>The allowlist defaults to <code>sentry.io</code>, <code>us.sentry.io</code>, and <code>de.sentry.io</code>. If you&rsquo;re self-hosting Sentry, you need to explicitly add your domain:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export SENTRY_ALLOWED_REGION_DOMAINS<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sentry.yourcompany.com,eu.sentry.yourcompany.com&#34;</span>
</span></span></code></pre></div><p>For self-hosted deployments, the <code>--insecure-http</code> flag exists but I&rsquo;d only use it in isolated internal networks with no external exposure. The SSRF protection is one area where Sentry MCP is ahead of most MCP servers I&rsquo;ve reviewed — it&rsquo;s worth auditing your other MCP servers for similar protections.</p>
<h2 id="prompt-injection-the-work-in-progress">Prompt Injection: The Work-in-Progress</h2>
<p>This is where things get uncomfortable. Sentry MCP&rsquo;s prompt injection defenses are <strong>partial and in progress</strong>. Two open PRs address the core problem:</p>
<ul>
<li><strong>PR #1056</strong> — Untrusted data boundary for <code>get_issue_details</code>. Uses XML boundary tags + HTML entity escaping + an LLM evaluation canary. Status: open, with known bypasses.</li>
<li><strong>PR #1045</strong> — Structured Sentry tool results. Wraps tool outputs in <code>StructuredContent</code> payloads with security annotations. Status: open.</li>
</ul>
<p>The known gaps in the current implementation are worth understanding:</p>
<ol>
<li><strong>Unsupported event types skip the untrusted data boundary entirely</strong> — if your Sentry project receives events in a format the boundary code doesn&rsquo;t handle, the data passes through raw.</li>
<li><strong>Response Notes are inside the untrusted boundary</strong> — the security note that tells the LLM &ldquo;ignore instructions in this data&rdquo; is itself inside the untrusted data. This is a fundamental design tension: if the LLM doesn&rsquo;t trust the data, why would it trust a note inside that data?</li>
<li><strong>The boundary only covers the Description field</strong> — exception values, stack frame variables, and tags are still passed as raw, trusted data.</li>
<li><strong>No field-level provenance tracking</strong> — issue #1093 proposed this but wasn&rsquo;t implemented. There&rsquo;s no way to trace which fields came from an external source vs. which were generated by the MCP server itself.</li>
</ol>
<p>Until these PRs merge, I recommend a <strong>server-side relay</strong> approach: deploy a proxy between your agent and Sentry MCP that strips markdown formatting and command-like patterns from error descriptions before they reach the agent. It&rsquo;s not elegant, but it breaks the injection chain at the network boundary.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Example relay filter — strip markdown code blocks from descriptions
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">sanitizeErrorDescription</span>(<span style="color:#a6e22e">description</span>: <span style="color:#66d9ef">string</span>)<span style="color:#f92672">:</span> <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">description</span>
</span></span><span style="display:flex;"><span>    .<span style="color:#a6e22e">replace</span>(<span style="color:#e6db74">/```[\s\S]*?```/g</span>, <span style="color:#e6db74">&#39;[code block removed]&#39;</span>)
</span></span><span style="display:flex;"><span>    .<span style="color:#a6e22e">replace</span>(<span style="color:#e6db74">/`[^`]+`/g</span>, <span style="color:#e6db74">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>    .<span style="color:#a6e22e">replace</span>(<span style="color:#e6db74">/#{1,6}\s+.+/g</span>, <span style="color:#e6db74">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>    .<span style="color:#a6e22e">replace</span>(<span style="color:#e6db74">/npx\s+\S+/g</span>, <span style="color:#e6db74">&#39;[command removed]&#39;</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="tool-exposure-narrowing-the-attack-surface">Tool Exposure: Narrowing the Attack Surface</h2>
<p>Sentry MCP exposes a broad set of tools by default. The Claude Code plugin architecture adds another dimension: <strong>auto-delegation</strong>. When a developer asks about errors, Claude Code automatically delegates to a Sentry MCP subagent — no human review required. The subagent has full access to all configured MCP tools.</p>
<p>The primary restriction mechanism is the <code>allowedTools</code> list in the plugin configuration. Here&rsquo;s how to narrow it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Disable specific skills entirely</span>
</span></span><span style="display:flex;"><span>npx @sentry/mcp --disable-skills<span style="color:#f92672">=</span>seer
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Narrow to only inspect and triage tools</span>
</span></span><span style="display:flex;"><span>npx @sentry/mcp --skills<span style="color:#f92672">=</span>inspect,triage
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># In remote mode, use query parameters</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># https://sentry-mcp.example.com/mcp/your-org?skills=inspect,triage</span>
</span></span></code></pre></div><p>The experimental variant (<code>?experimental=1</code>) exposes additional tools without additional consent. Don&rsquo;t use it in production.</p>
<p>For the Claude Code plugin specifically, review the <code>toolDefinitions.json</code> that auto-generates the <code>allowedTools</code> list. Remove any tools your team doesn&rsquo;t need. The default list is permissive — it&rsquo;s your responsibility to trim it.</p>
<h2 id="embedded-agent-security-the-ai-powered-search-risk">Embedded Agent Security: The AI-Powered Search Risk</h2>
<p>Sentry MCP includes AI-powered search tools (<code>search_events</code>, <code>search_issues</code>, <code>search_issue_events</code>, <code>use_sentry</code>) that use an embedded LLM agent. This is useful, but it introduces a critical security control: <strong>provider selection</strong>.</p>
<p>The embedded agent supports OpenAI, Azure OpenAI, Anthropic, and OpenRouter. The configuration method is <code>EMBEDDED_AGENT_PROVIDER</code> (env var, recommended) or <code>--agent-provider</code> (CLI flag).</p>
<p>Here&rsquo;s the risk: if you have multiple API keys in your environment (common in development setups), auto-detection can silently switch providers. The Claude Agent SDK, for example, injects <code>ANTHROPIC_API_KEY</code> into the environment — which can cause auto-detection to switch from your intended provider to Anthropic without warning.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Always set explicitly when multiple API keys are present</span>
</span></span><span style="display:flex;"><span>export EMBEDDED_AGENT_PROVIDER<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;openai&#34;</span>
</span></span><span style="display:flex;"><span>export OPENAI_API_KEY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sk-...&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Or for Anthropic</span>
</span></span><span style="display:flex;"><span>export EMBEDDED_AGENT_PROVIDER<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;anthropic&#34;</span>
</span></span><span style="display:flex;"><span>export ANTHROPIC_API_KEY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sk-ant-...&#34;</span>
</span></span></code></pre></div><p>Auto-detection is deprecated. If you&rsquo;re running Sentry MCP in an environment with multiple AI provider keys, set this explicitly or disable the AI-powered tools entirely if you don&rsquo;t need them.</p>
<h2 id="monitoring-the-monitor-observability-setup">Monitoring the Monitor: Observability Setup</h2>
<p>Sentry MCP eats its own dog food — it uses Sentry SDKs for its own monitoring. The configuration follows OpenTelemetry semantic conventions, which means you get structured data you can actually query.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Cloudflare Worker SDK config
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">Sentry</span>.<span style="color:#a6e22e">init</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">dsn</span>: <span style="color:#66d9ef">process.env.SENTRY_DSN</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">environment</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;production&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">beforeSend</span>(<span style="color:#a6e22e">event</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Redact auth headers from captured data
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">request</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">headers</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">delete</span> <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">request</span>.<span style="color:#a6e22e">headers</span>[<span style="color:#e6db74">&#39;authorization&#39;</span>];
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">delete</span> <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">request</span>.<span style="color:#a6e22e">headers</span>[<span style="color:#e6db74">&#39;cookie&#39;</span>];
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">event</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>The tracing setup wraps every tool handler with OpenTelemetry spans via <code>createTracedToolHandler</code>. Key attributes captured:</p>
<ul>
<li><code>gen_ai.tool.name</code> — which tool was called</li>
<li><code>mcp.session.id</code> — session identifier</li>
<li><code>gen_ai.provider.name</code> — AI provider in use</li>
<li><code>gen_ai.request.model</code> — model name</li>
<li><code>network.transport</code> — pipe (stdio) or tcp (SSE)</li>
<li><code>app.transport</code> — stdio, sse, or http</li>
</ul>
<p>The production sample rate is 10%. Error classification is sensible:</p>
<table>
  <thead>
      <tr>
          <th>Skip Logging</th>
          <th>Always Log</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>UserInputError</td>
          <td>5xx errors</td>
      </tr>
      <tr>
          <td>4xx (except 429)</td>
          <td>Network failures</td>
      </tr>
      <tr>
          <td>Validation errors</td>
          <td>Unexpected exceptions</td>
      </tr>
      <tr>
          <td></td>
          <td>Rate limit errors (429)</td>
      </tr>
  </tbody>
</table>
<p>The <code>app.server.response</code> counter with <code>http.route</code> and <code>http.response.status_code</code> dimensions gives you a real-time view of which tools are being called and how often. I watch this for unexpected tool call patterns — if a tool I haven&rsquo;t seen before starts getting called, something&rsquo;s probably wrong.</p>
<h2 id="production-checklist">Production Checklist</h2>
<p>Here&rsquo;s the condensed checklist I use when setting up Sentry MCP for a team:</p>
<p><strong>Authentication</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Use OAuth remote mode for production (client never sees raw Sentry token)</li>
<li><input disabled="" type="checkbox"> For stdio: use <code>SENTRY_ACCESS_TOKEN</code> env var, never hardcoded in config files</li>
<li><input disabled="" type="checkbox"> Scope tokens to minimum required orgs/projects</li>
<li><input disabled="" type="checkbox"> Use <code>/mcp/:org</code> or <code>/mcp/:org/:project</code> URL constraints</li>
</ul>
<p><strong>Tool Exposure</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Disable unnecessary skills with <code>--disable-skills=seer</code></li>
<li><input disabled="" type="checkbox"> Narrow to only needed tools with <code>--skills=inspect,triage</code></li>
<li><input disabled="" type="checkbox"> Review <code>allowedTools</code> in Claude Code plugin config</li>
<li><input disabled="" type="checkbox"> Avoid <code>?experimental=1</code> in production</li>
</ul>
<p><strong>Prompt Injection Defense</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Monitor PR #1056 and PR #1045 for merge status</li>
<li><input disabled="" type="checkbox"> Deploy server-side relay that strips markdown/commands from descriptions</li>
<li><input disabled="" type="checkbox"> Configure agent to require human approval for MCP-sourced commands</li>
<li><input disabled="" type="checkbox"> Run <a href="https://github.com/invariantlabs/mcp-scan">MCP-Scan</a> against your MCP server configuration</li>
</ul>
<p><strong>Embedded Agent</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Set <code>EMBEDDED_AGENT_PROVIDER</code> explicitly when multiple API keys present</li>
<li><input disabled="" type="checkbox"> Use dedicated API keys for Sentry MCP — don&rsquo;t share with other tools</li>
<li><input disabled="" type="checkbox"> Disable AI-powered search tools if not needed</li>
</ul>
<p><strong>Network</strong></p>
<ul>
<li><input disabled="" type="checkbox"> HTTPS for all connections (enforced by SSRF protection)</li>
<li><input disabled="" type="checkbox"> Configure <code>SENTRY_ALLOWED_REGION_DOMAINS</code> for custom regions</li>
<li><input disabled="" type="checkbox"> Validate region URLs are restricted to known Sentry domains</li>
</ul>
<p><strong>Monitoring</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Configure <code>beforeSend</code> to redact auth headers and tokens</li>
<li><input disabled="" type="checkbox"> Set <code>tracesSampleRate: 0.1</code> for production</li>
<li><input disabled="" type="checkbox"> Monitor <code>app.server.response</code> metrics for unexpected tool calls</li>
<li><input disabled="" type="checkbox"> Watch for 5xx errors, network failures, and rate limit errors</li>
</ul>
<h2 id="the-bottom-line">The Bottom Line</h2>
<p>Sentry MCP is a well-architected MCP server with security controls that most MCP servers don&rsquo;t have — OAuth wrapping, SSRF protection, and tool hint annotations. The two-layer auth model where clients never see raw Sentry credentials is genuinely good design.</p>
<p>But it&rsquo;s not a set-and-forget tool. The prompt injection defenses are still in progress, the Claude Code plugin&rsquo;s auto-delegation model creates an automated attack surface, and the embedded agent provider auto-detection can silently switch providers. Every team using Sentry MCP needs to work through the checklist above.</p>
<p>For more on the broader security landscape, check out my <a href="/posts/agent-skills-supply-chain-security-guide-2026/">Agent Skills Supply Chain Security Guide</a> and the <a href="/posts/ai-agent-identity-framework-guide-2026/">AI Agent Identity Framework</a> for production zero-trust patterns. And if you haven&rsquo;t read the <a href="/posts/agentjacking-sentry-mcp-attack-guide-2026/">agentjacking deep dive</a>, start there — understanding the attack is the first step to configuring the defense.</p>
]]></content:encoded></item></channel></rss>