Bug Ticket Agent — AI-Powered Sentry Triage into Notion
This is a submission for the Notion MCP Challenge.
What I Built
Every time an error fires in production, someone on your team has to open Sentry, read through the stack trace, decide how urgent it is, and manually write a ticket. That happens at 2am. People skip steps. Duplicates pile up.
Bug Ticket Agent automates the entire triage workflow. It listens for Sentry webhooks, runs a Claude AI agent to analyze each error, and writes a structured, prioritized ticket directly into a Notion database — including deduplication, AI-generated summaries, and suggested fixes.
Key Capabilities
- Automatic intake — receives Sentry webhook events and queues them for processing
- Intelligent deduplication — searches Notion before creating anything; updates existing tickets instead of duplicating
- AI priority assignment — Claude assigns P0–P3 based on environment, user impact, and frequency
- Structured tickets — every ticket gets an error type, stack trace, affected users, AI summary, and a concrete fix suggestion
- Escalation alerts — P0/P1 production errors are flagged immediately for PagerDuty/Slack wiring
- Queue-based processing — serialized job queue prevents race conditions when Sentry fires bursts of events
Demo
[Terminal A — server running]
🐛 bug-ticket-agent running on http://localhost:3000
[Terminal B — send a P0]
$ npm run demo -- --scenario p0
→ Webhook received: "TypeError: Cannot read properties of undefined (reading 'charge')"
→ Triage started...
→ Searching Notion for duplicates... none found
→ Assigning priority: P0 (payment service, 247 affected users, production)
→ Writing summary and suggested fix...
→ Creating Notion ticket... ✓ page_id: abc123
⚠️ ESCALATION ALERT: P0 detected in production — notify on-call immediately
[Notion database — ticket appears]
Title: TypeError: Cannot read properties of undefined (reading 'charge')
Priority: P0
Env: production
Users: 247
Summary: "Payment processing failed due to a null reference on the charge object..."
Fix: "Add a null check for stripe.charge in src/payments/processor.ts:88"
[Send the same error again]
$ npm run demo -- --scenario duplicate
→ Searching Notion for duplicates... found existing ticket (page_id: abc123)
→ Updating frequency: 12 → 18 occurrences, last seen: now
→ Done. No duplicate created.
Show Us the Code
GitHub: bug-ticket-agent
Sentry Webhook
│
▼
Express Server ──→ Job Queue (serialized)
│
▼
Claude Triage Agent
├── Search Notion (duplicate check)
├── Assign P0–P3 priority
├── Write summary + fix suggestion
└── Create or Update Notion page
│
▼
Notion Database
(Bug Tracker, live)
Tech stack: TypeScript · Express · Claude claude-sonnet-4-6 · Notion MCP Server · Zod
How I Used Notion MCP
1. Claude Discovers Tools at Runtime
Instead of hardcoding Notion API calls, I spin up the official Notion MCP server over stdio and let Claude fetch the available tools on startup. Claude then decides which tools to call and in what order — search-database, create-page, update-page — based on what it finds.
This means zero Notion API wrapper code on my end. If Notion adds new MCP tools, the agent gets them for free.
2. Deduplication via MCP Search
The first thing Claude does in every triage loop is call the Notion search tool with the error title. If it finds a matching ticket, it updates frequency and last-seen instead of creating a new one. This logic lives entirely in the agent prompt — not in hand-written code:
1. Search the database for an existing ticket matching this error title.
2. If duplicate found: update frequency and last seen. Report the page ID.
3. If no duplicate: assign priority, write summary and fix, create a new page.
3. Structured Tickets via MCP Create
When Claude creates a ticket, it maps the error data to Notion's property schema — priority as a select, environment as a multi-select, stack trace as a code block, summary and fix as rich text. The Notion MCP server handles all the property formatting; Claude just fills in the values.
4. The Priority Rubric
Claude doesn't guess — it applies a deterministic rubric from the system prompt:
| Priority | Criteria |
|---|---|
| P0 | Production down, data loss, auth/payment broken, or >100 affected users |
| P1 | Major feature broken, >10 affected users, recurring in production |
| P2 | Minor degradation, <10 users, workaround exists, or staging |
| P3 | Cosmetic, single user, dev environment, very low frequency |
What This Unlocks
Before this: an engineer gets paged, opens Sentry, reads the error, manually creates a Notion ticket, assigns a priority based on gut feel, and hopes they didn't miss a duplicate.
After this: the ticket is already in Notion by the time the engineer opens Slack. It has a priority, a summary, a suggested fix, and no duplicate. The engineer can go straight to fixing instead of triaging.
The Notion MCP server made this possible without building a custom Notion integration. Claude handled all the tool orchestration — I just described what I wanted in the system prompt.


Top comments (1)
Nice