@rui.branco/sentry-mcp 1.0.2 → 1.0.4

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 +32 -8
  2. package/index.js +404 -1
  3. package/package.json +7 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Sentry MCP Server
2
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.
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that brings Sentry error tracking context directly into any MCP-compatible AI client — Claude Code, Codex CLI, Google's Antigravity (Gemini), Cursor, Windsurf, Zed, and others. Fetch issues, inspect stack traces, manage issue status, and search errors without leaving your development environment.
4
4
 
5
5
  ## Overview
6
6
 
@@ -8,7 +8,7 @@ When debugging production errors, context switching between Sentry and your code
8
8
 
9
9
  - **Fetching full issue details** - Get error metadata, event counts, assignment, and status instantly
10
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
11
+ - **Managing issues** - Resolve, reopen, ignore, and assign issues directly from your AI client
12
12
  - **Searching errors** - Use Sentry's search syntax to find issues across your project
13
13
 
14
14
  ## Features
@@ -28,15 +28,39 @@ When debugging production errors, context switching between Sentry and your code
28
28
  ### Prerequisites
29
29
 
30
30
  - Node.js 18+
31
- - [Claude Code](https://claude.ai/code) CLI
31
+ - An MCP-compatible AI client (see [Step 1](#step-1-register-with-your-mcp-client) below)
32
32
  - Sentry account with API access
33
33
 
34
- ### Step 1: Add to Claude Code
34
+ ### Step 1: Register with your MCP client
35
+
36
+ Pick the snippet for your client. All of them launch the server over stdio.
37
+
38
+ **Claude Code:**
35
39
 
36
40
  ```bash
37
41
  claude mcp add --transport stdio sentry -- npx -y @rui.branco/sentry-mcp
38
42
  ```
39
43
 
44
+ **Codex CLI:** add to `~/.codex/config.toml`:
45
+
46
+ ```toml
47
+ [mcp_servers.sentry]
48
+ command = "npx"
49
+ args = ["-y", "@rui.branco/sentry-mcp"]
50
+ ```
51
+
52
+ **Google Antigravity / Gemini CLI:** add to `~/.gemini/settings.json`:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "sentry": { "command": "npx", "args": ["-y", "@rui.branco/sentry-mcp"] }
58
+ }
59
+ }
60
+ ```
61
+
62
+ **Cursor, Windsurf, Zed, Cline, Continue, etc.:** add the same `command`/`args` pair to whatever JSON config that client uses for MCP servers (the exact path varies, but the shape is standard across clients).
63
+
40
64
  ### Step 2: Get Your Sentry API Token
41
65
 
42
66
  1. Go to [Sentry Auth Tokens](https://sentry.io/settings/account/api/auth-tokens/) (or your self-hosted Sentry equivalent)
@@ -68,7 +92,7 @@ npx @rui.branco/sentry-mcp setup
68
92
 
69
93
  ### Step 4: Verify
70
94
 
71
- Restart Claude Code and run `/mcp` to verify the server is connected.
95
+ Restart your AI client and check its MCP status (e.g. `/mcp` in Claude Code, `mcp` in Codex CLI, or the equivalent panel in your client) to verify the server is connected.
72
96
 
73
97
  ### Alternative: Manual Installation
74
98
 
@@ -80,7 +104,7 @@ cd ~/.config/sentry-mcp && npm install
80
104
  node setup.js
81
105
  ```
82
106
 
83
- Then add to Claude Code:
107
+ Then register the local entry point with your MCP client (replace the `npx -y @rui.branco/sentry-mcp` command from Step 1 with `node $HOME/.config/sentry-mcp/index.js`). Example for Claude Code:
84
108
 
85
109
  ```bash
86
110
  claude mcp add --transport stdio sentry -- node $HOME/.config/sentry-mcp/index.js
@@ -214,6 +238,6 @@ MIT
214
238
 
215
239
  ## Related
216
240
 
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
241
+ - [jira-mcp](https://github.com/rui-branco/jira-mcp) - Jira MCP server (works with any MCP-compatible AI client)
242
+ - [figma-mcp](https://github.com/rui-branco/figma-mcp) - Figma MCP server (works with any MCP-compatible AI client)
219
243
  - [Model Context Protocol](https://modelcontextprotocol.io/) - MCP specification
package/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // Handle setup command
4
4
  if (process.argv[2] === "setup") {
5
5
  require("./setup.js");
6
- return;
6
+ process.exit(0);
7
7
  }
8
8
 
9
9
  const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
@@ -383,6 +383,232 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
383
383
  required: ["issueId", "assignee"],
384
384
  },
385
385
  },
386
+ {
387
+ name: "sentry_list_releases",
388
+ description:
389
+ "List releases for a Sentry project. Returns release versions, dates, and artifact counts.",
390
+ inputSchema: {
391
+ type: "object",
392
+ properties: {
393
+ query: {
394
+ type: "string",
395
+ description:
396
+ "Optional search query to filter releases by version (e.g. '26.05').",
397
+ },
398
+ instance: {
399
+ type: "string",
400
+ description:
401
+ "Instance name (for multi-instance setups). Uses default instance if omitted.",
402
+ },
403
+ },
404
+ required: [],
405
+ },
406
+ },
407
+ {
408
+ name: "sentry_get_release_artifacts",
409
+ description:
410
+ "List artifacts (source maps, JS bundles) uploaded for a specific release. Use to verify source maps were uploaded correctly.",
411
+ inputSchema: {
412
+ type: "object",
413
+ properties: {
414
+ version: {
415
+ type: "string",
416
+ description: "Release version string (e.g. '26.05.4b185-TRAIN').",
417
+ },
418
+ instance: {
419
+ type: "string",
420
+ description:
421
+ "Instance name (for multi-instance setups). Uses default instance if omitted.",
422
+ },
423
+ },
424
+ required: ["version"],
425
+ },
426
+ },
427
+ {
428
+ name: "sentry_create_release",
429
+ description:
430
+ "Create a new release in Sentry. Optionally associate commits and set a ref.",
431
+ inputSchema: {
432
+ type: "object",
433
+ properties: {
434
+ version: {
435
+ type: "string",
436
+ description: "Release version string (e.g. '1.0.0', '26.05.4b185-TRAIN').",
437
+ },
438
+ ref: {
439
+ type: "string",
440
+ description: "Optional git ref (commit SHA or tag).",
441
+ },
442
+ projects: {
443
+ type: "array",
444
+ items: { type: "string" },
445
+ description: "Project slugs to associate with this release. Defaults to the instance's projectSlug.",
446
+ },
447
+ instance: {
448
+ type: "string",
449
+ description: "Instance name. Uses default if omitted.",
450
+ },
451
+ },
452
+ required: ["version"],
453
+ },
454
+ },
455
+ {
456
+ name: "sentry_finalize_release",
457
+ description:
458
+ "Finalize a release (mark it as deployed/complete). Sets dateReleased to now.",
459
+ inputSchema: {
460
+ type: "object",
461
+ properties: {
462
+ version: {
463
+ type: "string",
464
+ description: "Release version string.",
465
+ },
466
+ instance: {
467
+ type: "string",
468
+ description: "Instance name. Uses default if omitted.",
469
+ },
470
+ },
471
+ required: ["version"],
472
+ },
473
+ },
474
+ {
475
+ name: "sentry_delete_release",
476
+ description:
477
+ "Delete a release and all its artifacts from Sentry.",
478
+ inputSchema: {
479
+ type: "object",
480
+ properties: {
481
+ version: {
482
+ type: "string",
483
+ description: "Release version string to delete.",
484
+ },
485
+ instance: {
486
+ type: "string",
487
+ description: "Instance name. Uses default if omitted.",
488
+ },
489
+ },
490
+ required: ["version"],
491
+ },
492
+ },
493
+ {
494
+ name: "sentry_create_deploy",
495
+ description:
496
+ "Create a deploy for a release. Records when and where a release was deployed.",
497
+ inputSchema: {
498
+ type: "object",
499
+ properties: {
500
+ version: {
501
+ type: "string",
502
+ description: "Release version string.",
503
+ },
504
+ environment: {
505
+ type: "string",
506
+ description: "Environment name (e.g. 'production', 'staging', 'QA', 'TRAIN').",
507
+ },
508
+ name: {
509
+ type: "string",
510
+ description: "Optional deploy name.",
511
+ },
512
+ instance: {
513
+ type: "string",
514
+ description: "Instance name. Uses default if omitted.",
515
+ },
516
+ },
517
+ required: ["version", "environment"],
518
+ },
519
+ },
520
+ {
521
+ name: "sentry_list_deploys",
522
+ description:
523
+ "List deploys for a release.",
524
+ inputSchema: {
525
+ type: "object",
526
+ properties: {
527
+ version: {
528
+ type: "string",
529
+ description: "Release version string.",
530
+ },
531
+ instance: {
532
+ type: "string",
533
+ description: "Instance name. Uses default if omitted.",
534
+ },
535
+ },
536
+ required: ["version"],
537
+ },
538
+ },
539
+ {
540
+ name: "sentry_delete_artifact",
541
+ description:
542
+ "Delete a specific artifact from a release by its ID.",
543
+ inputSchema: {
544
+ type: "object",
545
+ properties: {
546
+ version: {
547
+ type: "string",
548
+ description: "Release version string.",
549
+ },
550
+ artifactId: {
551
+ type: "string",
552
+ description: "Artifact ID to delete (from sentry_get_release_artifacts).",
553
+ },
554
+ instance: {
555
+ type: "string",
556
+ description: "Instance name. Uses default if omitted.",
557
+ },
558
+ },
559
+ required: ["version", "artifactId"],
560
+ },
561
+ },
562
+ {
563
+ name: "sentry_set_commits",
564
+ description:
565
+ "Associate commits with a release. Provide a repository and commit range.",
566
+ inputSchema: {
567
+ type: "object",
568
+ properties: {
569
+ version: {
570
+ type: "string",
571
+ description: "Release version string.",
572
+ },
573
+ repository: {
574
+ type: "string",
575
+ description: "Repository name (e.g. 'org/repo').",
576
+ },
577
+ commit: {
578
+ type: "string",
579
+ description: "Current commit SHA (HEAD of the release).",
580
+ },
581
+ previousCommit: {
582
+ type: "string",
583
+ description: "Previous commit SHA (start of range). If omitted, uses the previous release's commit.",
584
+ },
585
+ instance: {
586
+ type: "string",
587
+ description: "Instance name. Uses default if omitted.",
588
+ },
589
+ },
590
+ required: ["version", "repository", "commit"],
591
+ },
592
+ },
593
+ {
594
+ name: "sentry_get_release",
595
+ description:
596
+ "Get full details for a specific release including projects, commits, authors, and deploy info.",
597
+ inputSchema: {
598
+ type: "object",
599
+ properties: {
600
+ version: {
601
+ type: "string",
602
+ description: "Release version string.",
603
+ },
604
+ instance: {
605
+ type: "string",
606
+ description: "Instance name. Uses default if omitted.",
607
+ },
608
+ },
609
+ required: ["version"],
610
+ },
611
+ },
386
612
  {
387
613
  name: "sentry_list_instances",
388
614
  description:
@@ -610,6 +836,183 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
610
836
  ],
611
837
  };
612
838
 
839
+ } else if (name === "sentry_list_releases") {
840
+ const inst = getInstanceByName(args.instance);
841
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
842
+ if (!inst.projectSlug) throw new Error("No projectSlug configured for this instance.");
843
+ const params = new URLSearchParams();
844
+ if (args.query) params.set("query", args.query);
845
+ const data = await fetchSentry(
846
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/?project=${encodeURIComponent(inst.projectSlug)}&${params}`,
847
+ inst,
848
+ );
849
+ if (!data || !data.length) {
850
+ return {
851
+ content: [{ type: "text", text: `No releases found.${args.query ? ` Query: ${args.query}` : ""}` }],
852
+ };
853
+ }
854
+ const lines = data.slice(0, 25).map((r) => {
855
+ const artifacts = r.newGroups != null ? ` | New issues: ${r.newGroups}` : "";
856
+ return `- **${r.version}** | Created: ${r.dateCreated || "N/A"}${artifacts}${r.lastEvent ? ` | Last event: ${r.lastEvent}` : ""}`;
857
+ });
858
+ return {
859
+ content: [
860
+ {
861
+ type: "text",
862
+ text: `Found ${data.length} release(s):\n\n${lines.join("\n")}`,
863
+ },
864
+ ],
865
+ };
866
+
867
+ } else if (name === "sentry_get_release_artifacts") {
868
+ const inst = getInstanceByName(args.instance);
869
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
870
+ const data = await fetchSentry(
871
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/files/`,
872
+ inst,
873
+ );
874
+ if (!data || !data.length) {
875
+ return {
876
+ content: [{ type: "text", text: `No artifacts found for release "${args.version}". Source maps may not have been uploaded.` }],
877
+ };
878
+ }
879
+ const lines = data.map((a) => {
880
+ const size = a.size ? ` (${(a.size / 1024).toFixed(1)} KB)` : "";
881
+ return `- ${a.name}${size} [id: ${a.id}]`;
882
+ });
883
+ return {
884
+ content: [
885
+ {
886
+ type: "text",
887
+ text: `Found ${data.length} artifact(s) for release "${args.version}":\n\n${lines.join("\n")}`,
888
+ },
889
+ ],
890
+ };
891
+
892
+ } else if (name === "sentry_create_release") {
893
+ const inst = getInstanceByName(args.instance);
894
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
895
+ const projects = args.projects || (inst.projectSlug ? [inst.projectSlug] : []);
896
+ if (!projects.length) throw new Error("No projects specified and no projectSlug configured.");
897
+ const body = { version: args.version, projects };
898
+ if (args.ref) body.ref = args.ref;
899
+ const data = await fetchSentry(
900
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/`,
901
+ inst,
902
+ { method: "POST", body },
903
+ );
904
+ return {
905
+ content: [{ type: "text", text: `Release "${data.version}" created for projects: ${projects.join(", ")}` }],
906
+ };
907
+
908
+ } else if (name === "sentry_finalize_release") {
909
+ const inst = getInstanceByName(args.instance);
910
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
911
+ await fetchSentry(
912
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/`,
913
+ inst,
914
+ { method: "PUT", body: { dateReleased: new Date().toISOString() } },
915
+ );
916
+ return {
917
+ content: [{ type: "text", text: `Release "${args.version}" finalized.` }],
918
+ };
919
+
920
+ } else if (name === "sentry_delete_release") {
921
+ const inst = getInstanceByName(args.instance);
922
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
923
+ await fetchSentry(
924
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/`,
925
+ inst,
926
+ { method: "DELETE" },
927
+ );
928
+ return {
929
+ content: [{ type: "text", text: `Release "${args.version}" deleted.` }],
930
+ };
931
+
932
+ } else if (name === "sentry_create_deploy") {
933
+ const inst = getInstanceByName(args.instance);
934
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
935
+ const body = { environment: args.environment };
936
+ if (args.name) body.name = args.name;
937
+ const data = await fetchSentry(
938
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/deploys/`,
939
+ inst,
940
+ { method: "POST", body },
941
+ );
942
+ return {
943
+ content: [{ type: "text", text: `Deploy created for release "${args.version}" to environment "${args.environment}".` }],
944
+ };
945
+
946
+ } else if (name === "sentry_list_deploys") {
947
+ const inst = getInstanceByName(args.instance);
948
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
949
+ const data = await fetchSentry(
950
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/deploys/`,
951
+ inst,
952
+ );
953
+ if (!data || !data.length) {
954
+ return {
955
+ content: [{ type: "text", text: `No deploys found for release "${args.version}".` }],
956
+ };
957
+ }
958
+ const lines = data.map((d) => `- **${d.environment}** | ${d.dateFinished || d.dateStarted || "N/A"}${d.name ? ` | ${d.name}` : ""}`);
959
+ return {
960
+ content: [{ type: "text", text: `${data.length} deploy(s) for "${args.version}":\n\n${lines.join("\n")}` }],
961
+ };
962
+
963
+ } else if (name === "sentry_delete_artifact") {
964
+ const inst = getInstanceByName(args.instance);
965
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
966
+ await fetchSentry(
967
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/files/${encodeURIComponent(args.artifactId)}/`,
968
+ inst,
969
+ { method: "DELETE" },
970
+ );
971
+ return {
972
+ content: [{ type: "text", text: `Artifact ${args.artifactId} deleted from release "${args.version}".` }],
973
+ };
974
+
975
+ } else if (name === "sentry_set_commits") {
976
+ const inst = getInstanceByName(args.instance);
977
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
978
+ const commitSpec = { repository: args.repository, currentId: args.commit };
979
+ if (args.previousCommit) commitSpec.previousId = args.previousCommit;
980
+ await fetchSentry(
981
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/commits/`,
982
+ inst,
983
+ { method: "PUT", body: { commits: [commitSpec] } },
984
+ );
985
+ return {
986
+ content: [{ type: "text", text: `Commits associated with release "${args.version}" (repo: ${args.repository}, HEAD: ${args.commit}).` }],
987
+ };
988
+
989
+ } else if (name === "sentry_get_release") {
990
+ const inst = getInstanceByName(args.instance);
991
+ if (!inst) throw new Error("No Sentry instance configured. Run: sentry-mcp setup");
992
+ const data = await fetchSentry(
993
+ `/api/0/organizations/${encodeURIComponent(inst.orgSlug)}/releases/${encodeURIComponent(args.version)}/`,
994
+ inst,
995
+ );
996
+ const lines = [];
997
+ lines.push(`**${data.version}**`);
998
+ lines.push(`- Status: ${data.status || "open"}`);
999
+ lines.push(`- Created: ${data.dateCreated || "N/A"}`);
1000
+ if (data.dateReleased) lines.push(`- Released: ${data.dateReleased}`);
1001
+ if (data.firstEvent) lines.push(`- First event: ${data.firstEvent}`);
1002
+ if (data.lastEvent) lines.push(`- Last event: ${data.lastEvent}`);
1003
+ lines.push(`- New issues: ${data.newGroups || 0}`);
1004
+ lines.push(`- Commits: ${data.commitCount || 0}`);
1005
+ lines.push(`- Deploys: ${data.deployCount || 0}`);
1006
+ if (data.projects?.length) {
1007
+ lines.push(`- Projects: ${data.projects.map((p) => p.slug || p.name).join(", ")}`);
1008
+ }
1009
+ if (data.authors?.length) {
1010
+ lines.push(`- Authors: ${data.authors.map((a) => a.name || a.email).join(", ")}`);
1011
+ }
1012
+ return {
1013
+ content: [{ type: "text", text: lines.join("\n") }],
1014
+ };
1015
+
613
1016
  } else if (name === "sentry_list_instances") {
614
1017
  if (!instances.length) {
615
1018
  return {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rui.branco/sentry-mcp",
3
- "version": "1.0.2",
4
- "description": "Sentry MCP server for Claude Code - fetch issues, search errors, resolve/ignore issues, get stack traces and breadcrumbs",
3
+ "version": "1.0.4",
4
+ "description": "Sentry MCP server for any MCP-compatible AI client (Claude Code, Codex, Antigravity, Cursor, etc.) - fetch issues, search errors, resolve/ignore issues, get stack traces and breadcrumbs",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "sentry-mcp": "./index.js"
@@ -12,7 +12,8 @@
12
12
  "README.md"
13
13
  ],
14
14
  "scripts": {
15
- "setup": "node setup.js"
15
+ "setup": "node setup.js",
16
+ "test": "vitest run"
16
17
  },
17
18
  "keywords": [
18
19
  "mcp",
@@ -27,5 +28,8 @@
27
28
  "dependencies": {
28
29
  "@modelcontextprotocol/sdk": "^1.25.3",
29
30
  "node-fetch": "^2.7.0"
31
+ },
32
+ "devDependencies": {
33
+ "vitest": "^4.1.6"
30
34
  }
31
35
  }