Skip to content

Commit d8dc5bf

Browse files
feat(mcp): Add priority parameter to update_issue (#2155)
`update_issue` only took `status` and `assignedTo`. That meant an agent couldn't change an issue's priority without also resolving, ignoring, or reassigning it. This came up in user feedback (MCP-SERVER-GND). Sentry's issue update endpoint already accepts `priority` with the values `low`, `medium`, and `high`. It's validated in `GroupValidator` and handled by `handle_priority` in `group_index/update.py`. A manual priority also sets `priority_locked_at`, so Sentry stops changing it automatically, and the param description says so. Issue types that don't allow manual priority get a 400 from Sentry, which the tool returns to the caller as is. What changed: - New optional `priority` input, sent in the PUT body. Passing only `priority` is now a valid update. - If the issue already has the requested priority, the PUT is skipped, the same way it is for status and assignee. - The structured output now includes `changes.priority` and `current.priority`. The tool description is also much shorter: about 730 characters, down from about 2,020. It no longer repeats enum values and per-field rules, because those are already in the input schema. It keeps a few examples and the hints for rules that span more than one field. <!-- junior-request-attribution:start --> via @dcramer. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC08J1NSPU6S%3A1794354108.041609 --> -- [View Session](https://junior-prod.sentry.dev/conversations/slack%3AC08J1NSPU6S%3A1794354108.041609) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC08J1NSPU6S%3A1794354108.041609/?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 <23610+dcramer@users.noreply.github.com>
1 parent 16dcd29 commit d8dc5bf

8 files changed

Lines changed: 166 additions & 24 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4124,11 +4124,13 @@ export class SentryApiService {
41244124
ignoreUserCount,
41254125
ignoreUserWindow,
41264126
statusDetails,
4127+
priority,
41274128
}: {
41284129
organizationSlug: string;
41294130
issueId: string;
41304131
status?: string;
41314132
assignedTo?: string | null;
4133+
priority?: string;
41324134
substatus?: string;
41334135
ignoreDuration?: number;
41344136
ignoreCount?: number;
@@ -4149,8 +4151,10 @@ export class SentryApiService {
41494151
ignoreUserCount?: number;
41504152
ignoreUserWindow?: number;
41514153
statusDetails?: IssueResolutionDetails;
4154+
priority?: string;
41524155
} = {};
41534156
if (status !== undefined) updateData.status = status;
4157+
if (priority !== undefined) updateData.priority = priority;
41544158
if (statusDetails !== undefined) updateData.statusDetails = statusDetails;
41554159
if (assignedTo !== undefined) updateData.assignedTo = assignedTo ?? "";
41564160
if (substatus !== undefined) updateData.substatus = substatus;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ export const IssueSchema = z
860860
platform: z.string().nullable().optional(),
861861
status: z.string(),
862862
substatus: z.string().nullable().optional(),
863+
priority: z.string().nullable().optional(),
863864
culprit: z.string().nullable(),
864865
type: z
865866
.union([

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ export const ParamIssueStatus = z
215215
"The new status for the issue. Valid values are 'resolved', 'resolvedInNextRelease', 'unresolved', and 'ignored'.",
216216
);
217217

218+
export const ParamIssuePriority = z
219+
.enum(["low", "medium", "high"])
220+
.describe(
221+
"The new priority for the issue. Setting it manually stops Sentry from changing it automatically.",
222+
);
223+
218224
export const ParamIssueIgnoreMode = z
219225
.enum([
220226
"untilEscalating",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@
525525
},
526526
{
527527
"name": "update_issue",
528-
"description": "Update a Sentry issue's status or assignment.\n\nUse this to resolve, reopen, assign, unassign, or ignore an issue.\n\n<examples>\n```\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='resolved')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', assignedTo='user:123456')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', assignedTo=null)\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', ignoreMode='forever')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', ignoreMode='untilOccurrenceCount', ignoreCount=100, ignoreWindowMinutes=60)\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', reason='Ignoring because this is expected noise from the staging deploy')\n```\n</examples>\n\n<hints>\n- Provide `issueUrl` or `organizationSlug` + `issueId`.\n- At least one of `status` or `assignedTo` is required.\n- Omit `assignedTo` to leave assignment unchanged. Pass `null` to unassign; otherwise use `user:ID` or `team:ID_OR_SLUG`.\n- Use the `whoami` tool to find your user ID for self-assignment.\n- Status values: `resolved`, `resolvedInNextRelease`, `unresolved`, `ignored`.\n- `status='resolved'` takes optional `inRelease`, or `inCommit` + `inCommitRepository`.\n- `status='ignored'` defaults to `ignoreMode='untilEscalating'`.\n- Ignore modes: `untilEscalating`, `forever`, `forDuration`, `untilOccurrenceCount`, `untilUserCount`.\n- Matching ignore inputs are `ignoreDurationMinutes`, `ignoreCount` + optional `ignoreWindowMinutes`, or `ignoreUserCount` + optional `ignoreUserWindowMinutes`.\n- To switch an already ignored issue between `untilEscalating`, `forever`, and condition-based ignore modes, first set `status='unresolved'`, then ignore it again with the new rule.\n- `reason` is optional. When provided, it will be posted as a comment on the issue's activity feed explaining why the action was taken.\n</hints>",
528+
"description": "Update a Sentry issue's status, assignment, or priority.\n\n<examples>\n```\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='resolved')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', assignedTo='user:123456')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', ignoreMode='forever')\n```\n</examples>\n\n<hints>\n- Provide `issueUrl` or `organizationSlug` + `issueId`.\n- At least one of `status`, `assignedTo`, or `priority` is required.\n- `status='ignored'` defaults to `ignoreMode='untilEscalating'`.\n- To switch an already ignored issue between `untilEscalating`, `forever`, and condition-based ignore modes, first set `status='unresolved'`, then ignore it again.\n</hints>",
529529
"requiredScopes": ["event:write"]
530530
},
531531
{

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

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14481,7 +14481,7 @@
1448114481
{
1448214482
"name": "update_issue",
1448314483
"title": "Update Issue",
14484-
"description": "Update a Sentry issue's status or assignment.\n\nUse this to resolve, reopen, assign, unassign, or ignore an issue.\n\n<examples>\n```\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='resolved')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', assignedTo='user:123456')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', assignedTo=null)\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', ignoreMode='forever')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', ignoreMode='untilOccurrenceCount', ignoreCount=100, ignoreWindowMinutes=60)\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', reason='Ignoring because this is expected noise from the staging deploy')\n```\n</examples>\n\n<hints>\n- Provide `issueUrl` or `organizationSlug` + `issueId`.\n- At least one of `status` or `assignedTo` is required.\n- Omit `assignedTo` to leave assignment unchanged. Pass `null` to unassign; otherwise use `user:ID` or `team:ID_OR_SLUG`.\n- Use the `whoami` tool to find your user ID for self-assignment.\n- Status values: `resolved`, `resolvedInNextRelease`, `unresolved`, `ignored`.\n- `status='resolved'` takes optional `inRelease`, or `inCommit` + `inCommitRepository`.\n- `status='ignored'` defaults to `ignoreMode='untilEscalating'`.\n- Ignore modes: `untilEscalating`, `forever`, `forDuration`, `untilOccurrenceCount`, `untilUserCount`.\n- Matching ignore inputs are `ignoreDurationMinutes`, `ignoreCount` + optional `ignoreWindowMinutes`, or `ignoreUserCount` + optional `ignoreUserWindowMinutes`.\n- To switch an already ignored issue between `untilEscalating`, `forever`, and condition-based ignore modes, first set `status='unresolved'`, then ignore it again with the new rule.\n- `reason` is optional. When provided, it will be posted as a comment on the issue's activity feed explaining why the action was taken.\n</hints>",
14484+
"description": "Update a Sentry issue's status, assignment, or priority.\n\n<examples>\n```\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='resolved')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', assignedTo='user:123456')\nupdate_issue(organizationSlug='my-org', issueId='PROJECT-123', status='ignored', ignoreMode='forever')\n```\n</examples>\n\n<hints>\n- Provide `issueUrl` or `organizationSlug` + `issueId`.\n- At least one of `status`, `assignedTo`, or `priority` is required.\n- `status='ignored'` defaults to `ignoreMode='untilEscalating'`.\n- To switch an already ignored issue between `untilEscalating`, `forever`, and condition-based ignore modes, first set `status='unresolved'`, then ignore it again.\n</hints>",
1448514485
"inputSchema": {
1448614486
"type": "object",
1448714487
"properties": {
@@ -14532,6 +14532,11 @@
1453214532
],
1453314533
"description": "The assignee in format 'user:ID' or 'team:ID_OR_SLUG' where ID is numeric. Pass null to unassign the issue. Example: 'user:123456', 'team:789', or 'team:my-team-slug'. Use the `whoami` tool to find your user ID."
1453414534
},
14535+
"priority": {
14536+
"type": "string",
14537+
"enum": ["low", "medium", "high"],
14538+
"description": "The new priority for the issue. Setting it manually stops Sentry from changing it automatically."
14539+
},
1453514540
"ignoreMode": {
1453614541
"type": "string",
1453714542
"enum": [
@@ -14676,9 +14681,29 @@
1467614681
"type": "null"
1467714682
}
1467814683
]
14684+
},
14685+
"priority": {
14686+
"anyOf": [
14687+
{
14688+
"type": "object",
14689+
"properties": {
14690+
"from": {
14691+
"type": "string"
14692+
},
14693+
"to": {
14694+
"type": "string"
14695+
}
14696+
},
14697+
"required": ["from", "to"],
14698+
"additionalProperties": false
14699+
},
14700+
{
14701+
"type": "null"
14702+
}
14703+
]
1467914704
}
1468014705
},
14681-
"required": ["status", "ignoreBehavior", "assignedTo"],
14706+
"required": ["status", "ignoreBehavior", "assignedTo", "priority"],
1468214707
"additionalProperties": false
1468314708
},
1468414709
"current": {
@@ -14692,9 +14717,12 @@
1469214717
},
1469314718
"assignedTo": {
1469414719
"type": "string"
14720+
},
14721+
"priority": {
14722+
"type": ["string", "null"]
1469514723
}
1469614724
},
14697-
"required": ["status", "ignoreBehavior", "assignedTo"],
14725+
"required": ["status", "ignoreBehavior", "assignedTo", "priority"],
1469814726
"additionalProperties": false
1469914727
},
1470014728
"effect": {

‎packages/mcp-core/src/tools/catalog/update-issue.test.ts‎

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ describe("update_issue", () => {
5858
"changes": {
5959
"assignedTo": null,
6060
"ignoreBehavior": null,
61+
"priority": null,
6162
"status": {
6263
"from": "unresolved",
6364
"to": "resolved",
@@ -66,6 +67,7 @@ describe("update_issue", () => {
6667
"current": {
6768
"assignedTo": "Jane Developer",
6869
"ignoreBehavior": null,
70+
"priority": "medium",
6971
"status": "resolved",
7072
},
7173
"effect": "The issue is now marked as resolved and will no longer generate alerts",
@@ -144,6 +146,7 @@ describe("update_issue", () => {
144146
status: null,
145147
ignoreBehavior: null,
146148
assignedTo: null,
149+
priority: null,
147150
});
148151
expect(output.current.status).toBe("unresolved");
149152
expect(output.effect).toBeNull();
@@ -169,11 +172,13 @@ describe("update_issue", () => {
169172
"to": "john.doe",
170173
},
171174
"ignoreBehavior": null,
175+
"priority": null,
172176
"status": null,
173177
},
174178
"current": {
175179
"assignedTo": "john.doe",
176180
"ignoreBehavior": null,
181+
"priority": "medium",
177182
"status": "unresolved",
178183
},
179184
"effect": null,
@@ -338,9 +343,84 @@ describe("update_issue", () => {
338343
status: null,
339344
ignoreBehavior: null,
340345
assignedTo: null,
346+
priority: null,
341347
});
342348
});
343349

350+
it("updates issue priority without changing status or assignment", async () => {
351+
let putBody: unknown;
352+
mswServer.use(
353+
http.put(
354+
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/",
355+
async ({ request }) => {
356+
putBody = await request.json();
357+
return HttpResponse.json(createIssue({ priority: "low" }));
358+
},
359+
{ once: true },
360+
),
361+
);
362+
363+
const result = await updateIssue.handler(
364+
{
365+
organizationSlug: "sentry-mcp-evals",
366+
issueId: "CLOUDFLARE-MCP-41",
367+
priority: "low",
368+
issueUrl: undefined,
369+
regionUrl: null,
370+
},
371+
serverContext,
372+
);
373+
374+
expect(putBody).toEqual({ priority: "low" });
375+
const output = structured(result);
376+
expect(output.updated).toBe(true);
377+
expect(output.changes).toEqual({
378+
status: null,
379+
ignoreBehavior: null,
380+
assignedTo: null,
381+
priority: { from: "medium", to: "low" },
382+
});
383+
expect(output.current.status).toBe("unresolved");
384+
expect(output.current.priority).toBe("low");
385+
expect(output.effect).toBeNull();
386+
});
387+
388+
it("skips priority updates when the requested priority is already set", async () => {
389+
let putCalled = false;
390+
const currentIssue = createIssue({ priority: "high" });
391+
392+
mswServer.use(
393+
http.get(
394+
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/",
395+
() => HttpResponse.json(currentIssue),
396+
),
397+
http.put(
398+
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/",
399+
() => {
400+
putCalled = true;
401+
return HttpResponse.json(currentIssue);
402+
},
403+
),
404+
);
405+
406+
const result = await updateIssue.handler(
407+
{
408+
organizationSlug: "sentry-mcp-evals",
409+
issueId: "CLOUDFLARE-MCP-41",
410+
priority: "high",
411+
issueUrl: undefined,
412+
regionUrl: null,
413+
},
414+
serverContext,
415+
);
416+
417+
expect(putCalled).toBe(false);
418+
const output = structured(result);
419+
expect(output.updated).toBe(false);
420+
expect(output.changes.priority).toBeNull();
421+
expect(output.current.priority).toBe("high");
422+
});
423+
344424
it("updates both status and assignment", async () => {
345425
const result = await updateIssue.handler(
346426
{
@@ -361,6 +441,7 @@ describe("update_issue", () => {
361441
"to": "You",
362442
},
363443
"ignoreBehavior": null,
444+
"priority": null,
364445
"status": {
365446
"from": "unresolved",
366447
"to": "resolved",
@@ -369,6 +450,7 @@ describe("update_issue", () => {
369450
"current": {
370451
"assignedTo": "me",
371452
"ignoreBehavior": null,
453+
"priority": "medium",
372454
"status": "resolved",
373455
},
374456
"effect": "The issue is now marked as resolved and will no longer generate alerts",
@@ -512,6 +594,7 @@ describe("update_issue", () => {
512594
"changes": {
513595
"assignedTo": null,
514596
"ignoreBehavior": null,
597+
"priority": null,
515598
"status": {
516599
"from": "unresolved",
517600
"to": "resolvedInRelease",
@@ -520,6 +603,7 @@ describe("update_issue", () => {
520603
"current": {
521604
"assignedTo": "Jane Developer",
522605
"ignoreBehavior": null,
606+
"priority": "medium",
523607
"status": "resolvedInRelease",
524608
},
525609
"effect": "The issue is now marked as resolved in release backend@1.4.2. Events from newer releases will reopen it as a regression",
@@ -872,6 +956,7 @@ describe("update_issue", () => {
872956
"from": null,
873957
"to": "Until escalating",
874958
},
959+
"priority": null,
875960
"status": {
876961
"from": "unresolved",
877962
"to": "ignored",
@@ -880,6 +965,7 @@ describe("update_issue", () => {
880965
"current": {
881966
"assignedTo": "Jane Developer",
882967
"ignoreBehavior": "Until escalating",
968+
"priority": "medium",
883969
"status": "ignored",
884970
},
885971
"effect": "The issue is now ignored until it escalates",
@@ -934,6 +1020,7 @@ describe("update_issue", () => {
9341020
"from": null,
9351021
"to": "Until it occurs 100 times in 60 minutes",
9361022
},
1023+
"priority": null,
9371024
"status": {
9381025
"from": "unresolved",
9391026
"to": "ignored",
@@ -942,6 +1029,7 @@ describe("update_issue", () => {
9421029
"current": {
9431030
"assignedTo": "Jane Developer",
9441031
"ignoreBehavior": "Until it occurs 100 times in 60 minutes",
1032+
"priority": "medium",
9451033
"status": "ignored",
9461034
},
9471035
"effect": "The issue is now ignored until it occurs 100 times in 60 minutes",
@@ -1313,7 +1401,7 @@ describe("update_issue", () => {
13131401
serverContext,
13141402
),
13151403
).rejects.toThrow(
1316-
"At least one of `status` or `assignedTo` must be provided to update the issue",
1404+
"At least one of `status`, `assignedTo`, or `priority` must be provided to update the issue",
13171405
);
13181406
});
13191407

0 commit comments

Comments
 (0)