@rui.branco/sentry-mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +219 -0
  2. package/index.js +24 -97
  3. package/package.json +3 -2
package/README.md ADDED
@@ -0,0 +1,219 @@
1
+ # Sentry MCP Server
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that brings Sentry error tracking context directly into Claude Code. Fetch issues, inspect stack traces, manage issue status, and search errors without leaving your development environment.
4
+
5
+ ## Overview
6
+
7
+ When debugging production errors, context switching between Sentry and your code editor breaks flow and wastes time. This MCP server solves that by:
8
+
9
+ - **Fetching full issue details** - Get error metadata, event counts, assignment, and status instantly
10
+ - **Inspecting stack traces** - View full stack traces with in-app frame markers, breadcrumbs, and HTTP request context
11
+ - **Managing issues** - Resolve, reopen, ignore, and assign issues directly from Claude Code
12
+ - **Searching errors** - Use Sentry's search syntax to find issues across your project
13
+
14
+ ## Features
15
+
16
+ | Feature | Description |
17
+ |---------|-------------|
18
+ | Issue Details | Title, level, status, event count, user count, culprit, assignee |
19
+ | Stack Traces | Full exception chain with in-app frames highlighted |
20
+ | Breadcrumbs | Last N events leading up to the error |
21
+ | HTTP Request | Method, URL, headers from the request that triggered the error |
22
+ | Tags & Contexts | Device, browser, OS, user info, and custom tags |
23
+ | Search | Full Sentry search syntax support |
24
+ | Multi-Instance | Support for multiple Sentry organizations/instances |
25
+
26
+ ## Installation
27
+
28
+ ### Prerequisites
29
+
30
+ - Node.js 18+
31
+ - [Claude Code](https://claude.ai/code) CLI
32
+ - Sentry account with API access
33
+
34
+ ### Step 1: Add to Claude Code
35
+
36
+ ```bash
37
+ claude mcp add --transport stdio sentry -- npx -y @rui.branco/sentry-mcp
38
+ ```
39
+
40
+ ### Step 2: Get Your Sentry API Token
41
+
42
+ 1. Go to [Sentry Auth Tokens](https://sentry.io/settings/account/api/auth-tokens/) (or your self-hosted Sentry equivalent)
43
+ 2. Click **"Create New Token"**
44
+ 3. Select scopes: `event:read`, `project:read`, `org:read`, `member:read`
45
+ 4. Click **"Create Token"**
46
+ 5. Copy the token
47
+
48
+ ### Step 3: Configure Credentials
49
+
50
+ Run the setup with your credentials:
51
+
52
+ ```bash
53
+ npx @rui.branco/sentry-mcp setup "YOUR_AUTH_TOKEN" "https://sentry.io" "your-org-slug" "your-project-slug"
54
+ ```
55
+
56
+ | Parameter | Description | Example |
57
+ |-----------|-------------|---------|
58
+ | Auth Token | The token you created in Step 2 | `sntrys_eyJpYX...` |
59
+ | Base URL | Your Sentry instance URL | `https://sentry.io` or `https://my-org.sentry.io` |
60
+ | Org Slug | Your Sentry organization slug | `my-company` |
61
+ | Project Slug | Default project slug (optional) | `frontend` |
62
+
63
+ Or run interactively (will prompt for each value):
64
+
65
+ ```bash
66
+ npx @rui.branco/sentry-mcp setup
67
+ ```
68
+
69
+ ### Step 4: Verify
70
+
71
+ Restart Claude Code and run `/mcp` to verify the server is connected.
72
+
73
+ ### Alternative: Manual Installation
74
+
75
+ If you prefer to install manually:
76
+
77
+ ```bash
78
+ git clone https://github.com/rui-branco/sentry-mcp.git ~/.config/sentry-mcp
79
+ cd ~/.config/sentry-mcp && npm install
80
+ node setup.js
81
+ ```
82
+
83
+ Then add to Claude Code:
84
+
85
+ ```bash
86
+ claude mcp add --transport stdio sentry -- node $HOME/.config/sentry-mcp/index.js
87
+ ```
88
+
89
+ ## Usage
90
+
91
+ ### List Issues
92
+
93
+ ```
94
+ > Show me unresolved Sentry issues
95
+
96
+ # Returns all unresolved issues with metadata
97
+ ```
98
+
99
+ ### Inspect an Error
100
+
101
+ ```
102
+ > Get the latest event for Sentry issue 12345
103
+
104
+ # Returns full stack trace, breadcrumbs, HTTP request, tags, and contexts
105
+ ```
106
+
107
+ ### Resolve After Fixing
108
+
109
+ ```
110
+ > Resolve Sentry issue 12345
111
+
112
+ # Marks the issue as resolved in Sentry
113
+ ```
114
+
115
+ ### Search Issues
116
+
117
+ ```
118
+ > Search Sentry for "TypeError" errors in the last 24 hours
119
+
120
+ # Uses Sentry search: is:unresolved TypeError lastSeen:-24h
121
+ ```
122
+
123
+ ### Example Output
124
+
125
+ ```
126
+ ## Stack Trace
127
+
128
+ **TypeError:** Cannot read properties of undefined (reading 'map')
129
+
130
+ ```
131
+ -> renderItems (src/components/List.tsx:42:15)
132
+ -> List (src/components/List.tsx:28:3)
133
+ React.createElement (react-dom.js:1234)
134
+ ```
135
+
136
+ ## Breadcrumbs (last 5)
137
+
138
+ - **navigation** [info] /dashboard -> /items
139
+ - **http** [info] GET /api/items (200)
140
+ - **ui.click** [info] button.load-more
141
+ - **http** [error] GET /api/items?page=2 (500)
142
+ - **console** [error] Unhandled error in component
143
+
144
+ ## HTTP Request
145
+
146
+ `GET /api/items?page=2`
147
+ ```
148
+
149
+ ## Multi-Instance Setup
150
+
151
+ For multiple Sentry organizations, add instances via setup:
152
+
153
+ ```bash
154
+ npx @rui.branco/sentry-mcp setup add "work" "TOKEN_1" "https://sentry.io" "work-org" "work-project"
155
+ npx @rui.branco/sentry-mcp setup add "personal" "TOKEN_2" "https://sentry.io" "personal-org" "side-project"
156
+ ```
157
+
158
+ Or via MCP tool at runtime:
159
+
160
+ ```
161
+ > Add a new Sentry instance called "work" with org "my-company"
162
+ ```
163
+
164
+ Use the `instance` parameter on any tool to target a specific instance, or omit it to use the default.
165
+
166
+ ## API Reference
167
+
168
+ ### Tools
169
+
170
+ | Tool | Description | Parameters |
171
+ |------|-------------|------------|
172
+ | `sentry_list_projects` | List all Sentry projects accessible by the token | `instance` |
173
+ | `sentry_get_issues` | Get unresolved issues for the configured project | `query`, `instance` |
174
+ | `sentry_get_issue` | Get full details for a specific issue | `issueId` (required), `instance` |
175
+ | `sentry_get_latest_event` | Get latest event with stack trace, breadcrumbs, HTTP request | `issueId` (required), `instance` |
176
+ | `sentry_resolve_issue` | Mark an issue as resolved | `issueId` (required), `instance` |
177
+ | `sentry_unresolve_issue` | Reopen a resolved issue | `issueId` (required), `instance` |
178
+ | `sentry_ignore_issue` | Ignore/snooze an issue | `issueId` (required), `instance` |
179
+ | `sentry_search_issues` | Search with custom Sentry query | `query` (required), `instance` |
180
+ | `sentry_assign_issue` | Assign an issue to a user or team | `issueId` (required), `assignee` (required), `instance` |
181
+ | `sentry_list_instances` | List all configured instances | none |
182
+ | `sentry_add_instance` | Add or update an instance at runtime | `name`, `token`, `baseUrl`, `orgSlug` (all required), `projectSlug`, `projects`, `setDefault` |
183
+ | `sentry_remove_instance` | Remove an instance | `name` (required) |
184
+
185
+ ### Configuration
186
+
187
+ Config stored at `~/.config/sentry-mcp/config.json`:
188
+
189
+ ```json
190
+ {
191
+ "instances": [
192
+ {
193
+ "name": "work",
194
+ "token": "YOUR_AUTH_TOKEN",
195
+ "baseUrl": "https://sentry.io",
196
+ "orgSlug": "my-company",
197
+ "projectSlug": "frontend",
198
+ "projects": ["frontend", "backend"]
199
+ }
200
+ ],
201
+ "defaultInstance": "work"
202
+ }
203
+ ```
204
+
205
+ ## Security
206
+
207
+ - API tokens are stored locally in `~/.config/sentry-mcp/config.json`
208
+ - Config files are excluded from git via `.gitignore`
209
+ - Tokens are never logged or transmitted except to the Sentry API
210
+
211
+ ## License
212
+
213
+ MIT
214
+
215
+ ## Related
216
+
217
+ - [jira-mcp](https://github.com/rui-branco/jira-mcp) - Jira MCP server for Claude Code
218
+ - [figma-mcp](https://github.com/rui-branco/figma-mcp) - Figma MCP server for Claude Code
219
+ - [Model Context Protocol](https://modelcontextprotocol.io/) - MCP specification
package/index.js CHANGED
@@ -156,7 +156,26 @@ async function fetchSentry(apiPath, instance, options = {}) {
156
156
 
157
157
  // ── Helpers ─────────────────────────────────────────────────────────────────
158
158
 
159
- function formatIssue(issue) {
159
+ // Strip noisy/internal fields that waste context but keep everything useful
160
+ function cleanIssueData(issue) {
161
+ const skip = new Set(["pluginActions", "pluginContexts", "pluginIssues", "activity", "seenBy", "stats"]);
162
+ const cleaned = {};
163
+ for (const [k, v] of Object.entries(issue)) {
164
+ if (!skip.has(k) && v != null && v !== "") cleaned[k] = v;
165
+ }
166
+ return cleaned;
167
+ }
168
+
169
+ function cleanEventData(event) {
170
+ const skip = new Set(["_meta", "groupID", "eventID", "sdk", "dist", "fingerprints"]);
171
+ const cleaned = {};
172
+ for (const [k, v] of Object.entries(event)) {
173
+ if (!skip.has(k) && v != null && v !== "") cleaned[k] = v;
174
+ }
175
+ return cleaned;
176
+ }
177
+
178
+ function formatIssueSummary(issue) {
160
179
  const meta = issue.metadata || {};
161
180
  const lines = [];
162
181
  lines.push(`**${issue.title}**`);
@@ -175,98 +194,6 @@ function formatIssue(issue) {
175
194
  return lines.join("\n");
176
195
  }
177
196
 
178
- function formatEvent(event) {
179
- const lines = [];
180
-
181
- // Stack traces
182
- const exceptions =
183
- event.entries?.find((e) => e.type === "exception")?.data?.values || [];
184
- if (exceptions.length) {
185
- lines.push("## Stack Trace\n");
186
- for (const ex of exceptions) {
187
- if (ex.type || ex.value) lines.push(`**${ex.type || "Exception"}:** ${ex.value || ""}\n`);
188
- const frames = ex.stacktrace?.frames;
189
- if (frames?.length) {
190
- lines.push("```");
191
- for (const f of frames) {
192
- const loc = f.lineNo ? `:${f.lineNo}${f.colNo ? ":" + f.colNo : ""}` : "";
193
- const marker = f.inApp ? "-> " : " ";
194
- lines.push(`${marker}${f.function || "?"} (${f.filename || "?"}${loc})`);
195
- }
196
- lines.push("```\n");
197
- }
198
- }
199
- }
200
-
201
- // Breadcrumbs
202
- const breadcrumbs =
203
- event.entries?.find((e) => e.type === "breadcrumbs")?.data?.values || [];
204
- if (breadcrumbs.length) {
205
- const last = breadcrumbs.slice(-20);
206
- lines.push(`## Breadcrumbs (last ${last.length})\n`);
207
- for (const bc of last) {
208
- const msg = bc.message || bc.data?.url || bc.data?.to || "";
209
- lines.push(`- **${bc.category || "?"}** [${bc.level || "info"}] ${msg}`);
210
- }
211
- lines.push("");
212
- }
213
-
214
- // HTTP Request
215
- const httpReq =
216
- event.entries?.find((e) => e.type === "request")?.data;
217
- if (httpReq) {
218
- lines.push("## HTTP Request\n");
219
- lines.push(`\`${httpReq.method || "GET"} ${httpReq.url || ""}\``);
220
- if (httpReq.headers) {
221
- lines.push("\n```");
222
- for (const [k, v] of httpReq.headers) {
223
- lines.push(`${k}: ${v}`);
224
- }
225
- lines.push("```");
226
- }
227
- lines.push("");
228
- }
229
-
230
- // Tags
231
- if (event.tags?.length) {
232
- lines.push("## Tags\n");
233
- lines.push("| Key | Value |");
234
- lines.push("|-----|-------|");
235
- for (const t of event.tags) {
236
- lines.push(`| ${t.key} | ${t.value} |`);
237
- }
238
- lines.push("");
239
- }
240
-
241
- // Contexts
242
- if (event.contexts) {
243
- lines.push("## Contexts\n");
244
- for (const [name, ctx] of Object.entries(event.contexts)) {
245
- if (name === "trace" || name === "replay" || !ctx || typeof ctx !== "object") continue;
246
- const entries = Object.entries(ctx).filter(([k, v]) => k !== "type" && v != null && v !== "");
247
- if (!entries.length) continue;
248
- const title = name.charAt(0).toUpperCase() + name.slice(1).replace(/_/g, " ");
249
- lines.push(`**${title}:**`);
250
- for (const [k, v] of entries.slice(0, 15)) {
251
- const display = typeof v === "object" ? JSON.stringify(v) : String(v);
252
- lines.push(`- ${k}: ${display}`);
253
- }
254
- lines.push("");
255
- }
256
- }
257
-
258
- // User
259
- if (event.user) {
260
- const u = event.user;
261
- lines.push("## User\n");
262
- lines.push(`${u.username || u.name || u.email || u.id || "unknown"}${u.email ? " (" + u.email + ")" : ""}`);
263
- if (u.ip_address) lines.push(`IP: ${u.ip_address}`);
264
- lines.push("");
265
- }
266
-
267
- return lines.join("\n") || "No event data available.";
268
- }
269
-
270
197
  // ── MCP Server ──────────────────────────────────────────────────────────────
271
198
 
272
199
  const server = new Server(
@@ -573,7 +500,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
573
500
  content: [{ type: "text", text: `No issues found for query: ${query}` }],
574
501
  };
575
502
  }
576
- const formatted = data.map(formatIssue);
503
+ const formatted = data.map(formatIssueSummary);
577
504
  return {
578
505
  content: [
579
506
  {
@@ -591,7 +518,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
591
518
  inst,
592
519
  );
593
520
  return {
594
- content: [{ type: "text", text: formatIssue(data) }],
521
+ content: [{ type: "text", text: JSON.stringify(cleanIssueData(data), null, 2) }],
595
522
  };
596
523
 
597
524
  } else if (name === "sentry_get_latest_event") {
@@ -602,7 +529,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
602
529
  inst,
603
530
  );
604
531
  return {
605
- content: [{ type: "text", text: formatEvent(data) }],
532
+ content: [{ type: "text", text: JSON.stringify(cleanEventData(data), null, 2) }],
606
533
  };
607
534
 
608
535
  } else if (name === "sentry_resolve_issue") {
@@ -659,7 +586,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
659
586
  content: [{ type: "text", text: `No issues found for query: ${args.query}` }],
660
587
  };
661
588
  }
662
- const formatted = data.map(formatIssue);
589
+ const formatted = data.map(formatIssueSummary);
663
590
  return {
664
591
  content: [
665
592
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rui.branco/sentry-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Sentry MCP server for Claude Code - fetch issues, search errors, resolve/ignore issues, get stack traces and breadcrumbs",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "index.js",
11
- "setup.js"
11
+ "setup.js",
12
+ "README.md"
12
13
  ],
13
14
  "scripts": {
14
15
  "setup": "node setup.js"