Skip to content
Closed
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
152 changes: 152 additions & 0 deletions packages/mcp-core/src/api-client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ import {
AutofixRunStateSchema,
ClientKeyListSchema,
ClientKeySchema,
CustomInboundFilterListSchema,
CustomInboundFilterSchema,
CommitListSchema,
CommittersResponseSchema,
DashboardListSchema,
Expand Down Expand Up @@ -130,6 +132,8 @@ import type {
SearchAgentState,
ClientKey,
ClientKeyList,
CustomInboundFilter,
CustomInboundFilterList,
CommitList,
CommitterList,
Dashboard,
Expand Down Expand Up @@ -346,6 +350,13 @@ type UpdateProjectRequest = {
platform?: string;
};

type CustomInboundFilterWriteRequest = {
name?: string | null;
active?: boolean;
dataType: string;
conditions: Array<{ type: string; value: string[] }>;
};

type UpdateClientKeyRequest = {
name?: string;
isActive?: boolean;
Expand Down Expand Up @@ -2856,6 +2867,147 @@ export class SentryApiService {
return ClientKeyListSchema.parse(body);
}

/**
* Lists the custom inbound filters of a project.
*
* Source: src/sentry/api/endpoints/project_custom_inbound_filters.py
*
* @param params Query parameters
* @param params.organizationSlug Organization identifier
* @param params.projectSlug Project identifier
* @param params.limit Maximum number of filters per page
* @param params.cursor Pagination cursor from a previous call's nextCursor
* @param opts Request options
* @returns A page of custom inbound filters, plus a cursor for the next page (null once exhausted)
*/
async listCustomInboundFilters(
{
organizationSlug,
projectSlug,
limit,
cursor,
}: {
organizationSlug: string;
projectSlug: string;
limit?: number;
cursor?: string | null;
},
opts?: RequestOptions,
): Promise<{ filters: CustomInboundFilterList; nextCursor: string | null }> {
const queryParams = new URLSearchParams();
queryParams.set("per_page", String(limit ?? 25));
if (cursor) {
queryParams.set("cursor", cursor);
}
const basePath = apiPath`/projects/${organizationSlug}/${projectSlug}/custom-inbound-filters/`;
const response = await this.request(
`${basePath}?${queryParams.toString()}`,
undefined,
opts,
);
const body = await this.parseJsonResponse(response);
return {
filters: CustomInboundFilterListSchema.parse(body),
nextCursor: getNextCursor(response.headers.get("link")),
};
}

/**
* Creates a custom inbound filter in a project.
*
* Source: src/sentry/api/endpoints/project_custom_inbound_filters.py
*/
async createCustomInboundFilter(
{
organizationSlug,
projectSlug,
...filter
}: {
organizationSlug: string;
projectSlug: string;
} & CustomInboundFilterWriteRequest,
opts?: RequestOptions,
): Promise<CustomInboundFilter> {
const body = await this.requestJSON(
apiPath`/projects/${organizationSlug}/${projectSlug}/custom-inbound-filters/`,
{
method: "POST",
body: JSON.stringify(filter),
},
opts,
);
return CustomInboundFilterSchema.parse(body);
}

/**
* Updates a custom inbound filter. Only the provided fields change.
*
* Source: src/sentry/api/endpoints/project_custom_inbound_filters.py
*/
async updateCustomInboundFilter(
{
organizationSlug,
projectSlug,
filterId,
...filter
}: {
organizationSlug: string;
projectSlug: string;
filterId: string;
} & Partial<CustomInboundFilterWriteRequest>,
opts?: RequestOptions,
): Promise<CustomInboundFilter> {
const updateData: Partial<CustomInboundFilterWriteRequest> = {};
if (filter.name !== undefined) {
updateData.name = filter.name;
}
if (filter.active !== undefined) {
updateData.active = filter.active;
}
if (filter.dataType !== undefined) {
updateData.dataType = filter.dataType;
}
if (filter.conditions !== undefined) {
updateData.conditions = filter.conditions;
}
const body = await this.requestJSON(
apiPath`/projects/${organizationSlug}/${projectSlug}/custom-inbound-filters/${filterId}/`,
{
method: "PUT",
body: JSON.stringify(updateData),
},
opts,
);
return CustomInboundFilterSchema.parse(body);
}

/**
* Deletes a custom inbound filter.
*
* Source: src/sentry/api/endpoints/project_custom_inbound_filters.py
*/
async deleteCustomInboundFilter(
{
organizationSlug,
projectSlug,
filterId,
}: {
organizationSlug: string;
projectSlug: string;
filterId: string;
},
opts?: RequestOptions,
): Promise<void> {
// Treat 404 as success so repeated deletes are idempotent.
await this.request(
apiPath`/projects/${organizationSlug}/${projectSlug}/custom-inbound-filters/${filterId}/`,
{
method: "DELETE",
},
{ ...opts, allowStatuses: [404] },
);
}

/**
* Lists releases for an organization or specific project.
*
Expand Down
19 changes: 19 additions & 0 deletions packages/mcp-core/src/api-client/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,25 @@ export const ClientKeySchema = z

export const ClientKeyListSchema = z.array(ClientKeySchema);

export const CustomInboundFilterConditionSchema = z.object({
type: z.string(),
value: z.array(z.string()),
});

export const CustomInboundFilterSchema = z
.object({
id: z.union([z.string(), z.number()]),
name: z.string().nullable(),
active: z.boolean(),
dataType: z.string(),
conditions: z.array(CustomInboundFilterConditionSchema),
dateCreated: z.string().datetime().nullable(),
dateUpdated: z.string().datetime().nullable(),
})
.passthrough();

export const CustomInboundFilterListSchema = z.array(CustomInboundFilterSchema);

const ReleaseProjectSchema = z
.object({
id: z.union([z.string(), z.number()]),
Expand Down
6 changes: 6 additions & 0 deletions packages/mcp-core/src/api-client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ import type {
SearchAgentStateSchema,
ClientKeyListSchema,
ClientKeySchema,
CustomInboundFilterListSchema,
CustomInboundFilterSchema,
CommitListSchema,
CommitSchema,
CommittersResponseSchema,
Expand Down Expand Up @@ -278,6 +280,10 @@ export type OrganizationEnvironmentList = z.infer<
>;
export type TagList = z.infer<typeof TagListSchema>;
export type ClientKeyList = z.infer<typeof ClientKeyListSchema>;
export type CustomInboundFilter = z.infer<typeof CustomInboundFilterSchema>;
export type CustomInboundFilterList = z.infer<
typeof CustomInboundFilterListSchema
>;

// Dashboard types
export type Dashboard = z.infer<typeof DashboardSchema>;
Expand Down
3 changes: 3 additions & 0 deletions packages/mcp-core/src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,9 @@ describe("buildServer", () => {
["remove_team_from_project", ["project-management"]],
["create_dsn", ["project-management"]],
["find_dsns", ["project-management"]],
["create_custom_inbound_filter", ["project-management"]],
["update_custom_inbound_filter", ["project-management"]],
["delete_custom_inbound_filter", ["project-management"]],
] as const) {
const grantedServer = buildServer({
context: {
Expand Down
Loading
Loading