Skip to content

ref(mcp): Call shared organization endpoints from SentryApiService - #1741

Merged
BYK merged 7 commits into
mainfrom
feat/shared-api-client-organizations
Oct 29, 2026
Merged

BYK merged 7 commits into
mainfrom
feat/shared-api-client-organizations

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Task 8.4, part 2 of the shared API client stack (#1669). It used to be stacked on #1737, which has merged, so it now targets main.

This adds the first shared endpoint wrappers to toolkit-core's api-client/, and the MCP's SentryApiService now calls them. The code moves as is, so it can go to @sentry/api later without changes.

Changes

  • endpoints/organizations adds getAuthenticatedUser, listOrganizations and getOrganization. Each one takes an ApiTarget (the request core plus an origin) and a Standard Schema from the host, and returns that schema's output. toolkit-core doesn't depend on Zod or valibot when the code runs.
  • The request core gains sdkClient, which sends one GET to an expected path and rejects any other path or method, and listPage, which walks a list endpoint up to a limit and validates each page. These are the MCP's sdkClient and sdkListPage, moved as is. The only change is that validation now uses Standard Schema.
  • SentryApiService still decides which host to call: the control host for the current user and for the organization list on public SaaS. Its own sdkClient and sdkListPage copies are deleted, and the teams and projects lists use the shared ones.
  • toolkit-core now depends on @sentry/api. To keep a single TypeScript copy, the pnpm catalog's TypeScript moves from ^5.8.3 to ^5.9.3, and toolkit-core and the CLI use catalog: instead of pinning their own version. Every workspace package now resolves TypeScript 5.9.3, and 5.8.3 drops out of the lockfile. The cache policy changes one type to match TypeScript 5.9's DOM BodyInit. Nothing changes at runtime.

One change in behaviour: if a response fails its schema in these calls, the error is now the shared ApiResponseValidationError instead of a ZodError. Both still reach the tool error handler as unexpected errors.

--

View Junior Session in Sentry

createApiClientCore sends a request through the host's transport with
the host's headers, turns network failures into readable errors
(ConfigurationError for an unreachable host), maps error responses to
the status's ApiError through readErrorResponse, and parses JSON bodies.
The transport, headers, and logger are injected, so it stays
browser-portable. It is the MCP's request path, moved as is; the next
commit switches SentryApiService onto it.
request and requestJSON keep their signatures and delegate to
createApiClientCore, so no endpoint method changes. SentryApiService
supplies its headers (User-Agent, Authorization, MCP attribution), the
shared transport, and logWarn under the api.client scope. Its copies of
the network error messages and the error-response mapping are deleted.

The one difference: the error-response warnings now log the host the
request went to, rather than the service's default host, when a call
overrides the host.
…oducts use

The shared client builds on @sentry/api operations next. toolkit-core
pinned TypeScript through the ^5.8.3 catalog (5.8.3) while mcp-core and
the CLI resolve 5.9.3, so adding @sentry/api resolved a second copy with
other peers. Using ^5.9.3, as the CLI does, keeps one copy.

TypeScript 5.9's DOM BodyInit takes only ArrayBuffer-backed views, so
the cache policy now says so when it builds a Response from a decoded
body. No runtime change.
sdkClient gives a @sentry/api operation a client that sends one GET to
an expected path through the core's request, and rejects any other
target or method. listPage walks a list endpoint up to a limit, asking
for exactly the rows still missing, and validates each page with the
host's Standard Schema. Both are the MCP's sdkClient and sdkListPage,
moved as is apart from validation.
endpoints/organizations in toolkit-core has getAuthenticatedUser,
listOrganizations, and getOrganization. Each takes an ApiTarget (the
request core and an origin) and the host's Standard Schema, and returns
that schema's output. SentryApiService calls them with its Zod schemas
and keeps choosing the host: the control host for the current user and
the organization list on public SaaS.

Its sdkClient and the teams and projects lists now use the core's
sdkClient and listPage; its own copies are deleted, and so is
getAuthenticatedUser's unused options argument.

A response that fails its schema in these calls now throws the shared
ApiResponseValidationError instead of a ZodError. Both reach the tool
error handler as unexpected errors.
@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sentry-local Ready Ready Preview Oct 29, 2026 9:56pm UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Oct 29, 2026
Base automatically changed from feat/shared-api-client-core to main October 29, 2026 21:44
Comment thread packages/toolkit-core/package.json Resolved
main gained #1737 as a squash commit whose tree matches this branch's
copy of the request core (fc6aa08), so every conflict resolves to this
branch's side and the merged tree is unchanged.
Instead of pinning ^5.9.3 in toolkit-core alone, the pnpm catalog moves
from ^5.8.3 to ^5.9.3, and toolkit-core and the CLI go back to
`catalog:`. Every workspace package now resolves the same TypeScript
5.9.3, and 5.8.3 drops out of the lockfile.
@BYK
BYK enabled auto-merge (squash) October 29, 2026 21:54
@BYK
BYK merged commit 5a47bdf into main Oct 29, 2026
40 of 43 checks passed
@BYK
BYK deleted the feat/shared-api-client-organizations branch October 29, 2026 22:07
sentry-junior Bot added a commit that referenced this pull request Nov 3, 2026
…1820

Co-Authored-By: Claude <noreply@anthropic.com>
BYK added a commit that referenced this pull request Nov 3, 2026
#1828)

Task 8.4 of the shared API client plan
([#1669](#1669)): the next
domain after organizations (#1741).

This moves the team and project endpoints from `SentryApiService` into
`toolkit-core`'s `api-client/endpoints/`. `SentryApiService` now calls
them with its Zod schemas. The code moves as is.

**Changes**
- `endpoints/teams` adds `listTeams` (on `@sentry/api`'s
`listOrganizationTeams`) and `createTeam`.
- `endpoints/projects` adds `listProjects` (on
`listOrganizationProjects`), `getProject`, `createProject`,
`updateProject`, and the project's teams: `listProjectTeams`,
`addProjectTeam`, and `removeProjectTeam`.
- Writes go through the request core, since the core's `sdkClient` only
sends GETs. `getProject` and `listProjectTeams` keep their hand-built
requests. `@sentry/api` has operations for both (`getProject`,
`listProjectTeams`), but moving them onto those operations is a separate
change.
- The core gains `nextPageCursor(response)`. `listPage` and
`listProjectTeams` use it, and the next domains will too.
- The endpoint tests share a scripted-transport helper in
`toolkit-core/src/test-utils/`. That folder is left out of the browser
typecheck, and `organizations.test.ts` uses the helper now as well.

**One change in behaviour:** as with #1741, a response that fails its
schema in these calls now throws the shared `ApiResponseValidationError`
instead of a `ZodError`. Both still reach the tool error handler as
unexpected errors.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK pushed a commit that referenced this pull request Nov 3, 2026
#1834)

Task 8.4 of the shared API client plan
([#1669](#1669)). This is
stacked on #1828.

This moves the project setup endpoints from `SentryApiService` into
`toolkit-core`'s `api-client/endpoints/`. `SentryApiService` calls them
with its Zod schemas. The code moves as is.

- `endpoints/client-keys`: `listClientKeys`, `createClientKey`, and
`updateClientKey`. `updateClientKey` still sends only the fields that
are set, and keeps an explicit `rateLimit: null`.
`SentryApiService.updateClientKey` now takes the shared
`UpdateClientKeyParams` type, and its local request types are deleted.
- `endpoints/repositories`: `listRepositories`, which walks every page,
and `linkProjectRepository`, which sends the bulk code-mapping POST with
one root mapping.
- `endpoints/organizations` gains `updateAgenticOnboardingStatus`.

`@sentry/api` has no operation for `POST
/organizations/{org}/onboarding/agent/status/`, so that wrapper will
stay hand-written.

**One change in behaviour:** as with #1741, a response that fails its
schema now throws the shared `ApiResponseValidationError` instead of a
`ZodError`.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
BYK pushed a commit that referenced this pull request Nov 3, 2026
Task 8.4 of the shared API client plan
([#1669](#1669)). This is
stacked on #1834.

This moves the dashboard and release endpoints from `SentryApiService`
into `toolkit-core`'s `api-client/endpoints/`. `SentryApiService` calls
them with its Zod schemas. The code moves as is.

- `endpoints/dashboards`: `listDashboards` (one page, with the next
cursor) and `getDashboard`.
- `endpoints/releases`: `listReleases`, `getRelease`,
`listReleaseDeploys`, `listReleaseCommits`, and `getEventCommitters`.
With a project, release reads still go to the project's endpoints,
otherwise to the organization's.
- `api-path` gains `withQuery`, for the path-plus-optional-query pattern
the wrappers repeat. `getOrganization` and `getProject` use it too; the
URLs they send don't change.

`@sentry/api` has no operation for the project-scoped release details
route or for event committers, so those wrappers stay hand-written.

**One change in behaviour:** as with #1741, a response that fails its
schema now throws the shared `ApiResponseValidationError` instead of a
`ZodError`.

Checked with `pnpm run tsc`, `pnpm run lint`, `pnpm run docs:check`, and
the toolkit-core, mcp-core, mcp-server bundle, and mcp-cloudflare tests.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 3, 2026
…1839)

Task 8.4 of the shared API client plan
([#1669](#1669)). This is
stacked on #1838.

This moves the cron monitor endpoints and the time range helpers from
`SentryApiService` into `toolkit-core`'s shared API client.
`SentryApiService` calls them with its Zod schemas. The code moves as
is.

- `api-client/time-params`: `normalizeStatsPeriod`, `parseStatsPeriod`,
`applyTimeParams` (`statsPeriod` or a `start`/`end` pair), and
`applyStatsMixinTimeParams` (the `since`/`until` seconds `StatsMixin`
endpoints read). It rejects a range that sets both a period and dates,
or only one end. `defaultStatsPeriod` holds the check-in list's "24
hours unless a range is given" default, which the uptime-check list
reuses in the next PR. Most other `SentryApiService` methods still call
these helpers; they now import them from `toolkit-core`.
- `endpoints/monitors`: `listMonitors`, `getMonitor` (organization or
project route), `deleteMonitorEnvironment` (a 404 still counts as
already deleted), `listMonitorCheckIns`, and `getMonitorStats`.

`@sentry/api` has no operation for monitor stats or for deleting a
monitor environment, so those wrappers stay hand-written.

**One change in behaviour:** as with #1741, a response that fails its
schema now throws the shared `ApiResponseValidationError` instead of a
`ZodError`.

Checked with `pnpm run tsc`, `pnpm run lint`, `pnpm run docs:check`, and
the toolkit-core, mcp-core, mcp-server bundle, and mcp-cloudflare tests.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 3, 2026
Task 8.4 of the shared API client plan
([#1669](#1669)). This is
stacked on #1839.

This moves the uptime monitor endpoints from `SentryApiService` into
`toolkit-core`'s `api-client/endpoints/uptime-monitors`.
`SentryApiService` calls them with its Zod schemas. The code moves as
is.

- `listUptimeMonitors` takes a numeric `projectId`, since the endpoint
filters by ID. `SentryApiService` still looks the project slug up first.
- `getUptimeMonitor` and `listUptimeMonitorChecks`. The checks list
still defaults to 24 hours unless a range is given, through
`defaultStatsPeriod` from #1839.
- `createUptimeMonitor` and `updateUptimeMonitor` share one field list,
so the body still holds every field that is set (an explicit `null`
included), in the same order as before.
- `deleteUptimeMonitor` still counts a 404 as already deleted.

`@sentry/api` has no uptime operations at all, so these wrappers stay
hand-written.

**One change in behaviour:** as with #1741, a response that fails its
schema now throws the shared `ApiResponseValidationError` instead of a
`ZodError`.

Checked with `pnpm run tsc`, `pnpm run lint`, `pnpm run docs:check`, and
the toolkit-core, mcp-core, mcp-server bundle, and mcp-cloudflare tests
(run on the top of the stack, the replay branch at `73bfc0b3`, before
the later rebases), plus the uptime tool tests on this commit.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 3, 2026
Task 8.4 of the shared API client plan
([#1669](#1669)). This is
stacked on #1840.

This moves the trace, profiling, and preprod snapshot endpoints from
`SentryApiService` into `toolkit-core`'s `api-client/endpoints/`.
`SentryApiService` calls them with its Zod schemas. The code moves as
is.

- `endpoints/traces`: `getTraceMeta` and `getTrace`, with the same
defaults (`14d`, `limit=1000`, `project=-1`).
- `endpoints/profiling`: `getFlamegraph` (still escapes the transaction
name), `getTransactionProfile`, and `getProfileChunks`.
`SentryApiService.getProfileChunk` keeps the MCP-only shaping: it throws
`ApiNotFoundError` when there are no chunks and backfills `profiler_id`.
- `endpoints/snapshots`: `getSnapshot`, `getSnapshotImage`, and
`getLatestBaseSnapshot`. They return the body unvalidated, as before.
`fetchImageByUrl` stays in `SentryApiService`, since it isn't an
endpoint.

`@sentry/api` has operations for all of these routes. The wrappers still
build their requests by hand so the change stays a move. Switching them
to the operations can follow.

**Intentional changes:**

- As with #1741, a response that fails its schema now throws the shared
`ApiResponseValidationError` instead of a `ZodError`.
- `getSnapshot` with `compactMetadata: false` no longer sends a trailing
`?` with an empty query.
- The `ApiNotFoundError` for an empty chunk list carries the validated
body as `responseBody`, not the raw one.

Checked with `pnpm run tsc`, `pnpm run lint`, `pnpm run docs:check`, and
the toolkit-core, mcp-core, mcp-server bundle, and mcp-cloudflare tests
on the top of the stack, plus the trace, profile, and snapshot tool
tests on this commit.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 3, 2026
Task 8.4 of the shared API client plan
([#1669](#1669)). This is
stacked on #1841 and is the last PR in the stack.

This moves the replay endpoints from `SentryApiService` into
`toolkit-core`'s `api-client/endpoints/replays`. `SentryApiService`
calls them with its Zod schemas. The code moves as is.

- `searchReplays` keeps the same query: `query`, `per_page`, `project`,
`sort`, repeated `environment` and `field`, and the time range.
- `getReplay` and `listIssueReplayIds`. `SentryApiService` still unwraps
the `{ data }` envelope and picks the issue's entry from the keyed
replay-count result.
- `visitReplayRecordingSegments` still pages 100 segments at a time,
follows the `Link` cursor, and stops at `maxSegments`, with `truncated`
set the same way as before. It reads the cursor with the shared
`nextPageCursor`, which parses the header the same way the old
`getNextCursor` did.
- `docs/contributing/api-patterns.md` gets a short "Where Endpoint Code
Lives" section that describes the pattern this stack follows.

**One change in behaviour:** as with #1741, a response that fails its
schema now throws the shared `ApiResponseValidationError` instead of a
`ZodError`.

Checked with `pnpm run tsc`, `pnpm run lint`, `pnpm run docs:check`, and
the toolkit-core, mcp-core, mcp-server bundle, and mcp-cloudflare tests
on this branch before the last rebase. `pnpm run tsc` passes again after
rebasing onto the current main.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Ab061171f2ec2e6cf6f09a6828cadc9fa/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Burak Yigit Kaya <126780+BYK@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — e4ce5c40 Deployed Oct 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant