Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/mcp-core/src/api-client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3259,7 +3259,7 @@ export class SentryApiService {
opts?: RequestOptions,
): Promise<Event> {
const body = await this.requestJSON(
`/organizations/${organizationSlug}/issues/${issueId}/events/${eventId}/`,
`/organizations/${encodeURIComponent(organizationSlug)}/issues/${encodeURIComponent(issueId)}/events/${encodeURIComponent(eventId)}/`,
undefined,
opts,
);
Expand Down
9 changes: 6 additions & 3 deletions packages/mcp-core/src/internal/tool-helpers/breadcrumbs.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import type { SentryApiService } from "../../api-client/index";

/**
* Fetches breadcrumbs from the latest event for an issue and formats them.
* Fetches breadcrumbs from an issue event and formats them.
* Defaults to the latest event when eventId is omitted.
*/
export async function fetchAndFormatBreadcrumbs(
apiService: SentryApiService,
organizationSlug: string,
issueId: string,
eventId: string = "latest",
): Promise<string> {
const event = await apiService.getLatestEventForIssue({
const event = await apiService.getEventForIssue({
organizationSlug,
issueId,
eventId,
Comment thread
sentry-warden[bot] marked this conversation as resolved.
});

const breadcrumbEntry = event.entries.find((e) => e.type === "breadcrumbs");
Expand Down Expand Up @@ -40,7 +43,7 @@ export function formatBreadcrumbs(
"",
`**Event ID**: ${eventId}`,
"",
"No breadcrumbs found in the latest event for this issue.",
"No breadcrumbs found in this event for the issue.",
].join("\n");
}

Expand Down
18 changes: 17 additions & 1 deletion packages/mcp-core/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,23 @@ export const ParamSentryGuide = z
"Use either a platform (e.g., 'javascript', 'python') or platform/guide combination (e.g., 'javascript/nextjs', 'python/django').",
);

export const ParamEventId = z.string().trim().describe("The ID of the event.");
export const ParamEventId = z
.string()
.trim()
.regex(
/^[0-9a-fA-F]{32}$/,
"Event ID must be a 32-character hexadecimal string",
)
.describe("The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`");

/**
* Issue-scoped event selector used by tools that can target either a concrete
* event or the issue's latest event.
*/
export const ParamEventIdOrLatest = z
.union([z.literal("latest"), ParamEventId])
.default("latest")
.describe("The event ID for the issue, or `latest`. Defaults to `latest`.");

export const ParamAttachmentId = z
.string()
Expand Down
10 changes: 5 additions & 5 deletions packages/mcp-core/src/skillDefinitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
},
{
"name": "get_event_stacktrace",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='abc123', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"requiredScopes": ["event:read"]
},
{
Expand All @@ -79,7 +79,7 @@
},
{
"name": "get_issue_breadcrumbs",
"description": "Get the breadcrumb trail from the latest event for a Sentry issue.\n\nUse this tool when you need to:\n- See the user and application actions leading up to an error\n- Inspect navigation, console, HTTP, and other breadcrumb events\n- Reconstruct the immediate context before an issue occurred\n\n<examples>\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123')\nget_issue_breadcrumbs(issueUrl='https://my-org.sentry.io/issues/PROJECT-123/')\n</examples>",
"description": "Get the breadcrumb trail from a Sentry issue event.\nDefaults to the latest event when eventId is omitted.\n\nUse this tool when you need to:\n- See the user and application actions leading up to an error\n- Inspect navigation, console, HTTP, and other breadcrumb events\n- Reconstruct the immediate context before an issue occurred\n- Compare breadcrumb trails across multiple events in the same issue\n\n<examples>\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123')\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243')\nget_issue_breadcrumbs(issueUrl='https://my-org.sentry.io/issues/PROJECT-123/')\n</examples>",
"requiredScopes": ["event:read"]
},
{
Expand Down Expand Up @@ -214,7 +214,7 @@
},
{
"name": "get_event_stacktrace",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='abc123', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"requiredScopes": ["event:read"]
},
{
Expand Down Expand Up @@ -320,7 +320,7 @@
},
{
"name": "get_event_stacktrace",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='abc123', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"requiredScopes": ["event:read"]
},
{
Expand All @@ -330,7 +330,7 @@
},
{
"name": "get_issue_breadcrumbs",
"description": "Get the breadcrumb trail from the latest event for a Sentry issue.\n\nUse this tool when you need to:\n- See the user and application actions leading up to an error\n- Inspect navigation, console, HTTP, and other breadcrumb events\n- Reconstruct the immediate context before an issue occurred\n\n<examples>\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123')\nget_issue_breadcrumbs(issueUrl='https://my-org.sentry.io/issues/PROJECT-123/')\n</examples>",
"description": "Get the breadcrumb trail from a Sentry issue event.\nDefaults to the latest event when eventId is omitted.\n\nUse this tool when you need to:\n- See the user and application actions leading up to an error\n- Inspect navigation, console, HTTP, and other breadcrumb events\n- Reconstruct the immediate context before an issue occurred\n- Compare breadcrumb trails across multiple events in the same issue\n\n<examples>\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123')\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243')\nget_issue_breadcrumbs(issueUrl='https://my-org.sentry.io/issues/PROJECT-123/')\n</examples>",
"requiredScopes": ["event:read"]
},
{
Expand Down
39 changes: 33 additions & 6 deletions packages/mcp-core/src/toolDefinitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2321,7 +2321,8 @@
},
"eventId": {
"type": "string",
"description": "The ID of the event."
"pattern": "^[0-9a-fA-F]{32}$",
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
},
"attachmentId": {
"default": null,
Expand Down Expand Up @@ -2356,7 +2357,7 @@
},
{
"name": "get_event_stacktrace",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='abc123', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"description": "Get a full thread stacktrace from a specific Sentry event.\n\nUse this tool when you need to:\n- Fetch the full stacktrace for a thread listed in issue details\n- Inspect a non-crashed thread from an event with multiple threads\n- Get Sentry's default selected thread stacktrace when no thread is specified\n\n<examples>\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243', thread=259)\nget_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')\n</examples>\n\n<hints>\n- `thread` is optional. If omitted, this returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.\n- Pass `thread` as a numeric Thread ID or exact thread Name from the issue details thread list.\n- If the issue details show only one useful thread, omit `thread`.\n</hints>",
"inputSchema": {
"type": "object",
"properties": {
Expand All @@ -2382,8 +2383,18 @@
},
"eventId": {
"default": "latest",
"description": "The event ID for the issue. Defaults to `latest`.",
"type": "string"
"description": "The event ID for the issue, or `latest`. Defaults to `latest`.",
"anyOf": [
{
"type": "string",
"const": "latest"
},
{
"type": "string",
"pattern": "^[0-9a-fA-F]{32}$",
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
}
]
},
"thread": {
"description": "Optional thread selector. Pass a numeric thread ID, or an exact thread name string. If omitted, returns the same default thread Sentry selects: first crashed thread, then first thread with a stacktrace, then first thread.",
Expand Down Expand Up @@ -2455,7 +2466,7 @@
},
{
"name": "get_issue_breadcrumbs",
"description": "Get the breadcrumb trail from the latest event for a Sentry issue.\n\nUse this tool when you need to:\n- See the user and application actions leading up to an error\n- Inspect navigation, console, HTTP, and other breadcrumb events\n- Reconstruct the immediate context before an issue occurred\n\n<examples>\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123')\nget_issue_breadcrumbs(issueUrl='https://my-org.sentry.io/issues/PROJECT-123/')\n</examples>",
"description": "Get the breadcrumb trail from a Sentry issue event.\nDefaults to the latest event when eventId is omitted.\n\nUse this tool when you need to:\n- See the user and application actions leading up to an error\n- Inspect navigation, console, HTTP, and other breadcrumb events\n- Reconstruct the immediate context before an issue occurred\n- Compare breadcrumb trails across multiple events in the same issue\n\n<examples>\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123')\nget_issue_breadcrumbs(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243')\nget_issue_breadcrumbs(issueUrl='https://my-org.sentry.io/issues/PROJECT-123/')\n</examples>",
"inputSchema": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -2483,6 +2494,21 @@
"type": "string",
"format": "uri",
"description": "The URL of the issue. e.g. https://my-organization.sentry.io/issues/PROJECT-1Z43"
},
"eventId": {
"default": "latest",
"description": "The event ID for the issue, or `latest`. Defaults to `latest`.",
"anyOf": [
{
"type": "string",
"const": "latest"
},
{
"type": "string",
"pattern": "^[0-9a-fA-F]{32}$",
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
}
]
}
}
},
Expand Down Expand Up @@ -2518,7 +2544,8 @@
},
"eventId": {
"type": "string",
"description": "The ID of the event."
"pattern": "^[0-9a-fA-F]{32}$",
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
},
"issueUrl": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect } from "vitest";
import { describe, expect, it } from "vitest";
import getEventAttachment from "./get-event-attachment.js";

describe("get_event_attachment", () => {
Expand Down Expand Up @@ -110,7 +110,7 @@ describe("get_event_attachment", () => {
{
organizationSlug: "sentry-mcp-evals",
projectSlug: "cloudflare-mcp",
eventId: "octet-stream-event-id",
eventId: "d49541c747cb4d8aa3efb70ca5aba244",
attachmentId: "456",
regionUrl: null,
},
Expand Down
9 changes: 3 additions & 6 deletions packages/mcp-core/src/tools/catalog/get-event-stacktrace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { defineTool } from "../../internal/tool-helpers/define";
import { enhanceNotFoundError } from "../../internal/tool-helpers/enhance-error";
import { ensureIssueWithinProjectConstraint } from "../../internal/tool-helpers/issue";
import {
ParamEventIdOrLatest,
ParamIssueShortId,
ParamOrganizationSlug,
ParamRegionUrl,
Expand All @@ -27,7 +28,7 @@ export default defineTool({
"",
"<examples>",
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')",
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='abc123', thread=259)",
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243', thread=259)",
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')",
"</examples>",
"",
Expand All @@ -41,11 +42,7 @@ export default defineTool({
organizationSlug: ParamOrganizationSlug,
regionUrl: ParamRegionUrl.nullable().default(null),
issueId: ParamIssueShortId,
eventId: z
.string()
.trim()
.default("latest")
.describe("The event ID for the issue. Defaults to `latest`."),
eventId: ParamEventIdOrLatest,
thread: z
.union([z.number().int(), z.string().trim().min(1)])
.optional()
Expand Down
Loading
Loading