mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
improvement(airtable): added more tools (#3396)
This commit is contained in:
@@ -26,12 +26,63 @@ In Sim, the Airtable integration enables your agents to interact with your Airta
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrates Airtable into the workflow. Can create, get, list, or update Airtable records. Can be used in trigger mode to trigger a workflow when an update is made to an Airtable table.
|
||||
Integrates Airtable into the workflow. Can list bases, list tables (with schema), and create, get, list, or update records. Can also be used in trigger mode to trigger a workflow when an update is made to an Airtable table.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `airtable_list_bases`
|
||||
|
||||
List all Airtable bases the user has access to
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `offset` | string | No | Pagination offset for retrieving additional bases |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `bases` | array | List of Airtable bases |
|
||||
| ↳ `id` | string | Base ID \(starts with "app"\) |
|
||||
| ↳ `name` | string | Base name |
|
||||
| ↳ `permissionLevel` | string | Permission level \(none, read, comment, edit, create\) |
|
||||
| `metadata` | json | Pagination and count metadata |
|
||||
| ↳ `offset` | string | Offset for next page of results |
|
||||
| ↳ `totalBases` | number | Number of bases returned |
|
||||
|
||||
### `airtable_list_tables`
|
||||
|
||||
List all tables and their schema in an Airtable base
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `baseId` | string | Yes | Airtable base ID \(starts with "app", e.g., "appXXXXXXXXXXXXXX"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tables` | array | List of tables in the base with their schema |
|
||||
| ↳ `id` | string | Table ID \(starts with "tbl"\) |
|
||||
| ↳ `name` | string | Table name |
|
||||
| ↳ `description` | string | Table description |
|
||||
| ↳ `primaryFieldId` | string | ID of the primary field |
|
||||
| ↳ `fields` | array | List of fields in the table |
|
||||
| ↳ `id` | string | Field ID \(starts with "fld"\) |
|
||||
| ↳ `name` | string | Field name |
|
||||
| ↳ `type` | string | Field type \(singleLineText, multilineText, number, checkbox, singleSelect, multipleSelects, date, dateTime, attachment, linkedRecord, etc.\) |
|
||||
| ↳ `description` | string | Field description |
|
||||
| ↳ `options` | json | Field-specific options \(choices, etc.\) |
|
||||
| `metadata` | json | Base info and count metadata |
|
||||
| ↳ `baseId` | string | The base ID queried |
|
||||
| ↳ `totalTables` | number | Number of tables in the base |
|
||||
|
||||
### `airtable_list_records`
|
||||
|
||||
Read records from an Airtable table
|
||||
@@ -49,8 +100,13 @@ Read records from an Airtable table
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | json | Array of retrieved Airtable records |
|
||||
| `records` | array | Array of retrieved Airtable records |
|
||||
| ↳ `id` | string | Record ID |
|
||||
| ↳ `createdTime` | string | Record creation timestamp |
|
||||
| ↳ `fields` | json | Record field values |
|
||||
| `metadata` | json | Operation metadata including pagination offset and total records count |
|
||||
| ↳ `offset` | string | Pagination offset for next page |
|
||||
| ↳ `totalRecords` | number | Number of records returned |
|
||||
|
||||
### `airtable_get_record`
|
||||
|
||||
@@ -68,8 +124,12 @@ Retrieve a single record from an Airtable table by its ID
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Retrieved Airtable record with id, createdTime, and fields |
|
||||
| `metadata` | json | Operation metadata including record count |
|
||||
| `record` | json | Retrieved Airtable record |
|
||||
| ↳ `id` | string | Record ID |
|
||||
| ↳ `createdTime` | string | Record creation timestamp |
|
||||
| ↳ `fields` | json | Record field values |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| ↳ `recordCount` | number | Number of records returned \(always 1\) |
|
||||
|
||||
### `airtable_create_records`
|
||||
|
||||
@@ -88,8 +148,12 @@ Write new records to an Airtable table
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | json | Array of created Airtable records |
|
||||
| `records` | array | Array of created Airtable records |
|
||||
| ↳ `id` | string | Record ID |
|
||||
| ↳ `createdTime` | string | Record creation timestamp |
|
||||
| ↳ `fields` | json | Record field values |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| ↳ `recordCount` | number | Number of records created |
|
||||
|
||||
### `airtable_update_record`
|
||||
|
||||
@@ -108,8 +172,13 @@ Update an existing record in an Airtable table by ID
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Updated Airtable record with id, createdTime, and fields |
|
||||
| `metadata` | json | Operation metadata including record count and updated field names |
|
||||
| `record` | json | Updated Airtable record |
|
||||
| ↳ `id` | string | Record ID |
|
||||
| ↳ `createdTime` | string | Record creation timestamp |
|
||||
| ↳ `fields` | json | Record field values |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| ↳ `recordCount` | number | Number of records updated \(always 1\) |
|
||||
| ↳ `updatedFields` | array | List of field names that were updated |
|
||||
|
||||
### `airtable_update_multiple_records`
|
||||
|
||||
@@ -127,7 +196,12 @@ Update multiple existing records in an Airtable table
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | json | Array of updated Airtable records |
|
||||
| `metadata` | json | Operation metadata including record count and updated record IDs |
|
||||
| `records` | array | Array of updated Airtable records |
|
||||
| ↳ `id` | string | Record ID |
|
||||
| ↳ `createdTime` | string | Record creation timestamp |
|
||||
| ↳ `fields` | json | Record field values |
|
||||
| `metadata` | json | Operation metadata |
|
||||
| ↳ `recordCount` | number | Number of records updated |
|
||||
| ↳ `updatedRecordIds` | array | List of updated record IDs |
|
||||
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'offline.access': 'Access account when not using the application',
|
||||
'data.records:read': 'Read records',
|
||||
'data.records:write': 'Write to records',
|
||||
'schema.bases:read': 'View bases and tables',
|
||||
'webhook:manage': 'Manage webhooks',
|
||||
'page.read': 'Read Notion pages',
|
||||
'page.write': 'Write to Notion pages',
|
||||
|
||||
@@ -10,7 +10,7 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
description: 'Read, create, and update Airtable',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrates Airtable into the workflow. Can create, get, list, or update Airtable records. Can be used in trigger mode to trigger a workflow when an update is made to an Airtable table.',
|
||||
'Integrates Airtable into the workflow. Can list bases, list tables (with schema), and create, get, list, or update records. Can also be used in trigger mode to trigger a workflow when an update is made to an Airtable table.',
|
||||
docsLink: 'https://docs.sim.ai/tools/airtable',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
@@ -21,10 +21,13 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'List Bases', id: 'listBases' },
|
||||
{ label: 'List Tables', id: 'listTables' },
|
||||
{ label: 'List Records', id: 'list' },
|
||||
{ label: 'Get Record', id: 'get' },
|
||||
{ label: 'Create Records', id: 'create' },
|
||||
{ label: 'Update Record', id: 'update' },
|
||||
{ label: 'Update Multiple Records', id: 'updateMultiple' },
|
||||
],
|
||||
value: () => 'list',
|
||||
},
|
||||
@@ -38,6 +41,7 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
requiredScopes: [
|
||||
'data.records:read',
|
||||
'data.records:write',
|
||||
'schema.bases:read',
|
||||
'user.email:read',
|
||||
'webhook:manage',
|
||||
],
|
||||
@@ -59,7 +63,8 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your base ID (e.g., appXXXXXXXXXXXXXX)',
|
||||
dependsOn: ['credential'],
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'listBases', not: true },
|
||||
required: { field: 'operation', value: 'listBases', not: true },
|
||||
},
|
||||
{
|
||||
id: 'tableId',
|
||||
@@ -67,7 +72,8 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter table ID (e.g., tblXXXXXXXXXXXXXX)',
|
||||
dependsOn: ['credential', 'baseId'],
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['listBases', 'listTables'], not: true },
|
||||
required: { field: 'operation', value: ['listBases', 'listTables'], not: true },
|
||||
},
|
||||
{
|
||||
id: 'recordId',
|
||||
@@ -83,6 +89,7 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum records to return',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'filterFormula',
|
||||
@@ -90,6 +97,7 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
type: 'long-input',
|
||||
placeholder: 'Airtable formula to filter records (optional)',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate an Airtable filter formula based on the user's description.
|
||||
@@ -206,6 +214,8 @@ Return ONLY the valid JSON object - no explanations, no markdown.`,
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'airtable_list_bases',
|
||||
'airtable_list_tables',
|
||||
'airtable_list_records',
|
||||
'airtable_get_record',
|
||||
'airtable_create_records',
|
||||
@@ -215,6 +225,10 @@ Return ONLY the valid JSON object - no explanations, no markdown.`,
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'listBases':
|
||||
return 'airtable_list_bases'
|
||||
case 'listTables':
|
||||
return 'airtable_list_tables'
|
||||
case 'list':
|
||||
return 'airtable_list_records'
|
||||
case 'get':
|
||||
@@ -278,6 +292,11 @@ Return ONLY the valid JSON object - no explanations, no markdown.`,
|
||||
},
|
||||
// Output structure depends on the operation, covered by AirtableResponse union type
|
||||
outputs: {
|
||||
// List Bases output
|
||||
bases: { type: 'json', description: 'List of accessible Airtable bases' },
|
||||
// List Tables output
|
||||
tables: { type: 'json', description: 'List of tables in the base with schema' },
|
||||
// Record outputs
|
||||
records: { type: 'json', description: 'Retrieved record data' }, // Optional: for list, create, updateMultiple
|
||||
record: { type: 'json', description: 'Single record data' }, // Optional: for get, update single
|
||||
metadata: { type: 'json', description: 'Operation metadata' }, // Required: present in all responses
|
||||
|
||||
@@ -2155,7 +2155,13 @@ export const auth = betterAuth({
|
||||
authorizationUrl: 'https://airtable.com/oauth2/v1/authorize',
|
||||
tokenUrl: 'https://airtable.com/oauth2/v1/token',
|
||||
userInfoUrl: 'https://api.airtable.com/v0/meta/whoami',
|
||||
scopes: ['data.records:read', 'data.records:write', 'user.email:read', 'webhook:manage'],
|
||||
scopes: [
|
||||
'data.records:read',
|
||||
'data.records:write',
|
||||
'schema.bases:read',
|
||||
'user.email:read',
|
||||
'webhook:manage',
|
||||
],
|
||||
responseType: 'code',
|
||||
pkce: true,
|
||||
accessType: 'offline',
|
||||
|
||||
@@ -486,7 +486,13 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
providerId: 'airtable',
|
||||
icon: AirtableIcon,
|
||||
baseProviderIcon: AirtableIcon,
|
||||
scopes: ['data.records:read', 'data.records:write', 'user.email:read', 'webhook:manage'],
|
||||
scopes: [
|
||||
'data.records:read',
|
||||
'data.records:write',
|
||||
'schema.bases:read',
|
||||
'user.email:read',
|
||||
'webhook:manage',
|
||||
],
|
||||
},
|
||||
},
|
||||
defaultService: 'airtable',
|
||||
|
||||
@@ -41,7 +41,8 @@ export const airtableCreateRecordsTool: ToolConfig<AirtableCreateParams, Airtabl
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.airtable.com/v0/${params.baseId}/${params.tableId}`,
|
||||
url: (params) =>
|
||||
`https://api.airtable.com/v0/${params.baseId?.trim()}/${params.tableId?.trim()}`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
@@ -55,9 +56,9 @@ export const airtableCreateRecordsTool: ToolConfig<AirtableCreateParams, Airtabl
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
records: data.records || [],
|
||||
records: data.records ?? [],
|
||||
metadata: {
|
||||
recordCount: (data.records || []).length,
|
||||
recordCount: (data.records ?? []).length,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -65,20 +66,23 @@ export const airtableCreateRecordsTool: ToolConfig<AirtableCreateParams, Airtabl
|
||||
|
||||
outputs: {
|
||||
records: {
|
||||
type: 'json',
|
||||
type: 'array',
|
||||
description: 'Array of created Airtable records',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
createdTime: { type: 'string' },
|
||||
fields: { type: 'object' },
|
||||
id: { type: 'string', description: 'Record ID' },
|
||||
createdTime: { type: 'string', description: 'Record creation timestamp' },
|
||||
fields: { type: 'json', description: 'Record field values' },
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Operation metadata',
|
||||
properties: {
|
||||
recordCount: { type: 'number', description: 'Number of records created' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export const airtableGetRecordTool: ToolConfig<AirtableGetParams, AirtableGetRes
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.airtable.com/v0/${params.baseId}/${params.tableId}/${params.recordId}`,
|
||||
`https://api.airtable.com/v0/${params.baseId?.trim()}/${params.tableId?.trim()}/${params.recordId?.trim()}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
@@ -65,11 +65,19 @@ export const airtableGetRecordTool: ToolConfig<AirtableGetParams, AirtableGetRes
|
||||
outputs: {
|
||||
record: {
|
||||
type: 'json',
|
||||
description: 'Retrieved Airtable record with id, createdTime, and fields',
|
||||
description: 'Retrieved Airtable record',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Record ID' },
|
||||
createdTime: { type: 'string', description: 'Record creation timestamp' },
|
||||
fields: { type: 'json', description: 'Record field values' },
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Operation metadata including record count',
|
||||
description: 'Operation metadata',
|
||||
properties: {
|
||||
recordCount: { type: 'number', description: 'Number of records returned (always 1)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { airtableCreateRecordsTool } from '@/tools/airtable/create_records'
|
||||
import { airtableGetRecordTool } from '@/tools/airtable/get_record'
|
||||
import { airtableListBasesTool } from '@/tools/airtable/list_bases'
|
||||
import { airtableListRecordsTool } from '@/tools/airtable/list_records'
|
||||
import { airtableListTablesTool } from '@/tools/airtable/list_tables'
|
||||
import { airtableUpdateMultipleRecordsTool } from '@/tools/airtable/update_multiple_records'
|
||||
import { airtableUpdateRecordTool } from '@/tools/airtable/update_record'
|
||||
|
||||
export {
|
||||
airtableCreateRecordsTool,
|
||||
airtableGetRecordTool,
|
||||
airtableListBasesTool,
|
||||
airtableListRecordsTool,
|
||||
airtableListTablesTool,
|
||||
airtableUpdateMultipleRecordsTool,
|
||||
airtableUpdateRecordTool,
|
||||
}
|
||||
|
||||
export * from './types'
|
||||
|
||||
91
apps/sim/tools/airtable/list_bases.ts
Normal file
91
apps/sim/tools/airtable/list_bases.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type { AirtableListBasesParams, AirtableListBasesResponse } from '@/tools/airtable/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const airtableListBasesTool: ToolConfig<AirtableListBasesParams, AirtableListBasesResponse> =
|
||||
{
|
||||
id: 'airtable_list_bases',
|
||||
name: 'Airtable List Bases',
|
||||
description: 'List all Airtable bases the user has access to',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'airtable',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
offset: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination offset for retrieving additional bases',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = 'https://api.airtable.com/v0/meta/bases'
|
||||
if (params.offset) {
|
||||
return `${url}?offset=${encodeURIComponent(params.offset)}`
|
||||
}
|
||||
return url
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
bases: (data.bases ?? []).map(
|
||||
(base: { id: string; name: string; permissionLevel: string }) => ({
|
||||
id: base.id,
|
||||
name: base.name,
|
||||
permissionLevel: base.permissionLevel,
|
||||
})
|
||||
),
|
||||
metadata: {
|
||||
offset: data.offset ?? null,
|
||||
totalBases: (data.bases ?? []).length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
bases: {
|
||||
type: 'array',
|
||||
description: 'List of Airtable bases',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Base ID (starts with "app")' },
|
||||
name: { type: 'string', description: 'Base name' },
|
||||
permissionLevel: {
|
||||
type: 'string',
|
||||
description: 'Permission level (none, read, comment, edit, create)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Pagination and count metadata',
|
||||
properties: {
|
||||
offset: { type: 'string', description: 'Offset for next page of results' },
|
||||
totalBases: { type: 'number', description: 'Number of bases returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -47,14 +47,10 @@ export const airtableListRecordsTool: ToolConfig<AirtableListParams, AirtableLis
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = `https://api.airtable.com/v0/${params.baseId}/${params.tableId}`
|
||||
const url = `https://api.airtable.com/v0/${params.baseId?.trim()}/${params.tableId?.trim()}`
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.maxRecords) queryParams.append('maxRecords', Number(params.maxRecords).toString())
|
||||
if (params.filterFormula) {
|
||||
// Airtable formulas often contain characters needing encoding,
|
||||
// but standard encodeURIComponent might over-encode.
|
||||
// Simple replacement for single quotes is often sufficient.
|
||||
// More complex formulas might need careful encoding.
|
||||
const encodedFormula = params.filterFormula.replace(/'/g, "'")
|
||||
queryParams.append('filterByFormula', encodedFormula)
|
||||
}
|
||||
@@ -74,10 +70,10 @@ export const airtableListRecordsTool: ToolConfig<AirtableListParams, AirtableLis
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
records: data.records || [],
|
||||
records: data.records ?? [],
|
||||
metadata: {
|
||||
offset: data.offset,
|
||||
totalRecords: (data.records || []).length,
|
||||
offset: data.offset ?? null,
|
||||
totalRecords: (data.records ?? []).length,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -85,20 +81,24 @@ export const airtableListRecordsTool: ToolConfig<AirtableListParams, AirtableLis
|
||||
|
||||
outputs: {
|
||||
records: {
|
||||
type: 'json',
|
||||
type: 'array',
|
||||
description: 'Array of retrieved Airtable records',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
createdTime: { type: 'string' },
|
||||
fields: { type: 'object' },
|
||||
id: { type: 'string', description: 'Record ID' },
|
||||
createdTime: { type: 'string', description: 'Record creation timestamp' },
|
||||
fields: { type: 'json', description: 'Record field values' },
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Operation metadata including pagination offset and total records count',
|
||||
properties: {
|
||||
offset: { type: 'string', description: 'Pagination offset for next page' },
|
||||
totalRecords: { type: 'number', description: 'Number of records returned' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
124
apps/sim/tools/airtable/list_tables.ts
Normal file
124
apps/sim/tools/airtable/list_tables.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type {
|
||||
AirtableField,
|
||||
AirtableListTablesParams,
|
||||
AirtableListTablesResponse,
|
||||
AirtableTable,
|
||||
} from '@/tools/airtable/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const airtableListTablesTool: ToolConfig<
|
||||
AirtableListTablesParams,
|
||||
AirtableListTablesResponse
|
||||
> = {
|
||||
id: 'airtable_list_tables',
|
||||
name: 'Airtable List Tables',
|
||||
description: 'List all tables and their schema in an Airtable base',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'airtable',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
baseId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Airtable base ID (starts with "app", e.g., "appXXXXXXXXXXXXXX")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.airtable.com/v0/meta/bases/${params.baseId?.trim()}/tables`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const data = await response.json()
|
||||
const tables: AirtableTable[] = (data.tables ?? []).map(
|
||||
(table: {
|
||||
id: string
|
||||
name: string
|
||||
description?: string
|
||||
primaryFieldId: string
|
||||
fields: AirtableField[]
|
||||
}) => ({
|
||||
id: table.id,
|
||||
name: table.name,
|
||||
description: table.description ?? null,
|
||||
primaryFieldId: table.primaryFieldId,
|
||||
fields: (table.fields ?? []).map((field: AirtableField) => ({
|
||||
id: field.id,
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
description: field.description ?? null,
|
||||
options: field.options ?? null,
|
||||
})),
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tables,
|
||||
metadata: {
|
||||
baseId: params?.baseId ?? '',
|
||||
totalTables: tables.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
tables: {
|
||||
type: 'array',
|
||||
description: 'List of tables in the base with their schema',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Table ID (starts with "tbl")' },
|
||||
name: { type: 'string', description: 'Table name' },
|
||||
description: { type: 'string', description: 'Table description' },
|
||||
primaryFieldId: { type: 'string', description: 'ID of the primary field' },
|
||||
fields: {
|
||||
type: 'array',
|
||||
description: 'List of fields in the table',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Field ID (starts with "fld")' },
|
||||
name: { type: 'string', description: 'Field name' },
|
||||
type: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Field type (singleLineText, multilineText, number, checkbox, singleSelect, multipleSelects, date, dateTime, attachment, linkedRecord, etc.)',
|
||||
},
|
||||
description: { type: 'string', description: 'Field description' },
|
||||
options: { type: 'json', description: 'Field-specific options (choices, etc.)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Base info and count metadata',
|
||||
properties: {
|
||||
baseId: { type: 'string', description: 'The base ID queried' },
|
||||
totalTables: { type: 'number', description: 'Number of tables in the base' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -7,12 +7,85 @@ export interface AirtableRecord {
|
||||
fields: Record<string, any>
|
||||
}
|
||||
|
||||
export interface AirtableBase {
|
||||
id: string
|
||||
name: string
|
||||
permissionLevel: 'none' | 'read' | 'comment' | 'edit' | 'create'
|
||||
}
|
||||
|
||||
export interface AirtableFieldOption {
|
||||
id: string
|
||||
name: string
|
||||
color?: string
|
||||
}
|
||||
|
||||
export interface AirtableField {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
description?: string
|
||||
options?: {
|
||||
choices?: AirtableFieldOption[]
|
||||
linkedTableId?: string
|
||||
isReversed?: boolean
|
||||
prefersSingleRecordLink?: boolean
|
||||
inverseLinkFieldId?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export interface AirtableTable {
|
||||
id: string
|
||||
name: string
|
||||
description?: string
|
||||
primaryFieldId: string
|
||||
fields: AirtableField[]
|
||||
}
|
||||
|
||||
export interface AirtableView {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
|
||||
interface AirtableBaseParams {
|
||||
accessToken: string
|
||||
baseId: string
|
||||
tableId: string
|
||||
}
|
||||
|
||||
// List Bases Types
|
||||
export interface AirtableListBasesParams {
|
||||
accessToken: string
|
||||
offset?: string
|
||||
}
|
||||
|
||||
export interface AirtableListBasesResponse extends ToolResponse {
|
||||
output: {
|
||||
bases: AirtableBase[]
|
||||
metadata: {
|
||||
offset?: string
|
||||
totalBases: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List Tables Types (Get Base Schema)
|
||||
export interface AirtableListTablesParams {
|
||||
accessToken: string
|
||||
baseId: string
|
||||
}
|
||||
|
||||
export interface AirtableListTablesResponse extends ToolResponse {
|
||||
output: {
|
||||
tables: AirtableTable[]
|
||||
metadata: {
|
||||
baseId: string
|
||||
totalTables: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List Records Types
|
||||
export interface AirtableListParams extends AirtableBaseParams {
|
||||
maxRecords?: number
|
||||
@@ -89,6 +162,8 @@ export interface AirtableUpdateMultipleResponse extends ToolResponse {
|
||||
}
|
||||
|
||||
export type AirtableResponse =
|
||||
| AirtableListBasesResponse
|
||||
| AirtableListTablesResponse
|
||||
| AirtableListResponse
|
||||
| AirtableGetResponse
|
||||
| AirtableCreateResponse
|
||||
|
||||
@@ -46,8 +46,8 @@ export const airtableUpdateMultipleRecordsTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
// The API endpoint uses PATCH for multiple record updates as well
|
||||
url: (params) => `https://api.airtable.com/v0/${params.baseId}/${params.tableId}`,
|
||||
url: (params) =>
|
||||
`https://api.airtable.com/v0/${params.baseId?.trim()}/${params.tableId?.trim()}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
@@ -58,13 +58,14 @@ export const airtableUpdateMultipleRecordsTool: ToolConfig<
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const records = data.records ?? []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
records: data.records || [], // API returns an array of updated records
|
||||
records,
|
||||
metadata: {
|
||||
recordCount: (data.records || []).length,
|
||||
updatedRecordIds: (data.records || []).map((r: any) => r.id),
|
||||
recordCount: records.length,
|
||||
updatedRecordIds: records.map((r: { id: string }) => r.id),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -72,20 +73,24 @@ export const airtableUpdateMultipleRecordsTool: ToolConfig<
|
||||
|
||||
outputs: {
|
||||
records: {
|
||||
type: 'json',
|
||||
type: 'array',
|
||||
description: 'Array of updated Airtable records',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
createdTime: { type: 'string' },
|
||||
fields: { type: 'object' },
|
||||
id: { type: 'string', description: 'Record ID' },
|
||||
createdTime: { type: 'string', description: 'Record creation timestamp' },
|
||||
fields: { type: 'json', description: 'Record field values' },
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Operation metadata including record count and updated record IDs',
|
||||
description: 'Operation metadata',
|
||||
properties: {
|
||||
recordCount: { type: 'number', description: 'Number of records updated' },
|
||||
updatedRecordIds: { type: 'array', description: 'List of updated record IDs' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,9 +46,8 @@ export const airtableUpdateRecordTool: ToolConfig<AirtableUpdateParams, Airtable
|
||||
},
|
||||
|
||||
request: {
|
||||
// The API endpoint uses PATCH for single record updates
|
||||
url: (params) =>
|
||||
`https://api.airtable.com/v0/${params.baseId}/${params.tableId}/${params.recordId}`,
|
||||
`https://api.airtable.com/v0/${params.baseId?.trim()}/${params.tableId?.trim()}/${params.recordId?.trim()}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
@@ -62,10 +61,10 @@ export const airtableUpdateRecordTool: ToolConfig<AirtableUpdateParams, Airtable
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
record: data, // API returns the single updated record object
|
||||
record: data,
|
||||
metadata: {
|
||||
recordCount: 1,
|
||||
updatedFields: Object.keys(data.fields || {}),
|
||||
updatedFields: Object.keys(data.fields ?? {}),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -74,11 +73,20 @@ export const airtableUpdateRecordTool: ToolConfig<AirtableUpdateParams, Airtable
|
||||
outputs: {
|
||||
record: {
|
||||
type: 'json',
|
||||
description: 'Updated Airtable record with id, createdTime, and fields',
|
||||
description: 'Updated Airtable record',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Record ID' },
|
||||
createdTime: { type: 'string', description: 'Record creation timestamp' },
|
||||
fields: { type: 'json', description: 'Record field values' },
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Operation metadata including record count and updated field names',
|
||||
description: 'Operation metadata',
|
||||
properties: {
|
||||
recordCount: { type: 'number', description: 'Number of records updated (always 1)' },
|
||||
updatedFields: { type: 'array', description: 'List of field names that were updated' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@ import {
|
||||
import {
|
||||
airtableCreateRecordsTool,
|
||||
airtableGetRecordTool,
|
||||
airtableListBasesTool,
|
||||
airtableListRecordsTool,
|
||||
airtableListTablesTool,
|
||||
airtableUpdateMultipleRecordsTool,
|
||||
airtableUpdateRecordTool,
|
||||
} from '@/tools/airtable'
|
||||
import { airweaveSearchTool } from '@/tools/airweave'
|
||||
@@ -3431,7 +3434,10 @@ export const tools: Record<string, ToolConfig> = {
|
||||
algolia_delete_by_filter: algoliaDeleteByFilterTool,
|
||||
airtable_create_records: airtableCreateRecordsTool,
|
||||
airtable_get_record: airtableGetRecordTool,
|
||||
airtable_list_bases: airtableListBasesTool,
|
||||
airtable_list_records: airtableListRecordsTool,
|
||||
airtable_list_tables: airtableListTablesTool,
|
||||
airtable_update_multiple_records: airtableUpdateMultipleRecordsTool,
|
||||
airtable_update_record: airtableUpdateRecordTool,
|
||||
attio_assert_record: attioAssertRecordTool,
|
||||
attio_create_comment: attioCreateCommentTool,
|
||||
|
||||
Reference in New Issue
Block a user