Skip to content

Commit 9a4d77c

Browse files
feat(tools): Allow eventId on get_issue_breadcrumbs (#1224)
Let `get_issue_breadcrumbs` take an optional `eventId` so callers can inspect breadcrumbs for more than the newest event in an issue. Omitted values still resolve to `latest`. **Event selection** The tool and helper now call `getEventForIssue` with `eventId` (default `latest`), matching the existing stacktrace tool pattern. Generated tool/skill definitions were regenerated. Fixes #1222 <!-- junior-request-attribution:start --> Requested by **David Cramer**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC08J1NSPU6S%3A1785772387.387109 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC08J1NSPU6S%3A1785772387.387109) [Sentry](https://sentry.sentry.io/explore/conversations/slack%3AC08J1NSPU6S%3A1785772387.387109/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: David Cramer <david@sentry.io>
1 parent d19d3c7 commit 9a4d77c

11 files changed

Lines changed: 113 additions & 32 deletions

File tree

‎packages/mcp-core/src/api-client/client.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3259,7 +3259,7 @@ export class SentryApiService {
32593259
opts?: RequestOptions,
32603260
): Promise<Event> {
32613261
const body = await this.requestJSON(
3262-
`/organizations/${organizationSlug}/issues/${issueId}/events/${eventId}/`,
3262+
`/organizations/${encodeURIComponent(organizationSlug)}/issues/${encodeURIComponent(issueId)}/events/${encodeURIComponent(eventId)}/`,
32633263
undefined,
32643264
opts,
32653265
);

‎packages/mcp-core/src/internal/tool-helpers/breadcrumbs.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import type { SentryApiService } from "../../api-client/index";
22

33
/**
4-
* Fetches breadcrumbs from the latest event for an issue and formats them.
4+
* Fetches breadcrumbs from an issue event and formats them.
5+
* Defaults to the latest event when eventId is omitted.
56
*/
67
export async function fetchAndFormatBreadcrumbs(
78
apiService: SentryApiService,
89
organizationSlug: string,
910
issueId: string,
11+
eventId: string = "latest",
1012
): Promise<string> {
11-
const event = await apiService.getLatestEventForIssue({
13+
const event = await apiService.getEventForIssue({
1214
organizationSlug,
1315
issueId,
16+
eventId,
1417
});
1518

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

‎packages/mcp-core/src/schema.ts‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,23 @@ export const ParamSentryGuide = z
237237
"Use either a platform (e.g., 'javascript', 'python') or platform/guide combination (e.g., 'javascript/nextjs', 'python/django').",
238238
);
239239

240-
export const ParamEventId = z.string().trim().describe("The ID of the event.");
240+
export const ParamEventId = z
241+
.string()
242+
.trim()
243+
.regex(
244+
/^[0-9a-fA-F]{32}$/,
245+
"Event ID must be a 32-character hexadecimal string",
246+
)
247+
.describe("The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`");
248+
249+
/**
250+
* Issue-scoped event selector used by tools that can target either a concrete
251+
* event or the issue's latest event.
252+
*/
253+
export const ParamEventIdOrLatest = z
254+
.union([z.literal("latest"), ParamEventId])
255+
.default("latest")
256+
.describe("The event ID for the issue, or `latest`. Defaults to `latest`.");
241257

242258
export const ParamAttachmentId = z
243259
.string()

‎packages/mcp-core/src/skillDefinitions.json‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
},
7070
{
7171
"name": "get_event_stacktrace",
72-
"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>",
72+
"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>",
7373
"requiredScopes": ["event:read"]
7474
},
7575
{
@@ -79,7 +79,7 @@
7979
},
8080
{
8181
"name": "get_issue_breadcrumbs",
82-
"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>",
82+
"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>",
8383
"requiredScopes": ["event:read"]
8484
},
8585
{
@@ -214,7 +214,7 @@
214214
},
215215
{
216216
"name": "get_event_stacktrace",
217-
"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>",
217+
"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>",
218218
"requiredScopes": ["event:read"]
219219
},
220220
{
@@ -320,7 +320,7 @@
320320
},
321321
{
322322
"name": "get_event_stacktrace",
323-
"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>",
323+
"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>",
324324
"requiredScopes": ["event:read"]
325325
},
326326
{
@@ -330,7 +330,7 @@
330330
},
331331
{
332332
"name": "get_issue_breadcrumbs",
333-
"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>",
333+
"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>",
334334
"requiredScopes": ["event:read"]
335335
},
336336
{

‎packages/mcp-core/src/toolDefinitions.json‎

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,7 +2321,8 @@
23212321
},
23222322
"eventId": {
23232323
"type": "string",
2324-
"description": "The ID of the event."
2324+
"pattern": "^[0-9a-fA-F]{32}$",
2325+
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
23252326
},
23262327
"attachmentId": {
23272328
"default": null,
@@ -2356,7 +2357,7 @@
23562357
},
23572358
{
23582359
"name": "get_event_stacktrace",
2359-
"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>",
2360+
"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>",
23602361
"inputSchema": {
23612362
"type": "object",
23622363
"properties": {
@@ -2382,8 +2383,18 @@
23822383
},
23832384
"eventId": {
23842385
"default": "latest",
2385-
"description": "The event ID for the issue. Defaults to `latest`.",
2386-
"type": "string"
2386+
"description": "The event ID for the issue, or `latest`. Defaults to `latest`.",
2387+
"anyOf": [
2388+
{
2389+
"type": "string",
2390+
"const": "latest"
2391+
},
2392+
{
2393+
"type": "string",
2394+
"pattern": "^[0-9a-fA-F]{32}$",
2395+
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
2396+
}
2397+
]
23872398
},
23882399
"thread": {
23892400
"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.",
@@ -2455,7 +2466,7 @@
24552466
},
24562467
{
24572468
"name": "get_issue_breadcrumbs",
2458-
"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>",
2469+
"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>",
24592470
"inputSchema": {
24602471
"type": "object",
24612472
"properties": {
@@ -2483,6 +2494,21 @@
24832494
"type": "string",
24842495
"format": "uri",
24852496
"description": "The URL of the issue. e.g. https://my-organization.sentry.io/issues/PROJECT-1Z43"
2497+
},
2498+
"eventId": {
2499+
"default": "latest",
2500+
"description": "The event ID for the issue, or `latest`. Defaults to `latest`.",
2501+
"anyOf": [
2502+
{
2503+
"type": "string",
2504+
"const": "latest"
2505+
},
2506+
{
2507+
"type": "string",
2508+
"pattern": "^[0-9a-fA-F]{32}$",
2509+
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
2510+
}
2511+
]
24862512
}
24872513
}
24882514
},
@@ -2518,7 +2544,8 @@
25182544
},
25192545
"eventId": {
25202546
"type": "string",
2521-
"description": "The ID of the event."
2547+
"pattern": "^[0-9a-fA-F]{32}$",
2548+
"description": "The ID of the event. e.g. `c49541c747cb4d8aa3efb70ca5aba243`"
25222549
},
25232550
"issueUrl": {
25242551
"type": "string",

‎packages/mcp-core/src/tools/catalog/get-event-attachment.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, it, expect } from "vitest";
1+
import { describe, expect, it } from "vitest";
22
import getEventAttachment from "./get-event-attachment.js";
33

44
describe("get_event_attachment", () => {
@@ -110,7 +110,7 @@ describe("get_event_attachment", () => {
110110
{
111111
organizationSlug: "sentry-mcp-evals",
112112
projectSlug: "cloudflare-mcp",
113-
eventId: "octet-stream-event-id",
113+
eventId: "d49541c747cb4d8aa3efb70ca5aba244",
114114
attachmentId: "456",
115115
regionUrl: null,
116116
},

‎packages/mcp-core/src/tools/catalog/get-event-stacktrace.ts‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { defineTool } from "../../internal/tool-helpers/define";
66
import { enhanceNotFoundError } from "../../internal/tool-helpers/enhance-error";
77
import { ensureIssueWithinProjectConstraint } from "../../internal/tool-helpers/issue";
88
import {
9+
ParamEventIdOrLatest,
910
ParamIssueShortId,
1011
ParamOrganizationSlug,
1112
ParamRegionUrl,
@@ -27,7 +28,7 @@ export default defineTool({
2728
"",
2829
"<examples>",
2930
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123')",
30-
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='abc123', thread=259)",
31+
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', eventId='c49541c747cb4d8aa3efb70ca5aba243', thread=259)",
3132
"get_event_stacktrace(organizationSlug='my-org', issueId='PROJECT-123', thread='main')",
3233
"</examples>",
3334
"",
@@ -41,11 +42,7 @@ export default defineTool({
4142
organizationSlug: ParamOrganizationSlug,
4243
regionUrl: ParamRegionUrl.nullable().default(null),
4344
issueId: ParamIssueShortId,
44-
eventId: z
45-
.string()
46-
.trim()
47-
.default("latest")
48-
.describe("The event ID for the issue. Defaults to `latest`."),
45+
eventId: ParamEventIdOrLatest,
4946
thread: z
5047
.union([z.number().int(), z.string().trim().min(1)])
5148
.optional()

0 commit comments

Comments
 (0)