Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 63 additions & 69 deletions packages/cli/src/types/sentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import type {
OrgReleaseResponse as SdkReleaseResponse,
BaseTeam as SdkTeam,
} from "@sentry/api";
import type {
SentryIssueIdentity,
SentryNamedResource,
} from "@sentry/toolkit-core/resource-identity";
import {
vBaseTeam,
vEventAttachmentDetailsResponse,
Expand Down Expand Up @@ -71,15 +75,13 @@ import {
* Organization.flags.disable_member_project_creation is set — project creation
* requires org:write scope or team:admin on the target team.
*/
export type SentryOrganization = Partial<SdkOrganizationList[number]> & {
id: string;
slug: string;
name: string;
/** False when org admins have restricted project creation to owners/managers/team-admins. Default for new orgs. */
allowMemberProjectCreation?: boolean;
/** The authenticated user's role in this org ("member", "admin", "manager", "owner"). */
orgRole?: string;
};
export type SentryOrganization = Partial<SdkOrganizationList[number]> &
SentryNamedResource<string> & {
/** False when org admins have restricted project creation to owners/managers/team-admins. Default for new orgs. */
allowMemberProjectCreation?: boolean;
/** The authenticated user's role in this org ("member", "admin", "manager", "owner"). */
orgRole?: string;
};

// Project

Expand All @@ -93,35 +95,33 @@ type SdkProjectListItem = SdkProjectList[number];
* The `organization` field is present in detail responses but absent in list responses,
* so it is declared as an optional extension.
*/
export type SentryProject = Partial<SdkProjectListItem> & {
id: string;
slug: string;
name: string;
/**
* Organization context (present in detail responses, absent in list).
*
* `name` is optional because `getProject()` passes `?collapse=organization`
* to skip full-org serialization on the server (~400-500ms faster). The
* collapsed payload only carries `{id, slug}`. Callers needing a display
* name should use `resolveOrgDisplayName()` which falls back to the
* cached organizations list.
*/
organization?: {
id: string;
slug: string;
name?: string;
[key: string]: unknown;
export type SentryProject = Partial<SdkProjectListItem> &
SentryNamedResource<string> & {
/**
* Organization context (present in detail responses, absent in list).
*
* `name` is optional because `getProject()` passes `?collapse=organization`
* to skip full-org serialization on the server (~400-500ms faster). The
* collapsed payload only carries `{id, slug}`. Callers needing a display
* name should use `resolveOrgDisplayName()` which falls back to the
* cached organizations list.
*/
organization?: {
id: string;
slug: string;
name?: string;
[key: string]: unknown;
};
/**
* Project status (returned by API but not in the OpenAPI spec).
*
* Overlay convention: the SDK type (`SdkProjectListItem`) carries every
* documented field; this overlay adds ONLY fields the API returns but the
* spec omits, each a backend `@extend_schema` candidate. Keep it minimal —
* do not restate fields the SDK already types.
*/
status?: string;
};
/**
* Project status (returned by API but not in the OpenAPI spec).
*
* Overlay convention: the SDK type (`SdkProjectListItem`) carries every
* documented field; this overlay adds ONLY fields the API returns but the
* spec omits, each a backend `@extend_schema` candidate. Keep it minimal —
* do not restate fields the SDK already types.
*/
status?: string;
};

// Issue Constants

Expand Down Expand Up @@ -166,34 +166,32 @@ export type IssueLevel = (typeof ISSUE_LEVELS)[number];
* The `metadata` field is overridden from the SDK's discriminated union to a single
* object with all optional fields, matching how the API actually returns data.
*/
export type SentryIssue = Omit<Partial<SdkIssueDetail>, "metadata"> & {
id: string;
shortId: string;
title: string;
/** Issue metadata (value, filename, function, etc.) */
metadata?: {
value?: string;
type?: string;
filename?: string;
function?: string;
title?: string;
display_title_with_tree_label?: boolean;
[key: string]: unknown;
export type SentryIssue = Omit<Partial<SdkIssueDetail>, "metadata"> &
SentryIssueIdentity<string> & {
/** Issue metadata (value, filename, function, etc.) */
metadata?: {
value?: string;
type?: string;
filename?: string;
function?: string;
title?: string;
display_title_with_tree_label?: boolean;
[key: string]: unknown;
};
/** Issue substatus (not in OpenAPI spec) */
substatus?: string | null;
/** Issue priority (not in OpenAPI spec) */
priority?: string;
/** Whether the issue is unhandled (not in OpenAPI spec) */
isUnhandled?: boolean;
/** Platform of the issue (not in OpenAPI spec) */
platform?: string;
/**
* Seer AI fixability score (0-1). Higher = easier to fix automatically.
* `null` when Seer has not analyzed this issue; absent when the org has Seer disabled.
*/
seerFixabilityScore?: number | null;
};
/** Issue substatus (not in OpenAPI spec) */
substatus?: string | null;
/** Issue priority (not in OpenAPI spec) */
priority?: string;
/** Whether the issue is unhandled (not in OpenAPI spec) */
isUnhandled?: boolean;
/** Platform of the issue (not in OpenAPI spec) */
platform?: string;
/**
* Seer AI fixability score (0-1). Higher = easier to fix automatically.
* `null` when Seer has not analyzed this issue; absent when the org has Seer disabled.
*/
seerFixabilityScore?: number | null;
};

/**
* Valibot schema describing the key fields of a {@link SentryIssue} for JSON output.
Expand Down Expand Up @@ -1416,11 +1414,7 @@ export const SentryTeamSchema = looseObject({
* schema and type are allowed to diverge: the schema curates a user-facing
* subset of fields, the type follows the SDK's structural superset.
*/
export type SentryTeam = Partial<SdkTeam> & {
id: string;
slug: string;
name: string;
};
export type SentryTeam = Partial<SdkTeam> & SentryNamedResource<string>;

// Product Trials

Expand Down
12 changes: 8 additions & 4 deletions packages/mcp-core/src/api-client/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
* }
* ```
*/
import type {
SentryIssueIdentity,
SentryNamedResource,
} from "@sentry/toolkit-core/resource-identity";
import { z } from "zod";

/**
Expand Down Expand Up @@ -88,7 +92,7 @@ export const OrganizationSchema = z
features: z.array(z.string()).optional(),
hideAiFeatures: z.boolean().optional(),
})
.passthrough();
.passthrough() satisfies z.ZodType<SentryNamedResource>;

export const OrganizationListSchema = z.array(OrganizationSchema);

Expand All @@ -98,7 +102,7 @@ export const TeamSchema = z
slug: z.string(),
name: z.string(),
})
.passthrough();
.passthrough() satisfies z.ZodType<SentryNamedResource>;

export const TeamListSchema = z.array(TeamSchema);

Expand All @@ -113,7 +117,7 @@ export const ProjectSchema = z
hasLogs: z.boolean().optional(),
firstTransactionEvent: z.boolean().optional(),
})
.passthrough();
.passthrough() satisfies z.ZodType<SentryNamedResource>;

export const ProjectListSchema = z.array(ProjectSchema);

Expand Down Expand Up @@ -930,7 +934,7 @@ export const IssueSchema = z
.passthrough()
.transform((issue) =>
Object.assign(issue, { shortId: issue.shortId ?? String(issue.id) }),
);
) satisfies z.ZodType<SentryIssueIdentity>;

export const IssueListSchema = z.array(IssueSchema);

Expand Down
5 changes: 5 additions & 0 deletions packages/toolkit-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ credential-safe SaaS HTTPS origins, encodes API path identifiers, and deduplicat
in-flight region lookups. Each product retains its own credential storage,
regional cache identity and persistence, host allowlists, routing, polling
deadline, HTTP transport, response validation, and user-facing errors.

The `resource-identity` types describe the identifiers shared by organization,
project, team, and issue responses. CLI overlays retain SDK-derived string IDs;
MCP schemas accept numeric IDs too and check their parsed outputs against these
types. Each product keeps its own response validation.
4 changes: 4 additions & 0 deletions packages/toolkit-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
"types": "./src/region-cache.ts",
"default": "./src/region-cache.ts"
},
"./resource-identity": {
"types": "./src/resource-identity.ts",
"default": "./src/resource-identity.ts"
},
"./sentry-host": {
"types": "./src/sentry-host.ts",
"default": "./src/sentry-host.ts"
Expand Down
20 changes: 20 additions & 0 deletions packages/toolkit-core/src/resource-identity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** Sentry resource IDs may be strings or numbers across API endpoints. */
export type SentryResourceId = string | number;

/** Identifiers and display names common to organizations, projects, and teams. */
export type SentryNamedResource<
Id extends SentryResourceId = SentryResourceId,
> = {
id: Id;
slug: string;
name: string;
};

/** Identifiers and title common to issue list and detail responses. */
export type SentryIssueIdentity<
Id extends SentryResourceId = SentryResourceId,
> = {
id: Id;
shortId: string;
title: string;
};
Loading