chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
import type { SentryGetEventParams, SentryGetEventResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getEventTool: ToolConfig<SentryGetEventParams, SentryGetEventResponse> = {
|
||||
id: 'sentry_events_get',
|
||||
name: 'Get Event',
|
||||
description:
|
||||
'Retrieve detailed information about a specific Sentry event by its ID. Returns complete event details including stack traces, breadcrumbs, context, and user information.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
projectSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the project (e.g., "my-project")',
|
||||
},
|
||||
eventId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique ID of the event to retrieve (e.g., "abc123def456")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://sentry.io/api/0/projects/${params.organizationSlug}/${params.projectSlug}/events/${params.eventId}/`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const event = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
event: {
|
||||
id: event.id,
|
||||
eventID: event.eventID,
|
||||
projectID: event.projectID,
|
||||
groupID: event.groupID,
|
||||
message: event.message || '',
|
||||
title: event.title,
|
||||
location: event.location ?? null,
|
||||
culprit: event.culprit ?? null,
|
||||
dateCreated: event.dateCreated,
|
||||
dateReceived: event.dateReceived,
|
||||
user: event.user
|
||||
? {
|
||||
id: event.user.id,
|
||||
email: event.user.email,
|
||||
username: event.user.username,
|
||||
ipAddress: event.user.ip_address,
|
||||
name: event.user.name,
|
||||
}
|
||||
: null,
|
||||
tags:
|
||||
event.tags?.map((tag: any) => ({
|
||||
key: tag.key,
|
||||
value: tag.value,
|
||||
})) || [],
|
||||
contexts: event.contexts || {},
|
||||
platform: event.platform ?? null,
|
||||
type: event.type ?? null,
|
||||
metadata: {
|
||||
type: event.metadata?.type || null,
|
||||
value: event.metadata?.value || null,
|
||||
function: event.metadata?.function || null,
|
||||
},
|
||||
entries: event.entries || [],
|
||||
errors: event.errors || [],
|
||||
dist: event.dist ?? null,
|
||||
fingerprints: event.fingerprints || [],
|
||||
size: event.size ?? null,
|
||||
release: event.release ?? null,
|
||||
sdk: event.sdk
|
||||
? {
|
||||
name: event.sdk.name,
|
||||
version: event.sdk.version,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
event: {
|
||||
type: 'object',
|
||||
description: 'Detailed information about the Sentry event',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique event ID' },
|
||||
eventID: { type: 'string', description: 'Event identifier' },
|
||||
projectID: { type: 'string', description: 'Project ID' },
|
||||
groupID: { type: 'string', description: 'Issue group ID this event belongs to' },
|
||||
message: { type: 'string', description: 'Event message' },
|
||||
title: { type: 'string', description: 'Event title' },
|
||||
location: { type: 'string', description: 'Location information', optional: true },
|
||||
culprit: {
|
||||
type: 'string',
|
||||
description: 'Function or location that caused the event',
|
||||
optional: true,
|
||||
},
|
||||
dateCreated: { type: 'string', description: 'When the event was created (ISO timestamp)' },
|
||||
dateReceived: {
|
||||
type: 'string',
|
||||
description: 'When Sentry received the event (ISO timestamp)',
|
||||
},
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'User information associated with the event',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
email: { type: 'string', description: 'User email' },
|
||||
username: { type: 'string', description: 'Username' },
|
||||
ipAddress: { type: 'string', description: 'IP address' },
|
||||
name: { type: 'string', description: 'User display name' },
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Tags associated with the event',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
key: { type: 'string', description: 'Tag key' },
|
||||
value: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
contexts: {
|
||||
type: 'object',
|
||||
description: 'Additional context data (device, OS, browser, etc.)',
|
||||
},
|
||||
platform: {
|
||||
type: 'string',
|
||||
description: 'Platform where the event occurred',
|
||||
optional: true,
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Event type (error, transaction, etc.)',
|
||||
optional: true,
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Error metadata',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Type of error (e.g., TypeError, ValueError)' },
|
||||
value: { type: 'string', description: 'Error message or value' },
|
||||
function: { type: 'string', description: 'Function where the error occurred' },
|
||||
},
|
||||
},
|
||||
entries: {
|
||||
type: 'array',
|
||||
description: 'Event entries including exception, breadcrumbs, and request data',
|
||||
},
|
||||
errors: {
|
||||
type: 'array',
|
||||
description: 'Processing errors that occurred',
|
||||
},
|
||||
dist: { type: 'string', description: 'Distribution identifier', optional: true },
|
||||
fingerprints: {
|
||||
type: 'array',
|
||||
description: 'Fingerprints used for grouping events',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
size: { type: 'number', description: 'Event size in bytes', optional: true },
|
||||
release: {
|
||||
type: 'object',
|
||||
description: 'Release associated with the event (version, dateCreated)',
|
||||
optional: true,
|
||||
},
|
||||
|
||||
sdk: {
|
||||
type: 'object',
|
||||
description: 'SDK information',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'SDK name' },
|
||||
version: { type: 'string', description: 'SDK version' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
import type { SentryListEventsParams, SentryListEventsResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listEventsTool: ToolConfig<SentryListEventsParams, SentryListEventsResponse> = {
|
||||
id: 'sentry_events_list',
|
||||
name: 'List Events',
|
||||
description:
|
||||
'List events from a Sentry project. Can be filtered by issue ID, query, or time period. Returns event details including context, tags, and user information.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
projectSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the project to list events from (e.g., "my-project")',
|
||||
},
|
||||
issueId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter events by a specific issue ID (e.g., "12345")',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search query to filter events. Only applied when an Issue ID is provided (the issue events endpoint); the project events endpoint ignores it. Supports Sentry search syntax (e.g., "user.email:*@example.com")',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor for retrieving next page of results',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of events to return per page (max: 100)',
|
||||
},
|
||||
statsPeriod: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Time period to query (e.g., "24h", "7d", "14d"). Cannot be combined with absolute start/end.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
let baseUrl: string
|
||||
|
||||
if (params.issueId && params.issueId !== null && params.issueId !== '') {
|
||||
baseUrl = `https://sentry.io/api/0/organizations/${params.organizationSlug}/issues/${params.issueId}/events/`
|
||||
} else {
|
||||
baseUrl = `https://sentry.io/api/0/projects/${params.organizationSlug}/${params.projectSlug}/events/`
|
||||
}
|
||||
|
||||
const isIssueScoped = Boolean(
|
||||
params.issueId && params.issueId !== null && params.issueId !== ''
|
||||
)
|
||||
const queryParams: string[] = []
|
||||
|
||||
if (isIssueScoped && params.query && params.query !== null && params.query !== '') {
|
||||
queryParams.push(`query=${encodeURIComponent(params.query)}`)
|
||||
}
|
||||
|
||||
if (params.cursor && params.cursor !== null && params.cursor !== '') {
|
||||
queryParams.push(`cursor=${encodeURIComponent(params.cursor)}`)
|
||||
}
|
||||
|
||||
if (params.limit && params.limit !== null) {
|
||||
queryParams.push(`per_page=${Number(params.limit)}`)
|
||||
}
|
||||
|
||||
if (params.statsPeriod && params.statsPeriod !== null && params.statsPeriod !== '') {
|
||||
queryParams.push(`statsPeriod=${encodeURIComponent(params.statsPeriod)}`)
|
||||
}
|
||||
|
||||
queryParams.push('full=1')
|
||||
|
||||
return `${baseUrl}?${queryParams.join('&')}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const linkHeader = response.headers.get('Link')
|
||||
let nextCursor: string | undefined
|
||||
let hasMore = false
|
||||
|
||||
if (linkHeader) {
|
||||
const nextMatch = linkHeader.match(
|
||||
/<[^>]*cursor=([^&>]+)[^>]*>;\s*rel="next";\s*results="true"/
|
||||
)
|
||||
if (nextMatch) {
|
||||
nextCursor = decodeURIComponent(nextMatch[1])
|
||||
hasMore = true
|
||||
}
|
||||
}
|
||||
|
||||
const events = Array.isArray(data) ? data : []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
events: events.map((event: any) => ({
|
||||
id: event.id,
|
||||
eventID: event.eventID,
|
||||
projectID: event.projectID,
|
||||
groupID: event.groupID,
|
||||
message: event.message || '',
|
||||
title: event.title,
|
||||
location: event.location ?? null,
|
||||
culprit: event.culprit ?? null,
|
||||
dateCreated: event.dateCreated,
|
||||
dateReceived: event.dateReceived,
|
||||
user: event.user
|
||||
? {
|
||||
id: event.user.id,
|
||||
email: event.user.email,
|
||||
username: event.user.username,
|
||||
ipAddress: event.user.ip_address,
|
||||
name: event.user.name,
|
||||
}
|
||||
: null,
|
||||
tags:
|
||||
event.tags?.map((tag: any) => ({
|
||||
key: tag.key,
|
||||
value: tag.value,
|
||||
})) || [],
|
||||
contexts: event.contexts || {},
|
||||
platform: event.platform ?? null,
|
||||
type: event.type ?? null,
|
||||
metadata: {
|
||||
type: event.metadata?.type || null,
|
||||
value: event.metadata?.value || null,
|
||||
function: event.metadata?.function || null,
|
||||
},
|
||||
entries: event.entries || [],
|
||||
errors: event.errors || [],
|
||||
dist: event.dist ?? null,
|
||||
fingerprints: event.fingerprints || [],
|
||||
size: event.size ?? null,
|
||||
release: event.release ?? null,
|
||||
sdk: event.sdk
|
||||
? {
|
||||
name: event.sdk.name,
|
||||
version: event.sdk.version,
|
||||
}
|
||||
: null,
|
||||
})),
|
||||
metadata: {
|
||||
nextCursor,
|
||||
hasMore,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
events: {
|
||||
type: 'array',
|
||||
description: 'List of Sentry events',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique event ID' },
|
||||
eventID: { type: 'string', description: 'Event identifier' },
|
||||
projectID: { type: 'string', description: 'Project ID' },
|
||||
groupID: { type: 'string', description: 'Issue group ID' },
|
||||
message: { type: 'string', description: 'Event message' },
|
||||
title: { type: 'string', description: 'Event title' },
|
||||
location: { type: 'string', description: 'Location information', optional: true },
|
||||
culprit: {
|
||||
type: 'string',
|
||||
description: 'Function or location that caused the event',
|
||||
optional: true,
|
||||
},
|
||||
dateCreated: {
|
||||
type: 'string',
|
||||
description: 'When the event was created (ISO timestamp)',
|
||||
},
|
||||
dateReceived: {
|
||||
type: 'string',
|
||||
description: 'When Sentry received the event (ISO timestamp)',
|
||||
},
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'User information associated with the event',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
email: { type: 'string', description: 'User email' },
|
||||
username: { type: 'string', description: 'Username' },
|
||||
ipAddress: { type: 'string', description: 'IP address' },
|
||||
name: { type: 'string', description: 'User display name' },
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Tags associated with the event',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
key: { type: 'string', description: 'Tag key' },
|
||||
value: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
contexts: { type: 'object', description: 'Additional context data (device, OS, etc.)' },
|
||||
platform: {
|
||||
type: 'string',
|
||||
description: 'Platform where the event occurred',
|
||||
optional: true,
|
||||
},
|
||||
type: { type: 'string', description: 'Event type', optional: true },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Error metadata',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Type of error (e.g., TypeError)' },
|
||||
value: { type: 'string', description: 'Error message or value' },
|
||||
function: { type: 'string', description: 'Function where the error occurred' },
|
||||
},
|
||||
},
|
||||
entries: { type: 'array', description: 'Event entries (exception, breadcrumbs, etc.)' },
|
||||
errors: { type: 'array', description: 'Processing errors' },
|
||||
dist: { type: 'string', description: 'Distribution identifier', optional: true },
|
||||
fingerprints: { type: 'array', description: 'Fingerprints for grouping' },
|
||||
size: { type: 'number', description: 'Event size in bytes', optional: true },
|
||||
release: {
|
||||
type: 'object',
|
||||
description: 'Release associated with the event (version, dateCreated)',
|
||||
optional: true,
|
||||
},
|
||||
sdk: {
|
||||
type: 'object',
|
||||
description: 'SDK information',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'SDK name' },
|
||||
version: { type: 'string', description: 'SDK version' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Pagination metadata',
|
||||
properties: {
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for the next page of results (if available)',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results available',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export { getEventTool } from './events_get'
|
||||
export { listEventsTool } from './events_list'
|
||||
export { getIssueTool } from './issues_get'
|
||||
export { listIssuesTool } from './issues_list'
|
||||
export { updateIssueTool } from './issues_update'
|
||||
export { createProjectTool } from './projects_create'
|
||||
export { getProjectTool } from './projects_get'
|
||||
export { listProjectsTool } from './projects_list'
|
||||
export { updateProjectTool } from './projects_update'
|
||||
export { createReleaseTool } from './releases_create'
|
||||
export { createDeployTool } from './releases_deploy'
|
||||
export { listReleasesTool } from './releases_list'
|
||||
export { listTeamsTool } from './teams_list'
|
||||
export type * from './types'
|
||||
@@ -0,0 +1,183 @@
|
||||
import type { SentryGetIssueParams, SentryGetIssueResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getIssueTool: ToolConfig<SentryGetIssueParams, SentryGetIssueResponse> = {
|
||||
id: 'sentry_issues_get',
|
||||
name: 'Get Issue',
|
||||
description:
|
||||
'Retrieve detailed information about a specific Sentry issue by its ID. Returns complete issue details including metadata, tags, and statistics.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
issueId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique ID of the issue to retrieve (e.g., "12345")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://sentry.io/api/0/organizations/${params.organizationSlug}/issues/${params.issueId}/`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const issue = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
issue: {
|
||||
id: issue.id,
|
||||
shortId: issue.shortId,
|
||||
title: issue.title,
|
||||
culprit: issue.culprit ?? null,
|
||||
permalink: issue.permalink,
|
||||
logger: issue.logger ?? null,
|
||||
level: issue.level,
|
||||
status: issue.status,
|
||||
substatus: issue.substatus ?? null,
|
||||
priority: issue.priority ?? null,
|
||||
statusDetails: issue.statusDetails || {},
|
||||
isPublic: issue.isPublic,
|
||||
platform: issue.platform ?? null,
|
||||
project: {
|
||||
id: issue.project?.id || '',
|
||||
name: issue.project?.name || '',
|
||||
slug: issue.project?.slug || '',
|
||||
platform: issue.project?.platform || '',
|
||||
},
|
||||
type: issue.type ?? null,
|
||||
metadata: {
|
||||
type: issue.metadata?.type || null,
|
||||
value: issue.metadata?.value || null,
|
||||
function: issue.metadata?.function || null,
|
||||
},
|
||||
numComments: issue.numComments || 0,
|
||||
assignedTo: issue.assignedTo
|
||||
? {
|
||||
id: issue.assignedTo.id,
|
||||
name: issue.assignedTo.name,
|
||||
email: issue.assignedTo.email,
|
||||
}
|
||||
: null,
|
||||
isBookmarked: issue.isBookmarked,
|
||||
isSubscribed: issue.isSubscribed,
|
||||
subscriptionDetails: issue.subscriptionDetails ?? null,
|
||||
hasSeen: issue.hasSeen,
|
||||
annotations: issue.annotations || [],
|
||||
isUnhandled: issue.isUnhandled,
|
||||
count: issue.count,
|
||||
userCount: issue.userCount || 0,
|
||||
firstSeen: issue.firstSeen,
|
||||
lastSeen: issue.lastSeen,
|
||||
stats: issue.stats || {},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
issue: {
|
||||
type: 'object',
|
||||
description: 'Detailed information about the Sentry issue',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique issue ID' },
|
||||
shortId: { type: 'string', description: 'Short issue identifier' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
culprit: {
|
||||
type: 'string',
|
||||
description: 'Function or location that caused the issue',
|
||||
optional: true,
|
||||
},
|
||||
permalink: { type: 'string', description: 'Direct link to the issue in Sentry' },
|
||||
logger: {
|
||||
type: 'string',
|
||||
description: 'Logger name that reported the issue',
|
||||
optional: true,
|
||||
},
|
||||
level: { type: 'string', description: 'Severity level (error, warning, info, etc.)' },
|
||||
status: { type: 'string', description: 'Current issue status' },
|
||||
substatus: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Issue substatus (e.g., ongoing, escalating, new, archived_until_escalating)',
|
||||
optional: true,
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
description: 'Issue priority (high, medium, or low)',
|
||||
optional: true,
|
||||
},
|
||||
statusDetails: {
|
||||
type: 'object',
|
||||
description: 'Additional details about the status',
|
||||
},
|
||||
isPublic: { type: 'boolean', description: 'Whether the issue is publicly visible' },
|
||||
platform: {
|
||||
type: 'string',
|
||||
description: 'Platform where the issue occurred',
|
||||
optional: true,
|
||||
},
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'Project information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project ID' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
slug: { type: 'string', description: 'Project slug' },
|
||||
platform: { type: 'string', description: 'Project platform' },
|
||||
},
|
||||
},
|
||||
type: { type: 'string', description: 'Issue type', optional: true },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Error metadata',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Type of error (e.g., TypeError, ValueError)' },
|
||||
value: { type: 'string', description: 'Error message or value' },
|
||||
function: { type: 'string', description: 'Function where the error occurred' },
|
||||
},
|
||||
},
|
||||
numComments: { type: 'number', description: 'Number of comments on the issue' },
|
||||
assignedTo: {
|
||||
type: 'object',
|
||||
description: 'User assigned to the issue (if any)',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
name: { type: 'string', description: 'User name' },
|
||||
email: { type: 'string', description: 'User email' },
|
||||
},
|
||||
},
|
||||
isBookmarked: { type: 'boolean', description: 'Whether the issue is bookmarked' },
|
||||
isSubscribed: { type: 'boolean', description: 'Whether the user is subscribed to updates' },
|
||||
hasSeen: { type: 'boolean', description: 'Whether the user has seen this issue' },
|
||||
annotations: { type: 'array', description: 'Issue annotations' },
|
||||
isUnhandled: { type: 'boolean', description: 'Whether the issue is unhandled' },
|
||||
count: { type: 'string', description: 'Total number of occurrences' },
|
||||
userCount: { type: 'number', description: 'Number of unique users affected' },
|
||||
firstSeen: { type: 'string', description: 'When the issue was first seen (ISO timestamp)' },
|
||||
lastSeen: { type: 'string', description: 'When the issue was last seen (ISO timestamp)' },
|
||||
stats: { type: 'object', description: 'Statistical information about the issue' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
import type { SentryListIssuesParams, SentryListIssuesResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listIssuesTool: ToolConfig<SentryListIssuesParams, SentryListIssuesResponse> = {
|
||||
id: 'sentry_issues_list',
|
||||
name: 'List Issues',
|
||||
description:
|
||||
'List issues from Sentry for a specific organization and optionally a specific project. Returns issue details including status, error counts, and last seen timestamps.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
projectSlug: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter issues by numeric project ID (e.g., "4501234"). This organization-scoped endpoint requires the numeric project ID, not the project slug.',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search query to filter issues. Supports Sentry search syntax (e.g., "is:unresolved", "level:error")',
|
||||
},
|
||||
statsPeriod: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Time window for the per-issue stats series (e.g., "24h", "14d"). Note: this controls the stats returned with each issue, not which issues are returned — use the query (e.g., "age:-7d", "lastSeen:-24h") to filter results.',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor for retrieving next page of results',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of issues to return per page (max: 100)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Filter by issue status: unresolved, resolved, or ignored. The legacy "ignored"/"muted" values map to Sentry\'s current "archived" search token.',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Sort order: date, new, trends, freq, or user (default: date)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://sentry.io/api/0/organizations/${params.organizationSlug}/issues/`
|
||||
const queryParams: string[] = []
|
||||
|
||||
if (params.projectSlug && params.projectSlug !== null && params.projectSlug !== '') {
|
||||
queryParams.push(`project=${encodeURIComponent(params.projectSlug)}`)
|
||||
}
|
||||
|
||||
let searchQuery = params.query && params.query !== null ? params.query.trim() : ''
|
||||
if (params.status && params.status !== null && params.status !== '') {
|
||||
const statusToken =
|
||||
params.status === 'ignored' || params.status === 'muted' ? 'archived' : params.status
|
||||
searchQuery = searchQuery ? `${searchQuery} is:${statusToken}` : `is:${statusToken}`
|
||||
}
|
||||
queryParams.push(`query=${encodeURIComponent(searchQuery)}`)
|
||||
|
||||
if (params.statsPeriod && params.statsPeriod !== null && params.statsPeriod !== '') {
|
||||
queryParams.push(`statsPeriod=${encodeURIComponent(params.statsPeriod)}`)
|
||||
}
|
||||
|
||||
if (params.cursor && params.cursor !== null && params.cursor !== '') {
|
||||
queryParams.push(`cursor=${encodeURIComponent(params.cursor)}`)
|
||||
}
|
||||
|
||||
if (params.limit && params.limit !== null) {
|
||||
queryParams.push(`limit=${Number(params.limit)}`)
|
||||
}
|
||||
|
||||
if (params.sort && params.sort !== null && params.sort !== '') {
|
||||
const sortValue = params.sort === 'priority' ? 'trends' : params.sort
|
||||
queryParams.push(`sort=${encodeURIComponent(sortValue)}`)
|
||||
}
|
||||
|
||||
return queryParams.length > 0 ? `${baseUrl}?${queryParams.join('&')}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const linkHeader = response.headers.get('Link')
|
||||
let nextCursor: string | undefined
|
||||
let hasMore = false
|
||||
|
||||
if (linkHeader) {
|
||||
const nextMatch = linkHeader.match(
|
||||
/<[^>]*cursor=([^&>]+)[^>]*>;\s*rel="next";\s*results="true"/
|
||||
)
|
||||
if (nextMatch) {
|
||||
nextCursor = decodeURIComponent(nextMatch[1])
|
||||
hasMore = true
|
||||
}
|
||||
}
|
||||
|
||||
const issues = Array.isArray(data) ? data : []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
issues: issues.map((issue: any) => ({
|
||||
id: issue.id,
|
||||
shortId: issue.shortId,
|
||||
title: issue.title,
|
||||
culprit: issue.culprit ?? null,
|
||||
permalink: issue.permalink,
|
||||
logger: issue.logger ?? null,
|
||||
level: issue.level,
|
||||
status: issue.status,
|
||||
substatus: issue.substatus ?? null,
|
||||
priority: issue.priority ?? null,
|
||||
statusDetails: issue.statusDetails || {},
|
||||
isPublic: issue.isPublic,
|
||||
platform: issue.platform ?? null,
|
||||
project: {
|
||||
id: issue.project?.id || '',
|
||||
name: issue.project?.name || '',
|
||||
slug: issue.project?.slug || '',
|
||||
platform: issue.project?.platform || '',
|
||||
},
|
||||
type: issue.type ?? null,
|
||||
metadata: {
|
||||
type: issue.metadata?.type || null,
|
||||
value: issue.metadata?.value || null,
|
||||
function: issue.metadata?.function || null,
|
||||
},
|
||||
numComments: issue.numComments || 0,
|
||||
assignedTo: issue.assignedTo
|
||||
? {
|
||||
id: issue.assignedTo.id,
|
||||
name: issue.assignedTo.name,
|
||||
email: issue.assignedTo.email,
|
||||
}
|
||||
: null,
|
||||
isBookmarked: issue.isBookmarked,
|
||||
isSubscribed: issue.isSubscribed,
|
||||
subscriptionDetails: issue.subscriptionDetails ?? null,
|
||||
hasSeen: issue.hasSeen,
|
||||
annotations: issue.annotations || [],
|
||||
isUnhandled: issue.isUnhandled,
|
||||
count: issue.count,
|
||||
userCount: issue.userCount || 0,
|
||||
firstSeen: issue.firstSeen,
|
||||
lastSeen: issue.lastSeen,
|
||||
stats: issue.stats || {},
|
||||
})),
|
||||
metadata: {
|
||||
nextCursor,
|
||||
hasMore,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
issues: {
|
||||
type: 'array',
|
||||
description: 'List of Sentry issues',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique issue ID' },
|
||||
shortId: { type: 'string', description: 'Short issue identifier' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
culprit: {
|
||||
type: 'string',
|
||||
description: 'Function or location that caused the issue',
|
||||
optional: true,
|
||||
},
|
||||
permalink: { type: 'string', description: 'Direct link to the issue in Sentry' },
|
||||
logger: {
|
||||
type: 'string',
|
||||
description: 'Logger name that reported the issue',
|
||||
optional: true,
|
||||
},
|
||||
level: { type: 'string', description: 'Severity level (error, warning, info, etc.)' },
|
||||
status: { type: 'string', description: 'Current issue status' },
|
||||
substatus: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Issue substatus (e.g., ongoing, escalating, new, archived_until_escalating)',
|
||||
optional: true,
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
description: 'Issue priority (high, medium, or low)',
|
||||
optional: true,
|
||||
},
|
||||
statusDetails: { type: 'object', description: 'Additional details about the status' },
|
||||
isPublic: { type: 'boolean', description: 'Whether the issue is publicly visible' },
|
||||
platform: {
|
||||
type: 'string',
|
||||
description: 'Platform where the issue occurred',
|
||||
optional: true,
|
||||
},
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'Project information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project ID' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
slug: { type: 'string', description: 'Project slug' },
|
||||
platform: { type: 'string', description: 'Project platform' },
|
||||
},
|
||||
},
|
||||
type: { type: 'string', description: 'Issue type', optional: true },
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Error metadata',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Type of error (e.g., TypeError)' },
|
||||
value: { type: 'string', description: 'Error message or value' },
|
||||
function: { type: 'string', description: 'Function where the error occurred' },
|
||||
},
|
||||
},
|
||||
numComments: { type: 'number', description: 'Number of comments on the issue' },
|
||||
assignedTo: {
|
||||
type: 'object',
|
||||
description: 'User assigned to the issue',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
name: { type: 'string', description: 'User name' },
|
||||
email: { type: 'string', description: 'User email' },
|
||||
},
|
||||
},
|
||||
isBookmarked: { type: 'boolean', description: 'Whether the issue is bookmarked' },
|
||||
isSubscribed: { type: 'boolean', description: 'Whether subscribed to updates' },
|
||||
hasSeen: { type: 'boolean', description: 'Whether the user has seen this issue' },
|
||||
annotations: { type: 'array', description: 'Issue annotations' },
|
||||
isUnhandled: { type: 'boolean', description: 'Whether the issue is unhandled' },
|
||||
count: { type: 'string', description: 'Total number of occurrences' },
|
||||
userCount: { type: 'number', description: 'Number of unique users affected' },
|
||||
firstSeen: {
|
||||
type: 'string',
|
||||
description: 'When the issue was first seen (ISO timestamp)',
|
||||
},
|
||||
lastSeen: { type: 'string', description: 'When the issue was last seen (ISO timestamp)' },
|
||||
stats: { type: 'object', description: 'Statistical information about the issue' },
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Pagination metadata',
|
||||
properties: {
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for the next page of results (if available)',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results available',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import type { SentryUpdateIssueParams, SentryUpdateIssueResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateIssueTool: ToolConfig<SentryUpdateIssueParams, SentryUpdateIssueResponse> = {
|
||||
id: 'sentry_issues_update',
|
||||
name: 'Update Issue',
|
||||
description:
|
||||
'Update a Sentry issue by changing its status, assignment, bookmark state, or other properties. Returns the updated issue details.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
issueId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique ID of the issue to update (e.g., "12345")',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'New status for the issue: resolved, unresolved, ignored, or resolvedInNextRelease',
|
||||
},
|
||||
assignedTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Actor to assign the issue to, in the form "user:<id>" or "team:<id>" (a bare username or email is also accepted). Use an empty string to unassign.',
|
||||
},
|
||||
isBookmarked: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to bookmark the issue',
|
||||
},
|
||||
isSubscribed: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to subscribe to issue updates',
|
||||
},
|
||||
isPublic: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Whether the issue should be publicly visible',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://sentry.io/api/0/organizations/${params.organizationSlug}/issues/${params.issueId}/`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
|
||||
if (params.status !== undefined && params.status !== null && params.status !== '') {
|
||||
body.status = params.status
|
||||
}
|
||||
|
||||
if (params.assignedTo !== undefined && params.assignedTo !== null) {
|
||||
body.assignedTo = params.assignedTo === '' ? null : params.assignedTo
|
||||
}
|
||||
|
||||
if (params.isBookmarked !== undefined && params.isBookmarked !== null) {
|
||||
body.isBookmarked = params.isBookmarked
|
||||
}
|
||||
|
||||
if (params.isSubscribed !== undefined && params.isSubscribed !== null) {
|
||||
body.isSubscribed = params.isSubscribed
|
||||
}
|
||||
|
||||
if (params.isPublic !== undefined && params.isPublic !== null) {
|
||||
body.isPublic = params.isPublic
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const issue = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
issue: {
|
||||
id: issue.id,
|
||||
shortId: issue.shortId,
|
||||
title: issue.title,
|
||||
culprit: issue.culprit ?? null,
|
||||
permalink: issue.permalink,
|
||||
logger: issue.logger ?? null,
|
||||
level: issue.level,
|
||||
status: issue.status,
|
||||
substatus: issue.substatus ?? null,
|
||||
priority: issue.priority ?? null,
|
||||
statusDetails: issue.statusDetails || {},
|
||||
isPublic: issue.isPublic,
|
||||
platform: issue.platform ?? null,
|
||||
project: {
|
||||
id: issue.project?.id || '',
|
||||
name: issue.project?.name || '',
|
||||
slug: issue.project?.slug || '',
|
||||
platform: issue.project?.platform || '',
|
||||
},
|
||||
type: issue.type ?? null,
|
||||
metadata: {
|
||||
type: issue.metadata?.type || null,
|
||||
value: issue.metadata?.value || null,
|
||||
function: issue.metadata?.function || null,
|
||||
},
|
||||
numComments: issue.numComments || 0,
|
||||
assignedTo: issue.assignedTo
|
||||
? {
|
||||
id: issue.assignedTo.id,
|
||||
name: issue.assignedTo.name,
|
||||
email: issue.assignedTo.email,
|
||||
}
|
||||
: null,
|
||||
isBookmarked: issue.isBookmarked,
|
||||
isSubscribed: issue.isSubscribed,
|
||||
subscriptionDetails: issue.subscriptionDetails ?? null,
|
||||
hasSeen: issue.hasSeen,
|
||||
annotations: issue.annotations || [],
|
||||
isUnhandled: issue.isUnhandled,
|
||||
count: issue.count,
|
||||
userCount: issue.userCount || 0,
|
||||
firstSeen: issue.firstSeen,
|
||||
lastSeen: issue.lastSeen,
|
||||
stats: issue.stats || {},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
issue: {
|
||||
type: 'object',
|
||||
description: 'The updated Sentry issue',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique issue ID' },
|
||||
shortId: { type: 'string', description: 'Short issue identifier' },
|
||||
title: { type: 'string', description: 'Issue title' },
|
||||
status: { type: 'string', description: 'Updated issue status' },
|
||||
substatus: {
|
||||
type: 'string',
|
||||
description: 'Issue substatus after the update',
|
||||
optional: true,
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
description: 'Issue priority (high, medium, or low)',
|
||||
optional: true,
|
||||
},
|
||||
assignedTo: {
|
||||
type: 'object',
|
||||
description: 'User assigned to the issue (if any)',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
name: { type: 'string', description: 'User name' },
|
||||
email: { type: 'string', description: 'User email' },
|
||||
},
|
||||
},
|
||||
isBookmarked: { type: 'boolean', description: 'Whether the issue is bookmarked' },
|
||||
isSubscribed: { type: 'boolean', description: 'Whether the user is subscribed to updates' },
|
||||
isPublic: { type: 'boolean', description: 'Whether the issue is publicly visible' },
|
||||
permalink: { type: 'string', description: 'Direct link to the issue in Sentry' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import type { SentryCreateProjectParams, SentryCreateProjectResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createProjectTool: ToolConfig<SentryCreateProjectParams, SentryCreateProjectResponse> =
|
||||
{
|
||||
id: 'sentry_projects_create',
|
||||
name: 'Create Project',
|
||||
description:
|
||||
'Create a new Sentry project in an organization. Requires a team to associate the project with. Returns the created project details.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the project',
|
||||
},
|
||||
teamSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the team that will own this project',
|
||||
},
|
||||
slug: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'URL-friendly project identifier (auto-generated from name if not provided)',
|
||||
},
|
||||
platform: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Platform/language for the project (e.g., javascript, python, node, react-native). If not specified, defaults to "other"',
|
||||
},
|
||||
defaultRules: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Whether to create default alert rules (default: true)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://sentry.io/api/0/teams/${params.organizationSlug}/${params.teamSlug}/projects/`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
name: params.name,
|
||||
}
|
||||
|
||||
if (params.slug && params.slug !== null && params.slug !== '') {
|
||||
body.slug = params.slug
|
||||
}
|
||||
|
||||
if (params.platform && params.platform !== null && params.platform !== '') {
|
||||
body.platform = params.platform
|
||||
}
|
||||
|
||||
if (params.defaultRules !== undefined && params.defaultRules !== null) {
|
||||
body.default_rules = params.defaultRules
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const project = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
project: {
|
||||
id: project.id,
|
||||
slug: project.slug,
|
||||
name: project.name,
|
||||
platform: project.platform ?? null,
|
||||
dateCreated: project.dateCreated,
|
||||
isBookmarked: project.isBookmarked,
|
||||
isMember: project.isMember,
|
||||
features: project.features || [],
|
||||
firstEvent: project.firstEvent ?? null,
|
||||
firstTransactionEvent: project.firstTransactionEvent ?? null,
|
||||
access: project.access || [],
|
||||
hasAccess: project.hasAccess,
|
||||
hasMinifiedStackTrace: project.hasMinifiedStackTrace,
|
||||
hasMonitors: project.hasMonitors,
|
||||
hasProfiles: project.hasProfiles,
|
||||
hasReplays: project.hasReplays,
|
||||
hasSessions: project.hasSessions,
|
||||
isInternal: project.isInternal,
|
||||
organization: {
|
||||
id: project.organization?.id || '',
|
||||
slug: project.organization?.slug || '',
|
||||
name: project.organization?.name || '',
|
||||
},
|
||||
team: {
|
||||
id: project.team?.id || '',
|
||||
name: project.team?.name || '',
|
||||
slug: project.team?.slug || '',
|
||||
},
|
||||
teams:
|
||||
project.teams?.map((team: any) => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
slug: team.slug,
|
||||
})) || [],
|
||||
status: project.status ?? null,
|
||||
color: project.color ?? null,
|
||||
isPublic: project.isPublic,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The newly created Sentry project',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique project ID' },
|
||||
slug: { type: 'string', description: 'URL-friendly project identifier' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
platform: { type: 'string', description: 'Platform/language', optional: true },
|
||||
dateCreated: {
|
||||
type: 'string',
|
||||
description: 'When the project was created (ISO timestamp)',
|
||||
},
|
||||
isBookmarked: { type: 'boolean', description: 'Whether the project is bookmarked' },
|
||||
isMember: { type: 'boolean', description: 'Whether the user is a member' },
|
||||
hasAccess: { type: 'boolean', description: 'Whether the user has access' },
|
||||
features: { type: 'array', description: 'Enabled features' },
|
||||
firstEvent: { type: 'string', description: 'First event timestamp', optional: true },
|
||||
organization: {
|
||||
type: 'object',
|
||||
description: 'Organization information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Organization ID' },
|
||||
slug: { type: 'string', description: 'Organization slug' },
|
||||
name: { type: 'string', description: 'Organization name' },
|
||||
},
|
||||
},
|
||||
team: {
|
||||
type: 'object',
|
||||
description: 'Primary team for the project',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Team ID' },
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
slug: { type: 'string', description: 'Team slug' },
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'Teams associated with the project',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Team ID' },
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
slug: { type: 'string', description: 'Team slug' },
|
||||
},
|
||||
},
|
||||
},
|
||||
status: { type: 'string', description: 'Project status', optional: true },
|
||||
color: { type: 'string', description: 'Project color code', optional: true },
|
||||
isPublic: { type: 'boolean', description: 'Whether the project is public' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
import type { SentryGetProjectParams, SentryGetProjectResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getProjectTool: ToolConfig<SentryGetProjectParams, SentryGetProjectResponse> = {
|
||||
id: 'sentry_projects_get',
|
||||
name: 'Get Project',
|
||||
description:
|
||||
'Retrieve detailed information about a specific Sentry project by its slug. Returns complete project details including teams, features, and configuration.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
projectSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the project to retrieve (e.g., "my-project")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://sentry.io/api/0/projects/${params.organizationSlug}/${params.projectSlug}/`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const project = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
project: {
|
||||
id: project.id,
|
||||
slug: project.slug,
|
||||
name: project.name,
|
||||
platform: project.platform ?? null,
|
||||
dateCreated: project.dateCreated,
|
||||
isBookmarked: project.isBookmarked,
|
||||
isMember: project.isMember,
|
||||
features: project.features || [],
|
||||
firstEvent: project.firstEvent ?? null,
|
||||
firstTransactionEvent: project.firstTransactionEvent ?? null,
|
||||
access: project.access || [],
|
||||
hasAccess: project.hasAccess,
|
||||
hasMinifiedStackTrace: project.hasMinifiedStackTrace,
|
||||
hasMonitors: project.hasMonitors,
|
||||
hasProfiles: project.hasProfiles,
|
||||
hasReplays: project.hasReplays,
|
||||
hasSessions: project.hasSessions,
|
||||
isInternal: project.isInternal,
|
||||
organization: {
|
||||
id: project.organization?.id || '',
|
||||
slug: project.organization?.slug || '',
|
||||
name: project.organization?.name || '',
|
||||
},
|
||||
team: {
|
||||
id: project.team?.id || '',
|
||||
name: project.team?.name || '',
|
||||
slug: project.team?.slug || '',
|
||||
},
|
||||
teams:
|
||||
project.teams?.map((team: any) => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
slug: team.slug,
|
||||
})) || [],
|
||||
status: project.status ?? null,
|
||||
color: project.color ?? null,
|
||||
isPublic: project.isPublic,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'Detailed information about the Sentry project',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique project ID' },
|
||||
slug: { type: 'string', description: 'URL-friendly project identifier' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
platform: {
|
||||
type: 'string',
|
||||
description: 'Platform/language (e.g., javascript, python)',
|
||||
optional: true,
|
||||
},
|
||||
dateCreated: {
|
||||
type: 'string',
|
||||
description: 'When the project was created (ISO timestamp)',
|
||||
},
|
||||
isBookmarked: { type: 'boolean', description: 'Whether the project is bookmarked' },
|
||||
isMember: { type: 'boolean', description: 'Whether the user is a member of the project' },
|
||||
features: {
|
||||
type: 'array',
|
||||
description: 'Enabled features for the project',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
firstEvent: {
|
||||
type: 'string',
|
||||
description: 'When the first event was received (ISO timestamp)',
|
||||
optional: true,
|
||||
},
|
||||
firstTransactionEvent: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the project has received its first transaction event',
|
||||
optional: true,
|
||||
},
|
||||
access: { type: 'array', description: 'Access permissions' },
|
||||
organization: {
|
||||
type: 'object',
|
||||
description: 'Organization information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Organization ID' },
|
||||
slug: { type: 'string', description: 'Organization slug' },
|
||||
name: { type: 'string', description: 'Organization name' },
|
||||
},
|
||||
},
|
||||
team: {
|
||||
type: 'object',
|
||||
description: 'Primary team for the project',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Team ID' },
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
slug: { type: 'string', description: 'Team slug' },
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'Teams associated with the project',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Team ID' },
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
slug: { type: 'string', description: 'Team slug' },
|
||||
},
|
||||
},
|
||||
},
|
||||
status: { type: 'string', description: 'Project status', optional: true },
|
||||
color: { type: 'string', description: 'Project color code', optional: true },
|
||||
isPublic: { type: 'boolean', description: 'Whether the project is publicly visible' },
|
||||
isInternal: { type: 'boolean', description: 'Whether the project is internal' },
|
||||
hasAccess: { type: 'boolean', description: 'Whether the user has access to this project' },
|
||||
hasMinifiedStackTrace: {
|
||||
type: 'boolean',
|
||||
description: 'Whether minified stack traces are available',
|
||||
},
|
||||
hasMonitors: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the project has monitors configured',
|
||||
},
|
||||
hasProfiles: { type: 'boolean', description: 'Whether the project has profiling enabled' },
|
||||
hasReplays: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the project has session replays enabled',
|
||||
},
|
||||
hasSessions: { type: 'boolean', description: 'Whether the project has sessions enabled' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import type { SentryListProjectsParams, SentryListProjectsResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listProjectsTool: ToolConfig<SentryListProjectsParams, SentryListProjectsResponse> = {
|
||||
id: 'sentry_projects_list',
|
||||
name: 'List Projects',
|
||||
description:
|
||||
'List all projects in a Sentry organization. Returns project details including name, platform, teams, and configuration.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor for retrieving next page of results',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of projects to return per page (default: 25, max: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://sentry.io/api/0/organizations/${params.organizationSlug}/projects/`
|
||||
const queryParams: string[] = []
|
||||
|
||||
if (params.cursor && params.cursor !== null && params.cursor !== '') {
|
||||
queryParams.push(`cursor=${encodeURIComponent(params.cursor)}`)
|
||||
}
|
||||
|
||||
if (params.limit && params.limit !== null) {
|
||||
queryParams.push(`per_page=${Number(params.limit)}`)
|
||||
}
|
||||
|
||||
return queryParams.length > 0 ? `${baseUrl}?${queryParams.join('&')}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const linkHeader = response.headers.get('Link')
|
||||
let nextCursor: string | undefined
|
||||
let hasMore = false
|
||||
|
||||
if (linkHeader) {
|
||||
const nextMatch = linkHeader.match(
|
||||
/<[^>]*cursor=([^&>]+)[^>]*>;\s*rel="next";\s*results="true"/
|
||||
)
|
||||
if (nextMatch) {
|
||||
nextCursor = decodeURIComponent(nextMatch[1])
|
||||
hasMore = true
|
||||
}
|
||||
}
|
||||
|
||||
const projects = Array.isArray(data) ? data : []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
projects: projects.map((project: any) => ({
|
||||
id: project.id,
|
||||
slug: project.slug,
|
||||
name: project.name,
|
||||
platform: project.platform ?? null,
|
||||
dateCreated: project.dateCreated,
|
||||
isBookmarked: project.isBookmarked,
|
||||
isMember: project.isMember,
|
||||
features: project.features || [],
|
||||
firstEvent: project.firstEvent ?? null,
|
||||
firstTransactionEvent: project.firstTransactionEvent ?? null,
|
||||
access: project.access || [],
|
||||
hasAccess: project.hasAccess,
|
||||
hasMinifiedStackTrace: project.hasMinifiedStackTrace,
|
||||
hasMonitors: project.hasMonitors,
|
||||
hasProfiles: project.hasProfiles,
|
||||
hasReplays: project.hasReplays,
|
||||
hasSessions: project.hasSessions,
|
||||
isInternal: project.isInternal,
|
||||
organization: {
|
||||
id: project.organization?.id || '',
|
||||
slug: project.organization?.slug || '',
|
||||
name: project.organization?.name || '',
|
||||
},
|
||||
team: {
|
||||
id: project.team?.id || '',
|
||||
name: project.team?.name || '',
|
||||
slug: project.team?.slug || '',
|
||||
},
|
||||
teams:
|
||||
project.teams?.map((team: any) => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
slug: team.slug,
|
||||
})) || [],
|
||||
status: project.status ?? null,
|
||||
color: project.color ?? null,
|
||||
isPublic: project.isPublic,
|
||||
})),
|
||||
metadata: {
|
||||
nextCursor,
|
||||
hasMore,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
projects: {
|
||||
type: 'array',
|
||||
description: 'List of Sentry projects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique project ID' },
|
||||
slug: { type: 'string', description: 'URL-friendly project identifier' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
platform: {
|
||||
type: 'string',
|
||||
description: 'Platform/language (e.g., javascript, python)',
|
||||
optional: true,
|
||||
},
|
||||
dateCreated: {
|
||||
type: 'string',
|
||||
description: 'When the project was created (ISO timestamp)',
|
||||
},
|
||||
isBookmarked: { type: 'boolean', description: 'Whether the project is bookmarked' },
|
||||
isMember: { type: 'boolean', description: 'Whether the user is a member of the project' },
|
||||
features: { type: 'array', description: 'Enabled features for the project' },
|
||||
organization: {
|
||||
type: 'object',
|
||||
description: 'Organization information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Organization ID' },
|
||||
slug: { type: 'string', description: 'Organization slug' },
|
||||
name: { type: 'string', description: 'Organization name' },
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'Teams associated with the project',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Team ID' },
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
slug: { type: 'string', description: 'Team slug' },
|
||||
},
|
||||
},
|
||||
},
|
||||
status: { type: 'string', description: 'Project status', optional: true },
|
||||
isPublic: { type: 'boolean', description: 'Whether the project is publicly visible' },
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Pagination metadata',
|
||||
properties: {
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for the next page of results (if available)',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results available',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import type { SentryUpdateProjectParams, SentryUpdateProjectResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateProjectTool: ToolConfig<SentryUpdateProjectParams, SentryUpdateProjectResponse> =
|
||||
{
|
||||
id: 'sentry_projects_update',
|
||||
name: 'Update Project',
|
||||
description:
|
||||
'Update a Sentry project by changing its name, slug, platform, or other settings. Returns the updated project details.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
projectSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the project to update (e.g., "my-project")',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name for the project',
|
||||
},
|
||||
slug: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New URL-friendly project identifier',
|
||||
},
|
||||
platform: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New platform/language for the project (e.g., javascript, python, node)',
|
||||
},
|
||||
isBookmarked: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to bookmark the project',
|
||||
},
|
||||
digestsMinDelay: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Minimum delay (in seconds) for digest notifications',
|
||||
},
|
||||
digestsMaxDelay: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum delay (in seconds) for digest notifications',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://sentry.io/api/0/projects/${params.organizationSlug}/${params.projectSlug}/`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
|
||||
if (params.name !== undefined && params.name !== null && params.name !== '') {
|
||||
body.name = params.name
|
||||
}
|
||||
|
||||
if (params.slug !== undefined && params.slug !== null && params.slug !== '') {
|
||||
body.slug = params.slug
|
||||
}
|
||||
|
||||
if (params.platform !== undefined && params.platform !== null && params.platform !== '') {
|
||||
body.platform = params.platform
|
||||
}
|
||||
|
||||
if (params.isBookmarked !== undefined && params.isBookmarked !== null) {
|
||||
body.isBookmarked = params.isBookmarked
|
||||
}
|
||||
|
||||
if (params.digestsMinDelay !== undefined && params.digestsMinDelay !== null) {
|
||||
body.digestsMinDelay = Number(params.digestsMinDelay)
|
||||
}
|
||||
|
||||
if (params.digestsMaxDelay !== undefined && params.digestsMaxDelay !== null) {
|
||||
body.digestsMaxDelay = Number(params.digestsMaxDelay)
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const project = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
project: {
|
||||
id: project.id,
|
||||
slug: project.slug,
|
||||
name: project.name,
|
||||
platform: project.platform ?? null,
|
||||
dateCreated: project.dateCreated,
|
||||
isBookmarked: project.isBookmarked,
|
||||
isMember: project.isMember,
|
||||
features: project.features || [],
|
||||
firstEvent: project.firstEvent ?? null,
|
||||
firstTransactionEvent: project.firstTransactionEvent ?? null,
|
||||
access: project.access || [],
|
||||
hasAccess: project.hasAccess,
|
||||
hasMinifiedStackTrace: project.hasMinifiedStackTrace,
|
||||
hasMonitors: project.hasMonitors,
|
||||
hasProfiles: project.hasProfiles,
|
||||
hasReplays: project.hasReplays,
|
||||
hasSessions: project.hasSessions,
|
||||
isInternal: project.isInternal,
|
||||
organization: {
|
||||
id: project.organization?.id || '',
|
||||
slug: project.organization?.slug || '',
|
||||
name: project.organization?.name || '',
|
||||
},
|
||||
team: {
|
||||
id: project.team?.id || '',
|
||||
name: project.team?.name || '',
|
||||
slug: project.team?.slug || '',
|
||||
},
|
||||
teams:
|
||||
project.teams?.map((team: any) => ({
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
slug: team.slug,
|
||||
})) || [],
|
||||
status: project.status ?? null,
|
||||
color: project.color ?? null,
|
||||
isPublic: project.isPublic,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The updated Sentry project',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique project ID' },
|
||||
slug: { type: 'string', description: 'URL-friendly project identifier' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
platform: { type: 'string', description: 'Platform/language', optional: true },
|
||||
isBookmarked: { type: 'boolean', description: 'Whether the project is bookmarked' },
|
||||
organization: {
|
||||
type: 'object',
|
||||
description: 'Organization information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Organization ID' },
|
||||
slug: { type: 'string', description: 'Organization slug' },
|
||||
name: { type: 'string', description: 'Organization name' },
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'Teams associated with the project',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Team ID' },
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
slug: { type: 'string', description: 'Team slug' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
import type { SentryCreateReleaseParams, SentryCreateReleaseResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createReleaseTool: ToolConfig<SentryCreateReleaseParams, SentryCreateReleaseResponse> =
|
||||
{
|
||||
id: 'sentry_releases_create',
|
||||
name: 'Create Release',
|
||||
description:
|
||||
'Create a new release in Sentry. A release is a version of your code deployed to an environment. Can include commit information and associated projects. Returns the created release details.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
version: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Version identifier for the release (e.g., "2.0.0", "my-app@1.0.0", or a git commit SHA)',
|
||||
},
|
||||
projects: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of project slugs to associate with this release',
|
||||
},
|
||||
ref: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Git reference (commit SHA, tag, or branch) for this release',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'URL pointing to the release (e.g., GitHub release page)',
|
||||
},
|
||||
dateReleased: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'ISO 8601 timestamp for when the release was deployed (defaults to current time)',
|
||||
},
|
||||
commits: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'JSON array of commit objects with id, repository (optional), and message (optional). Example: [{"id":"abc123","message":"Fix bug"}]',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://sentry.io/api/0/organizations/${params.organizationSlug}/releases/`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
version: params.version,
|
||||
projects: params.projects
|
||||
.split(',')
|
||||
.map((p: string) => p.trim())
|
||||
.filter((p: string) => p.length > 0),
|
||||
}
|
||||
|
||||
if (params.ref && params.ref !== null && params.ref !== '') {
|
||||
body.ref = params.ref
|
||||
}
|
||||
|
||||
if (params.url && params.url !== null && params.url !== '') {
|
||||
body.url = params.url
|
||||
}
|
||||
|
||||
if (params.dateReleased && params.dateReleased !== null && params.dateReleased !== '') {
|
||||
body.dateReleased = params.dateReleased
|
||||
}
|
||||
|
||||
if (params.commits && params.commits !== null && params.commits !== '') {
|
||||
try {
|
||||
body.commits = JSON.parse(params.commits)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const release = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
release: {
|
||||
id: release.id,
|
||||
version: release.version,
|
||||
shortVersion: release.shortVersion,
|
||||
ref: release.ref ?? null,
|
||||
url: release.url ?? null,
|
||||
dateReleased: release.dateReleased ?? null,
|
||||
dateCreated: release.dateCreated,
|
||||
dateStarted: release.dateStarted ?? null,
|
||||
data: release.data || {},
|
||||
newGroups: release.newGroups || 0,
|
||||
owner: release.owner
|
||||
? {
|
||||
id: release.owner.id,
|
||||
name: release.owner.name,
|
||||
email: release.owner.email,
|
||||
}
|
||||
: null,
|
||||
commitCount: release.commitCount || 0,
|
||||
lastCommit: release.lastCommit
|
||||
? {
|
||||
id: release.lastCommit.id,
|
||||
message: release.lastCommit.message,
|
||||
dateCreated: release.lastCommit.dateCreated,
|
||||
}
|
||||
: null,
|
||||
deployCount: release.deployCount || 0,
|
||||
lastDeploy: release.lastDeploy
|
||||
? {
|
||||
id: release.lastDeploy.id,
|
||||
environment: release.lastDeploy.environment,
|
||||
dateStarted: release.lastDeploy.dateStarted,
|
||||
dateFinished: release.lastDeploy.dateFinished,
|
||||
}
|
||||
: null,
|
||||
authors:
|
||||
release.authors?.map((author: any) => ({
|
||||
id: author.id,
|
||||
name: author.name,
|
||||
email: author.email,
|
||||
})) || [],
|
||||
projects:
|
||||
release.projects?.map((project: any) => ({
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
slug: project.slug,
|
||||
platform: project.platform,
|
||||
})) || [],
|
||||
firstEvent: release.firstEvent ?? null,
|
||||
lastEvent: release.lastEvent ?? null,
|
||||
versionInfo: {
|
||||
buildHash: release.versionInfo?.buildHash || null,
|
||||
version: {
|
||||
raw: release.versionInfo?.version?.raw || release.version,
|
||||
},
|
||||
package: release.versionInfo?.package || null,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
release: {
|
||||
type: 'object',
|
||||
description: 'The newly created Sentry release',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique release ID' },
|
||||
version: { type: 'string', description: 'Release version identifier' },
|
||||
shortVersion: { type: 'string', description: 'Shortened version identifier' },
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Git reference (commit SHA, tag, or branch)',
|
||||
optional: true,
|
||||
},
|
||||
url: { type: 'string', description: 'URL to the release', optional: true },
|
||||
dateReleased: {
|
||||
type: 'string',
|
||||
description: 'When the release was deployed (ISO timestamp)',
|
||||
optional: true,
|
||||
},
|
||||
dateCreated: {
|
||||
type: 'string',
|
||||
description: 'When the release was created (ISO timestamp)',
|
||||
},
|
||||
dateStarted: {
|
||||
type: 'string',
|
||||
description: 'When the release started (ISO timestamp)',
|
||||
optional: true,
|
||||
},
|
||||
newGroups: { type: 'number', description: 'Number of new issues introduced' },
|
||||
commitCount: { type: 'number', description: 'Number of commits in this release' },
|
||||
deployCount: { type: 'number', description: 'Number of deploys for this release' },
|
||||
owner: {
|
||||
type: 'object',
|
||||
description: 'Release owner',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Owner ID' },
|
||||
name: { type: 'string', description: 'Owner name' },
|
||||
email: { type: 'string', description: 'Owner email' },
|
||||
},
|
||||
},
|
||||
lastCommit: {
|
||||
type: 'object',
|
||||
description: 'Last commit in the release',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Commit SHA' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
dateCreated: { type: 'string', description: 'Commit timestamp' },
|
||||
},
|
||||
},
|
||||
lastDeploy: {
|
||||
type: 'object',
|
||||
description: 'Last deploy of the release',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Deploy ID' },
|
||||
environment: { type: 'string', description: 'Deploy environment' },
|
||||
dateStarted: { type: 'string', description: 'Deploy start timestamp' },
|
||||
dateFinished: { type: 'string', description: 'Deploy finish timestamp' },
|
||||
},
|
||||
},
|
||||
authors: {
|
||||
type: 'array',
|
||||
description: 'Authors of commits in the release',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Author ID' },
|
||||
name: { type: 'string', description: 'Author name' },
|
||||
email: { type: 'string', description: 'Author email' },
|
||||
},
|
||||
},
|
||||
},
|
||||
projects: {
|
||||
type: 'array',
|
||||
description: 'Projects associated with this release',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project ID' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
slug: { type: 'string', description: 'Project slug' },
|
||||
platform: { type: 'string', description: 'Project platform' },
|
||||
},
|
||||
},
|
||||
},
|
||||
firstEvent: { type: 'string', description: 'First event timestamp', optional: true },
|
||||
lastEvent: { type: 'string', description: 'Last event timestamp', optional: true },
|
||||
versionInfo: {
|
||||
type: 'object',
|
||||
description: 'Version metadata',
|
||||
properties: {
|
||||
buildHash: { type: 'string', description: 'Build hash' },
|
||||
version: {
|
||||
type: 'object',
|
||||
description: 'Version details',
|
||||
properties: {
|
||||
raw: { type: 'string', description: 'Raw version string' },
|
||||
},
|
||||
},
|
||||
package: { type: 'string', description: 'Package name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import type { SentryCreateDeployParams, SentryCreateDeployResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createDeployTool: ToolConfig<SentryCreateDeployParams, SentryCreateDeployResponse> = {
|
||||
id: 'sentry_releases_deploy',
|
||||
name: 'Create Deploy',
|
||||
description:
|
||||
'Create a deploy record for a Sentry release in a specific environment. Deploys track when and where releases are deployed. Returns the created deploy details.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
version: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Version identifier of the release being deployed (e.g., "1.0.0" or "abc123")',
|
||||
},
|
||||
environment: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Environment name where the release is being deployed (e.g., "production", "staging")',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional name for this deploy (e.g., "Deploy v2.0 to Production")',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'URL pointing to the deploy (e.g., CI/CD pipeline URL)',
|
||||
},
|
||||
dateStarted: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'ISO 8601 timestamp for when the deploy started (defaults to current time)',
|
||||
},
|
||||
dateFinished: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'ISO 8601 timestamp for when the deploy finished',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://sentry.io/api/0/organizations/${params.organizationSlug}/releases/${encodeURIComponent(params.version)}/deploys/`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
environment: params.environment,
|
||||
}
|
||||
|
||||
if (params.name && params.name !== null && params.name !== '') {
|
||||
body.name = params.name
|
||||
}
|
||||
|
||||
if (params.url && params.url !== null && params.url !== '') {
|
||||
body.url = params.url
|
||||
}
|
||||
|
||||
if (params.dateStarted && params.dateStarted !== null && params.dateStarted !== '') {
|
||||
body.dateStarted = params.dateStarted
|
||||
}
|
||||
|
||||
if (params.dateFinished && params.dateFinished !== null && params.dateFinished !== '') {
|
||||
body.dateFinished = params.dateFinished
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const deploy = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deploy: {
|
||||
id: deploy.id,
|
||||
environment: deploy.environment,
|
||||
name: deploy.name ?? null,
|
||||
url: deploy.url ?? null,
|
||||
dateStarted: deploy.dateStarted,
|
||||
dateFinished: deploy.dateFinished ?? null,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deploy: {
|
||||
type: 'object',
|
||||
description: 'The newly created deploy record',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique deploy ID' },
|
||||
environment: {
|
||||
type: 'string',
|
||||
description: 'Environment name where the release was deployed',
|
||||
},
|
||||
name: { type: 'string', description: 'Name of the deploy', optional: true },
|
||||
url: { type: 'string', description: 'URL pointing to the deploy', optional: true },
|
||||
dateStarted: {
|
||||
type: 'string',
|
||||
description: 'When the deploy started (ISO timestamp)',
|
||||
},
|
||||
dateFinished: {
|
||||
type: 'string',
|
||||
description: 'When the deploy finished (ISO timestamp)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
import type { SentryListReleasesParams, SentryListReleasesResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listReleasesTool: ToolConfig<SentryListReleasesParams, SentryListReleasesResponse> = {
|
||||
id: 'sentry_releases_list',
|
||||
name: 'List Releases',
|
||||
description:
|
||||
'List releases for a Sentry organization or project. Returns release details including version, commits, deploy information, and associated projects.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
projectSlug: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter releases by numeric project ID (e.g., "4501234"). This organization-scoped endpoint requires the numeric project ID, not the project slug.',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search query to filter releases (e.g., "1.0" to match version patterns)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor for retrieving next page of results',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of releases to return per page (default: 25, max: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://sentry.io/api/0/organizations/${params.organizationSlug}/releases/`
|
||||
const queryParams: string[] = []
|
||||
|
||||
if (params.projectSlug && params.projectSlug !== null && params.projectSlug !== '') {
|
||||
queryParams.push(`project=${encodeURIComponent(params.projectSlug)}`)
|
||||
}
|
||||
|
||||
if (params.query && params.query !== null && params.query !== '') {
|
||||
queryParams.push(`query=${encodeURIComponent(params.query)}`)
|
||||
}
|
||||
|
||||
if (params.cursor && params.cursor !== null && params.cursor !== '') {
|
||||
queryParams.push(`cursor=${encodeURIComponent(params.cursor)}`)
|
||||
}
|
||||
|
||||
if (params.limit && params.limit !== null) {
|
||||
queryParams.push(`per_page=${Number(params.limit)}`)
|
||||
}
|
||||
|
||||
return queryParams.length > 0 ? `${baseUrl}?${queryParams.join('&')}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const linkHeader = response.headers.get('Link')
|
||||
let nextCursor: string | undefined
|
||||
let hasMore = false
|
||||
|
||||
if (linkHeader) {
|
||||
const nextMatch = linkHeader.match(
|
||||
/<[^>]*cursor=([^&>]+)[^>]*>;\s*rel="next";\s*results="true"/
|
||||
)
|
||||
if (nextMatch) {
|
||||
nextCursor = decodeURIComponent(nextMatch[1])
|
||||
hasMore = true
|
||||
}
|
||||
}
|
||||
|
||||
const releases = Array.isArray(data) ? data : []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
releases: releases.map((release: any) => ({
|
||||
id: release.id,
|
||||
version: release.version,
|
||||
shortVersion: release.shortVersion,
|
||||
ref: release.ref ?? null,
|
||||
url: release.url ?? null,
|
||||
dateReleased: release.dateReleased ?? null,
|
||||
dateCreated: release.dateCreated,
|
||||
dateStarted: release.dateStarted ?? null,
|
||||
data: release.data || {},
|
||||
newGroups: release.newGroups || 0,
|
||||
owner: release.owner
|
||||
? {
|
||||
id: release.owner.id,
|
||||
name: release.owner.name,
|
||||
email: release.owner.email,
|
||||
}
|
||||
: null,
|
||||
commitCount: release.commitCount || 0,
|
||||
lastCommit: release.lastCommit
|
||||
? {
|
||||
id: release.lastCommit.id,
|
||||
message: release.lastCommit.message,
|
||||
dateCreated: release.lastCommit.dateCreated,
|
||||
}
|
||||
: null,
|
||||
deployCount: release.deployCount || 0,
|
||||
lastDeploy: release.lastDeploy
|
||||
? {
|
||||
id: release.lastDeploy.id,
|
||||
environment: release.lastDeploy.environment,
|
||||
dateStarted: release.lastDeploy.dateStarted,
|
||||
dateFinished: release.lastDeploy.dateFinished,
|
||||
}
|
||||
: null,
|
||||
authors:
|
||||
release.authors?.map((author: any) => ({
|
||||
id: author.id,
|
||||
name: author.name,
|
||||
email: author.email,
|
||||
})) || [],
|
||||
projects:
|
||||
release.projects?.map((project: any) => ({
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
slug: project.slug,
|
||||
platform: project.platform,
|
||||
})) || [],
|
||||
firstEvent: release.firstEvent ?? null,
|
||||
lastEvent: release.lastEvent ?? null,
|
||||
versionInfo: {
|
||||
buildHash: release.versionInfo?.buildHash || null,
|
||||
version: {
|
||||
raw: release.versionInfo?.version?.raw || release.version,
|
||||
},
|
||||
package: release.versionInfo?.package || null,
|
||||
},
|
||||
})),
|
||||
metadata: {
|
||||
nextCursor,
|
||||
hasMore,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
releases: {
|
||||
type: 'array',
|
||||
description: 'List of Sentry releases',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique release ID' },
|
||||
version: { type: 'string', description: 'Release version identifier' },
|
||||
shortVersion: { type: 'string', description: 'Shortened version identifier' },
|
||||
ref: {
|
||||
type: 'string',
|
||||
description: 'Git reference (commit SHA, tag, or branch)',
|
||||
optional: true,
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'URL to the release (e.g., GitHub release page)',
|
||||
optional: true,
|
||||
},
|
||||
dateReleased: {
|
||||
type: 'string',
|
||||
description: 'When the release was deployed (ISO timestamp)',
|
||||
optional: true,
|
||||
},
|
||||
dateCreated: {
|
||||
type: 'string',
|
||||
description: 'When the release was created (ISO timestamp)',
|
||||
},
|
||||
dateStarted: {
|
||||
type: 'string',
|
||||
description: 'When the release started (ISO timestamp)',
|
||||
optional: true,
|
||||
},
|
||||
newGroups: {
|
||||
type: 'number',
|
||||
description: 'Number of new issues introduced in this release',
|
||||
},
|
||||
owner: {
|
||||
type: 'object',
|
||||
description: 'Owner of the release',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
name: { type: 'string', description: 'User name' },
|
||||
email: { type: 'string', description: 'User email' },
|
||||
},
|
||||
},
|
||||
commitCount: { type: 'number', description: 'Number of commits in this release' },
|
||||
deployCount: { type: 'number', description: 'Number of deploys for this release' },
|
||||
lastCommit: {
|
||||
type: 'object',
|
||||
description: 'Last commit in the release',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Commit SHA' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
dateCreated: { type: 'string', description: 'Commit timestamp' },
|
||||
},
|
||||
},
|
||||
lastDeploy: {
|
||||
type: 'object',
|
||||
description: 'Last deploy of the release',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Deploy ID' },
|
||||
environment: { type: 'string', description: 'Deploy environment' },
|
||||
dateStarted: { type: 'string', description: 'Deploy start timestamp' },
|
||||
dateFinished: { type: 'string', description: 'Deploy finish timestamp' },
|
||||
},
|
||||
},
|
||||
authors: {
|
||||
type: 'array',
|
||||
description: 'Authors of commits in the release',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Author ID' },
|
||||
name: { type: 'string', description: 'Author name' },
|
||||
email: { type: 'string', description: 'Author email' },
|
||||
},
|
||||
},
|
||||
},
|
||||
projects: {
|
||||
type: 'array',
|
||||
description: 'Projects associated with this release',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project ID' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
slug: { type: 'string', description: 'Project slug' },
|
||||
platform: { type: 'string', description: 'Project platform' },
|
||||
},
|
||||
},
|
||||
},
|
||||
firstEvent: { type: 'string', description: 'First event timestamp', optional: true },
|
||||
lastEvent: { type: 'string', description: 'Last event timestamp', optional: true },
|
||||
versionInfo: {
|
||||
type: 'object',
|
||||
description: 'Version metadata',
|
||||
properties: {
|
||||
buildHash: { type: 'string', description: 'Build hash' },
|
||||
version: {
|
||||
type: 'object',
|
||||
description: 'Version details',
|
||||
properties: {
|
||||
raw: { type: 'string', description: 'Raw version string' },
|
||||
},
|
||||
},
|
||||
package: { type: 'string', description: 'Package name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Pagination metadata',
|
||||
properties: {
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for the next page of results (if available)',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results available',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
import type { SentryListTeamsParams, SentryListTeamsResponse } from '@/tools/sentry/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listTeamsTool: ToolConfig<SentryListTeamsParams, SentryListTeamsResponse> = {
|
||||
id: 'sentry_teams_list',
|
||||
name: 'List Teams',
|
||||
description:
|
||||
'List all teams in a Sentry organization. Useful for discovering the team slug required when creating a project. Returns team details including slug, name, member count, and associated projects.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Sentry API authentication token',
|
||||
},
|
||||
organizationSlug: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The slug of the organization (e.g., "my-org")',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter teams by name or slug',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor for retrieving next page of results',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of teams to return per page (default: 25, max: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://sentry.io/api/0/organizations/${params.organizationSlug}/teams/`
|
||||
const queryParams: string[] = []
|
||||
|
||||
if (params.query && params.query !== null && params.query !== '') {
|
||||
queryParams.push(`query=${encodeURIComponent(params.query)}`)
|
||||
}
|
||||
|
||||
if (params.cursor && params.cursor !== null && params.cursor !== '') {
|
||||
queryParams.push(`cursor=${encodeURIComponent(params.cursor)}`)
|
||||
}
|
||||
|
||||
if (params.limit && params.limit !== null) {
|
||||
queryParams.push(`per_page=${Number(params.limit)}`)
|
||||
}
|
||||
|
||||
return queryParams.length > 0 ? `${baseUrl}?${queryParams.join('&')}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const linkHeader = response.headers.get('Link')
|
||||
let nextCursor: string | undefined
|
||||
let hasMore = false
|
||||
|
||||
if (linkHeader) {
|
||||
const nextMatch = linkHeader.match(
|
||||
/<[^>]*cursor=([^&>]+)[^>]*>;\s*rel="next";\s*results="true"/
|
||||
)
|
||||
if (nextMatch) {
|
||||
nextCursor = decodeURIComponent(nextMatch[1])
|
||||
hasMore = true
|
||||
}
|
||||
}
|
||||
|
||||
const teams = Array.isArray(data) ? data : []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
teams: teams.map((team: any) => ({
|
||||
id: team.id,
|
||||
slug: team.slug,
|
||||
name: team.name,
|
||||
dateCreated: team.dateCreated,
|
||||
isMember: team.isMember,
|
||||
teamRole: team.teamRole ?? null,
|
||||
hasAccess: team.hasAccess,
|
||||
isPending: team.isPending,
|
||||
memberCount: team.memberCount || 0,
|
||||
projects:
|
||||
team.projects?.map((project: any) => ({
|
||||
id: project.id,
|
||||
slug: project.slug,
|
||||
name: project.name,
|
||||
platform: project.platform ?? null,
|
||||
})) || [],
|
||||
})),
|
||||
metadata: {
|
||||
nextCursor,
|
||||
hasMore,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
teams: {
|
||||
type: 'array',
|
||||
description: 'List of Sentry teams',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique team ID' },
|
||||
slug: {
|
||||
type: 'string',
|
||||
description: 'URL-friendly team identifier (used to own projects)',
|
||||
},
|
||||
name: { type: 'string', description: 'Team name' },
|
||||
dateCreated: {
|
||||
type: 'string',
|
||||
description: 'When the team was created (ISO timestamp)',
|
||||
},
|
||||
isMember: { type: 'boolean', description: 'Whether the user is a member of the team' },
|
||||
teamRole: {
|
||||
type: 'string',
|
||||
description: 'The role of the user on the team',
|
||||
optional: true,
|
||||
},
|
||||
hasAccess: { type: 'boolean', description: 'Whether the user has access to this team' },
|
||||
isPending: { type: 'boolean', description: 'Whether team membership is pending' },
|
||||
memberCount: { type: 'number', description: 'Number of members in the team' },
|
||||
projects: {
|
||||
type: 'array',
|
||||
description: 'Projects owned by this team',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Project ID' },
|
||||
slug: { type: 'string', description: 'Project slug' },
|
||||
name: { type: 'string', description: 'Project name' },
|
||||
platform: { type: 'string', description: 'Project platform', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Pagination metadata',
|
||||
properties: {
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for the next page of results (if available)',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results available',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Base parameter interface shared across all Sentry tools
|
||||
*/
|
||||
interface SentryBaseParams {
|
||||
apiKey: string
|
||||
organizationSlug: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentry issue representation
|
||||
*/
|
||||
interface SentryIssue {
|
||||
id: string
|
||||
shortId: string
|
||||
title: string
|
||||
culprit: string
|
||||
permalink: string
|
||||
logger: string | null
|
||||
level: string
|
||||
status: string
|
||||
substatus: string | null
|
||||
priority: string | null
|
||||
statusDetails: Record<string, any>
|
||||
isPublic: boolean
|
||||
platform: string
|
||||
project: {
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
platform: string
|
||||
}
|
||||
type: string
|
||||
metadata: {
|
||||
type: string | null
|
||||
value: string | null
|
||||
function: string | null
|
||||
}
|
||||
numComments: number
|
||||
assignedTo: {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
} | null
|
||||
isBookmarked: boolean
|
||||
isSubscribed: boolean
|
||||
subscriptionDetails: Record<string, any> | null
|
||||
hasSeen: boolean
|
||||
annotations: string[]
|
||||
isUnhandled: boolean
|
||||
count: string
|
||||
userCount: number
|
||||
firstSeen: string
|
||||
lastSeen: string
|
||||
stats: Record<string, any>
|
||||
}
|
||||
|
||||
export interface SentryListIssuesParams extends SentryBaseParams {
|
||||
projectSlug?: string
|
||||
query?: string
|
||||
statsPeriod?: string
|
||||
cursor?: string
|
||||
limit?: number
|
||||
status?: string
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export interface SentryListIssuesResponse extends ToolResponse {
|
||||
output: {
|
||||
issues: SentryIssue[]
|
||||
metadata: {
|
||||
nextCursor?: string
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryGetIssueParams extends SentryBaseParams {
|
||||
issueId: string
|
||||
}
|
||||
|
||||
export interface SentryGetIssueResponse extends ToolResponse {
|
||||
output: {
|
||||
issue: SentryIssue
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryUpdateIssueParams extends SentryBaseParams {
|
||||
issueId: string
|
||||
status?: string
|
||||
assignedTo?: string
|
||||
isBookmarked?: boolean
|
||||
isSubscribed?: boolean
|
||||
isPublic?: boolean
|
||||
}
|
||||
|
||||
export interface SentryUpdateIssueResponse extends ToolResponse {
|
||||
output: {
|
||||
issue: SentryIssue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentry project representation
|
||||
*/
|
||||
interface SentryProject {
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
platform: string
|
||||
dateCreated: string
|
||||
isBookmarked: boolean
|
||||
isMember: boolean
|
||||
features: string[]
|
||||
firstEvent: string | null
|
||||
firstTransactionEvent: boolean | null
|
||||
access: string[]
|
||||
hasAccess: boolean
|
||||
hasMinifiedStackTrace: boolean
|
||||
hasMonitors: boolean
|
||||
hasProfiles: boolean
|
||||
hasReplays: boolean
|
||||
hasSessions: boolean
|
||||
isInternal: boolean
|
||||
organization: {
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
}
|
||||
team: {
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
}
|
||||
teams: Array<{
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
}>
|
||||
status: string
|
||||
color: string
|
||||
isPublic: boolean
|
||||
}
|
||||
|
||||
export interface SentryListProjectsParams extends SentryBaseParams {
|
||||
cursor?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface SentryListProjectsResponse extends ToolResponse {
|
||||
output: {
|
||||
projects: SentryProject[]
|
||||
metadata: {
|
||||
nextCursor?: string
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryGetProjectParams extends SentryBaseParams {
|
||||
projectSlug: string
|
||||
}
|
||||
|
||||
export interface SentryGetProjectResponse extends ToolResponse {
|
||||
output: {
|
||||
project: SentryProject
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryCreateProjectParams extends SentryBaseParams {
|
||||
name: string
|
||||
slug?: string
|
||||
platform?: string
|
||||
teamSlug: string
|
||||
defaultRules?: boolean
|
||||
}
|
||||
|
||||
export interface SentryCreateProjectResponse extends ToolResponse {
|
||||
output: {
|
||||
project: SentryProject
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryUpdateProjectParams extends SentryBaseParams {
|
||||
projectSlug: string
|
||||
name?: string
|
||||
slug?: string
|
||||
platform?: string
|
||||
isBookmarked?: boolean
|
||||
digestsMinDelay?: number
|
||||
digestsMaxDelay?: number
|
||||
}
|
||||
|
||||
export interface SentryUpdateProjectResponse extends ToolResponse {
|
||||
output: {
|
||||
project: SentryProject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentry team representation
|
||||
*/
|
||||
interface SentryTeam {
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
dateCreated: string
|
||||
isMember: boolean
|
||||
teamRole: string | null
|
||||
hasAccess: boolean
|
||||
isPending: boolean
|
||||
memberCount: number
|
||||
projects: Array<{
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
platform: string | null
|
||||
}>
|
||||
}
|
||||
|
||||
export interface SentryListTeamsParams extends SentryBaseParams {
|
||||
query?: string
|
||||
cursor?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface SentryListTeamsResponse extends ToolResponse {
|
||||
output: {
|
||||
teams: SentryTeam[]
|
||||
metadata: {
|
||||
nextCursor?: string
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentry event representation
|
||||
*/
|
||||
interface SentryEvent {
|
||||
id: string
|
||||
eventID: string
|
||||
projectID: string
|
||||
groupID: string
|
||||
message: string
|
||||
title: string
|
||||
location: string | null
|
||||
culprit: string
|
||||
dateCreated: string
|
||||
dateReceived: string
|
||||
user: {
|
||||
id: string
|
||||
email: string
|
||||
username: string
|
||||
ipAddress: string
|
||||
name: string
|
||||
} | null
|
||||
tags: Array<{
|
||||
key: string
|
||||
value: string
|
||||
}>
|
||||
contexts: Record<string, any>
|
||||
platform: string
|
||||
type: string
|
||||
metadata: {
|
||||
type: string | null
|
||||
value: string | null
|
||||
function: string | null
|
||||
}
|
||||
entries: Array<{
|
||||
type: string
|
||||
data: Record<string, any>
|
||||
}>
|
||||
errors: Array<{
|
||||
type: string
|
||||
message: string
|
||||
data: Record<string, any>
|
||||
}>
|
||||
dist: string | null
|
||||
fingerprints: string[]
|
||||
size: number | null
|
||||
release: Record<string, any> | null
|
||||
sdk: {
|
||||
name: string
|
||||
version: string
|
||||
} | null
|
||||
}
|
||||
|
||||
export interface SentryListEventsParams extends SentryBaseParams {
|
||||
projectSlug: string
|
||||
issueId?: string
|
||||
query?: string
|
||||
cursor?: string
|
||||
limit?: number
|
||||
statsPeriod?: string
|
||||
}
|
||||
|
||||
export interface SentryListEventsResponse extends ToolResponse {
|
||||
output: {
|
||||
events: SentryEvent[]
|
||||
metadata: {
|
||||
nextCursor?: string
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryGetEventParams extends SentryBaseParams {
|
||||
projectSlug: string
|
||||
eventId: string
|
||||
}
|
||||
|
||||
export interface SentryGetEventResponse extends ToolResponse {
|
||||
output: {
|
||||
event: SentryEvent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentry release representation
|
||||
*/
|
||||
interface SentryRelease {
|
||||
id: string
|
||||
version: string
|
||||
shortVersion: string
|
||||
ref: string | null
|
||||
url: string | null
|
||||
dateReleased: string | null
|
||||
dateCreated: string
|
||||
dateStarted: string | null
|
||||
data: Record<string, any>
|
||||
newGroups: number
|
||||
owner: {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
} | null
|
||||
commitCount: number
|
||||
lastCommit: {
|
||||
id: string
|
||||
message: string
|
||||
dateCreated: string
|
||||
} | null
|
||||
deployCount: number
|
||||
lastDeploy: {
|
||||
id: string
|
||||
environment: string
|
||||
dateStarted: string
|
||||
dateFinished: string
|
||||
} | null
|
||||
authors: Array<{
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
}>
|
||||
projects: Array<{
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
platform: string
|
||||
}>
|
||||
firstEvent: string | null
|
||||
lastEvent: string | null
|
||||
versionInfo: {
|
||||
buildHash: string | null
|
||||
version: {
|
||||
raw: string
|
||||
}
|
||||
package: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryListReleasesParams extends SentryBaseParams {
|
||||
projectSlug?: string
|
||||
query?: string
|
||||
cursor?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface SentryListReleasesResponse extends ToolResponse {
|
||||
output: {
|
||||
releases: SentryRelease[]
|
||||
metadata: {
|
||||
nextCursor?: string
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryCreateReleaseParams extends SentryBaseParams {
|
||||
version: string
|
||||
ref?: string
|
||||
url?: string
|
||||
projects: string
|
||||
dateReleased?: string
|
||||
commits?: string
|
||||
}
|
||||
|
||||
export interface SentryCreateReleaseResponse extends ToolResponse {
|
||||
output: {
|
||||
release: SentryRelease
|
||||
}
|
||||
}
|
||||
|
||||
export interface SentryCreateDeployParams extends SentryBaseParams {
|
||||
version: string
|
||||
environment: string
|
||||
name?: string
|
||||
url?: string
|
||||
dateStarted?: string
|
||||
dateFinished?: string
|
||||
}
|
||||
|
||||
export interface SentryCreateDeployResponse extends ToolResponse {
|
||||
output: {
|
||||
deploy: {
|
||||
id: string
|
||||
environment: string
|
||||
name: string | null
|
||||
url: string | null
|
||||
dateStarted: string
|
||||
dateFinished: string | null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Union response type for all Sentry operations
|
||||
*/
|
||||
export type SentryResponse =
|
||||
| SentryListTeamsResponse
|
||||
| SentryListIssuesResponse
|
||||
| SentryGetIssueResponse
|
||||
| SentryUpdateIssueResponse
|
||||
| SentryListProjectsResponse
|
||||
| SentryGetProjectResponse
|
||||
| SentryCreateProjectResponse
|
||||
| SentryUpdateProjectResponse
|
||||
| SentryListEventsResponse
|
||||
| SentryGetEventResponse
|
||||
| SentryListReleasesResponse
|
||||
| SentryCreateReleaseResponse
|
||||
| SentryCreateDeployResponse
|
||||
Reference in New Issue
Block a user