Skip to content

Commit 10db79e

Browse files
betegoncodex
andcommitted
fix(api): accept null legacy event context
Allow event responses with explicitly null extra data to load issue details while retaining validation of arrays, scalars, and other event fields. Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
1 parent 3ef57b5 commit 10db79e

4 files changed

Lines changed: 40 additions & 2 deletions

File tree

‎docs/contributing/tool-responses.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ omits the section. Commit lookup is optional: failures do not prevent issue
125125
details from loading, but unexpected server or response-validation failures
126126
are reported to Sentry.
127127

128+
The event API's legacy `context` field (extra data) can be `null`. Issue details
129+
must still load; omit the `Extra Data` section for null or empty values while
130+
preserving the event's error, stacktrace, and structured `contexts` data.
131+
128132
## Structured Content
129133

130134
MCP tools may expose `structuredContent` alongside generated text `content`.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ describe("getEventForIssue context validation", () => {
3333
});
3434

3535
it.each([
36-
{ context: null, contextType: "null" },
3736
{ context: ["private-extra-value"], contextType: "array" },
3837
{ context: "private-extra-value", contextType: "string" },
3938
{ context: 12345, contextType: "number" },
4039
{ context: false, contextType: "boolean" },
4140
// Valid context can accompany a failure in another field.
41+
{ context: null, contextType: "null", title: null },
4242
{ context: undefined, contextType: "undefined", title: null },
4343
{
4444
context: { "private-extra-key": "private-extra-value" },
@@ -68,6 +68,7 @@ describe("getEventForIssue context validation", () => {
6868

6969
it.each([
7070
undefined,
71+
null,
7172
{},
7273
{ array: [1, "two"], nested: { value: false }, nullable: null },
7374
])("preserves valid context: %j", async (context) => {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,8 @@ const BaseEventSchema = z.object({
11331133
.optional(),
11341134
// "context" (singular) is the legacy "extra" field for arbitrary user-defined data
11351135
// This is different from "contexts" (plural) which are structured contexts
1136-
context: z.record(z.string(), z.unknown()).optional(),
1136+
// Sentry preserves null when the event's extra data is explicitly null.
1137+
context: z.record(z.string(), z.unknown()).nullable().optional(),
11371138
sdk: z
11381139
.object({
11391140
name: z.string().nullable().optional(),

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
createRegressedIssue,
99
createUnknownEvent,
1010
createUnsupportedIssue,
11+
eventFixture,
1112
issueNullCulpritFixture,
1213
mswServer,
1314
} from "@sentry/mcp-server-mocks";
@@ -2055,6 +2056,37 @@ describe("get_issue_details", () => {
20552056
expect(result).toContain("### Additional Context");
20562057
});
20572058

2059+
it("returns event details when legacy context is null", async () => {
2060+
mswServer.use(
2061+
http.get(
2062+
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/",
2063+
() => HttpResponse.json({ ...eventFixture, context: null }),
2064+
{ once: true },
2065+
),
2066+
);
2067+
2068+
const result = await getIssueDetails.handler(
2069+
{
2070+
organizationSlug: "sentry-mcp-evals",
2071+
issueId: "CLOUDFLARE-MCP-41",
2072+
eventId: undefined,
2073+
issueUrl: undefined,
2074+
regionUrl: null,
2075+
},
2076+
baseContext,
2077+
);
2078+
2079+
expect(result).toContain("## Event Details");
2080+
expect(result).toContain(`**Event ID**: ${eventFixture.id}`);
2081+
expect(result).toContain(
2082+
"Error: Tool list_organizations is already registered",
2083+
);
2084+
expect(result).toContain("index.js:19631:28 (Object.fetch)");
2085+
expect(result).toContain("### Additional Context");
2086+
expect(result).toContain('name: "cloudflare"');
2087+
expect(result).not.toContain("### Extra Data");
2088+
});
2089+
20582090
it("handles regressed performance issues (generic type with empty entries)", async () => {
20592091
// This tests the actual structure from issue #633
20602092
// Regressed performance issues have:

0 commit comments

Comments
 (0)