Build a Sentry MCP Server for Agentic Error Triage & Release Monitoring in 2026
An error feed is exactly what an agent should triage: pull issues, check the release that broke, drill into a stack trace. Build a Python FastMCP server for Sentry with read-first tools and guarded writes.
Deepak Bagada
CEO, SaaSNext
- Sentry's error feed becomes agent-callable via typed FastMCP tools — issues, search, detail, release health.
- Read-first triage: list/search/detail/health are read-only; resolve and comment are the only writes.
- A scoped Sentry auth token injected via env; read-only by default, event:write only if agents resolve issues.
- Every resolve carries a reason string and consumes a per-session write budget — no runaway backlog clearing.
- Every tool call writes a JSONL audit line so error-feed reads and writes are attributable.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
The observability stack that used to be read by humans is now read by agents. Sentry has become the default error-tracking and release-health platform for production teams, and it is also one of the platforms that adopted MCP explicitly in 2026 — which makes sense, because an error feed is exactly the kind of high-signal, high-volume data an agent should triage: pull the newest issues, group them by fingerprint, check which release introduced the regression, and open a fix. Doing that by clicking through a dashboard is a full-time job; doing it through typed MCP tools is a background task an engineer supervises.
This guide builds a production Sentry MCP server in Python with FastMCP: issue enumeration and search, event and stack-trace retrieval, release health, and two guarded writes (resolving an issue and commenting) — secured with a scoped Sentry auth token and read-first by default. It follows the governance-first architecture we catalogue in the MCP directory, and it is designed to feed the incident-response and alert-triage patterns in our AI workflows library.
Server Design Overview
graph TD
A[Engineer / Agent] --> M[Sentry MCP Server]
M --> T1[list_issues]
M --> T2[search_issues]
M --> T3[get_issue_detail]
M --> T4[get_release_health]
M --> T5[resolve_issue]
M --> T6[add_comment]
T1 --> S[Sentry API]
T2 --> S
T3 --> S
T4 --> S
T5 --> S
T6 --> S
S --> K[Scoped Auth Token]
M --> L[Audit Log]
The server is a read-first triage surface with two explicit, rate-limited writes. Issue listing, search, detail, and release health are the workhorses — agents can answer "what broke, when, and in which release" without a human touching the console. Resolving an issue and adding a comment are the only state-changing tools, and both are named, audited, and gated behind a budget, because a mis-issued resolve is how signal disappears.
Part 1 — Authentication and setup
.env
SENTRY_AUTH_TOKEN=sntrys_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
SENTRY_ORG=acme-corp
SENTRY_PROJECT=checkout-api
SENTRY_API_BASE=https://sentry.io/api/0
AUDIT_LOG=./audit.jsonl
WRITE_BUDGET_PER_SESSION=20
auth.py
import os, json, time
def headers() -> dict:
# Sentry auth tokens are bearer credentials; scope them to read + narrow write.
return {
"Authorization": f"Bearer {os.environ['SENTRY_AUTH_TOKEN']}",
"Content-Type": "application/json",
}
def audit(tool: str, meta: dict) -> None:
line = json.dumps({"ts": time.time(), "tool": tool, **meta})
with open(os.environ["AUDIT_LOG"], "a") as f:
f.write(line + "
")
def write_budget_ok() -> bool:
# Counts writes this process has performed; enforced in the write tools.
return write_count() < int(os.environ["WRITE_BUDGET_PER_SESSION"])
Sentry issues tokens per user and per scope: read-only tokens for the triage tools, and a token with event:write + project:write only if agents should resolve issues. The audit helper appends a JSONL line for every tool call, and write_budget_ok enforces a per-session write budget — an agent can triage thousands of issues but only resolve a bounded number per session, which is the correct posture when an AI is touching the state of a production error feed.
Part 2 — The FastMCP server
server.py
import os, httpx
from fastmcp import FastMCP
server = FastMCP(name="sentry-triage", version="2.1.0")
API = os.environ["SENTRY_API_BASE"]
ORG = os.environ["SENTRY_ORG"]
PROJECT = os.environ["SENTRY_PROJECT"]
@server.tool(description="List the newest issues for the project, optionally by status.")
def list_issues(status: str = "unresolved", limit: int = 25) -> list:
params = {"project": PROJECT, "query": f"is:{status}", "limit": limit}
r = httpx.get(f"{API}/projects/{ORG}/{PROJECT}/issues/",
params=params, headers=headers(), timeout=15)
r.raise_for_status()
audit("list_issues", {"status": status, "limit": limit})
return [{
"id": i["id"], "title": i["title"],
"level": i["level"], "count": i["count"],
"first_seen": i["firstSeen"], "last_seen": i["lastSeen"],
"permalink": i["permalink"],
} for i in r.json()]
@server.tool(description="Search issues by free-text query and filters.")
def search_issues(query: str, status: str = "unresolved", limit: int = 25) -> list:
params = {"project": PROJECT, "query": f"is:{status} {query}", "limit": limit}
r = httpx.get(f"{API}/projects/{ORG}/{PROJECT}/issues/",
params=params, headers=headers(), timeout=15)
r.raise_for_status()
audit("search_issues", {"query": query, "limit": limit})
return [{"id": i["id"], "title": i["title"], "count": i["count"]}
for i in r.json()]
@server.tool(description="Get full detail for one issue, including stack trace metadata.")
def get_issue_detail(issue_id: str) -> dict:
r = httpx.get(f"{API}/issues/{issue_id}/", headers=headers(), timeout=15)
r.raise_for_status()
audit("get_issue_detail", {"issue_id": issue_id})
return r.json()
The pattern repeats cleanly: typed tools translate to Sentry API calls, responses are trimmed to the fields an agent needs, and every call is audited. The inputSchema for each tool is inferred from the Python signature by FastMCP, so any MCP client can introspect the tools before calling. Trimming the response — title, level, count, first/last seen, permalink — is what keeps triage loops cheap: the agent gets the signal without pulling the full stack trace until it actually drills in.
Part 3 — Release health and the guarded writes
releases.py
from fastmcp import FastMCP
import httpx, os
API = os.environ["SENTRY_API_BASE"]
ORG = os.environ["SENTRY_ORG"]
@server.tool(description="Get release health: crash-free rate and error counts per release.")
def get_release_health(release: str | None = None, limit: int = 10) -> list:
params = {"project": PROJECT, "summary": "1", "limit": limit}
if release:
params["query"] = release
r = httpx.get(f"{API}/organizations/{ORG}/releases/",
params=params, headers=headers(), timeout=15)
r.raise_for_status()
audit("get_release_health", {"release": release or "latest", "limit": limit})
return [{"version": rel["version"],
"crash_free_rate": rel.get("crashFreeRate"),
"error_count": rel.get("errorCount")} for rel in r.json()]
@server.tool(description="Resolve an issue. Write operation, rate-limited per session.")
def resolve_issue(issue_id: str, reason: str) -> dict:
if not write_budget_ok():
raise ValueError("write budget exceeded for this session")
if not reason.strip():
raise ValueError("reason is required so resolves are reviewable")
r = httpx.put(f"{API}/issues/{issue_id}/", headers=headers(),
json={"status": "resolved"}, timeout=15)
r.raise_for_status()
audit("resolve_issue", {"issue_id": issue_id, "reason": reason})
return {"issue_id": issue_id, "status": "resolved", "reason": reason}
@server.tool(description="Add a comment to an issue. Write operation, rate-limited.")
def add_comment(issue_id: str, comment: str) -> dict:
if not write_budget_ok():
raise ValueError("write budget exceeded for this session")
r = httpx.post(f"{API}/issues/{issue_id}/comments/", headers=headers(),
json={"comment": comment}, timeout=15)
r.raise_for_status()
audit("add_comment", {"issue_id": issue_id})
return {"issue_id": issue_id, "comment_id": r.json()["id"]}
The writes are where the governance lives. resolve_issue demands a reason and refuses to act without one, and both writes consume the per-session budget — an agent cannot resolve the entire backlog in one runaway loop. The reason string is the reviewable artifact: when an engineer audits the session, every state change carries its justification. That bounded-write posture is the same one we recommend across every server in the MCP directory.
Part 4 — Client configuration and launch
mcpServers config
{
"mcpServers": {
"sentry-triage": {
"command": "python",
"args": ["-m", "sentry_triage_mcp"],
"env": {
"SENTRY_AUTH_TOKEN": "${SENTRY_AUTH_TOKEN}",
"SENTRY_ORG": "acme-corp",
"SENTRY_PROJECT": "checkout-api",
"WRITE_BUDGET_PER_SESSION": "20"
}
}
}
}
main.py
from server import server
import releases # registers release health + write tools
if __name__ == "__main__":
server.run(transport="stdio") # desktop; or transport="http" for remote
The mcpServers block wires the server into Claude Desktop, Cursor, or any MCP client: python -m sentry_triage_mcp starts it, env injects the scoped token (never a literal secret), and the typed tools appear automatically. stdio serves desktop agents; HTTP serves remote or stateless MCP 2026-07-28 deployments behind a gateway, where the token stays on the server side and clients authenticate at the edge. Deployment guidance for both transports lives in the MCP directory.
Security & governance checklist
- Read-first triage. List, search, detail, and release health are read-only; resolve and comment are the only writes.
- Scoped tokens. A read-only token by default;
event:writeandproject:writeonly if agents should change issue state. - Write budget per session. Resolves and comments consume a bounded budget, so a runaway loop cannot clear the backlog.
- Reason required. Every resolve carries a reason string — the reviewable artifact for audits.
- Audit every call. JSONL audit of tool, timestamp, and parameters; error-feed reads must be attributable.
Frequently Asked Questions
Q: Why expose Sentry through MCP instead of the dashboard?
A: Because triage is the task: an agent can pull the newest issues, search by fingerprint, check which release introduced a regression, and drill into a stack trace without a human clicking through the console — while the engineer supervises typed tool calls and the audit trail.
Q: How is Sentry authentication handled?
A: A scoped Sentry auth token used as a bearer credential, injected via environment variables. Use a read-only token by default and add event:write/project:write scopes only if agents should resolve issues or comment.
Q: Which tools are safe to expose read-first?
A: list_issues, search_issues, get_issue_detail, and get_release_health are read-only and safe. resolve_issue and add_comment are state-changing and carry a per-session write budget plus a mandatory reason field.
Q: Can this server run for remote agents?
A: Yes. The HTTP transport serves the same tools for remote or stateless MCP 2026-07-28 deployments, typically behind a gateway where the Sentry token stays server-side and OAuth handles client auth at the edge.
Q: How do I keep token costs low on triage?
A: Trim every tool response to the fields an agent needs, filter issues at the API level with the query parameter, and fetch full stack traces only in get_issue_detail — which agents should call after narrowing to a handful of candidates.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
CEO, SaaSNext
Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.
Build a Twilio Voice MCP Server for Agentic Outbound Calls & IVR Automation in 2026
Next Story →Build a Long-Running Agent Task Workflow with the MCP Tasks Extension & LangGraph in 2026
Related Intelligence Analysis
Vercel AI SDK Tool Calling React: 5 Steps (2026)
Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...
Fact-Density vs. Word Count: The New SEO for 2026
Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...
NVIDIA Audex vs Qwen3.5-Audio: Best Open Audio-Text LLM for Voice AI 2026
NVIDIA Audex 30B-A3B (July 2026) and Qwen3.5-35B-A3B are the two leading open audio-text LLMs. Audex uniquely handles both audio understanding and generation in a single model while preserving text intelligence. Qwen3.5-...