feat(integrations): add amplitude, google pagespeed insights, and pagerduty integrations (#3385)

* feat(integrations): add amplitude and google pagespeed insights integrations

* verified and regen docs

* fix icons

* fix(integrations): add pagerduty to tool and block registries

Re-add registry entries that were reverted after initial commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* more updates

* ack comemnts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waleed
2026-02-28 18:56:34 -08:00
committed by GitHub
parent 3788660366
commit ee20e119de
36 changed files with 4826 additions and 20 deletions

View File

@@ -1209,6 +1209,17 @@ export function AlgoliaIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function AmplitudeIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 49 49'>
<path
fill='#FFFFFF'
d='M23.4,15.3c0.6,1.8,1.2,4.1,1.9,6.7c-2.6,0-5.3-0.1-7.8-0.1h-1.3c1.5-5.7,3.2-10.1,4.6-11.1 c0.1-0.1,0.2-0.1,0.4-0.1c0.2,0,0.3,0.1,0.5,0.3C21.9,11.5,22.5,12.7,23.4,15.3z M49,24.5C49,38,38,49,24.5,49S0,38,0,24.5 S11,0,24.5,0S49,11,49,24.5z M42.7,23.9c0-0.6-0.4-1.2-1-1.3l0,0l0,0l0,0c-0.1,0-0.1,0-0.2,0h-0.2c-4.1-0.3-8.4-0.4-12.4-0.5l0,0 C27,14.8,24.5,7.4,21.3,7.4c-3,0-5.8,4.9-8.2,14.5c-1.7,0-3.2,0-4.6-0.1c-0.1,0-0.2,0-0.2,0c-0.3,0-0.5,0-0.5,0 c-0.8,0.1-1.4,0.9-1.4,1.7c0,0.8,0.6,1.6,1.5,1.7l0,0h4.6c-0.4,1.9-0.8,3.8-1.1,5.6l-0.1,0.8l0,0c0,0.6,0.5,1.1,1.1,1.1 c0.4,0,0.8-0.2,1-0.5l0,0l2.2-7.1h10.7c0.8,3.1,1.7,6.3,2.8,9.3c0.6,1.6,2,5.4,4.4,5.4l0,0c3.6,0,5-5.8,5.9-9.6 c0.2-0.8,0.4-1.5,0.5-2.1l0.1-0.2l0,0c0-0.1,0-0.2,0-0.3c-0.1-0.2-0.2-0.3-0.4-0.4c-0.3-0.1-0.5,0.1-0.6,0.4l0,0l-0.1,0.2 c-0.3,0.8-0.6,1.6-0.8,2.3v0.1c-1.6,4.4-2.3,6.4-3.7,6.4l0,0l0,0l0,0c-1.8,0-3.5-7.3-4.1-10.1c-0.1-0.5-0.2-0.9-0.3-1.3h11.7 c0.2,0,0.4-0.1,0.6-0.1l0,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0l0,0c0,0,0.1,0,0.1-0.1l0,0C42.5,24.6,42.7,24.3,42.7,23.9z'
/>
</svg>
)
}
export function GoogleBooksIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 478.633 540.068'>
@@ -1938,13 +1949,11 @@ export function ElevenLabsIcon(props: SVGProps<SVGSVGElement>) {
export function LinkupIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'>
<g transform='translate(12, 12) scale(1.3) translate(-12, -12)'>
<path
d='M20.2 14.1c-.4-.3-1.6-.4-2.9-.2.5-1.4 1.3-3.9.1-5-.6-.5-1.5-.7-2.6-.5-.3 0-.6.1-1 .2-1.1-1.6-2.4-2.5-3.8-2.5-1.6 0-3.1 1-4.1 2.9-1.2 2.1-1.9 5.1-1.9 8.8v.03l.4.3c3-.9 7.5-2.3 10.7-2.9 0 .9.1 1.9.1 2.8v.03l.4.3c.1 0 5.4-1.7 5.3-3.3 0-.2-.1-.5-.3-.7zM19.9 14.7c.03.4-1.7 1.4-4 2.3.5-.7 1-1.6 1.3-2.5 1.4-.1 2.4-.1 2.7.2zM16.4 14.6c-.3.7-.7 1.4-1.2 2-.02-.6-.1-1.2-.2-1.8.4-.1.9-.1 1.4-.2zM16.5 9.4c.8.7.9 2.4.1 5.1-.5.1-1 .1-1.5.2-.3-2-.9-3.8-1.7-5.3.3-.1.6-.2.8-.2.9-.1 1.7.05 2.3.2zM9.5 6.8c1.2 0 2.3.7 3.2 2.1-2.8 1.1-5.9 3.4-8.4 7.8.2-5.1 1.9-9.9 5.2-9.9zM4.7 17c3.4-4.9 6.4-6.8 8.4-7.8.7 1.3 1.2 2.9 1.5 4.8-3.2.6-7.3 1.8-9.9 3z'
fill='#000000'
/>
</g>
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 154 107' fill='none'>
<path
d='M150.677 72.7113C146.612 70.2493 137.909 69.542 124.794 70.6076C128.992 57.6776 133.757 35.3911 121.323 25.1527C115.886 20.6743 107.471 19.0437 97.6162 20.5594C94.6758 21.0142 91.5752 21.7445 88.3878 22.732C78.8667 8.28165 66.2954 0 53.8613 0C39.4288 0 26.1304 9.3381 16.4081 26.2872C5.67515 45.014 0 71.9626 0 104.23V104.533L3.60356 106.94L3.88251 106.825C30.5754 95.5628 67.5759 85.0718 100.593 79.4037C101.604 87.644 102.116 95.9945 102.116 104.235V104.52L105.491 107L105.761 106.913C106.255 106.752 155.159 90.8822 153.979 77.5894C153.856 76.2022 153.183 74.2271 150.677 72.7113ZM148.409 78.09C148.715 81.5442 133.236 91.0568 111.838 98.8883C115.968 92.0995 119.818 84.1715 122.777 76.3584C135.659 75.1411 144.531 75.5545 147.792 77.5296C148.377 77.8833 148.409 78.09 148.409 78.09ZM116.668 77.0106C114.084 83.3769 110.951 89.6329 107.54 95.2458C107.334 89.5135 106.913 83.8821 106.296 78.4621C109.922 77.8971 113.407 77.4102 116.668 77.0106ZM117.774 29.4979C125.379 35.7585 125.782 51.3205 118.867 71.1772C114.747 71.6319 110.284 72.2382 105.596 72.9777C103.049 55.1742 98.2839 39.966 91.4243 27.7525C94.566 26.8155 96.9669 26.3469 98.4622 26.1127C106.721 24.8404 113.581 26.0438 117.774 29.4979ZM53.8567 5.62215C65.0561 5.62215 74.8882 12.0022 83.0922 24.5923C57.7027 34.5413 30.3193 59.4092 5.78032 94.8003C7.43119 51.4813 23.0299 5.62215 53.8613 5.62215M10.1933 98.2406C40.7504 53.9341 68.2024 36.4429 86.0739 29.5852C92.4487 41.2383 97.2046 56.5522 99.8433 73.9331C70.5209 79.0316 35.6377 88.4983 10.1933 98.2406Z'
fill='#000000'
/>
</svg>
)
}
@@ -2453,6 +2462,17 @@ export function OutlookIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function PagerDutyIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64' fill='none'>
<path
d='M6.704 59.217H0v-33.65c0-3.455 1.418-5.544 2.604-6.704 2.63-2.58 6.2-2.656 6.782-2.656h10.546c3.765 0 5.93 1.52 7.117 2.8 2.346 2.553 2.372 5.853 2.32 6.73v12.687c0 3.662-1.496 5.828-2.733 6.988-2.553 2.398-5.93 2.45-6.73 2.424H6.704zm13.46-18.102c.36 0 1.367-.103 1.908-.62.413-.387.62-1.083.62-2.1v-13.02c0-.36-.077-1.315-.593-1.857-.5-.516-1.444-.62-2.166-.62h-10.6c-2.63 0-2.63 1.985-2.63 2.656v15.55zM57.296 4.783H64V38.46c0 3.455-1.418 5.544-2.604 6.704-2.63 2.58-6.2 2.656-6.782 2.656H44.068c-3.765 0-5.93-1.52-7.117-2.8-2.346-2.553-2.372-5.853-2.32-6.73V25.62c0-3.662 1.496-5.828 2.733-6.988 2.553-2.398 5.93-2.45 6.73-2.424h13.202zM43.836 22.9c-.36 0-1.367.103-1.908.62-.413.387-.62 1.083-.62 2.1v13.02c0 .36.077 1.315.593 1.857.5.516 1.444.62 2.166.62h10.598c2.656-.026 2.656-2 2.656-2.682V22.9z'
fill='#06AC38'
/>
</svg>
)
}
export function MicrosoftExcelIcon(props: SVGProps<SVGSVGElement>) {
const id = useId()
const gradientId = `excel_gradient_${id}`
@@ -3996,10 +4016,10 @@ export function IntercomIcon(props: SVGProps<SVGSVGElement>) {
export function LoopsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 256 256' fill='none' xmlns='http://www.w3.org/2000/svg'>
<svg {...props} viewBox='0 0 214 186' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
fill='#FD4E00'
d='M192.352 88.042c0-7.012-5.685-12.697-12.697-12.697s-12.697 5.685-12.697 12.697c0 .634.052 1.255.142 1.866a25.248 25.248 0 0 0-4.9-.49c-14.006 0-25.36 11.354-25.36 25.36 0 1.63.16 3.222.456 4.765a37.8 37.8 0 0 0-9.296-1.173c-20.95 0-37.935 16.985-37.935 37.935S107.05 194.24 128 194.24s37.935-16.985 37.935-37.935a37.7 37.7 0 0 0-3.78-16.555 25.2 25.2 0 0 0 12.487-3.336 25.2 25.2 0 0 0 4.558 3.336v.02c14.006 0 25.36-11.354 25.36-25.36 0-12.48-9.018-22.855-20.888-24.996a12.6 12.6 0 0 0 8.68-11.972m-77.05 68.263c0-7.012 5.685-12.697 12.697-12.697s12.697 5.685 12.697 12.697c0 7.013-5.685 12.697-12.697 12.697s-12.697-5.685-12.697-12.697'
d='M122.19,0 H90.27 C40.51,0 0,39.88 0,92.95 C0,141.07 38.93,183.77 90.27,183.77 H122.19 C172.61,183.77 213.31,142.82 213.31,92.95 C213.31,43.29 173.09,0 122.19,0 Z M10.82,92.54 C10.82,50.19 45.91,11.49 91.96,11.49 C138.73,11.49 172.69,50.33 172.69,92.13 C172.69,117.76 154.06,139.09 129.02,143.31 C145.16,131.15 155.48,112.73 155.48,92.4 C155.48,59.09 127.44,28.82 92.37,28.82 C57.23,28.82 28.51,57.23 28.51,92.91 C28.51,122.63 43.61,151.08 69.99,168.21 L71.74,169.33 C35.99,161.39 10.82,130.11 10.82,92.54 Z M106.33,42.76 C128.88,50.19 143.91,68.92 143.91,92.26 C143.91,114.23 128.68,134.63 106.12,141.71 C105.44,141.96 105.17,141.96 105.17,141.96 C83.91,135.76 69.29,116.38 69.29,92.71 C69.29,69.91 83.71,50.33 106.33,42.76 Z M120.91,172.13 C76.11,172.13 40.09,137.21 40.09,93.32 C40.09,67.03 57.17,46.11 83.98,41.33 C67.04,53.83 57.3,71.71 57.3,92.71 C57.3,125.75 82.94,155.33 120.77,155.33 C155.01,155.33 184.31,125.2 184.31,92.47 C184.31,62.34 169.96,34.06 141.92,14.55 L141.65,14.34 C175.81,23.68 202.26,54.11 202.26,92.81 C202.26,135.69 166.38,172.13 120.91,172.13 Z'
fill='#FB5001'
/>
</svg>
)
@@ -5578,6 +5598,35 @@ export function GoogleMapsIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function GooglePagespeedIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='-1.74 -1.81 285.55 266.85' xmlns='http://www.w3.org/2000/svg'>
<path
d='M272.73 37.23v179.68a18.58 18.58 0 0 1-18.57 18.59H18.65A18.58 18.58 0 0 1 .06 216.94V37.23z'
fill='#e1e1e1'
/>
<path
d='M18.65 0h235.5a18.58 18.58 0 0 1 18.58 18.56v18.67H.07V18.59A18.58 18.58 0 0 1 18.64 0z'
fill='#c2c2c2'
/>
<path
d='M136.3 92.96a99 99 0 0 0-99 99v.13c0 2.08-.12 4.64 0 6.2h43.25a54.87 54.87 0 0 1 0-6.2 55.81 55.81 0 0 1 85.06-47.45l31.12-31.12a98.76 98.76 0 0 0-60.44-20.57z'
fill='#4285f4'
/>
<path
d='M196.73 113.46l-31.14 31.14a55.74 55.74 0 0 1 26.56 47.45 54.87 54.87 0 0 1 0 6.2h43.39c.12-1.48 0-4.12 0-6.2a99 99 0 0 0-38.81-78.59z'
fill='#f44336'
/>
<circle cx='24.85' cy='18.59' fill='#eee' r='6.2' />
<circle cx='49.65' cy='18.59' fill='#eee' r='6.2' />
<path
d='M197.01 117.23a3.05 3.05 0 0 0 .59-1.81 3.11 3.11 0 0 0-3.1-3.1 3 3 0 0 0-1.91.68l-67.56 52a18.58 18.58 0 1 0 27.24 24.33l44.73-72.1z'
fill='#9e9e9e'
/>
</svg>
)
}
export function GoogleTranslateIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 998.1 998.3'>

View File

@@ -9,6 +9,7 @@ import {
AirtableIcon,
AirweaveIcon,
AlgoliaIcon,
AmplitudeIcon,
ApifyIcon,
ApolloIcon,
ArxivIcon,
@@ -56,6 +57,7 @@ import {
GoogleGroupsIcon,
GoogleIcon,
GoogleMapsIcon,
GooglePagespeedIcon,
GoogleSheetsIcon,
GoogleSlidesIcon,
GoogleTasksIcon,
@@ -102,6 +104,7 @@ import {
OpenAIIcon,
OutlookIcon,
PackageSearchIcon,
PagerDutyIcon,
ParallelIcon,
PerplexityIcon,
PineconeIcon,
@@ -167,6 +170,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
airtable: AirtableIcon,
airweave: AirweaveIcon,
algolia: AlgoliaIcon,
amplitude: AmplitudeIcon,
apify: ApifyIcon,
apollo: ApolloIcon,
arxiv: ArxivIcon,
@@ -211,6 +215,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
google_forms: GoogleFormsIcon,
google_groups: GoogleGroupsIcon,
google_maps: GoogleMapsIcon,
google_pagespeed: GooglePagespeedIcon,
google_search: GoogleIcon,
google_sheets_v2: GoogleSheetsIcon,
google_slides_v2: GoogleSlidesIcon,
@@ -258,6 +263,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
onepassword: OnePasswordIcon,
openai: OpenAIIcon,
outlook: OutlookIcon,
pagerduty: PagerDutyIcon,
parallel_ai: ParallelIcon,
perplexity: PerplexityIcon,
pinecone: PineconeIcon,

View File

@@ -0,0 +1,313 @@
---
title: Amplitude
description: Track events and query analytics from Amplitude
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="amplitude"
color="#1B1F3B"
/>
{/* MANUAL-CONTENT-START:intro */}
[Amplitude](https://amplitude.com/) is a leading digital analytics platform that helps teams understand user behavior, measure product performance, and make data-driven decisions at scale.
The Amplitude integration in Sim connects with the Amplitude HTTP and Dashboard REST APIs using API key and secret key authentication, allowing your agents to track events, manage user properties, and query analytics data programmatically. This API-based approach ensures secure access to Amplitude's full suite of analytics capabilities.
With the Amplitude integration, your agents can:
- **Track events**: Send custom events to Amplitude with rich properties, revenue data, and user context directly from your workflows
- **Identify users**: Set and update user properties using operations like $set, $setOnce, $add, $append, and $unset to maintain detailed user profiles
- **Search for users**: Look up users by User ID, Device ID, or Amplitude ID to retrieve profile information and metadata
- **Query event analytics**: Run event segmentation queries with grouping, custom metrics (uniques, totals, averages, DAU percentages), and flexible date ranges
- **Monitor user activity**: Retrieve event streams for specific users to understand individual user journeys and behavior patterns
- **Analyze active users**: Get active or new user counts over time with daily, weekly, or monthly granularity
- **Track revenue**: Access revenue LTV metrics including ARPU, ARPPU, total revenue, and paying user counts
In Sim, the Amplitude integration enables powerful analytics automation scenarios. Your agents can track product events in real time based on workflow triggers, enrich user profiles as new data becomes available, query segmentation data to inform downstream decisions, or build monitoring workflows that alert on changes in key metrics. By connecting Sim with Amplitude, you can build intelligent agents that bridge the gap between analytics insights and automated action, enabling data-driven workflows that respond to user behavior patterns and product performance trends.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate Amplitude into your workflow to track events, identify users and groups, search for users, query analytics, and retrieve revenue data.
## Tools
### `amplitude_send_event`
Track an event in Amplitude using the HTTP V2 API.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `userId` | string | No | User ID \(required if no device_id\) |
| `deviceId` | string | No | Device ID \(required if no user_id\) |
| `eventType` | string | Yes | Name of the event \(e.g., "page_view", "purchase"\) |
| `eventProperties` | string | No | JSON object of custom event properties |
| `userProperties` | string | No | JSON object of user properties to set \(supports $set, $setOnce, $add, $append, $unset\) |
| `time` | string | No | Event timestamp in milliseconds since epoch |
| `sessionId` | string | No | Session start time in milliseconds since epoch |
| `insertId` | string | No | Unique ID for deduplication \(within 7-day window\) |
| `appVersion` | string | No | Application version string |
| `platform` | string | No | Platform \(e.g., "Web", "iOS", "Android"\) |
| `country` | string | No | Two-letter country code |
| `language` | string | No | Language code \(e.g., "en"\) |
| `ip` | string | No | IP address for geo-location |
| `price` | string | No | Price of the item purchased |
| `quantity` | string | No | Quantity of items purchased |
| `revenue` | string | No | Revenue amount |
| `productId` | string | No | Product identifier |
| `revenueType` | string | No | Revenue type \(e.g., "purchase", "refund"\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `code` | number | Response code \(200 for success\) |
| `eventsIngested` | number | Number of events ingested |
| `payloadSizeBytes` | number | Size of the payload in bytes |
| `serverUploadTime` | number | Server upload timestamp |
### `amplitude_identify_user`
Set user properties in Amplitude using the Identify API. Supports $set, $setOnce, $add, $append, $unset operations.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `userId` | string | No | User ID \(required if no device_id\) |
| `deviceId` | string | No | Device ID \(required if no user_id\) |
| `userProperties` | string | Yes | JSON object of user properties. Use operations like $set, $setOnce, $add, $append, $unset. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `code` | number | HTTP response status code |
| `message` | string | Response message |
### `amplitude_group_identify`
Set group-level properties in Amplitude. Supports $set, $setOnce, $add, $append, $unset operations.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `groupType` | string | Yes | Group classification \(e.g., "company", "org_id"\) |
| `groupValue` | string | Yes | Specific group identifier \(e.g., "Acme Corp"\) |
| `groupProperties` | string | Yes | JSON object of group properties. Use operations like $set, $setOnce, $add, $append, $unset. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `code` | number | HTTP response status code |
| `message` | string | Response message |
### `amplitude_user_search`
Search for a user by User ID, Device ID, or Amplitude ID using the Dashboard REST API.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `secretKey` | string | Yes | Amplitude Secret Key |
| `user` | string | Yes | User ID, Device ID, or Amplitude ID to search for |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `matches` | array | List of matching users |
| ↳ `amplitudeId` | number | Amplitude internal user ID |
| ↳ `userId` | string | External user ID |
| `type` | string | Match type \(e.g., match_user_or_device_id\) |
### `amplitude_user_activity`
Get the event stream for a specific user by their Amplitude ID.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `secretKey` | string | Yes | Amplitude Secret Key |
| `amplitudeId` | string | Yes | Amplitude internal user ID |
| `offset` | string | No | Offset for pagination \(default 0\) |
| `limit` | string | No | Maximum number of events to return \(default 1000, max 1000\) |
| `direction` | string | No | Sort direction: "latest" or "earliest" \(default: latest\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `events` | array | List of user events |
| ↳ `eventType` | string | Type of event |
| ↳ `eventTime` | string | Event timestamp |
| ↳ `eventProperties` | json | Custom event properties |
| ↳ `userProperties` | json | User properties at event time |
| ↳ `sessionId` | number | Session ID |
| ↳ `platform` | string | Platform |
| ↳ `country` | string | Country |
| ↳ `city` | string | City |
| `userData` | json | User metadata |
| ↳ `userId` | string | External user ID |
| ↳ `canonicalAmplitudeId` | number | Canonical Amplitude ID |
| ↳ `numEvents` | number | Total event count |
| ↳ `numSessions` | number | Total session count |
| ↳ `platform` | string | Primary platform |
| ↳ `country` | string | Country |
### `amplitude_user_profile`
Get a user profile including properties, cohort memberships, and computed properties.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Yes | Amplitude Secret Key |
| `userId` | string | No | External user ID \(required if no device_id\) |
| `deviceId` | string | No | Device ID \(required if no user_id\) |
| `getAmpProps` | string | No | Include Amplitude user properties \(true/false, default: false\) |
| `getCohortIds` | string | No | Include cohort IDs the user belongs to \(true/false, default: false\) |
| `getComputations` | string | No | Include computed user properties \(true/false, default: false\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | External user ID |
| `deviceId` | string | Device ID |
| `ampProps` | json | Amplitude user properties \(library, first_used, last_used, custom properties\) |
| `cohortIds` | array | List of cohort IDs the user belongs to |
| `computations` | json | Computed user properties |
### `amplitude_event_segmentation`
Query event analytics data with segmentation. Get event counts, uniques, averages, and more.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `secretKey` | string | Yes | Amplitude Secret Key |
| `eventType` | string | Yes | Event type name to analyze |
| `start` | string | Yes | Start date in YYYYMMDD format |
| `end` | string | Yes | End date in YYYYMMDD format |
| `metric` | string | No | Metric type: uniques, totals, pct_dau, average, histogram, sums, value_avg, or formula \(default: uniques\) |
| `interval` | string | No | Time interval: 1 \(daily\), 7 \(weekly\), or 30 \(monthly\) |
| `groupBy` | string | No | Property name to group by \(prefix custom user properties with "gp:"\) |
| `limit` | string | No | Maximum number of group-by values \(max 1000\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | json | Time-series data arrays indexed by series |
| `seriesLabels` | array | Labels for each data series |
| `seriesCollapsed` | json | Collapsed aggregate totals per series |
| `xValues` | array | Date values for the x-axis |
### `amplitude_get_active_users`
Get active or new user counts over a date range from the Dashboard REST API.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `secretKey` | string | Yes | Amplitude Secret Key |
| `start` | string | Yes | Start date in YYYYMMDD format |
| `end` | string | Yes | End date in YYYYMMDD format |
| `metric` | string | No | Metric type: "active" or "new" \(default: active\) |
| `interval` | string | No | Time interval: 1 \(daily\), 7 \(weekly\), or 30 \(monthly\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | json | Array of data series with user counts per time interval |
| `seriesMeta` | array | Metadata labels for each data series \(e.g., segment names\) |
| `xValues` | array | Date values for the x-axis |
### `amplitude_realtime_active_users`
Get real-time active user counts at 5-minute granularity for the last 2 days.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `secretKey` | string | Yes | Amplitude Secret Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | json | Array of data series with active user counts at 5-minute intervals |
| `seriesLabels` | array | Labels for each series \(e.g., "Today", "Yesterday"\) |
| `xValues` | array | Time values for the x-axis \(e.g., "15:00", "15:05"\) |
### `amplitude_list_events`
List all event types in the Amplitude project with their weekly totals and unique counts.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `secretKey` | string | Yes | Amplitude Secret Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `events` | array | List of event types in the project |
| ↳ `value` | string | Event type name |
| ↳ `displayName` | string | Event display name |
| ↳ `totals` | number | Weekly total count |
| ↳ `hidden` | boolean | Whether the event is hidden |
| ↳ `deleted` | boolean | Whether the event is deleted |
### `amplitude_get_revenue`
Get revenue LTV data including ARPU, ARPPU, total revenue, and paying user counts.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Amplitude API Key |
| `secretKey` | string | Yes | Amplitude Secret Key |
| `start` | string | Yes | Start date in YYYYMMDD format |
| `end` | string | Yes | End date in YYYYMMDD format |
| `metric` | string | No | Metric: 0 \(ARPU\), 1 \(ARPPU\), 2 \(Total Revenue\), 3 \(Paying Users\) |
| `interval` | string | No | Time interval: 1 \(daily\), 7 \(weekly\), or 30 \(monthly\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | json | Array of revenue data series |
| `seriesLabels` | array | Labels for each data series |
| `xValues` | array | Date values for the x-axis |

View File

@@ -0,0 +1,84 @@
---
title: Google PageSpeed
description: Analyze webpage performance with Google PageSpeed Insights
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="google_pagespeed"
color="#E0E0E0"
/>
{/* MANUAL-CONTENT-START:intro */}
[Google PageSpeed Insights](https://pagespeed.web.dev/) is a web performance analysis tool powered by Lighthouse that evaluates the quality of web pages across multiple dimensions including performance, accessibility, SEO, and best practices.
With the Google PageSpeed integration in Sim, you can:
- **Analyze webpage performance**: Get detailed performance scores and metrics for any public URL, including First Contentful Paint, Largest Contentful Paint, and Speed Index
- **Evaluate accessibility**: Check how well a webpage meets accessibility standards and identify areas for improvement
- **Audit SEO**: Assess a page's search engine optimization and discover opportunities to improve rankings
- **Review best practices**: Verify that a webpage follows modern web development best practices
- **Compare strategies**: Run analyses using either desktop or mobile strategies to understand performance across device types
- **Localize results**: Retrieve analysis results in different locales for internationalized reporting
In Sim, the Google PageSpeed integration enables your agents to programmatically audit web pages as part of automated workflows. This is useful for monitoring site performance over time, triggering alerts when scores drop below thresholds, generating performance reports, and ensuring that deployed changes meet quality standards before release.
### Getting Your API Key
1. Go to the [Google Cloud Console](https://console.cloud.google.com/)
2. Create or select a project
3. Enable the **PageSpeed Insights API** from the API Library
4. Navigate to **Credentials** and create an API key
5. Use the API key in the Sim block configuration
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Analyze web pages for performance, accessibility, SEO, and best practices using Google PageSpeed Insights API powered by Lighthouse.
## Tools
### `google_pagespeed_analyze`
Analyze a webpage for performance, accessibility, SEO, and best practices using Google PageSpeed Insights.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Google PageSpeed Insights API Key |
| `url` | string | Yes | The URL of the webpage to analyze |
| `category` | string | No | Lighthouse categories to analyze \(comma-separated\): performance, accessibility, best-practices, seo |
| `strategy` | string | No | Analysis strategy: desktop or mobile |
| `locale` | string | No | Locale for results \(e.g., en, fr, de\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `finalUrl` | string | The final URL after redirects |
| `performanceScore` | number | Performance category score \(0-1\) |
| `accessibilityScore` | number | Accessibility category score \(0-1\) |
| `bestPracticesScore` | number | Best Practices category score \(0-1\) |
| `seoScore` | number | SEO category score \(0-1\) |
| `firstContentfulPaint` | string | Time to First Contentful Paint \(display value\) |
| `firstContentfulPaintMs` | number | Time to First Contentful Paint in milliseconds |
| `largestContentfulPaint` | string | Time to Largest Contentful Paint \(display value\) |
| `largestContentfulPaintMs` | number | Time to Largest Contentful Paint in milliseconds |
| `totalBlockingTime` | string | Total Blocking Time \(display value\) |
| `totalBlockingTimeMs` | number | Total Blocking Time in milliseconds |
| `cumulativeLayoutShift` | string | Cumulative Layout Shift \(display value\) |
| `cumulativeLayoutShiftValue` | number | Cumulative Layout Shift numeric value |
| `speedIndex` | string | Speed Index \(display value\) |
| `speedIndexMs` | number | Speed Index in milliseconds |
| `interactive` | string | Time to Interactive \(display value\) |
| `interactiveMs` | number | Time to Interactive in milliseconds |
| `overallCategory` | string | Overall loading experience category \(FAST, AVERAGE, SLOW, or NONE\) |
| `analysisTimestamp` | string | UTC timestamp of the analysis |
| `lighthouseVersion` | string | Version of Lighthouse used for the analysis |

View File

@@ -6,6 +6,7 @@
"airtable",
"airweave",
"algolia",
"amplitude",
"apify",
"apollo",
"arxiv",
@@ -50,6 +51,7 @@
"google_forms",
"google_groups",
"google_maps",
"google_pagespeed",
"google_search",
"google_sheets",
"google_slides",
@@ -97,6 +99,7 @@
"onepassword",
"openai",
"outlook",
"pagerduty",
"parallel_ai",
"perplexity",
"pinecone",

View File

@@ -0,0 +1,217 @@
---
title: PagerDuty
description: Manage incidents and on-call schedules with PagerDuty
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="pagerduty"
color="#06AC38"
/>
{/* MANUAL-CONTENT-START:intro */}
[PagerDuty](https://www.pagerduty.com/) is a leading incident management platform that helps engineering and operations teams detect, triage, and resolve infrastructure and application issues in real time. PagerDuty integrates with monitoring tools, orchestrates on-call schedules, and ensures the right people are alerted when incidents occur.
The PagerDuty integration in Sim connects with the PagerDuty REST API v2 using API key authentication, enabling your agents to manage the full incident lifecycle and query on-call information programmatically.
With the PagerDuty integration, your agents can:
- **List and filter incidents**: Retrieve incidents filtered by status (triggered, acknowledged, resolved), service, date range, and sort order to monitor your operational health
- **Create incidents**: Trigger new incidents on specific services with custom titles, descriptions, urgency levels, and assignees directly from your workflows
- **Update incidents**: Acknowledge or resolve incidents, change urgency, and add resolution notes to keep your incident management in sync with automated processes
- **Add notes to incidents**: Attach contextual information, investigation findings, or automated diagnostics as notes on existing incidents
- **List services**: Query your PagerDuty service catalog to discover service IDs and metadata for use in other operations
- **Check on-call schedules**: Retrieve current on-call entries filtered by escalation policy or schedule to determine who is responsible at any given time
In Sim, the PagerDuty integration enables powerful incident automation scenarios. Your agents can automatically create incidents based on monitoring alerts, enrich incidents with diagnostic data from other tools, resolve incidents when automated remediation succeeds, or build escalation workflows that check on-call schedules and route notifications accordingly. By connecting Sim with PagerDuty, you can build intelligent agents that bridge the gap between detection and response, reducing mean time to resolution and ensuring consistent incident handling across your organization.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate PagerDuty into your workflow to list, create, and update incidents, add notes, list services, and check on-call schedules.
## Tools
### `pagerduty_list_incidents`
List incidents from PagerDuty with optional filters.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | PagerDuty REST API Key |
| `statuses` | string | No | Comma-separated statuses to filter \(triggered, acknowledged, resolved\) |
| `serviceIds` | string | No | Comma-separated service IDs to filter |
| `since` | string | No | Start date filter \(ISO 8601 format\) |
| `until` | string | No | End date filter \(ISO 8601 format\) |
| `sortBy` | string | No | Sort field \(e.g., created_at:desc\) |
| `limit` | string | No | Maximum number of results \(max 100\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `incidents` | array | Array of incidents |
| ↳ `id` | string | Incident ID |
| ↳ `incidentNumber` | number | Incident number |
| ↳ `title` | string | Incident title |
| ↳ `status` | string | Incident status |
| ↳ `urgency` | string | Incident urgency |
| ↳ `createdAt` | string | Creation timestamp |
| ↳ `updatedAt` | string | Last updated timestamp |
| ↳ `serviceName` | string | Service name |
| ↳ `serviceId` | string | Service ID |
| ↳ `assigneeName` | string | Assignee name |
| ↳ `assigneeId` | string | Assignee ID |
| ↳ `escalationPolicyName` | string | Escalation policy name |
| ↳ `htmlUrl` | string | PagerDuty web URL |
| `total` | number | Total number of matching incidents |
| `more` | boolean | Whether more results are available |
### `pagerduty_create_incident`
Create a new incident in PagerDuty.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | PagerDuty REST API Key |
| `fromEmail` | string | Yes | Email address of a valid PagerDuty user |
| `title` | string | Yes | Incident title/summary |
| `serviceId` | string | Yes | ID of the PagerDuty service |
| `urgency` | string | No | Urgency level \(high or low\) |
| `body` | string | No | Detailed description of the incident |
| `escalationPolicyId` | string | No | Escalation policy ID to assign |
| `assigneeId` | string | No | User ID to assign the incident to |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Created incident ID |
| `incidentNumber` | number | Incident number |
| `title` | string | Incident title |
| `status` | string | Incident status |
| `urgency` | string | Incident urgency |
| `createdAt` | string | Creation timestamp |
| `serviceName` | string | Service name |
| `serviceId` | string | Service ID |
| `htmlUrl` | string | PagerDuty web URL |
### `pagerduty_update_incident`
Update an incident in PagerDuty (acknowledge, resolve, change urgency, etc.).
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | PagerDuty REST API Key |
| `fromEmail` | string | Yes | Email address of a valid PagerDuty user |
| `incidentId` | string | Yes | ID of the incident to update |
| `status` | string | No | New status \(acknowledged or resolved\) |
| `title` | string | No | New incident title |
| `urgency` | string | No | New urgency \(high or low\) |
| `escalationLevel` | string | No | Escalation level to escalate to |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Incident ID |
| `incidentNumber` | number | Incident number |
| `title` | string | Incident title |
| `status` | string | Updated status |
| `urgency` | string | Updated urgency |
| `updatedAt` | string | Last updated timestamp |
| `htmlUrl` | string | PagerDuty web URL |
### `pagerduty_add_note`
Add a note to an existing PagerDuty incident.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | PagerDuty REST API Key |
| `fromEmail` | string | Yes | Email address of a valid PagerDuty user |
| `incidentId` | string | Yes | ID of the incident to add the note to |
| `content` | string | Yes | Note content text |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Note ID |
| `content` | string | Note content |
| `createdAt` | string | Creation timestamp |
| `userName` | string | Name of the user who created the note |
### `pagerduty_list_services`
List services from PagerDuty with optional name filter.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | PagerDuty REST API Key |
| `query` | string | No | Filter services by name |
| `limit` | string | No | Maximum number of results \(max 100\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `services` | array | Array of services |
| ↳ `id` | string | Service ID |
| ↳ `name` | string | Service name |
| ↳ `description` | string | Service description |
| ↳ `status` | string | Service status |
| ↳ `escalationPolicyName` | string | Escalation policy name |
| ↳ `escalationPolicyId` | string | Escalation policy ID |
| ↳ `createdAt` | string | Creation timestamp |
| ↳ `htmlUrl` | string | PagerDuty web URL |
| `total` | number | Total number of matching services |
| `more` | boolean | Whether more results are available |
### `pagerduty_list_oncalls`
List current on-call entries from PagerDuty.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | PagerDuty REST API Key |
| `escalationPolicyIds` | string | No | Comma-separated escalation policy IDs to filter |
| `scheduleIds` | string | No | Comma-separated schedule IDs to filter |
| `since` | string | No | Start time filter \(ISO 8601 format\) |
| `until` | string | No | End time filter \(ISO 8601 format\) |
| `limit` | string | No | Maximum number of results \(max 100\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `oncalls` | array | Array of on-call entries |
| ↳ `userName` | string | On-call user name |
| ↳ `userId` | string | On-call user ID |
| ↳ `escalationLevel` | number | Escalation level |
| ↳ `escalationPolicyName` | string | Escalation policy name |
| ↳ `escalationPolicyId` | string | Escalation policy ID |
| ↳ `scheduleName` | string | Schedule name |
| ↳ `scheduleId` | string | Schedule ID |
| ↳ `start` | string | On-call start time |
| ↳ `end` | string | On-call end time |
| `total` | number | Total number of matching on-call entries |
| `more` | boolean | Whether more results are available |

View File

@@ -0,0 +1,745 @@
import { AmplitudeIcon } from '@/components/icons'
import { AuthMode, type BlockConfig } from '@/blocks/types'
export const AmplitudeBlock: BlockConfig = {
type: 'amplitude',
name: 'Amplitude',
description: 'Track events and query analytics from Amplitude',
longDescription:
'Integrate Amplitude into your workflow to track events, identify users and groups, search for users, query analytics, and retrieve revenue data.',
docsLink: 'https://docs.sim.ai/tools/amplitude',
category: 'tools',
bgColor: '#1B1F3B',
icon: AmplitudeIcon,
authMode: AuthMode.ApiKey,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Send Event', id: 'send_event' },
{ label: 'Identify User', id: 'identify_user' },
{ label: 'Group Identify', id: 'group_identify' },
{ label: 'User Search', id: 'user_search' },
{ label: 'User Activity', id: 'user_activity' },
{ label: 'User Profile', id: 'user_profile' },
{ label: 'Event Segmentation', id: 'event_segmentation' },
{ label: 'Get Active Users', id: 'get_active_users' },
{ label: 'Real-time Active Users', id: 'realtime_active_users' },
{ label: 'List Events', id: 'list_events' },
{ label: 'Get Revenue', id: 'get_revenue' },
],
value: () => 'send_event',
},
// API Key (required for all operations)
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
required: true,
placeholder: 'Enter your Amplitude API Key',
password: true,
condition: {
field: 'operation',
value: 'user_profile',
not: true,
},
},
// API Key for user_profile (not required - uses only secretKey)
// User Profile uses Api-Key header with secret key only
// Secret Key (required for Dashboard REST API operations + User Profile)
{
id: 'secretKey',
title: 'Secret Key',
type: 'short-input',
required: {
field: 'operation',
value: [
'user_search',
'user_activity',
'user_profile',
'event_segmentation',
'get_active_users',
'realtime_active_users',
'list_events',
'get_revenue',
],
},
placeholder: 'Enter your Amplitude Secret Key',
password: true,
condition: {
field: 'operation',
value: [
'user_search',
'user_activity',
'user_profile',
'event_segmentation',
'get_active_users',
'realtime_active_users',
'list_events',
'get_revenue',
],
},
},
// --- Send Event fields ---
{
id: 'eventType',
title: 'Event Type',
type: 'short-input',
required: { field: 'operation', value: 'send_event' },
placeholder: 'e.g., page_view, purchase, signup',
condition: { field: 'operation', value: 'send_event' },
},
{
id: 'userId',
title: 'User ID',
type: 'short-input',
placeholder: 'User identifier',
condition: { field: 'operation', value: ['send_event', 'identify_user'] },
},
{
id: 'profileUserId',
title: 'User ID',
type: 'short-input',
placeholder: 'External user ID (required if no Device ID)',
condition: { field: 'operation', value: 'user_profile' },
},
{
id: 'deviceId',
title: 'Device ID',
type: 'short-input',
placeholder: 'Device identifier',
condition: { field: 'operation', value: ['send_event', 'identify_user'] },
mode: 'advanced',
},
{
id: 'profileDeviceId',
title: 'Device ID',
type: 'short-input',
placeholder: 'Device ID (required if no User ID)',
condition: { field: 'operation', value: 'user_profile' },
mode: 'advanced',
},
{
id: 'eventProperties',
title: 'Event Properties',
type: 'long-input',
placeholder: '{"button": "signup", "page": "/home"}',
condition: { field: 'operation', value: 'send_event' },
wandConfig: {
enabled: true,
prompt:
'Generate a JSON object of event properties for an Amplitude event. Return ONLY the JSON object - no explanations, no extra text.',
generationType: 'json-object',
},
},
{
id: 'sendEventUserProperties',
title: 'User Properties',
type: 'long-input',
placeholder: '{"$set": {"plan": "premium"}}',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate a JSON object of user properties for Amplitude. Use $set, $setOnce, $add, $append, or $unset operations. Return ONLY the JSON object - no explanations, no extra text.',
generationType: 'json-object',
},
},
{
id: 'platform',
title: 'Platform',
type: 'short-input',
placeholder: 'e.g., Web, iOS, Android',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'appVersion',
title: 'App Version',
type: 'short-input',
placeholder: 'e.g., 1.0.0',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'insertId',
title: 'Insert ID',
type: 'short-input',
placeholder: 'Unique ID for deduplication',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'price',
title: 'Price',
type: 'short-input',
placeholder: '9.99',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'quantity',
title: 'Quantity',
type: 'short-input',
placeholder: '1',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'revenue',
title: 'Revenue',
type: 'short-input',
placeholder: '9.99',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'productId',
title: 'Product ID',
type: 'short-input',
placeholder: 'Product identifier',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'revenueType',
title: 'Revenue Type',
type: 'short-input',
placeholder: 'e.g., purchase, refund',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'country',
title: 'Country',
type: 'short-input',
placeholder: 'Two-letter country code (e.g., US)',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'language',
title: 'Language',
type: 'short-input',
placeholder: 'Language code (e.g., en)',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'ip',
title: 'IP Address',
type: 'short-input',
placeholder: 'IP for geo-location (use "$remote" for request IP)',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
{
id: 'time',
title: 'Timestamp',
type: 'short-input',
placeholder: 'Milliseconds since epoch',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate a timestamp in milliseconds since epoch for the current time. Return ONLY the number - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'sessionId',
title: 'Session ID',
type: 'short-input',
placeholder: 'Session start time in milliseconds (-1 for no session)',
condition: { field: 'operation', value: 'send_event' },
mode: 'advanced',
},
// --- Identify User fields ---
{
id: 'identifyUserProperties',
title: 'User Properties',
type: 'long-input',
required: { field: 'operation', value: 'identify_user' },
placeholder: '{"$set": {"plan": "premium", "company": "Acme"}}',
condition: { field: 'operation', value: 'identify_user' },
wandConfig: {
enabled: true,
prompt:
'Generate a JSON object of user properties for Amplitude Identify API. Use $set, $setOnce, $add, $append, or $unset operations. Return ONLY the JSON object - no explanations, no extra text.',
generationType: 'json-object',
},
},
// --- Group Identify fields ---
{
id: 'groupType',
title: 'Group Type',
type: 'short-input',
required: { field: 'operation', value: 'group_identify' },
placeholder: 'e.g., company, org_id',
condition: { field: 'operation', value: 'group_identify' },
},
{
id: 'groupValue',
title: 'Group Value',
type: 'short-input',
required: { field: 'operation', value: 'group_identify' },
placeholder: 'e.g., Acme Corp',
condition: { field: 'operation', value: 'group_identify' },
},
{
id: 'groupProperties',
title: 'Group Properties',
type: 'long-input',
required: { field: 'operation', value: 'group_identify' },
placeholder: '{"$set": {"industry": "tech", "employee_count": 500}}',
condition: { field: 'operation', value: 'group_identify' },
wandConfig: {
enabled: true,
prompt:
'Generate a JSON object of group properties for Amplitude Group Identify API. Use $set, $setOnce, $add, $append, or $unset operations. Return ONLY the JSON object - no explanations, no extra text.',
generationType: 'json-object',
},
},
// --- User Search fields ---
{
id: 'searchUser',
title: 'User',
type: 'short-input',
required: { field: 'operation', value: 'user_search' },
placeholder: 'User ID, Device ID, or Amplitude ID',
condition: { field: 'operation', value: 'user_search' },
},
// --- User Activity fields ---
{
id: 'amplitudeId',
title: 'Amplitude ID',
type: 'short-input',
required: { field: 'operation', value: 'user_activity' },
placeholder: 'Amplitude internal user ID',
condition: { field: 'operation', value: 'user_activity' },
},
{
id: 'activityOffset',
title: 'Offset',
type: 'short-input',
placeholder: '0',
condition: { field: 'operation', value: 'user_activity' },
mode: 'advanced',
},
{
id: 'activityLimit',
title: 'Limit',
type: 'short-input',
placeholder: '1000',
condition: { field: 'operation', value: 'user_activity' },
mode: 'advanced',
},
{
id: 'activityDirection',
title: 'Direction',
type: 'dropdown',
options: [
{ label: 'Latest First', id: 'latest' },
{ label: 'Earliest First', id: 'earliest' },
],
value: () => 'latest',
condition: { field: 'operation', value: 'user_activity' },
mode: 'advanced',
},
// --- User Profile fields ---
{
id: 'getAmpProps',
title: 'Include User Properties',
type: 'dropdown',
options: [
{ label: 'No', id: 'false' },
{ label: 'Yes', id: 'true' },
],
value: () => 'false',
condition: { field: 'operation', value: 'user_profile' },
mode: 'advanced',
},
{
id: 'getCohortIds',
title: 'Include Cohort IDs',
type: 'dropdown',
options: [
{ label: 'No', id: 'false' },
{ label: 'Yes', id: 'true' },
],
value: () => 'false',
condition: { field: 'operation', value: 'user_profile' },
mode: 'advanced',
},
{
id: 'getComputations',
title: 'Include Computed Properties',
type: 'dropdown',
options: [
{ label: 'No', id: 'false' },
{ label: 'Yes', id: 'true' },
],
value: () => 'false',
condition: { field: 'operation', value: 'user_profile' },
mode: 'advanced',
},
// --- Event Segmentation fields ---
{
id: 'segmentationEventType',
title: 'Event Type',
type: 'short-input',
required: { field: 'operation', value: 'event_segmentation' },
placeholder: 'Event type to analyze',
condition: { field: 'operation', value: 'event_segmentation' },
},
{
id: 'segmentationStart',
title: 'Start Date',
type: 'short-input',
required: { field: 'operation', value: 'event_segmentation' },
placeholder: 'YYYYMMDD',
condition: { field: 'operation', value: 'event_segmentation' },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYYMMDD format. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'segmentationEnd',
title: 'End Date',
type: 'short-input',
required: { field: 'operation', value: 'event_segmentation' },
placeholder: 'YYYYMMDD',
condition: { field: 'operation', value: 'event_segmentation' },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYYMMDD format. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'segmentationMetric',
title: 'Metric',
type: 'dropdown',
options: [
{ label: 'Uniques', id: 'uniques' },
{ label: 'Totals', id: 'totals' },
{ label: '% DAU', id: 'pct_dau' },
{ label: 'Average', id: 'average' },
{ label: 'Histogram', id: 'histogram' },
{ label: 'Sums', id: 'sums' },
{ label: 'Value Average', id: 'value_avg' },
{ label: 'Formula', id: 'formula' },
],
value: () => 'uniques',
condition: { field: 'operation', value: 'event_segmentation' },
mode: 'advanced',
},
{
id: 'segmentationInterval',
title: 'Interval',
type: 'dropdown',
options: [
{ label: 'Daily', id: '1' },
{ label: 'Weekly', id: '7' },
{ label: 'Monthly', id: '30' },
],
value: () => '1',
condition: { field: 'operation', value: 'event_segmentation' },
mode: 'advanced',
},
{
id: 'segmentationGroupBy',
title: 'Group By',
type: 'short-input',
placeholder: 'Property name (prefix custom with "gp:")',
condition: { field: 'operation', value: 'event_segmentation' },
mode: 'advanced',
},
{
id: 'segmentationLimit',
title: 'Limit',
type: 'short-input',
placeholder: 'Max group-by values (max 1000)',
condition: { field: 'operation', value: 'event_segmentation' },
mode: 'advanced',
},
// --- Get Active Users fields ---
{
id: 'activeUsersStart',
title: 'Start Date',
type: 'short-input',
required: { field: 'operation', value: 'get_active_users' },
placeholder: 'YYYYMMDD',
condition: { field: 'operation', value: 'get_active_users' },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYYMMDD format. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'activeUsersEnd',
title: 'End Date',
type: 'short-input',
required: { field: 'operation', value: 'get_active_users' },
placeholder: 'YYYYMMDD',
condition: { field: 'operation', value: 'get_active_users' },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYYMMDD format. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'activeUsersMetric',
title: 'Metric',
type: 'dropdown',
options: [
{ label: 'Active Users', id: 'active' },
{ label: 'New Users', id: 'new' },
],
value: () => 'active',
condition: { field: 'operation', value: 'get_active_users' },
mode: 'advanced',
},
{
id: 'activeUsersInterval',
title: 'Interval',
type: 'dropdown',
options: [
{ label: 'Daily', id: '1' },
{ label: 'Weekly', id: '7' },
{ label: 'Monthly', id: '30' },
],
value: () => '1',
condition: { field: 'operation', value: 'get_active_users' },
mode: 'advanced',
},
// --- Get Revenue fields ---
{
id: 'revenueStart',
title: 'Start Date',
type: 'short-input',
required: { field: 'operation', value: 'get_revenue' },
placeholder: 'YYYYMMDD',
condition: { field: 'operation', value: 'get_revenue' },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYYMMDD format. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'revenueEnd',
title: 'End Date',
type: 'short-input',
required: { field: 'operation', value: 'get_revenue' },
placeholder: 'YYYYMMDD',
condition: { field: 'operation', value: 'get_revenue' },
wandConfig: {
enabled: true,
prompt:
'Generate a date in YYYYMMDD format. Return ONLY the date string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'revenueMetric',
title: 'Metric',
type: 'dropdown',
options: [
{ label: 'ARPU', id: '0' },
{ label: 'ARPPU', id: '1' },
{ label: 'Total Revenue', id: '2' },
{ label: 'Paying Users', id: '3' },
],
value: () => '2',
condition: { field: 'operation', value: 'get_revenue' },
mode: 'advanced',
},
{
id: 'revenueInterval',
title: 'Interval',
type: 'dropdown',
options: [
{ label: 'Daily', id: '1' },
{ label: 'Weekly', id: '7' },
{ label: 'Monthly', id: '30' },
],
value: () => '1',
condition: { field: 'operation', value: 'get_revenue' },
mode: 'advanced',
},
],
tools: {
access: [
'amplitude_send_event',
'amplitude_identify_user',
'amplitude_group_identify',
'amplitude_user_search',
'amplitude_user_activity',
'amplitude_user_profile',
'amplitude_event_segmentation',
'amplitude_get_active_users',
'amplitude_realtime_active_users',
'amplitude_list_events',
'amplitude_get_revenue',
],
config: {
tool: (params) => `amplitude_${params.operation}`,
params: (params) => {
const result: Record<string, unknown> = {}
switch (params.operation) {
case 'send_event':
if (params.sendEventUserProperties)
result.userProperties = params.sendEventUserProperties
break
case 'identify_user':
if (params.identifyUserProperties) result.userProperties = params.identifyUserProperties
break
case 'user_search':
if (params.searchUser) result.user = params.searchUser
break
case 'user_activity':
if (params.activityOffset) result.offset = params.activityOffset
if (params.activityLimit) result.limit = params.activityLimit
if (params.activityDirection) result.direction = params.activityDirection
break
case 'user_profile':
if (params.profileUserId) result.userId = params.profileUserId
if (params.profileDeviceId) result.deviceId = params.profileDeviceId
break
case 'event_segmentation':
if (params.segmentationEventType) result.eventType = params.segmentationEventType
if (params.segmentationStart) result.start = params.segmentationStart
if (params.segmentationEnd) result.end = params.segmentationEnd
if (params.segmentationMetric) result.metric = params.segmentationMetric
if (params.segmentationInterval) result.interval = params.segmentationInterval
if (params.segmentationGroupBy) result.groupBy = params.segmentationGroupBy
if (params.segmentationLimit) result.limit = params.segmentationLimit
break
case 'get_active_users':
if (params.activeUsersStart) result.start = params.activeUsersStart
if (params.activeUsersEnd) result.end = params.activeUsersEnd
if (params.activeUsersMetric) result.metric = params.activeUsersMetric
if (params.activeUsersInterval) result.interval = params.activeUsersInterval
break
case 'get_revenue':
if (params.revenueStart) result.start = params.revenueStart
if (params.revenueEnd) result.end = params.revenueEnd
if (params.revenueMetric) result.metric = params.revenueMetric
if (params.revenueInterval) result.interval = params.revenueInterval
break
}
return result
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'Amplitude API Key' },
secretKey: { type: 'string', description: 'Amplitude Secret Key' },
eventType: { type: 'string', description: 'Event type name' },
userId: { type: 'string', description: 'User ID' },
deviceId: { type: 'string', description: 'Device ID' },
eventProperties: { type: 'string', description: 'Event properties JSON' },
sendEventUserProperties: { type: 'string', description: 'User properties for send event' },
identifyUserProperties: { type: 'string', description: 'User properties for identify' },
groupType: { type: 'string', description: 'Group type classification' },
groupValue: { type: 'string', description: 'Group identifier value' },
groupProperties: { type: 'string', description: 'Group properties JSON' },
searchUser: { type: 'string', description: 'User to search for' },
amplitudeId: { type: 'string', description: 'Amplitude internal user ID' },
profileUserId: { type: 'string', description: 'User ID for profile lookup' },
profileDeviceId: { type: 'string', description: 'Device ID for profile lookup' },
segmentationEventType: { type: 'string', description: 'Event type to analyze' },
segmentationStart: { type: 'string', description: 'Segmentation start date' },
segmentationEnd: { type: 'string', description: 'Segmentation end date' },
activeUsersStart: { type: 'string', description: 'Active users start date' },
activeUsersEnd: { type: 'string', description: 'Active users end date' },
revenueStart: { type: 'string', description: 'Revenue start date' },
revenueEnd: { type: 'string', description: 'Revenue end date' },
},
outputs: {
code: {
type: 'number',
description: 'Response status code',
},
message: {
type: 'string',
description: 'Response message (identify_user, group_identify)',
},
eventsIngested: {
type: 'number',
description: 'Number of events ingested (send_event)',
},
matches: {
type: 'json',
description: 'User search matches (amplitudeId, userId)',
},
events: {
type: 'json',
description: 'Event list (list_events, user_activity)',
},
userData: {
type: 'json',
description: 'User metadata (user_activity)',
},
series: {
type: 'json',
description: 'Time-series data (segmentation, active_users, revenue, realtime)',
},
seriesLabels: {
type: 'json',
description: 'Labels for each data series (segmentation, realtime, revenue)',
},
seriesMeta: {
type: 'json',
description: 'Metadata labels for data series (active_users)',
},
seriesCollapsed: {
type: 'json',
description: 'Collapsed aggregate totals per series (segmentation)',
},
xValues: {
type: 'json',
description: 'X-axis date/time values for chart data',
},
},
}

View File

@@ -0,0 +1,86 @@
import { GooglePagespeedIcon } from '@/components/icons'
import { AuthMode, type BlockConfig } from '@/blocks/types'
import type { GooglePagespeedAnalyzeResponse } from '@/tools/google_pagespeed/types'
export const GooglePagespeedBlock: BlockConfig<GooglePagespeedAnalyzeResponse> = {
type: 'google_pagespeed',
name: 'Google PageSpeed',
description: 'Analyze webpage performance with Google PageSpeed Insights',
longDescription:
'Analyze web pages for performance, accessibility, SEO, and best practices using Google PageSpeed Insights API powered by Lighthouse.',
docsLink: 'https://docs.sim.ai/tools/google_pagespeed',
category: 'tools',
bgColor: '#E0E0E0',
icon: GooglePagespeedIcon,
authMode: AuthMode.ApiKey,
subBlocks: [
{
id: 'url',
title: 'URL',
type: 'short-input',
required: true,
placeholder: 'https://example.com',
},
{
id: 'strategy',
title: 'Strategy',
type: 'dropdown',
options: [
{ label: 'Desktop', id: 'desktop' },
{ label: 'Mobile', id: 'mobile' },
],
value: () => 'desktop',
},
{
id: 'category',
title: 'Categories',
type: 'short-input',
placeholder: 'performance, accessibility, best-practices, seo',
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate a comma-separated list of Google PageSpeed Insights categories to analyze. Valid values are: performance, accessibility, best-practices, seo. Return ONLY the comma-separated list - no explanations, no extra text.',
},
},
{
id: 'locale',
title: 'Locale',
type: 'short-input',
placeholder: 'en',
mode: 'advanced',
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
required: true,
placeholder: 'Enter your Google PageSpeed API key',
password: true,
},
],
tools: {
access: ['google_pagespeed_analyze'],
config: {
tool: () => 'google_pagespeed_analyze',
},
},
inputs: {
url: { type: 'string', description: 'URL to analyze' },
strategy: { type: 'string', description: 'Analysis strategy (desktop or mobile)' },
category: { type: 'string', description: 'Comma-separated categories to analyze' },
locale: { type: 'string', description: 'Locale for results' },
apiKey: { type: 'string', description: 'Google PageSpeed API key' },
},
outputs: {
response: {
type: 'json',
description:
'PageSpeed analysis results including category scores (performanceScore, accessibilityScore, bestPracticesScore, seoScore), Core Web Vitals display values and numeric values (firstContentfulPaint, largestContentfulPaint, totalBlockingTime, cumulativeLayoutShift, speedIndex, interactive), and metadata (finalUrl, overallCategory, analysisTimestamp, lighthouseVersion)',
},
},
}

View File

@@ -0,0 +1,482 @@
import { PagerDutyIcon } from '@/components/icons'
import { AuthMode, type BlockConfig } from '@/blocks/types'
export const PagerDutyBlock: BlockConfig = {
type: 'pagerduty',
name: 'PagerDuty',
description: 'Manage incidents and on-call schedules with PagerDuty',
longDescription:
'Integrate PagerDuty into your workflow to list, create, and update incidents, add notes, list services, and check on-call schedules.',
docsLink: 'https://docs.sim.ai/tools/pagerduty',
category: 'tools',
bgColor: '#06AC38',
icon: PagerDutyIcon,
authMode: AuthMode.ApiKey,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'List Incidents', id: 'list_incidents' },
{ label: 'Create Incident', id: 'create_incident' },
{ label: 'Update Incident', id: 'update_incident' },
{ label: 'Add Note', id: 'add_note' },
{ label: 'List Services', id: 'list_services' },
{ label: 'List On-Calls', id: 'list_oncalls' },
],
value: () => 'list_incidents',
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
required: true,
placeholder: 'Enter your PagerDuty REST API Key',
password: true,
},
{
id: 'fromEmail',
title: 'From Email',
type: 'short-input',
required: {
field: 'operation',
value: ['create_incident', 'update_incident', 'add_note'],
},
placeholder: 'Valid PagerDuty user email (required for write operations)',
condition: {
field: 'operation',
value: ['create_incident', 'update_incident', 'add_note'],
},
},
// --- List Incidents fields ---
{
id: 'statuses',
title: 'Statuses',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Triggered', id: 'triggered' },
{ label: 'Acknowledged', id: 'acknowledged' },
{ label: 'Resolved', id: 'resolved' },
],
value: () => '',
condition: { field: 'operation', value: 'list_incidents' },
},
{
id: 'listServiceIds',
title: 'Service IDs',
type: 'short-input',
placeholder: 'Comma-separated service IDs to filter',
condition: { field: 'operation', value: 'list_incidents' },
mode: 'advanced',
},
{
id: 'listSince',
title: 'Since',
type: 'short-input',
placeholder: 'Start date (ISO 8601, e.g., 2024-01-01T00:00:00Z)',
condition: { field: 'operation', value: 'list_incidents' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate an ISO 8601 timestamp. Return ONLY the timestamp string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'listUntil',
title: 'Until',
type: 'short-input',
placeholder: 'End date (ISO 8601, e.g., 2024-12-31T23:59:59Z)',
condition: { field: 'operation', value: 'list_incidents' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate an ISO 8601 timestamp. Return ONLY the timestamp string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'listSortBy',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Created At (newest)', id: 'created_at:desc' },
{ label: 'Created At (oldest)', id: 'created_at:asc' },
],
value: () => 'created_at:desc',
condition: { field: 'operation', value: 'list_incidents' },
mode: 'advanced',
},
{
id: 'listLimit',
title: 'Limit',
type: 'short-input',
placeholder: '25',
condition: { field: 'operation', value: 'list_incidents' },
mode: 'advanced',
},
// --- Create Incident fields ---
{
id: 'title',
title: 'Title',
type: 'short-input',
required: { field: 'operation', value: 'create_incident' },
placeholder: 'Incident title/summary',
condition: { field: 'operation', value: 'create_incident' },
},
{
id: 'createServiceId',
title: 'Service ID',
type: 'short-input',
required: { field: 'operation', value: 'create_incident' },
placeholder: 'PagerDuty service ID',
condition: { field: 'operation', value: 'create_incident' },
},
{
id: 'createUrgency',
title: 'Urgency',
type: 'dropdown',
options: [
{ label: 'High', id: 'high' },
{ label: 'Low', id: 'low' },
],
value: () => 'high',
condition: { field: 'operation', value: 'create_incident' },
},
{
id: 'body',
title: 'Description',
type: 'long-input',
placeholder: 'Detailed description of the incident',
condition: { field: 'operation', value: 'create_incident' },
},
{
id: 'escalationPolicyId',
title: 'Escalation Policy ID',
type: 'short-input',
placeholder: 'Escalation policy ID (optional)',
condition: { field: 'operation', value: 'create_incident' },
mode: 'advanced',
},
{
id: 'assigneeId',
title: 'Assignee User ID',
type: 'short-input',
placeholder: 'User ID to assign (optional)',
condition: { field: 'operation', value: 'create_incident' },
mode: 'advanced',
},
// --- Update Incident fields ---
{
id: 'updateIncidentId',
title: 'Incident ID',
type: 'short-input',
required: { field: 'operation', value: 'update_incident' },
placeholder: 'ID of the incident to update',
condition: { field: 'operation', value: 'update_incident' },
},
{
id: 'updateStatus',
title: 'Status',
type: 'dropdown',
options: [
{ label: 'No Change', id: '' },
{ label: 'Acknowledged', id: 'acknowledged' },
{ label: 'Resolved', id: 'resolved' },
],
value: () => '',
condition: { field: 'operation', value: 'update_incident' },
},
{
id: 'updateTitle',
title: 'New Title',
type: 'short-input',
placeholder: 'New incident title (optional)',
condition: { field: 'operation', value: 'update_incident' },
mode: 'advanced',
},
{
id: 'updateUrgency',
title: 'Urgency',
type: 'dropdown',
options: [
{ label: 'No Change', id: '' },
{ label: 'High', id: 'high' },
{ label: 'Low', id: 'low' },
],
value: () => '',
condition: { field: 'operation', value: 'update_incident' },
mode: 'advanced',
},
{
id: 'updateEscalationLevel',
title: 'Escalation Level',
type: 'short-input',
placeholder: 'Escalation level number (e.g., 2)',
condition: { field: 'operation', value: 'update_incident' },
mode: 'advanced',
},
// --- Add Note fields ---
{
id: 'noteIncidentId',
title: 'Incident ID',
type: 'short-input',
required: { field: 'operation', value: 'add_note' },
placeholder: 'ID of the incident',
condition: { field: 'operation', value: 'add_note' },
},
{
id: 'noteContent',
title: 'Note Content',
type: 'long-input',
required: { field: 'operation', value: 'add_note' },
placeholder: 'Note text to add to the incident',
condition: { field: 'operation', value: 'add_note' },
},
// --- List Services fields ---
{
id: 'serviceQuery',
title: 'Search Query',
type: 'short-input',
placeholder: 'Filter services by name',
condition: { field: 'operation', value: 'list_services' },
},
{
id: 'serviceLimit',
title: 'Limit',
type: 'short-input',
placeholder: '25',
condition: { field: 'operation', value: 'list_services' },
mode: 'advanced',
},
// --- List On-Calls fields ---
{
id: 'oncallEscalationPolicyIds',
title: 'Escalation Policy IDs',
type: 'short-input',
placeholder: 'Comma-separated escalation policy IDs',
condition: { field: 'operation', value: 'list_oncalls' },
},
{
id: 'oncallScheduleIds',
title: 'Schedule IDs',
type: 'short-input',
placeholder: 'Comma-separated schedule IDs',
condition: { field: 'operation', value: 'list_oncalls' },
mode: 'advanced',
},
{
id: 'oncallLimit',
title: 'Limit',
type: 'short-input',
placeholder: '25',
condition: { field: 'operation', value: 'list_oncalls' },
mode: 'advanced',
},
{
id: 'oncallSince',
title: 'Since',
type: 'short-input',
placeholder: 'Start time (ISO 8601)',
condition: { field: 'operation', value: 'list_oncalls' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate an ISO 8601 timestamp. Return ONLY the timestamp string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
{
id: 'oncallUntil',
title: 'Until',
type: 'short-input',
placeholder: 'End time (ISO 8601)',
condition: { field: 'operation', value: 'list_oncalls' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate an ISO 8601 timestamp. Return ONLY the timestamp string - no explanations, no extra text.',
generationType: 'timestamp',
},
},
],
tools: {
access: [
'pagerduty_list_incidents',
'pagerduty_create_incident',
'pagerduty_update_incident',
'pagerduty_add_note',
'pagerduty_list_services',
'pagerduty_list_oncalls',
],
config: {
tool: (params) => `pagerduty_${params.operation}`,
params: (params) => {
const result: Record<string, unknown> = {}
switch (params.operation) {
case 'list_incidents':
if (params.statuses) result.statuses = params.statuses
if (params.listServiceIds) result.serviceIds = params.listServiceIds
if (params.listSince) result.since = params.listSince
if (params.listUntil) result.until = params.listUntil
if (params.listSortBy) result.sortBy = params.listSortBy
if (params.listLimit) result.limit = params.listLimit
break
case 'create_incident':
if (params.createServiceId) result.serviceId = params.createServiceId
if (params.createUrgency) result.urgency = params.createUrgency
break
case 'update_incident':
if (params.updateIncidentId) result.incidentId = params.updateIncidentId
if (params.updateStatus) result.status = params.updateStatus
if (params.updateTitle) result.title = params.updateTitle
if (params.updateUrgency) result.urgency = params.updateUrgency
if (params.updateEscalationLevel) result.escalationLevel = params.updateEscalationLevel
break
case 'add_note':
if (params.noteIncidentId) result.incidentId = params.noteIncidentId
if (params.noteContent) result.content = params.noteContent
break
case 'list_services':
if (params.serviceQuery) result.query = params.serviceQuery
if (params.serviceLimit) result.limit = params.serviceLimit
break
case 'list_oncalls':
if (params.oncallEscalationPolicyIds)
result.escalationPolicyIds = params.oncallEscalationPolicyIds
if (params.oncallScheduleIds) result.scheduleIds = params.oncallScheduleIds
if (params.oncallSince) result.since = params.oncallSince
if (params.oncallUntil) result.until = params.oncallUntil
if (params.oncallLimit) result.limit = params.oncallLimit
break
}
return result
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'PagerDuty REST API Key' },
fromEmail: { type: 'string', description: 'Valid PagerDuty user email' },
statuses: { type: 'string', description: 'Status filter for incidents' },
listServiceIds: { type: 'string', description: 'Service IDs filter' },
listSince: { type: 'string', description: 'Start date filter' },
listUntil: { type: 'string', description: 'End date filter' },
title: { type: 'string', description: 'Incident title' },
createServiceId: { type: 'string', description: 'Service ID for new incident' },
createUrgency: { type: 'string', description: 'Urgency level' },
body: { type: 'string', description: 'Incident description' },
updateIncidentId: { type: 'string', description: 'Incident ID to update' },
updateStatus: { type: 'string', description: 'New status' },
noteIncidentId: { type: 'string', description: 'Incident ID for note' },
noteContent: { type: 'string', description: 'Note content' },
escalationPolicyId: { type: 'string', description: 'Escalation policy ID' },
assigneeId: { type: 'string', description: 'Assignee user ID' },
updateTitle: { type: 'string', description: 'New incident title' },
updateUrgency: { type: 'string', description: 'New urgency level' },
updateEscalationLevel: { type: 'string', description: 'Escalation level number' },
listSortBy: { type: 'string', description: 'Sort field' },
listLimit: { type: 'string', description: 'Max results for incidents' },
serviceQuery: { type: 'string', description: 'Service name filter' },
serviceLimit: { type: 'string', description: 'Max results for services' },
oncallEscalationPolicyIds: { type: 'string', description: 'Escalation policy IDs filter' },
oncallScheduleIds: { type: 'string', description: 'Schedule IDs filter' },
oncallSince: { type: 'string', description: 'On-call start time filter' },
oncallUntil: { type: 'string', description: 'On-call end time filter' },
oncallLimit: { type: 'string', description: 'Max results for on-calls' },
},
outputs: {
incidents: {
type: 'json',
description: 'Array of incidents (list_incidents)',
},
total: {
type: 'number',
description: 'Total count of results',
},
more: {
type: 'boolean',
description: 'Whether more results are available',
},
id: {
type: 'string',
description: 'Created/updated resource ID',
},
incidentNumber: {
type: 'number',
description: 'Incident number',
},
title: {
type: 'string',
description: 'Incident title',
},
status: {
type: 'string',
description: 'Incident status',
},
urgency: {
type: 'string',
description: 'Incident urgency',
},
createdAt: {
type: 'string',
description: 'Creation timestamp',
},
updatedAt: {
type: 'string',
description: 'Last updated timestamp',
},
serviceName: {
type: 'string',
description: 'Service name',
},
serviceId: {
type: 'string',
description: 'Service ID',
},
htmlUrl: {
type: 'string',
description: 'PagerDuty web URL',
},
content: {
type: 'string',
description: 'Note content (add_note)',
},
userName: {
type: 'string',
description: 'User name (add_note)',
},
services: {
type: 'json',
description: 'Array of services (list_services)',
},
oncalls: {
type: 'json',
description: 'Array of on-call entries (list_oncalls)',
},
},
}

View File

@@ -4,6 +4,7 @@ import { AhrefsBlock } from '@/blocks/blocks/ahrefs'
import { AirtableBlock } from '@/blocks/blocks/airtable'
import { AirweaveBlock } from '@/blocks/blocks/airweave'
import { AlgoliaBlock } from '@/blocks/blocks/algolia'
import { AmplitudeBlock } from '@/blocks/blocks/amplitude'
import { ApiBlock } from '@/blocks/blocks/api'
import { ApiTriggerBlock } from '@/blocks/blocks/api_trigger'
import { ApifyBlock } from '@/blocks/blocks/apify'
@@ -56,6 +57,7 @@ import { GoogleDriveBlock } from '@/blocks/blocks/google_drive'
import { GoogleFormsBlock } from '@/blocks/blocks/google_forms'
import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups'
import { GoogleMapsBlock } from '@/blocks/blocks/google_maps'
import { GooglePagespeedBlock } from '@/blocks/blocks/google_pagespeed'
import { GoogleSheetsBlock, GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets'
import { GoogleSlidesBlock, GoogleSlidesV2Block } from '@/blocks/blocks/google_slides'
import { GoogleTasksBlock } from '@/blocks/blocks/google_tasks'
@@ -112,6 +114,7 @@ import { OneDriveBlock } from '@/blocks/blocks/onedrive'
import { OnePasswordBlock } from '@/blocks/blocks/onepassword'
import { OpenAIBlock } from '@/blocks/blocks/openai'
import { OutlookBlock } from '@/blocks/blocks/outlook'
import { PagerDutyBlock } from '@/blocks/blocks/pagerduty'
import { ParallelBlock } from '@/blocks/blocks/parallel'
import { PerplexityBlock } from '@/blocks/blocks/perplexity'
import { PineconeBlock } from '@/blocks/blocks/pinecone'
@@ -193,6 +196,7 @@ export const registry: Record<string, BlockConfig> = {
airtable: AirtableBlock,
airweave: AirweaveBlock,
algolia: AlgoliaBlock,
amplitude: AmplitudeBlock,
api: ApiBlock,
api_trigger: ApiTriggerBlock,
apify: ApifyBlock,
@@ -250,6 +254,7 @@ export const registry: Record<string, BlockConfig> = {
google_forms: GoogleFormsBlock,
google_groups: GoogleGroupsBlock,
google_maps: GoogleMapsBlock,
google_pagespeed: GooglePagespeedBlock,
google_tasks: GoogleTasksBlock,
google_translate: GoogleTranslateBlock,
gong: GongBlock,
@@ -313,6 +318,7 @@ export const registry: Record<string, BlockConfig> = {
onedrive: OneDriveBlock,
openai: OpenAIBlock,
outlook: OutlookBlock,
pagerduty: PagerDutyBlock,
parallel_ai: ParallelBlock,
perplexity: PerplexityBlock,
pinecone: PineconeBlock,

View File

@@ -1209,6 +1209,17 @@ export function AlgoliaIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function AmplitudeIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 49 49'>
<path
fill='#FFFFFF'
d='M23.4,15.3c0.6,1.8,1.2,4.1,1.9,6.7c-2.6,0-5.3-0.1-7.8-0.1h-1.3c1.5-5.7,3.2-10.1,4.6-11.1 c0.1-0.1,0.2-0.1,0.4-0.1c0.2,0,0.3,0.1,0.5,0.3C21.9,11.5,22.5,12.7,23.4,15.3z M49,24.5C49,38,38,49,24.5,49S0,38,0,24.5 S11,0,24.5,0S49,11,49,24.5z M42.7,23.9c0-0.6-0.4-1.2-1-1.3l0,0l0,0l0,0c-0.1,0-0.1,0-0.2,0h-0.2c-4.1-0.3-8.4-0.4-12.4-0.5l0,0 C27,14.8,24.5,7.4,21.3,7.4c-3,0-5.8,4.9-8.2,14.5c-1.7,0-3.2,0-4.6-0.1c-0.1,0-0.2,0-0.2,0c-0.3,0-0.5,0-0.5,0 c-0.8,0.1-1.4,0.9-1.4,1.7c0,0.8,0.6,1.6,1.5,1.7l0,0h4.6c-0.4,1.9-0.8,3.8-1.1,5.6l-0.1,0.8l0,0c0,0.6,0.5,1.1,1.1,1.1 c0.4,0,0.8-0.2,1-0.5l0,0l2.2-7.1h10.7c0.8,3.1,1.7,6.3,2.8,9.3c0.6,1.6,2,5.4,4.4,5.4l0,0c3.6,0,5-5.8,5.9-9.6 c0.2-0.8,0.4-1.5,0.5-2.1l0.1-0.2l0,0c0-0.1,0-0.2,0-0.3c-0.1-0.2-0.2-0.3-0.4-0.4c-0.3-0.1-0.5,0.1-0.6,0.4l0,0l-0.1,0.2 c-0.3,0.8-0.6,1.6-0.8,2.3v0.1c-1.6,4.4-2.3,6.4-3.7,6.4l0,0l0,0l0,0c-1.8,0-3.5-7.3-4.1-10.1c-0.1-0.5-0.2-0.9-0.3-1.3h11.7 c0.2,0,0.4-0.1,0.6-0.1l0,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0l0,0c0,0,0.1,0,0.1-0.1l0,0C42.5,24.6,42.7,24.3,42.7,23.9z'
/>
</svg>
)
}
export function GoogleBooksIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 478.633 540.068'>
@@ -1938,13 +1949,11 @@ export function ElevenLabsIcon(props: SVGProps<SVGSVGElement>) {
export function LinkupIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'>
<g transform='translate(12, 12) scale(1.3) translate(-12, -12)'>
<path
d='M20.2 14.1c-.4-.3-1.6-.4-2.9-.2.5-1.4 1.3-3.9.1-5-.6-.5-1.5-.7-2.6-.5-.3 0-.6.1-1 .2-1.1-1.6-2.4-2.5-3.8-2.5-1.6 0-3.1 1-4.1 2.9-1.2 2.1-1.9 5.1-1.9 8.8v.03l.4.3c3-.9 7.5-2.3 10.7-2.9 0 .9.1 1.9.1 2.8v.03l.4.3c.1 0 5.4-1.7 5.3-3.3 0-.2-.1-.5-.3-.7zM19.9 14.7c.03.4-1.7 1.4-4 2.3.5-.7 1-1.6 1.3-2.5 1.4-.1 2.4-.1 2.7.2zM16.4 14.6c-.3.7-.7 1.4-1.2 2-.02-.6-.1-1.2-.2-1.8.4-.1.9-.1 1.4-.2zM16.5 9.4c.8.7.9 2.4.1 5.1-.5.1-1 .1-1.5.2-.3-2-.9-3.8-1.7-5.3.3-.1.6-.2.8-.2.9-.1 1.7.05 2.3.2zM9.5 6.8c1.2 0 2.3.7 3.2 2.1-2.8 1.1-5.9 3.4-8.4 7.8.2-5.1 1.9-9.9 5.2-9.9zM4.7 17c3.4-4.9 6.4-6.8 8.4-7.8.7 1.3 1.2 2.9 1.5 4.8-3.2.6-7.3 1.8-9.9 3z'
fill='#000000'
/>
</g>
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 154 107' fill='none'>
<path
d='M150.677 72.7113C146.612 70.2493 137.909 69.542 124.794 70.6076C128.992 57.6776 133.757 35.3911 121.323 25.1527C115.886 20.6743 107.471 19.0437 97.6162 20.5594C94.6758 21.0142 91.5752 21.7445 88.3878 22.732C78.8667 8.28165 66.2954 0 53.8613 0C39.4288 0 26.1304 9.3381 16.4081 26.2872C5.67515 45.014 0 71.9626 0 104.23V104.533L3.60356 106.94L3.88251 106.825C30.5754 95.5628 67.5759 85.0718 100.593 79.4037C101.604 87.644 102.116 95.9945 102.116 104.235V104.52L105.491 107L105.761 106.913C106.255 106.752 155.159 90.8822 153.979 77.5894C153.856 76.2022 153.183 74.2271 150.677 72.7113ZM148.409 78.09C148.715 81.5442 133.236 91.0568 111.838 98.8883C115.968 92.0995 119.818 84.1715 122.777 76.3584C135.659 75.1411 144.531 75.5545 147.792 77.5296C148.377 77.8833 148.409 78.09 148.409 78.09ZM116.668 77.0106C114.084 83.3769 110.951 89.6329 107.54 95.2458C107.334 89.5135 106.913 83.8821 106.296 78.4621C109.922 77.8971 113.407 77.4102 116.668 77.0106ZM117.774 29.4979C125.379 35.7585 125.782 51.3205 118.867 71.1772C114.747 71.6319 110.284 72.2382 105.596 72.9777C103.049 55.1742 98.2839 39.966 91.4243 27.7525C94.566 26.8155 96.9669 26.3469 98.4622 26.1127C106.721 24.8404 113.581 26.0438 117.774 29.4979ZM53.8567 5.62215C65.0561 5.62215 74.8882 12.0022 83.0922 24.5923C57.7027 34.5413 30.3193 59.4092 5.78032 94.8003C7.43119 51.4813 23.0299 5.62215 53.8613 5.62215M10.1933 98.2406C40.7504 53.9341 68.2024 36.4429 86.0739 29.5852C92.4487 41.2383 97.2046 56.5522 99.8433 73.9331C70.5209 79.0316 35.6377 88.4983 10.1933 98.2406Z'
fill='#000000'
/>
</svg>
)
}
@@ -2453,6 +2462,17 @@ export function OutlookIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function PagerDutyIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64' fill='none'>
<path
d='M6.704 59.217H0v-33.65c0-3.455 1.418-5.544 2.604-6.704 2.63-2.58 6.2-2.656 6.782-2.656h10.546c3.765 0 5.93 1.52 7.117 2.8 2.346 2.553 2.372 5.853 2.32 6.73v12.687c0 3.662-1.496 5.828-2.733 6.988-2.553 2.398-5.93 2.45-6.73 2.424H6.704zm13.46-18.102c.36 0 1.367-.103 1.908-.62.413-.387.62-1.083.62-2.1v-13.02c0-.36-.077-1.315-.593-1.857-.5-.516-1.444-.62-2.166-.62h-10.6c-2.63 0-2.63 1.985-2.63 2.656v15.55zM57.296 4.783H64V38.46c0 3.455-1.418 5.544-2.604 6.704-2.63 2.58-6.2 2.656-6.782 2.656H44.068c-3.765 0-5.93-1.52-7.117-2.8-2.346-2.553-2.372-5.853-2.32-6.73V25.62c0-3.662 1.496-5.828 2.733-6.988 2.553-2.398 5.93-2.45 6.73-2.424h13.202zM43.836 22.9c-.36 0-1.367.103-1.908.62-.413.387-.62 1.083-.62 2.1v13.02c0 .36.077 1.315.593 1.857.5.516 1.444.62 2.166.62h10.598c2.656-.026 2.656-2 2.656-2.682V22.9z'
fill='#06AC38'
/>
</svg>
)
}
export function MicrosoftExcelIcon(props: SVGProps<SVGSVGElement>) {
const id = useId()
const gradientId = `excel_gradient_${id}`
@@ -3996,10 +4016,10 @@ export function IntercomIcon(props: SVGProps<SVGSVGElement>) {
export function LoopsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 256 256' fill='none' xmlns='http://www.w3.org/2000/svg'>
<svg {...props} viewBox='0 0 214 186' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
fill='#FD4E00'
d='M192.352 88.042c0-7.012-5.685-12.697-12.697-12.697s-12.697 5.685-12.697 12.697c0 .634.052 1.255.142 1.866a25.248 25.248 0 0 0-4.9-.49c-14.006 0-25.36 11.354-25.36 25.36 0 1.63.16 3.222.456 4.765a37.8 37.8 0 0 0-9.296-1.173c-20.95 0-37.935 16.985-37.935 37.935S107.05 194.24 128 194.24s37.935-16.985 37.935-37.935a37.7 37.7 0 0 0-3.78-16.555 25.2 25.2 0 0 0 12.487-3.336 25.2 25.2 0 0 0 4.558 3.336v.02c14.006 0 25.36-11.354 25.36-25.36 0-12.48-9.018-22.855-20.888-24.996a12.6 12.6 0 0 0 8.68-11.972m-77.05 68.263c0-7.012 5.685-12.697 12.697-12.697s12.697 5.685 12.697 12.697c0 7.013-5.685 12.697-12.697 12.697s-12.697-5.685-12.697-12.697'
d='M122.19,0 H90.27 C40.51,0 0,39.88 0,92.95 C0,141.07 38.93,183.77 90.27,183.77 H122.19 C172.61,183.77 213.31,142.82 213.31,92.95 C213.31,43.29 173.09,0 122.19,0 Z M10.82,92.54 C10.82,50.19 45.91,11.49 91.96,11.49 C138.73,11.49 172.69,50.33 172.69,92.13 C172.69,117.76 154.06,139.09 129.02,143.31 C145.16,131.15 155.48,112.73 155.48,92.4 C155.48,59.09 127.44,28.82 92.37,28.82 C57.23,28.82 28.51,57.23 28.51,92.91 C28.51,122.63 43.61,151.08 69.99,168.21 L71.74,169.33 C35.99,161.39 10.82,130.11 10.82,92.54 Z M106.33,42.76 C128.88,50.19 143.91,68.92 143.91,92.26 C143.91,114.23 128.68,134.63 106.12,141.71 C105.44,141.96 105.17,141.96 105.17,141.96 C83.91,135.76 69.29,116.38 69.29,92.71 C69.29,69.91 83.71,50.33 106.33,42.76 Z M120.91,172.13 C76.11,172.13 40.09,137.21 40.09,93.32 C40.09,67.03 57.17,46.11 83.98,41.33 C67.04,53.83 57.3,71.71 57.3,92.71 C57.3,125.75 82.94,155.33 120.77,155.33 C155.01,155.33 184.31,125.2 184.31,92.47 C184.31,62.34 169.96,34.06 141.92,14.55 L141.65,14.34 C175.81,23.68 202.26,54.11 202.26,92.81 C202.26,135.69 166.38,172.13 120.91,172.13 Z'
fill='#FB5001'
/>
</svg>
)
@@ -5578,6 +5598,35 @@ export function GoogleMapsIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function GooglePagespeedIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='-1.74 -1.81 285.55 266.85' xmlns='http://www.w3.org/2000/svg'>
<path
d='M272.73 37.23v179.68a18.58 18.58 0 0 1-18.57 18.59H18.65A18.58 18.58 0 0 1 .06 216.94V37.23z'
fill='#e1e1e1'
/>
<path
d='M18.65 0h235.5a18.58 18.58 0 0 1 18.58 18.56v18.67H.07V18.59A18.58 18.58 0 0 1 18.64 0z'
fill='#c2c2c2'
/>
<path
d='M136.3 92.96a99 99 0 0 0-99 99v.13c0 2.08-.12 4.64 0 6.2h43.25a54.87 54.87 0 0 1 0-6.2 55.81 55.81 0 0 1 85.06-47.45l31.12-31.12a98.76 98.76 0 0 0-60.44-20.57z'
fill='#4285f4'
/>
<path
d='M196.73 113.46l-31.14 31.14a55.74 55.74 0 0 1 26.56 47.45 54.87 54.87 0 0 1 0 6.2h43.39c.12-1.48 0-4.12 0-6.2a99 99 0 0 0-38.81-78.59z'
fill='#f44336'
/>
<circle cx='24.85' cy='18.59' fill='#eee' r='6.2' />
<circle cx='49.65' cy='18.59' fill='#eee' r='6.2' />
<path
d='M197.01 117.23a3.05 3.05 0 0 0 .59-1.81 3.11 3.11 0 0 0-3.1-3.1 3 3 0 0 0-1.91.68l-67.56 52a18.58 18.58 0 1 0 27.24 24.33l44.73-72.1z'
fill='#9e9e9e'
/>
</svg>
)
}
export function GoogleTranslateIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 998.1 998.3'>

View File

@@ -0,0 +1,134 @@
import type {
AmplitudeEventSegmentationParams,
AmplitudeEventSegmentationResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const eventSegmentationTool: ToolConfig<
AmplitudeEventSegmentationParams,
AmplitudeEventSegmentationResponse
> = {
id: 'amplitude_event_segmentation',
name: 'Amplitude Event Segmentation',
description:
'Query event analytics data with segmentation. Get event counts, uniques, averages, and more.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
eventType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Event type name to analyze',
},
start: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date in YYYYMMDD format',
},
end: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date in YYYYMMDD format',
},
metric: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Metric type: uniques, totals, pct_dau, average, histogram, sums, value_avg, or formula (default: uniques)',
},
interval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Time interval: 1 (daily), 7 (weekly), or 30 (monthly)',
},
groupBy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Property name to group by (prefix custom user properties with "gp:")',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of group-by values (max 1000)',
},
},
request: {
url: (params) => {
const url = new URL('https://amplitude.com/api/2/events/segmentation')
const eventObj = JSON.stringify({ event_type: params.eventType })
url.searchParams.set('e', eventObj)
url.searchParams.set('start', params.start)
url.searchParams.set('end', params.end)
if (params.metric) url.searchParams.set('m', params.metric)
if (params.interval) url.searchParams.set('i', params.interval)
if (params.groupBy) url.searchParams.set('g', params.groupBy)
if (params.limit) url.searchParams.set('limit', params.limit)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:${params.secretKey}`)}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude Event Segmentation API error: ${response.status}`)
}
const result = data.data ?? {}
return {
success: true,
output: {
series: result.series ?? [],
seriesLabels: result.seriesLabels ?? [],
seriesCollapsed: result.seriesCollapsed ?? [],
xValues: result.xValues ?? [],
},
}
},
outputs: {
series: {
type: 'json',
description: 'Time-series data arrays indexed by series',
},
seriesLabels: {
type: 'array',
description: 'Labels for each data series',
items: { type: 'string' },
},
seriesCollapsed: {
type: 'json',
description: 'Collapsed aggregate totals per series',
},
xValues: {
type: 'array',
description: 'Date values for the x-axis',
items: { type: 'string' },
},
},
}

View File

@@ -0,0 +1,105 @@
import type {
AmplitudeGetActiveUsersParams,
AmplitudeGetActiveUsersResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const getActiveUsersTool: ToolConfig<
AmplitudeGetActiveUsersParams,
AmplitudeGetActiveUsersResponse
> = {
id: 'amplitude_get_active_users',
name: 'Amplitude Get Active Users',
description: 'Get active or new user counts over a date range from the Dashboard REST API.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
start: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date in YYYYMMDD format',
},
end: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date in YYYYMMDD format',
},
metric: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Metric type: "active" or "new" (default: active)',
},
interval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Time interval: 1 (daily), 7 (weekly), or 30 (monthly)',
},
},
request: {
url: (params) => {
const url = new URL('https://amplitude.com/api/2/users')
url.searchParams.set('start', params.start)
url.searchParams.set('end', params.end)
if (params.metric) url.searchParams.set('m', params.metric)
if (params.interval) url.searchParams.set('i', params.interval)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:${params.secretKey}`)}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude Active Users API error: ${response.status}`)
}
const result = data.data ?? {}
return {
success: true,
output: {
series: result.series ?? [],
seriesMeta: result.seriesMeta ?? [],
xValues: result.xValues ?? [],
},
}
},
outputs: {
series: {
type: 'json',
description: 'Array of data series with user counts per time interval',
},
seriesMeta: {
type: 'array',
description: 'Metadata labels for each data series (e.g., segment names)',
items: { type: 'string' },
},
xValues: {
type: 'array',
description: 'Date values for the x-axis',
items: { type: 'string' },
},
},
}

View File

@@ -0,0 +1,102 @@
import type {
AmplitudeGetRevenueParams,
AmplitudeGetRevenueResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const getRevenueTool: ToolConfig<AmplitudeGetRevenueParams, AmplitudeGetRevenueResponse> = {
id: 'amplitude_get_revenue',
name: 'Amplitude Get Revenue',
description: 'Get revenue LTV data including ARPU, ARPPU, total revenue, and paying user counts.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
start: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date in YYYYMMDD format',
},
end: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date in YYYYMMDD format',
},
metric: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Metric: 0 (ARPU), 1 (ARPPU), 2 (Total Revenue), 3 (Paying Users)',
},
interval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Time interval: 1 (daily), 7 (weekly), or 30 (monthly)',
},
},
request: {
url: (params) => {
const url = new URL('https://amplitude.com/api/2/revenue/ltv')
url.searchParams.set('start', params.start)
url.searchParams.set('end', params.end)
if (params.metric) url.searchParams.set('m', params.metric)
if (params.interval) url.searchParams.set('i', params.interval)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:${params.secretKey}`)}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude Revenue API error: ${response.status}`)
}
const result = data.data ?? {}
return {
success: true,
output: {
series: result.series ?? [],
seriesLabels: result.seriesLabels ?? [],
xValues: result.xValues ?? [],
},
}
},
outputs: {
series: {
type: 'json',
description: 'Array of revenue data series',
},
seriesLabels: {
type: 'array',
description: 'Labels for each data series',
items: { type: 'string' },
},
xValues: {
type: 'array',
description: 'Date values for the x-axis',
items: { type: 'string' },
},
},
}

View File

@@ -0,0 +1,99 @@
import type {
AmplitudeGroupIdentifyParams,
AmplitudeGroupIdentifyResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const groupIdentifyTool: ToolConfig<
AmplitudeGroupIdentifyParams,
AmplitudeGroupIdentifyResponse
> = {
id: 'amplitude_group_identify',
name: 'Amplitude Group Identify',
description:
'Set group-level properties in Amplitude. Supports $set, $setOnce, $add, $append, $unset operations.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
groupType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group classification (e.g., "company", "org_id")',
},
groupValue: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Specific group identifier (e.g., "Acme Corp")',
},
groupProperties: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'JSON object of group properties. Use operations like $set, $setOnce, $add, $append, $unset.',
},
},
request: {
url: 'https://api2.amplitude.com/groupidentify',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => {
let groupProperties: Record<string, unknown> = {}
try {
groupProperties = JSON.parse(params.groupProperties)
} catch {
groupProperties = {}
}
return {
api_key: params.apiKey,
identification: [
{
group_type: params.groupType,
group_value: params.groupValue,
group_properties: groupProperties,
},
],
}
},
},
transformResponse: async (response: Response) => {
const text = await response.text()
if (!response.ok) {
throw new Error(`Amplitude Group Identify API error: ${text}`)
}
return {
success: true,
output: {
code: response.status,
message: text || null,
},
}
},
outputs: {
code: {
type: 'number',
description: 'HTTP response status code',
},
message: {
type: 'string',
description: 'Response message',
optional: true,
},
},
}

View File

@@ -0,0 +1,97 @@
import type {
AmplitudeIdentifyUserParams,
AmplitudeIdentifyUserResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const identifyUserTool: ToolConfig<
AmplitudeIdentifyUserParams,
AmplitudeIdentifyUserResponse
> = {
id: 'amplitude_identify_user',
name: 'Amplitude Identify User',
description:
'Set user properties in Amplitude using the Identify API. Supports $set, $setOnce, $add, $append, $unset operations.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
userId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'User ID (required if no device_id)',
},
deviceId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Device ID (required if no user_id)',
},
userProperties: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'JSON object of user properties. Use operations like $set, $setOnce, $add, $append, $unset.',
},
},
request: {
url: 'https://api2.amplitude.com/identify',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => {
const identification: Record<string, unknown> = {}
if (params.userId) identification.user_id = params.userId
if (params.deviceId) identification.device_id = params.deviceId
try {
identification.user_properties = JSON.parse(params.userProperties)
} catch {
identification.user_properties = {}
}
return {
api_key: params.apiKey,
identification: [identification],
}
},
},
transformResponse: async (response: Response) => {
const text = await response.text()
if (!response.ok) {
throw new Error(`Amplitude Identify API error: ${text}`)
}
return {
success: true,
output: {
code: response.status,
message: text || null,
},
}
},
outputs: {
code: {
type: 'number',
description: 'HTTP response status code',
},
message: {
type: 'string',
description: 'Response message',
optional: true,
},
},
}

View File

@@ -0,0 +1,23 @@
import { eventSegmentationTool } from '@/tools/amplitude/event_segmentation'
import { getActiveUsersTool } from '@/tools/amplitude/get_active_users'
import { getRevenueTool } from '@/tools/amplitude/get_revenue'
import { groupIdentifyTool } from '@/tools/amplitude/group_identify'
import { identifyUserTool } from '@/tools/amplitude/identify_user'
import { listEventsTool } from '@/tools/amplitude/list_events'
import { realtimeActiveUsersTool } from '@/tools/amplitude/realtime_active_users'
import { sendEventTool } from '@/tools/amplitude/send_event'
import { userActivityTool } from '@/tools/amplitude/user_activity'
import { userProfileTool } from '@/tools/amplitude/user_profile'
import { userSearchTool } from '@/tools/amplitude/user_search'
export const amplitudeSendEventTool = sendEventTool
export const amplitudeIdentifyUserTool = identifyUserTool
export const amplitudeGroupIdentifyTool = groupIdentifyTool
export const amplitudeUserSearchTool = userSearchTool
export const amplitudeUserActivityTool = userActivityTool
export const amplitudeUserProfileTool = userProfileTool
export const amplitudeEventSegmentationTool = eventSegmentationTool
export const amplitudeGetActiveUsersTool = getActiveUsersTool
export const amplitudeRealtimeActiveUsersTool = realtimeActiveUsersTool
export const amplitudeListEventsTool = listEventsTool
export const amplitudeGetRevenueTool = getRevenueTool

View File

@@ -0,0 +1,79 @@
import type {
AmplitudeListEventsParams,
AmplitudeListEventsResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const listEventsTool: ToolConfig<AmplitudeListEventsParams, AmplitudeListEventsResponse> = {
id: 'amplitude_list_events',
name: 'Amplitude List Events',
description:
'List all event types in the Amplitude project with their weekly totals and unique counts.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
},
request: {
url: 'https://amplitude.com/api/2/events/list',
method: 'GET',
headers: (params) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:${params.secretKey}`)}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude List Events API error: ${response.status}`)
}
const events = (data.data ?? []).map(
(e: Record<string, unknown>) =>
({
value: (e.value as string) ?? '',
displayName: (e.display as string) ?? null,
totals: (e.totals as number) ?? 0,
hidden: (e.hidden as boolean) ?? false,
deleted: (e.deleted as boolean) ?? false,
}) as const
)
return {
success: true,
output: {
events,
},
}
},
outputs: {
events: {
type: 'array',
description: 'List of event types in the project',
items: {
type: 'object',
properties: {
value: { type: 'string', description: 'Event type name' },
displayName: { type: 'string', description: 'Event display name' },
totals: { type: 'number', description: 'Weekly total count' },
hidden: { type: 'boolean', description: 'Whether the event is hidden' },
deleted: { type: 'boolean', description: 'Whether the event is deleted' },
},
},
},
},
}

View File

@@ -0,0 +1,74 @@
import type {
AmplitudeRealtimeActiveUsersParams,
AmplitudeRealtimeActiveUsersResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const realtimeActiveUsersTool: ToolConfig<
AmplitudeRealtimeActiveUsersParams,
AmplitudeRealtimeActiveUsersResponse
> = {
id: 'amplitude_realtime_active_users',
name: 'Amplitude Real-time Active Users',
description: 'Get real-time active user counts at 5-minute granularity for the last 2 days.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
},
request: {
url: 'https://amplitude.com/api/2/realtime',
method: 'GET',
headers: (params) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:${params.secretKey}`)}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude Real-time API error: ${response.status}`)
}
const result = data.data ?? {}
return {
success: true,
output: {
series: result.series ?? [],
seriesLabels: result.seriesLabels ?? [],
xValues: result.xValues ?? [],
},
}
},
outputs: {
series: {
type: 'json',
description: 'Array of data series with active user counts at 5-minute intervals',
},
seriesLabels: {
type: 'array',
description: 'Labels for each series (e.g., "Today", "Yesterday")',
items: { type: 'string' },
},
xValues: {
type: 'array',
description: 'Time values for the x-axis (e.g., "15:00", "15:05")',
items: { type: 'string' },
},
},
}

View File

@@ -0,0 +1,214 @@
import type { AmplitudeSendEventParams, AmplitudeSendEventResponse } from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const sendEventTool: ToolConfig<AmplitudeSendEventParams, AmplitudeSendEventResponse> = {
id: 'amplitude_send_event',
name: 'Amplitude Send Event',
description: 'Track an event in Amplitude using the HTTP V2 API.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
userId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'User ID (required if no device_id)',
},
deviceId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Device ID (required if no user_id)',
},
eventType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the event (e.g., "page_view", "purchase")',
},
eventProperties: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON object of custom event properties',
},
userProperties: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON object of user properties to set (supports $set, $setOnce, $add, $append, $unset)',
},
time: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Event timestamp in milliseconds since epoch',
},
sessionId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Session start time in milliseconds since epoch',
},
insertId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Unique ID for deduplication (within 7-day window)',
},
appVersion: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Application version string',
},
platform: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Platform (e.g., "Web", "iOS", "Android")',
},
country: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Two-letter country code',
},
language: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Language code (e.g., "en")',
},
ip: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'IP address for geo-location',
},
price: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Price of the item purchased',
},
quantity: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Quantity of items purchased',
},
revenue: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Revenue amount',
},
productId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Product identifier',
},
revenueType: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Revenue type (e.g., "purchase", "refund")',
},
},
request: {
url: 'https://api2.amplitude.com/2/httpapi',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => {
const event: Record<string, unknown> = {
event_type: params.eventType,
}
if (params.userId) event.user_id = params.userId
if (params.deviceId) event.device_id = params.deviceId
if (params.time) event.time = Number(params.time)
if (params.sessionId) event.session_id = Number(params.sessionId)
if (params.insertId) event.insert_id = params.insertId
if (params.appVersion) event.app_version = params.appVersion
if (params.platform) event.platform = params.platform
if (params.country) event.country = params.country
if (params.language) event.language = params.language
if (params.ip) event.ip = params.ip
if (params.price) event.price = Number(params.price)
if (params.quantity) event.quantity = Number(params.quantity)
if (params.revenue) event.revenue = Number(params.revenue)
if (params.productId) event.product_id = params.productId
if (params.revenueType) event.revenue_type = params.revenueType
if (params.eventProperties) {
try {
event.event_properties = JSON.parse(params.eventProperties)
} catch {
event.event_properties = {}
}
}
if (params.userProperties) {
try {
event.user_properties = JSON.parse(params.userProperties)
} catch {
event.user_properties = {}
}
}
return {
api_key: params.apiKey,
events: [event],
}
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (data.code !== 200) {
throw new Error(data.error || `Amplitude API error: code ${data.code}`)
}
return {
success: true,
output: {
code: data.code ?? 200,
eventsIngested: data.events_ingested ?? 0,
payloadSizeBytes: data.payload_size_bytes ?? 0,
serverUploadTime: data.server_upload_time ?? 0,
},
}
},
outputs: {
code: {
type: 'number',
description: 'Response code (200 for success)',
},
eventsIngested: {
type: 'number',
description: 'Number of events ingested',
},
payloadSizeBytes: {
type: 'number',
description: 'Size of the payload in bytes',
},
serverUploadTime: {
type: 'number',
description: 'Server upload timestamp',
},
},
}

View File

@@ -0,0 +1,241 @@
import type { ToolResponse } from '@/tools/types'
/**
* Base params shared by endpoints using API key in body.
*/
export interface AmplitudeApiKeyParams {
apiKey: string
}
/**
* Base params shared by endpoints using Basic Auth (api_key:secret_key).
*/
export interface AmplitudeBasicAuthParams {
apiKey: string
secretKey: string
}
/**
* Send Event params (HTTP V2 API).
*/
export interface AmplitudeSendEventParams extends AmplitudeApiKeyParams {
userId?: string
deviceId?: string
eventType: string
eventProperties?: string
userProperties?: string
time?: string
sessionId?: string
insertId?: string
appVersion?: string
platform?: string
country?: string
language?: string
ip?: string
price?: string
quantity?: string
revenue?: string
productId?: string
revenueType?: string
}
export interface AmplitudeSendEventResponse extends ToolResponse {
output: {
code: number
eventsIngested: number
payloadSizeBytes: number
serverUploadTime: number
}
}
/**
* Identify User params (Identify API).
*/
export interface AmplitudeIdentifyUserParams extends AmplitudeApiKeyParams {
userId?: string
deviceId?: string
userProperties: string
}
export interface AmplitudeIdentifyUserResponse extends ToolResponse {
output: {
code: number
message: string | null
}
}
/**
* Group Identify params (Group Identify API).
*/
export interface AmplitudeGroupIdentifyParams extends AmplitudeApiKeyParams {
groupType: string
groupValue: string
groupProperties: string
}
export interface AmplitudeGroupIdentifyResponse extends ToolResponse {
output: {
code: number
message: string | null
}
}
/**
* User Search params (Dashboard REST API).
*/
export interface AmplitudeUserSearchParams extends AmplitudeBasicAuthParams {
user: string
}
export interface AmplitudeUserSearchResponse extends ToolResponse {
output: {
matches: Array<{
amplitudeId: number
userId: string | null
}>
type: string | null
}
}
/**
* User Activity params (Dashboard REST API).
*/
export interface AmplitudeUserActivityParams extends AmplitudeBasicAuthParams {
amplitudeId: string
offset?: string
limit?: string
direction?: string
}
export interface AmplitudeUserActivityResponse extends ToolResponse {
output: {
events: Array<{
eventType: string
eventTime: string
eventProperties: Record<string, unknown>
userProperties: Record<string, unknown>
sessionId: number | null
platform: string | null
country: string | null
city: string | null
}>
userData: {
userId: string | null
canonicalAmplitudeId: number | null
numEvents: number | null
numSessions: number | null
platform: string | null
country: string | null
} | null
}
}
/**
* User Profile params (User Profile API).
*/
export interface AmplitudeUserProfileParams {
secretKey: string
userId?: string
deviceId?: string
getAmpProps?: string
getCohortIds?: string
getComputations?: string
}
export interface AmplitudeUserProfileResponse extends ToolResponse {
output: {
userId: string | null
deviceId: string | null
ampProps: Record<string, unknown> | null
cohortIds: string[] | null
computations: Record<string, unknown> | null
}
}
/**
* Event Segmentation params (Dashboard REST API).
*/
export interface AmplitudeEventSegmentationParams extends AmplitudeBasicAuthParams {
eventType: string
start: string
end: string
metric?: string
interval?: string
groupBy?: string
limit?: string
}
export interface AmplitudeEventSegmentationResponse extends ToolResponse {
output: {
series: unknown[]
seriesLabels: string[]
seriesCollapsed: unknown[]
xValues: string[]
}
}
/**
* Get Active Users params (Dashboard REST API).
*/
export interface AmplitudeGetActiveUsersParams extends AmplitudeBasicAuthParams {
start: string
end: string
metric?: string
interval?: string
}
export interface AmplitudeGetActiveUsersResponse extends ToolResponse {
output: {
series: number[][]
seriesMeta: string[]
xValues: string[]
}
}
/**
* Real-time Active Users params (Dashboard REST API).
*/
export interface AmplitudeRealtimeActiveUsersParams extends AmplitudeBasicAuthParams {}
export interface AmplitudeRealtimeActiveUsersResponse extends ToolResponse {
output: {
series: number[][]
seriesLabels: string[]
xValues: string[]
}
}
/**
* List Events params (Dashboard REST API).
*/
export interface AmplitudeListEventsParams extends AmplitudeBasicAuthParams {}
export interface AmplitudeListEventsResponse extends ToolResponse {
output: {
events: Array<{
value: string
displayName: string | null
totals: number
hidden: boolean
deleted: boolean
}>
}
}
/**
* Get Revenue params (Dashboard REST API).
*/
export interface AmplitudeGetRevenueParams extends AmplitudeBasicAuthParams {
start: string
end: string
metric?: string
interval?: string
}
export interface AmplitudeGetRevenueResponse extends ToolResponse {
output: {
series: unknown[]
seriesLabels: string[]
xValues: string[]
}
}

View File

@@ -0,0 +1,144 @@
import type {
AmplitudeUserActivityParams,
AmplitudeUserActivityResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const userActivityTool: ToolConfig<
AmplitudeUserActivityParams,
AmplitudeUserActivityResponse
> = {
id: 'amplitude_user_activity',
name: 'Amplitude User Activity',
description: 'Get the event stream for a specific user by their Amplitude ID.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
amplitudeId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Amplitude internal user ID',
},
offset: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination (default 0)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of events to return (default 1000, max 1000)',
},
direction: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort direction: "latest" or "earliest" (default: latest)',
},
},
request: {
url: (params) => {
const url = new URL('https://amplitude.com/api/2/useractivity')
url.searchParams.set('user', params.amplitudeId.trim())
if (params.offset) url.searchParams.set('offset', params.offset)
if (params.limit) url.searchParams.set('limit', params.limit)
if (params.direction) url.searchParams.set('direction', params.direction)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:${params.secretKey}`)}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude User Activity API error: ${response.status}`)
}
const events = (data.events ?? []).map(
(e: Record<string, unknown>) =>
({
eventType: (e.event_type as string) ?? '',
eventTime: (e.event_time as string) ?? '',
eventProperties: (e.event_properties as Record<string, unknown>) ?? {},
userProperties: (e.user_properties as Record<string, unknown>) ?? {},
sessionId: (e.session_id as number) ?? null,
platform: (e.platform as string) ?? null,
country: (e.country as string) ?? null,
city: (e.city as string) ?? null,
}) as const
)
const ud = data.userData as Record<string, unknown> | undefined
const userData = ud
? {
userId: (ud.user_id as string) ?? null,
canonicalAmplitudeId: (ud.canonical_amplitude_id as number) ?? null,
numEvents: (ud.num_events as number) ?? null,
numSessions: (ud.num_sessions as number) ?? null,
platform: (ud.platform as string) ?? null,
country: (ud.country as string) ?? null,
}
: null
return {
success: true,
output: {
events,
userData,
},
}
},
outputs: {
events: {
type: 'array',
description: 'List of user events',
items: {
type: 'object',
properties: {
eventType: { type: 'string', description: 'Type of event' },
eventTime: { type: 'string', description: 'Event timestamp' },
eventProperties: { type: 'json', description: 'Custom event properties' },
userProperties: { type: 'json', description: 'User properties at event time' },
sessionId: { type: 'number', description: 'Session ID' },
platform: { type: 'string', description: 'Platform' },
country: { type: 'string', description: 'Country' },
city: { type: 'string', description: 'City' },
},
},
},
userData: {
type: 'json',
description: 'User metadata',
optional: true,
properties: {
userId: { type: 'string', description: 'External user ID' },
canonicalAmplitudeId: { type: 'number', description: 'Canonical Amplitude ID' },
numEvents: { type: 'number', description: 'Total event count' },
numSessions: { type: 'number', description: 'Total session count' },
platform: { type: 'string', description: 'Primary platform' },
country: { type: 'string', description: 'Country' },
},
},
},
}

View File

@@ -0,0 +1,120 @@
import type {
AmplitudeUserProfileParams,
AmplitudeUserProfileResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const userProfileTool: ToolConfig<AmplitudeUserProfileParams, AmplitudeUserProfileResponse> =
{
id: 'amplitude_user_profile',
name: 'Amplitude User Profile',
description:
'Get a user profile including properties, cohort memberships, and computed properties.',
version: '1.0.0',
params: {
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
userId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'External user ID (required if no device_id)',
},
deviceId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Device ID (required if no user_id)',
},
getAmpProps: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include Amplitude user properties (true/false, default: false)',
},
getCohortIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include cohort IDs the user belongs to (true/false, default: false)',
},
getComputations: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include computed user properties (true/false, default: false)',
},
},
request: {
url: (params) => {
const url = new URL('https://profile-api.amplitude.com/v1/userprofile')
if (params.userId) url.searchParams.set('user_id', params.userId.trim())
if (params.deviceId) url.searchParams.set('device_id', params.deviceId.trim())
if (params.getAmpProps) url.searchParams.set('get_amp_props', params.getAmpProps)
if (params.getCohortIds) url.searchParams.set('get_cohort_ids', params.getCohortIds)
if (params.getComputations) url.searchParams.set('get_computations', params.getComputations)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Api-Key ${params.secretKey}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude User Profile API error: ${response.status}`)
}
const userData = data.userData ?? {}
return {
success: true,
output: {
userId: (userData.user_id as string) ?? null,
deviceId: (userData.device_id as string) ?? null,
ampProps: (userData.amp_props as Record<string, unknown>) ?? null,
cohortIds: (userData.cohort_ids as string[]) ?? null,
computations: (userData.computations as Record<string, unknown>) ?? null,
},
}
},
outputs: {
userId: {
type: 'string',
description: 'External user ID',
optional: true,
},
deviceId: {
type: 'string',
description: 'Device ID',
optional: true,
},
ampProps: {
type: 'json',
description:
'Amplitude user properties (library, first_used, last_used, custom properties)',
optional: true,
},
cohortIds: {
type: 'array',
description: 'List of cohort IDs the user belongs to',
optional: true,
items: { type: 'string' },
},
computations: {
type: 'json',
description: 'Computed user properties',
optional: true,
},
},
}

View File

@@ -0,0 +1,89 @@
import type {
AmplitudeUserSearchParams,
AmplitudeUserSearchResponse,
} from '@/tools/amplitude/types'
import type { ToolConfig } from '@/tools/types'
export const userSearchTool: ToolConfig<AmplitudeUserSearchParams, AmplitudeUserSearchResponse> = {
id: 'amplitude_user_search',
name: 'Amplitude User Search',
description:
'Search for a user by User ID, Device ID, or Amplitude ID using the Dashboard REST API.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude API Key',
},
secretKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Amplitude Secret Key',
},
user: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID, Device ID, or Amplitude ID to search for',
},
},
request: {
url: (params) => {
const url = new URL('https://amplitude.com/api/2/usersearch')
url.searchParams.set('user', params.user.trim())
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Basic ${btoa(`${params.apiKey}:${params.secretKey}`)}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || `Amplitude User Search API error: ${response.status}`)
}
const matches = (data.matches ?? []).map(
(m: Record<string, unknown>) =>
({
amplitudeId: (m.amplitude_id as number) ?? 0,
userId: (m.user_id as string) ?? null,
}) as const
)
return {
success: true,
output: {
matches,
type: (data.type as string) ?? null,
},
}
},
outputs: {
matches: {
type: 'array',
description: 'List of matching users',
items: {
type: 'object',
properties: {
amplitudeId: { type: 'number', description: 'Amplitude internal user ID' },
userId: { type: 'string', description: 'External user ID' },
},
},
},
type: {
type: 'string',
description: 'Match type (e.g., match_user_or_device_id)',
optional: true,
},
},
}

View File

@@ -0,0 +1,223 @@
import type {
GooglePagespeedAnalyzeParams,
GooglePagespeedAnalyzeResponse,
} from '@/tools/google_pagespeed/types'
import type { ToolConfig } from '@/tools/types'
export const analyzeTool: ToolConfig<GooglePagespeedAnalyzeParams, GooglePagespeedAnalyzeResponse> =
{
id: 'google_pagespeed_analyze',
name: 'Google PageSpeed Analyze',
description:
'Analyze a webpage for performance, accessibility, SEO, and best practices using Google PageSpeed Insights.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Google PageSpeed Insights API Key',
},
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The URL of the webpage to analyze',
},
category: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Lighthouse categories to analyze (comma-separated): performance, accessibility, best-practices, seo',
},
strategy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Analysis strategy: desktop or mobile',
},
locale: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Locale for results (e.g., en, fr, de)',
},
},
request: {
url: (params) => {
const url = new URL('https://www.googleapis.com/pagespeedonline/v5/runPagespeed')
url.searchParams.append('url', params.url.trim())
url.searchParams.append('key', params.apiKey)
if (params.category) {
const categories = params.category.split(',').map((c) => c.trim())
for (const cat of categories) {
url.searchParams.append('category', cat)
}
} else {
url.searchParams.append('category', 'performance')
url.searchParams.append('category', 'accessibility')
url.searchParams.append('category', 'best-practices')
url.searchParams.append('category', 'seo')
}
if (params.strategy) {
url.searchParams.append('strategy', params.strategy)
}
if (params.locale) {
url.searchParams.append('locale', params.locale)
}
return url.toString()
},
method: 'GET',
headers: () => ({
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message ?? 'Failed to analyze page')
}
const lighthouse = data.lighthouseResult ?? {}
const categories = lighthouse.categories ?? {}
const audits = lighthouse.audits ?? {}
const loadingExperience = data.loadingExperience ?? {}
return {
success: true,
output: {
finalUrl: data.id ?? null,
performanceScore: categories.performance?.score ?? null,
accessibilityScore: categories.accessibility?.score ?? null,
bestPracticesScore: categories['best-practices']?.score ?? null,
seoScore: categories.seo?.score ?? null,
firstContentfulPaint: audits['first-contentful-paint']?.displayValue ?? null,
firstContentfulPaintMs: audits['first-contentful-paint']?.numericValue ?? null,
largestContentfulPaint: audits['largest-contentful-paint']?.displayValue ?? null,
largestContentfulPaintMs: audits['largest-contentful-paint']?.numericValue ?? null,
totalBlockingTime: audits['total-blocking-time']?.displayValue ?? null,
totalBlockingTimeMs: audits['total-blocking-time']?.numericValue ?? null,
cumulativeLayoutShift: audits['cumulative-layout-shift']?.displayValue ?? null,
cumulativeLayoutShiftValue: audits['cumulative-layout-shift']?.numericValue ?? null,
speedIndex: audits['speed-index']?.displayValue ?? null,
speedIndexMs: audits['speed-index']?.numericValue ?? null,
interactive: audits.interactive?.displayValue ?? null,
interactiveMs: audits.interactive?.numericValue ?? null,
overallCategory: loadingExperience.overall_category ?? null,
analysisTimestamp: data.analysisUTCTimestamp ?? null,
lighthouseVersion: lighthouse.lighthouseVersion ?? null,
},
}
},
outputs: {
finalUrl: {
type: 'string',
description: 'The final URL after redirects',
optional: true,
},
performanceScore: {
type: 'number',
description: 'Performance category score (0-1)',
optional: true,
},
accessibilityScore: {
type: 'number',
description: 'Accessibility category score (0-1)',
optional: true,
},
bestPracticesScore: {
type: 'number',
description: 'Best Practices category score (0-1)',
optional: true,
},
seoScore: {
type: 'number',
description: 'SEO category score (0-1)',
optional: true,
},
firstContentfulPaint: {
type: 'string',
description: 'Time to First Contentful Paint (display value)',
optional: true,
},
firstContentfulPaintMs: {
type: 'number',
description: 'Time to First Contentful Paint in milliseconds',
optional: true,
},
largestContentfulPaint: {
type: 'string',
description: 'Time to Largest Contentful Paint (display value)',
optional: true,
},
largestContentfulPaintMs: {
type: 'number',
description: 'Time to Largest Contentful Paint in milliseconds',
optional: true,
},
totalBlockingTime: {
type: 'string',
description: 'Total Blocking Time (display value)',
optional: true,
},
totalBlockingTimeMs: {
type: 'number',
description: 'Total Blocking Time in milliseconds',
optional: true,
},
cumulativeLayoutShift: {
type: 'string',
description: 'Cumulative Layout Shift (display value)',
optional: true,
},
cumulativeLayoutShiftValue: {
type: 'number',
description: 'Cumulative Layout Shift numeric value',
optional: true,
},
speedIndex: {
type: 'string',
description: 'Speed Index (display value)',
optional: true,
},
speedIndexMs: {
type: 'number',
description: 'Speed Index in milliseconds',
optional: true,
},
interactive: {
type: 'string',
description: 'Time to Interactive (display value)',
optional: true,
},
interactiveMs: {
type: 'number',
description: 'Time to Interactive in milliseconds',
optional: true,
},
overallCategory: {
type: 'string',
description: 'Overall loading experience category (FAST, AVERAGE, SLOW, or NONE)',
optional: true,
},
analysisTimestamp: {
type: 'string',
description: 'UTC timestamp of the analysis',
optional: true,
},
lighthouseVersion: {
type: 'string',
description: 'Version of Lighthouse used for the analysis',
optional: true,
},
},
}

View File

@@ -0,0 +1,5 @@
import { analyzeTool } from '@/tools/google_pagespeed/analyze'
export const googlePagespeedAnalyzeTool = analyzeTool
export * from '@/tools/google_pagespeed/types'

View File

@@ -0,0 +1,37 @@
import type { ToolResponse } from '@/tools/types'
export interface GooglePagespeedBaseParams {
apiKey: string
}
export interface GooglePagespeedAnalyzeParams extends GooglePagespeedBaseParams {
url: string
category?: string
strategy?: string
locale?: string
}
export interface GooglePagespeedAnalyzeResponse extends ToolResponse {
output: {
finalUrl: string | null
performanceScore: number | null
accessibilityScore: number | null
bestPracticesScore: number | null
seoScore: number | null
firstContentfulPaint: string | null
firstContentfulPaintMs: number | null
largestContentfulPaint: string | null
largestContentfulPaintMs: number | null
totalBlockingTime: string | null
totalBlockingTimeMs: number | null
cumulativeLayoutShift: string | null
cumulativeLayoutShiftValue: number | null
speedIndex: string | null
speedIndexMs: number | null
interactive: string | null
interactiveMs: number | null
overallCategory: string | null
analysisTimestamp: string | null
lighthouseVersion: string | null
}
}

View File

@@ -0,0 +1,78 @@
import type { PagerDutyAddNoteParams, PagerDutyAddNoteResponse } from '@/tools/pagerduty/types'
import type { ToolConfig } from '@/tools/types'
export const addNoteTool: ToolConfig<PagerDutyAddNoteParams, PagerDutyAddNoteResponse> = {
id: 'pagerduty_add_note',
name: 'PagerDuty Add Note',
description: 'Add a note to an existing PagerDuty incident.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PagerDuty REST API Key',
},
fromEmail: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Email address of a valid PagerDuty user',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the incident to add the note to',
},
content: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Note content text',
},
},
request: {
url: (params) => `https://api.pagerduty.com/incidents/${params.incidentId.trim()}/notes`,
method: 'POST',
headers: (params) => ({
Authorization: `Token token=${params.apiKey}`,
Accept: 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
From: params.fromEmail,
}),
body: (params) => ({
note: {
content: params.content,
},
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || `PagerDuty API error: ${response.status}`)
}
const note = data.note ?? {}
return {
success: true,
output: {
id: note.id ?? null,
content: note.content ?? null,
createdAt: note.created_at ?? null,
userName: note.user?.summary ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Note ID' },
content: { type: 'string', description: 'Note content' },
createdAt: { type: 'string', description: 'Creation timestamp' },
userName: { type: 'string', description: 'Name of the user who created the note' },
},
}

View File

@@ -0,0 +1,149 @@
import type {
PagerDutyCreateIncidentParams,
PagerDutyCreateIncidentResponse,
} from '@/tools/pagerduty/types'
import type { ToolConfig } from '@/tools/types'
export const createIncidentTool: ToolConfig<
PagerDutyCreateIncidentParams,
PagerDutyCreateIncidentResponse
> = {
id: 'pagerduty_create_incident',
name: 'PagerDuty Create Incident',
description: 'Create a new incident in PagerDuty.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PagerDuty REST API Key',
},
fromEmail: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Email address of a valid PagerDuty user',
},
title: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Incident title/summary',
},
serviceId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the PagerDuty service',
},
urgency: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Urgency level (high or low)',
},
body: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Detailed description of the incident',
},
escalationPolicyId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Escalation policy ID to assign',
},
assigneeId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'User ID to assign the incident to',
},
},
request: {
url: 'https://api.pagerduty.com/incidents',
method: 'POST',
headers: (params) => ({
Authorization: `Token token=${params.apiKey}`,
Accept: 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
From: params.fromEmail,
}),
body: (params) => {
const incident: Record<string, unknown> = {
type: 'incident',
title: params.title,
service: {
id: params.serviceId,
type: 'service_reference',
},
}
if (params.urgency) incident.urgency = params.urgency
if (params.body) {
incident.body = {
type: 'incident_body',
details: params.body,
}
}
if (params.escalationPolicyId) {
incident.escalation_policy = {
id: params.escalationPolicyId,
type: 'escalation_policy_reference',
}
}
if (params.assigneeId) {
incident.assignments = [
{
assignee: {
id: params.assigneeId,
type: 'user_reference',
},
},
]
}
return { incident }
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || `PagerDuty API error: ${response.status}`)
}
const inc = data.incident ?? {}
return {
success: true,
output: {
id: inc.id ?? null,
incidentNumber: inc.incident_number ?? null,
title: inc.title ?? null,
status: inc.status ?? null,
urgency: inc.urgency ?? null,
createdAt: inc.created_at ?? null,
serviceName: inc.service?.summary ?? null,
serviceId: inc.service?.id ?? null,
htmlUrl: inc.html_url ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Created incident ID' },
incidentNumber: { type: 'number', description: 'Incident number' },
title: { type: 'string', description: 'Incident title' },
status: { type: 'string', description: 'Incident status' },
urgency: { type: 'string', description: 'Incident urgency' },
createdAt: { type: 'string', description: 'Creation timestamp' },
serviceName: { type: 'string', description: 'Service name' },
serviceId: { type: 'string', description: 'Service ID' },
htmlUrl: { type: 'string', description: 'PagerDuty web URL' },
},
}

View File

@@ -0,0 +1,13 @@
import { addNoteTool } from '@/tools/pagerduty/add_note'
import { createIncidentTool } from '@/tools/pagerduty/create_incident'
import { listIncidentsTool } from '@/tools/pagerduty/list_incidents'
import { listOncallsTool } from '@/tools/pagerduty/list_oncalls'
import { listServicesTool } from '@/tools/pagerduty/list_services'
import { updateIncidentTool } from '@/tools/pagerduty/update_incident'
export const pagerdutyListIncidentsTool = listIncidentsTool
export const pagerdutyCreateIncidentTool = createIncidentTool
export const pagerdutyUpdateIncidentTool = updateIncidentTool
export const pagerdutyAddNoteTool = addNoteTool
export const pagerdutyListServicesTool = listServicesTool
export const pagerdutyListOncallsTool = listOncallsTool

View File

@@ -0,0 +1,161 @@
import type {
PagerDutyListIncidentsParams,
PagerDutyListIncidentsResponse,
} from '@/tools/pagerduty/types'
import type { ToolConfig } from '@/tools/types'
export const listIncidentsTool: ToolConfig<
PagerDutyListIncidentsParams,
PagerDutyListIncidentsResponse
> = {
id: 'pagerduty_list_incidents',
name: 'PagerDuty List Incidents',
description: 'List incidents from PagerDuty with optional filters.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PagerDuty REST API Key',
},
statuses: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated statuses to filter (triggered, acknowledged, resolved)',
},
serviceIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated service IDs to filter',
},
since: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Start date filter (ISO 8601 format)',
},
until: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'End date filter (ISO 8601 format)',
},
sortBy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort field (e.g., created_at:desc)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (max 100)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.statuses) {
for (const s of params.statuses.split(',')) {
query.append('statuses[]', s.trim())
}
}
if (params.serviceIds) {
for (const id of params.serviceIds.split(',')) {
query.append('service_ids[]', id.trim())
}
}
if (params.since) query.set('since', params.since)
if (params.until) query.set('until', params.until)
if (params.sortBy) query.set('sort_by', params.sortBy)
if (params.limit) query.set('limit', params.limit)
query.append('include[]', 'services')
const qs = query.toString()
return `https://api.pagerduty.com/incidents${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Token token=${params.apiKey}`,
Accept: 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || `PagerDuty API error: ${response.status}`)
}
return {
success: true,
output: {
incidents: (data.incidents ?? []).map(
(
inc: Record<string, unknown> & {
service?: Record<string, unknown>
assignments?: Array<Record<string, unknown> & { assignee?: Record<string, unknown> }>
escalation_policy?: Record<string, unknown>
}
) => ({
id: inc.id ?? null,
incidentNumber: inc.incident_number ?? null,
title: inc.title ?? null,
status: inc.status ?? null,
urgency: inc.urgency ?? null,
createdAt: inc.created_at ?? null,
updatedAt: inc.updated_at ?? null,
serviceName: inc.service?.summary ?? null,
serviceId: inc.service?.id ?? null,
assigneeName: inc.assignments?.[0]?.assignee?.summary ?? null,
assigneeId: inc.assignments?.[0]?.assignee?.id ?? null,
escalationPolicyName: inc.escalation_policy?.summary ?? null,
htmlUrl: inc.html_url ?? null,
})
),
total: data.total ?? 0,
more: data.more ?? false,
},
}
},
outputs: {
incidents: {
type: 'array',
description: 'Array of incidents',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Incident ID' },
incidentNumber: { type: 'number', description: 'Incident number' },
title: { type: 'string', description: 'Incident title' },
status: { type: 'string', description: 'Incident status' },
urgency: { type: 'string', description: 'Incident urgency' },
createdAt: { type: 'string', description: 'Creation timestamp' },
updatedAt: { type: 'string', description: 'Last updated timestamp' },
serviceName: { type: 'string', description: 'Service name' },
serviceId: { type: 'string', description: 'Service ID' },
assigneeName: { type: 'string', description: 'Assignee name' },
assigneeId: { type: 'string', description: 'Assignee ID' },
escalationPolicyName: { type: 'string', description: 'Escalation policy name' },
htmlUrl: { type: 'string', description: 'PagerDuty web URL' },
},
},
},
total: {
type: 'number',
description: 'Total number of matching incidents',
},
more: {
type: 'boolean',
description: 'Whether more results are available',
},
},
}

View File

@@ -0,0 +1,145 @@
import type {
PagerDutyListOncallsParams,
PagerDutyListOncallsResponse,
} from '@/tools/pagerduty/types'
import type { ToolConfig } from '@/tools/types'
export const listOncallsTool: ToolConfig<PagerDutyListOncallsParams, PagerDutyListOncallsResponse> =
{
id: 'pagerduty_list_oncalls',
name: 'PagerDuty List On-Calls',
description: 'List current on-call entries from PagerDuty.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PagerDuty REST API Key',
},
escalationPolicyIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated escalation policy IDs to filter',
},
scheduleIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated schedule IDs to filter',
},
since: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Start time filter (ISO 8601 format)',
},
until: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'End time filter (ISO 8601 format)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (max 100)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.escalationPolicyIds) {
for (const id of params.escalationPolicyIds.split(',')) {
query.append('escalation_policy_ids[]', id.trim())
}
}
if (params.scheduleIds) {
for (const id of params.scheduleIds.split(',')) {
query.append('schedule_ids[]', id.trim())
}
}
if (params.since) query.set('since', params.since)
if (params.until) query.set('until', params.until)
if (params.limit) query.set('limit', params.limit)
const qs = query.toString()
return `https://api.pagerduty.com/oncalls${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Token token=${params.apiKey}`,
Accept: 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || `PagerDuty API error: ${response.status}`)
}
const oncalls = (data.oncalls ?? []).map(
(
oc: Record<string, unknown> & {
user?: Record<string, unknown>
escalation_policy?: Record<string, unknown>
schedule?: Record<string, unknown>
}
) => ({
userName: oc.user?.summary ?? null,
userId: oc.user?.id ?? null,
escalationLevel: oc.escalation_level ?? 0,
escalationPolicyName: oc.escalation_policy?.summary ?? null,
escalationPolicyId: oc.escalation_policy?.id ?? null,
scheduleName: oc.schedule?.summary ?? null,
scheduleId: oc.schedule?.id ?? null,
start: oc.start ?? null,
end: oc.end ?? null,
})
)
return {
success: true,
output: {
oncalls,
total: data.total ?? oncalls.length,
more: data.more ?? false,
},
}
},
outputs: {
oncalls: {
type: 'array',
description: 'Array of on-call entries',
items: {
type: 'object',
properties: {
userName: { type: 'string', description: 'On-call user name' },
userId: { type: 'string', description: 'On-call user ID' },
escalationLevel: { type: 'number', description: 'Escalation level' },
escalationPolicyName: { type: 'string', description: 'Escalation policy name' },
escalationPolicyId: { type: 'string', description: 'Escalation policy ID' },
scheduleName: { type: 'string', description: 'Schedule name' },
scheduleId: { type: 'string', description: 'Schedule ID' },
start: { type: 'string', description: 'On-call start time' },
end: { type: 'string', description: 'On-call end time' },
},
},
},
total: {
type: 'number',
description: 'Total number of matching on-call entries',
},
more: {
type: 'boolean',
description: 'Whether more results are available',
},
},
}

View File

@@ -0,0 +1,108 @@
import type {
PagerDutyListServicesParams,
PagerDutyListServicesResponse,
} from '@/tools/pagerduty/types'
import type { ToolConfig } from '@/tools/types'
export const listServicesTool: ToolConfig<
PagerDutyListServicesParams,
PagerDutyListServicesResponse
> = {
id: 'pagerduty_list_services',
name: 'PagerDuty List Services',
description: 'List services from PagerDuty with optional name filter.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PagerDuty REST API Key',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter services by name',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (max 100)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.query) query.set('query', params.query)
if (params.limit) query.set('limit', params.limit)
const qs = query.toString()
return `https://api.pagerduty.com/services${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Token token=${params.apiKey}`,
Accept: 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || `PagerDuty API error: ${response.status}`)
}
return {
success: true,
output: {
services: (data.services ?? []).map(
(svc: Record<string, unknown> & { escalation_policy?: Record<string, unknown> }) => ({
id: svc.id ?? null,
name: svc.name ?? null,
description: svc.description ?? null,
status: svc.status ?? null,
escalationPolicyName: svc.escalation_policy?.summary ?? null,
escalationPolicyId: svc.escalation_policy?.id ?? null,
createdAt: svc.created_at ?? null,
htmlUrl: svc.html_url ?? null,
})
),
total: data.total ?? 0,
more: data.more ?? false,
},
}
},
outputs: {
services: {
type: 'array',
description: 'Array of services',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Service ID' },
name: { type: 'string', description: 'Service name' },
description: { type: 'string', description: 'Service description' },
status: { type: 'string', description: 'Service status' },
escalationPolicyName: { type: 'string', description: 'Escalation policy name' },
escalationPolicyId: { type: 'string', description: 'Escalation policy ID' },
createdAt: { type: 'string', description: 'Creation timestamp' },
htmlUrl: { type: 'string', description: 'PagerDuty web URL' },
},
},
},
total: {
type: 'number',
description: 'Total number of matching services',
},
more: {
type: 'boolean',
description: 'Whether more results are available',
},
},
}

View File

@@ -0,0 +1,169 @@
import type { ToolResponse } from '@/tools/types'
/**
* Base params shared by all PagerDuty endpoints.
*/
export interface PagerDutyBaseParams {
apiKey: string
}
/**
* Params that require a From header for write operations.
*/
export interface PagerDutyWriteParams extends PagerDutyBaseParams {
fromEmail: string
}
/**
* List Incidents params.
*/
export interface PagerDutyListIncidentsParams extends PagerDutyBaseParams {
statuses?: string
serviceIds?: string
since?: string
until?: string
sortBy?: string
limit?: string
}
export interface PagerDutyListIncidentsResponse extends ToolResponse {
output: {
incidents: Array<{
id: string
incidentNumber: number
title: string
status: string
urgency: string
createdAt: string
updatedAt: string | null
serviceName: string | null
serviceId: string | null
assigneeName: string | null
assigneeId: string | null
escalationPolicyName: string | null
htmlUrl: string | null
}>
total: number
more: boolean
}
}
/**
* Create Incident params.
*/
export interface PagerDutyCreateIncidentParams extends PagerDutyWriteParams {
title: string
serviceId: string
urgency?: string
body?: string
escalationPolicyId?: string
assigneeId?: string
}
export interface PagerDutyCreateIncidentResponse extends ToolResponse {
output: {
id: string
incidentNumber: number
title: string
status: string
urgency: string
createdAt: string
serviceName: string | null
serviceId: string | null
htmlUrl: string | null
}
}
/**
* Update Incident params.
*/
export interface PagerDutyUpdateIncidentParams extends PagerDutyWriteParams {
incidentId: string
status?: string
title?: string
urgency?: string
escalationLevel?: string
}
export interface PagerDutyUpdateIncidentResponse extends ToolResponse {
output: {
id: string
incidentNumber: number
title: string
status: string
urgency: string
updatedAt: string | null
htmlUrl: string | null
}
}
/**
* Add Note to Incident params.
*/
export interface PagerDutyAddNoteParams extends PagerDutyWriteParams {
incidentId: string
content: string
}
export interface PagerDutyAddNoteResponse extends ToolResponse {
output: {
id: string
content: string
createdAt: string
userName: string | null
}
}
/**
* List Services params.
*/
export interface PagerDutyListServicesParams extends PagerDutyBaseParams {
query?: string
limit?: string
}
export interface PagerDutyListServicesResponse extends ToolResponse {
output: {
services: Array<{
id: string
name: string
description: string | null
status: string
escalationPolicyName: string | null
escalationPolicyId: string | null
createdAt: string
htmlUrl: string | null
}>
total: number
more: boolean
}
}
/**
* List On-Calls params.
*/
export interface PagerDutyListOncallsParams extends PagerDutyBaseParams {
escalationPolicyIds?: string
scheduleIds?: string
since?: string
until?: string
limit?: string
}
export interface PagerDutyListOncallsResponse extends ToolResponse {
output: {
oncalls: Array<{
userName: string | null
userId: string | null
escalationLevel: number
escalationPolicyName: string | null
escalationPolicyId: string | null
scheduleName: string | null
scheduleId: string | null
start: string | null
end: string | null
}>
total: number
more: boolean
}
}

View File

@@ -0,0 +1,117 @@
import type {
PagerDutyUpdateIncidentParams,
PagerDutyUpdateIncidentResponse,
} from '@/tools/pagerduty/types'
import type { ToolConfig } from '@/tools/types'
export const updateIncidentTool: ToolConfig<
PagerDutyUpdateIncidentParams,
PagerDutyUpdateIncidentResponse
> = {
id: 'pagerduty_update_incident',
name: 'PagerDuty Update Incident',
description: 'Update an incident in PagerDuty (acknowledge, resolve, change urgency, etc.).',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PagerDuty REST API Key',
},
fromEmail: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Email address of a valid PagerDuty user',
},
incidentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the incident to update',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New status (acknowledged or resolved)',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New incident title',
},
urgency: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New urgency (high or low)',
},
escalationLevel: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Escalation level to escalate to',
},
},
request: {
url: (params) => `https://api.pagerduty.com/incidents/${params.incidentId.trim()}`,
method: 'PUT',
headers: (params) => ({
Authorization: `Token token=${params.apiKey}`,
Accept: 'application/vnd.pagerduty+json;version=2',
'Content-Type': 'application/json',
From: params.fromEmail,
}),
body: (params) => {
const incident: Record<string, unknown> = {
id: params.incidentId,
type: 'incident',
}
if (params.status) incident.status = params.status
if (params.title) incident.title = params.title
if (params.urgency) incident.urgency = params.urgency
if (params.escalationLevel) {
incident.escalation_level = Number(params.escalationLevel)
}
return { incident }
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || `PagerDuty API error: ${response.status}`)
}
const inc = data.incident ?? {}
return {
success: true,
output: {
id: inc.id ?? null,
incidentNumber: inc.incident_number ?? null,
title: inc.title ?? null,
status: inc.status ?? null,
urgency: inc.urgency ?? null,
updatedAt: inc.updated_at ?? null,
htmlUrl: inc.html_url ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Incident ID' },
incidentNumber: { type: 'number', description: 'Incident number' },
title: { type: 'string', description: 'Incident title' },
status: { type: 'string', description: 'Updated status' },
urgency: { type: 'string', description: 'Updated urgency' },
updatedAt: { type: 'string', description: 'Last updated timestamp' },
htmlUrl: { type: 'string', description: 'PagerDuty web URL' },
},
}

View File

@@ -42,6 +42,19 @@ import {
algoliaSearchTool,
algoliaUpdateSettingsTool,
} from '@/tools/algolia'
import {
amplitudeEventSegmentationTool,
amplitudeGetActiveUsersTool,
amplitudeGetRevenueTool,
amplitudeGroupIdentifyTool,
amplitudeIdentifyUserTool,
amplitudeListEventsTool,
amplitudeRealtimeActiveUsersTool,
amplitudeSendEventTool,
amplitudeUserActivityTool,
amplitudeUserProfileTool,
amplitudeUserSearchTool,
} from '@/tools/amplitude'
import { apifyRunActorAsyncTool, apifyRunActorSyncTool } from '@/tools/apify'
import {
apolloAccountBulkCreateTool,
@@ -786,6 +799,7 @@ import {
googleMapsTimezoneTool,
googleMapsValidateAddressTool,
} from '@/tools/google_maps'
import { googlePagespeedAnalyzeTool } from '@/tools/google_pagespeed'
import {
googleSheetsAppendTool,
googleSheetsAppendV2Tool,
@@ -1444,6 +1458,14 @@ import {
outlookReadTool,
outlookSendTool,
} from '@/tools/outlook'
import {
pagerdutyAddNoteTool,
pagerdutyCreateIncidentTool,
pagerdutyListIncidentsTool,
pagerdutyListOncallsTool,
pagerdutyListServicesTool,
pagerdutyUpdateIncidentTool,
} from '@/tools/pagerduty'
import { parallelDeepResearchTool, parallelExtractTool, parallelSearchTool } from '@/tools/parallel'
import { perplexityChatTool, perplexitySearchTool } from '@/tools/perplexity'
import {
@@ -2248,6 +2270,17 @@ export const tools: Record<string, ToolConfig> = {
a2a_send_message: a2aSendMessageTool,
a2a_set_push_notification: a2aSetPushNotificationTool,
airweave_search: airweaveSearchTool,
amplitude_send_event: amplitudeSendEventTool,
amplitude_identify_user: amplitudeIdentifyUserTool,
amplitude_group_identify: amplitudeGroupIdentifyTool,
amplitude_user_search: amplitudeUserSearchTool,
amplitude_user_activity: amplitudeUserActivityTool,
amplitude_user_profile: amplitudeUserProfileTool,
amplitude_event_segmentation: amplitudeEventSegmentationTool,
amplitude_get_active_users: amplitudeGetActiveUsersTool,
amplitude_realtime_active_users: amplitudeRealtimeActiveUsersTool,
amplitude_list_events: amplitudeListEventsTool,
amplitude_get_revenue: amplitudeGetRevenueTool,
arxiv_get_author_papers: arxivGetAuthorPapersTool,
arxiv_get_paper: arxivGetPaperTool,
arxiv_search: arxivSearchTool,
@@ -3163,6 +3196,7 @@ export const tools: Record<string, ToolConfig> = {
google_maps_speed_limits: googleMapsSpeedLimitsTool,
google_maps_timezone: googleMapsTimezoneTool,
google_maps_validate_address: googleMapsValidateAddressTool,
google_pagespeed_analyze: googlePagespeedAnalyzeTool,
google_tasks_create: googleTasksCreateTool,
google_tasks_delete: googleTasksDeleteTool,
google_tasks_get: googleTasksGetTool,
@@ -3637,6 +3671,12 @@ export const tools: Record<string, ToolConfig> = {
outlook_mark_unread: outlookMarkUnreadTool,
outlook_delete: outlookDeleteTool,
outlook_copy: outlookCopyTool,
pagerduty_list_incidents: pagerdutyListIncidentsTool,
pagerduty_create_incident: pagerdutyCreateIncidentTool,
pagerduty_update_incident: pagerdutyUpdateIncidentTool,
pagerduty_add_note: pagerdutyAddNoteTool,
pagerduty_list_services: pagerdutyListServicesTool,
pagerduty_list_oncalls: pagerdutyListOncallsTool,
linear_read_issues: linearReadIssuesTool,
linear_create_issue: linearCreateIssueTool,
linear_get_issue: linearGetIssueTool,