feat(tools): added redis, upstash, algolia, and revenuecat (#3261)

* feat(tools): added redis, upstash, algolia, and revenuecat

* ack comment
This commit is contained in:
Waleed
2026-02-19 16:13:06 -08:00
committed by GitHub
parent 3c470ab0f8
commit 61c7afc19e
86 changed files with 9778 additions and 0 deletions
+82
View File
@@ -1157,6 +1157,17 @@ export function AirweaveIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function AlgoliaIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 50 50'>
<path
fill='#FFFFFF'
d='M25,0C11.3,0,0.2,11,0,24.6C-0.2,38.4,11,49.9,24.8,50c4.3,0,8.4-1,12-3c0.4-0.2,0.4-0.7,0.1-1l-2.3-2.1 c-0.5-0.4-1.2-0.5-1.7-0.3c-2.5,1.1-5.3,1.6-8.2,1.6c-11.2-0.1-20.2-9.4-20-20.6C4.9,13.6,13.9,4.7,25,4.7h20.3v36L33.7,30.5 c-0.4-0.3-0.9-0.3-1.2,0.1c-1.8,2.4-4.9,4-8.2,3.7c-4.6-0.3-8.4-4-8.7-8.7c-0.4-5.5,4-10.2,9.4-10.2c4.9,0,9,3.8,9.4,8.6 c0,0.4,0.2,0.8,0.6,1.1l3,2.7c0.3,0.3,0.9,0.1,1-0.3c0.2-1.2,0.3-2.4,0.2-3.6c-0.5-7-6.2-12.7-13.2-13.1c-8.1-0.5-14.8,5.8-15,13.7 c-0.2,7.7,6.1,14.4,13.8,14.5c3.2,0.1,6.2-0.9,8.6-2.7l15,13.3c0.6,0.6,1.7,0.1,1.7-0.7v-48C50,0.4,49.5,0,49,0L25,0 C25,0,25,0,25,0z'
/>
</svg>
)
}
export function GoogleBooksIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 478.633 540.068'>
@@ -5737,3 +5748,74 @@ export function CloudflareIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function UpstashIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 341' width='24' height='24'>
<path
fill='#00C98D'
d='M0 298.417c56.554 56.553 148.247 56.553 204.801 0c56.554-56.554 56.554-148.247 0-204.801l-25.6 25.6c42.415 42.416 42.415 111.185 0 153.6c-42.416 42.416-111.185 42.416-153.601 0z'
/>
<path
fill='#00C98D'
d='M51.2 247.216c28.277 28.277 74.123 28.277 102.4 0c28.277-28.276 28.277-74.123 0-102.4l-25.6 25.6c14.14 14.138 14.14 37.061 0 51.2c-14.138 14.139-37.061 14.139-51.2 0zM256 42.415c-56.554-56.553-148.247-56.553-204.8 0c-56.555 56.555-56.555 148.247 0 204.801l25.599-25.6c-42.415-42.415-42.415-111.185 0-153.6c42.416-42.416 111.185-42.416 153.6 0z'
/>
<path
fill='#00C98D'
d='M204.8 93.616c-28.276-28.277-74.124-28.277-102.4 0c-28.278 28.277-28.278 74.123 0 102.4l25.6-25.6c-14.14-14.138-14.14-37.061 0-51.2c14.138-14.139 37.06-14.139 51.2 0z'
/>
<path
fill='#FFF'
fillOpacity='.4'
d='M256 42.415c-56.554-56.553-148.247-56.553-204.8 0c-56.555 56.555-56.555 148.247 0 204.801l25.599-25.6c-42.415-42.415-42.415-111.185 0-153.6c42.416-42.416 111.185-42.416 153.6 0z'
/>
<path
fill='#FFF'
fillOpacity='.4'
d='M204.8 93.616c-28.276-28.277-74.124-28.277-102.4 0c-28.278 28.277-28.278 74.123 0 102.4l25.6-25.6c-14.14-14.138-14.14-37.061 0-51.2c14.138-14.139 37.06-14.139 51.2 0z'
/>
</svg>
)
}
export function RevenueCatIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
width='512'
height='512'
viewBox='0 0 512 512'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M95 109.774C110.152 106.108 133.612 104 154.795 104C212.046 104 246.32 123.928 246.32 174.646C246.32 205.746 233.737 226.264 214.005 237.437L261.765 318.946C258.05 321.632 250.035 323.176 238.864 323.176C226.282 323.176 217.987 321.672 211.982 318.946L172.225 248.3H167.645C157.789 248.305 147.945 247.601 138.18 246.192V319.255C134.172 321.672 127.022 323.176 116.73 323.176C106.73 323.176 99.2874 321.659 95 319.255V109.774ZM137.643 207.848C145.772 209.263 153.997 209.968 162.235 209.956C187.12 209.956 202.285 200.556 202.285 177.057C202.285 152.886 186.268 142.949 157.668 142.949C150.956 142.918 144.255 143.515 137.643 144.735V207.848Z'
fill='#FFFFFF'
/>
<path
d='M428.529 329.244C428.529 365.526 410.145 375.494 396.306 382.195C360.972 399.32 304.368 379.4 244.206 373.338C189.732 366.214 135.706 361.522 127.309 373.738C124.152 376.832 123.481 386.798 127.309 390.862C138.604 402.85 168.061 394.493 188.919 390.714C195.391 389.694 201.933 392.099 206.079 397.021C210.226 401.944 211.349 408.637 209.024 414.58C206.699 420.522 201.28 424.811 194.809 425.831C185.379 427.264 175.85 427.989 166.306 428C145.988 428 120.442 424.495 105.943 409.072C98.7232 401.4 91.3266 387.78 97.0271 366.465C107.875 326.074 172.807 336.052 248.033 343.633C300.41 348.907 357.23 366.465 379.934 350.343C385.721 346.234 396.517 337.022 390.698 329.244C384.879 321.467 375.353 325.684 362.838 325.684C300.152 325.684 263.238 285.302 263.238 217.916C263.247 167.292 284.176 131.892 318.287 115.09C333.109 107.789 350.421 104 369.587 104C386.292 104 403.269 106.931 414.11 113.366C420.847 123.032 423.778 140.305 422.306 153.201C408.247 146.466 395.36 142.949 378.669 142.949C337.365 142.949 308.947 164.039 308.947 214.985C308.947 265.932 337.065 286.149 376.611 286.149C387.869 286.035 403.1 284.67 422.306 282.053C426.455 297.498 428.529 313.228 428.529 329.244Z'
fill='#FFFFFF'
/>
</svg>
)
}
export function RedisIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='0 0 512 512'
xmlns='http://www.w3.org/2000/svg'
fillRule='evenodd'
clipRule='evenodd'
strokeLinejoin='round'
strokeMiterlimit='2'
>
<path
d='M479.14 279.864c-34.584 43.578-71.94 93.385-146.645 93.385-66.73 0-91.59-58.858-93.337-106.672 14.62 30.915 43.203 55.949 87.804 54.792C412.737 318.6 471.53 241.127 471.53 170.57c0-84.388-62.947-145.262-172.24-145.262-78.165 0-175.004 29.743-238.646 76.782-.689 48.42 26.286 111.369 35.972 104.452 55.17-39.67 98.918-65.203 141.35-78.01C175.153 198.58 24.451 361.219 6 389.85c2.076 26.286 34.588 96.842 50.496 96.842 4.841 0 8.993-2.768 13.835-7.61 45.433-51.046 82.472-96.816 115.412-140.933 4.627 64.658 36.42 143.702 125.307 143.702 79.55 0 158.408-57.414 194.377-186.767 4.149-15.911-15.22-28.362-26.286-15.22zm-90.616-104.449c0 40.81-40.118 60.87-76.782 60.87-19.596 0-34.648-5.145-46.554-11.832 21.906-33.168 43.59-67.182 66.887-103.593 41.08 6.953 56.449 29.788 56.449 54.555z'
fill='#FFFFFF'
fillRule='nonzero'
/>
</svg>
)
}
+8
View File
@@ -8,6 +8,7 @@ import {
AhrefsIcon,
AirtableIcon,
AirweaveIcon,
AlgoliaIcon,
ApifyIcon,
ApolloIcon,
ArxivIcon,
@@ -98,8 +99,10 @@ import {
QdrantIcon,
RDSIcon,
RedditIcon,
RedisIcon,
ReductoIcon,
ResendIcon,
RevenueCatIcon,
S3Icon,
SalesforceIcon,
SearchIcon,
@@ -127,6 +130,7 @@ import {
TTSIcon,
TwilioIcon,
TypeformIcon,
UpstashIcon,
VercelIcon,
VideoIcon,
WealthboxIcon,
@@ -148,6 +152,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
ahrefs: AhrefsIcon,
airtable: AirtableIcon,
airweave: AirweaveIcon,
algolia: AlgoliaIcon,
apify: ApifyIcon,
apollo: ApolloIcon,
arxiv: ArxivIcon,
@@ -236,8 +241,10 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
qdrant: QdrantIcon,
rds: RDSIcon,
reddit: RedditIcon,
redis: RedisIcon,
reducto_v2: ReductoIcon,
resend: ResendIcon,
revenuecat: RevenueCatIcon,
s3: S3Icon,
salesforce: SalesforceIcon,
search: SearchIcon,
@@ -267,6 +274,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
twilio_sms: TwilioIcon,
twilio_voice: TwilioIcon,
typeform: TypeformIcon,
upstash: UpstashIcon,
vercel: VercelIcon,
video_generator_v2: VideoIcon,
vision_v2: EyeIcon,
+404
View File
@@ -0,0 +1,404 @@
---
title: Algolia
description: Search and manage Algolia indices
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="algolia"
color="#003DFF"
/>
{/* MANUAL-CONTENT-START:intro */}
[Algolia](https://www.algolia.com/) is a powerful hosted search platform that enables developers and teams to deliver fast, relevant search experiences in their apps and websites. Algolia provides full-text, faceted, and filtered search as well as analytics and advanced ranking capabilities.
With Algolia, you can:
- **Deliver lightning-fast search**: Provide instant search results as users type, with typo tolerance and synonyms
- **Manage and update records**: Easily add, update, or delete objects/records in your indices
- **Perform advanced filtering**: Use filters, facets, and custom ranking to refine and organize search results
- **Configure index settings**: Adjust relevance, ranking, attributes for search, and more to optimize user experience
- **Scale confidently**: Algolia handles massive traffic and data volumes with globally distributed infrastructure
- **Gain insights**: Track analytics, search patterns, and user engagement
In Sim, the Algolia integration allows your agents to search, manage, and configure Algolia indices directly within your workflows. Use Algolia to power dynamic data exploration, automate record updates, run batch operations, and more—all from a single tool in your workspace.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate Algolia into your workflow. Search indices, manage records (add, update, delete, browse), configure index settings, and perform batch operations.
## Tools
### `algolia_search`
Search an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia API Key |
| `indexName` | string | Yes | Name of the Algolia index to search |
| `query` | string | Yes | Search query text |
| `hitsPerPage` | number | No | Number of hits per page \(default: 20\) |
| `page` | number | No | Page number to retrieve \(default: 0\) |
| `filters` | string | No | Filter string \(e.g., "category:electronics AND price &lt; 100"\) |
| `attributesToRetrieve` | string | No | Comma-separated list of attributes to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `hits` | array | Array of matching records |
| ↳ `objectID` | string | Unique identifier of the record |
| ↳ `_highlightResult` | object | Highlighted attributes matching the query. Each attribute has value, matchLevel \(none, partial, full\), and matchedWords |
| ↳ `_snippetResult` | object | Snippeted attributes matching the query. Each attribute has value and matchLevel |
| ↳ `_rankingInfo` | object | Ranking information for the hit. Only present when getRankingInfo is enabled |
| ↳ `nbTypos` | number | Number of typos in the query match |
| ↳ `firstMatchedWord` | number | Position of the first matched word |
| ↳ `geoDistance` | number | Distance in meters for geo-search results |
| ↳ `nbExactWords` | number | Number of exactly matched words |
| ↳ `userScore` | number | Custom ranking score |
| ↳ `words` | number | Number of matched words |
| `nbHits` | number | Total number of matching hits |
| `page` | number | Current page number \(zero-based\) |
| `nbPages` | number | Total number of pages available |
| `hitsPerPage` | number | Number of hits per page \(1-1000, default 20\) |
| `processingTimeMS` | number | Server-side processing time in milliseconds |
| `query` | string | The search query that was executed |
| `parsedQuery` | string | The query string after normalization and stop word removal |
| `facets` | object | Facet counts keyed by facet name, each containing value-count pairs |
| `facets_stats` | object | Statistics \(min, max, avg, sum\) for numeric facets |
| `exhaustive` | object | Exhaustiveness flags for facetsCount, facetValues, nbHits, rulesMatch, and typo |
### `algolia_add_record`
Add or replace a record in an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key |
| `indexName` | string | Yes | Name of the Algolia index |
| `objectID` | string | No | Object ID for the record \(auto-generated if not provided\) |
| `record` | json | Yes | JSON object representing the record to add |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the indexing operation |
| `objectID` | string | The object ID of the added or replaced record |
| `createdAt` | string | Timestamp when the record was created \(only present when objectID is auto-generated\) |
| `updatedAt` | string | Timestamp when the record was updated \(only present when replacing an existing record\) |
### `algolia_get_record`
Get a record by objectID from an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia API Key |
| `indexName` | string | Yes | Name of the Algolia index |
| `objectID` | string | Yes | The objectID of the record to retrieve |
| `attributesToRetrieve` | string | No | Comma-separated list of attributes to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `objectID` | string | The objectID of the retrieved record |
| `record` | object | The record data \(all attributes\) |
### `algolia_get_records`
Retrieve multiple records by objectID from one or more Algolia indices
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia API Key |
| `indexName` | string | Yes | Default index name for all requests |
| `requests` | json | Yes | Array of objects specifying records to retrieve. Each must have "objectID" and optionally "indexName" and "attributesToRetrieve". |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `results` | array | Array of retrieved records \(null entries for records not found\) |
| ↳ `objectID` | string | Unique identifier of the record |
### `algolia_partial_update_record`
Partially update a record in an Algolia index without replacing it entirely
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key |
| `indexName` | string | Yes | Name of the Algolia index |
| `objectID` | string | Yes | The objectID of the record to update |
| `attributes` | json | Yes | JSON object with attributes to update. Supports built-in operations like \{"stock": \{"_operation": "Decrement", "value": 1\}\} |
| `createIfNotExists` | boolean | No | Whether to create the record if it does not exist \(default: true\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the update operation |
| `objectID` | string | The objectID of the updated record |
| `updatedAt` | string | Timestamp when the record was updated |
### `algolia_delete_record`
Delete a record by objectID from an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key |
| `indexName` | string | Yes | Name of the Algolia index |
| `objectID` | string | Yes | The objectID of the record to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the deletion |
| `deletedAt` | string | Timestamp when the record was deleted |
### `algolia_browse_records`
Browse and iterate over all records in an Algolia index using cursor pagination
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia API Key \(must have browse ACL\) |
| `indexName` | string | Yes | Name of the Algolia index to browse |
| `query` | string | No | Search query to filter browsed records |
| `filters` | string | No | Filter string to narrow down results |
| `attributesToRetrieve` | string | No | Comma-separated list of attributes to retrieve |
| `hitsPerPage` | number | No | Number of hits per page \(default: 1000, max: 1000\) |
| `cursor` | string | No | Cursor from a previous browse response for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `hits` | array | Array of records from the index \(up to 1000 per request\) |
| ↳ `objectID` | string | Unique identifier of the record |
| `cursor` | string | Opaque cursor string for retrieving the next page of results. Absent when no more results exist. |
| `nbHits` | number | Total number of records matching the browse criteria |
| `page` | number | Current page number \(zero-based\) |
| `nbPages` | number | Total number of pages available |
| `hitsPerPage` | number | Number of hits per page \(1-1000, default 1000 for browse\) |
| `processingTimeMS` | number | Server-side processing time in milliseconds |
### `algolia_batch_operations`
Perform batch add, update, partial update, or delete operations on records in an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key |
| `indexName` | string | Yes | Name of the Algolia index |
| `requests` | json | Yes | Array of batch operations. Each item has "action" \(addObject, updateObject, partialUpdateObject, partialUpdateObjectNoCreate, deleteObject\) and "body" \(the record data, must include objectID for update/delete\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the batch operation |
| `objectIDs` | array | Array of object IDs affected by the batch operation |
### `algolia_list_indices`
List all indices in an Algolia application
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia API Key |
| `page` | number | No | Page number for paginating indices \(default: not paginated\) |
| `hitsPerPage` | number | No | Number of indices per page \(default: 100\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `indices` | array | List of indices in the application |
| ↳ `name` | string | Name of the index |
| ↳ `entries` | number | Number of records in the index |
| ↳ `dataSize` | number | Size of the index data in bytes |
| ↳ `fileSize` | number | Size of the index files in bytes |
| ↳ `lastBuildTimeS` | number | Last build duration in seconds |
| ↳ `numberOfPendingTasks` | number | Number of pending indexing tasks |
| ↳ `pendingTask` | boolean | Whether the index has pending tasks |
| ↳ `createdAt` | string | Timestamp when the index was created |
| ↳ `updatedAt` | string | Timestamp when the index was last updated |
| ↳ `primary` | string | Name of the primary index \(if this is a replica\) |
| ↳ `replicas` | array | List of replica index names |
| ↳ `virtual` | boolean | Whether the index is a virtual replica |
| `nbPages` | number | Total number of pages of indices |
### `algolia_get_settings`
Retrieve the settings of an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia API Key |
| `indexName` | string | Yes | Name of the Algolia index |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `searchableAttributes` | array | List of searchable attributes |
| `attributesForFaceting` | array | Attributes used for faceting |
| `ranking` | array | Ranking criteria |
| `customRanking` | array | Custom ranking criteria |
| `replicas` | array | List of replica index names |
| `hitsPerPage` | number | Default number of hits per page |
| `maxValuesPerFacet` | number | Maximum number of facet values returned |
| `highlightPreTag` | string | HTML tag inserted before highlighted parts |
| `highlightPostTag` | string | HTML tag inserted after highlighted parts |
| `paginationLimitedTo` | number | Maximum number of hits accessible via pagination |
### `algolia_update_settings`
Update the settings of an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key \(must have editSettings ACL\) |
| `indexName` | string | Yes | Name of the Algolia index |
| `settings` | json | Yes | JSON object with settings to update \(e.g., \{"searchableAttributes": \["name", "description"\], "customRanking": \["desc\(popularity\)"\]\}\) |
| `forwardToReplicas` | boolean | No | Whether to apply changes to replica indices \(default: false\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the settings update |
| `updatedAt` | string | Timestamp when the settings were updated |
### `algolia_delete_index`
Delete an entire Algolia index and all its records
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key \(must have deleteIndex ACL\) |
| `indexName` | string | Yes | Name of the Algolia index to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the index deletion |
| `deletedAt` | string | Timestamp when the index was deleted |
### `algolia_copy_move_index`
Copy or move an Algolia index to a new destination
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key |
| `indexName` | string | Yes | Name of the source index |
| `operation` | string | Yes | Operation to perform: "copy" or "move" |
| `destination` | string | Yes | Name of the destination index |
| `scope` | json | No | Array of scopes to copy \(only for "copy" operation\): \["settings", "synonyms", "rules"\]. Omit to copy everything including records. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the copy/move operation |
| `updatedAt` | string | Timestamp when the operation was performed |
### `algolia_clear_records`
Clear all records from an Algolia index while keeping settings, synonyms, and rules
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key \(must have deleteIndex ACL\) |
| `indexName` | string | Yes | Name of the Algolia index to clear |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the clear operation |
| `updatedAt` | string | Timestamp when the records were cleared |
### `algolia_delete_by_filter`
Delete all records matching a filter from an Algolia index
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `applicationId` | string | Yes | Algolia Application ID |
| `apiKey` | string | Yes | Algolia Admin API Key \(must have deleteIndex ACL\) |
| `indexName` | string | Yes | Name of the Algolia index |
| `filters` | string | No | Filter expression to match records for deletion \(e.g., "category:outdated"\) |
| `facetFilters` | json | No | Array of facet filters \(e.g., \["brand:Acme"\]\) |
| `numericFilters` | json | No | Array of numeric filters \(e.g., \["price &gt; 100"\]\) |
| `tagFilters` | json | No | Array of tag filters using the _tags attribute \(e.g., \["published"\]\) |
| `aroundLatLng` | string | No | Coordinates for geo-search filter \(e.g., "40.71,-74.01"\) |
| `aroundRadius` | number | No | Maximum radius in meters for geo-search, or "all" for unlimited |
| `insideBoundingBox` | json | No | Bounding box coordinates as \[\[lat1, lng1, lat2, lng2\]\] for geo-search filter |
| `insidePolygon` | json | No | Polygon coordinates as \[\[lat1, lng1, lat2, lng2, lat3, lng3, ...\]\] for geo-search filter |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `taskID` | number | Algolia task ID for tracking the delete-by-filter operation |
| `updatedAt` | string | Timestamp when the operation was performed |
@@ -5,6 +5,7 @@
"ahrefs",
"airtable",
"airweave",
"algolia",
"apify",
"apollo",
"arxiv",
@@ -93,8 +94,10 @@
"qdrant",
"rds",
"reddit",
"redis",
"reducto",
"resend",
"revenuecat",
"s3",
"salesforce",
"search",
@@ -125,6 +128,7 @@
"twilio_sms",
"twilio_voice",
"typeform",
"upstash",
"vercel",
"video_generator",
"vision",
+452
View File
@@ -0,0 +1,452 @@
---
title: Redis
description: Key-value operations with Redis
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="redis"
color="#FF4438"
/>
{/* MANUAL-CONTENT-START:intro */}
[Redis](https://redis.io/) is an open-source, in-memory data structure store, used as a distributed key-value database, cache, and message broker. Redis supports a variety of data structures including strings, hashes, lists, sets, and more, making it highly flexible for different application scenarios.
With Redis, you can:
- **Store and retrieve key-value data instantly**: Use Redis as a fast database, cache, or session store for high performance.
- **Work with multiple data structures**: Manage not just strings, but also lists, hashes, sets, sorted sets, streams, and bitmaps.
- **Perform atomic operations**: Safely manipulate data using atomic commands and transactions.
- **Support pub/sub messaging**: Use Redis’s publisher/subscriber features for real-time event handling and messaging.
- **Set automatic expiration policies**: Assign TTLs to keys for caching and time-sensitive data.
- **Scale horizontally**: Use Redis Cluster for sharding, high availability, and scalable workloads.
In Sim, the Redis integration lets your agents connect to any Redis-compatible instance to perform key-value, hash, list, and utility operations. You can build workflows that involve storing, retrieving, or manipulating data in Redis, or manage your app’s cache, sessions, or real-time messaging, directly within your Sim workspace.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Connect to any Redis instance to perform key-value, hash, list, and utility operations via a direct connection.
## Tools
### `redis_get`
Get the value of a key from Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was retrieved |
| `value` | string | The value of the key, or null if the key does not exist |
### `redis_set`
Set the value of a key in Redis with an optional expiration time in seconds.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to set |
| `value` | string | Yes | The value to store |
| `ex` | number | No | Expiration time in seconds \(optional\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was set |
| `result` | string | The result of the SET operation \(typically "OK"\) |
### `redis_delete`
Delete a key from Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was deleted |
| `deletedCount` | number | Number of keys deleted \(0 if key did not exist, 1 if deleted\) |
### `redis_keys`
List all keys matching a pattern in Redis. Avoid using on large databases in production; use the Redis Command tool with SCAN for large key spaces.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `pattern` | string | No | Pattern to match keys \(default: * for all keys\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pattern` | string | The pattern used to match keys |
| `keys` | array | List of keys matching the pattern |
| `count` | number | Number of keys found |
### `redis_command`
Execute a raw Redis command as a JSON array (e.g. [
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `command` | string | Yes | Redis command as a JSON array \(e.g. \["SET", "key", "value"\]\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `command` | string | The command that was executed |
| `result` | json | The result of the command |
### `redis_hset`
Set a field in a hash stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The hash key |
| `field` | string | Yes | The field name within the hash |
| `value` | string | Yes | The value to set for the field |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The hash key |
| `field` | string | The field that was set |
| `result` | number | Number of fields added \(1 if new, 0 if updated\) |
### `redis_hget`
Get the value of a field in a hash stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The hash key |
| `field` | string | Yes | The field name to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The hash key |
| `field` | string | The field that was retrieved |
| `value` | string | The field value, or null if the field or key does not exist |
### `redis_hgetall`
Get all fields and values of a hash stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The hash key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The hash key |
| `fields` | object | All field-value pairs in the hash as a key-value object. Empty object if the key does not exist. |
| `fieldCount` | number | Number of fields in the hash |
### `redis_hdel`
Delete a field from a hash stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The hash key |
| `field` | string | Yes | The field name to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The hash key |
| `field` | string | The field that was deleted |
| `deleted` | number | Number of fields removed \(1 if deleted, 0 if field did not exist\) |
### `redis_incr`
Increment the integer value of a key by one in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to increment |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was incremented |
| `value` | number | The new value after increment |
### `redis_incrby`
Increment the integer value of a key by a given amount in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to increment |
| `increment` | number | Yes | Amount to increment by \(negative to decrement\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was incremented |
| `value` | number | The new value after increment |
### `redis_expire`
Set an expiration time (in seconds) on a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to set expiration on |
| `seconds` | number | Yes | Timeout in seconds |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that expiration was set on |
| `result` | number | 1 if the timeout was set, 0 if the key does not exist |
### `redis_ttl`
Get the remaining time to live (in seconds) of a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to check TTL for |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was checked |
| `ttl` | number | Remaining TTL in seconds. Positive integer if TTL set, -1 if no expiration, -2 if key does not exist. |
### `redis_persist`
Remove the expiration from a key in Redis, making it persist indefinitely.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to persist |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was persisted |
| `result` | number | 1 if the expiration was removed, 0 if the key does not exist or has no expiration |
### `redis_lpush`
Prepend a value to a list stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The list key |
| `value` | string | Yes | The value to prepend |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `length` | number | Length of the list after the push |
### `redis_rpush`
Append a value to the end of a list stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The list key |
| `value` | string | Yes | The value to append |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `length` | number | Length of the list after the push |
### `redis_lpop`
Remove and return the first element of a list stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The list key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `value` | string | The removed element, or null if the list is empty |
### `redis_rpop`
Remove and return the last element of a list stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The list key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `value` | string | The removed element, or null if the list is empty |
### `redis_llen`
Get the length of a list stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The list key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `length` | number | The length of the list, or 0 if the key does not exist |
### `redis_lrange`
Get a range of elements from a list stored at a key in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The list key |
| `start` | number | Yes | Start index \(0-based\) |
| `stop` | number | Yes | Stop index \(-1 for all elements\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `values` | array | List elements in the specified range |
| `count` | number | Number of elements returned |
### `redis_exists`
Check if a key exists in Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to check |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was checked |
| `exists` | boolean | Whether the key exists \(true\) or not \(false\) |
### `redis_setnx`
Set the value of a key in Redis only if the key does not already exist.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Redis connection URL \(e.g. redis://user:password@host:port\) |
| `key` | string | Yes | The key to set |
| `value` | string | Yes | The value to store |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was set |
| `wasSet` | boolean | Whether the key was set \(true\) or already existed \(false\) |
@@ -0,0 +1,456 @@
---
title: RevenueCat
description: Manage in-app subscriptions and entitlements
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="revenuecat"
color="#F25A5A"
/>
{/* MANUAL-CONTENT-START:intro */}
[RevenueCat](https://www.revenuecat.com/) is a subscription management platform that enables you to easily set up, manage, and analyze in-app subscriptions for your apps. With RevenueCat, you can handle the complexities of in-app purchases across platforms like iOS, Android, and web—all through a single unified API.
With RevenueCat, you can:
- **Manage subscribers**: Track user subscriptions, entitlements, and purchases across all platforms in real time
- **Simplify implementation**: Integrate RevenueCat’s SDKs to abstract away App Store and Play Store purchase logic
- **Automate entitlement logic**: Define and manage what features users should receive when they purchase or renew
- **Analyze revenue**: Access dashboards and analytics to view churn, LTV, revenue, active subscriptions, and more
- **Grant or revoke entitlements**: Manually adjust user access (for example, for customer support or promotions)
- **Operate globally**: Support purchases, refunds, and promotions worldwide with ease
In Sim, the RevenueCat integration allows your agents to fetch and manage subscriber data, review and update entitlements, and automate subscription-related workflows. Use RevenueCat to centralize subscription operations for your apps directly within your Sim workspace.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate RevenueCat into the workflow. Manage subscribers, entitlements, offerings, and Google Play subscriptions. Retrieve customer subscription status, grant or revoke promotional entitlements, record purchases, update subscriber attributes, and manage Google Play subscription billing.
## Tools
### `revenuecat_get_customer`
Retrieve subscriber information by app user ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `subscriber` | object | The subscriber object with subscriptions and entitlements |
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
| ↳ `original_app_user_id` | string | Original app user ID |
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
| ↳ `store_transaction_id` | string | Store transaction identifier |
| ↳ `original_transaction_id` | string | Original transaction identifier |
| ↳ `purchase_date` | string | ISO 8601 purchase date |
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
| ↳ `grant_date` | string | ISO 8601 grant date |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `product_identifier` | string | Product identifier |
| ↳ `is_active` | boolean | Whether the entitlement is active |
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
| ↳ `store` | string | Store the entitlement was granted from |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
| `metadata` | object | Subscriber summary metadata |
| ↳ `app_user_id` | string | The app user ID |
| ↳ `first_seen` | string | ISO 8601 date when the subscriber was first seen |
| ↳ `active_entitlements` | number | Number of active entitlements |
| ↳ `active_subscriptions` | number | Number of active subscriptions |
### `revenuecat_delete_customer`
Permanently delete a subscriber and all associated data
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the subscriber was deleted |
| `app_user_id` | string | The deleted app user ID |
### `revenuecat_create_purchase`
Record a purchase (receipt) for a subscriber via the REST API
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat API key \(public or secret\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
| `fetchToken` | string | Yes | The receipt token or purchase token from the store \(App Store receipt, Google Play purchase token, or Stripe subscription ID\) |
| `productId` | string | Yes | The product identifier for the purchase |
| `price` | number | No | The price of the product in the currency specified |
| `currency` | string | No | ISO 4217 currency code \(e.g., USD, EUR\) |
| `isRestore` | boolean | No | Whether this is a restore of a previous purchase |
| `platform` | string | No | Platform of the purchase \(ios, android, amazon, macos, stripe\). Required for Stripe and Paddle purchases. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `subscriber` | object | The updated subscriber object after recording the purchase |
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
| ↳ `original_app_user_id` | string | Original app user ID |
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
| ↳ `store_transaction_id` | string | Store transaction identifier |
| ↳ `original_transaction_id` | string | Original transaction identifier |
| ↳ `purchase_date` | string | ISO 8601 purchase date |
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
| ↳ `grant_date` | string | ISO 8601 grant date |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `product_identifier` | string | Product identifier |
| ↳ `is_active` | boolean | Whether the entitlement is active |
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
| ↳ `store` | string | Store the entitlement was granted from |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
### `revenuecat_grant_entitlement`
Grant a promotional entitlement to a subscriber
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
| `entitlementIdentifier` | string | Yes | The entitlement identifier to grant |
| `duration` | string | Yes | Duration of the entitlement \(daily, three_day, weekly, monthly, two_month, three_month, six_month, yearly, lifetime\) |
| `startTimeMs` | number | No | Optional start time in milliseconds since Unix epoch. Set to a past time to achieve custom durations shorter than daily. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `subscriber` | object | The updated subscriber object after granting the entitlement |
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
| ↳ `original_app_user_id` | string | Original app user ID |
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
| ↳ `store_transaction_id` | string | Store transaction identifier |
| ↳ `original_transaction_id` | string | Original transaction identifier |
| ↳ `purchase_date` | string | ISO 8601 purchase date |
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
| ↳ `grant_date` | string | ISO 8601 grant date |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `product_identifier` | string | Product identifier |
| ↳ `is_active` | boolean | Whether the entitlement is active |
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
| ↳ `store` | string | Store the entitlement was granted from |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
### `revenuecat_revoke_entitlement`
Revoke all promotional entitlements for a specific entitlement identifier
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
| `entitlementIdentifier` | string | Yes | The entitlement identifier to revoke |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `subscriber` | object | The updated subscriber object after revoking the entitlement |
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
| ↳ `original_app_user_id` | string | Original app user ID |
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
| ↳ `store_transaction_id` | string | Store transaction identifier |
| ↳ `original_transaction_id` | string | Original transaction identifier |
| ↳ `purchase_date` | string | ISO 8601 purchase date |
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
| ↳ `grant_date` | string | ISO 8601 grant date |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `product_identifier` | string | Product identifier |
| ↳ `is_active` | boolean | Whether the entitlement is active |
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
| ↳ `store` | string | Store the entitlement was granted from |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
### `revenuecat_list_offerings`
List all offerings configured for the project
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat API key |
| `appUserId` | string | Yes | An app user ID to retrieve offerings for |
| `platform` | string | No | Platform to filter offerings \(ios, android, stripe, etc.\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `current_offering_id` | string | The identifier of the current offering |
| `offerings` | array | List of offerings |
| ↳ `identifier` | string | Offering identifier |
| ↳ `description` | string | Offering description |
| ↳ `packages` | array | List of packages in the offering |
| ↳ `identifier` | string | Package identifier |
| ↳ `platform_product_identifier` | string | Platform-specific product identifier |
| `metadata` | object | Offerings metadata |
| ↳ `count` | number | Number of offerings returned |
| ↳ `current_offering_id` | string | Current offering identifier |
### `revenuecat_update_subscriber_attributes`
Update custom subscriber attributes (e.g., $email, $displayName, or custom key-value pairs)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
| `attributes` | json | Yes | JSON object of attributes to set. Each key maps to an object with a "value" field. Example: \{"$email": \{"value": "user@example.com"\}, "$displayName": \{"value": "John"\}\} |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `updated` | boolean | Whether the subscriber attributes were successfully updated |
| `app_user_id` | string | The app user ID of the updated subscriber |
### `revenuecat_defer_google_subscription`
Defer a Google Play subscription by extending its billing date by a number of days (Google Play only)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
| `productId` | string | Yes | The Google Play product identifier of the subscription to defer \(use the part before the colon for products set up after Feb 2023\) |
| `extendByDays` | number | Yes | Number of days to extend the subscription by \(1-365\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `subscriber` | object | The updated subscriber object after deferring the Google subscription |
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
| ↳ `original_app_user_id` | string | Original app user ID |
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
| ↳ `store_transaction_id` | string | Store transaction identifier |
| ↳ `original_transaction_id` | string | Original transaction identifier |
| ↳ `purchase_date` | string | ISO 8601 purchase date |
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
| ↳ `grant_date` | string | ISO 8601 grant date |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `product_identifier` | string | Product identifier |
| ↳ `is_active` | boolean | Whether the entitlement is active |
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
| ↳ `store` | string | Store the entitlement was granted from |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
### `revenuecat_refund_google_subscription`
Refund and optionally revoke a Google Play subscription (Google Play only)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
| `productId` | string | Yes | The Google Play product identifier of the subscription to refund |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `subscriber` | object | The updated subscriber object after refunding the Google subscription |
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
| ↳ `original_app_user_id` | string | Original app user ID |
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
| ↳ `store_transaction_id` | string | Store transaction identifier |
| ↳ `original_transaction_id` | string | Original transaction identifier |
| ↳ `purchase_date` | string | ISO 8601 purchase date |
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
| ↳ `grant_date` | string | ISO 8601 grant date |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `product_identifier` | string | Product identifier |
| ↳ `is_active` | boolean | Whether the entitlement is active |
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
| ↳ `store` | string | Store the entitlement was granted from |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
### `revenuecat_revoke_google_subscription`
Immediately revoke access to a Google Play subscription and issue a refund (Google Play only)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | RevenueCat secret API key \(sk_...\) |
| `appUserId` | string | Yes | The app user ID of the subscriber |
| `productId` | string | Yes | The Google Play product identifier of the subscription to revoke |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `subscriber` | object | The updated subscriber object after revoking the Google subscription |
| ↳ `first_seen` | string | ISO 8601 date when subscriber was first seen |
| ↳ `original_app_user_id` | string | Original app user ID |
| ↳ `original_purchase_date` | string | ISO 8601 date of original purchase |
| ↳ `management_url` | string | URL for managing the subscriber subscriptions |
| ↳ `subscriptions` | object | Map of product identifiers to subscription objects |
| ↳ `store_transaction_id` | string | Store transaction identifier |
| ↳ `original_transaction_id` | string | Original transaction identifier |
| ↳ `purchase_date` | string | ISO 8601 purchase date |
| ↳ `original_purchase_date` | string | ISO 8601 date of the original purchase |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `is_sandbox` | boolean | Whether this is a sandbox purchase |
| ↳ `unsubscribe_detected_at` | string | ISO 8601 date when unsubscribe was detected |
| ↳ `billing_issues_detected_at` | string | ISO 8601 date when billing issues were detected |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `ownership_type` | string | Ownership type \(purchased, family_shared\) |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional, prepaid\) |
| ↳ `store` | string | Store the subscription was purchased from \(app_store, play_store, stripe, etc.\) |
| ↳ `refunded_at` | string | ISO 8601 date when subscription was refunded |
| ↳ `auto_resume_date` | string | ISO 8601 date when a paused subscription will auto-resume |
| ↳ `product_plan_identifier` | string | Google Play base plan identifier \(for products set up after Feb 2023\) |
| ↳ `entitlements` | object | Map of entitlement identifiers to entitlement objects |
| ↳ `grant_date` | string | ISO 8601 grant date |
| ↳ `expires_date` | string | ISO 8601 expiration date |
| ↳ `product_identifier` | string | Product identifier |
| ↳ `is_active` | boolean | Whether the entitlement is active |
| ↳ `will_renew` | boolean | Whether the entitlement will renew |
| ↳ `period_type` | string | Period type \(normal, trial, intro, promotional\) |
| ↳ `purchase_date` | string | ISO 8601 date of the latest purchase or renewal |
| ↳ `store` | string | Store the entitlement was granted from |
| ↳ `grace_period_expires_date` | string | ISO 8601 grace period expiration date |
| ↳ `non_subscriptions` | object | Map of non-subscription product identifiers to arrays of purchase objects |
+357
View File
@@ -0,0 +1,357 @@
---
title: Upstash
description: Serverless Redis with Upstash
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="upstash"
color="#181C1E"
/>
{/* MANUAL-CONTENT-START:intro */}
[Upstash](https://upstash.com/) is a serverless data platform designed for modern applications that need fast, simple, and scalable data storage with minimal setup. Upstash specializes in providing Redis and Kafka as fully managed, pay-per-request cloud services, making it a popular choice for developers building serverless, edge, and event-driven architectures.
With Upstash Redis, you can:
- **Store and retrieve data instantly**: Read and write key-value pairs, hashes, lists, sets, and more—all over a high-performance REST API.
- **Scale serverlessly**: No infrastructure to manage. Upstash automatically scales with your app and charges only for what you use.
- **Access globally**: Deploy near your users with multi-region support and global distribution.
- **Integrate easily**: Use Upstash’s REST API in serverless functions, edge workers, Next.js, Vercel, Cloudflare Workers, and more.
- **Automate with scripts**: Run Lua scripts for advanced transactions and automation.
- **Ensure security**: Protect your data with built-in authentication and TLS encryption.
In Sim, the Upstash integration empowers your agents and workflows to read, write, and manage data in Upstash Redis using simple, unified commands—perfect for building scalable automations, caching results, managing queues, and more, all without dealing with server management.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Connect to Upstash Redis to perform key-value, hash, list, and utility operations via the REST API.
## Tools
### `upstash_redis_get`
Get the value of a key from Upstash Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was retrieved |
| `value` | json | The value of the key \(string\), or null if not found |
### `upstash_redis_set`
Set the value of a key in Upstash Redis with an optional expiration time in seconds.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to set |
| `value` | string | Yes | The value to store |
| `ex` | number | No | Expiration time in seconds \(optional\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was set |
| `result` | string | The result of the SET operation \(typically "OK"\) |
### `upstash_redis_delete`
Delete a key from Upstash Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was deleted |
| `deletedCount` | number | Number of keys deleted \(0 if key did not exist, 1 if deleted\) |
### `upstash_redis_keys`
List keys matching a pattern in Upstash Redis. Defaults to listing all keys (*).
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `pattern` | string | No | Pattern to match keys \(e.g., "user:*"\). Defaults to "*" for all keys. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pattern` | string | The pattern used to match keys |
| `keys` | array | List of keys matching the pattern |
| `count` | number | Number of keys found |
### `upstash_redis_command`
Execute an arbitrary Redis command against Upstash Redis. Pass the full command as a JSON array (e.g., [
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `command` | string | Yes | Redis command as a JSON array \(e.g., \["HSET", "myhash", "field1", "value1"\]\) or a simple command string \(e.g., "PING"\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `command` | string | The command that was executed |
| `result` | json | The result of the Redis command |
### `upstash_redis_hset`
Set a field in a hash stored at a key in Upstash Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The hash key |
| `field` | string | Yes | The field name within the hash |
| `value` | string | Yes | The value to store in the hash field |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The hash key |
| `field` | string | The field that was set |
| `result` | number | Number of new fields added \(0 if field was updated, 1 if new\) |
### `upstash_redis_hget`
Get the value of a field in a hash stored at a key in Upstash Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The hash key |
| `field` | string | Yes | The field name to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The hash key |
| `field` | string | The field that was retrieved |
| `value` | json | The value of the hash field \(string\), or null if not found |
### `upstash_redis_hgetall`
Get all fields and values of a hash stored at a key in Upstash Redis.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The hash key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The hash key |
| `fields` | object | All field-value pairs in the hash, keyed by field name |
| `fieldCount` | number | Number of fields in the hash |
### `upstash_redis_incr`
Atomically increment the integer value of a key by one in Upstash Redis. If the key does not exist, it is set to 0 before incrementing.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to increment |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was incremented |
| `value` | number | The new value after incrementing |
### `upstash_redis_expire`
Set a timeout on a key in Upstash Redis. After the timeout, the key is deleted.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to set expiration on |
| `seconds` | number | Yes | Timeout in seconds |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that expiration was set on |
| `result` | number | 1 if the timeout was set, 0 if the key does not exist |
### `upstash_redis_ttl`
Get the remaining time to live of a key in Upstash Redis. Returns -1 if the key has no expiration, -2 if the key does not exist.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to check TTL for |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key checked |
| `ttl` | number | Remaining TTL in seconds. Positive integer if the key has a TTL set, -1 if the key exists with no expiration, -2 if the key does not exist. |
### `upstash_redis_lpush`
Prepend a value to the beginning of a list in Upstash Redis. Creates the list if it does not exist.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The list key |
| `value` | string | Yes | The value to prepend to the list |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `length` | number | The length of the list after the push |
### `upstash_redis_lrange`
Get a range of elements from a list in Upstash Redis. Use 0 and -1 for start and stop to get all elements.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The list key |
| `start` | number | Yes | Start index \(0-based, negative values count from end\) |
| `stop` | number | Yes | Stop index \(inclusive, -1 for last element\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The list key |
| `values` | array | List of elements in the specified range |
| `count` | number | Number of elements returned |
### `upstash_redis_exists`
Check if a key exists in Upstash Redis. Returns true if the key exists, false otherwise.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to check |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was checked |
| `exists` | boolean | Whether the key exists \(true\) or not \(false\) |
### `upstash_redis_setnx`
Set the value of a key only if it does not already exist. Returns true if the key was set, false if it already existed.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to set |
| `value` | string | Yes | The value to store if the key does not exist |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was attempted to set |
| `wasSet` | boolean | Whether the key was set \(true\) or already existed \(false\) |
### `upstash_redis_incrby`
Increment the integer value of a key by a given amount. Use a negative value to decrement. If the key does not exist, it is set to 0 before the operation.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `restUrl` | string | Yes | Upstash Redis REST URL |
| `restToken` | string | Yes | Upstash Redis REST Token |
| `key` | string | Yes | The key to increment |
| `increment` | number | Yes | Amount to increment by \(use negative value to decrement\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `key` | string | The key that was incremented |
| `value` | number | The new value after incrementing |
@@ -0,0 +1,57 @@
import { createLogger } from '@sim/logger'
import Redis from 'ioredis'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
const logger = createLogger('RedisAPI')
const RequestSchema = z.object({
url: z.string().min(1, 'Redis connection URL is required'),
command: z.string().min(1, 'Redis command is required'),
args: z.array(z.union([z.string(), z.number()])).default([]),
})
export async function POST(request: NextRequest) {
let client: Redis | null = null
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const { url, command, args } = RequestSchema.parse(body)
client = new Redis(url, {
connectTimeout: 10000,
commandTimeout: 10000,
maxRetriesPerRequest: 1,
lazyConnect: true,
})
await client.connect()
const cmd = command.toUpperCase()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = await (client as any).call(cmd, ...args)
await client.quit()
client = null
return NextResponse.json({ result })
} catch (error) {
logger.error('Redis command failed', { error })
const errorMessage = error instanceof Error ? error.message : 'Redis command failed'
return NextResponse.json({ error: errorMessage }, { status: 500 })
} finally {
if (client) {
try {
await client.quit()
} catch {
client.disconnect()
}
}
}
}
+646
View File
@@ -0,0 +1,646 @@
import { AlgoliaIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
export const AlgoliaBlock: BlockConfig = {
type: 'algolia',
name: 'Algolia',
description: 'Search and manage Algolia indices',
longDescription:
'Integrate Algolia into your workflow. Search indices, manage records (add, update, delete, browse), configure index settings, and perform batch operations.',
docsLink: 'https://docs.sim.ai/tools/algolia',
category: 'tools',
bgColor: '#003DFF',
icon: AlgoliaIcon,
authMode: AuthMode.ApiKey,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Search', id: 'search' },
{ label: 'Add Record', id: 'add_record' },
{ label: 'Get Record', id: 'get_record' },
{ label: 'Get Records', id: 'get_records' },
{ label: 'Partial Update Record', id: 'partial_update_record' },
{ label: 'Delete Record', id: 'delete_record' },
{ label: 'Browse Records', id: 'browse_records' },
{ label: 'Batch Operations', id: 'batch_operations' },
{ label: 'List Indices', id: 'list_indices' },
{ label: 'Get Settings', id: 'get_settings' },
{ label: 'Update Settings', id: 'update_settings' },
{ label: 'Delete Index', id: 'delete_index' },
{ label: 'Copy/Move Index', id: 'copy_move_index' },
{ label: 'Clear Records', id: 'clear_records' },
{ label: 'Delete By Filter', id: 'delete_by_filter' },
],
value: () => 'search',
},
// Index name - needed for all except list_indices
{
id: 'indexName',
title: 'Index Name',
type: 'short-input',
placeholder: 'my_index',
condition: { field: 'operation', value: 'list_indices', not: true },
required: { field: 'operation', value: 'list_indices', not: true },
},
// Search fields
{
id: 'query',
title: 'Search Query',
type: 'short-input',
placeholder: 'Enter search query',
condition: { field: 'operation', value: ['search', 'browse_records'] },
required: { field: 'operation', value: 'search' },
},
{
id: 'hitsPerPage',
title: 'Hits Per Page',
type: 'short-input',
placeholder: '20',
condition: { field: 'operation', value: ['search', 'browse_records'] },
mode: 'advanced',
},
{
id: 'page',
title: 'Page',
type: 'short-input',
placeholder: '0',
condition: { field: 'operation', value: 'search' },
mode: 'advanced',
},
{
id: 'filters',
title: 'Filters',
type: 'short-input',
placeholder: 'category:electronics AND price < 100',
condition: { field: 'operation', value: ['search', 'browse_records'] },
wandConfig: {
enabled: true,
prompt: `Generate an Algolia filter expression based on the user's description.
Available operators: AND, OR, NOT
Comparison: =, !=, <, >, <=, >=
Facet filters: attribute:value
Numeric filters: attribute operator value
Boolean filters: attribute:true / attribute:false
Tag filters: _tags:value
Examples:
- "category:electronics AND price < 100"
- "brand:Apple OR brand:Samsung"
- "inStock:true AND NOT category:deprecated"
- "(category:electronics OR category:books) AND price >= 10"
Return ONLY the filter string, no quotes or explanation.`,
},
},
{
id: 'attributesToRetrieve',
title: 'Attributes to Retrieve',
type: 'short-input',
placeholder: 'name,description,price',
condition: { field: 'operation', value: ['search', 'get_record', 'browse_records'] },
mode: 'advanced',
},
// Browse cursor
{
id: 'cursor',
title: 'Cursor',
type: 'short-input',
placeholder: 'Cursor from previous browse response',
condition: { field: 'operation', value: 'browse_records' },
mode: 'advanced',
},
// Add record fields
{
id: 'record',
title: 'Record',
type: 'long-input',
placeholder: '{"name": "Product", "price": 29.99}',
condition: { field: 'operation', value: 'add_record' },
required: { field: 'operation', value: 'add_record' },
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for an Algolia record based on the user's description.
### CONTEXT
{context}
### GUIDELINES
- Return ONLY a valid JSON object starting with { and ending with }
- Include relevant attributes as key-value pairs
- Do NOT include objectID unless the user explicitly specifies one
- Use appropriate types: strings, numbers, booleans, arrays
### EXAMPLE
User: "A product with name, price, and categories"
Output:
{"name": "Example Product", "price": 29.99, "categories": ["electronics", "gadgets"]}
Return ONLY the JSON object.`,
placeholder: 'Describe the record to add...',
generationType: 'json-object',
},
},
// Partial update fields
{
id: 'attributes',
title: 'Attributes to Update',
type: 'long-input',
placeholder: '{"price": 24.99, "stock": {"_operation": "Decrement", "value": 1}}',
condition: { field: 'operation', value: 'partial_update_record' },
required: { field: 'operation', value: 'partial_update_record' },
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for an Algolia partial update based on the user's description.
### CONTEXT
{context}
### GUIDELINES
- Return ONLY a valid JSON object starting with { and ending with }
- For simple updates, use key-value pairs: {"price": 24.99}
- For built-in operations, use the _operation syntax:
- Increment: {"count": {"_operation": "Increment", "value": 1}}
- Decrement: {"stock": {"_operation": "Decrement", "value": 1}}
- Add to array: {"tags": {"_operation": "Add", "value": "new-tag"}}
- Remove from array: {"tags": {"_operation": "Remove", "value": "old-tag"}}
- AddUnique: {"tags": {"_operation": "AddUnique", "value": "unique-tag"}}
- IncrementFrom: {"version": {"_operation": "IncrementFrom", "value": 0}}
- IncrementSet: {"views": {"_operation": "IncrementSet", "value": 1}}
### EXAMPLE
User: "Decrease stock by 2 and add a sale tag"
Output:
{"stock": {"_operation": "Decrement", "value": 2}, "tags": {"_operation": "Add", "value": "sale"}}
Return ONLY the JSON object.`,
placeholder: 'Describe the attributes to update...',
generationType: 'json-object',
},
},
{
id: 'createIfNotExists',
title: 'Create If Not Exists',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
condition: { field: 'operation', value: 'partial_update_record' },
value: () => 'true',
mode: 'advanced',
},
// Batch operations field
{
id: 'requests',
title: 'Batch Requests',
type: 'long-input',
placeholder:
'[{"action": "addObject", "body": {"name": "Item"}}, {"action": "deleteObject", "body": {"objectID": "123"}}]',
condition: { field: 'operation', value: 'batch_operations' },
required: { field: 'operation', value: 'batch_operations' },
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of Algolia batch operations based on the user's description.
### CONTEXT
{context}
### GUIDELINES
- Return ONLY a valid JSON array starting with [ and ending with ]
- Each item must have "action" and "body" properties
- Valid actions: addObject, updateObject, partialUpdateObject, partialUpdateObjectNoCreate, deleteObject, delete, clear
- For deleteObject, body must include objectID
- For updateObject, body must include objectID
- For addObject, objectID is optional (auto-generated if omitted)
### EXAMPLE
User: "Add two products and delete one with ID old-123"
Output:
[
{"action": "addObject", "body": {"name": "Product A", "price": 19.99}},
{"action": "addObject", "body": {"name": "Product B", "price": 29.99}},
{"action": "deleteObject", "body": {"objectID": "old-123"}}
]
Return ONLY the JSON array.`,
placeholder: 'Describe the batch operations to perform...',
generationType: 'json-object',
},
},
// Update settings fields
{
id: 'settings',
title: 'Settings',
type: 'long-input',
placeholder:
'{"searchableAttributes": ["name", "description"], "customRanking": ["desc(popularity)"]}',
condition: { field: 'operation', value: 'update_settings' },
required: { field: 'operation', value: 'update_settings' },
wandConfig: {
enabled: true,
prompt: `Generate a valid Algolia index settings JSON object based on the user's description.
### CONTEXT
{context}
### GUIDELINES
- Return ONLY a valid JSON object starting with { and ending with }
- Common settings include:
- searchableAttributes: array of attribute names (ordered by priority)
- attributesForFaceting: array of attributes for filtering/faceting (prefix with "filterOnly(" or "searchable(" as needed)
- customRanking: array of "asc(attr)" or "desc(attr)" expressions
- ranking: array of ranking criteria (e.g., "typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom")
- replicas: array of replica index names
- hitsPerPage: number of results per page
- paginationLimitedTo: max pagination depth
- highlightPreTag / highlightPostTag: HTML tags for highlighting
### EXAMPLE
User: "Make name and description searchable, add category faceting, rank by popularity"
Output:
{"searchableAttributes": ["name", "description"], "attributesForFaceting": ["category"], "customRanking": ["desc(popularity)"]}
Return ONLY the JSON object.`,
placeholder: 'Describe the settings to apply...',
generationType: 'json-object',
},
},
{
id: 'forwardToReplicas',
title: 'Forward to Replicas',
type: 'dropdown',
options: [
{ label: 'No', id: 'false' },
{ label: 'Yes', id: 'true' },
],
condition: { field: 'operation', value: 'update_settings' },
value: () => 'false',
mode: 'advanced',
},
// Copy/Move index fields
{
id: 'copyMoveOperation',
title: 'Copy or Move',
type: 'dropdown',
options: [
{ label: 'Copy', id: 'copy' },
{ label: 'Move', id: 'move' },
],
condition: { field: 'operation', value: 'copy_move_index' },
value: () => 'copy',
},
{
id: 'destination',
title: 'Destination Index',
type: 'short-input',
placeholder: 'my_index_backup',
condition: { field: 'operation', value: 'copy_move_index' },
required: { field: 'operation', value: 'copy_move_index' },
},
{
id: 'scope',
title: 'Scope (Copy Only)',
type: 'short-input',
placeholder: '["settings", "synonyms", "rules"]',
condition: { field: 'operation', value: 'copy_move_index' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of Algolia copy scopes based on the user's description.
### CONTEXT
{context}
### GUIDELINES
- Return ONLY a valid JSON array
- Valid scope values: "settings", "synonyms", "rules"
- Omitting scope copies everything including records
- Only applies to copy operations, not move
### EXAMPLE
User: "Copy only settings and synonyms"
Output:
["settings", "synonyms"]
Return ONLY the JSON array.`,
placeholder: 'Describe what to copy...',
generationType: 'json-object',
},
},
// Delete by filter fields
{
id: 'deleteFilters',
title: 'Filter Expression',
type: 'short-input',
placeholder: 'category:outdated AND price < 10',
condition: { field: 'operation', value: 'delete_by_filter' },
required: { field: 'operation', value: 'delete_by_filter' },
wandConfig: {
enabled: true,
prompt: `Generate an Algolia filter expression for deleting records based on the user's description.
Available operators: AND, OR, NOT
Comparison: =, !=, <, >, <=, >=
Facet filters: attribute:value
Numeric filters: attribute operator value
Examples:
- "category:outdated AND price < 10"
- "status:archived OR lastUpdated < 1609459200"
- "NOT category:active"
Return ONLY the filter string, no quotes or explanation.`,
},
},
{
id: 'facetFilters',
title: 'Facet Filters',
type: 'short-input',
placeholder: '["brand:Acme"]',
condition: { field: 'operation', value: 'delete_by_filter' },
mode: 'advanced',
},
{
id: 'numericFilters',
title: 'Numeric Filters',
type: 'short-input',
placeholder: '["price > 100"]',
condition: { field: 'operation', value: 'delete_by_filter' },
mode: 'advanced',
},
{
id: 'tagFilters',
title: 'Tag Filters',
type: 'short-input',
placeholder: '["published", "archived"]',
condition: { field: 'operation', value: 'delete_by_filter' },
mode: 'advanced',
},
{
id: 'aroundLatLng',
title: 'Around Lat/Lng',
type: 'short-input',
placeholder: '40.71,-74.01',
condition: { field: 'operation', value: 'delete_by_filter' },
mode: 'advanced',
},
{
id: 'aroundRadius',
title: 'Around Radius (m)',
type: 'short-input',
placeholder: '1000 or "all"',
condition: { field: 'operation', value: 'delete_by_filter' },
mode: 'advanced',
},
{
id: 'insideBoundingBox',
title: 'Inside Bounding Box',
type: 'short-input',
placeholder: '[[47.3165,0.757,47.3424,0.8012]]',
condition: { field: 'operation', value: 'delete_by_filter' },
mode: 'advanced',
},
{
id: 'insidePolygon',
title: 'Inside Polygon',
type: 'short-input',
placeholder: '[[47.3165,0.757,47.3424,0.8012,47.33,0.78]]',
condition: { field: 'operation', value: 'delete_by_filter' },
mode: 'advanced',
},
// Get records (batch) field
{
id: 'getRecordsRequests',
title: 'Record Requests',
type: 'long-input',
placeholder: '[{"objectID": "id1"}, {"objectID": "id2", "attributesToRetrieve": ["name"]}]',
condition: { field: 'operation', value: 'get_records' },
required: { field: 'operation', value: 'get_records' },
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of Algolia get-records requests based on the user's description.
### CONTEXT
{context}
### GUIDELINES
- Return ONLY a valid JSON array starting with [ and ending with ]
- Each item must have "objectID" (required)
- Optionally include "indexName" to fetch from a different index
- Optionally include "attributesToRetrieve" as an array of attribute names
### EXAMPLE
User: "Get products with IDs abc and xyz, only returning name and price"
Output:
[{"objectID": "abc", "attributesToRetrieve": ["name", "price"]}, {"objectID": "xyz", "attributesToRetrieve": ["name", "price"]}]
Return ONLY the JSON array.`,
placeholder: 'Describe the records to retrieve...',
generationType: 'json-object',
},
},
// List indices pagination
{
id: 'listPage',
title: 'Page',
type: 'short-input',
placeholder: '0',
condition: { field: 'operation', value: 'list_indices' },
mode: 'advanced',
},
{
id: 'listHitsPerPage',
title: 'Indices Per Page',
type: 'short-input',
placeholder: '100',
condition: { field: 'operation', value: 'list_indices' },
mode: 'advanced',
},
// Object ID - for add (optional), get, partial update, delete
{
id: 'objectID',
title: 'Object ID',
type: 'short-input',
placeholder: 'my-record-123',
condition: {
field: 'operation',
value: ['add_record', 'get_record', 'partial_update_record', 'delete_record'],
},
required: {
field: 'operation',
value: ['get_record', 'partial_update_record', 'delete_record'],
},
},
// Common credentials
{
id: 'applicationId',
title: 'Application ID',
type: 'short-input',
placeholder: 'Your Algolia Application ID',
password: true,
required: true,
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Your Algolia API Key',
password: true,
required: true,
},
],
tools: {
access: [
'algolia_search',
'algolia_add_record',
'algolia_get_record',
'algolia_get_records',
'algolia_partial_update_record',
'algolia_delete_record',
'algolia_browse_records',
'algolia_batch_operations',
'algolia_list_indices',
'algolia_get_settings',
'algolia_update_settings',
'algolia_delete_index',
'algolia_copy_move_index',
'algolia_clear_records',
'algolia_delete_by_filter',
],
config: {
tool: (params: Record<string, unknown>) => {
const op = params.operation as string
if (op === 'partial_update_record') {
params.createIfNotExists = params.createIfNotExists !== 'false'
}
if (op === 'update_settings' && params.forwardToReplicas === 'true') {
params.forwardToReplicas = true
} else if (op === 'update_settings') {
params.forwardToReplicas = false
}
if (op === 'copy_move_index') {
params.operation = params.copyMoveOperation
}
if (op === 'delete_by_filter') {
params.filters = params.deleteFilters
}
if (op === 'get_records') {
params.requests = params.getRecordsRequests
}
if (op === 'list_indices') {
if (params.listPage !== undefined) params.page = params.listPage
if (params.listHitsPerPage !== undefined) params.hitsPerPage = params.listHitsPerPage
}
return `algolia_${op}`
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
indexName: { type: 'string', description: 'Algolia index name' },
query: { type: 'string', description: 'Search query' },
hitsPerPage: { type: 'string', description: 'Number of hits per page' },
page: { type: 'string', description: 'Page number' },
filters: { type: 'string', description: 'Algolia filter string' },
attributesToRetrieve: { type: 'string', description: 'Attributes to retrieve' },
cursor: { type: 'string', description: 'Browse cursor for pagination' },
record: { type: 'json', description: 'Record data to add' },
attributes: { type: 'json', description: 'Attributes to partially update' },
createIfNotExists: { type: 'string', description: 'Create record if not exists' },
requests: { type: 'json', description: 'Batch operation requests' },
settings: { type: 'json', description: 'Index settings to update' },
forwardToReplicas: { type: 'string', description: 'Forward settings to replicas' },
objectID: { type: 'string', description: 'Object ID' },
copyMoveOperation: { type: 'string', description: 'Copy or move operation' },
destination: { type: 'string', description: 'Destination index name' },
scope: { type: 'json', description: 'Scopes to copy (settings, synonyms, rules)' },
deleteFilters: { type: 'string', description: 'Filter expression for delete by filter' },
facetFilters: { type: 'json', description: 'Facet filters for delete by filter' },
numericFilters: { type: 'json', description: 'Numeric filters for delete by filter' },
tagFilters: {
type: 'json',
description: 'Tag filters using the _tags attribute for delete by filter',
},
aroundLatLng: { type: 'string', description: 'Geo-search coordinates (lat,lng)' },
aroundRadius: { type: 'string', description: 'Geo-search radius in meters or "all"' },
insideBoundingBox: { type: 'json', description: 'Bounding box coordinates for geo-search' },
insidePolygon: { type: 'json', description: 'Polygon coordinates for geo-search' },
getRecordsRequests: {
type: 'json',
description: 'Array of objects with objectID to retrieve multiple records',
},
listPage: { type: 'string', description: 'Page number for list indices pagination' },
listHitsPerPage: { type: 'string', description: 'Indices per page for list indices' },
applicationId: { type: 'string', description: 'Algolia Application ID' },
apiKey: { type: 'string', description: 'Algolia API Key' },
},
outputs: {
hits: { type: 'array', description: 'Search result hits or browsed records' },
nbHits: { type: 'number', description: 'Total number of hits' },
page: { type: 'number', description: 'Current page number (zero-based)' },
nbPages: { type: 'number', description: 'Total number of pages available' },
hitsPerPage: { type: 'number', description: 'Number of hits per page' },
processingTimeMS: {
type: 'number',
description: 'Server-side processing time in milliseconds',
},
query: { type: 'string', description: 'Search query that was executed' },
parsedQuery: { type: 'string', description: 'Query after normalization and stop word removal' },
facets: { type: 'json', description: 'Facet counts by facet name' },
facets_stats: {
type: 'json',
description: 'Statistics (min, max, avg, sum) for numeric facets',
},
exhaustive: { type: 'json', description: 'Exhaustiveness flags for the search results' },
taskID: { type: 'number', description: 'Algolia task ID for tracking async operations' },
objectID: { type: 'string', description: 'Object ID of the affected record' },
objectIDs: { type: 'array', description: 'Object IDs affected by batch operations' },
createdAt: { type: 'string', description: 'ISO 8601 timestamp when the record was created' },
updatedAt: {
type: 'string',
description: 'ISO 8601 timestamp when the record or settings were updated',
},
deletedAt: {
type: 'string',
description: 'ISO 8601 timestamp when the record or index was deleted',
},
record: { type: 'json', description: 'Retrieved record data (user-defined attributes)' },
results: { type: 'array', description: 'Array of retrieved records from get_records' },
cursor: {
type: 'string',
description:
'Opaque cursor string for retrieving the next page of browse results. Absent when no more results exist.',
},
indices: { type: 'array', description: 'List of indices in the application' },
searchableAttributes: { type: 'array', description: 'List of searchable attributes' },
attributesForFaceting: { type: 'array', description: 'Attributes configured for faceting' },
ranking: { type: 'array', description: 'Ranking criteria for the index' },
customRanking: { type: 'array', description: 'Custom ranking criteria' },
replicas: { type: 'array', description: 'List of replica index names' },
maxValuesPerFacet: {
type: 'number',
description: 'Maximum number of facet values returned (default 100)',
},
highlightPreTag: {
type: 'string',
description: 'HTML tag inserted before highlighted parts (default "<em>")',
},
highlightPostTag: {
type: 'string',
description: 'HTML tag inserted after highlighted parts (default "</em>")',
},
paginationLimitedTo: {
type: 'number',
description: 'Maximum number of hits accessible via pagination (default 1000)',
},
},
}
+320
View File
@@ -0,0 +1,320 @@
import { RedisIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type {
RedisCommandResponse,
RedisDeleteResponse,
RedisExistsResponse,
RedisExpireResponse,
RedisGetResponse,
RedisHDelResponse,
RedisHGetAllResponse,
RedisHGetResponse,
RedisHSetResponse,
RedisIncrbyResponse,
RedisIncrResponse,
RedisKeysResponse,
RedisLLenResponse,
RedisLPopResponse,
RedisLPushResponse,
RedisLRangeResponse,
RedisPersistResponse,
RedisRPopResponse,
RedisRPushResponse,
RedisSetnxResponse,
RedisSetResponse,
RedisTtlResponse,
} from '@/tools/redis/types'
type RedisResponse =
| RedisGetResponse
| RedisSetResponse
| RedisDeleteResponse
| RedisKeysResponse
| RedisCommandResponse
| RedisHSetResponse
| RedisHGetResponse
| RedisHGetAllResponse
| RedisHDelResponse
| RedisIncrResponse
| RedisIncrbyResponse
| RedisExpireResponse
| RedisTtlResponse
| RedisPersistResponse
| RedisLPushResponse
| RedisRPushResponse
| RedisLPopResponse
| RedisRPopResponse
| RedisLLenResponse
| RedisLRangeResponse
| RedisExistsResponse
| RedisSetnxResponse
const KEY_OPERATIONS = [
'get',
'set',
'delete',
'hset',
'hget',
'hgetall',
'hdel',
'incr',
'incrby',
'exists',
'setnx',
'lpush',
'rpush',
'lpop',
'rpop',
'llen',
'lrange',
'expire',
'persist',
'ttl',
] as const
export const RedisBlock: BlockConfig<RedisResponse> = {
type: 'redis',
name: 'Redis',
description: 'Key-value operations with Redis',
longDescription:
'Connect to any Redis instance to perform key-value, hash, list, and utility operations via a direct connection.',
docsLink: 'https://docs.sim.ai/tools/redis',
category: 'tools',
bgColor: '#FF4438',
authMode: AuthMode.ApiKey,
icon: RedisIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Get', id: 'get' },
{ label: 'Set', id: 'set' },
{ label: 'Delete', id: 'delete' },
{ label: 'List Keys', id: 'keys' },
{ label: 'HSET', id: 'hset' },
{ label: 'HGET', id: 'hget' },
{ label: 'HGETALL', id: 'hgetall' },
{ label: 'HDEL', id: 'hdel' },
{ label: 'INCR', id: 'incr' },
{ label: 'INCRBY', id: 'incrby' },
{ label: 'EXISTS', id: 'exists' },
{ label: 'SETNX', id: 'setnx' },
{ label: 'LPUSH', id: 'lpush' },
{ label: 'RPUSH', id: 'rpush' },
{ label: 'LPOP', id: 'lpop' },
{ label: 'RPOP', id: 'rpop' },
{ label: 'LLEN', id: 'llen' },
{ label: 'LRANGE', id: 'lrange' },
{ label: 'EXPIRE', id: 'expire' },
{ label: 'PERSIST', id: 'persist' },
{ label: 'TTL', id: 'ttl' },
{ label: 'Command', id: 'command' },
],
value: () => 'get',
},
{
id: 'url',
title: 'Connection URL',
type: 'short-input',
placeholder: 'redis://user:password@host:port',
password: true,
required: true,
},
{
id: 'key',
title: 'Key',
type: 'short-input',
placeholder: 'my-key',
condition: {
field: 'operation',
value: [...KEY_OPERATIONS],
},
required: {
field: 'operation',
value: [...KEY_OPERATIONS],
},
},
{
id: 'value',
title: 'Value',
type: 'long-input',
placeholder: 'Value to store',
condition: { field: 'operation', value: ['set', 'setnx', 'hset', 'lpush', 'rpush'] },
required: { field: 'operation', value: ['set', 'setnx', 'hset', 'lpush', 'rpush'] },
},
{
id: 'ex',
title: 'Expiration (seconds)',
type: 'short-input',
placeholder: 'Optional TTL in seconds',
condition: { field: 'operation', value: 'set' },
mode: 'advanced',
},
{
id: 'field',
title: 'Field',
type: 'short-input',
placeholder: 'Hash field name',
condition: { field: 'operation', value: ['hset', 'hget', 'hdel'] },
required: { field: 'operation', value: ['hset', 'hget', 'hdel'] },
},
{
id: 'pattern',
title: 'Pattern',
type: 'short-input',
placeholder: '* (all keys) or user:* (prefix match)',
condition: { field: 'operation', value: 'keys' },
mode: 'advanced',
},
{
id: 'seconds',
title: 'Seconds',
type: 'short-input',
placeholder: 'Timeout in seconds',
condition: { field: 'operation', value: 'expire' },
required: { field: 'operation', value: 'expire' },
},
{
id: 'increment',
title: 'Increment',
type: 'short-input',
placeholder: 'Amount to increment by (negative to decrement)',
condition: { field: 'operation', value: 'incrby' },
required: { field: 'operation', value: 'incrby' },
},
{
id: 'start',
title: 'Start Index',
type: 'short-input',
placeholder: '0',
condition: { field: 'operation', value: 'lrange' },
required: { field: 'operation', value: 'lrange' },
mode: 'advanced',
},
{
id: 'stop',
title: 'Stop Index',
type: 'short-input',
placeholder: '-1 (all elements)',
condition: { field: 'operation', value: 'lrange' },
required: { field: 'operation', value: 'lrange' },
mode: 'advanced',
},
{
id: 'command',
title: 'Command',
type: 'code',
placeholder: '["HSET", "myhash", "field1", "value1"]',
condition: { field: 'operation', value: 'command' },
required: { field: 'operation', value: 'command' },
},
],
tools: {
access: [
'redis_get',
'redis_set',
'redis_delete',
'redis_keys',
'redis_command',
'redis_hset',
'redis_hget',
'redis_hgetall',
'redis_hdel',
'redis_incr',
'redis_incrby',
'redis_expire',
'redis_ttl',
'redis_persist',
'redis_lpush',
'redis_rpush',
'redis_lpop',
'redis_rpop',
'redis_llen',
'redis_lrange',
'redis_exists',
'redis_setnx',
],
config: {
tool: (params) => {
if (params.ex) {
params.ex = Number(params.ex)
}
if (params.seconds !== undefined) {
params.seconds = Number(params.seconds)
}
if (params.start !== undefined) {
params.start = Number(params.start)
}
if (params.stop !== undefined) {
params.stop = Number(params.stop)
}
if (params.increment !== undefined) {
params.increment = Number(params.increment)
}
return `redis_${params.operation}`
},
},
},
inputs: {
operation: { type: 'string', description: 'Redis operation to perform' },
url: { type: 'string', description: 'Redis connection URL' },
key: { type: 'string', description: 'Redis key' },
value: { type: 'string', description: 'Value to store' },
ex: { type: 'number', description: 'Expiration time in seconds (SET)' },
field: { type: 'string', description: 'Hash field name (HSET/HGET/HDEL)' },
pattern: { type: 'string', description: 'Pattern to match keys (KEYS)' },
seconds: { type: 'number', description: 'Timeout in seconds (EXPIRE)' },
start: { type: 'number', description: 'Start index (LRANGE)' },
stop: { type: 'number', description: 'Stop index (LRANGE)' },
command: { type: 'string', description: 'Redis command as JSON array (Command)' },
increment: { type: 'number', description: 'Amount to increment by (INCRBY)' },
},
outputs: {
value: {
type: 'json',
description:
'Retrieved value (Get, HGET, LPOP, RPOP: string or null) or new value after increment (INCR, INCRBY: number)',
},
result: {
type: 'json',
description: 'Operation result (Set, HSET, EXPIRE, PERSIST, Command operations)',
},
deletedCount: { type: 'number', description: 'Number of keys deleted (Delete operation)' },
deleted: { type: 'number', description: 'Number of fields deleted (HDEL operation)' },
keys: { type: 'array', description: 'List of keys matching the pattern (Keys operation)' },
count: { type: 'number', description: 'Number of items found (Keys, LRANGE operations)' },
key: { type: 'string', description: 'The key operated on' },
fields: {
type: 'json',
description: 'Hash field-value pairs keyed by field name (HGETALL operation)',
},
fieldCount: { type: 'number', description: 'Number of fields in the hash (HGETALL operation)' },
field: { type: 'string', description: 'Hash field name (HSET, HGET, HDEL operations)' },
ttl: {
type: 'number',
description:
'Remaining TTL in seconds. Positive integer if TTL set, -1 if no expiration, -2 if key does not exist.',
},
length: {
type: 'number',
description: 'List length (LPUSH, RPUSH, LLEN operations)',
},
values: {
type: 'array',
description: 'List elements in the specified range (LRANGE operation)',
},
command: { type: 'string', description: 'The command that was executed (Command operation)' },
pattern: { type: 'string', description: 'The pattern used to match keys (Keys operation)' },
exists: {
type: 'boolean',
description: 'Whether the key exists (true) or not (false) (EXISTS operation)',
},
wasSet: {
type: 'boolean',
description: 'Whether the key was set (true) or already existed (false) (SETNX operation)',
},
},
}
+327
View File
@@ -0,0 +1,327 @@
import { RevenueCatIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { RevenueCatResponse } from '@/tools/revenuecat/types'
export const RevenueCatBlock: BlockConfig<RevenueCatResponse> = {
type: 'revenuecat',
name: 'RevenueCat',
description: 'Manage in-app subscriptions and entitlements',
authMode: AuthMode.ApiKey,
longDescription:
'Integrate RevenueCat into the workflow. Manage subscribers, entitlements, offerings, and Google Play subscriptions. Retrieve customer subscription status, grant or revoke promotional entitlements, record purchases, update subscriber attributes, and manage Google Play subscription billing.',
docsLink: 'https://docs.sim.ai/tools/revenuecat',
category: 'tools',
bgColor: '#F25A5A',
icon: RevenueCatIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Get Customer', id: 'get_customer' },
{ label: 'Delete Customer', id: 'delete_customer' },
{ label: 'Create Purchase', id: 'create_purchase' },
{ label: 'Grant Entitlement', id: 'grant_entitlement' },
{ label: 'Revoke Entitlement', id: 'revoke_entitlement' },
{ label: 'List Offerings', id: 'list_offerings' },
{ label: 'Update Subscriber Attributes', id: 'update_subscriber_attributes' },
{ label: 'Defer Google Subscription', id: 'defer_google_subscription' },
{ label: 'Refund Google Subscription', id: 'refund_google_subscription' },
{ label: 'Revoke Google Subscription', id: 'revoke_google_subscription' },
],
value: () => 'get_customer',
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
password: true,
placeholder: 'Enter your RevenueCat API key',
required: true,
},
{
id: 'appUserId',
title: 'App User ID',
type: 'short-input',
placeholder: 'Enter the app user ID',
required: true,
},
{
id: 'entitlementIdentifier',
title: 'Entitlement Identifier',
type: 'short-input',
placeholder: 'e.g., premium, pro',
condition: {
field: 'operation',
value: ['grant_entitlement', 'revoke_entitlement'],
},
required: {
field: 'operation',
value: ['grant_entitlement', 'revoke_entitlement'],
},
},
{
id: 'duration',
title: 'Duration',
type: 'dropdown',
options: [
{ label: 'Daily', id: 'daily' },
{ label: '3 Days', id: 'three_day' },
{ label: 'Weekly', id: 'weekly' },
{ label: 'Monthly', id: 'monthly' },
{ label: '2 Months', id: 'two_month' },
{ label: '3 Months', id: 'three_month' },
{ label: '6 Months', id: 'six_month' },
{ label: 'Yearly', id: 'yearly' },
{ label: 'Lifetime', id: 'lifetime' },
],
value: () => 'monthly',
condition: {
field: 'operation',
value: 'grant_entitlement',
},
},
{
id: 'startTimeMs',
title: 'Start Time (ms)',
type: 'short-input',
placeholder: 'Optional start time in ms since epoch',
condition: {
field: 'operation',
value: 'grant_entitlement',
},
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: `Generate a Unix epoch timestamp in milliseconds based on the user's description.
The timestamp should represent the start time of a promotional entitlement.
Setting a start time in the past allows shorter effective durations.
Examples:
- "right now" -> current time in milliseconds
- "1 hour ago" -> current time minus 3600000 milliseconds
- "yesterday" -> current time minus 86400000 milliseconds
Return ONLY the numeric timestamp, no text.`,
},
},
{
id: 'fetchToken',
title: 'Fetch Token',
type: 'short-input',
placeholder: 'Store receipt or purchase token (e.g., sub_...)',
condition: {
field: 'operation',
value: 'create_purchase',
},
required: {
field: 'operation',
value: 'create_purchase',
},
},
{
id: 'productId',
title: 'Product ID',
type: 'short-input',
placeholder: 'Product identifier',
condition: {
field: 'operation',
value: [
'create_purchase',
'defer_google_subscription',
'refund_google_subscription',
'revoke_google_subscription',
],
},
required: {
field: 'operation',
value: [
'create_purchase',
'defer_google_subscription',
'refund_google_subscription',
'revoke_google_subscription',
],
},
},
{
id: 'price',
title: 'Price',
type: 'short-input',
placeholder: 'e.g., 9.99',
condition: {
field: 'operation',
value: 'create_purchase',
},
mode: 'advanced',
},
{
id: 'currency',
title: 'Currency',
type: 'short-input',
placeholder: 'e.g., USD',
condition: {
field: 'operation',
value: 'create_purchase',
},
mode: 'advanced',
},
{
id: 'isRestore',
title: 'Is Restore',
type: 'dropdown',
options: [
{ label: 'No', id: 'false' },
{ label: 'Yes', id: 'true' },
],
value: () => 'false',
condition: {
field: 'operation',
value: 'create_purchase',
},
mode: 'advanced',
},
{
id: 'purchasePlatform',
title: 'Platform',
type: 'dropdown',
options: [
{ label: 'iOS', id: 'ios' },
{ label: 'Android', id: 'android' },
{ label: 'Amazon', id: 'amazon' },
{ label: 'macOS', id: 'macos' },
{ label: 'Stripe', id: 'stripe' },
],
condition: {
field: 'operation',
value: 'create_purchase',
},
mode: 'advanced',
},
{
id: 'attributes',
title: 'Attributes',
type: 'long-input',
placeholder: '{"$email": {"value": "user@example.com"}}',
condition: {
field: 'operation',
value: 'update_subscriber_attributes',
},
required: {
field: 'operation',
value: 'update_subscriber_attributes',
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON object of RevenueCat subscriber attributes based on the user's description.
Each attribute key maps to an object with a "value" field.
Reserved attribute keys start with "$": $email, $displayName, $phoneNumber, $mediaSource, $campaign, $adGroup, $ad, $keyword, $creative, $iterableUserId, $iterableCampaignId, $iterableTemplateId, $onesignalId, $airshipChannelId, $cleverTapId, $firebaseAppInstanceId.
Custom attributes use plain keys without "$".
Examples:
- "set email to john@example.com and name to John" ->
{"$email": {"value": "john@example.com"}, "$displayName": {"value": "John"}}
- "set plan to premium and team to acme" ->
{"plan": {"value": "premium"}, "team": {"value": "acme"}}
Return ONLY valid JSON.`,
},
},
{
id: 'extendByDays',
title: 'Extend By Days',
type: 'short-input',
placeholder: 'Number of days to extend (1-365)',
condition: {
field: 'operation',
value: 'defer_google_subscription',
},
required: {
field: 'operation',
value: 'defer_google_subscription',
},
},
{
id: 'platform',
title: 'Platform',
type: 'dropdown',
options: [
{ label: 'iOS', id: 'ios' },
{ label: 'Android', id: 'android' },
{ label: 'Amazon', id: 'amazon' },
{ label: 'macOS', id: 'macos' },
{ label: 'Stripe', id: 'stripe' },
],
condition: {
field: 'operation',
value: 'list_offerings',
},
},
],
tools: {
access: [
'revenuecat_get_customer',
'revenuecat_delete_customer',
'revenuecat_create_purchase',
'revenuecat_grant_entitlement',
'revenuecat_revoke_entitlement',
'revenuecat_list_offerings',
'revenuecat_update_subscriber_attributes',
'revenuecat_defer_google_subscription',
'revenuecat_refund_google_subscription',
'revenuecat_revoke_google_subscription',
],
config: {
tool: (params) => {
if (params.purchasePlatform && params.operation === 'create_purchase') {
params.platform = params.purchasePlatform
}
if (params.isRestore !== undefined) {
params.isRestore = params.isRestore === 'true'
}
if (params.price !== undefined && params.price !== '') {
params.price = Number(params.price)
}
if (params.extendByDays !== undefined && params.extendByDays !== '') {
params.extendByDays = Number(params.extendByDays)
}
if (params.startTimeMs !== undefined && params.startTimeMs !== '') {
params.startTimeMs = Number(params.startTimeMs)
}
return `revenuecat_${params.operation}`
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'RevenueCat API key' },
appUserId: { type: 'string', description: 'App user ID' },
entitlementIdentifier: { type: 'string', description: 'Entitlement identifier' },
duration: { type: 'string', description: 'Promotional entitlement duration' },
startTimeMs: { type: 'number', description: 'Custom start time in ms since epoch' },
fetchToken: { type: 'string', description: 'Store receipt or purchase token' },
productId: { type: 'string', description: 'Product identifier' },
price: { type: 'number', description: 'Product price' },
currency: { type: 'string', description: 'ISO 4217 currency code' },
isRestore: { type: 'boolean', description: 'Whether this is a restore purchase' },
purchasePlatform: { type: 'string', description: 'Platform for the purchase' },
attributes: { type: 'string', description: 'JSON object of subscriber attributes' },
extendByDays: { type: 'number', description: 'Number of days to extend (1-365)' },
platform: { type: 'string', description: 'Platform filter for offerings' },
},
outputs: {
subscriber: {
type: 'json',
description: 'Subscriber object with subscriptions and entitlements',
},
offerings: {
type: 'json',
description: 'Array of offerings with packages',
},
current_offering_id: { type: 'string', description: 'Current offering identifier' },
metadata: { type: 'json', description: 'Operation metadata' },
deleted: { type: 'boolean', description: 'Whether the subscriber was deleted' },
app_user_id: { type: 'string', description: 'The app user ID' },
updated: { type: 'boolean', description: 'Whether the attributes were updated' },
},
}
+313
View File
@@ -0,0 +1,313 @@
import { UpstashIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type {
UpstashRedisCommandResponse,
UpstashRedisDeleteResponse,
UpstashRedisExistsResponse,
UpstashRedisExpireResponse,
UpstashRedisGetResponse,
UpstashRedisHGetAllResponse,
UpstashRedisHGetResponse,
UpstashRedisHSetResponse,
UpstashRedisIncrbyResponse,
UpstashRedisIncrResponse,
UpstashRedisKeysResponse,
UpstashRedisLPushResponse,
UpstashRedisLRangeResponse,
UpstashRedisSetnxResponse,
UpstashRedisSetResponse,
UpstashRedisTtlResponse,
} from '@/tools/upstash/types'
type UpstashResponse =
| UpstashRedisGetResponse
| UpstashRedisSetResponse
| UpstashRedisDeleteResponse
| UpstashRedisKeysResponse
| UpstashRedisCommandResponse
| UpstashRedisHSetResponse
| UpstashRedisHGetResponse
| UpstashRedisHGetAllResponse
| UpstashRedisIncrResponse
| UpstashRedisIncrbyResponse
| UpstashRedisExpireResponse
| UpstashRedisTtlResponse
| UpstashRedisLPushResponse
| UpstashRedisLRangeResponse
| UpstashRedisExistsResponse
| UpstashRedisSetnxResponse
export const UpstashBlock: BlockConfig<UpstashResponse> = {
type: 'upstash',
name: 'Upstash',
description: 'Serverless Redis with Upstash',
longDescription:
'Connect to Upstash Redis to perform key-value, hash, list, and utility operations via the REST API.',
docsLink: 'https://docs.sim.ai/tools/upstash',
category: 'tools',
bgColor: '#181C1E',
authMode: AuthMode.ApiKey,
icon: UpstashIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Get', id: 'get' },
{ label: 'Set', id: 'set' },
{ label: 'Delete', id: 'delete' },
{ label: 'List Keys', id: 'keys' },
{ label: 'HSET', id: 'hset' },
{ label: 'HGET', id: 'hget' },
{ label: 'HGETALL', id: 'hgetall' },
{ label: 'INCR', id: 'incr' },
{ label: 'INCRBY', id: 'incrby' },
{ label: 'EXISTS', id: 'exists' },
{ label: 'SETNX', id: 'setnx' },
{ label: 'LPUSH', id: 'lpush' },
{ label: 'LRANGE', id: 'lrange' },
{ label: 'EXPIRE', id: 'expire' },
{ label: 'TTL', id: 'ttl' },
{ label: 'Command', id: 'command' },
],
value: () => 'get',
},
{
id: 'restUrl',
title: 'REST URL',
type: 'short-input',
placeholder: 'https://your-database.upstash.io',
password: true,
required: true,
},
{
id: 'restToken',
title: 'REST Token',
type: 'short-input',
placeholder: 'Enter your Upstash Redis REST token',
password: true,
required: true,
},
// Key field (used by most operations)
{
id: 'key',
title: 'Key',
type: 'short-input',
placeholder: 'my-key',
condition: {
field: 'operation',
value: [
'get',
'set',
'delete',
'hset',
'hget',
'hgetall',
'incr',
'incrby',
'exists',
'setnx',
'lpush',
'lrange',
'expire',
'ttl',
],
},
required: {
field: 'operation',
value: [
'get',
'set',
'delete',
'hset',
'hget',
'hgetall',
'incr',
'incrby',
'exists',
'setnx',
'lpush',
'lrange',
'expire',
'ttl',
],
},
},
// Value field (Get/Set/HSET/LPUSH)
{
id: 'value',
title: 'Value',
type: 'long-input',
placeholder: 'Value to store',
condition: { field: 'operation', value: ['set', 'setnx', 'hset', 'lpush'] },
required: { field: 'operation', value: ['set', 'setnx', 'hset', 'lpush'] },
},
// Expiration for SET
{
id: 'ex',
title: 'Expiration (seconds)',
type: 'short-input',
placeholder: 'Optional TTL in seconds',
condition: { field: 'operation', value: 'set' },
mode: 'advanced',
},
// Hash field (HSET/HGET)
{
id: 'field',
title: 'Field',
type: 'short-input',
placeholder: 'Hash field name',
condition: { field: 'operation', value: ['hset', 'hget'] },
required: { field: 'operation', value: ['hset', 'hget'] },
},
// Pattern for KEYS
{
id: 'pattern',
title: 'Pattern',
type: 'short-input',
placeholder: '* (all keys) or user:* (prefix match)',
condition: { field: 'operation', value: 'keys' },
mode: 'advanced',
},
// Seconds for EXPIRE
{
id: 'seconds',
title: 'Seconds',
type: 'short-input',
placeholder: 'Timeout in seconds',
condition: { field: 'operation', value: 'expire' },
required: { field: 'operation', value: 'expire' },
},
// Increment for INCRBY
{
id: 'increment',
title: 'Increment',
type: 'short-input',
placeholder: 'Amount to increment by (negative to decrement)',
condition: { field: 'operation', value: 'incrby' },
required: { field: 'operation', value: 'incrby' },
},
// Start/Stop for LRANGE
{
id: 'start',
title: 'Start Index',
type: 'short-input',
placeholder: '0',
condition: { field: 'operation', value: 'lrange' },
required: { field: 'operation', value: 'lrange' },
mode: 'advanced',
},
{
id: 'stop',
title: 'Stop Index',
type: 'short-input',
placeholder: '-1 (all elements)',
condition: { field: 'operation', value: 'lrange' },
required: { field: 'operation', value: 'lrange' },
mode: 'advanced',
},
// Command for raw Redis
{
id: 'command',
title: 'Command',
type: 'code',
placeholder: '["HSET", "myhash", "field1", "value1"]',
condition: { field: 'operation', value: 'command' },
required: { field: 'operation', value: 'command' },
},
],
tools: {
access: [
'upstash_redis_get',
'upstash_redis_set',
'upstash_redis_delete',
'upstash_redis_keys',
'upstash_redis_command',
'upstash_redis_hset',
'upstash_redis_hget',
'upstash_redis_hgetall',
'upstash_redis_incr',
'upstash_redis_expire',
'upstash_redis_ttl',
'upstash_redis_lpush',
'upstash_redis_lrange',
'upstash_redis_exists',
'upstash_redis_setnx',
'upstash_redis_incrby',
],
config: {
tool: (params) => {
if (params.ex) {
params.ex = Number(params.ex)
}
if (params.seconds !== undefined) {
params.seconds = Number(params.seconds)
}
if (params.start !== undefined) {
params.start = Number(params.start)
}
if (params.stop !== undefined) {
params.stop = Number(params.stop)
}
if (params.increment !== undefined) {
params.increment = Number(params.increment)
}
return `upstash_redis_${params.operation}`
},
},
},
inputs: {
operation: { type: 'string', description: 'Redis operation to perform' },
restUrl: { type: 'string', description: 'Upstash Redis REST URL' },
restToken: { type: 'string', description: 'Upstash Redis REST token' },
key: { type: 'string', description: 'Redis key' },
value: { type: 'string', description: 'Value to store' },
ex: { type: 'number', description: 'Expiration time in seconds (SET)' },
field: { type: 'string', description: 'Hash field name (HSET/HGET)' },
pattern: { type: 'string', description: 'Pattern to match keys (KEYS)' },
seconds: { type: 'number', description: 'Timeout in seconds (EXPIRE)' },
start: { type: 'number', description: 'Start index (LRANGE)' },
stop: { type: 'number', description: 'Stop index (LRANGE)' },
command: { type: 'string', description: 'Redis command as JSON array (Command)' },
increment: { type: 'number', description: 'Amount to increment by (INCRBY)' },
},
outputs: {
value: { type: 'json', description: 'Retrieved value (Get, HGET, INCR, INCRBY operations)' },
result: {
type: 'json',
description: 'Operation result (Set, HSET, EXPIRE, Command operations)',
},
deletedCount: { type: 'number', description: 'Number of keys deleted (Delete operation)' },
keys: { type: 'array', description: 'List of keys matching the pattern (Keys operation)' },
count: { type: 'number', description: 'Number of items found (Keys, LRANGE operations)' },
key: { type: 'string', description: 'The key operated on' },
fields: {
type: 'json',
description: 'Hash field-value pairs keyed by field name (HGETALL operation)',
},
fieldCount: { type: 'number', description: 'Number of fields in the hash (HGETALL operation)' },
field: { type: 'string', description: 'Hash field name (HSET, HGET operations)' },
ttl: {
type: 'number',
description:
'Remaining TTL in seconds. Positive integer if TTL set, -1 if no expiration, -2 if key does not exist.',
},
length: { type: 'number', description: 'List length after push (LPUSH operation)' },
values: {
type: 'array',
description: 'List elements in the specified range (LRANGE operation)',
},
command: { type: 'string', description: 'The command that was executed (Command operation)' },
pattern: { type: 'string', description: 'The pattern used to match keys (Keys operation)' },
exists: {
type: 'boolean',
description: 'Whether the key exists (true) or not (false) (EXISTS operation)',
},
wasSet: {
type: 'boolean',
description: 'Whether the key was set (true) or already existed (false) (SETNX operation)',
},
},
}
+8
View File
@@ -3,6 +3,7 @@ import { AgentBlock } from '@/blocks/blocks/agent'
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 { ApiBlock } from '@/blocks/blocks/api'
import { ApiTriggerBlock } from '@/blocks/blocks/api_trigger'
import { ApifyBlock } from '@/blocks/blocks/apify'
@@ -108,9 +109,11 @@ import { PulseBlock, PulseV2Block } from '@/blocks/blocks/pulse'
import { QdrantBlock } from '@/blocks/blocks/qdrant'
import { RDSBlock } from '@/blocks/blocks/rds'
import { RedditBlock } from '@/blocks/blocks/reddit'
import { RedisBlock } from '@/blocks/blocks/redis'
import { ReductoBlock, ReductoV2Block } from '@/blocks/blocks/reducto'
import { ResendBlock } from '@/blocks/blocks/resend'
import { ResponseBlock } from '@/blocks/blocks/response'
import { RevenueCatBlock } from '@/blocks/blocks/revenuecat'
import { RouterBlock, RouterV2Block } from '@/blocks/blocks/router'
import { RssBlock } from '@/blocks/blocks/rss'
import { S3Block } from '@/blocks/blocks/s3'
@@ -148,6 +151,7 @@ import { TtsBlock } from '@/blocks/blocks/tts'
import { TwilioSMSBlock } from '@/blocks/blocks/twilio'
import { TwilioVoiceBlock } from '@/blocks/blocks/twilio_voice'
import { TypeformBlock } from '@/blocks/blocks/typeform'
import { UpstashBlock } from '@/blocks/blocks/upstash'
import { VariablesBlock } from '@/blocks/blocks/variables'
import { VercelBlock } from '@/blocks/blocks/vercel'
import { VideoGeneratorBlock, VideoGeneratorV2Block } from '@/blocks/blocks/video_generator'
@@ -175,6 +179,7 @@ export const registry: Record<string, BlockConfig> = {
ahrefs: AhrefsBlock,
airtable: AirtableBlock,
airweave: AirweaveBlock,
algolia: AlgoliaBlock,
api: ApiBlock,
api_trigger: ApiTriggerBlock,
apify: ApifyBlock,
@@ -293,10 +298,12 @@ export const registry: Record<string, BlockConfig> = {
qdrant: QdrantBlock,
rds: RDSBlock,
reddit: RedditBlock,
redis: RedisBlock,
reducto: ReductoBlock,
reducto_v2: ReductoV2Block,
resend: ResendBlock,
response: ResponseBlock,
revenuecat: RevenueCatBlock,
router: RouterBlock,
router_v2: RouterV2Block,
rss: RssBlock,
@@ -337,6 +344,7 @@ export const registry: Record<string, BlockConfig> = {
twilio_sms: TwilioSMSBlock,
twilio_voice: TwilioVoiceBlock,
typeform: TypeformBlock,
upstash: UpstashBlock,
vercel: VercelBlock,
variables: VariablesBlock,
video_generator: VideoGeneratorBlock,
+82
View File
@@ -1157,6 +1157,17 @@ export function AirweaveIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function AlgoliaIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 50 50'>
<path
fill='#FFFFFF'
d='M25,0C11.3,0,0.2,11,0,24.6C-0.2,38.4,11,49.9,24.8,50c4.3,0,8.4-1,12-3c0.4-0.2,0.4-0.7,0.1-1l-2.3-2.1 c-0.5-0.4-1.2-0.5-1.7-0.3c-2.5,1.1-5.3,1.6-8.2,1.6c-11.2-0.1-20.2-9.4-20-20.6C4.9,13.6,13.9,4.7,25,4.7h20.3v36L33.7,30.5 c-0.4-0.3-0.9-0.3-1.2,0.1c-1.8,2.4-4.9,4-8.2,3.7c-4.6-0.3-8.4-4-8.7-8.7c-0.4-5.5,4-10.2,9.4-10.2c4.9,0,9,3.8,9.4,8.6 c0,0.4,0.2,0.8,0.6,1.1l3,2.7c0.3,0.3,0.9,0.1,1-0.3c0.2-1.2,0.3-2.4,0.2-3.6c-0.5-7-6.2-12.7-13.2-13.1c-8.1-0.5-14.8,5.8-15,13.7 c-0.2,7.7,6.1,14.4,13.8,14.5c3.2,0.1,6.2-0.9,8.6-2.7l15,13.3c0.6,0.6,1.7,0.1,1.7-0.7v-48C50,0.4,49.5,0,49,0L25,0 C25,0,25,0,25,0z'
/>
</svg>
)
}
export function GoogleBooksIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 478.633 540.068'>
@@ -5737,3 +5748,74 @@ export function CloudflareIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function UpstashIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 341' width='24' height='24'>
<path
fill='#00C98D'
d='M0 298.417c56.554 56.553 148.247 56.553 204.801 0c56.554-56.554 56.554-148.247 0-204.801l-25.6 25.6c42.415 42.416 42.415 111.185 0 153.6c-42.416 42.416-111.185 42.416-153.601 0z'
/>
<path
fill='#00C98D'
d='M51.2 247.216c28.277 28.277 74.123 28.277 102.4 0c28.277-28.276 28.277-74.123 0-102.4l-25.6 25.6c14.14 14.138 14.14 37.061 0 51.2c-14.138 14.139-37.061 14.139-51.2 0zM256 42.415c-56.554-56.553-148.247-56.553-204.8 0c-56.555 56.555-56.555 148.247 0 204.801l25.599-25.6c-42.415-42.415-42.415-111.185 0-153.6c42.416-42.416 111.185-42.416 153.6 0z'
/>
<path
fill='#00C98D'
d='M204.8 93.616c-28.276-28.277-74.124-28.277-102.4 0c-28.278 28.277-28.278 74.123 0 102.4l25.6-25.6c-14.14-14.138-14.14-37.061 0-51.2c14.138-14.139 37.06-14.139 51.2 0z'
/>
<path
fill='#FFF'
fillOpacity='.4'
d='M256 42.415c-56.554-56.553-148.247-56.553-204.8 0c-56.555 56.555-56.555 148.247 0 204.801l25.599-25.6c-42.415-42.415-42.415-111.185 0-153.6c42.416-42.416 111.185-42.416 153.6 0z'
/>
<path
fill='#FFF'
fillOpacity='.4'
d='M204.8 93.616c-28.276-28.277-74.124-28.277-102.4 0c-28.278 28.277-28.278 74.123 0 102.4l25.6-25.6c-14.14-14.138-14.14-37.061 0-51.2c14.138-14.139 37.06-14.139 51.2 0z'
/>
</svg>
)
}
export function RevenueCatIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
width='512'
height='512'
viewBox='0 0 512 512'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M95 109.774C110.152 106.108 133.612 104 154.795 104C212.046 104 246.32 123.928 246.32 174.646C246.32 205.746 233.737 226.264 214.005 237.437L261.765 318.946C258.05 321.632 250.035 323.176 238.864 323.176C226.282 323.176 217.987 321.672 211.982 318.946L172.225 248.3H167.645C157.789 248.305 147.945 247.601 138.18 246.192V319.255C134.172 321.672 127.022 323.176 116.73 323.176C106.73 323.176 99.2874 321.659 95 319.255V109.774ZM137.643 207.848C145.772 209.263 153.997 209.968 162.235 209.956C187.12 209.956 202.285 200.556 202.285 177.057C202.285 152.886 186.268 142.949 157.668 142.949C150.956 142.918 144.255 143.515 137.643 144.735V207.848Z'
fill='#FFFFFF'
/>
<path
d='M428.529 329.244C428.529 365.526 410.145 375.494 396.306 382.195C360.972 399.32 304.368 379.4 244.206 373.338C189.732 366.214 135.706 361.522 127.309 373.738C124.152 376.832 123.481 386.798 127.309 390.862C138.604 402.85 168.061 394.493 188.919 390.714C195.391 389.694 201.933 392.099 206.079 397.021C210.226 401.944 211.349 408.637 209.024 414.58C206.699 420.522 201.28 424.811 194.809 425.831C185.379 427.264 175.85 427.989 166.306 428C145.988 428 120.442 424.495 105.943 409.072C98.7232 401.4 91.3266 387.78 97.0271 366.465C107.875 326.074 172.807 336.052 248.033 343.633C300.41 348.907 357.23 366.465 379.934 350.343C385.721 346.234 396.517 337.022 390.698 329.244C384.879 321.467 375.353 325.684 362.838 325.684C300.152 325.684 263.238 285.302 263.238 217.916C263.247 167.292 284.176 131.892 318.287 115.09C333.109 107.789 350.421 104 369.587 104C386.292 104 403.269 106.931 414.11 113.366C420.847 123.032 423.778 140.305 422.306 153.201C408.247 146.466 395.36 142.949 378.669 142.949C337.365 142.949 308.947 164.039 308.947 214.985C308.947 265.932 337.065 286.149 376.611 286.149C387.869 286.035 403.1 284.67 422.306 282.053C426.455 297.498 428.529 313.228 428.529 329.244Z'
fill='#FFFFFF'
/>
</svg>
)
}
export function RedisIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='0 0 512 512'
xmlns='http://www.w3.org/2000/svg'
fillRule='evenodd'
clipRule='evenodd'
strokeLinejoin='round'
strokeMiterlimit='2'
>
<path
d='M479.14 279.864c-34.584 43.578-71.94 93.385-146.645 93.385-66.73 0-91.59-58.858-93.337-106.672 14.62 30.915 43.203 55.949 87.804 54.792C412.737 318.6 471.53 241.127 471.53 170.57c0-84.388-62.947-145.262-172.24-145.262-78.165 0-175.004 29.743-238.646 76.782-.689 48.42 26.286 111.369 35.972 104.452 55.17-39.67 98.918-65.203 141.35-78.01C175.153 198.58 24.451 361.219 6 389.85c2.076 26.286 34.588 96.842 50.496 96.842 4.841 0 8.993-2.768 13.835-7.61 45.433-51.046 82.472-96.816 115.412-140.933 4.627 64.658 36.42 143.702 125.307 143.702 79.55 0 158.408-57.414 194.377-186.767 4.149-15.911-15.22-28.362-26.286-15.22zm-90.616-104.449c0 40.81-40.118 60.87-76.782 60.87-19.596 0-34.648-5.145-46.554-11.832 21.906-33.168 43.59-67.182 66.887-103.593 41.08 6.953 56.449 29.788 56.449 54.555z'
fill='#FFFFFF'
fillRule='nonzero'
/>
</svg>
)
}
+98
View File
@@ -0,0 +1,98 @@
import type { AlgoliaAddRecordParams, AlgoliaAddRecordResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const addRecordTool: ToolConfig<AlgoliaAddRecordParams, AlgoliaAddRecordResponse> = {
id: 'algolia_add_record',
name: 'Algolia Add Record',
description: 'Add or replace a record in an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
objectID: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Object ID for the record (auto-generated if not provided)',
},
record: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description: 'JSON object representing the record to add',
},
},
request: {
url: (params) => {
const base = `https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}`
if (params.objectID) {
return `${base}/${encodeURIComponent(params.objectID)}`
}
return base
},
method: (params) => (params.objectID ? 'PUT' : 'POST'),
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const record = typeof params.record === 'string' ? JSON.parse(params.record) : params.record
return record
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
objectID: data.objectID ?? '',
createdAt: data.createdAt ?? null,
updatedAt: data.updatedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the indexing operation',
},
objectID: {
type: 'string',
description: 'The object ID of the added or replaced record',
},
createdAt: {
type: 'string',
description:
'Timestamp when the record was created (only present when objectID is auto-generated)',
optional: true,
},
updatedAt: {
type: 'string',
description:
'Timestamp when the record was updated (only present when replacing an existing record)',
optional: true,
},
},
}
@@ -0,0 +1,86 @@
import type {
AlgoliaBatchOperationsParams,
AlgoliaBatchOperationsResponse,
} from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const batchOperationsTool: ToolConfig<
AlgoliaBatchOperationsParams,
AlgoliaBatchOperationsResponse
> = {
id: 'algolia_batch_operations',
name: 'Algolia Batch Operations',
description:
'Perform batch add, update, partial update, or delete operations on records in an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
requests: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'Array of batch operations. Each item has "action" (addObject, updateObject, partialUpdateObject, partialUpdateObjectNoCreate, deleteObject) and "body" (the record data, must include objectID for update/delete)',
},
},
request: {
url: (params) =>
`https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/batch`,
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const requests =
typeof params.requests === 'string' ? JSON.parse(params.requests) : params.requests
return { requests }
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
objectIDs: data.objectIDs ?? [],
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the batch operation',
},
objectIDs: {
type: 'array',
description: 'Array of object IDs affected by the batch operation',
items: {
type: 'string',
description: 'Unique identifier of an affected record',
},
},
},
}
+151
View File
@@ -0,0 +1,151 @@
import type {
AlgoliaBrowseRecordsParams,
AlgoliaBrowseRecordsResponse,
} from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const browseRecordsTool: ToolConfig<
AlgoliaBrowseRecordsParams,
AlgoliaBrowseRecordsResponse
> = {
id: 'algolia_browse_records',
name: 'Algolia Browse Records',
description: 'Browse and iterate over all records in an Algolia index using cursor pagination',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia API Key (must have browse ACL)',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index to browse',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search query to filter browsed records',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter string to narrow down results',
},
attributesToRetrieve: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of attributes to retrieve',
},
hitsPerPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of hits per page (default: 1000, max: 1000)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor from a previous browse response for pagination',
},
},
request: {
url: (params) =>
`https://${params.applicationId}-dsn.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/browse`,
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
if (params.cursor) {
return { cursor: params.cursor }
}
const body: Record<string, unknown> = {}
if (params.query) body.query = params.query
if (params.filters) body.filters = params.filters
if (params.attributesToRetrieve) {
body.attributesToRetrieve = params.attributesToRetrieve
.split(',')
.map((a: string) => a.trim())
}
if (params.hitsPerPage !== undefined) body.hitsPerPage = Number(params.hitsPerPage)
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
hits: data.hits ?? [],
cursor: data.cursor ?? null,
nbHits: data.nbHits ?? 0,
page: data.page ?? 0,
nbPages: data.nbPages ?? 0,
hitsPerPage: data.hitsPerPage ?? 1000,
processingTimeMS: data.processingTimeMS ?? 0,
},
}
},
outputs: {
hits: {
type: 'array',
description: 'Array of records from the index (up to 1000 per request)',
items: {
type: 'object',
description: 'A record object containing objectID plus any requested attributes',
properties: {
objectID: {
type: 'string',
description: 'Unique identifier of the record',
},
},
},
},
cursor: {
type: 'string',
description:
'Opaque cursor string for retrieving the next page of results. Absent when no more results exist.',
optional: true,
},
nbHits: {
type: 'number',
description: 'Total number of records matching the browse criteria',
},
page: {
type: 'number',
description: 'Current page number (zero-based)',
},
nbPages: {
type: 'number',
description: 'Total number of pages available',
},
hitsPerPage: {
type: 'number',
description: 'Number of hits per page (1-1000, default 1000 for browse)',
},
processingTimeMS: {
type: 'number',
description: 'Server-side processing time in milliseconds',
},
},
}
+66
View File
@@ -0,0 +1,66 @@
import type { AlgoliaClearRecordsParams, AlgoliaClearRecordsResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const clearRecordsTool: ToolConfig<AlgoliaClearRecordsParams, AlgoliaClearRecordsResponse> =
{
id: 'algolia_clear_records',
name: 'Algolia Clear Records',
description:
'Clear all records from an Algolia index while keeping settings, synonyms, and rules',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key (must have deleteIndex ACL)',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index to clear',
},
},
request: {
url: (params) =>
`https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/clear`,
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
updatedAt: data.updatedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the clear operation',
},
updatedAt: {
type: 'string',
description: 'Timestamp when the records were cleared',
optional: true,
},
},
}
+100
View File
@@ -0,0 +1,100 @@
import type {
AlgoliaCopyMoveIndexParams,
AlgoliaCopyMoveIndexResponse,
} from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const copyMoveIndexTool: ToolConfig<
AlgoliaCopyMoveIndexParams,
AlgoliaCopyMoveIndexResponse
> = {
id: 'algolia_copy_move_index',
name: 'Algolia Copy/Move Index',
description: 'Copy or move an Algolia index to a new destination',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the source index',
},
operation: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Operation to perform: "copy" or "move"',
},
destination: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the destination index',
},
scope: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Array of scopes to copy (only for "copy" operation): ["settings", "synonyms", "rules"]. Omit to copy everything including records.',
},
},
request: {
url: (params) =>
`https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/operation`,
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
operation: params.operation,
destination: params.destination,
}
if (params.scope) {
const scope = typeof params.scope === 'string' ? JSON.parse(params.scope) : params.scope
body.scope = scope
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
updatedAt: data.updatedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the copy/move operation',
},
updatedAt: {
type: 'string',
description: 'Timestamp when the operation was performed',
optional: true,
},
},
}
+160
View File
@@ -0,0 +1,160 @@
import type {
AlgoliaDeleteByFilterParams,
AlgoliaDeleteByFilterResponse,
} from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const deleteByFilterTool: ToolConfig<
AlgoliaDeleteByFilterParams,
AlgoliaDeleteByFilterResponse
> = {
id: 'algolia_delete_by_filter',
name: 'Algolia Delete By Filter',
description: 'Delete all records matching a filter from an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key (must have deleteIndex ACL)',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter expression to match records for deletion (e.g., "category:outdated")',
},
facetFilters: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Array of facet filters (e.g., ["brand:Acme"])',
},
numericFilters: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Array of numeric filters (e.g., ["price > 100"])',
},
tagFilters: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Array of tag filters using the _tags attribute (e.g., ["published"])',
},
aroundLatLng: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Coordinates for geo-search filter (e.g., "40.71,-74.01")',
},
aroundRadius: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum radius in meters for geo-search, or "all" for unlimited',
},
insideBoundingBox: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Bounding box coordinates as [[lat1, lng1, lat2, lng2]] for geo-search filter',
},
insidePolygon: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Polygon coordinates as [[lat1, lng1, lat2, lng2, lat3, lng3, ...]] for geo-search filter',
},
},
request: {
url: (params) =>
`https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/deleteByQuery`,
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.filters) {
body.filters = params.filters
}
if (params.facetFilters) {
body.facetFilters =
typeof params.facetFilters === 'string'
? JSON.parse(params.facetFilters)
: params.facetFilters
}
if (params.numericFilters) {
body.numericFilters =
typeof params.numericFilters === 'string'
? JSON.parse(params.numericFilters)
: params.numericFilters
}
if (params.tagFilters) {
body.tagFilters =
typeof params.tagFilters === 'string' ? JSON.parse(params.tagFilters) : params.tagFilters
}
if (params.aroundLatLng) {
body.aroundLatLng = params.aroundLatLng
}
if (params.aroundRadius !== undefined) {
body.aroundRadius = params.aroundRadius === 'all' ? 'all' : Number(params.aroundRadius)
}
if (params.insideBoundingBox) {
body.insideBoundingBox =
typeof params.insideBoundingBox === 'string'
? JSON.parse(params.insideBoundingBox)
: params.insideBoundingBox
}
if (params.insidePolygon) {
body.insidePolygon =
typeof params.insidePolygon === 'string'
? JSON.parse(params.insidePolygon)
: params.insidePolygon
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
updatedAt: data.updatedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the delete-by-filter operation',
},
updatedAt: {
type: 'string',
description: 'Timestamp when the operation was performed',
optional: true,
},
},
}
+63
View File
@@ -0,0 +1,63 @@
import type { AlgoliaDeleteIndexParams, AlgoliaDeleteIndexResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const deleteIndexTool: ToolConfig<AlgoliaDeleteIndexParams, AlgoliaDeleteIndexResponse> = {
id: 'algolia_delete_index',
name: 'Algolia Delete Index',
description: 'Delete an entire Algolia index and all its records',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key (must have deleteIndex ACL)',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index to delete',
},
},
request: {
method: 'DELETE',
url: (params) =>
`https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}`,
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
}),
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
deletedAt: data.deletedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the index deletion',
},
deletedAt: {
type: 'string',
description: 'Timestamp when the index was deleted',
optional: true,
},
},
}
+69
View File
@@ -0,0 +1,69 @@
import type { AlgoliaDeleteRecordParams, AlgoliaDeleteRecordResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const deleteRecordTool: ToolConfig<AlgoliaDeleteRecordParams, AlgoliaDeleteRecordResponse> =
{
id: 'algolia_delete_record',
name: 'Algolia Delete Record',
description: 'Delete a record by objectID from an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
objectID: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The objectID of the record to delete',
},
},
request: {
method: 'DELETE',
url: (params) =>
`https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/${encodeURIComponent(params.objectID)}`,
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
}),
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
deletedAt: data.deletedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the deletion',
},
deletedAt: {
type: 'string',
description: 'Timestamp when the record was deleted',
},
},
}
+80
View File
@@ -0,0 +1,80 @@
import type { AlgoliaGetRecordParams, AlgoliaGetRecordResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const getRecordTool: ToolConfig<AlgoliaGetRecordParams, AlgoliaGetRecordResponse> = {
id: 'algolia_get_record',
name: 'Algolia Get Record',
description: 'Get a record by objectID from an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
objectID: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The objectID of the record to retrieve',
},
attributesToRetrieve: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of attributes to retrieve',
},
},
request: {
method: 'GET',
url: (params) => {
const base = `https://${params.applicationId}-dsn.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/${encodeURIComponent(params.objectID)}`
if (params.attributesToRetrieve) {
return `${base}?attributesToRetrieve=${encodeURIComponent(params.attributesToRetrieve)}`
}
return base
},
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const { objectID, ...rest } = data
return {
success: true,
output: {
objectID: objectID ?? '',
record: rest,
},
}
},
outputs: {
objectID: {
type: 'string',
description: 'The objectID of the retrieved record',
},
record: {
type: 'object',
description: 'The record data (all attributes)',
},
},
}
+84
View File
@@ -0,0 +1,84 @@
import type { AlgoliaGetRecordsParams, AlgoliaGetRecordsResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const getRecordsTool: ToolConfig<AlgoliaGetRecordsParams, AlgoliaGetRecordsResponse> = {
id: 'algolia_get_records',
name: 'Algolia Get Records',
description: 'Retrieve multiple records by objectID from one or more Algolia indices',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Default index name for all requests',
},
requests: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'Array of objects specifying records to retrieve. Each must have "objectID" and optionally "indexName" and "attributesToRetrieve".',
},
},
request: {
url: (params) => `https://${params.applicationId}-dsn.algolia.net/1/indexes/*/objects`,
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const parsed =
typeof params.requests === 'string' ? JSON.parse(params.requests) : params.requests
const requests = (parsed as Record<string, unknown>[]).map((req) => ({
...req,
indexName: req.indexName ?? params.indexName,
}))
return { requests }
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
results: data.results ?? [],
},
}
},
outputs: {
results: {
type: 'array',
description: 'Array of retrieved records (null entries for records not found)',
items: {
type: 'object',
description:
'A record object containing objectID and user-defined attributes, or null if not found',
properties: {
objectID: {
type: 'string',
description: 'Unique identifier of the record',
},
},
},
},
},
}
+108
View File
@@ -0,0 +1,108 @@
import type { AlgoliaGetSettingsParams, AlgoliaGetSettingsResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const getSettingsTool: ToolConfig<AlgoliaGetSettingsParams, AlgoliaGetSettingsResponse> = {
id: 'algolia_get_settings',
name: 'Algolia Get Settings',
description: 'Retrieve the settings of an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
},
request: {
method: 'GET',
url: (params) =>
`https://${params.applicationId}-dsn.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/settings`,
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
}),
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
searchableAttributes: data.searchableAttributes ?? null,
attributesForFaceting: data.attributesForFaceting ?? [],
ranking: data.ranking ?? [],
customRanking: data.customRanking ?? [],
replicas: data.replicas ?? [],
hitsPerPage: data.hitsPerPage ?? 20,
maxValuesPerFacet: data.maxValuesPerFacet ?? 100,
highlightPreTag: data.highlightPreTag ?? '<em>',
highlightPostTag: data.highlightPostTag ?? '</em>',
paginationLimitedTo: data.paginationLimitedTo ?? 1000,
},
}
},
outputs: {
searchableAttributes: {
type: 'array',
description: 'List of searchable attributes',
optional: true,
items: { type: 'string', description: 'Searchable attribute name or expression' },
},
attributesForFaceting: {
type: 'array',
description: 'Attributes used for faceting',
items: { type: 'string', description: 'Faceting attribute name or expression' },
},
ranking: {
type: 'array',
description: 'Ranking criteria',
items: { type: 'string', description: 'Ranking criterion' },
},
customRanking: {
type: 'array',
description: 'Custom ranking criteria',
items: { type: 'string', description: 'Custom ranking expression (e.g., desc(popularity))' },
},
replicas: {
type: 'array',
description: 'List of replica index names',
items: { type: 'string', description: 'Replica index name' },
},
hitsPerPage: {
type: 'number',
description: 'Default number of hits per page',
},
maxValuesPerFacet: {
type: 'number',
description: 'Maximum number of facet values returned',
},
highlightPreTag: {
type: 'string',
description: 'HTML tag inserted before highlighted parts',
},
highlightPostTag: {
type: 'string',
description: 'HTML tag inserted after highlighted parts',
},
paginationLimitedTo: {
type: 'number',
description: 'Maximum number of hits accessible via pagination',
},
},
}
+31
View File
@@ -0,0 +1,31 @@
import { addRecordTool } from '@/tools/algolia/add_record'
import { batchOperationsTool } from '@/tools/algolia/batch_operations'
import { browseRecordsTool } from '@/tools/algolia/browse_records'
import { clearRecordsTool } from '@/tools/algolia/clear_records'
import { copyMoveIndexTool } from '@/tools/algolia/copy_move_index'
import { deleteByFilterTool } from '@/tools/algolia/delete_by_filter'
import { deleteIndexTool } from '@/tools/algolia/delete_index'
import { deleteRecordTool } from '@/tools/algolia/delete_record'
import { getRecordTool } from '@/tools/algolia/get_record'
import { getRecordsTool } from '@/tools/algolia/get_records'
import { getSettingsTool } from '@/tools/algolia/get_settings'
import { listIndicesTool } from '@/tools/algolia/list_indices'
import { partialUpdateRecordTool } from '@/tools/algolia/partial_update_record'
import { searchTool } from '@/tools/algolia/search'
import { updateSettingsTool } from '@/tools/algolia/update_settings'
export const algoliaSearchTool = searchTool
export const algoliaAddRecordTool = addRecordTool
export const algoliaGetRecordTool = getRecordTool
export const algoliaGetRecordsTool = getRecordsTool
export const algoliaDeleteRecordTool = deleteRecordTool
export const algoliaPartialUpdateRecordTool = partialUpdateRecordTool
export const algoliaBrowseRecordsTool = browseRecordsTool
export const algoliaBatchOperationsTool = batchOperationsTool
export const algoliaListIndicesTool = listIndicesTool
export const algoliaGetSettingsTool = getSettingsTool
export const algoliaUpdateSettingsTool = updateSettingsTool
export const algoliaDeleteIndexTool = deleteIndexTool
export const algoliaCopyMoveIndexTool = copyMoveIndexTool
export const algoliaClearRecordsTool = clearRecordsTool
export const algoliaDeleteByFilterTool = deleteByFilterTool
+136
View File
@@ -0,0 +1,136 @@
import type { AlgoliaListIndicesParams, AlgoliaListIndicesResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const listIndicesTool: ToolConfig<AlgoliaListIndicesParams, AlgoliaListIndicesResponse> = {
id: 'algolia_list_indices',
name: 'Algolia List Indices',
description: 'List all indices in an Algolia application',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia API Key',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for paginating indices (default: not paginated)',
},
hitsPerPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of indices per page (default: 100)',
},
},
request: {
method: 'GET',
url: (params) => {
const base = `https://${params.applicationId}.algolia.net/1/indexes`
const queryParams: string[] = []
if (params.page !== undefined) queryParams.push(`page=${params.page}`)
if (params.hitsPerPage !== undefined) queryParams.push(`hitsPerPage=${params.hitsPerPage}`)
return queryParams.length > 0 ? `${base}?${queryParams.join('&')}` : base
},
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const indices = (data.items ?? []).map(
(item: {
name: string
entries: number
dataSize: number
fileSize: number
lastBuildTimeS: number
numberOfPendingTasks: number
pendingTask: boolean
createdAt: string
updatedAt: string
primary?: string
replicas?: string[]
virtual?: boolean
}) => ({
name: item.name ?? '',
entries: item.entries ?? 0,
dataSize: item.dataSize ?? 0,
fileSize: item.fileSize ?? 0,
lastBuildTimeS: item.lastBuildTimeS ?? 0,
numberOfPendingTasks: item.numberOfPendingTasks ?? 0,
pendingTask: item.pendingTask ?? false,
createdAt: item.createdAt ?? '',
updatedAt: item.updatedAt ?? '',
primary: item.primary ?? null,
replicas: item.replicas ?? [],
virtual: item.virtual ?? false,
})
)
return {
success: true,
output: {
indices,
nbPages: data.nbPages ?? 1,
},
}
},
outputs: {
indices: {
type: 'array',
description: 'List of indices in the application',
items: {
type: 'object',
description: 'An Algolia index',
properties: {
name: { type: 'string', description: 'Name of the index' },
entries: { type: 'number', description: 'Number of records in the index' },
dataSize: { type: 'number', description: 'Size of the index data in bytes' },
fileSize: { type: 'number', description: 'Size of the index files in bytes' },
lastBuildTimeS: { type: 'number', description: 'Last build duration in seconds' },
numberOfPendingTasks: {
type: 'number',
description: 'Number of pending indexing tasks',
},
pendingTask: { type: 'boolean', description: 'Whether the index has pending tasks' },
createdAt: { type: 'string', description: 'Timestamp when the index was created' },
updatedAt: { type: 'string', description: 'Timestamp when the index was last updated' },
primary: {
type: 'string',
description: 'Name of the primary index (if this is a replica)',
optional: true,
},
replicas: {
type: 'array',
description: 'List of replica index names',
optional: true,
items: { type: 'string', description: 'Replica index name' },
},
virtual: {
type: 'boolean',
description: 'Whether the index is a virtual replica',
optional: true,
},
},
},
},
nbPages: {
type: 'number',
description: 'Total number of pages of indices',
},
},
}
@@ -0,0 +1,103 @@
import type {
AlgoliaPartialUpdateRecordParams,
AlgoliaPartialUpdateRecordResponse,
} from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const partialUpdateRecordTool: ToolConfig<
AlgoliaPartialUpdateRecordParams,
AlgoliaPartialUpdateRecordResponse
> = {
id: 'algolia_partial_update_record',
name: 'Algolia Partial Update Record',
description: 'Partially update a record in an Algolia index without replacing it entirely',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
objectID: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The objectID of the record to update',
},
attributes: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'JSON object with attributes to update. Supports built-in operations like {"stock": {"_operation": "Decrement", "value": 1}}',
},
createIfNotExists: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to create the record if it does not exist (default: true)',
},
},
request: {
url: (params) => {
const base = `https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/${encodeURIComponent(params.objectID)}/partial`
if (params.createIfNotExists === false) {
return `${base}?createIfNotExists=false`
}
return base
},
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const attributes =
typeof params.attributes === 'string' ? JSON.parse(params.attributes) : params.attributes
return attributes
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
objectID: data.objectID ?? '',
updatedAt: data.updatedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the update operation',
},
objectID: {
type: 'string',
description: 'The objectID of the updated record',
},
updatedAt: {
type: 'string',
description: 'Timestamp when the record was updated',
},
},
}
+204
View File
@@ -0,0 +1,204 @@
import type { AlgoliaSearchParams, AlgoliaSearchResponse } from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const searchTool: ToolConfig<AlgoliaSearchParams, AlgoliaSearchResponse> = {
id: 'algolia_search',
name: 'Algolia Search',
description: 'Search an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia API Key',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index to search',
},
query: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Search query text',
},
hitsPerPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of hits per page (default: 20)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number to retrieve (default: 0)',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter string (e.g., "category:electronics AND price < 100")',
},
attributesToRetrieve: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of attributes to retrieve',
},
},
request: {
url: (params) => `https://${params.applicationId}-dsn.algolia.net/1/indexes/*/queries`,
method: 'POST',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const request: Record<string, unknown> = {
indexName: params.indexName,
query: params.query,
}
if (params.hitsPerPage !== undefined) request.hitsPerPage = Number(params.hitsPerPage)
if (params.page !== undefined) request.page = Number(params.page)
if (params.filters) request.filters = params.filters
if (params.attributesToRetrieve) {
request.attributesToRetrieve = params.attributesToRetrieve
.split(',')
.map((a: string) => a.trim())
}
return { requests: [request] }
},
},
transformResponse: async (response) => {
const data = await response.json()
const result = data.results?.[0] ?? {}
return {
success: true,
output: {
hits: result.hits ?? [],
nbHits: result.nbHits ?? 0,
page: result.page ?? 0,
nbPages: result.nbPages ?? 0,
hitsPerPage: result.hitsPerPage ?? 20,
processingTimeMS: result.processingTimeMS ?? 0,
query: result.query ?? '',
parsedQuery: result.parsedQuery ?? null,
facets: result.facets ?? null,
facets_stats: result.facets_stats ?? null,
exhaustive: result.exhaustive ?? null,
},
}
},
outputs: {
hits: {
type: 'array',
description: 'Array of matching records',
items: {
type: 'object',
description:
'A search result hit containing objectID plus any user-defined attributes from the index',
properties: {
objectID: {
type: 'string',
description: 'Unique identifier of the record',
},
_highlightResult: {
type: 'object',
description:
'Highlighted attributes matching the query. Each attribute has value, matchLevel (none, partial, full), and matchedWords',
optional: true,
},
_snippetResult: {
type: 'object',
description:
'Snippeted attributes matching the query. Each attribute has value and matchLevel',
optional: true,
},
_rankingInfo: {
type: 'object',
description:
'Ranking information for the hit. Only present when getRankingInfo is enabled',
optional: true,
properties: {
nbTypos: { type: 'number', description: 'Number of typos in the query match' },
firstMatchedWord: {
type: 'number',
description: 'Position of the first matched word',
},
geoDistance: {
type: 'number',
description: 'Distance in meters for geo-search results',
},
nbExactWords: {
type: 'number',
description: 'Number of exactly matched words',
},
userScore: { type: 'number', description: 'Custom ranking score' },
words: { type: 'number', description: 'Number of matched words' },
},
},
},
},
},
nbHits: {
type: 'number',
description: 'Total number of matching hits',
},
page: {
type: 'number',
description: 'Current page number (zero-based)',
},
nbPages: {
type: 'number',
description: 'Total number of pages available',
},
hitsPerPage: {
type: 'number',
description: 'Number of hits per page (1-1000, default 20)',
},
processingTimeMS: {
type: 'number',
description: 'Server-side processing time in milliseconds',
},
query: {
type: 'string',
description: 'The search query that was executed',
},
parsedQuery: {
type: 'string',
description: 'The query string after normalization and stop word removal',
optional: true,
},
facets: {
type: 'object',
description: 'Facet counts keyed by facet name, each containing value-count pairs',
optional: true,
},
facets_stats: {
type: 'object',
description: 'Statistics (min, max, avg, sum) for numeric facets',
optional: true,
},
exhaustive: {
type: 'object',
description:
'Exhaustiveness flags for facetsCount, facetValues, nbHits, rulesMatch, and typo',
optional: true,
},
},
}
+268
View File
@@ -0,0 +1,268 @@
import type { ToolResponse } from '@/tools/types'
export interface AlgoliaBaseParams {
applicationId: string
apiKey: string
}
// Search
export interface AlgoliaSearchParams extends AlgoliaBaseParams {
indexName: string
query: string
hitsPerPage?: number | string
page?: number | string
filters?: string
attributesToRetrieve?: string
}
export interface AlgoliaSearchResponse extends ToolResponse {
output: {
hits: Record<string, unknown>[]
nbHits: number
page: number
nbPages: number
hitsPerPage: number
processingTimeMS: number
query: string
parsedQuery: string | null
facets: Record<string, Record<string, number>> | null
facets_stats: Record<string, { min: number; max: number; avg: number; sum: number }> | null
exhaustive: Record<string, boolean> | null
}
}
// Add Record
export interface AlgoliaAddRecordParams extends AlgoliaBaseParams {
indexName: string
objectID?: string
record: string | Record<string, unknown>
}
export interface AlgoliaAddRecordResponse extends ToolResponse {
output: {
taskID: number
objectID: string
createdAt: string | null
updatedAt: string | null
}
}
// Get Record
export interface AlgoliaGetRecordParams extends AlgoliaBaseParams {
indexName: string
objectID: string
attributesToRetrieve?: string
}
export interface AlgoliaGetRecordResponse extends ToolResponse {
output: {
objectID: string
record: Record<string, unknown>
}
}
// Get Multiple Records
export interface AlgoliaGetRecordsParams extends AlgoliaBaseParams {
indexName: string
requests:
| string
| {
objectID: string
indexName?: string
attributesToRetrieve?: string[]
}[]
}
export interface AlgoliaGetRecordsResponse extends ToolResponse {
output: {
results: (Record<string, unknown> | null)[]
}
}
// Delete Record
export interface AlgoliaDeleteRecordParams extends AlgoliaBaseParams {
indexName: string
objectID: string
}
export interface AlgoliaDeleteRecordResponse extends ToolResponse {
output: {
taskID: number
deletedAt: string | null
}
}
// Partial Update Record
export interface AlgoliaPartialUpdateRecordParams extends AlgoliaBaseParams {
indexName: string
objectID: string
attributes: string | Record<string, unknown>
createIfNotExists?: boolean
}
export interface AlgoliaPartialUpdateRecordResponse extends ToolResponse {
output: {
taskID: number
objectID: string
updatedAt: string | null
}
}
// Browse Records
export interface AlgoliaBrowseRecordsParams extends AlgoliaBaseParams {
indexName: string
query?: string
filters?: string
attributesToRetrieve?: string
hitsPerPage?: number | string
cursor?: string
}
export interface AlgoliaBrowseRecordsResponse extends ToolResponse {
output: {
hits: Record<string, unknown>[]
cursor: string | null
nbHits: number
page: number
nbPages: number
hitsPerPage: number
processingTimeMS: number
}
}
// Batch Operations
export interface AlgoliaBatchOperationsParams extends AlgoliaBaseParams {
indexName: string
requests:
| string
| {
action: string
body: Record<string, unknown>
}[]
}
export interface AlgoliaBatchOperationsResponse extends ToolResponse {
output: {
taskID: number
objectIDs: string[]
}
}
// List Indices
export interface AlgoliaListIndicesParams extends AlgoliaBaseParams {
page?: number | string
hitsPerPage?: number | string
}
export interface AlgoliaListIndicesResponse extends ToolResponse {
output: {
indices: {
name: string
entries: number
dataSize: number
fileSize: number
lastBuildTimeS: number
numberOfPendingTasks: number
pendingTask: boolean
createdAt: string
updatedAt: string
primary: string | null
replicas: string[]
virtual: boolean
}[]
nbPages: number
}
}
// Get Settings
export interface AlgoliaGetSettingsParams extends AlgoliaBaseParams {
indexName: string
}
export interface AlgoliaGetSettingsResponse extends ToolResponse {
output: {
searchableAttributes: string[] | null
attributesForFaceting: string[]
ranking: string[]
customRanking: string[]
replicas: string[]
hitsPerPage: number
maxValuesPerFacet: number
highlightPreTag: string
highlightPostTag: string
paginationLimitedTo: number
}
}
// Update Settings
export interface AlgoliaUpdateSettingsParams extends AlgoliaBaseParams {
indexName: string
settings: string | Record<string, unknown>
forwardToReplicas?: boolean
}
export interface AlgoliaUpdateSettingsResponse extends ToolResponse {
output: {
taskID: number
updatedAt: string | null
}
}
// Delete Index
export interface AlgoliaDeleteIndexParams extends AlgoliaBaseParams {
indexName: string
}
export interface AlgoliaDeleteIndexResponse extends ToolResponse {
output: {
taskID: number
deletedAt: string | null
}
}
// Copy/Move Index
export interface AlgoliaCopyMoveIndexParams extends AlgoliaBaseParams {
indexName: string
operation: string
destination: string
scope?: string | string[]
}
export interface AlgoliaCopyMoveIndexResponse extends ToolResponse {
output: {
taskID: number
updatedAt: string | null
}
}
// Clear Records
export interface AlgoliaClearRecordsParams extends AlgoliaBaseParams {
indexName: string
}
export interface AlgoliaClearRecordsResponse extends ToolResponse {
output: {
taskID: number
updatedAt: string | null
}
}
// Delete By Filter
export interface AlgoliaDeleteByFilterParams extends AlgoliaBaseParams {
indexName: string
filters: string
facetFilters?: string | string[]
numericFilters?: string | string[]
tagFilters?: string | string[]
aroundLatLng?: string
aroundRadius?: number | string
insideBoundingBox?: string | number[][]
insidePolygon?: string | number[][]
}
export interface AlgoliaDeleteByFilterResponse extends ToolResponse {
output: {
taskID: number
updatedAt: string | null
}
}
+93
View File
@@ -0,0 +1,93 @@
import type {
AlgoliaUpdateSettingsParams,
AlgoliaUpdateSettingsResponse,
} from '@/tools/algolia/types'
import type { ToolConfig } from '@/tools/types'
export const updateSettingsTool: ToolConfig<
AlgoliaUpdateSettingsParams,
AlgoliaUpdateSettingsResponse
> = {
id: 'algolia_update_settings',
name: 'Algolia Update Settings',
description: 'Update the settings of an Algolia index',
version: '1.0',
params: {
applicationId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Application ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Algolia Admin API Key (must have editSettings ACL)',
},
indexName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the Algolia index',
},
settings: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'JSON object with settings to update (e.g., {"searchableAttributes": ["name", "description"], "customRanking": ["desc(popularity)"]})',
},
forwardToReplicas: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to apply changes to replica indices (default: false)',
},
},
request: {
url: (params) => {
const base = `https://${params.applicationId}.algolia.net/1/indexes/${encodeURIComponent(params.indexName)}/settings`
if (params.forwardToReplicas) {
return `${base}?forwardToReplicas=true`
}
return base
},
method: 'PUT',
headers: (params) => ({
'x-algolia-application-id': params.applicationId,
'x-algolia-api-key': params.apiKey,
'Content-Type': 'application/json',
}),
body: (params) => {
const settings =
typeof params.settings === 'string' ? JSON.parse(params.settings) : params.settings
return settings
},
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
taskID: data.taskID ?? 0,
updatedAt: data.updatedAt ?? null,
},
}
},
outputs: {
taskID: {
type: 'number',
description: 'Algolia task ID for tracking the settings update',
},
updatedAt: {
type: 'string',
description: 'Timestamp when the settings were updated',
optional: true,
},
},
}
+68
View File
@@ -0,0 +1,68 @@
import type { RedisCommandParams, RedisCommandResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisCommandTool: ToolConfig<RedisCommandParams, RedisCommandResponse> = {
id: 'redis_command',
name: 'Redis Command',
description:
'Execute a raw Redis command as a JSON array (e.g. ["HSET", "key", "field", "value"]).',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
command: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Redis command as a JSON array (e.g. ["SET", "key", "value"])',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => {
let parsed: unknown
try {
parsed = JSON.parse(params.command)
} catch {
throw new Error(
`Invalid JSON in command: ${params.command}. Expected a JSON array like ["SET", "key", "value"].`
)
}
const [cmd, ...args] = Array.isArray(parsed) ? parsed : [parsed]
return {
url: params.url,
command: String(cmd),
args: args.map(String),
}
},
},
transformResponse: async (response: Response, params?: RedisCommandParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to execute Redis command')
}
return {
success: true,
output: {
command: params?.command ?? '',
result: data.result ?? null,
},
}
},
outputs: {
command: { type: 'string', description: 'The command that was executed' },
result: { type: 'json', description: 'The result of the command' },
},
}
+59
View File
@@ -0,0 +1,59 @@
import type { RedisDeleteParams, RedisDeleteResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisDeleteTool: ToolConfig<RedisDeleteParams, RedisDeleteResponse> = {
id: 'redis_delete',
name: 'Redis Delete',
description: 'Delete a key from Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to delete',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'DEL',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisDeleteParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to delete key from Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
deletedCount: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was deleted' },
deletedCount: {
type: 'number',
description: 'Number of keys deleted (0 if key did not exist, 1 if deleted)',
},
},
}
+56
View File
@@ -0,0 +1,56 @@
import type { RedisExistsParams, RedisExistsResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisExistsTool: ToolConfig<RedisExistsParams, RedisExistsResponse> = {
id: 'redis_exists',
name: 'Redis EXISTS',
description: 'Check if a key exists in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to check',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'EXISTS',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisExistsParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to check key existence in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
exists: data.result === 1,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was checked' },
exists: { type: 'boolean', description: 'Whether the key exists (true) or not (false)' },
},
}
+65
View File
@@ -0,0 +1,65 @@
import type { RedisExpireParams, RedisExpireResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisExpireTool: ToolConfig<RedisExpireParams, RedisExpireResponse> = {
id: 'redis_expire',
name: 'Redis EXPIRE',
description: 'Set an expiration time (in seconds) on a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to set expiration on',
},
seconds: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Timeout in seconds',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'EXPIRE',
args: [params.key, params.seconds],
}),
},
transformResponse: async (response: Response, params?: RedisExpireParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to set expiration in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
result: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that expiration was set on' },
result: {
type: 'number',
description: '1 if the timeout was set, 0 if the key does not exist',
},
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { RedisGetParams, RedisGetResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisGetTool: ToolConfig<RedisGetParams, RedisGetResponse> = {
id: 'redis_get',
name: 'Redis Get',
description: 'Get the value of a key from Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to retrieve',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'GET',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisGetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get key from Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? null,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was retrieved' },
value: {
type: 'string',
description: 'The value of the key, or null if the key does not exist',
optional: true,
},
},
}
+67
View File
@@ -0,0 +1,67 @@
import type { RedisHDelParams, RedisHDelResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisHDelTool: ToolConfig<RedisHDelParams, RedisHDelResponse> = {
id: 'redis_hdel',
name: 'Redis HDEL',
description: 'Delete a field from a hash stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hash key',
},
field: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The field name to delete',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'HDEL',
args: [params.key, params.field],
}),
},
transformResponse: async (response: Response, params?: RedisHDelParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to delete hash field from Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
field: params?.field ?? '',
deleted: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The hash key' },
field: { type: 'string', description: 'The field that was deleted' },
deleted: {
type: 'number',
description: 'Number of fields removed (1 if deleted, 0 if field did not exist)',
},
},
}
+68
View File
@@ -0,0 +1,68 @@
import type { RedisHGetParams, RedisHGetResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisHGetTool: ToolConfig<RedisHGetParams, RedisHGetResponse> = {
id: 'redis_hget',
name: 'Redis HGET',
description: 'Get the value of a field in a hash stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hash key',
},
field: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The field name to retrieve',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'HGET',
args: [params.key, params.field],
}),
},
transformResponse: async (response: Response, params?: RedisHGetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get hash field from Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
field: params?.field ?? '',
value: data.result ?? null,
},
}
},
outputs: {
key: { type: 'string', description: 'The hash key' },
field: { type: 'string', description: 'The field that was retrieved' },
value: {
type: 'string',
description: 'The field value, or null if the field or key does not exist',
optional: true,
},
},
}
+75
View File
@@ -0,0 +1,75 @@
import type { RedisHGetAllParams, RedisHGetAllResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisHGetAllTool: ToolConfig<RedisHGetAllParams, RedisHGetAllResponse> = {
id: 'redis_hgetall',
name: 'Redis HGETALL',
description: 'Get all fields and values of a hash stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hash key',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'HGETALL',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisHGetAllParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get hash from Redis')
}
// ioredis .call() returns HGETALL as a flat array [field1, value1, field2, value2, ...]
// We convert it to a key-value object for usability
const raw = data.result
let fields: Record<string, string> = {}
if (Array.isArray(raw)) {
for (let i = 0; i < raw.length; i += 2) {
fields[raw[i]] = raw[i + 1] ?? ''
}
} else if (raw && typeof raw === 'object') {
fields = raw
}
return {
success: true,
output: {
key: params?.key ?? '',
fields,
fieldCount: Object.keys(fields).length,
},
}
},
outputs: {
key: { type: 'string', description: 'The hash key' },
fields: {
type: 'object',
description:
'All field-value pairs in the hash as a key-value object. Empty object if the key does not exist.',
},
fieldCount: { type: 'number', description: 'Number of fields in the hash' },
},
}
+73
View File
@@ -0,0 +1,73 @@
import type { RedisHSetParams, RedisHSetResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisHSetTool: ToolConfig<RedisHSetParams, RedisHSetResponse> = {
id: 'redis_hset',
name: 'Redis HSET',
description: 'Set a field in a hash stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hash key',
},
field: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The field name within the hash',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to set for the field',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'HSET',
args: [params.key, params.field, params.value],
}),
},
transformResponse: async (response: Response, params?: RedisHSetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to set hash field in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
field: params?.field ?? '',
result: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The hash key' },
field: { type: 'string', description: 'The field that was set' },
result: {
type: 'number',
description: 'Number of fields added (1 if new, 0 if updated)',
},
},
}
+56
View File
@@ -0,0 +1,56 @@
import type { RedisIncrParams, RedisIncrResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisIncrTool: ToolConfig<RedisIncrParams, RedisIncrResponse> = {
id: 'redis_incr',
name: 'Redis INCR',
description: 'Increment the integer value of a key by one in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to increment',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'INCR',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisIncrParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to increment key in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was incremented' },
value: { type: 'number', description: 'The new value after increment' },
},
}
+62
View File
@@ -0,0 +1,62 @@
import type { RedisIncrbyParams, RedisIncrbyResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisIncrbyTool: ToolConfig<RedisIncrbyParams, RedisIncrbyResponse> = {
id: 'redis_incrby',
name: 'Redis INCRBY',
description: 'Increment the integer value of a key by a given amount in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to increment',
},
increment: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Amount to increment by (negative to decrement)',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'INCRBY',
args: [params.key, params.increment],
}),
},
transformResponse: async (response: Response, params?: RedisIncrbyParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to increment key in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was incremented' },
value: { type: 'number', description: 'The new value after increment' },
},
}
+22
View File
@@ -0,0 +1,22 @@
export { redisCommandTool } from '@/tools/redis/command'
export { redisDeleteTool } from '@/tools/redis/delete'
export { redisExistsTool } from '@/tools/redis/exists'
export { redisExpireTool } from '@/tools/redis/expire'
export { redisGetTool } from '@/tools/redis/get'
export { redisHDelTool } from '@/tools/redis/hdel'
export { redisHGetTool } from '@/tools/redis/hget'
export { redisHGetAllTool } from '@/tools/redis/hgetall'
export { redisHSetTool } from '@/tools/redis/hset'
export { redisIncrTool } from '@/tools/redis/incr'
export { redisIncrbyTool } from '@/tools/redis/incrby'
export { redisKeysTool } from '@/tools/redis/keys'
export { redisLLenTool } from '@/tools/redis/llen'
export { redisLPopTool } from '@/tools/redis/lpop'
export { redisLPushTool } from '@/tools/redis/lpush'
export { redisLRangeTool } from '@/tools/redis/lrange'
export { redisPersistTool } from '@/tools/redis/persist'
export { redisRPopTool } from '@/tools/redis/rpop'
export { redisRPushTool } from '@/tools/redis/rpush'
export { redisSetTool } from '@/tools/redis/set'
export { redisSetnxTool } from '@/tools/redis/setnx'
export { redisTtlTool } from '@/tools/redis/ttl'
+65
View File
@@ -0,0 +1,65 @@
import type { RedisKeysParams, RedisKeysResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisKeysTool: ToolConfig<RedisKeysParams, RedisKeysResponse> = {
id: 'redis_keys',
name: 'Redis Keys',
description:
'List all keys matching a pattern in Redis. Avoid using on large databases in production; use the Redis Command tool with SCAN for large key spaces.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
pattern: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pattern to match keys (default: * for all keys)',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'KEYS',
args: [params.pattern || '*'],
}),
},
transformResponse: async (response: Response, params?: RedisKeysParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to list keys from Redis')
}
const keys = data.result ?? []
return {
success: true,
output: {
pattern: params?.pattern || '*',
keys,
count: keys.length,
},
}
},
outputs: {
pattern: { type: 'string', description: 'The pattern used to match keys' },
keys: {
type: 'array',
description: 'List of keys matching the pattern',
items: { type: 'string', description: 'A Redis key' },
},
count: { type: 'number', description: 'Number of keys found' },
},
}
+59
View File
@@ -0,0 +1,59 @@
import type { RedisLLenParams, RedisLLenResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisLLenTool: ToolConfig<RedisLLenParams, RedisLLenResponse> = {
id: 'redis_llen',
name: 'Redis LLEN',
description: 'Get the length of a list stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'LLEN',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisLLenParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get list length from Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
length: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
length: {
type: 'number',
description: 'The length of the list, or 0 if the key does not exist',
},
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { RedisLPopParams, RedisLPopResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisLPopTool: ToolConfig<RedisLPopParams, RedisLPopResponse> = {
id: 'redis_lpop',
name: 'Redis LPOP',
description: 'Remove and return the first element of a list stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'LPOP',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisLPopParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to pop from list in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? null,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
value: {
type: 'string',
description: 'The removed element, or null if the list is empty',
optional: true,
},
},
}
+62
View File
@@ -0,0 +1,62 @@
import type { RedisLPushParams, RedisLPushResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisLPushTool: ToolConfig<RedisLPushParams, RedisLPushResponse> = {
id: 'redis_lpush',
name: 'Redis LPUSH',
description: 'Prepend a value to a list stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to prepend',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'LPUSH',
args: [params.key, params.value],
}),
},
transformResponse: async (response: Response, params?: RedisLPushParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to push to list in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
length: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
length: { type: 'number', description: 'Length of the list after the push' },
},
}
+76
View File
@@ -0,0 +1,76 @@
import type { RedisLRangeParams, RedisLRangeResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisLRangeTool: ToolConfig<RedisLRangeParams, RedisLRangeResponse> = {
id: 'redis_lrange',
name: 'Redis LRANGE',
description: 'Get a range of elements from a list stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
start: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start index (0-based)',
},
stop: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Stop index (-1 for all elements)',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'LRANGE',
args: [params.key, params.start, params.stop],
}),
},
transformResponse: async (response: Response, params?: RedisLRangeParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get list range from Redis')
}
const values = data.result ?? []
return {
success: true,
output: {
key: params?.key ?? '',
values,
count: values.length,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
values: {
type: 'array',
description: 'List elements in the specified range',
items: { type: 'string', description: 'A list element' },
},
count: { type: 'number', description: 'Number of elements returned' },
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { RedisPersistParams, RedisPersistResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisPersistTool: ToolConfig<RedisPersistParams, RedisPersistResponse> = {
id: 'redis_persist',
name: 'Redis PERSIST',
description: 'Remove the expiration from a key in Redis, making it persist indefinitely.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to persist',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'PERSIST',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisPersistParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to persist key in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
result: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was persisted' },
result: {
type: 'number',
description:
'1 if the expiration was removed, 0 if the key does not exist or has no expiration',
},
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { RedisRPopParams, RedisRPopResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisRPopTool: ToolConfig<RedisRPopParams, RedisRPopResponse> = {
id: 'redis_rpop',
name: 'Redis RPOP',
description: 'Remove and return the last element of a list stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'RPOP',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisRPopParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to pop from list in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? null,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
value: {
type: 'string',
description: 'The removed element, or null if the list is empty',
optional: true,
},
},
}
+62
View File
@@ -0,0 +1,62 @@
import type { RedisRPushParams, RedisRPushResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisRPushTool: ToolConfig<RedisRPushParams, RedisRPushResponse> = {
id: 'redis_rpush',
name: 'Redis RPUSH',
description: 'Append a value to the end of a list stored at a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to append',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'RPUSH',
args: [params.key, params.value],
}),
},
transformResponse: async (response: Response, params?: RedisRPushParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to push to list in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
length: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
length: { type: 'number', description: 'Length of the list after the push' },
},
}
+68
View File
@@ -0,0 +1,68 @@
import type { RedisSetParams, RedisSetResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisSetTool: ToolConfig<RedisSetParams, RedisSetResponse> = {
id: 'redis_set',
name: 'Redis Set',
description: 'Set the value of a key in Redis with an optional expiration time in seconds.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to set',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to store',
},
ex: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Expiration time in seconds (optional)',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'SET',
args: params.ex ? [params.key, params.value, 'EX', params.ex] : [params.key, params.value],
}),
},
transformResponse: async (response: Response, params?: RedisSetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to set key in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
result: data.result ?? 'OK',
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was set' },
result: { type: 'string', description: 'The result of the SET operation (typically "OK")' },
},
}
+65
View File
@@ -0,0 +1,65 @@
import type { RedisSetnxParams, RedisSetnxResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisSetnxTool: ToolConfig<RedisSetnxParams, RedisSetnxResponse> = {
id: 'redis_setnx',
name: 'Redis SETNX',
description: 'Set the value of a key in Redis only if the key does not already exist.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to set',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to store',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'SETNX',
args: [params.key, params.value],
}),
},
transformResponse: async (response: Response, params?: RedisSetnxParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to set key in Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
wasSet: data.result === 1,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was set' },
wasSet: {
type: 'boolean',
description: 'Whether the key was set (true) or already existed (false)',
},
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { RedisTtlParams, RedisTtlResponse } from '@/tools/redis/types'
import type { ToolConfig } from '@/tools/types'
export const redisTtlTool: ToolConfig<RedisTtlParams, RedisTtlResponse> = {
id: 'redis_ttl',
name: 'Redis TTL',
description: 'Get the remaining time to live (in seconds) of a key in Redis.',
version: '1.0.0',
params: {
url: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Redis connection URL (e.g. redis://user:password@host:port)',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to check TTL for',
},
},
request: {
url: '/api/tools/redis/execute',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({
url: params.url,
command: 'TTL',
args: [params.key],
}),
},
transformResponse: async (response: Response, params?: RedisTtlParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get TTL from Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
ttl: data.result ?? -2,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was checked' },
ttl: {
type: 'number',
description:
'Remaining TTL in seconds. Positive integer if TTL set, -1 if no expiration, -2 if key does not exist.',
},
},
}
+266
View File
@@ -0,0 +1,266 @@
import type { ToolResponse } from '@/tools/types'
export interface RedisBaseParams {
url: string
}
export interface RedisGetParams extends RedisBaseParams {
key: string
}
export interface RedisSetParams extends RedisBaseParams {
key: string
value: string
ex?: number
}
export interface RedisDeleteParams extends RedisBaseParams {
key: string
}
export interface RedisKeysParams extends RedisBaseParams {
pattern?: string
}
export interface RedisCommandParams extends RedisBaseParams {
command: string
}
export interface RedisHSetParams extends RedisBaseParams {
key: string
field: string
value: string
}
export interface RedisHGetParams extends RedisBaseParams {
key: string
field: string
}
export interface RedisHGetAllParams extends RedisBaseParams {
key: string
}
export interface RedisIncrParams extends RedisBaseParams {
key: string
}
export interface RedisIncrbyParams extends RedisBaseParams {
key: string
increment: number
}
export interface RedisExistsParams extends RedisBaseParams {
key: string
}
export interface RedisSetnxParams extends RedisBaseParams {
key: string
value: string
}
export interface RedisExpireParams extends RedisBaseParams {
key: string
seconds: number
}
export interface RedisTtlParams extends RedisBaseParams {
key: string
}
export interface RedisLPushParams extends RedisBaseParams {
key: string
value: string
}
export interface RedisLRangeParams extends RedisBaseParams {
key: string
start: number
stop: number
}
export interface RedisRPushParams extends RedisBaseParams {
key: string
value: string
}
export interface RedisLPopParams extends RedisBaseParams {
key: string
}
export interface RedisRPopParams extends RedisBaseParams {
key: string
}
export interface RedisLLenParams extends RedisBaseParams {
key: string
}
export interface RedisHDelParams extends RedisBaseParams {
key: string
field: string
}
export interface RedisPersistParams extends RedisBaseParams {
key: string
}
export interface RedisGetResponse extends ToolResponse {
output: {
key: string
value: string | null
}
}
export interface RedisSetResponse extends ToolResponse {
output: {
key: string
result: string
}
}
export interface RedisDeleteResponse extends ToolResponse {
output: {
key: string
deletedCount: number
}
}
export interface RedisKeysResponse extends ToolResponse {
output: {
pattern: string
keys: string[]
count: number
}
}
export interface RedisCommandResponse extends ToolResponse {
output: {
command: string
result: unknown
}
}
export interface RedisHSetResponse extends ToolResponse {
output: {
key: string
field: string
result: number
}
}
export interface RedisHGetResponse extends ToolResponse {
output: {
key: string
field: string
value: string | null
}
}
export interface RedisHGetAllResponse extends ToolResponse {
output: {
key: string
fields: Record<string, string>
fieldCount: number
}
}
export interface RedisIncrResponse extends ToolResponse {
output: {
key: string
value: number
}
}
export interface RedisIncrbyResponse extends ToolResponse {
output: {
key: string
value: number
}
}
export interface RedisExistsResponse extends ToolResponse {
output: {
key: string
exists: boolean
}
}
export interface RedisSetnxResponse extends ToolResponse {
output: {
key: string
wasSet: boolean
}
}
export interface RedisExpireResponse extends ToolResponse {
output: {
key: string
result: number
}
}
export interface RedisTtlResponse extends ToolResponse {
output: {
key: string
ttl: number
}
}
export interface RedisLPushResponse extends ToolResponse {
output: {
key: string
length: number
}
}
export interface RedisLRangeResponse extends ToolResponse {
output: {
key: string
values: string[]
count: number
}
}
export interface RedisRPushResponse extends ToolResponse {
output: {
key: string
length: number
}
}
export interface RedisLPopResponse extends ToolResponse {
output: {
key: string
value: string | null
}
}
export interface RedisRPopResponse extends ToolResponse {
output: {
key: string
value: string | null
}
}
export interface RedisLLenResponse extends ToolResponse {
output: {
key: string
length: number
}
}
export interface RedisHDelResponse extends ToolResponse {
output: {
key: string
field: string
deleted: number
}
}
export interface RedisPersistResponse extends ToolResponse {
output: {
key: string
result: number
}
}
+134
View File
@@ -25,6 +25,23 @@ import {
airtableUpdateRecordTool,
} from '@/tools/airtable'
import { airweaveSearchTool } from '@/tools/airweave'
import {
algoliaAddRecordTool,
algoliaBatchOperationsTool,
algoliaBrowseRecordsTool,
algoliaClearRecordsTool,
algoliaCopyMoveIndexTool,
algoliaDeleteByFilterTool,
algoliaDeleteIndexTool,
algoliaDeleteRecordTool,
algoliaGetRecordsTool,
algoliaGetRecordTool,
algoliaGetSettingsTool,
algoliaListIndicesTool,
algoliaPartialUpdateRecordTool,
algoliaSearchTool,
algoliaUpdateSettingsTool,
} from '@/tools/algolia'
import { apifyRunActorAsyncTool, apifyRunActorSyncTool } from '@/tools/apify'
import {
apolloAccountBulkCreateTool,
@@ -1350,8 +1367,44 @@ import {
redditUnsaveTool,
redditVoteTool,
} from '@/tools/reddit'
import {
redisCommandTool,
redisDeleteTool,
redisExistsTool,
redisExpireTool,
redisGetTool,
redisHDelTool,
redisHGetAllTool,
redisHGetTool,
redisHSetTool,
redisIncrbyTool,
redisIncrTool,
redisKeysTool,
redisLLenTool,
redisLPopTool,
redisLPushTool,
redisLRangeTool,
redisPersistTool,
redisRPopTool,
redisRPushTool,
redisSetnxTool,
redisSetTool,
redisTtlTool,
} from '@/tools/redis'
import { reductoParserTool, reductoParserV2Tool } from '@/tools/reducto'
import { mailSendTool } from '@/tools/resend'
import {
revenuecatCreatePurchaseTool,
revenuecatDeferGoogleSubscriptionTool,
revenuecatDeleteCustomerTool,
revenuecatGetCustomerTool,
revenuecatGrantEntitlementTool,
revenuecatListOfferingsTool,
revenuecatRefundGoogleSubscriptionTool,
revenuecatRevokeEntitlementTool,
revenuecatRevokeGoogleSubscriptionTool,
revenuecatUpdateSubscriberAttributesTool,
} from '@/tools/revenuecat'
import {
s3CopyObjectTool,
s3DeleteObjectTool,
@@ -1742,6 +1795,24 @@ import {
typeformUpdateFormTool,
} from '@/tools/typeform'
import type { ToolConfig } from '@/tools/types'
import {
upstashRedisCommandTool,
upstashRedisDeleteTool,
upstashRedisExistsTool,
upstashRedisExpireTool,
upstashRedisGetTool,
upstashRedisHGetAllTool,
upstashRedisHGetTool,
upstashRedisHSetTool,
upstashRedisIncrbyTool,
upstashRedisIncrTool,
upstashRedisKeysTool,
upstashRedisLPushTool,
upstashRedisLRangeTool,
upstashRedisSetnxTool,
upstashRedisSetTool,
upstashRedisTtlTool,
} from '@/tools/upstash'
import {
vercelAddDomainTool,
vercelAddProjectDomainTool,
@@ -2639,6 +2710,38 @@ export const tools: Record<string, ToolConfig> = {
reddit_edit: redditEditTool,
reddit_delete: redditDeleteTool,
reddit_subscribe: redditSubscribeTool,
redis_get: redisGetTool,
redis_set: redisSetTool,
redis_delete: redisDeleteTool,
redis_keys: redisKeysTool,
redis_command: redisCommandTool,
redis_hset: redisHSetTool,
redis_hget: redisHGetTool,
redis_hgetall: redisHGetAllTool,
redis_incr: redisIncrTool,
redis_incrby: redisIncrbyTool,
redis_expire: redisExpireTool,
redis_ttl: redisTtlTool,
redis_lpush: redisLPushTool,
redis_lrange: redisLRangeTool,
redis_exists: redisExistsTool,
redis_setnx: redisSetnxTool,
redis_rpush: redisRPushTool,
redis_lpop: redisLPopTool,
redis_rpop: redisRPopTool,
redis_llen: redisLLenTool,
redis_hdel: redisHDelTool,
redis_persist: redisPersistTool,
revenuecat_get_customer: revenuecatGetCustomerTool,
revenuecat_delete_customer: revenuecatDeleteCustomerTool,
revenuecat_create_purchase: revenuecatCreatePurchaseTool,
revenuecat_grant_entitlement: revenuecatGrantEntitlementTool,
revenuecat_revoke_entitlement: revenuecatRevokeEntitlementTool,
revenuecat_list_offerings: revenuecatListOfferingsTool,
revenuecat_update_subscriber_attributes: revenuecatUpdateSubscriberAttributesTool,
revenuecat_defer_google_subscription: revenuecatDeferGoogleSubscriptionTool,
revenuecat_refund_google_subscription: revenuecatRefundGoogleSubscriptionTool,
revenuecat_revoke_google_subscription: revenuecatRevokeGoogleSubscriptionTool,
google_drive_copy: googleDriveCopyTool,
google_drive_create_folder: googleDriveCreateFolderTool,
google_drive_delete: googleDriveDeleteTool,
@@ -2864,6 +2967,21 @@ export const tools: Record<string, ToolConfig> = {
twilio_voice_make_call: makeCallTool,
twilio_voice_list_calls: listCallsTool,
twilio_voice_get_recording: getRecordingTool,
algolia_search: algoliaSearchTool,
algolia_add_record: algoliaAddRecordTool,
algolia_get_record: algoliaGetRecordTool,
algolia_get_records: algoliaGetRecordsTool,
algolia_delete_record: algoliaDeleteRecordTool,
algolia_partial_update_record: algoliaPartialUpdateRecordTool,
algolia_browse_records: algoliaBrowseRecordsTool,
algolia_batch_operations: algoliaBatchOperationsTool,
algolia_list_indices: algoliaListIndicesTool,
algolia_get_settings: algoliaGetSettingsTool,
algolia_update_settings: algoliaUpdateSettingsTool,
algolia_delete_index: algoliaDeleteIndexTool,
algolia_copy_move_index: algoliaCopyMoveIndexTool,
algolia_clear_records: algoliaClearRecordsTool,
algolia_delete_by_filter: algoliaDeleteByFilterTool,
airtable_create_records: airtableCreateRecordsTool,
airtable_get_record: airtableGetRecordTool,
airtable_list_records: airtableListRecordsTool,
@@ -3712,4 +3830,20 @@ export const tools: Record<string, ToolConfig> = {
spotify_set_repeat: spotifySetRepeatTool,
spotify_set_shuffle: spotifySetShuffleTool,
spotify_transfer_playback: spotifyTransferPlaybackTool,
upstash_redis_get: upstashRedisGetTool,
upstash_redis_set: upstashRedisSetTool,
upstash_redis_delete: upstashRedisDeleteTool,
upstash_redis_keys: upstashRedisKeysTool,
upstash_redis_command: upstashRedisCommandTool,
upstash_redis_hset: upstashRedisHSetTool,
upstash_redis_hget: upstashRedisHGetTool,
upstash_redis_hgetall: upstashRedisHGetAllTool,
upstash_redis_incr: upstashRedisIncrTool,
upstash_redis_incrby: upstashRedisIncrbyTool,
upstash_redis_expire: upstashRedisExpireTool,
upstash_redis_ttl: upstashRedisTtlTool,
upstash_redis_lpush: upstashRedisLPushTool,
upstash_redis_lrange: upstashRedisLRangeTool,
upstash_redis_exists: upstashRedisExistsTool,
upstash_redis_setnx: upstashRedisSetnxTool,
}
@@ -0,0 +1,117 @@
import type { CreatePurchaseParams, CreatePurchaseResponse } from '@/tools/revenuecat/types'
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatCreatePurchaseTool: ToolConfig<
CreatePurchaseParams,
CreatePurchaseResponse
> = {
id: 'revenuecat_create_purchase',
name: 'RevenueCat Create Purchase',
description: 'Record a purchase (receipt) for a subscriber via the REST API',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat API key (public or secret)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
fetchToken: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The receipt token or purchase token from the store (App Store receipt, Google Play purchase token, or Stripe subscription ID)',
},
productId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The product identifier for the purchase',
},
price: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'The price of the product in the currency specified',
},
currency: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ISO 4217 currency code (e.g., USD, EUR)',
},
isRestore: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether this is a restore of a previous purchase',
},
platform: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Platform of the purchase (ios, android, amazon, macos, stripe). Required for Stripe and Paddle purchases.',
},
},
request: {
url: () => 'https://api.revenuecat.com/v1/receipts',
method: 'POST',
headers: (params) => {
const headers: Record<string, string> = {
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}
if (params.platform) {
headers['X-Platform'] = params.platform
}
return headers
},
body: (params) => {
const body: Record<string, unknown> = {
app_user_id: params.appUserId,
fetch_token: params.fetchToken,
product_id: params.productId,
}
if (params.price !== undefined) body.price = params.price
if (params.currency) body.currency = params.currency
if (params.isRestore !== undefined) body.is_restore = params.isRestore
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const subscriber = data.subscriber ?? {}
return {
success: true,
output: {
subscriber: {
first_seen: subscriber.first_seen ?? '',
original_app_user_id: subscriber.original_app_user_id ?? '',
subscriptions: subscriber.subscriptions ?? {},
entitlements: subscriber.entitlements ?? {},
non_subscriptions: subscriber.non_subscriptions ?? {},
},
},
}
},
outputs: {
subscriber: {
...SUBSCRIBER_OUTPUT,
description: 'The updated subscriber object after recording the purchase',
},
},
}
@@ -0,0 +1,82 @@
import type {
DeferGoogleSubscriptionParams,
DeferGoogleSubscriptionResponse,
} from '@/tools/revenuecat/types'
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatDeferGoogleSubscriptionTool: ToolConfig<
DeferGoogleSubscriptionParams,
DeferGoogleSubscriptionResponse
> = {
id: 'revenuecat_defer_google_subscription',
name: 'RevenueCat Defer Google Subscription',
description:
'Defer a Google Play subscription by extending its billing date by a number of days (Google Play only)',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
productId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The Google Play product identifier of the subscription to defer (use the part before the colon for products set up after Feb 2023)',
},
extendByDays: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Number of days to extend the subscription by (1-365)',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/subscriptions/${encodeURIComponent(params.productId)}/defer`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => ({
extend_by_days: params.extendByDays,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const subscriber = data.subscriber ?? {}
return {
success: true,
output: {
subscriber: {
first_seen: subscriber.first_seen ?? '',
original_app_user_id: subscriber.original_app_user_id ?? '',
subscriptions: subscriber.subscriptions ?? {},
entitlements: subscriber.entitlements ?? {},
},
},
}
},
outputs: {
subscriber: {
...SUBSCRIBER_OUTPUT,
description: 'The updated subscriber object after deferring the Google subscription',
},
},
}
@@ -0,0 +1,53 @@
import type { DeleteCustomerParams, DeleteCustomerResponse } from '@/tools/revenuecat/types'
import { DELETE_OUTPUT_PROPERTIES } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatDeleteCustomerTool: ToolConfig<
DeleteCustomerParams,
DeleteCustomerResponse
> = {
id: 'revenuecat_delete_customer',
name: 'RevenueCat Delete Customer',
description: 'Permanently delete a subscriber and all associated data',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber to delete',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}`,
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response, params) => {
return {
success: response.ok,
output: {
deleted: response.ok,
app_user_id: params?.appUserId ?? '',
},
}
},
outputs: {
deleted: DELETE_OUTPUT_PROPERTIES.deleted,
app_user_id: DELETE_OUTPUT_PROPERTIES.app_user_id,
},
}
+80
View File
@@ -0,0 +1,80 @@
import type { CustomerResponse, GetCustomerParams } from '@/tools/revenuecat/types'
import { METADATA_OUTPUT_PROPERTIES, SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatGetCustomerTool: ToolConfig<GetCustomerParams, CustomerResponse> = {
id: 'revenuecat_get_customer',
name: 'RevenueCat Get Customer',
description: 'Retrieve subscriber information by app user ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const subscriber = data.subscriber ?? {}
const entitlements = subscriber.entitlements ?? {}
const subscriptions = subscriber.subscriptions ?? {}
const activeEntitlements = Object.values(entitlements).filter(
(e: unknown) => (e as Record<string, unknown>).is_active
).length
const activeSubscriptions = Object.keys(subscriptions).length
return {
success: true,
output: {
subscriber: {
first_seen: subscriber.first_seen ?? '',
original_app_user_id: subscriber.original_app_user_id ?? '',
original_purchase_date: subscriber.original_purchase_date ?? null,
management_url: subscriber.management_url ?? null,
subscriptions: subscriptions,
entitlements: entitlements,
non_subscriptions: subscriber.non_subscriptions ?? {},
},
metadata: {
app_user_id: subscriber.original_app_user_id ?? '',
first_seen: subscriber.first_seen ?? '',
active_entitlements: activeEntitlements,
active_subscriptions: activeSubscriptions,
},
},
}
},
outputs: {
subscriber: {
...SUBSCRIBER_OUTPUT,
description: 'The subscriber object with subscriptions and entitlements',
},
metadata: {
type: 'object',
description: 'Subscriber summary metadata',
properties: METADATA_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,87 @@
import type { GrantEntitlementParams, GrantEntitlementResponse } from '@/tools/revenuecat/types'
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatGrantEntitlementTool: ToolConfig<
GrantEntitlementParams,
GrantEntitlementResponse
> = {
id: 'revenuecat_grant_entitlement',
name: 'RevenueCat Grant Entitlement',
description: 'Grant a promotional entitlement to a subscriber',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
entitlementIdentifier: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The entitlement identifier to grant',
},
duration: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Duration of the entitlement (daily, three_day, weekly, monthly, two_month, three_month, six_month, yearly, lifetime)',
},
startTimeMs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description:
'Optional start time in milliseconds since Unix epoch. Set to a past time to achieve custom durations shorter than daily.',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/entitlements/${encodeURIComponent(params.entitlementIdentifier)}/promotional`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = { duration: params.duration }
if (params.startTimeMs !== undefined) body.start_time_ms = params.startTimeMs
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const subscriber = data.subscriber ?? {}
return {
success: true,
output: {
subscriber: {
first_seen: subscriber.first_seen ?? '',
original_app_user_id: subscriber.original_app_user_id ?? '',
subscriptions: subscriber.subscriptions ?? {},
entitlements: subscriber.entitlements ?? {},
},
},
}
},
outputs: {
subscriber: {
...SUBSCRIBER_OUTPUT,
description: 'The updated subscriber object after granting the entitlement',
},
},
}
+11
View File
@@ -0,0 +1,11 @@
export { revenuecatCreatePurchaseTool } from './create_purchase'
export { revenuecatDeferGoogleSubscriptionTool } from './defer_google_subscription'
export { revenuecatDeleteCustomerTool } from './delete_customer'
export { revenuecatGetCustomerTool } from './get_customer'
export { revenuecatGrantEntitlementTool } from './grant_entitlement'
export { revenuecatListOfferingsTool } from './list_offerings'
export { revenuecatRefundGoogleSubscriptionTool } from './refund_google_subscription'
export { revenuecatRevokeEntitlementTool } from './revoke_entitlement'
export { revenuecatRevokeGoogleSubscriptionTool } from './revoke_google_subscription'
export * from './types'
export { revenuecatUpdateSubscriberAttributesTool } from './update_subscriber_attributes'
@@ -0,0 +1,98 @@
import type { ListOfferingsParams, ListOfferingsResponse } from '@/tools/revenuecat/types'
import {
OFFERING_OUTPUT_PROPERTIES,
OFFERINGS_METADATA_OUTPUT_PROPERTIES,
} from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatListOfferingsTool: ToolConfig<ListOfferingsParams, ListOfferingsResponse> = {
id: 'revenuecat_list_offerings',
name: 'RevenueCat List Offerings',
description: 'List all offerings configured for the project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat API key',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'An app user ID to retrieve offerings for',
},
platform: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Platform to filter offerings (ios, android, stripe, etc.)',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/offerings`,
method: 'GET',
headers: (params) => {
const headers: Record<string, string> = {
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}
if (params.platform) {
headers['X-Platform'] = params.platform
}
return headers
},
},
transformResponse: async (response) => {
const data = await response.json()
const offerings = data.offerings ?? []
const currentOfferingId = data.current_offering_id ?? null
return {
success: true,
output: {
current_offering_id: currentOfferingId,
offerings: offerings.map((offering: Record<string, unknown>) => ({
identifier: (offering.identifier as string) ?? '',
description: (offering.description as string) ?? null,
packages: ((offering.packages as Array<Record<string, unknown>>) ?? []).map(
(pkg: Record<string, unknown>) => ({
identifier: (pkg.identifier as string) ?? '',
platform_product_identifier: (pkg.platform_product_identifier as string) ?? null,
})
),
})),
metadata: {
count: offerings.length,
current_offering_id: currentOfferingId,
},
},
}
},
outputs: {
current_offering_id: {
type: 'string',
description: 'The identifier of the current offering',
optional: true,
},
offerings: {
type: 'array',
description: 'List of offerings',
items: {
type: 'object',
properties: OFFERING_OUTPUT_PROPERTIES,
},
},
metadata: {
type: 'object',
description: 'Offerings metadata',
properties: OFFERINGS_METADATA_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,71 @@
import type {
RefundGoogleSubscriptionParams,
RefundGoogleSubscriptionResponse,
} from '@/tools/revenuecat/types'
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatRefundGoogleSubscriptionTool: ToolConfig<
RefundGoogleSubscriptionParams,
RefundGoogleSubscriptionResponse
> = {
id: 'revenuecat_refund_google_subscription',
name: 'RevenueCat Refund Google Subscription',
description: 'Refund and optionally revoke a Google Play subscription (Google Play only)',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
productId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The Google Play product identifier of the subscription to refund',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/subscriptions/${encodeURIComponent(params.productId)}/refund`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const subscriber = data.subscriber ?? {}
return {
success: true,
output: {
subscriber: {
first_seen: subscriber.first_seen ?? '',
original_app_user_id: subscriber.original_app_user_id ?? '',
subscriptions: subscriber.subscriptions ?? {},
entitlements: subscriber.entitlements ?? {},
},
},
}
},
outputs: {
subscriber: {
...SUBSCRIBER_OUTPUT,
description: 'The updated subscriber object after refunding the Google subscription',
},
},
}
@@ -0,0 +1,68 @@
import type { RevokeEntitlementParams, RevokeEntitlementResponse } from '@/tools/revenuecat/types'
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatRevokeEntitlementTool: ToolConfig<
RevokeEntitlementParams,
RevokeEntitlementResponse
> = {
id: 'revenuecat_revoke_entitlement',
name: 'RevenueCat Revoke Entitlement',
description: 'Revoke all promotional entitlements for a specific entitlement identifier',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
entitlementIdentifier: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The entitlement identifier to revoke',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/entitlements/${encodeURIComponent(params.entitlementIdentifier)}/revoke_promotionals`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const subscriber = data.subscriber ?? {}
return {
success: true,
output: {
subscriber: {
first_seen: subscriber.first_seen ?? '',
original_app_user_id: subscriber.original_app_user_id ?? '',
subscriptions: subscriber.subscriptions ?? {},
entitlements: subscriber.entitlements ?? {},
},
},
}
},
outputs: {
subscriber: {
...SUBSCRIBER_OUTPUT,
description: 'The updated subscriber object after revoking the entitlement',
},
},
}
@@ -0,0 +1,72 @@
import type {
RevokeGoogleSubscriptionParams,
RevokeGoogleSubscriptionResponse,
} from '@/tools/revenuecat/types'
import { SUBSCRIBER_OUTPUT } from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatRevokeGoogleSubscriptionTool: ToolConfig<
RevokeGoogleSubscriptionParams,
RevokeGoogleSubscriptionResponse
> = {
id: 'revenuecat_revoke_google_subscription',
name: 'RevenueCat Revoke Google Subscription',
description:
'Immediately revoke access to a Google Play subscription and issue a refund (Google Play only)',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
productId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The Google Play product identifier of the subscription to revoke',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/subscriptions/${encodeURIComponent(params.productId)}/revoke`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const subscriber = data.subscriber ?? {}
return {
success: true,
output: {
subscriber: {
first_seen: subscriber.first_seen ?? '',
original_app_user_id: subscriber.original_app_user_id ?? '',
subscriptions: subscriber.subscriptions ?? {},
entitlements: subscriber.entitlements ?? {},
},
},
}
},
outputs: {
subscriber: {
...SUBSCRIBER_OUTPUT,
description: 'The updated subscriber object after revoking the Google subscription',
},
},
}
+381
View File
@@ -0,0 +1,381 @@
import type { OutputProperty, ToolResponse } from '@/tools/types'
/**
* Shared output property definitions for RevenueCat API responses.
* Based on official RevenueCat API v1 documentation.
*/
export const SUBSCRIPTION_OUTPUT_PROPERTIES = {
store_transaction_id: {
type: 'string',
description: 'Store transaction identifier',
optional: true,
},
original_transaction_id: {
type: 'string',
description: 'Original transaction identifier',
optional: true,
},
purchase_date: { type: 'string', description: 'ISO 8601 purchase date', optional: true },
original_purchase_date: {
type: 'string',
description: 'ISO 8601 date of the original purchase',
optional: true,
},
expires_date: { type: 'string', description: 'ISO 8601 expiration date', optional: true },
is_sandbox: {
type: 'boolean',
description: 'Whether this is a sandbox purchase',
optional: true,
},
unsubscribe_detected_at: {
type: 'string',
description: 'ISO 8601 date when unsubscribe was detected',
optional: true,
},
billing_issues_detected_at: {
type: 'string',
description: 'ISO 8601 date when billing issues were detected',
optional: true,
},
grace_period_expires_date: {
type: 'string',
description: 'ISO 8601 grace period expiration date',
optional: true,
},
ownership_type: {
type: 'string',
description: 'Ownership type (purchased, family_shared)',
optional: true,
},
period_type: {
type: 'string',
description: 'Period type (normal, trial, intro, promotional, prepaid)',
optional: true,
},
store: {
type: 'string',
description: 'Store the subscription was purchased from (app_store, play_store, stripe, etc.)',
optional: true,
},
refunded_at: {
type: 'string',
description: 'ISO 8601 date when subscription was refunded',
optional: true,
},
auto_resume_date: {
type: 'string',
description: 'ISO 8601 date when a paused subscription will auto-resume',
optional: true,
},
product_plan_identifier: {
type: 'string',
description: 'Google Play base plan identifier (for products set up after Feb 2023)',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const ENTITLEMENT_OUTPUT_PROPERTIES = {
grant_date: { type: 'string', description: 'ISO 8601 grant date', optional: true },
expires_date: { type: 'string', description: 'ISO 8601 expiration date', optional: true },
product_identifier: { type: 'string', description: 'Product identifier', optional: true },
is_active: { type: 'boolean', description: 'Whether the entitlement is active' },
will_renew: {
type: 'boolean',
description: 'Whether the entitlement will renew',
optional: true,
},
period_type: {
type: 'string',
description: 'Period type (normal, trial, intro, promotional)',
optional: true,
},
purchase_date: {
type: 'string',
description: 'ISO 8601 date of the latest purchase or renewal',
optional: true,
},
store: {
type: 'string',
description: 'Store the entitlement was granted from',
optional: true,
},
grace_period_expires_date: {
type: 'string',
description: 'ISO 8601 grace period expiration date',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const SUBSCRIBER_OUTPUT_PROPERTIES = {
first_seen: { type: 'string', description: 'ISO 8601 date when subscriber was first seen' },
original_app_user_id: { type: 'string', description: 'Original app user ID' },
original_purchase_date: {
type: 'string',
description: 'ISO 8601 date of original purchase',
optional: true,
},
management_url: {
type: 'string',
description: 'URL for managing the subscriber subscriptions',
optional: true,
},
subscriptions: {
type: 'object',
description: 'Map of product identifiers to subscription objects',
properties: SUBSCRIPTION_OUTPUT_PROPERTIES,
},
entitlements: {
type: 'object',
description: 'Map of entitlement identifiers to entitlement objects',
properties: ENTITLEMENT_OUTPUT_PROPERTIES,
},
non_subscriptions: {
type: 'object',
description: 'Map of non-subscription product identifiers to arrays of purchase objects',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const SUBSCRIBER_OUTPUT: OutputProperty = {
type: 'object',
description: 'RevenueCat subscriber object',
properties: SUBSCRIBER_OUTPUT_PROPERTIES,
}
export const OFFERING_PACKAGE_OUTPUT_PROPERTIES = {
identifier: { type: 'string', description: 'Package identifier' },
platform_product_identifier: {
type: 'string',
description: 'Platform-specific product identifier',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const OFFERING_OUTPUT_PROPERTIES = {
identifier: { type: 'string', description: 'Offering identifier' },
description: { type: 'string', description: 'Offering description', optional: true },
packages: {
type: 'array',
description: 'List of packages in the offering',
items: {
type: 'object',
properties: OFFERING_PACKAGE_OUTPUT_PROPERTIES,
},
},
} as const satisfies Record<string, OutputProperty>
export const DELETE_OUTPUT_PROPERTIES = {
deleted: { type: 'boolean', description: 'Whether the subscriber was deleted' },
app_user_id: { type: 'string', description: 'The deleted app user ID' },
} as const satisfies Record<string, OutputProperty>
export const METADATA_OUTPUT_PROPERTIES = {
app_user_id: { type: 'string', description: 'The app user ID' },
first_seen: { type: 'string', description: 'ISO 8601 date when the subscriber was first seen' },
active_entitlements: { type: 'number', description: 'Number of active entitlements' },
active_subscriptions: { type: 'number', description: 'Number of active subscriptions' },
} as const satisfies Record<string, OutputProperty>
export const OFFERINGS_METADATA_OUTPUT_PROPERTIES = {
count: { type: 'number', description: 'Number of offerings returned' },
current_offering_id: {
type: 'string',
description: 'Current offering identifier',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
/**
* Base params interface for RevenueCat API calls
*/
export interface RevenueCatBaseParams {
apiKey: string
}
export interface GetCustomerParams extends RevenueCatBaseParams {
appUserId: string
}
export interface DeleteCustomerParams extends RevenueCatBaseParams {
appUserId: string
}
export interface GrantEntitlementParams extends RevenueCatBaseParams {
appUserId: string
entitlementIdentifier: string
duration: string
startTimeMs?: number
}
export interface RevokeEntitlementParams extends RevenueCatBaseParams {
appUserId: string
entitlementIdentifier: string
}
export interface ListOfferingsParams extends RevenueCatBaseParams {
appUserId: string
platform?: string
}
export interface CreatePurchaseParams extends RevenueCatBaseParams {
appUserId: string
fetchToken: string
productId: string
price?: number
currency?: string
isRestore?: boolean
platform?: string
}
export interface UpdateSubscriberAttributesParams extends RevenueCatBaseParams {
appUserId: string
attributes: string
}
export interface DeferGoogleSubscriptionParams extends RevenueCatBaseParams {
appUserId: string
productId: string
extendByDays: number
}
export interface RefundGoogleSubscriptionParams extends RevenueCatBaseParams {
appUserId: string
productId: string
}
export interface RevokeGoogleSubscriptionParams extends RevenueCatBaseParams {
appUserId: string
productId: string
}
export interface CustomerResponse extends ToolResponse {
output: {
subscriber: {
first_seen: string
original_app_user_id: string
original_purchase_date: string | null
management_url: string | null
subscriptions: Record<string, unknown>
entitlements: Record<string, unknown>
non_subscriptions: Record<string, unknown>
}
metadata: {
app_user_id: string
first_seen: string
active_entitlements: number
active_subscriptions: number
}
}
}
export interface DeleteCustomerResponse extends ToolResponse {
output: {
deleted: boolean
app_user_id: string
}
}
export interface GrantEntitlementResponse extends ToolResponse {
output: {
subscriber: {
first_seen: string
original_app_user_id: string
subscriptions: Record<string, unknown>
entitlements: Record<string, unknown>
}
}
}
export interface RevokeEntitlementResponse extends ToolResponse {
output: {
subscriber: {
first_seen: string
original_app_user_id: string
subscriptions: Record<string, unknown>
entitlements: Record<string, unknown>
}
}
}
export interface ListOfferingsResponse extends ToolResponse {
output: {
current_offering_id: string | null
offerings: Array<{
identifier: string
description: string | null
packages: Array<{
identifier: string
platform_product_identifier: string | null
}>
}>
metadata: {
count: number
current_offering_id: string | null
}
}
}
export interface CreatePurchaseResponse extends ToolResponse {
output: {
subscriber: {
first_seen: string
original_app_user_id: string
subscriptions: Record<string, unknown>
entitlements: Record<string, unknown>
non_subscriptions: Record<string, unknown>
}
}
}
export interface UpdateSubscriberAttributesResponse extends ToolResponse {
output: {
updated: boolean
app_user_id: string
}
}
export interface DeferGoogleSubscriptionResponse extends ToolResponse {
output: {
subscriber: {
first_seen: string
original_app_user_id: string
subscriptions: Record<string, unknown>
entitlements: Record<string, unknown>
}
}
}
export interface RefundGoogleSubscriptionResponse extends ToolResponse {
output: {
subscriber: {
first_seen: string
original_app_user_id: string
subscriptions: Record<string, unknown>
entitlements: Record<string, unknown>
}
}
}
export interface RevokeGoogleSubscriptionResponse extends ToolResponse {
output: {
subscriber: {
first_seen: string
original_app_user_id: string
subscriptions: Record<string, unknown>
entitlements: Record<string, unknown>
}
}
}
export type RevenueCatResponse =
| CustomerResponse
| DeleteCustomerResponse
| GrantEntitlementResponse
| RevokeEntitlementResponse
| ListOfferingsResponse
| CreatePurchaseResponse
| UpdateSubscriberAttributesResponse
| DeferGoogleSubscriptionResponse
| RefundGoogleSubscriptionResponse
| RevokeGoogleSubscriptionResponse
@@ -0,0 +1,74 @@
import type {
UpdateSubscriberAttributesParams,
UpdateSubscriberAttributesResponse,
} from '@/tools/revenuecat/types'
import type { ToolConfig } from '@/tools/types'
export const revenuecatUpdateSubscriberAttributesTool: ToolConfig<
UpdateSubscriberAttributesParams,
UpdateSubscriberAttributesResponse
> = {
id: 'revenuecat_update_subscriber_attributes',
name: 'RevenueCat Update Subscriber Attributes',
description:
'Update custom subscriber attributes (e.g., $email, $displayName, or custom key-value pairs)',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'RevenueCat secret API key (sk_...)',
},
appUserId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app user ID of the subscriber',
},
attributes: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'JSON object of attributes to set. Each key maps to an object with a "value" field. Example: {"$email": {"value": "user@example.com"}, "$displayName": {"value": "John"}}',
},
},
request: {
url: (params) =>
`https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(params.appUserId)}/attributes`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const attributes =
typeof params.attributes === 'string' ? JSON.parse(params.attributes) : params.attributes
return { attributes }
},
},
transformResponse: async (response, params) => {
return {
success: response.ok,
output: {
updated: response.ok,
app_user_id: params?.appUserId ?? '',
},
}
},
outputs: {
updated: {
type: 'boolean',
description: 'Whether the subscriber attributes were successfully updated',
},
app_user_id: {
type: 'string',
description: 'The app user ID of the updated subscriber',
},
},
}
+16
View File
@@ -0,0 +1,16 @@
export { upstashRedisCommandTool } from '@/tools/upstash/redis_command'
export { upstashRedisDeleteTool } from '@/tools/upstash/redis_delete'
export { upstashRedisExistsTool } from '@/tools/upstash/redis_exists'
export { upstashRedisExpireTool } from '@/tools/upstash/redis_expire'
export { upstashRedisGetTool } from '@/tools/upstash/redis_get'
export { upstashRedisHGetTool } from '@/tools/upstash/redis_hget'
export { upstashRedisHGetAllTool } from '@/tools/upstash/redis_hgetall'
export { upstashRedisHSetTool } from '@/tools/upstash/redis_hset'
export { upstashRedisIncrTool } from '@/tools/upstash/redis_incr'
export { upstashRedisIncrbyTool } from '@/tools/upstash/redis_incrby'
export { upstashRedisKeysTool } from '@/tools/upstash/redis_keys'
export { upstashRedisLPushTool } from '@/tools/upstash/redis_lpush'
export { upstashRedisLRangeTool } from '@/tools/upstash/redis_lrange'
export { upstashRedisSetTool } from '@/tools/upstash/redis_set'
export { upstashRedisSetnxTool } from '@/tools/upstash/redis_setnx'
export { upstashRedisTtlTool } from '@/tools/upstash/redis_ttl'
+76
View File
@@ -0,0 +1,76 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisCommandParams, UpstashRedisCommandResponse } from '@/tools/upstash/types'
export const upstashRedisCommandTool: ToolConfig<
UpstashRedisCommandParams,
UpstashRedisCommandResponse
> = {
id: 'upstash_redis_command',
name: 'Upstash Redis Command',
description:
'Execute an arbitrary Redis command against Upstash Redis. Pass the full command as a JSON array (e.g., ["HSET", "myhash", "field1", "value1"]).',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
command: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Redis command as a JSON array (e.g., ["HSET", "myhash", "field1", "value1"]) or a simple command string (e.g., "PING")',
},
},
request: {
url: (params) => params.restUrl,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
try {
const parsed = JSON.parse(params.command)
if (Array.isArray(parsed)) {
return parsed
}
return [String(parsed)]
} catch {
return params.command.trim().split(/\s+/)
}
},
},
transformResponse: async (response: Response, params?: UpstashRedisCommandParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to execute Redis command')
}
return {
success: true,
output: {
command: params?.command ?? '',
result: data.result ?? null,
},
}
},
outputs: {
command: { type: 'string', description: 'The command that was executed' },
result: { type: 'json', description: 'The result of the Redis command' },
},
}
+65
View File
@@ -0,0 +1,65 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisDeleteParams, UpstashRedisDeleteResponse } from '@/tools/upstash/types'
export const upstashRedisDeleteTool: ToolConfig<
UpstashRedisDeleteParams,
UpstashRedisDeleteResponse
> = {
id: 'upstash_redis_delete',
name: 'Upstash Redis Delete',
description: 'Delete a key from Upstash Redis.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to delete',
},
},
request: {
url: (params) => `${params.restUrl}/del/${encodeURIComponent(params.key)}`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisDeleteParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to delete key from Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
deletedCount: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was deleted' },
deletedCount: {
type: 'number',
description: 'Number of keys deleted (0 if key did not exist, 1 if deleted)',
},
},
}
+63
View File
@@ -0,0 +1,63 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisExistsParams, UpstashRedisExistsResponse } from '@/tools/upstash/types'
export const upstashRedisExistsTool: ToolConfig<
UpstashRedisExistsParams,
UpstashRedisExistsResponse
> = {
id: 'upstash_redis_exists',
name: 'Upstash Redis EXISTS',
description:
'Check if a key exists in Upstash Redis. Returns true if the key exists, false otherwise.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to check',
},
},
request: {
url: (params) => `${params.restUrl}/exists/${encodeURIComponent(params.key)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisExistsParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to check key existence in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
exists: data.result === 1,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was checked' },
exists: { type: 'boolean', description: 'Whether the key exists (true) or not (false)' },
},
}
+71
View File
@@ -0,0 +1,71 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisExpireParams, UpstashRedisExpireResponse } from '@/tools/upstash/types'
export const upstashRedisExpireTool: ToolConfig<
UpstashRedisExpireParams,
UpstashRedisExpireResponse
> = {
id: 'upstash_redis_expire',
name: 'Upstash Redis EXPIRE',
description: 'Set a timeout on a key in Upstash Redis. After the timeout, the key is deleted.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to set expiration on',
},
seconds: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Timeout in seconds',
},
},
request: {
url: (params) => `${params.restUrl}/expire/${encodeURIComponent(params.key)}/${params.seconds}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisExpireParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to EXPIRE in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
result: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that expiration was set on' },
result: {
type: 'number',
description: '1 if the timeout was set, 0 if the key does not exist',
},
},
}
+59
View File
@@ -0,0 +1,59 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisGetParams, UpstashRedisGetResponse } from '@/tools/upstash/types'
export const upstashRedisGetTool: ToolConfig<UpstashRedisGetParams, UpstashRedisGetResponse> = {
id: 'upstash_redis_get',
name: 'Upstash Redis Get',
description: 'Get the value of a key from Upstash Redis.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to retrieve',
},
},
request: {
url: (params) => `${params.restUrl}/get/${encodeURIComponent(params.key)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisGetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get key from Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? null,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was retrieved' },
value: { type: 'json', description: 'The value of the key (string), or null if not found' },
},
}
+71
View File
@@ -0,0 +1,71 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisHGetParams, UpstashRedisHGetResponse } from '@/tools/upstash/types'
export const upstashRedisHGetTool: ToolConfig<UpstashRedisHGetParams, UpstashRedisHGetResponse> = {
id: 'upstash_redis_hget',
name: 'Upstash Redis HGET',
description: 'Get the value of a field in a hash stored at a key in Upstash Redis.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hash key',
},
field: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The field name to retrieve',
},
},
request: {
url: (params) =>
`${params.restUrl}/hget/${encodeURIComponent(params.key)}/${encodeURIComponent(params.field)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisHGetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to HGET from Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
field: params?.field ?? '',
value: data.result ?? null,
},
}
},
outputs: {
key: { type: 'string', description: 'The hash key' },
field: { type: 'string', description: 'The field that was retrieved' },
value: {
type: 'json',
description: 'The value of the hash field (string), or null if not found',
},
},
}
+73
View File
@@ -0,0 +1,73 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisHGetAllParams, UpstashRedisHGetAllResponse } from '@/tools/upstash/types'
export const upstashRedisHGetAllTool: ToolConfig<
UpstashRedisHGetAllParams,
UpstashRedisHGetAllResponse
> = {
id: 'upstash_redis_hgetall',
name: 'Upstash Redis HGETALL',
description: 'Get all fields and values of a hash stored at a key in Upstash Redis.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hash key',
},
},
request: {
url: (params) => `${params.restUrl}/hgetall/${encodeURIComponent(params.key)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisHGetAllParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to HGETALL from Upstash Redis')
}
const result = data.result ?? []
const fields: Record<string, string> = {}
for (let i = 0; i < result.length; i += 2) {
fields[result[i]] = result[i + 1] ?? null
}
return {
success: true,
output: {
key: params?.key ?? '',
fields,
fieldCount: Object.keys(fields).length,
},
}
},
outputs: {
key: { type: 'string', description: 'The hash key' },
fields: {
type: 'object',
description: 'All field-value pairs in the hash, keyed by field name',
},
fieldCount: { type: 'number', description: 'Number of fields in the hash' },
},
}
+79
View File
@@ -0,0 +1,79 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisHSetParams, UpstashRedisHSetResponse } from '@/tools/upstash/types'
export const upstashRedisHSetTool: ToolConfig<UpstashRedisHSetParams, UpstashRedisHSetResponse> = {
id: 'upstash_redis_hset',
name: 'Upstash Redis HSET',
description: 'Set a field in a hash stored at a key in Upstash Redis.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hash key',
},
field: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The field name within the hash',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to store in the hash field',
},
},
request: {
url: (params) =>
`${params.restUrl}/hset/${encodeURIComponent(params.key)}/${encodeURIComponent(params.field)}`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
'Content-Type': 'text/plain',
}),
body: (params) => params.value,
},
transformResponse: async (response: Response, params?: UpstashRedisHSetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to HSET in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
field: params?.field ?? '',
result: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The hash key' },
field: { type: 'string', description: 'The field that was set' },
result: {
type: 'number',
description: 'Number of new fields added (0 if field was updated, 1 if new)',
},
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisIncrParams, UpstashRedisIncrResponse } from '@/tools/upstash/types'
export const upstashRedisIncrTool: ToolConfig<UpstashRedisIncrParams, UpstashRedisIncrResponse> = {
id: 'upstash_redis_incr',
name: 'Upstash Redis INCR',
description:
'Atomically increment the integer value of a key by one in Upstash Redis. If the key does not exist, it is set to 0 before incrementing.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to increment',
},
},
request: {
url: (params) => `${params.restUrl}/incr/${encodeURIComponent(params.key)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisIncrParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to INCR in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was incremented' },
value: { type: 'number', description: 'The new value after incrementing' },
},
}
+70
View File
@@ -0,0 +1,70 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisIncrbyParams, UpstashRedisIncrbyResponse } from '@/tools/upstash/types'
export const upstashRedisIncrbyTool: ToolConfig<
UpstashRedisIncrbyParams,
UpstashRedisIncrbyResponse
> = {
id: 'upstash_redis_incrby',
name: 'Upstash Redis INCRBY',
description:
'Increment the integer value of a key by a given amount. Use a negative value to decrement. If the key does not exist, it is set to 0 before the operation.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to increment',
},
increment: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Amount to increment by (use negative value to decrement)',
},
},
request: {
url: (params) =>
`${params.restUrl}/incrby/${encodeURIComponent(params.key)}/${params.increment}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisIncrbyParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to INCRBY in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
value: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was incremented' },
value: { type: 'number', description: 'The new value after incrementing' },
},
}
+70
View File
@@ -0,0 +1,70 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisKeysParams, UpstashRedisKeysResponse } from '@/tools/upstash/types'
export const upstashRedisKeysTool: ToolConfig<UpstashRedisKeysParams, UpstashRedisKeysResponse> = {
id: 'upstash_redis_keys',
name: 'Upstash Redis Keys',
description: 'List keys matching a pattern in Upstash Redis. Defaults to listing all keys (*).',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
pattern: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pattern to match keys (e.g., "user:*"). Defaults to "*" for all keys.',
},
},
request: {
url: (params) => {
const pattern = params.pattern || '*'
return `${params.restUrl}/keys/${encodeURIComponent(pattern)}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisKeysParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to list keys from Upstash Redis')
}
const keys = data.result ?? []
return {
success: true,
output: {
pattern: params?.pattern || '*',
keys,
count: keys.length,
},
}
},
outputs: {
pattern: { type: 'string', description: 'The pattern used to match keys' },
keys: {
type: 'array',
description: 'List of keys matching the pattern',
items: { type: 'string', description: 'A Redis key' },
},
count: { type: 'number', description: 'Number of keys found' },
},
}
+69
View File
@@ -0,0 +1,69 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisLPushParams, UpstashRedisLPushResponse } from '@/tools/upstash/types'
export const upstashRedisLPushTool: ToolConfig<UpstashRedisLPushParams, UpstashRedisLPushResponse> =
{
id: 'upstash_redis_lpush',
name: 'Upstash Redis LPUSH',
description:
'Prepend a value to the beginning of a list in Upstash Redis. Creates the list if it does not exist.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to prepend to the list',
},
},
request: {
url: (params) => `${params.restUrl}/lpush/${encodeURIComponent(params.key)}`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
'Content-Type': 'text/plain',
}),
body: (params) => params.value,
},
transformResponse: async (response: Response, params?: UpstashRedisLPushParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to LPUSH in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
length: data.result ?? 0,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
length: { type: 'number', description: 'The length of the list after the push' },
},
}
+84
View File
@@ -0,0 +1,84 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisLRangeParams, UpstashRedisLRangeResponse } from '@/tools/upstash/types'
export const upstashRedisLRangeTool: ToolConfig<
UpstashRedisLRangeParams,
UpstashRedisLRangeResponse
> = {
id: 'upstash_redis_lrange',
name: 'Upstash Redis LRANGE',
description:
'Get a range of elements from a list in Upstash Redis. Use 0 and -1 for start and stop to get all elements.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The list key',
},
start: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start index (0-based, negative values count from end)',
},
stop: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Stop index (inclusive, -1 for last element)',
},
},
request: {
url: (params) =>
`${params.restUrl}/lrange/${encodeURIComponent(params.key)}/${params.start}/${params.stop}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisLRangeParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to LRANGE from Upstash Redis')
}
const values = data.result ?? []
return {
success: true,
output: {
key: params?.key ?? '',
values,
count: values.length,
},
}
},
outputs: {
key: { type: 'string', description: 'The list key' },
values: {
type: 'array',
description: 'List of elements in the specified range',
items: { type: 'string', description: 'A list element' },
},
count: { type: 'number', description: 'Number of elements returned' },
},
}
+80
View File
@@ -0,0 +1,80 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisSetParams, UpstashRedisSetResponse } from '@/tools/upstash/types'
export const upstashRedisSetTool: ToolConfig<UpstashRedisSetParams, UpstashRedisSetResponse> = {
id: 'upstash_redis_set',
name: 'Upstash Redis Set',
description:
'Set the value of a key in Upstash Redis with an optional expiration time in seconds.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to set',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to store',
},
ex: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Expiration time in seconds (optional)',
},
},
request: {
url: (params) => {
const base = `${params.restUrl}/set/${encodeURIComponent(params.key)}`
if (params.ex) {
return `${base}?EX=${params.ex}`
}
return base
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
'Content-Type': 'text/plain',
}),
body: (params) => params.value,
},
transformResponse: async (response: Response, params?: UpstashRedisSetParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to set key in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
result: data.result ?? 'OK',
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was set' },
result: { type: 'string', description: 'The result of the SET operation (typically "OK")' },
},
}
+72
View File
@@ -0,0 +1,72 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisSetnxParams, UpstashRedisSetnxResponse } from '@/tools/upstash/types'
export const upstashRedisSetnxTool: ToolConfig<UpstashRedisSetnxParams, UpstashRedisSetnxResponse> =
{
id: 'upstash_redis_setnx',
name: 'Upstash Redis SETNX',
description:
'Set the value of a key only if it does not already exist. Returns true if the key was set, false if it already existed.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to set',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value to store if the key does not exist',
},
},
request: {
url: (params) => `${params.restUrl}/setnx/${encodeURIComponent(params.key)}`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
'Content-Type': 'text/plain',
}),
body: (params) => params.value,
},
transformResponse: async (response: Response, params?: UpstashRedisSetnxParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to SETNX in Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
wasSet: data.result === 1,
},
}
},
outputs: {
key: { type: 'string', description: 'The key that was attempted to set' },
wasSet: {
type: 'boolean',
description: 'Whether the key was set (true) or already existed (false)',
},
},
}
+64
View File
@@ -0,0 +1,64 @@
import type { ToolConfig } from '@/tools/types'
import type { UpstashRedisTtlParams, UpstashRedisTtlResponse } from '@/tools/upstash/types'
export const upstashRedisTtlTool: ToolConfig<UpstashRedisTtlParams, UpstashRedisTtlResponse> = {
id: 'upstash_redis_ttl',
name: 'Upstash Redis TTL',
description:
'Get the remaining time to live of a key in Upstash Redis. Returns -1 if the key has no expiration, -2 if the key does not exist.',
version: '1.0.0',
params: {
restUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST URL',
},
restToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Upstash Redis REST Token',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The key to check TTL for',
},
},
request: {
url: (params) => `${params.restUrl}/ttl/${encodeURIComponent(params.key)}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.restToken}`,
}),
},
transformResponse: async (response: Response, params?: UpstashRedisTtlParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get TTL from Upstash Redis')
}
return {
success: true,
output: {
key: params?.key ?? '',
ttl: data.result ?? -2,
},
}
},
outputs: {
key: { type: 'string', description: 'The key checked' },
ttl: {
type: 'number',
description:
'Remaining TTL in seconds. Positive integer if the key has a TTL set, -1 if the key exists with no expiration, -2 if the key does not exist.',
},
},
}
+198
View File
@@ -0,0 +1,198 @@
import type { ToolResponse } from '@/tools/types'
export interface UpstashRedisBaseParams {
restUrl: string
restToken: string
}
export interface UpstashRedisGetParams extends UpstashRedisBaseParams {
key: string
}
export interface UpstashRedisSetParams extends UpstashRedisBaseParams {
key: string
value: string
ex?: number
}
export interface UpstashRedisDeleteParams extends UpstashRedisBaseParams {
key: string
}
export interface UpstashRedisKeysParams extends UpstashRedisBaseParams {
pattern?: string
}
export interface UpstashRedisCommandParams extends UpstashRedisBaseParams {
command: string
}
export interface UpstashRedisHSetParams extends UpstashRedisBaseParams {
key: string
field: string
value: string
}
export interface UpstashRedisHGetParams extends UpstashRedisBaseParams {
key: string
field: string
}
export interface UpstashRedisHGetAllParams extends UpstashRedisBaseParams {
key: string
}
export interface UpstashRedisIncrParams extends UpstashRedisBaseParams {
key: string
}
export interface UpstashRedisExpireParams extends UpstashRedisBaseParams {
key: string
seconds: number
}
export interface UpstashRedisTtlParams extends UpstashRedisBaseParams {
key: string
}
export interface UpstashRedisLPushParams extends UpstashRedisBaseParams {
key: string
value: string
}
export interface UpstashRedisLRangeParams extends UpstashRedisBaseParams {
key: string
start: number
stop: number
}
export interface UpstashRedisGetResponse extends ToolResponse {
output: {
key: string
value: string | null
}
}
export interface UpstashRedisSetResponse extends ToolResponse {
output: {
key: string
result: string
}
}
export interface UpstashRedisDeleteResponse extends ToolResponse {
output: {
key: string
deletedCount: number
}
}
export interface UpstashRedisKeysResponse extends ToolResponse {
output: {
pattern: string
keys: string[]
count: number
}
}
export interface UpstashRedisCommandResponse extends ToolResponse {
output: {
command: string
result: unknown
}
}
export interface UpstashRedisHSetResponse extends ToolResponse {
output: {
key: string
field: string
result: number
}
}
export interface UpstashRedisHGetResponse extends ToolResponse {
output: {
key: string
field: string
value: string | null
}
}
export interface UpstashRedisHGetAllResponse extends ToolResponse {
output: {
key: string
fields: Record<string, string>
fieldCount: number
}
}
export interface UpstashRedisIncrResponse extends ToolResponse {
output: {
key: string
value: number
}
}
export interface UpstashRedisExpireResponse extends ToolResponse {
output: {
key: string
result: number
}
}
export interface UpstashRedisTtlResponse extends ToolResponse {
output: {
key: string
ttl: number
}
}
export interface UpstashRedisLPushResponse extends ToolResponse {
output: {
key: string
length: number
}
}
export interface UpstashRedisLRangeResponse extends ToolResponse {
output: {
key: string
values: string[]
count: number
}
}
export interface UpstashRedisExistsParams extends UpstashRedisBaseParams {
key: string
}
export interface UpstashRedisExistsResponse extends ToolResponse {
output: {
key: string
exists: boolean
}
}
export interface UpstashRedisSetnxParams extends UpstashRedisBaseParams {
key: string
value: string
}
export interface UpstashRedisSetnxResponse extends ToolResponse {
output: {
key: string
wasSet: boolean
}
}
export interface UpstashRedisIncrbyParams extends UpstashRedisBaseParams {
key: string
increment: number
}
export interface UpstashRedisIncrbyResponse extends ToolResponse {
output: {
key: string
value: number
}
}