improvement(airtable): added more tools (#3396)

This commit is contained in:
Waleed
2026-03-02 10:58:21 -08:00
committed by GitHub
parent cd88706ea4
commit afaa361801
15 changed files with 485 additions and 52 deletions

View File

@@ -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 |

View File

@@ -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',

View File

@@ -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

View File

@@ -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',

View File

@@ -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',

View File

@@ -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' },
},
},
},
}

View File

@@ -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)' },
},
},
},
}

View File

@@ -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'

View 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' },
},
},
},
}

View File

@@ -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' },
},
},
},
}

View 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' },
},
},
},
}

View File

@@ -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

View File

@@ -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' },
},
},
},
}

View File

@@ -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' },
},
},
},
}

View File

@@ -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,