chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (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
@@ -0,0 +1,96 @@
import type {
RootlyAcknowledgeAlertParams,
RootlyAcknowledgeAlertResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyAcknowledgeAlertTool: ToolConfig<
RootlyAcknowledgeAlertParams,
RootlyAcknowledgeAlertResponse
> = {
id: 'rootly_acknowledge_alert',
name: 'Rootly Acknowledge Alert',
description: 'Acknowledge an alert in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
alertId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the alert to acknowledge',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/acknowledge`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: () => ({ data: {} }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alert: {} as RootlyAcknowledgeAlertResponse['output']['alert'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
alert: {
id: data.data?.id ?? null,
shortId: attrs.short_id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
source: attrs.source ?? null,
status: attrs.status ?? null,
externalId: attrs.external_id ?? null,
externalUrl: attrs.external_url ?? null,
deduplicationKey: attrs.deduplication_key ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
endedAt: attrs.ended_at ?? null,
},
},
}
},
outputs: {
alert: {
type: 'object',
description: 'The acknowledged alert',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
}
+117
View File
@@ -0,0 +1,117 @@
import type {
RootlyAddIncidentEventParams,
RootlyAddIncidentEventResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyAddIncidentEventTool: ToolConfig<
RootlyAddIncidentEventParams,
RootlyAddIncidentEventResponse
> = {
id: 'rootly_add_incident_event',
name: 'Rootly Add Incident Event',
description: 'Add a timeline event to an existing incident in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to add the event to',
},
event: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The summary/description of the event',
},
visibility: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Event visibility (internal or external)',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/events`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {
event: params.event,
}
if (params.visibility) attributes.visibility = params.visibility
return { data: { type: 'incident_events', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: {
eventId: '',
event: '',
visibility: null,
occurredAt: null,
createdAt: '',
updatedAt: '',
},
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
eventId: data.data?.id ?? '',
event: attrs.event ?? '',
visibility: attrs.visibility ?? null,
occurredAt: attrs.occurred_at ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
},
}
},
outputs: {
eventId: {
type: 'string',
description: 'The ID of the created event',
},
event: {
type: 'string',
description: 'The event summary',
},
visibility: {
type: 'string',
description: 'Event visibility (internal or external)',
},
occurredAt: {
type: 'string',
description: 'When the event occurred',
},
createdAt: {
type: 'string',
description: 'Creation date',
},
updatedAt: {
type: 'string',
description: 'Last update date',
},
},
}
+117
View File
@@ -0,0 +1,117 @@
import type { RootlyAddSubscribersParams, RootlyIncidentActionResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyAddSubscribersTool: ToolConfig<
RootlyAddSubscribersParams,
RootlyIncidentActionResponse
> = {
id: 'rootly_add_subscribers',
name: 'Rootly Add Subscribers',
description: 'Subscribe users to a Rootly incident so they receive updates.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident',
},
userIds: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated user IDs to subscribe (use List Users to find IDs)',
},
},
request: {
url: (params) =>
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/add_subscribers`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => ({
data: {
type: 'incidents',
attributes: {
user_ids: params.userIds.split(',').map((s: string) => s.trim()),
},
},
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The incident after subscribers were added',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
@@ -0,0 +1,127 @@
import type {
RootlyAssignIncidentRoleParams,
RootlyIncidentActionResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyAssignIncidentRoleTool: ToolConfig<
RootlyAssignIncidentRoleParams,
RootlyIncidentActionResponse
> = {
id: 'rootly_assign_incident_role',
name: 'Rootly Assign Incident Role',
description: 'Assign an incident role (e.g. commander) to a user on a Rootly incident.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the user to assign (use List Users to find IDs)',
},
incidentRoleId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident role (use List Incident Roles to find IDs)',
},
},
request: {
url: (params) =>
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/assign_role_to_user`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => ({
data: {
type: 'incidents',
attributes: {
user_id: params.userId.trim(),
incident_role_id: params.incidentRoleId.trim(),
},
},
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The incident after the role assignment',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
+144
View File
@@ -0,0 +1,144 @@
import type {
RootlyCreateActionItemParams,
RootlyCreateActionItemResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyCreateActionItemTool: ToolConfig<
RootlyCreateActionItemParams,
RootlyCreateActionItemResponse
> = {
id: 'rootly_create_action_item',
name: 'Rootly Create Action Item',
description: 'Create a new action item for an incident in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to add the action item to',
},
summary: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The title of the action item',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'A detailed description of the action item',
},
kind: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The kind of action item (task, follow_up)',
},
priority: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Priority level (high, medium, low)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Action item status (open, in_progress, cancelled, done)',
},
assignedToUserId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The user ID to assign the action item to',
},
dueDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Due date for the action item',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/action_items`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {
summary: params.summary,
}
if (params.description) attributes.description = params.description
if (params.kind) attributes.kind = params.kind
if (params.priority) attributes.priority = params.priority
if (params.status) attributes.status = params.status
if (params.assignedToUserId) {
const numericId = Number.parseInt(params.assignedToUserId, 10)
if (!Number.isNaN(numericId)) attributes.assigned_to_user_id = numericId
}
if (params.dueDate) attributes.due_date = params.dueDate
return { data: { type: 'incident_action_items', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { actionItem: {} as RootlyCreateActionItemResponse['output']['actionItem'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
actionItem: {
id: data.data?.id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
kind: attrs.kind ?? null,
priority: attrs.priority ?? null,
status: attrs.status ?? null,
dueDate: attrs.due_date ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
},
},
}
},
outputs: {
actionItem: {
type: 'object',
description: 'The created action item',
properties: {
id: { type: 'string', description: 'Unique action item ID' },
summary: { type: 'string', description: 'Action item title' },
description: { type: 'string', description: 'Action item description' },
kind: { type: 'string', description: 'Action item kind (task, follow_up)' },
priority: { type: 'string', description: 'Priority level' },
status: { type: 'string', description: 'Action item status' },
dueDate: { type: 'string', description: 'Due date' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
}
+165
View File
@@ -0,0 +1,165 @@
import type { RootlyCreateAlertParams, RootlyCreateAlertResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyCreateAlertTool: ToolConfig<RootlyCreateAlertParams, RootlyCreateAlertResponse> =
{
id: 'rootly_create_alert',
name: 'Rootly Create Alert',
description: 'Create a new alert in Rootly for on-call notification and routing.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
summary: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The summary of the alert',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'A detailed description of the alert',
},
source: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The source of the alert (e.g., api, manual, datadog, pagerduty)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Alert status on creation (open, triggered)',
},
serviceIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated service IDs to attach',
},
groupIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated team/group IDs to attach',
},
environmentIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated environment IDs to attach',
},
externalId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'External ID for the alert',
},
externalUrl: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'External URL for the alert',
},
deduplicationKey: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Alerts sharing the same deduplication key are treated as a single alert',
},
},
request: {
url: 'https://api.rootly.com/v1/alerts',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {
summary: params.summary,
}
if (params.source) attributes.source = params.source
if (params.description) attributes.description = params.description
if (params.status) attributes.status = params.status
if (params.externalId) attributes.external_id = params.externalId
if (params.externalUrl) attributes.external_url = params.externalUrl
if (params.deduplicationKey) attributes.deduplication_key = params.deduplicationKey
if (params.serviceIds) {
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
}
if (params.groupIds) {
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
}
if (params.environmentIds) {
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
}
return { data: { type: 'alerts', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alert: {} as RootlyCreateAlertResponse['output']['alert'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
alert: {
id: data.data?.id ?? null,
shortId: attrs.short_id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
source: attrs.source ?? null,
status: attrs.status ?? null,
externalId: attrs.external_id ?? null,
externalUrl: attrs.external_url ?? null,
deduplicationKey: attrs.deduplication_key ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
endedAt: attrs.ended_at ?? null,
},
},
}
},
outputs: {
alert: {
type: 'object',
description: 'The created alert',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
}
+208
View File
@@ -0,0 +1,208 @@
import type { RootlyCreateIncidentParams, RootlyCreateIncidentResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyCreateIncidentTool: ToolConfig<
RootlyCreateIncidentParams,
RootlyCreateIncidentResponse
> = {
id: 'rootly_create_incident',
name: 'Rootly Create Incident',
description: 'Create a new incident in Rootly with optional severity, services, and teams.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The title of the incident (auto-generated if not provided)',
},
summary: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'A summary of the incident',
},
severityId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Severity ID to attach to the incident',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Incident status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)',
},
kind: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Incident kind (normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub)',
},
serviceIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated service IDs to attach',
},
environmentIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated environment IDs to attach',
},
groupIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated team/group IDs to attach',
},
incidentTypeIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated incident type IDs to attach',
},
functionalityIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated functionality IDs to attach',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Labels as JSON object, e.g. {"platform":"osx","version":"1.29"}',
},
private: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Create as a private incident (cannot be undone)',
},
},
request: {
url: 'https://api.rootly.com/v1/incidents',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.title) attributes.title = params.title
if (params.summary) attributes.summary = params.summary
if (params.severityId) attributes.severity_id = params.severityId
if (params.status) attributes.status = params.status
if (params.kind) attributes.kind = params.kind
if (params.private !== undefined) attributes.private = params.private
if (params.serviceIds) {
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
}
if (params.environmentIds) {
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
}
if (params.groupIds) {
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
}
if (params.incidentTypeIds) {
attributes.incident_type_ids = params.incidentTypeIds
.split(',')
.map((s: string) => s.trim())
}
if (params.functionalityIds) {
attributes.functionality_ids = params.functionalityIds
.split(',')
.map((s: string) => s.trim())
}
if (params.labels) {
try {
attributes.labels = JSON.parse(params.labels)
} catch {
attributes.labels = params.labels
}
}
return { data: { type: 'incidents', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
const errorMsg =
errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`
return {
success: false,
output: { incident: {} as RootlyCreateIncidentResponse['output']['incident'] },
error: errorMsg,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The created incident',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
@@ -0,0 +1,132 @@
import type {
RootlyCreateStatusPageEventParams,
RootlyCreateStatusPageEventResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyCreateStatusPageEventTool: ToolConfig<
RootlyCreateStatusPageEventParams,
RootlyCreateStatusPageEventResponse
> = {
id: 'rootly_create_status_page_event',
name: 'Rootly Create Status Page Event',
description: 'Post a public status page update for a Rootly incident.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident',
},
event: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The status page update message to publish',
},
statusPageId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The ID of the status page to post to',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Status to set (investigating, identified, monitoring, resolved, scheduled, in_progress, completed)',
},
notifySubscribers: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to notify status page subscribers',
},
shouldTweet: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to post the update to the linked Twitter/X account',
},
},
request: {
url: (params) =>
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/status-page-events`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {
event: params.event,
}
if (params.statusPageId) attributes.status_page_id = params.statusPageId.trim()
if (params.status) attributes.status = params.status
if (params.notifySubscribers !== undefined)
attributes.notify_subscribers = params.notifySubscribers
if (params.shouldTweet !== undefined) attributes.should_tweet = params.shouldTweet
return { data: { type: 'incident_status_page_events', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: {
statusPageEvent: {} as RootlyCreateStatusPageEventResponse['output']['statusPageEvent'],
},
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
statusPageEvent: {
id: data.data?.id ?? null,
event: attrs.event ?? '',
statusPageId: attrs.status_page_id ?? null,
status: attrs.status ?? null,
notifySubscribers: attrs.notify_subscribers ?? null,
shouldTweet: attrs.should_tweet ?? null,
startedAt: attrs.started_at ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
},
},
}
},
outputs: {
statusPageEvent: {
type: 'object',
description: 'The created status page event',
properties: {
id: { type: 'string', description: 'Unique status page event ID' },
event: { type: 'string', description: 'The published update message' },
statusPageId: { type: 'string', description: 'Status page ID' },
status: { type: 'string', description: 'Status that was set' },
notifySubscribers: { type: 'boolean', description: 'Whether subscribers were notified' },
shouldTweet: { type: 'boolean', description: 'Whether the update was tweeted' },
startedAt: { type: 'string', description: 'When the event started' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
}
@@ -0,0 +1,69 @@
import type {
RootlyDeleteActionItemParams,
RootlyDeleteActionItemResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyDeleteActionItemTool: ToolConfig<
RootlyDeleteActionItemParams,
RootlyDeleteActionItemResponse
> = {
id: 'rootly_delete_action_item',
name: 'Rootly Delete Action Item',
description: 'Delete a Rootly incident action item.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
actionItemId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the action item to delete',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/action_items/${params.actionItemId.trim()}`,
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { success: false, message: '' },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
return {
success: true,
output: {
success: true,
message: 'Action item deleted successfully',
},
}
},
outputs: {
success: {
type: 'boolean',
description: 'Whether the action item was deleted',
},
message: {
type: 'string',
description: 'Result message',
},
},
}
+64
View File
@@ -0,0 +1,64 @@
import type { RootlyDeleteIncidentParams, RootlyDeleteIncidentResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyDeleteIncidentTool: ToolConfig<
RootlyDeleteIncidentParams,
RootlyDeleteIncidentResponse
> = {
id: 'rootly_delete_incident',
name: 'Rootly Delete Incident',
description: 'Delete an incident by ID from Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to delete',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`,
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: {
success: false,
message:
errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
},
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
return {
success: true,
output: {
success: true,
message: 'Incident deleted successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether the deletion succeeded' },
message: { type: 'string', description: 'Result message' },
},
}
+113
View File
@@ -0,0 +1,113 @@
import type { RootlyAlertActionResponse, RootlyEscalateAlertParams } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyEscalateAlertTool: ToolConfig<
RootlyEscalateAlertParams,
RootlyAlertActionResponse
> = {
id: 'rootly_escalate_alert',
name: 'Rootly Escalate Alert',
description: 'Escalate a Rootly alert, optionally to a specific escalation policy or level.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
alertId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the alert to escalate',
},
escalationPolicyId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Escalation policy ID to escalate to (use List Escalation Policies to find IDs)',
},
escalationPolicyLevel: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Escalation policy level to escalate to',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/escalate`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.escalationPolicyId)
attributes.escalation_policy_id = params.escalationPolicyId.trim()
if (params.escalationPolicyLevel !== undefined)
attributes.escalation_policy_level = params.escalationPolicyLevel
if (Object.keys(attributes).length === 0) return { data: {} }
return { data: { type: 'alerts', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alert: {} as RootlyAlertActionResponse['output']['alert'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
alert: {
id: data.data?.id ?? null,
shortId: attrs.short_id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
source: attrs.source ?? null,
status: attrs.status ?? null,
externalId: attrs.external_id ?? null,
externalUrl: attrs.external_url ?? null,
deduplicationKey: attrs.deduplication_key ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
endedAt: attrs.ended_at ?? null,
},
},
}
},
outputs: {
alert: {
type: 'object',
description: 'The escalated alert',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
}
+89
View File
@@ -0,0 +1,89 @@
import type { RootlyGetAlertParams, RootlyGetAlertResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyGetAlertTool: ToolConfig<RootlyGetAlertParams, RootlyGetAlertResponse> = {
id: 'rootly_get_alert',
name: 'Rootly Get Alert',
description: 'Retrieve a single alert by ID from Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
alertId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the alert to retrieve',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}`,
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alert: {} as RootlyGetAlertResponse['output']['alert'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
alert: {
id: data.data?.id ?? null,
shortId: attrs.short_id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
source: attrs.source ?? null,
status: attrs.status ?? null,
externalId: attrs.external_id ?? null,
externalUrl: attrs.external_url ?? null,
deduplicationKey: attrs.deduplication_key ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
endedAt: attrs.ended_at ?? null,
},
},
}
},
outputs: {
alert: {
type: 'object',
description: 'The alert details',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
}
+100
View File
@@ -0,0 +1,100 @@
import type { RootlyGetIncidentParams, RootlyGetIncidentResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyGetIncidentTool: ToolConfig<RootlyGetIncidentParams, RootlyGetIncidentResponse> =
{
id: 'rootly_get_incident',
name: 'Rootly Get Incident',
description: 'Retrieve a single incident by ID from Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to retrieve',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`,
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyGetIncidentResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The incident details',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
+42
View File
@@ -0,0 +1,42 @@
export { rootlyAcknowledgeAlertTool } from '@/tools/rootly/acknowledge_alert'
export { rootlyAddIncidentEventTool } from '@/tools/rootly/add_incident_event'
export { rootlyAddSubscribersTool } from '@/tools/rootly/add_subscribers'
export { rootlyAssignIncidentRoleTool } from '@/tools/rootly/assign_incident_role'
export { rootlyCreateActionItemTool } from '@/tools/rootly/create_action_item'
export { rootlyCreateAlertTool } from '@/tools/rootly/create_alert'
export { rootlyCreateIncidentTool } from '@/tools/rootly/create_incident'
export { rootlyCreateStatusPageEventTool } from '@/tools/rootly/create_status_page_event'
export { rootlyDeleteActionItemTool } from '@/tools/rootly/delete_action_item'
export { rootlyDeleteIncidentTool } from '@/tools/rootly/delete_incident'
export { rootlyEscalateAlertTool } from '@/tools/rootly/escalate_alert'
export { rootlyGetAlertTool } from '@/tools/rootly/get_alert'
export { rootlyGetIncidentTool } from '@/tools/rootly/get_incident'
export { rootlyListActionItemsTool } from '@/tools/rootly/list_action_items'
export { rootlyListAlertsTool } from '@/tools/rootly/list_alerts'
export { rootlyListCausesTool } from '@/tools/rootly/list_causes'
export { rootlyListEnvironmentsTool } from '@/tools/rootly/list_environments'
export { rootlyListEscalationPoliciesTool } from '@/tools/rootly/list_escalation_policies'
export { rootlyListFunctionalitiesTool } from '@/tools/rootly/list_functionalities'
export { rootlyListIncidentEventsTool } from '@/tools/rootly/list_incident_events'
export { rootlyListIncidentRolesTool } from '@/tools/rootly/list_incident_roles'
export { rootlyListIncidentTypesTool } from '@/tools/rootly/list_incident_types'
export { rootlyListIncidentsTool } from '@/tools/rootly/list_incidents'
export { rootlyListOnCallsTool } from '@/tools/rootly/list_on_calls'
export { rootlyListPlaybooksTool } from '@/tools/rootly/list_playbooks'
export { rootlyListRetrospectivesTool } from '@/tools/rootly/list_retrospectives'
export { rootlyListSchedulesTool } from '@/tools/rootly/list_schedules'
export { rootlyListServicesTool } from '@/tools/rootly/list_services'
export { rootlyListSeveritiesTool } from '@/tools/rootly/list_severities'
export { rootlyListTeamsTool } from '@/tools/rootly/list_teams'
export { rootlyListUsersTool } from '@/tools/rootly/list_users'
export { rootlyMitigateIncidentTool } from '@/tools/rootly/mitigate_incident'
export { rootlyRemoveSubscribersTool } from '@/tools/rootly/remove_subscribers'
export { rootlyResolveAlertTool } from '@/tools/rootly/resolve_alert'
export { rootlyResolveIncidentTool } from '@/tools/rootly/resolve_incident'
export { rootlyRunWorkflowTool } from '@/tools/rootly/run_workflow'
export { rootlySnoozeAlertTool } from '@/tools/rootly/snooze_alert'
export * from '@/tools/rootly/types'
export { rootlyUnassignIncidentRoleTool } from '@/tools/rootly/unassign_incident_role'
export { rootlyUpdateActionItemTool } from '@/tools/rootly/update_action_item'
export { rootlyUpdateAlertTool } from '@/tools/rootly/update_alert'
export { rootlyUpdateIncidentTool } from '@/tools/rootly/update_incident'
+117
View File
@@ -0,0 +1,117 @@
import type {
RootlyListActionItemsParams,
RootlyListActionItemsResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListActionItemsTool: ToolConfig<
RootlyListActionItemsParams,
RootlyListActionItemsResponse
> = {
id: 'rootly_list_action_items',
name: 'Rootly List Action Items',
description: 'List action items for an incident in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to list action items for',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/action_items${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { actionItems: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const actionItems = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
summary: (attrs.summary as string) ?? '',
description: (attrs.description as string) ?? null,
kind: (attrs.kind as string) ?? null,
priority: (attrs.priority as string) ?? null,
status: (attrs.status as string) ?? null,
dueDate: (attrs.due_date as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
actionItems,
totalCount: data.meta?.total_count ?? actionItems.length,
},
}
},
outputs: {
actionItems: {
type: 'array',
description: 'List of action items',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique action item ID' },
summary: { type: 'string', description: 'Action item title' },
description: { type: 'string', description: 'Action item description' },
kind: { type: 'string', description: 'Action item kind (task, follow_up)' },
priority: { type: 'string', description: 'Priority level' },
status: { type: 'string', description: 'Action item status' },
dueDate: { type: 'string', description: 'Due date' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of action items returned',
},
},
}
+148
View File
@@ -0,0 +1,148 @@
import type { RootlyListAlertsParams, RootlyListAlertsResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListAlertsTool: ToolConfig<RootlyListAlertsParams, RootlyListAlertsResponse> = {
id: 'rootly_list_alerts',
name: 'Rootly List Alerts',
description: 'List alerts from Rootly with optional filtering by status, source, and services.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (open, triggered, acknowledged, resolved)',
},
source: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by source (e.g., api, datadog, pagerduty)',
},
services: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by service slugs (comma-separated)',
},
environments: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by environment slugs (comma-separated)',
},
groups: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by team/group slugs (comma-separated)',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.set('filter[status]', params.status)
if (params.source) queryParams.set('filter[source]', params.source)
if (params.services) queryParams.set('filter[services]', params.services)
if (params.environments) queryParams.set('filter[environments]', params.environments)
if (params.groups) queryParams.set('filter[groups]', params.groups)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/alerts${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alerts: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const alerts = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
shortId: (attrs.short_id as string) ?? null,
summary: (attrs.summary as string) ?? '',
description: (attrs.description as string) ?? null,
source: (attrs.source as string) ?? null,
status: (attrs.status as string) ?? null,
externalId: (attrs.external_id as string) ?? null,
externalUrl: (attrs.external_url as string) ?? null,
deduplicationKey: (attrs.deduplication_key as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
startedAt: (attrs.started_at as string) ?? null,
endedAt: (attrs.ended_at as string) ?? null,
}
})
return {
success: true,
output: {
alerts,
totalCount: data.meta?.total_count ?? alerts.length,
},
}
},
outputs: {
alerts: {
type: 'array',
description: 'List of alerts',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of alerts returned',
},
},
}
+108
View File
@@ -0,0 +1,108 @@
import type { RootlyListCausesParams, RootlyListCausesResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListCausesTool: ToolConfig<RootlyListCausesParams, RootlyListCausesResponse> = {
id: 'rootly_list_causes',
name: 'Rootly List Causes',
description: 'List causes from Rootly with optional search filtering.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter causes',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/causes${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { causes: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const causes = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
description: (attrs.description as string) ?? null,
position: (attrs.position as number) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
causes,
totalCount: data.meta?.total_count ?? causes.length,
},
}
},
outputs: {
causes: {
type: 'array',
description: 'List of causes',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique cause ID' },
name: { type: 'string', description: 'Cause name' },
slug: { type: 'string', description: 'Cause slug' },
description: { type: 'string', description: 'Cause description' },
position: { type: 'number', description: 'Cause position' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of causes returned',
},
},
}
+114
View File
@@ -0,0 +1,114 @@
import type {
RootlyListEnvironmentsParams,
RootlyListEnvironmentsResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListEnvironmentsTool: ToolConfig<
RootlyListEnvironmentsParams,
RootlyListEnvironmentsResponse
> = {
id: 'rootly_list_environments',
name: 'Rootly List Environments',
description: 'List environments configured in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter environments',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/environments${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { environments: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const environments = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
description: (attrs.description as string) ?? null,
color: (attrs.color as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
environments,
totalCount: data.meta?.total_count ?? environments.length,
},
}
},
outputs: {
environments: {
type: 'array',
description: 'List of environments',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique environment ID' },
name: { type: 'string', description: 'Environment name' },
slug: { type: 'string', description: 'Environment slug' },
description: { type: 'string', description: 'Environment description' },
color: { type: 'string', description: 'Environment color' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of environments returned',
},
},
}
@@ -0,0 +1,116 @@
import type {
RootlyListEscalationPoliciesParams,
RootlyListEscalationPoliciesResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListEscalationPoliciesTool: ToolConfig<
RootlyListEscalationPoliciesParams,
RootlyListEscalationPoliciesResponse
> = {
id: 'rootly_list_escalation_policies',
name: 'Rootly List Escalation Policies',
description: 'List escalation policies from Rootly with optional search filtering.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter escalation policies',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/escalation_policies${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { escalationPolicies: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const escalationPolicies = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
description: (attrs.description as string) ?? null,
repeatCount: (attrs.repeat_count as number) ?? null,
groupIds: (attrs.group_ids as string[]) ?? null,
serviceIds: (attrs.service_ids as string[]) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
escalationPolicies,
totalCount: data.meta?.total_count ?? escalationPolicies.length,
},
}
},
outputs: {
escalationPolicies: {
type: 'array',
description: 'List of escalation policies',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique escalation policy ID' },
name: { type: 'string', description: 'Escalation policy name' },
description: { type: 'string', description: 'Escalation policy description' },
repeatCount: { type: 'number', description: 'Number of times to repeat escalation' },
groupIds: { type: 'array', description: 'Associated group IDs' },
serviceIds: { type: 'array', description: 'Associated service IDs' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of escalation policies returned',
},
},
}
@@ -0,0 +1,114 @@
import type {
RootlyListFunctionalitiesParams,
RootlyListFunctionalitiesResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListFunctionalitiesTool: ToolConfig<
RootlyListFunctionalitiesParams,
RootlyListFunctionalitiesResponse
> = {
id: 'rootly_list_functionalities',
name: 'Rootly List Functionalities',
description: 'List functionalities configured in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter functionalities',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/functionalities${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { functionalities: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const functionalities = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
description: (attrs.description as string) ?? null,
color: (attrs.color as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
functionalities,
totalCount: data.meta?.total_count ?? functionalities.length,
},
}
},
outputs: {
functionalities: {
type: 'array',
description: 'List of functionalities',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique functionality ID' },
name: { type: 'string', description: 'Functionality name' },
slug: { type: 'string', description: 'Functionality slug' },
description: { type: 'string', description: 'Functionality description' },
color: { type: 'string', description: 'Functionality color' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of functionalities returned',
},
},
}
@@ -0,0 +1,111 @@
import type {
RootlyListIncidentEventsParams,
RootlyListIncidentEventsResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListIncidentEventsTool: ToolConfig<
RootlyListIncidentEventsParams,
RootlyListIncidentEventsResponse
> = {
id: 'rootly_list_incident_events',
name: 'Rootly List Incident Events',
description: 'List the timeline events for a Rootly incident.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/events${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { events: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const events = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
event: (attrs.event as string) ?? '',
visibility: (attrs.visibility as string) ?? null,
occurredAt: (attrs.occurred_at as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
events,
totalCount: data.meta?.total_count ?? events.length,
},
}
},
outputs: {
events: {
type: 'array',
description: 'List of incident timeline events',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique event ID' },
event: { type: 'string', description: 'The event description' },
visibility: { type: 'string', description: 'Event visibility (internal or external)' },
occurredAt: { type: 'string', description: 'When the event occurred' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of events returned',
},
},
}
@@ -0,0 +1,120 @@
import type {
RootlyListIncidentRolesParams,
RootlyListIncidentRolesResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListIncidentRolesTool: ToolConfig<
RootlyListIncidentRolesParams,
RootlyListIncidentRolesResponse
> = {
id: 'rootly_list_incident_roles',
name: 'Rootly List Incident Roles',
description: 'List incident roles configured in Rootly (e.g. commander, scribe).',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter incident roles',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/incident_roles${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incidentRoles: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const incidentRoles = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
summary: (attrs.summary as string) ?? null,
description: (attrs.description as string) ?? null,
position: (attrs.position as number) ?? null,
optional: (attrs.optional as boolean) ?? null,
enabled: (attrs.enabled as boolean) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
incidentRoles,
totalCount: data.meta?.total_count ?? incidentRoles.length,
},
}
},
outputs: {
incidentRoles: {
type: 'array',
description: 'List of incident roles',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique incident role ID' },
name: { type: 'string', description: 'Role name' },
slug: { type: 'string', description: 'Role slug' },
summary: { type: 'string', description: 'Role summary' },
description: { type: 'string', description: 'Role description' },
position: { type: 'number', description: 'Display position' },
optional: { type: 'boolean', description: 'Whether the role is optional' },
enabled: { type: 'boolean', description: 'Whether the role is enabled' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of incident roles returned',
},
},
}
@@ -0,0 +1,114 @@
import type {
RootlyListIncidentTypesParams,
RootlyListIncidentTypesResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListIncidentTypesTool: ToolConfig<
RootlyListIncidentTypesParams,
RootlyListIncidentTypesResponse
> = {
id: 'rootly_list_incident_types',
name: 'Rootly List Incident Types',
description: 'List incident types configured in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter incident types by name',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[name]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/incident_types${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incidentTypes: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const incidentTypes = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
description: (attrs.description as string) ?? null,
color: (attrs.color as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
incidentTypes,
totalCount: data.meta?.total_count ?? incidentTypes.length,
},
}
},
outputs: {
incidentTypes: {
type: 'array',
description: 'List of incident types',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique incident type ID' },
name: { type: 'string', description: 'Incident type name' },
slug: { type: 'string', description: 'Incident type slug' },
description: { type: 'string', description: 'Incident type description' },
color: { type: 'string', description: 'Incident type color' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of incident types returned',
},
},
}
+179
View File
@@ -0,0 +1,179 @@
import type { RootlyListIncidentsParams, RootlyListIncidentsResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListIncidentsTool: ToolConfig<
RootlyListIncidentsParams,
RootlyListIncidentsResponse
> = {
id: 'rootly_list_incidents',
name: 'Rootly List Incidents',
description: 'List incidents from Rootly with optional filtering by status, severity, and more.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)',
},
severity: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by severity slug',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter incidents',
},
services: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by service slugs (comma-separated)',
},
teams: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by team slugs (comma-separated)',
},
environments: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by environment slugs (comma-separated)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order (e.g., -created_at, created_at, -started_at)',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.set('filter[status]', params.status)
if (params.severity) queryParams.set('filter[severity]', params.severity)
if (params.search) queryParams.set('filter[search]', params.search)
if (params.services) queryParams.set('filter[services]', params.services)
if (params.teams) queryParams.set('filter[teams]', params.teams)
if (params.environments) queryParams.set('filter[environments]', params.environments)
if (params.sort) queryParams.set('sort', params.sort)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/incidents${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incidents: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const incidents = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
const severity = attrs.severity as Record<string, unknown> | undefined
const severityData = severity?.data as Record<string, unknown> | undefined
const severityAttrs = severityData?.attributes as Record<string, unknown> | undefined
return {
id: item.id ?? null,
sequentialId: (attrs.sequential_id as number) ?? null,
title: (attrs.title as string) ?? '',
slug: (attrs.slug as string) ?? null,
kind: (attrs.kind as string) ?? null,
summary: (attrs.summary as string) ?? null,
status: (attrs.status as string) ?? null,
private: (attrs.private as boolean) ?? false,
url: (attrs.url as string) ?? null,
shortUrl: (attrs.short_url as string) ?? null,
severityName: (severityAttrs?.name as string) ?? null,
severityId: (severityData?.id as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
startedAt: (attrs.started_at as string) ?? null,
mitigatedAt: (attrs.mitigated_at as string) ?? null,
resolvedAt: (attrs.resolved_at as string) ?? null,
closedAt: (attrs.closed_at as string) ?? null,
}
})
return {
success: true,
output: {
incidents,
totalCount: data.meta?.total_count ?? incidents.length,
},
}
},
outputs: {
incidents: {
type: 'array',
description: 'List of incidents',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of incidents returned',
},
},
}
+143
View File
@@ -0,0 +1,143 @@
import type { RootlyListOnCallsParams, RootlyListOnCallsResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListOnCallsTool: ToolConfig<RootlyListOnCallsParams, RootlyListOnCallsResponse> =
{
id: 'rootly_list_on_calls',
name: 'Rootly List On-Calls',
description: 'List current on-call entries from Rootly with optional filtering.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
scheduleIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated schedule IDs to filter by',
},
escalationPolicyIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated escalation policy IDs to filter by',
},
userIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated user IDs to filter by',
},
serviceIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated service IDs to filter by',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.scheduleIds) queryParams.set('filter[schedule_ids]', params.scheduleIds)
if (params.escalationPolicyIds)
queryParams.set('filter[escalation_policy_ids]', params.escalationPolicyIds)
if (params.userIds) queryParams.set('filter[user_ids]', params.userIds)
if (params.serviceIds) queryParams.set('filter[service_ids]', params.serviceIds)
queryParams.set('include', 'user,schedule,escalation_policy')
return `https://api.rootly.com/v1/oncalls?${queryParams.toString()}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { onCalls: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const included = (data.included || []) as Record<string, unknown>[]
const findIncluded = (
type: string,
id: string | null
): Record<string, unknown> | undefined =>
id ? included.find((i) => i.type === type && i.id === id) : undefined
const onCalls = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
const rels = (item.relationships || {}) as Record<string, Record<string, unknown>>
const userId = ((rels.user?.data as Record<string, unknown>)?.id as string) ?? null
const scheduleId = ((rels.schedule?.data as Record<string, unknown>)?.id as string) ?? null
const escalationPolicyId =
((rels.escalation_policy?.data as Record<string, unknown>)?.id as string) ?? null
const userIncl = findIncluded('users', userId)
const scheduleIncl = findIncluded('schedules', scheduleId)
return {
id: (item.id as string) ?? null,
userId,
userName: userIncl
? (((userIncl.attributes as Record<string, unknown>)?.full_name as string) ?? null)
: null,
scheduleId,
scheduleName: scheduleIncl
? (((scheduleIncl.attributes as Record<string, unknown>)?.name as string) ?? null)
: null,
escalationPolicyId,
startTime: (attrs.starts_at as string) ?? null,
endTime: (attrs.ends_at as string) ?? null,
}
})
return {
success: true,
output: {
onCalls,
totalCount: data.meta?.total_count ?? onCalls.length,
},
}
},
outputs: {
onCalls: {
type: 'array',
description: 'List of on-call entries',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique on-call entry ID' },
userId: { type: 'string', description: 'ID of the on-call user' },
userName: { type: 'string', description: 'Name of the on-call user' },
scheduleId: { type: 'string', description: 'ID of the associated schedule' },
scheduleName: { type: 'string', description: 'Name of the associated schedule' },
escalationPolicyId: {
type: 'string',
description: 'ID of the associated escalation policy',
},
startTime: { type: 'string', description: 'On-call start time' },
endTime: { type: 'string', description: 'On-call end time' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of on-call entries returned',
},
},
}
+102
View File
@@ -0,0 +1,102 @@
import type { RootlyListPlaybooksParams, RootlyListPlaybooksResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListPlaybooksTool: ToolConfig<
RootlyListPlaybooksParams,
RootlyListPlaybooksResponse
> = {
id: 'rootly_list_playbooks',
name: 'Rootly List Playbooks',
description: 'List playbooks from Rootly with pagination support.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/playbooks${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { playbooks: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const playbooks = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
title: (attrs.title as string) ?? '',
summary: (attrs.summary as string) ?? null,
externalUrl: (attrs.external_url as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
playbooks,
totalCount: data.meta?.total_count ?? playbooks.length,
},
}
},
outputs: {
playbooks: {
type: 'array',
description: 'List of playbooks',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique playbook ID' },
title: { type: 'string', description: 'Playbook title' },
summary: { type: 'string', description: 'Playbook summary' },
externalUrl: { type: 'string', description: 'External URL' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of playbooks returned',
},
},
}
@@ -0,0 +1,125 @@
import type {
RootlyListRetrospectivesParams,
RootlyListRetrospectivesResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListRetrospectivesTool: ToolConfig<
RootlyListRetrospectivesParams,
RootlyListRetrospectivesResponse
> = {
id: 'rootly_list_retrospectives',
name: 'Rootly List Retrospectives',
description: 'List incident retrospectives (post-mortems) from Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (draft, published)',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter retrospectives',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.set('filter[status]', params.status)
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/post_mortems${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { retrospectives: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const retrospectives = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
title: (attrs.title as string) ?? '',
status: (attrs.status as string) ?? null,
url: (attrs.url as string) ?? null,
startedAt: (attrs.started_at as string) ?? null,
mitigatedAt: (attrs.mitigated_at as string) ?? null,
resolvedAt: (attrs.resolved_at as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
retrospectives,
totalCount: data.meta?.total_count ?? retrospectives.length,
},
}
},
outputs: {
retrospectives: {
type: 'array',
description: 'List of retrospectives',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique retrospective ID' },
title: { type: 'string', description: 'Retrospective title' },
status: { type: 'string', description: 'Status (draft or published)' },
url: { type: 'string', description: 'URL to the retrospective' },
startedAt: { type: 'string', description: 'Incident start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of retrospectives returned',
},
},
}
+112
View File
@@ -0,0 +1,112 @@
import type { RootlyListSchedulesParams, RootlyListSchedulesResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListSchedulesTool: ToolConfig<
RootlyListSchedulesParams,
RootlyListSchedulesResponse
> = {
id: 'rootly_list_schedules',
name: 'Rootly List Schedules',
description: 'List on-call schedules from Rootly with optional search filtering.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter schedules',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/schedules${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { schedules: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const schedules = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
description: (attrs.description as string) ?? null,
allTimeCoverage: (attrs.all_time_coverage as boolean) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
schedules,
totalCount: data.meta?.total_count ?? schedules.length,
},
}
},
outputs: {
schedules: {
type: 'array',
description: 'List of schedules',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique schedule ID' },
name: { type: 'string', description: 'Schedule name' },
description: { type: 'string', description: 'Schedule description' },
allTimeCoverage: {
type: 'boolean',
description: 'Whether schedule provides 24/7 coverage',
},
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of schedules returned',
},
},
}
+111
View File
@@ -0,0 +1,111 @@
import type { RootlyListServicesParams, RootlyListServicesResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListServicesTool: ToolConfig<
RootlyListServicesParams,
RootlyListServicesResponse
> = {
id: 'rootly_list_services',
name: 'Rootly List Services',
description: 'List services from Rootly with optional search filtering.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter services',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/services${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { services: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const services = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
description: (attrs.description as string) ?? null,
color: (attrs.color as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
services,
totalCount: data.meta?.total_count ?? services.length,
},
}
},
outputs: {
services: {
type: 'array',
description: 'List of services',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique service ID' },
name: { type: 'string', description: 'Service name' },
slug: { type: 'string', description: 'Service slug' },
description: { type: 'string', description: 'Service description' },
color: { type: 'string', description: 'Service color' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of services returned',
},
},
}
+115
View File
@@ -0,0 +1,115 @@
import type { RootlyListSeveritiesParams, RootlyListSeveritiesResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListSeveritiesTool: ToolConfig<
RootlyListSeveritiesParams,
RootlyListSeveritiesResponse
> = {
id: 'rootly_list_severities',
name: 'Rootly List Severities',
description: 'List severity levels configured in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter severities',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/severities${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { severities: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const severities = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
description: (attrs.description as string) ?? null,
severity: (attrs.severity as string) ?? null,
color: (attrs.color as string) ?? null,
position: (attrs.position as number) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
severities,
totalCount: data.meta?.total_count ?? severities.length,
},
}
},
outputs: {
severities: {
type: 'array',
description: 'List of severity levels',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique severity ID' },
name: { type: 'string', description: 'Severity name' },
slug: { type: 'string', description: 'Severity slug' },
description: { type: 'string', description: 'Severity description' },
severity: { type: 'string', description: 'Severity level (critical, high, medium, low)' },
color: { type: 'string', description: 'Severity color' },
position: { type: 'number', description: 'Display position' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of severities returned',
},
},
}
+108
View File
@@ -0,0 +1,108 @@
import type { RootlyListTeamsParams, RootlyListTeamsResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListTeamsTool: ToolConfig<RootlyListTeamsParams, RootlyListTeamsResponse> = {
id: 'rootly_list_teams',
name: 'Rootly List Teams',
description: 'List teams (groups) configured in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter teams',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/teams${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { teams: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const teams = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
name: (attrs.name as string) ?? '',
slug: (attrs.slug as string) ?? null,
description: (attrs.description as string) ?? null,
color: (attrs.color as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
teams,
totalCount: data.meta?.total_count ?? teams.length,
},
}
},
outputs: {
teams: {
type: 'array',
description: 'List of teams',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique team ID' },
name: { type: 'string', description: 'Team name' },
slug: { type: 'string', description: 'Team slug' },
description: { type: 'string', description: 'Team description' },
color: { type: 'string', description: 'Team color' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of teams returned',
},
},
}
+117
View File
@@ -0,0 +1,117 @@
import type { RootlyListUsersParams, RootlyListUsersResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyListUsersTool: ToolConfig<RootlyListUsersParams, RootlyListUsersResponse> = {
id: 'rootly_list_users',
name: 'Rootly List Users',
description: 'List users from Rootly with optional search and email filtering.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter users',
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter users by email address',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of items per page (default: 20)',
},
pageNumber: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.search) queryParams.set('filter[search]', params.search)
if (params.email) queryParams.set('filter[email]', params.email)
if (params.pageSize) queryParams.set('page[size]', String(params.pageSize))
if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber))
const qs = queryParams.toString()
return `https://api.rootly.com/v1/users${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { users: [], totalCount: 0 },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const users = (data.data || []).map((item: Record<string, unknown>) => {
const attrs = (item.attributes || {}) as Record<string, unknown>
return {
id: item.id ?? null,
email: (attrs.email as string) ?? '',
firstName: (attrs.first_name as string) ?? null,
lastName: (attrs.last_name as string) ?? null,
fullName: (attrs.full_name as string) ?? null,
timeZone: (attrs.time_zone as string) ?? null,
createdAt: (attrs.created_at as string) ?? '',
updatedAt: (attrs.updated_at as string) ?? '',
}
})
return {
success: true,
output: {
users,
totalCount: data.meta?.total_count ?? users.length,
},
}
},
outputs: {
users: {
type: 'array',
description: 'List of users',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique user ID' },
email: { type: 'string', description: 'User email address' },
firstName: { type: 'string', description: 'User first name' },
lastName: { type: 'string', description: 'User last name' },
fullName: { type: 'string', description: 'User full name' },
timeZone: { type: 'string', description: 'User time zone' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
totalCount: {
type: 'number',
description: 'Total number of users returned',
},
},
}
+116
View File
@@ -0,0 +1,116 @@
import type {
RootlyIncidentActionResponse,
RootlyMitigateIncidentParams,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyMitigateIncidentTool: ToolConfig<
RootlyMitigateIncidentParams,
RootlyIncidentActionResponse
> = {
id: 'rootly_mitigate_incident',
name: 'Rootly Mitigate Incident',
description: 'Transition a Rootly incident to the mitigated state.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to mitigate',
},
mitigationMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'How was the incident mitigated?',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/mitigate`,
method: 'PUT',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.mitigationMessage) attributes.mitigation_message = params.mitigationMessage
return { data: { type: 'incidents', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The mitigated incident',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
+120
View File
@@ -0,0 +1,120 @@
import type {
RootlyIncidentActionResponse,
RootlyRemoveSubscribersParams,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyRemoveSubscribersTool: ToolConfig<
RootlyRemoveSubscribersParams,
RootlyIncidentActionResponse
> = {
id: 'rootly_remove_subscribers',
name: 'Rootly Remove Subscribers',
description: 'Unsubscribe users from a Rootly incident.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident',
},
userIds: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated user IDs to unsubscribe (use List Users to find IDs)',
},
},
request: {
url: (params) =>
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/remove_subscribers`,
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => ({
data: {
type: 'incidents',
attributes: {
user_ids: params.userIds.split(',').map((s: string) => s.trim()),
},
},
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The incident after subscribers were removed',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
+112
View File
@@ -0,0 +1,112 @@
import type { RootlyResolveAlertParams, RootlyResolveAlertResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyResolveAlertTool: ToolConfig<
RootlyResolveAlertParams,
RootlyResolveAlertResponse
> = {
id: 'rootly_resolve_alert',
name: 'Rootly Resolve Alert',
description: 'Resolve an alert in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
alertId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the alert to resolve',
},
resolutionMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Message describing how the alert was resolved',
},
resolveRelatedIncidents: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to also resolve related incidents',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/resolve`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage
if (params.resolveRelatedIncidents !== undefined)
attributes.resolve_related_incidents = params.resolveRelatedIncidents
if (Object.keys(attributes).length === 0) return { data: {} }
return { data: { type: 'alerts', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alert: {} as RootlyResolveAlertResponse['output']['alert'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
alert: {
id: data.data?.id ?? null,
shortId: attrs.short_id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
source: attrs.source ?? null,
status: attrs.status ?? null,
externalId: attrs.external_id ?? null,
externalUrl: attrs.external_url ?? null,
deduplicationKey: attrs.deduplication_key ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
endedAt: attrs.ended_at ?? null,
},
},
}
},
outputs: {
alert: {
type: 'object',
description: 'The resolved alert',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
}
+116
View File
@@ -0,0 +1,116 @@
import type {
RootlyIncidentActionResponse,
RootlyResolveIncidentParams,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyResolveIncidentTool: ToolConfig<
RootlyResolveIncidentParams,
RootlyIncidentActionResponse
> = {
id: 'rootly_resolve_incident',
name: 'Rootly Resolve Incident',
description: 'Transition a Rootly incident to the resolved state.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to resolve',
},
resolutionMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'How was the incident resolved?',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/resolve`,
method: 'PUT',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage
return { data: { type: 'incidents', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The resolved incident',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
+128
View File
@@ -0,0 +1,128 @@
import type { RootlyRunWorkflowParams, RootlyRunWorkflowResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyRunWorkflowTool: ToolConfig<RootlyRunWorkflowParams, RootlyRunWorkflowResponse> =
{
id: 'rootly_run_workflow',
name: 'Rootly Run Workflow',
description: 'Trigger a Rootly automation workflow, optionally scoped to an incident or alert.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
workflowId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the workflow to run',
},
incidentId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Incident ID to run the workflow against',
},
alertId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Alert ID to run the workflow against',
},
immediate: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description:
'Run immediately (true) or respect the workflow wait time (false). Default true',
},
checkConditions: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to evaluate the workflow conditions before running. Default false',
},
},
request: {
url: (params) =>
`https://api.rootly.com/v1/workflows/${params.workflowId.trim()}/workflow_runs`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.incidentId) attributes.incident_id = params.incidentId.trim()
if (params.alertId) attributes.alert_id = params.alertId.trim()
if (params.immediate !== undefined) attributes.immediate = params.immediate
if (params.checkConditions !== undefined)
attributes.check_conditions = params.checkConditions
return { data: { type: 'workflow_runs', attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { workflowRun: {} as RootlyRunWorkflowResponse['output']['workflowRun'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
workflowRun: {
id: data.data?.id ?? null,
workflowId: attrs.workflow_id ?? null,
status: attrs.status ?? null,
statusMessage: attrs.status_message ?? null,
triggeredBy: attrs.triggered_by ?? null,
incidentId: attrs.incident_id ?? null,
alertId: attrs.alert_id ?? null,
startedAt: attrs.started_at ?? null,
completedAt: attrs.completed_at ?? null,
failedAt: attrs.failed_at ?? null,
canceledAt: attrs.canceled_at ?? null,
},
},
}
},
outputs: {
workflowRun: {
type: 'object',
description: 'The triggered workflow run',
properties: {
id: { type: 'string', description: 'Unique workflow run ID' },
workflowId: { type: 'string', description: 'ID of the workflow that ran' },
status: {
type: 'string',
description:
'Run status (queued, started, completed, completed_with_errors, failed, canceled)',
},
statusMessage: { type: 'string', description: 'Status detail message' },
triggeredBy: {
type: 'string',
description: 'What triggered the run (system, user, workflow)',
},
incidentId: { type: 'string', description: 'Associated incident ID' },
alertId: { type: 'string', description: 'Associated alert ID' },
startedAt: { type: 'string', description: 'When the run started' },
completedAt: { type: 'string', description: 'When the run completed' },
failedAt: { type: 'string', description: 'When the run failed' },
canceledAt: { type: 'string', description: 'When the run was canceled' },
},
},
},
}
+102
View File
@@ -0,0 +1,102 @@
import type { RootlyAlertActionResponse, RootlySnoozeAlertParams } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlySnoozeAlertTool: ToolConfig<RootlySnoozeAlertParams, RootlyAlertActionResponse> =
{
id: 'rootly_snooze_alert',
name: 'Rootly Snooze Alert',
description: 'Snooze a Rootly alert for a set number of minutes.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
alertId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the alert to snooze',
},
delayMinutes: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Number of minutes to snooze the alert',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/snooze`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => ({
data: {
type: 'alerts',
attributes: { delay_minutes: params.delayMinutes },
},
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alert: {} as RootlyAlertActionResponse['output']['alert'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
alert: {
id: data.data?.id ?? null,
shortId: attrs.short_id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
source: attrs.source ?? null,
status: attrs.status ?? null,
externalId: attrs.external_id ?? null,
externalUrl: attrs.external_url ?? null,
deduplicationKey: attrs.deduplication_key ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
endedAt: attrs.ended_at ?? null,
},
},
}
},
outputs: {
alert: {
type: 'object',
description: 'The snoozed alert',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
}
+849
View File
@@ -0,0 +1,849 @@
import type { ToolResponse } from '@/tools/types'
/** Base parameters for all Rootly API operations */
interface RootlyBaseParams {
apiKey: string
}
/** Create Incident */
export interface RootlyCreateIncidentParams extends RootlyBaseParams {
title?: string
summary?: string
severityId?: string
status?: string
kind?: string
serviceIds?: string
environmentIds?: string
groupIds?: string
incidentTypeIds?: string
functionalityIds?: string
labels?: string
private?: boolean
}
interface RootlyIncidentData {
id: string | null
sequentialId: number | null
title: string
slug: string | null
kind: string | null
summary: string | null
status: string | null
private: boolean
url: string | null
shortUrl: string | null
severityName: string | null
severityId: string | null
createdAt: string
updatedAt: string
startedAt: string | null
mitigatedAt: string | null
resolvedAt: string | null
closedAt: string | null
}
export interface RootlyCreateIncidentResponse extends ToolResponse {
output: {
incident: RootlyIncidentData
}
}
/** Get Incident */
export interface RootlyGetIncidentParams extends RootlyBaseParams {
incidentId: string
}
export interface RootlyGetIncidentResponse extends ToolResponse {
output: {
incident: RootlyIncidentData
}
}
/** Update Incident */
export interface RootlyUpdateIncidentParams extends RootlyBaseParams {
incidentId: string
title?: string
summary?: string
severityId?: string
status?: string
kind?: string
private?: boolean
serviceIds?: string
environmentIds?: string
groupIds?: string
incidentTypeIds?: string
functionalityIds?: string
labels?: string
mitigationMessage?: string
resolutionMessage?: string
cancellationMessage?: string
}
export interface RootlyUpdateIncidentResponse extends ToolResponse {
output: {
incident: RootlyIncidentData
}
}
/** List Incidents */
export interface RootlyListIncidentsParams extends RootlyBaseParams {
status?: string
severity?: string
search?: string
services?: string
teams?: string
environments?: string
sort?: string
pageSize?: number
pageNumber?: number
}
export interface RootlyListIncidentsResponse extends ToolResponse {
output: {
incidents: RootlyIncidentData[]
totalCount: number
}
}
/** Create Alert */
export interface RootlyCreateAlertParams extends RootlyBaseParams {
summary: string
source?: string
description?: string
status?: string
serviceIds?: string
groupIds?: string
environmentIds?: string
externalId?: string
externalUrl?: string
deduplicationKey?: string
}
interface RootlyAlertData {
id: string | null
shortId: string | null
summary: string
description: string | null
source: string | null
status: string | null
externalId: string | null
externalUrl: string | null
deduplicationKey: string | null
createdAt: string
updatedAt: string
startedAt: string | null
endedAt: string | null
}
export interface RootlyCreateAlertResponse extends ToolResponse {
output: {
alert: RootlyAlertData
}
}
/** List Alerts */
export interface RootlyListAlertsParams extends RootlyBaseParams {
status?: string
source?: string
services?: string
environments?: string
groups?: string
pageSize?: number
pageNumber?: number
}
export interface RootlyListAlertsResponse extends ToolResponse {
output: {
alerts: RootlyAlertData[]
totalCount: number
}
}
/** Add Incident Event */
export interface RootlyAddIncidentEventParams extends RootlyBaseParams {
incidentId: string
event: string
visibility?: string
}
export interface RootlyAddIncidentEventResponse extends ToolResponse {
output: {
eventId: string
event: string
visibility: string | null
occurredAt: string | null
createdAt: string
updatedAt: string
}
}
/** List Services */
export interface RootlyListServicesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyServiceData {
id: string | null
name: string
slug: string | null
description: string | null
color: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListServicesResponse extends ToolResponse {
output: {
services: RootlyServiceData[]
totalCount: number
}
}
/** List Severities */
export interface RootlyListSeveritiesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlySeverityData {
id: string | null
name: string
slug: string | null
description: string | null
severity: string | null
color: string | null
position: number | null
createdAt: string
updatedAt: string
}
export interface RootlyListSeveritiesResponse extends ToolResponse {
output: {
severities: RootlySeverityData[]
totalCount: number
}
}
/** List Retrospectives */
export interface RootlyListRetrospectivesParams extends RootlyBaseParams {
status?: string
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyRetrospectiveData {
id: string | null
title: string
status: string | null
url: string | null
startedAt: string | null
mitigatedAt: string | null
resolvedAt: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListRetrospectivesResponse extends ToolResponse {
output: {
retrospectives: RootlyRetrospectiveData[]
totalCount: number
}
}
/** List Teams */
export interface RootlyListTeamsParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyTeamData {
id: string | null
name: string
slug: string | null
description: string | null
color: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListTeamsResponse extends ToolResponse {
output: {
teams: RootlyTeamData[]
totalCount: number
}
}
/** List Environments */
export interface RootlyListEnvironmentsParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyEnvironmentData {
id: string | null
name: string
slug: string | null
description: string | null
color: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListEnvironmentsResponse extends ToolResponse {
output: {
environments: RootlyEnvironmentData[]
totalCount: number
}
}
/** List Incident Types */
export interface RootlyListIncidentTypesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyIncidentTypeData {
id: string | null
name: string
slug: string | null
description: string | null
color: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListIncidentTypesResponse extends ToolResponse {
output: {
incidentTypes: RootlyIncidentTypeData[]
totalCount: number
}
}
/** List Causes */
export interface RootlyListCausesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyCauseData {
id: string | null
name: string
slug: string | null
description: string | null
position: number | null
createdAt: string
updatedAt: string
}
export interface RootlyListCausesResponse extends ToolResponse {
output: {
causes: RootlyCauseData[]
totalCount: number
}
}
/** List Playbooks */
export interface RootlyListPlaybooksParams extends RootlyBaseParams {
pageSize?: number
pageNumber?: number
}
interface RootlyPlaybookData {
id: string | null
title: string
summary: string | null
externalUrl: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListPlaybooksResponse extends ToolResponse {
output: {
playbooks: RootlyPlaybookData[]
totalCount: number
}
}
/** Delete Incident */
export interface RootlyDeleteIncidentParams extends RootlyBaseParams {
incidentId: string
}
export interface RootlyDeleteIncidentResponse extends ToolResponse {
output: {
success: boolean
message: string
}
}
/** Action Item Data */
interface RootlyActionItemData {
id: string | null
summary: string
description: string | null
kind: string | null
priority: string | null
status: string | null
dueDate: string | null
createdAt: string
updatedAt: string
}
/** Create Action Item */
export interface RootlyCreateActionItemParams extends RootlyBaseParams {
incidentId: string
summary: string
description?: string
kind?: string
priority?: string
status?: string
assignedToUserId?: string
dueDate?: string
}
export interface RootlyCreateActionItemResponse extends ToolResponse {
output: {
actionItem: RootlyActionItemData
}
}
/** List Action Items */
export interface RootlyListActionItemsParams extends RootlyBaseParams {
incidentId: string
pageSize?: number
pageNumber?: number
}
export interface RootlyListActionItemsResponse extends ToolResponse {
output: {
actionItems: RootlyActionItemData[]
totalCount: number
}
}
/** List Functionalities */
export interface RootlyListFunctionalitiesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyFunctionalityData {
id: string | null
name: string
slug: string | null
description: string | null
color: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListFunctionalitiesResponse extends ToolResponse {
output: {
functionalities: RootlyFunctionalityData[]
totalCount: number
}
}
/** Get Alert */
export interface RootlyGetAlertParams extends RootlyBaseParams {
alertId: string
}
export interface RootlyGetAlertResponse extends ToolResponse {
output: {
alert: RootlyAlertData
}
}
/** Update Alert */
export interface RootlyUpdateAlertParams extends RootlyBaseParams {
alertId: string
summary?: string
description?: string
source?: string
serviceIds?: string
groupIds?: string
environmentIds?: string
externalId?: string
externalUrl?: string
deduplicationKey?: string
}
export interface RootlyUpdateAlertResponse extends ToolResponse {
output: {
alert: RootlyAlertData
}
}
/** Acknowledge Alert */
export interface RootlyAcknowledgeAlertParams extends RootlyBaseParams {
alertId: string
}
export interface RootlyAcknowledgeAlertResponse extends ToolResponse {
output: {
alert: RootlyAlertData
}
}
/** Resolve Alert */
export interface RootlyResolveAlertParams extends RootlyBaseParams {
alertId: string
resolutionMessage?: string
resolveRelatedIncidents?: boolean
}
export interface RootlyResolveAlertResponse extends ToolResponse {
output: {
alert: RootlyAlertData
}
}
/** List Users */
export interface RootlyListUsersParams extends RootlyBaseParams {
search?: string
email?: string
pageSize?: number
pageNumber?: number
}
interface RootlyUserData {
id: string | null
email: string
firstName: string | null
lastName: string | null
fullName: string | null
timeZone: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListUsersResponse extends ToolResponse {
output: {
users: RootlyUserData[]
totalCount: number
}
}
/** List On-Calls */
export interface RootlyListOnCallsParams extends RootlyBaseParams {
scheduleIds?: string
escalationPolicyIds?: string
userIds?: string
serviceIds?: string
}
interface RootlyOnCallData {
id: string | null
userId: string | null
userName: string | null
scheduleId: string | null
scheduleName: string | null
escalationPolicyId: string | null
startTime: string | null
endTime: string | null
}
export interface RootlyListOnCallsResponse extends ToolResponse {
output: {
onCalls: RootlyOnCallData[]
totalCount: number
}
}
/** List Schedules */
export interface RootlyListSchedulesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyScheduleData {
id: string | null
name: string
description: string | null
allTimeCoverage: boolean | null
createdAt: string
updatedAt: string
}
export interface RootlyListSchedulesResponse extends ToolResponse {
output: {
schedules: RootlyScheduleData[]
totalCount: number
}
}
/** List Escalation Policies */
export interface RootlyListEscalationPoliciesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyEscalationPolicyData {
id: string | null
name: string
description: string | null
repeatCount: number | null
groupIds: string[] | null
serviceIds: string[] | null
createdAt: string
updatedAt: string
}
export interface RootlyListEscalationPoliciesResponse extends ToolResponse {
output: {
escalationPolicies: RootlyEscalationPolicyData[]
totalCount: number
}
}
/** Shared incident-action response (returns the standard incident object) */
export interface RootlyIncidentActionResponse extends ToolResponse {
output: {
incident: RootlyIncidentData
}
}
/** Shared alert-action response (returns the standard alert object) */
export interface RootlyAlertActionResponse extends ToolResponse {
output: {
alert: RootlyAlertData
}
}
/** Mitigate Incident */
export interface RootlyMitigateIncidentParams extends RootlyBaseParams {
incidentId: string
mitigationMessage?: string
}
/** Resolve Incident */
export interface RootlyResolveIncidentParams extends RootlyBaseParams {
incidentId: string
resolutionMessage?: string
}
/** Assign Incident Role */
export interface RootlyAssignIncidentRoleParams extends RootlyBaseParams {
incidentId: string
userId: string
incidentRoleId: string
}
/** Unassign Incident Role */
export interface RootlyUnassignIncidentRoleParams extends RootlyBaseParams {
incidentId: string
userId: string
incidentRoleId: string
}
/** Add Subscribers */
export interface RootlyAddSubscribersParams extends RootlyBaseParams {
incidentId: string
userIds: string
}
/** Remove Subscribers */
export interface RootlyRemoveSubscribersParams extends RootlyBaseParams {
incidentId: string
userIds: string
}
/** Create Status Page Event */
export interface RootlyCreateStatusPageEventParams extends RootlyBaseParams {
incidentId: string
event: string
statusPageId?: string
status?: string
notifySubscribers?: boolean
shouldTweet?: boolean
}
interface RootlyStatusPageEventData {
id: string | null
event: string
statusPageId: string | null
status: string | null
notifySubscribers: boolean | null
shouldTweet: boolean | null
startedAt: string | null
createdAt: string
updatedAt: string
}
export interface RootlyCreateStatusPageEventResponse extends ToolResponse {
output: {
statusPageEvent: RootlyStatusPageEventData
}
}
/** Update Action Item */
export interface RootlyUpdateActionItemParams extends RootlyBaseParams {
actionItemId: string
summary?: string
description?: string
kind?: string
priority?: string
status?: string
assignedToUserId?: string
dueDate?: string
}
export interface RootlyUpdateActionItemResponse extends ToolResponse {
output: {
actionItem: RootlyActionItemData
}
}
/** Delete Action Item */
export interface RootlyDeleteActionItemParams extends RootlyBaseParams {
actionItemId: string
}
export interface RootlyDeleteActionItemResponse extends ToolResponse {
output: {
success: boolean
message: string
}
}
/** Snooze Alert */
export interface RootlySnoozeAlertParams extends RootlyBaseParams {
alertId: string
delayMinutes: number
}
/** Escalate Alert */
export interface RootlyEscalateAlertParams extends RootlyBaseParams {
alertId: string
escalationPolicyId?: string
escalationPolicyLevel?: number
}
/** List Incident Events */
export interface RootlyListIncidentEventsParams extends RootlyBaseParams {
incidentId: string
pageSize?: number
pageNumber?: number
}
interface RootlyIncidentEventData {
id: string | null
event: string
visibility: string | null
occurredAt: string | null
createdAt: string
updatedAt: string
}
export interface RootlyListIncidentEventsResponse extends ToolResponse {
output: {
events: RootlyIncidentEventData[]
totalCount: number
}
}
/** Run Workflow */
export interface RootlyRunWorkflowParams extends RootlyBaseParams {
workflowId: string
incidentId?: string
alertId?: string
immediate?: boolean
checkConditions?: boolean
}
interface RootlyWorkflowRunData {
id: string | null
workflowId: string | null
status: string | null
statusMessage: string | null
triggeredBy: string | null
incidentId: string | null
alertId: string | null
startedAt: string | null
completedAt: string | null
failedAt: string | null
canceledAt: string | null
}
export interface RootlyRunWorkflowResponse extends ToolResponse {
output: {
workflowRun: RootlyWorkflowRunData
}
}
/** List Incident Roles */
export interface RootlyListIncidentRolesParams extends RootlyBaseParams {
search?: string
pageSize?: number
pageNumber?: number
}
interface RootlyIncidentRoleData {
id: string | null
name: string
slug: string | null
summary: string | null
description: string | null
position: number | null
optional: boolean | null
enabled: boolean | null
createdAt: string
updatedAt: string
}
export interface RootlyListIncidentRolesResponse extends ToolResponse {
output: {
incidentRoles: RootlyIncidentRoleData[]
totalCount: number
}
}
/** Union of all responses */
export type RootlyResponse =
| RootlyCreateIncidentResponse
| RootlyGetIncidentResponse
| RootlyUpdateIncidentResponse
| RootlyDeleteIncidentResponse
| RootlyListIncidentsResponse
| RootlyCreateAlertResponse
| RootlyGetAlertResponse
| RootlyUpdateAlertResponse
| RootlyAcknowledgeAlertResponse
| RootlyResolveAlertResponse
| RootlyListAlertsResponse
| RootlyAddIncidentEventResponse
| RootlyCreateActionItemResponse
| RootlyListActionItemsResponse
| RootlyListServicesResponse
| RootlyListSeveritiesResponse
| RootlyListRetrospectivesResponse
| RootlyListTeamsResponse
| RootlyListEnvironmentsResponse
| RootlyListIncidentTypesResponse
| RootlyListFunctionalitiesResponse
| RootlyListCausesResponse
| RootlyListPlaybooksResponse
| RootlyListUsersResponse
| RootlyListOnCallsResponse
| RootlyListSchedulesResponse
| RootlyListEscalationPoliciesResponse
| RootlyIncidentActionResponse
| RootlyAlertActionResponse
| RootlyCreateStatusPageEventResponse
| RootlyUpdateActionItemResponse
| RootlyDeleteActionItemResponse
| RootlyListIncidentEventsResponse
| RootlyRunWorkflowResponse
| RootlyListIncidentRolesResponse
@@ -0,0 +1,127 @@
import type {
RootlyIncidentActionResponse,
RootlyUnassignIncidentRoleParams,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyUnassignIncidentRoleTool: ToolConfig<
RootlyUnassignIncidentRoleParams,
RootlyIncidentActionResponse
> = {
id: 'rootly_unassign_incident_role',
name: 'Rootly Unassign Incident Role',
description: 'Remove an incident role assignment from a user on a Rootly incident.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the user to unassign (use List Users to find IDs)',
},
incidentRoleId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident role to remove (use List Incident Roles to find IDs)',
},
},
request: {
url: (params) =>
`https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/unassign_role_from_user`,
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => ({
data: {
type: 'incidents',
attributes: {
user_id: params.userId.trim(),
incident_role_id: params.incidentRoleId.trim(),
},
},
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyIncidentActionResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The incident after the role was unassigned',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}
+145
View File
@@ -0,0 +1,145 @@
import type {
RootlyUpdateActionItemParams,
RootlyUpdateActionItemResponse,
} from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyUpdateActionItemTool: ToolConfig<
RootlyUpdateActionItemParams,
RootlyUpdateActionItemResponse
> = {
id: 'rootly_update_action_item',
name: 'Rootly Update Action Item',
description: 'Update a Rootly incident action item (status, priority, assignee, etc.).',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
actionItemId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the action item to update',
},
summary: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated action item title',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated description',
},
kind: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The kind of action item (task, follow_up)',
},
priority: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Priority level (high, medium, low)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Action item status (open, in_progress, cancelled, done)',
},
assignedToUserId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The user ID to assign the action item to',
},
dueDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Due date for the action item',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/action_items/${params.actionItemId.trim()}`,
method: 'PUT',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.summary) attributes.summary = params.summary
if (params.description) attributes.description = params.description
if (params.kind) attributes.kind = params.kind
if (params.priority) attributes.priority = params.priority
if (params.status) attributes.status = params.status
if (params.assignedToUserId) {
const numericId = Number.parseInt(params.assignedToUserId, 10)
if (!Number.isNaN(numericId)) attributes.assigned_to_user_id = numericId
}
if (params.dueDate) attributes.due_date = params.dueDate
return {
data: { type: 'incident_action_items', id: params.actionItemId.trim(), attributes },
}
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { actionItem: {} as RootlyUpdateActionItemResponse['output']['actionItem'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
actionItem: {
id: data.data?.id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
kind: attrs.kind ?? null,
priority: attrs.priority ?? null,
status: attrs.status ?? null,
dueDate: attrs.due_date ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
},
},
}
},
outputs: {
actionItem: {
type: 'object',
description: 'The updated action item',
properties: {
id: { type: 'string', description: 'Unique action item ID' },
summary: { type: 'string', description: 'Action item title' },
description: { type: 'string', description: 'Action item description' },
kind: { type: 'string', description: 'Action item kind (task, follow_up)' },
priority: { type: 'string', description: 'Priority level' },
status: { type: 'string', description: 'Action item status' },
dueDate: { type: 'string', description: 'Due date' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
},
},
},
}
+163
View File
@@ -0,0 +1,163 @@
import type { RootlyUpdateAlertParams, RootlyUpdateAlertResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyUpdateAlertTool: ToolConfig<RootlyUpdateAlertParams, RootlyUpdateAlertResponse> =
{
id: 'rootly_update_alert',
name: 'Rootly Update Alert',
description: 'Update an existing alert in Rootly.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
alertId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the alert to update',
},
summary: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated alert summary',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated alert description',
},
source: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated alert source',
},
serviceIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated service IDs to attach',
},
groupIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated team/group IDs to attach',
},
environmentIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated environment IDs to attach',
},
externalId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated external ID',
},
externalUrl: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated external URL',
},
deduplicationKey: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated deduplication key',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}`,
method: 'PATCH',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.summary) attributes.summary = params.summary
if (params.description) attributes.description = params.description
if (params.source) attributes.source = params.source
if (params.externalId) attributes.external_id = params.externalId
if (params.externalUrl) attributes.external_url = params.externalUrl
if (params.deduplicationKey) attributes.deduplication_key = params.deduplicationKey
if (params.serviceIds) {
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
}
if (params.groupIds) {
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
}
if (params.environmentIds) {
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
}
return { data: { type: 'alerts', id: params.alertId.trim(), attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { alert: {} as RootlyUpdateAlertResponse['output']['alert'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
alert: {
id: data.data?.id ?? null,
shortId: attrs.short_id ?? null,
summary: attrs.summary ?? '',
description: attrs.description ?? null,
source: attrs.source ?? null,
status: attrs.status ?? null,
externalId: attrs.external_id ?? null,
externalUrl: attrs.external_url ?? null,
deduplicationKey: attrs.deduplication_key ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
endedAt: attrs.ended_at ?? null,
},
},
}
},
outputs: {
alert: {
type: 'object',
description: 'The updated alert',
properties: {
id: { type: 'string', description: 'Unique alert ID' },
shortId: { type: 'string', description: 'Short alert ID' },
summary: { type: 'string', description: 'Alert summary' },
description: { type: 'string', description: 'Alert description' },
source: { type: 'string', description: 'Alert source' },
status: { type: 'string', description: 'Alert status' },
externalId: { type: 'string', description: 'External ID' },
externalUrl: { type: 'string', description: 'External URL' },
deduplicationKey: { type: 'string', description: 'Deduplication key' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
endedAt: { type: 'string', description: 'End date' },
},
},
},
}
+233
View File
@@ -0,0 +1,233 @@
import type { RootlyUpdateIncidentParams, RootlyUpdateIncidentResponse } from '@/tools/rootly/types'
import type { ToolConfig } from '@/tools/types'
export const rootlyUpdateIncidentTool: ToolConfig<
RootlyUpdateIncidentParams,
RootlyUpdateIncidentResponse
> = {
id: 'rootly_update_incident',
name: 'Rootly Update Incident',
description: 'Update an existing incident in Rootly (status, severity, summary, etc.).',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Rootly API key',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the incident to update',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated incident title',
},
summary: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated incident summary',
},
severityId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated severity ID',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Updated status (in_triage, started, detected, acknowledged, mitigated, resolved, closed, cancelled, scheduled, in_progress, completed)',
},
kind: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Incident kind (normal, normal_sub, test, test_sub, example, example_sub, backfilled, scheduled, scheduled_sub)',
},
private: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Set incident as private (cannot be undone)',
},
serviceIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated service IDs',
},
environmentIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated environment IDs',
},
groupIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated team/group IDs',
},
incidentTypeIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated incident type IDs to attach',
},
functionalityIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated functionality IDs to attach',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Labels as JSON object, e.g. {"platform":"osx","version":"1.29"}',
},
mitigationMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'How was the incident mitigated?',
},
resolutionMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'How was the incident resolved?',
},
cancellationMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Why was the incident cancelled?',
},
},
request: {
url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`,
method: 'PUT',
headers: (params) => ({
'Content-Type': 'application/vnd.api+json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const attributes: Record<string, unknown> = {}
if (params.title) attributes.title = params.title
if (params.summary) attributes.summary = params.summary
if (params.severityId) attributes.severity_id = params.severityId
if (params.status) attributes.status = params.status
if (params.kind) attributes.kind = params.kind
if (params.private !== undefined) attributes.private = params.private
if (params.mitigationMessage) attributes.mitigation_message = params.mitigationMessage
if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage
if (params.cancellationMessage) attributes.cancellation_message = params.cancellationMessage
if (params.incidentTypeIds) {
attributes.incident_type_ids = params.incidentTypeIds
.split(',')
.map((s: string) => s.trim())
}
if (params.functionalityIds) {
attributes.functionality_ids = params.functionalityIds
.split(',')
.map((s: string) => s.trim())
}
if (params.labels) {
try {
attributes.labels = JSON.parse(params.labels)
} catch {
attributes.labels = params.labels
}
}
if (params.serviceIds) {
attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim())
}
if (params.environmentIds) {
attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim())
}
if (params.groupIds) {
attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim())
}
return { data: { type: 'incidents', id: params.incidentId.trim(), attributes } }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
output: { incident: {} as RootlyUpdateIncidentResponse['output']['incident'] },
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
}
}
const data = await response.json()
const attrs = data.data?.attributes || {}
return {
success: true,
output: {
incident: {
id: data.data?.id ?? null,
sequentialId: attrs.sequential_id ?? null,
title: attrs.title ?? '',
slug: attrs.slug ?? null,
kind: attrs.kind ?? null,
summary: attrs.summary ?? null,
status: attrs.status ?? null,
private: attrs.private ?? false,
url: attrs.url ?? null,
shortUrl: attrs.short_url ?? null,
severityName: attrs.severity?.data?.attributes?.name ?? null,
severityId: attrs.severity?.data?.id ?? null,
createdAt: attrs.created_at ?? '',
updatedAt: attrs.updated_at ?? '',
startedAt: attrs.started_at ?? null,
mitigatedAt: attrs.mitigated_at ?? null,
resolvedAt: attrs.resolved_at ?? null,
closedAt: attrs.closed_at ?? null,
},
},
}
},
outputs: {
incident: {
type: 'object',
description: 'The updated incident',
properties: {
id: { type: 'string', description: 'Unique incident ID' },
sequentialId: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
private: { type: 'boolean', description: 'Whether the incident is private' },
url: { type: 'string', description: 'URL to the incident' },
shortUrl: { type: 'string', description: 'Short URL to the incident' },
severityName: { type: 'string', description: 'Severity name' },
severityId: { type: 'string', description: 'Severity ID' },
createdAt: { type: 'string', description: 'Creation date' },
updatedAt: { type: 'string', description: 'Last update date' },
startedAt: { type: 'string', description: 'Start date' },
mitigatedAt: { type: 'string', description: 'Mitigation date' },
resolvedAt: { type: 'string', description: 'Resolution date' },
closedAt: { type: 'string', description: 'Closed date' },
},
},
},
}