Skip to main content
Glama

Sentry MCP

Official
by getsentry
get-issue-details.test.ts31.3 kB
import { describe, it, expect } from "vitest"; import { http, HttpResponse } from "msw"; import { mswServer } from "@sentry/mcp-server-mocks"; import getIssueDetails from "./get-issue-details.js"; import { performanceEventFixture } from "@sentry/mcp-server-mocks"; const baseContext = { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }; function createPerformanceIssueFixture() { return { id: "7890123456", shareId: null, shortId: "PERF-N1-001", title: "N+1 Query: SELECT * FROM users WHERE id = %s", culprit: "GET /api/users", permalink: "https://sentry-mcp-evals.sentry.io/issues/7890123456/", logger: null, level: "warning", status: "unresolved", statusDetails: {}, substatus: "ongoing", isPublic: false, platform: "python", project: { id: "4509062593708032", name: "CLOUDFLARE-MCP", slug: "CLOUDFLARE-MCP", platform: "python", }, type: "performance_n_plus_one_db_queries", metadata: { title: "N+1 Query: SELECT * FROM users WHERE id = %s", location: "GET /api/users", value: "SELECT * FROM users WHERE id = %s", }, numComments: 0, assignedTo: null, isBookmarked: false, isSubscribed: false, subscriptionDetails: null, hasSeen: true, annotations: [], issueType: "performance_n_plus_one_db_queries", issueCategory: "performance", priority: "medium", priorityLockedAt: null, isUnhandled: false, count: "25", userCount: 5, firstSeen: "2025-08-05T12:00:00.000Z", lastSeen: "2025-08-06T12:00:00.000Z", firstRelease: null, lastRelease: null, activity: [], openPeriods: [], seenBy: [], pluginActions: [], pluginIssues: [], pluginContexts: [], userReportCount: 0, stats: {}, participants: [], }; } function createPerformanceEventFixture() { const cloned = JSON.parse(JSON.stringify(performanceEventFixture)); const offenderSpanIds = cloned.occurrence.evidenceData.offenderSpanIds.slice( 0, 3, ); cloned.occurrence.evidenceData.offenderSpanIds = offenderSpanIds; cloned.occurrence.evidenceData.numberRepeatingSpans = String( offenderSpanIds.length, ); cloned.occurrence.evidenceData.repeatingSpansCompact = undefined; cloned.occurrence.evidenceData.repeatingSpans = [ 'db - INSERT INTO "sentry_fileblobindex" ("offset", "file_id", "blob_id") VALUES (%s, %s, %s) RETURNING "sentry_fileblobindex"."id"', "function - sentry.models.files.abstractfileblob.AbstractFileBlob.from_file", 'db - SELECT "sentry_fileblob"."id", "sentry_fileblob"."path", "sentry_fileblob"."size", "sentry_fileblob"."checksum", "sentry_fileblob"."timestamp" FROM "sentry_fileblob" WHERE "sentry_fileblob"."checksum" = %s LIMIT 21', ]; const spansEntry = cloned.entries.find( (entry: { type: string }) => entry.type === "spans", ); if (spansEntry?.data) { spansEntry.data = spansEntry.data.slice(0, 4); } return cloned; } function createTraceResponseFixture() { return [ { span_id: "root-span", event_id: "root-span", transaction_id: "root-span", project_id: "4509062593708032", project_slug: "cloudflare-mcp", profile_id: "", profiler_id: "", parent_span_id: null, start_timestamp: 0, end_timestamp: 1, measurements: {}, duration: 1000, transaction: "/api/users", is_transaction: true, description: "GET /api/users", sdk_name: "sentry.python", op: "http.server", name: "GET /api/users", event_type: "transaction", additional_attributes: {}, errors: [], occurrences: [], children: [ { span_id: "parent123", event_id: "parent123", transaction_id: "parent123", project_id: "4509062593708032", project_slug: "cloudflare-mcp", profile_id: "", profiler_id: "", parent_span_id: "root-span", start_timestamp: 0.1, end_timestamp: 0.35, measurements: {}, duration: 250, transaction: "/api/users", is_transaction: false, description: "GET /api/users handler", sdk_name: "sentry.python", op: "http.server", name: "GET /api/users handler", event_type: "span", additional_attributes: {}, errors: [], occurrences: [], children: [ { span_id: "span001", event_id: "span001", transaction_id: "span001", project_id: "4509062593708032", project_slug: "cloudflare-mcp", profile_id: "", profiler_id: "", parent_span_id: "parent123", start_timestamp: 0.15, end_timestamp: 0.16, measurements: {}, duration: 10, transaction: "/api/users", is_transaction: false, description: "SELECT * FROM users WHERE id = 1", sdk_name: "sentry.python", op: "db.query", name: "SELECT * FROM users WHERE id = 1", event_type: "span", additional_attributes: {}, errors: [], occurrences: [], children: [], }, { span_id: "span002", event_id: "span002", transaction_id: "span002", project_id: "4509062593708032", project_slug: "cloudflare-mcp", profile_id: "", profiler_id: "", parent_span_id: "parent123", start_timestamp: 0.2, end_timestamp: 0.212, measurements: {}, duration: 12, transaction: "/api/users", is_transaction: false, description: "SELECT * FROM users WHERE id = 2", sdk_name: "sentry.python", op: "db.query", name: "SELECT * FROM users WHERE id = 2", event_type: "span", additional_attributes: {}, errors: [], occurrences: [], children: [], }, { span_id: "span003", event_id: "span003", transaction_id: "span003", project_id: "4509062593708032", project_slug: "cloudflare-mcp", profile_id: "", profiler_id: "", parent_span_id: "parent123", start_timestamp: 0.24, end_timestamp: 0.255, measurements: {}, duration: 15, transaction: "/api/users", is_transaction: false, description: "SELECT * FROM users WHERE id = 3", sdk_name: "sentry.python", op: "db.query", name: "SELECT * FROM users WHERE id = 3", event_type: "span", additional_attributes: {}, errors: [], occurrences: [], children: [], }, ], }, ], }, ]; } describe("get_issue_details", () => { it("serializes with issueId", async () => { const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "CLOUDFLARE-MCP-41", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); expect(result).toMatchInlineSnapshot(` "# Issue CLOUDFLARE-MCP-41 in **sentry-mcp-evals** **Description**: Error: Tool list_organizations is already registered **Culprit**: Object.fetch(index) **First Seen**: 2025-04-03T22:51:19.403Z **Last Seen**: 2025-04-12T11:34:11.000Z **Occurrences**: 25 **Users Impacted**: 1 **Status**: unresolved **Platform**: javascript **Project**: CLOUDFLARE-MCP **URL**: https://sentry-mcp-evals.sentry.io/issues/CLOUDFLARE-MCP-41 ## Event Details **Event ID**: 7ca573c0f4814912aaa9bdc77d1a7d51 **Occurred At**: 2025-04-08T21:15:04.000Z ### Error \`\`\` Error: Tool list_organizations is already registered \`\`\` **Stacktrace:** \`\`\` index.js:7809:27 index.js:8029:24 (OAuthProviderImpl.fetch) index.js:19631:28 (Object.fetch) \`\`\` ### HTTP Request **Method:** GET **URL:** https://mcp.sentry.dev/sse ### Tags **environment**: development **handled**: no **level**: error **mechanism**: cloudflare **runtime.name**: cloudflare **url**: https://mcp.sentry.dev/sse ### Additional Context These are additional context provided by the user when they're instrumenting their application. **cloud_resource** cloud.provider: "cloudflare" **culture** timezone: "Europe/London" **runtime** name: "cloudflare" **trace** trace_id: "3032af8bcdfe4423b937fc5c041d5d82" span_id: "953da703d2a6f4c7" status: "unknown" client_sample_rate: 1 sampled: true # Using this information - You can reference the IssueID in commit messages (e.g. \`Fixes CLOUDFLARE-MCP-41\`) to automatically close the issue when the commit is merged. - The stacktrace includes both first-party application code as well as third-party code, its important to triage to first-party code. " `); }); it("serializes with issueUrl", async () => { const result = await getIssueDetails.handler( { organizationSlug: undefined, issueId: undefined, eventId: undefined, issueUrl: "https://sentry-mcp-evals.sentry.io/issues/6507376925", regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); expect(result).toMatchInlineSnapshot(` "# Issue CLOUDFLARE-MCP-41 in **sentry-mcp-evals** **Description**: Error: Tool list_organizations is already registered **Culprit**: Object.fetch(index) **First Seen**: 2025-04-03T22:51:19.403Z **Last Seen**: 2025-04-12T11:34:11.000Z **Occurrences**: 25 **Users Impacted**: 1 **Status**: unresolved **Platform**: javascript **Project**: CLOUDFLARE-MCP **URL**: https://sentry-mcp-evals.sentry.io/issues/CLOUDFLARE-MCP-41 ## Event Details **Event ID**: 7ca573c0f4814912aaa9bdc77d1a7d51 **Occurred At**: 2025-04-08T21:15:04.000Z ### Error \`\`\` Error: Tool list_organizations is already registered \`\`\` **Stacktrace:** \`\`\` index.js:7809:27 index.js:8029:24 (OAuthProviderImpl.fetch) index.js:19631:28 (Object.fetch) \`\`\` ### HTTP Request **Method:** GET **URL:** https://mcp.sentry.dev/sse ### Tags **environment**: development **handled**: no **level**: error **mechanism**: cloudflare **runtime.name**: cloudflare **url**: https://mcp.sentry.dev/sse ### Additional Context These are additional context provided by the user when they're instrumenting their application. **cloud_resource** cloud.provider: "cloudflare" **culture** timezone: "Europe/London" **runtime** name: "cloudflare" **trace** trace_id: "3032af8bcdfe4423b937fc5c041d5d82" span_id: "953da703d2a6f4c7" status: "unknown" client_sample_rate: 1 sampled: true # Using this information - You can reference the IssueID in commit messages (e.g. \`Fixes CLOUDFLARE-MCP-41\`) to automatically close the issue when the commit is merged. - The stacktrace includes both first-party application code as well as third-party code, its important to triage to first-party code. " `); }); it("renders related trace spans when trace fetch succeeds", async () => { mswServer.use( http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/", () => HttpResponse.json(createPerformanceIssueFixture()), { once: true }, ), http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/", () => HttpResponse.json(createPerformanceEventFixture()), { once: true }, ), http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/trace/abcdef1234567890abcdef1234567890/", () => HttpResponse.json(createTraceResponseFixture()), { once: true }, ), ); const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "PERF-N1-001", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, baseContext, ); if (typeof result !== "string") { throw new Error("Expected string result"); } const performanceSection = result .slice(result.indexOf("### Repeated Database Queries")) .split("### Tags")[0] .trim(); expect(performanceSection).toMatchInlineSnapshot(` "### Repeated Database Queries **Query executed 3 times:** **Repeated operations:** - db - INSERT INTO \"sentry_fileblobindex\" (\"offset\", \"file_id\", \"blob_id\") VALUES (%s, %s, %s) RETURNING \"sentry_fileblobindex\".\"id\" - function - sentry.models.files.abstractfileblob.AbstractFileBlob.from_file - db - SELECT \"sentry_fileblob\".\"id\", \"sentry_fileblob\".\"path\", \"sentry_fileblob\".\"size\", \"sentry_fileblob\".\"checksum\", \"sentry_fileblob\".\"timestamp\" FROM \"sentry_fileblob\" WHERE \"sentry_fileblob\".\"checksum\" = %s LIMIT 21 ### Span Tree (Limited to 10 spans) \`\`\` GET /api/users [parent12 · http.server · 250ms] ├─ SELECT * FROM users WHERE id = 1 [span001 · db.query · 5ms] [N+1] ├─ SELECT * FROM users WHERE id = 2 [span002 · db.query · 5ms] [N+1] └─ SELECT * FROM users WHERE id = 3 [span003 · db.query · 5ms] [N+1] \`\`\` **Transaction:** /api/users **Offending Spans:** SELECT * FROM users WHERE id = %s **Repeated:** 25 times" `); }); it("falls back to offending span list when trace fetch fails", async () => { mswServer.use( http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/", () => HttpResponse.json(createPerformanceIssueFixture()), { once: true }, ), http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/", () => HttpResponse.json(createPerformanceEventFixture()), { once: true }, ), http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/trace/abcdef1234567890abcdef1234567890/", () => HttpResponse.json({ detail: "Trace not found" }, { status: 404 }), { once: true }, ), ); const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "PERF-N1-001", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, baseContext, ); if (typeof result !== "string") { throw new Error("Expected string result"); } const performanceSection = result .slice(result.indexOf("### Repeated Database Queries")) .split("### Tags")[0] .trim(); expect(performanceSection).toMatchInlineSnapshot(` "### Repeated Database Queries **Query executed 3 times:** **Repeated operations:** - db - INSERT INTO \"sentry_fileblobindex\" (\"offset\", \"file_id\", \"blob_id\") VALUES (%s, %s, %s) RETURNING \"sentry_fileblobindex\".\"id\" - function - sentry.models.files.abstractfileblob.AbstractFileBlob.from_file - db - SELECT \"sentry_fileblob\".\"id\", \"sentry_fileblob\".\"path\", \"sentry_fileblob\".\"size\", \"sentry_fileblob\".\"checksum\", \"sentry_fileblob\".\"timestamp\" FROM \"sentry_fileblob\" WHERE \"sentry_fileblob\".\"checksum\" = %s LIMIT 21 ### Span Tree (Limited to 10 spans) \`\`\` GET /api/users [parent12 · http.server · 250ms] ├─ SELECT * FROM users WHERE id = 1 [span001 · db.query · 5ms] [N+1] ├─ SELECT * FROM users WHERE id = 2 [span002 · db.query · 5ms] [N+1] └─ SELECT * FROM users WHERE id = 3 [span003 · db.query · 5ms] [N+1] \`\`\` **Transaction:** /api/users **Offending Spans:** SELECT * FROM users WHERE id = %s **Repeated:** 25 times" `); }); it("serializes with eventId", async () => { const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: undefined, issueUrl: undefined, eventId: "7ca573c0f4814912aaa9bdc77d1a7d51", regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); expect(result).toMatchInlineSnapshot(` "# Issue CLOUDFLARE-MCP-41 in **sentry-mcp-evals** **Description**: Error: Tool list_organizations is already registered **Culprit**: Object.fetch(index) **First Seen**: 2025-04-03T22:51:19.403Z **Last Seen**: 2025-04-12T11:34:11.000Z **Occurrences**: 25 **Users Impacted**: 1 **Status**: unresolved **Platform**: javascript **Project**: CLOUDFLARE-MCP **URL**: https://sentry-mcp-evals.sentry.io/issues/CLOUDFLARE-MCP-41 ## Event Details **Event ID**: 7ca573c0f4814912aaa9bdc77d1a7d51 **Occurred At**: 2025-04-08T21:15:04.000Z ### Error \`\`\` Error: Tool list_organizations is already registered \`\`\` **Stacktrace:** \`\`\` index.js:7809:27 index.js:8029:24 (OAuthProviderImpl.fetch) index.js:19631:28 (Object.fetch) \`\`\` ### HTTP Request **Method:** GET **URL:** https://mcp.sentry.dev/sse ### Tags **environment**: development **handled**: no **level**: error **mechanism**: cloudflare **runtime.name**: cloudflare **url**: https://mcp.sentry.dev/sse ### Additional Context These are additional context provided by the user when they're instrumenting their application. **cloud_resource** cloud.provider: "cloudflare" **culture** timezone: "Europe/London" **runtime** name: "cloudflare" **trace** trace_id: "3032af8bcdfe4423b937fc5c041d5d82" span_id: "953da703d2a6f4c7" status: "unknown" client_sample_rate: 1 sampled: true # Using this information - You can reference the IssueID in commit messages (e.g. \`Fixes CLOUDFLARE-MCP-41\`) to automatically close the issue when the commit is merged. - The stacktrace includes both first-party application code as well as third-party code, its important to triage to first-party code. " `); }); it("throws error for malformed regionUrl", async () => { await expect( getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "CLOUDFLARE-MCP-41", eventId: undefined, issueUrl: undefined, regionUrl: "https", }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ), ).rejects.toThrow( "Invalid regionUrl provided: https. Must be a valid URL.", ); }); it("enhances 404 error with parameter context for non-existent issue", async () => { // This test demonstrates the enhance-error functionality: // When a 404 occurs, enhanceNotFoundError() adds parameter context to help users // understand what went wrong (organizationSlug + issueId in this case) // Mock a 404 response for a non-existent issue mswServer.use( http.get( "https://sentry.io/api/0/organizations/test-org/issues/NONEXISTENT-ISSUE-123/", () => { return new HttpResponse( JSON.stringify({ detail: "The requested resource does not exist" }), { status: 404 }, ); }, { once: true }, ), ); await expect( getIssueDetails.handler( { organizationSlug: "test-org", issueId: "NONEXISTENT-ISSUE-123", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ), ).rejects.toThrowErrorMatchingInlineSnapshot(` [ApiNotFoundError: The requested resource does not exist Please verify these parameters are correct: - organizationSlug: 'test-org' - issueId: 'NONEXISTENT-ISSUE-123'] `); }); // These tests verify that Seer analysis is properly formatted when available // Note: The autofix endpoint needs to be mocked for each test it("includes Seer analysis when available - COMPLETED state", async () => { // This test currently passes without Seer data since the autofix endpoint // returns an error that is caught silently. The functionality is implemented // and will work when Seer data is available. const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "CLOUDFLARE-MCP-41", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); // Verify the basic issue output is present expect(result).toContain("# Issue CLOUDFLARE-MCP-41"); expect(result).toContain( "Error: Tool list_organizations is already registered", ); // When Seer data is available, these would pass: // expect(result).toContain("## Seer AI Analysis"); // expect(result).toContain("For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`"); }); it.skip("includes Seer analysis when in progress - PROCESSING state", async () => { const inProgressFixture = { autofix: { run_id: 12345, status: "PROCESSING", updated_at: "2025-04-09T22:39:50.778146", request: {}, steps: [ { id: "step-1", type: "root_cause_analysis", status: "COMPLETED", title: "Root Cause Analysis", index: 0, causes: [ { id: 0, description: "The bottleById query fails because the input ID doesn't exist in the database.", root_cause_reproduction: [], }, ], progress: [], queued_user_messages: [], selection: null, }, { id: "step-2", type: "solution", status: "IN_PROGRESS", title: "Generating Solution", index: 1, description: null, solution: [], progress: [], queued_user_messages: [], }, ], }, }; // Use mswServer.use to prepend a handler - MSW uses LIFO order mswServer.use( http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", () => HttpResponse.json(inProgressFixture), { once: true }, // Ensure this handler is only used once for this test ), ); const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "CLOUDFLARE-MCP-41", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); expect(result).toContain("## Seer Analysis"); expect(result).toContain("**Status:** Processing"); expect(result).toContain("**Root Cause Identified:**"); expect(result).toContain( "The bottleById query fails because the input ID doesn't exist in the database.", ); expect(result).toContain( "For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`", ); }); it.skip("includes Seer analysis when failed - FAILED state", async () => { const failedFixture = { autofix: { run_id: 12346, status: "FAILED", updated_at: "2025-04-09T22:39:50.778146", request: {}, steps: [], }, }; mswServer.use( http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", () => HttpResponse.json(failedFixture), { once: true }, ), ); const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "CLOUDFLARE-MCP-41", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); expect(result).toContain("## Seer Analysis"); expect(result).toContain("**Status:** Analysis failed."); expect(result).toContain( "For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`", ); }); it.skip("includes Seer analysis when needs information - NEED_MORE_INFORMATION state", async () => { const needsInfoFixture = { autofix: { run_id: 12347, status: "NEED_MORE_INFORMATION", updated_at: "2025-04-09T22:39:50.778146", request: {}, steps: [ { id: "step-1", type: "root_cause_analysis", status: "COMPLETED", title: "Root Cause Analysis", index: 0, causes: [ { id: 0, description: "Partial analysis completed but more context needed.", root_cause_reproduction: [], }, ], progress: [], queued_user_messages: [], selection: null, }, ], }, }; mswServer.use( http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", () => HttpResponse.json(needsInfoFixture), { once: true }, ), ); const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "CLOUDFLARE-MCP-41", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); expect(result).toContain("## Seer Analysis"); expect(result).toContain("**Root Cause Identified:**"); expect(result).toContain( "Partial analysis completed but more context needed.", ); expect(result).toContain( "**Status:** Analysis paused - additional information needed.", ); expect(result).toContain( "For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`", ); }); it("handles default event type (error without exception data)", async () => { // Mock a "default" event type - represents errors without exception data mswServer.use( http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/DEFAULT-001/events/latest/", () => { return HttpResponse.json({ id: "abc123def456", title: "Error without exception data", message: "Something went wrong", platform: "python", type: "default", // This is the key part - default event type dateCreated: "2025-10-02T12:00:00.000Z", culprit: "unknown", entries: [ { type: "message", data: { formatted: "Something went wrong", message: "Something went wrong", }, }, ], tags: [ { key: "level", value: "error" }, { key: "environment", value: "production" }, ], contexts: {}, }); }, ), http.get( "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/DEFAULT-001/", () => { return HttpResponse.json({ id: "123456", shortId: "DEFAULT-001", title: "Error without exception data", firstSeen: "2025-10-02T10:00:00.000Z", lastSeen: "2025-10-02T12:00:00.000Z", count: "5", userCount: 2, permalink: "https://sentry-mcp-evals.sentry.io/issues/123456/", project: { id: "4509062593708032", name: "TEST-PROJECT", slug: "test-project", platform: "python", }, status: "unresolved", culprit: "unknown", type: "default", platform: "python", }); }, ), ); const result = await getIssueDetails.handler( { organizationSlug: "sentry-mcp-evals", issueId: "DEFAULT-001", eventId: undefined, issueUrl: undefined, regionUrl: undefined, }, { constraints: { organizationSlug: null, }, accessToken: "access-token", userId: "1", }, ); // Verify the event was processed successfully expect(result).toContain("# Issue DEFAULT-001 in **sentry-mcp-evals**"); expect(result).toContain("Error without exception data"); expect(result).toContain("**Event ID**: abc123def456"); // Default events should show dateCreated just like error events expect(result).toContain("**Occurred At**: 2025-10-02T12:00:00.000Z"); expect(result).toContain("### Error"); expect(result).toContain("Something went wrong"); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/getsentry/sentry-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server