import type { SentryListReleasesParams, SentryListReleasesResponse } from '@/tools/sentry/types' import type { ToolConfig } from '@/tools/types' export const listReleasesTool: ToolConfig = { 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', }, }, }, }, }