Skip to content

feat: adopt @sentry/api SDK for 38 of 40 API call sites#931

Open
MathurAditya724 wants to merge 35 commits into
mainfrom
feat/adopt-sentry-api-sdk
Open

feat: adopt @sentry/api SDK for 38 of 40 API call sites#931
MathurAditya724 wants to merge 35 commits into
mainfrom
feat/adopt-sentry-api-sdk

Conversation

@MathurAditya724

@MathurAditya724 MathurAditya724 commented May 5, 2026

Copy link
Copy Markdown
Member

Adds @sentry/api@^0.175.0 and migrates 38 of 40 API methods in SentryApiService from raw fetch (via requestJSON) to the typed SDK functions. The remaining 2 methods use internal/undocumented endpoints with no SDK equivalent.

What changed

Two helper methods added to SentryApiService:

  • getSdkConfig(opts?) — builds baseUrl + auth headers (including X-Sentry-MCP-Client-* identity headers) per call, supporting multi-region host overrides
  • unwrapSdkResult(result, context) — converts SDK discriminated-union results to the existing MCP error hierarchy (ApiError, ApiNotFoundError, ApiServerError, etc.), using the API detail text as the user-facing message when available

17 direct replacements (no casts)

listOrganizations, getOrganization, listTeams, createTeam, listProjects, getProject, createProject, updateProject, addTeamToProject, createClientKey, listClientKeys, listReleases, listIssues, getEventForIssue, getLatestEventForIssue, listEventsForIssue, startAutofix, getAutofixState

11 replacements with type casts

searchReplays, getIssue, getIssueTagValues, getIssueExternalLinks, getReplayDetails, listReplayIdsForIssue, getReplayRecordingSegments, updateIssue, searchErrors, searchSpans, searchEvents

10 new replacements (from SDK v0.171.0+)

  • listTagslistAnOrganization_sTags (with cast for useCache param)
  • listTraceItemAttributeslistTraceItemAttributes (replaces fetchTraceItemAttributesByType private helper)
  • listEventAttachmentslistAnEvent_sAttachments
  • getEventAttachment — metadata via listEventAttachments; binary download stays on raw request()
  • getTraceMetaretrieveTraceMetadata
  • getTraceretrieveATrace (with cast for limit/project params)
  • getFlamegraphretrieveAFlamegraphForAnOrganization
  • getTransactionProfileretrieveAProfile
  • getProfileChunkretrieveProfileChunksForAnOrganization
  • listReleases (project-level)listAProject_sReleases
  • listIssues (project-level)listAProject_sIssues (with cast for extra query params)

2 methods still on requestJSON

getAuthenticatedUser, regions endpoint — no SDK equivalent exists for these.

Schema improvements

  • TagSchema / TagListSchema rebased on zTagKeyDetailsDict from @sentry/api/zod
  • EventsResponseSchema, ExternalIssueListSchema, AutofixRunSchema use SDK-generated Zod schemas

Preserved behavior

  • Zod validation: All responses still pass through existing Zod schemas after SDK unwrapping
  • Error handling: unwrapSdkResult maps HTTP errors to the existing ApiError/ApiNotFoundError/ApiServerError types, using API detail text as the message
  • Multi-region: getSdkConfig builds per-call base URLs from opts.host overrides
  • Client identity: X-Sentry-MCP-Client-Id/Name/Family headers forwarded through SDK calls

Testing

  • TypeScript: clean (no new errors in client.ts)
  • Full test suite: 1033 tests passed, 4 skipped
  • Lint: clean

Dependencies

  • @sentry/api@^0.175.0 — latest version, includes SDK wrappers for organization tags, trace, profiling, project-level issues/releases, and event attachment endpoints

@MathurAditya724

Copy link
Copy Markdown
Member Author

fix-ci: starting attempt 1/3

@MathurAditya724

Copy link
Copy Markdown
Member Author

fix-ci: result — fixed 7 test failures across 5 test files.

Root causes:

  1. Agent provider conflict: ANTHROPIC_API_KEY env var leaked into tests alongside OPENAI_API_KEY, causing hasAgentProvider() to return false and routing tests through the direct (non-agent) code path. Fixed by clearing the env var in beforeEach/afterEach.
  2. MSW handler assertions: SDK sends limit not per_page for issues, and doesn't expose sort/per_page as query params on issue events. Updated handler assertions.
  3. Path traversal tests: SDK URL-encodes path params, neutralizing traversal attacks. Updated tests to verify the encoding works correctly instead of expecting errors.
  4. addTeamToProject mock: SDK sends POST with no body; removed request.json() call that threw SyntaxError.

All 69 tests in the 5 affected files pass. Client unit tests (72/72) still pass.

@dcramer

dcramer commented May 5, 2026

Copy link
Copy Markdown
Member

One f/u - i was hoping we'd be able to remove a bunch of typed schemas. Im not sure what we need to do to solve it, but it'd be great if @sentry/api had native zod structs that we could use that were either autogenerated or at minimum, we maintain them in one place vs here and cli etc

Comment thread packages/mcp-core/src/api-client/client.ts Outdated
Comment thread packages/mcp-core/src/api-client/client.ts Outdated
Comment thread packages/mcp-core/src/api-client/client.ts
Comment thread packages/mcp-core/src/api-client/client.ts Outdated
Comment thread packages/mcp-core/src/api-client/client.ts
Comment thread packages/mcp-core/src/api-client/client.ts Outdated
Comment thread packages/mcp-core/src/tools/get-issue-tag-values.test.ts Outdated
Comment thread packages/mcp-core/src/api-client/client.ts
Comment thread packages/mcp-core/src/api-client/client.ts Outdated
@MathurAditya724

Copy link
Copy Markdown
Member Author

Bumped @sentry/api to ^0.141.0 — this picks up the new @sentry/api/zod subpath export with auto-generated Zod v3 schemas from the OpenAPI spec (sentry-api-schema#70). These schemas can eventually replace the hand-written ones in packages/mcp-core/src/api-client/schema.ts.

Comment thread packages/mcp-core/src/api-client/client.ts
Comment thread packages/mcp-core/src/api-client/client.ts Outdated
@dcramer dcramer force-pushed the feat/adopt-sentry-api-sdk branch from eef83a3 to 67afdaa Compare May 14, 2026 17:31
MathurAditya724 and others added 9 commits June 11, 2026 17:05
Bump @sentry/api to ^0.172.0 which exports the SdkResult type from
getsentry/sentry-api-schema#74.

The SDK functions return RequestResult whose conditional generic
encoding (TData[keyof TData]) is not structurally assignable to
SdkResult, so unwrapSdkResult keeps `any` for the parameter type.
Updated the JSDoc to reference SdkResult as the runtime contract
and explain why the type mismatch exists.

Co-Authored-By: Claude (claude-opus-4-6)
Address Warden review findings:

- unwrapSdkResult now uses the API's detail text as the error message
  when available (e.g. 'The requested resource does not exist') instead
  of the internal context label ('getIssue: 404 Not Found'). Falls back
  to the context-prefixed message when no detail is present.

- Remove dead code in listOrganizations: queryParams, queryString, and
  path variables were left over from the pre-SDK migration and are no
  longer referenced.

Co-Authored-By: Claude (claude-opus-4-6)
…ry casts

sentry#116782 fixed issue_id from int to str and added per_page to the
List Organizations spec. With 0.174.0 these casts are no longer needed:
- 8x `issue_id: issueId as unknown as number` → plain issueId
- 3x `as Parameters<typeof sdkListYourOrganizations>[0]` (per_page now declared)
- 1x `as Parameters<typeof sdkListAnIssueSEvents>[0]` (issue_id was the only reason)

returnIds and allowAggregateConditions are now typed as boolean in the spec,
so update the values passed (true/false instead of strings) and strengthen
the remaining casts for project/-1 and useRpc to `as unknown as`.
Bump @sentry/api to ^0.175.0 which adds the organization tags
endpoint to the OpenAPI spec. Migrate listTags from requestJSON
to sdkListAnOrganizationSTags, reducing the remaining requestJSON
methods from 3 to 2 (getAuthenticatedUser and regions endpoint).

Co-Authored-By: Claude (claude-opus-4-6)
The events explore API expects the string '0' to disable aggregate
conditions. A boolean false could be serialized differently by the
SDK's request builder.

Co-Authored-By: Claude (claude-opus-4-6)
Neither param is read by the backend:
- project: "-1" in listReplayIdsForIssue — not in ReplayCountQueryParamsValidator
- useRpc: "1" in searchSpans — not referenced anywhere in organization_events.py

Removing them also drops the two remaining as-unknown-as casts on those call sites.
…s to requestJSON

searchReplays was building a Record<string,unknown> query object and casting
the entire SDK call. All replay params are already in the spec so the cast
was only needed because of the dynamic object construction. Inline the query
directly — the only remaining targeted cast is for field[], which the spec
types as a strict enum but the API accepts arbitrary strings at runtime.

Also reverts listTags to requestJSON. sdkListAnOrganizationSTags was removed
from the public spec in @sentry/api ≥0.175.0, so the SDK function no longer
exists. The endpoint has no SDK equivalent; requestJSON is the right fallback.
project is processed by OrganizationEventsEndpointBase.get_snuba_params(),
not by the endpoint's own validator. -1 is the all-accessible-projects
sentinel — without it the API narrows to membership-only projects, which
misses replays for issues in projects the caller can access but isn't a
member of.
Handle SDK response shapes and replay query validation after rebasing the API SDK branch onto main. Keep API detail messages in tests and preserve multi-environment replay queries.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
@dcramer dcramer force-pushed the feat/adopt-sentry-api-sdk branch from b52d182 to 950be09 Compare June 12, 2026 00:16
Comment thread packages/mcp-core/src/api-client/client.ts
Comment thread packages/mcp-core/src/api-client/client.ts
…ionId names

The operationId migration in getsentry/sentry shipped clean REST-token
operationIds (listProjectIssues, getOrganizationIssue, getOrganizationReplayCount,
startOrganizationIssueAutofix, ...), now published in @sentry/api 0.228.0.

Rename the imported SDK functions from the old normalized names
(listAProject_sIssues, retrieveAnIssue, retrieveACountOfReplaysForAGivenIssueOrTransaction,
...) to the tokens. The sdk* aliases are unchanged, so all call sites stay as-is.

Verified: tsc clean (0 source errors); api-client tests 93/93 pass.
Comment thread packages/mcp-core/package.json
Replace the hand-rolled Link-header regex in getNextCursor with the SDK's
parseSentryLinkHeader helper (exported from @sentry/api). It honors the same
results="true" qualifier; getNextCursor stays a thin adapter normalizing the
helper's undefined nextCursor to null, so all 4 call sites and the string|null
contract are unchanged.

Verified: tsc clean; client tests 93/93; full mcp-core suite unchanged
(1159 pass, 2 pre-existing autofix failures).
unwrapSdkResult took `result: any` and returned `result.data as T` with no
constraint, so the SDK's generated response type was erased to unknown at the
seam and re-derived by hand at every call site.

Type the param with a minimal structural shape ({ data?, error?, response? })
that the SDK's RequestResult satisfies — sidestepping the strict SdkResult
assignability issue that motivated the any — so TData infers from the SDK's
`data` field and the generated response type flows to callers. Removes the
explicit-any (and its eslint-disable). Pure type change; behavior unchanged.

Verified: tsc 0 source errors; TData proven a real inferred type (dropping the
cast errors 'TData | undefined not assignable to TData'); client tests 93/93.
Comment thread packages/mcp-core/src/api-client/client.ts Outdated
…o NaN

searchReplays sent project: [Number(projectId)] to the SDK. The SDK types the
replays project param as Array<number | string> ("a list of project IDs or
slugs"), and the pre-SDK implementation forwarded projectId as a string — so
Number() turns a slug into NaN and breaks slug-based filtering for direct
callers. Pass projectId through unchanged.

(Leaving getFlamegraph/profile-chunks as-is: their callers resolve slug->numeric
ID upstream by design, and the chunks SDK param is number, not number|string.)

Adds a regression test asserting a slug passes through as project=<slug>; it
fails ('NaN') against the old code. Verified: tsc clean; client tests 94/94.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 52b2cd5. Configure here.

Comment thread packages/mcp-core/src/api-client/client.ts Outdated
Comment thread packages/mcp-core/src/api-client/client.ts Outdated
Comment thread packages/mcp-core/src/tools/support/search-events/utils.test.ts
…ode engine

Addresses three cursor findings:
- getFlamegraph: SDK types project as Array<number|string> (IDs or slugs) and
  the prior code forwarded projectId.toString(); forward projectId as-is so a
  slug doesn't become NaN.
- getProfileChunk: same regression. The SDK under-types its project as a scalar
  number (vs the sibling flamegraph endpoint's number|string), so forward with a
  narrowing cast + comment; the value serializes to the query string regardless.
- engines.node: @sentry/api requires Node >=22 but the workspace declared >=20.
  Bump root + mcp-core/mcp-server/mcp-server-evals/mcp-server-mocks to >=22.

Adds regression tests for both profiling methods (assert the outgoing project
param is the slug, captured before response parsing); both fail ('NaN') against
the old code. Verified: tsc clean; client tests 96/96.
getsentry/sentry#118051 fixed the profiling-chunks `project` param to be
documented as an ID-or-slug (string) instead of a scalar int, so @sentry/api
0.232.0 now types getProfileChunk's `project` as string.

Replace the temporary `projectId as number` cast with `String(projectId)` —
numeric ids serialize as-is, slugs are preserved, no cast. The slug guard test
still passes.

Verified: tsc clean (no other breakage from 0.228->0.232); full mcp-core suite
unchanged (1162 pass; the 2 fails remain the pre-existing autofix
run_id/sentry_run_id tests, tracked separately).
The autofix POST endpoint's response type (AutofixPostResponse) is
{ run_id: int, sentry_run_id: str | None } — it returns both fields. The
generated @sentry/api zod schema (which AutofixRunSchema wraps) therefore
requires the sentry_run_id key, but our mocks/fixtures predated it and returned
only { run_id }, so the schema parse failed in tests.

Add sentry_run_id to the three autofix POST mocks (server-mocks + the two test
fixtures). No runtime change — the MCP still keys off run_id; this just makes the
fixtures match the real response shape. (GET-state mocks keep run_id only, which
their schema expects.)

Verified: full mcp-core suite green (1164 pass, 0 fail); tsc clean.
@betegon betegon deployed to Actions June 19, 2026 08:08 — with GitHub Actions Active
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants