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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+203
View File
@@ -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' },
},
},
},
},
},
}
+283
View File
@@ -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',
},
},
},
},
}
+14
View File
@@ -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'
+183
View File
@@ -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' },
},
},
},
}
+293
View File
@@ -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',
},
},
},
},
}
+190
View File
@@ -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' },
},
},
},
}
+190
View File
@@ -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' },
},
},
},
}
+178
View File
@@ -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' },
},
},
},
}
+192
View File
@@ -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',
},
},
},
},
}
+191
View File
@@ -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' },
},
},
},
},
},
},
}
+276
View File
@@ -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' },
},
},
},
},
},
}
+138
View File
@@ -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,
},
},
},
},
}
+297
View File
@@ -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',
},
},
},
},
}
+175
View File
@@ -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',
},
},
},
},
}
+444
View File
@@ -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