@rashidazarang/airtable-mcp 1.5.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/.github/ISSUE_TEMPLATE/bug-report.yml +173 -0
  2. package/.github/ISSUE_TEMPLATE/feature-request.yml +209 -0
  3. package/.github/ISSUE_TEMPLATE/security-report.yml +216 -0
  4. package/.github/pull_request_template.md +245 -0
  5. package/.github/workflows/ci-cd.yml +408 -0
  6. package/.github/workflows/security-audit.yml +316 -0
  7. package/API_DOCUMENTATION.md +897 -0
  8. package/CODE_OF_CONDUCT.md +181 -0
  9. package/Dockerfile.production +127 -0
  10. package/README.md +55 -10
  11. package/RELEASE_NOTES_v1.6.0.md +248 -0
  12. package/airtable-clipper/CHANGELOG.md +198 -0
  13. package/airtable-clipper/CHROME_STORE_SUBMISSION.md +343 -0
  14. package/airtable-clipper/LAUNCH_STRATEGY.md +495 -0
  15. package/airtable-clipper/LICENSE +21 -0
  16. package/airtable-clipper/OAUTH_SETUP.md +51 -0
  17. package/airtable-clipper/PRIVACY_POLICY.md +187 -0
  18. package/airtable-clipper/README.md +575 -0
  19. package/airtable-clipper/SUBMIT_TO_CHROME_STORE.md +273 -0
  20. package/airtable-clipper/build.sh +85 -0
  21. package/airtable-clipper/docs/QUICK_START.md +99 -0
  22. package/airtable-clipper/docs/SETUP.md +291 -0
  23. package/airtable-clipper/extension/background.js +337 -0
  24. package/airtable-clipper/extension/base-setup.html +324 -0
  25. package/airtable-clipper/extension/base-setup.js +471 -0
  26. package/airtable-clipper/extension/content.js +771 -0
  27. package/airtable-clipper/extension/icons/README.md +69 -0
  28. package/airtable-clipper/extension/icons/icon-16.png +3 -0
  29. package/airtable-clipper/extension/manifest.json +73 -0
  30. package/airtable-clipper/extension/popup.html +144 -0
  31. package/airtable-clipper/extension/popup.js +475 -0
  32. package/airtable-clipper/extension/styles/content.css +229 -0
  33. package/airtable-clipper/extension/styles/popup.css +477 -0
  34. package/airtable-clipper/privacy-policy.md +63 -0
  35. package/airtable-clipper/releases/v1.0.0/background.js +337 -0
  36. package/airtable-clipper/releases/v1.0.0/base-setup.html +324 -0
  37. package/airtable-clipper/releases/v1.0.0/base-setup.js +471 -0
  38. package/airtable-clipper/releases/v1.0.0/content.js +771 -0
  39. package/airtable-clipper/releases/v1.0.0/icons/README.md +69 -0
  40. package/airtable-clipper/releases/v1.0.0/icons/icon-128.png +2 -0
  41. package/airtable-clipper/releases/v1.0.0/icons/icon-16.png +3 -0
  42. package/airtable-clipper/releases/v1.0.0/icons/icon-32.png +2 -0
  43. package/airtable-clipper/releases/v1.0.0/icons/icon-48.png +2 -0
  44. package/airtable-clipper/releases/v1.0.0/manifest.json +73 -0
  45. package/airtable-clipper/releases/v1.0.0/popup.html +144 -0
  46. package/airtable-clipper/releases/v1.0.0/popup.js +475 -0
  47. package/airtable-clipper/releases/v1.0.0/sidepanel.html +25 -0
  48. package/airtable-clipper/releases/v1.0.0/styles/content.css +229 -0
  49. package/airtable-clipper/releases/v1.0.0/styles/popup.css +477 -0
  50. package/airtable-clipper/releases/v1.0.1/background.js +337 -0
  51. package/airtable-clipper/releases/v1.0.1/base-setup.html +324 -0
  52. package/airtable-clipper/releases/v1.0.1/base-setup.js +471 -0
  53. package/airtable-clipper/releases/v1.0.1/content.js +771 -0
  54. package/airtable-clipper/releases/v1.0.1/icons/README.md +69 -0
  55. package/airtable-clipper/releases/v1.0.1/icons/icon-128.png +2 -0
  56. package/airtable-clipper/releases/v1.0.1/icons/icon-16.png +3 -0
  57. package/airtable-clipper/releases/v1.0.1/icons/icon-32.png +2 -0
  58. package/airtable-clipper/releases/v1.0.1/icons/icon-48.png +2 -0
  59. package/airtable-clipper/releases/v1.0.1/manifest.json +70 -0
  60. package/airtable-clipper/releases/v1.0.1/popup.html +157 -0
  61. package/airtable-clipper/releases/v1.0.1/popup.js +562 -0
  62. package/airtable-clipper/releases/v1.0.1/sidepanel.html +25 -0
  63. package/airtable-clipper/releases/v1.0.1/styles/content.css +229 -0
  64. package/airtable-clipper/releases/v1.0.1/styles/popup.css +647 -0
  65. package/airtable-clipper/releases/v1.0.2/background.js +337 -0
  66. package/airtable-clipper/releases/v1.0.2/base-setup.html +324 -0
  67. package/airtable-clipper/releases/v1.0.2/base-setup.js +471 -0
  68. package/airtable-clipper/releases/v1.0.2/content.js +771 -0
  69. package/airtable-clipper/releases/v1.0.2/icons/README.md +69 -0
  70. package/airtable-clipper/releases/v1.0.2/icons/icon-128.png +2 -0
  71. package/airtable-clipper/releases/v1.0.2/icons/icon-16.png +3 -0
  72. package/airtable-clipper/releases/v1.0.2/icons/icon-32.png +2 -0
  73. package/airtable-clipper/releases/v1.0.2/icons/icon-48.png +2 -0
  74. package/airtable-clipper/releases/v1.0.2/manifest.json +62 -0
  75. package/airtable-clipper/releases/v1.0.2/popup.html +157 -0
  76. package/airtable-clipper/releases/v1.0.2/popup.js +567 -0
  77. package/airtable-clipper/releases/v1.0.2/sidepanel.html +25 -0
  78. package/airtable-clipper/releases/v1.0.2/styles/content.css +229 -0
  79. package/airtable-clipper/releases/v1.0.2/styles/popup.css +647 -0
  80. package/airtable-clipper/terms-of-service.md +124 -0
  81. package/airtable-clipper/test-credentials.md +61 -0
  82. package/airtable-clipper/test-extension/background.js +337 -0
  83. package/airtable-clipper/test-extension/base-setup.html +324 -0
  84. package/airtable-clipper/test-extension/base-setup.js +471 -0
  85. package/airtable-clipper/test-extension/content.js +873 -0
  86. package/airtable-clipper/test-extension/icons/README.md +69 -0
  87. package/airtable-clipper/test-extension/icons/icon-128.png +2 -0
  88. package/airtable-clipper/test-extension/icons/icon-16.png +3 -0
  89. package/airtable-clipper/test-extension/icons/icon-32.png +2 -0
  90. package/airtable-clipper/test-extension/icons/icon-48.png +2 -0
  91. package/airtable-clipper/test-extension/manifest.json +72 -0
  92. package/airtable-clipper/test-extension/popup.html +274 -0
  93. package/airtable-clipper/test-extension/popup.js +729 -0
  94. package/airtable-clipper/test-extension/sidepanel.html +25 -0
  95. package/airtable-clipper/test-extension/styles/content.css +229 -0
  96. package/airtable-clipper/test-extension/styles/popup.css +794 -0
  97. package/airtable_mcp_v2.js +1505 -0
  98. package/airtable_mcp_v2_oauth.js +1048 -0
  99. package/airtable_mcp_v3_advanced.js +1161 -0
  100. package/airtable_simple.js +447 -1
  101. package/airtable_simple_production.js +532 -0
  102. package/docker-compose.production.yml +366 -0
  103. package/helm/airtable-mcp/Chart.yaml +122 -0
  104. package/helm/airtable-mcp/values.yaml +538 -0
  105. package/k8s/deployment.yaml +402 -0
  106. package/k8s/namespace.yaml +108 -0
  107. package/k8s/service.yaml +194 -0
  108. package/monitoring/alerts.yml +289 -0
  109. package/monitoring/prometheus.yml +224 -0
  110. package/package.json +6 -6
  111. package/test_v1.6.0_comprehensive.sh +187 -0
  112. package/.claude/settings.local.json +0 -12
  113. package/airtable-mcp-1.1.0.tgz +0 -0
  114. package/airtable_enhanced.js +0 -499
  115. package/airtable_simple_v1.2.4_backup.js +0 -277
  116. package/airtable_v1.4.0.js +0 -654
  117. package/rashidazarang-airtable-mcp-1.1.0.tgz +0 -0
  118. package/rashidazarang-airtable-mcp-1.2.0.tgz +0 -0
  119. package/rashidazarang-airtable-mcp-1.2.1.tgz +0 -0
@@ -55,7 +55,7 @@ function log(level, message, ...args) {
55
55
  }
56
56
  }
57
57
 
58
- log(LOG_LEVELS.INFO, `Starting Enhanced Airtable MCP server v1.5.0`);
58
+ log(LOG_LEVELS.INFO, `Starting Enhanced Airtable MCP server v1.6.0`);
59
59
  log(LOG_LEVELS.INFO, `Authentication configured`);
60
60
  log(LOG_LEVELS.INFO, `Base connection established`);
61
61
 
@@ -463,6 +463,196 @@ const server = http.createServer(async (req, res) => {
463
463
  },
464
464
  required: ['table']
465
465
  }
466
+ },
467
+ {
468
+ name: 'upload_attachment',
469
+ description: 'Upload/attach a file from URL to a record',
470
+ inputSchema: {
471
+ type: 'object',
472
+ properties: {
473
+ table: { type: 'string', description: 'Table name or ID' },
474
+ recordId: { type: 'string', description: 'Record ID to attach file to' },
475
+ fieldName: { type: 'string', description: 'Name of the attachment field' },
476
+ url: { type: 'string', description: 'Public URL of the file to attach' },
477
+ filename: { type: 'string', description: 'Optional filename for the attachment' }
478
+ },
479
+ required: ['table', 'recordId', 'fieldName', 'url']
480
+ }
481
+ },
482
+ {
483
+ name: 'batch_create_records',
484
+ description: 'Create multiple records at once (up to 10)',
485
+ inputSchema: {
486
+ type: 'object',
487
+ properties: {
488
+ table: { type: 'string', description: 'Table name or ID' },
489
+ records: {
490
+ type: 'array',
491
+ description: 'Array of record objects to create (max 10)',
492
+ items: {
493
+ type: 'object',
494
+ properties: {
495
+ fields: { type: 'object', description: 'Record fields' }
496
+ },
497
+ required: ['fields']
498
+ },
499
+ maxItems: 10
500
+ }
501
+ },
502
+ required: ['table', 'records']
503
+ }
504
+ },
505
+ {
506
+ name: 'batch_update_records',
507
+ description: 'Update multiple records at once (up to 10)',
508
+ inputSchema: {
509
+ type: 'object',
510
+ properties: {
511
+ table: { type: 'string', description: 'Table name or ID' },
512
+ records: {
513
+ type: 'array',
514
+ description: 'Array of record objects to update (max 10)',
515
+ items: {
516
+ type: 'object',
517
+ properties: {
518
+ id: { type: 'string', description: 'Record ID' },
519
+ fields: { type: 'object', description: 'Fields to update' }
520
+ },
521
+ required: ['id', 'fields']
522
+ },
523
+ maxItems: 10
524
+ }
525
+ },
526
+ required: ['table', 'records']
527
+ }
528
+ },
529
+ {
530
+ name: 'batch_delete_records',
531
+ description: 'Delete multiple records at once (up to 10)',
532
+ inputSchema: {
533
+ type: 'object',
534
+ properties: {
535
+ table: { type: 'string', description: 'Table name or ID' },
536
+ recordIds: {
537
+ type: 'array',
538
+ description: 'Array of record IDs to delete (max 10)',
539
+ items: { type: 'string' },
540
+ maxItems: 10
541
+ }
542
+ },
543
+ required: ['table', 'recordIds']
544
+ }
545
+ },
546
+ {
547
+ name: 'batch_upsert_records',
548
+ description: 'Update existing records or create new ones based on key fields',
549
+ inputSchema: {
550
+ type: 'object',
551
+ properties: {
552
+ table: { type: 'string', description: 'Table name or ID' },
553
+ records: {
554
+ type: 'array',
555
+ description: 'Array of record objects (max 10)',
556
+ items: {
557
+ type: 'object',
558
+ properties: {
559
+ fields: { type: 'object', description: 'Record fields' }
560
+ },
561
+ required: ['fields']
562
+ },
563
+ maxItems: 10
564
+ },
565
+ keyFields: {
566
+ type: 'array',
567
+ description: 'Fields to use for matching existing records',
568
+ items: { type: 'string' }
569
+ }
570
+ },
571
+ required: ['table', 'records', 'keyFields']
572
+ }
573
+ },
574
+ {
575
+ name: 'create_view',
576
+ description: 'Create a new view for a table',
577
+ inputSchema: {
578
+ type: 'object',
579
+ properties: {
580
+ table: { type: 'string', description: 'Table name or ID' },
581
+ name: { type: 'string', description: 'Name for the new view' },
582
+ type: { type: 'string', description: 'View type (grid, form, calendar, etc.)', enum: ['grid', 'form', 'calendar', 'gallery', 'kanban', 'timeline', 'gantt'] },
583
+ visibleFieldIds: { type: 'array', description: 'Array of field IDs to show in view', items: { type: 'string' } },
584
+ fieldOrder: { type: 'array', description: 'Order of fields in view', items: { type: 'string' } }
585
+ },
586
+ required: ['table', 'name', 'type']
587
+ }
588
+ },
589
+ {
590
+ name: 'get_view_metadata',
591
+ description: 'Get detailed metadata for a specific view',
592
+ inputSchema: {
593
+ type: 'object',
594
+ properties: {
595
+ table: { type: 'string', description: 'Table name or ID' },
596
+ viewId: { type: 'string', description: 'View ID' }
597
+ },
598
+ required: ['table', 'viewId']
599
+ }
600
+ },
601
+ {
602
+ name: 'create_base',
603
+ description: 'Create a new Airtable base',
604
+ inputSchema: {
605
+ type: 'object',
606
+ properties: {
607
+ name: { type: 'string', description: 'Name for the new base' },
608
+ workspaceId: { type: 'string', description: 'Workspace ID to create the base in' },
609
+ tables: {
610
+ type: 'array',
611
+ description: 'Initial tables to create in the base',
612
+ items: {
613
+ type: 'object',
614
+ properties: {
615
+ name: { type: 'string', description: 'Table name' },
616
+ description: { type: 'string', description: 'Table description' },
617
+ fields: {
618
+ type: 'array',
619
+ description: 'Table fields',
620
+ items: {
621
+ type: 'object',
622
+ properties: {
623
+ name: { type: 'string', description: 'Field name' },
624
+ type: { type: 'string', description: 'Field type' }
625
+ },
626
+ required: ['name', 'type']
627
+ }
628
+ }
629
+ },
630
+ required: ['name', 'fields']
631
+ }
632
+ }
633
+ },
634
+ required: ['name', 'tables']
635
+ }
636
+ },
637
+ {
638
+ name: 'list_collaborators',
639
+ description: 'List collaborators and their permissions for the current base',
640
+ inputSchema: {
641
+ type: 'object',
642
+ properties: {
643
+ baseId: { type: 'string', description: 'Base ID (optional, defaults to current base)' }
644
+ }
645
+ }
646
+ },
647
+ {
648
+ name: 'list_shares',
649
+ description: 'List shared views and their configurations',
650
+ inputSchema: {
651
+ type: 'object',
652
+ properties: {
653
+ baseId: { type: 'string', description: 'Base ID (optional, defaults to current base)' }
654
+ }
655
+ }
466
656
  }
467
657
  ]
468
658
  }
@@ -1029,6 +1219,262 @@ const server = http.createServer(async (req, res) => {
1029
1219
  }
1030
1220
  }
1031
1221
 
1222
+ // NEW v1.6.0 TOOLS - Attachment and Batch Operations
1223
+ else if (toolName === 'upload_attachment') {
1224
+ const { table, recordId, fieldName, url, filename } = toolParams;
1225
+
1226
+ const attachment = { url };
1227
+ if (filename) attachment.filename = filename;
1228
+
1229
+ const updateBody = {
1230
+ fields: {
1231
+ [fieldName]: [attachment]
1232
+ }
1233
+ };
1234
+
1235
+ result = await callAirtableAPI(`${table}/${recordId}`, 'PATCH', updateBody);
1236
+
1237
+ responseText = `Successfully attached file to record ${recordId}:\n`;
1238
+ responseText += `Field: ${fieldName}\n`;
1239
+ responseText += `URL: ${url}\n`;
1240
+ if (filename) responseText += `Filename: ${filename}\n`;
1241
+ }
1242
+
1243
+ else if (toolName === 'batch_create_records') {
1244
+ const { table, records } = toolParams;
1245
+
1246
+ if (records.length > 10) {
1247
+ responseText = 'Error: Cannot create more than 10 records at once. Please split into smaller batches.';
1248
+ } else {
1249
+ const body = { records };
1250
+ result = await callAirtableAPI(table, 'POST', body);
1251
+
1252
+ responseText = `Successfully created ${result.records.length} records:\n`;
1253
+ result.records.forEach((record, index) => {
1254
+ responseText += `${index + 1}. ID: ${record.id}\n`;
1255
+ const fields = Object.keys(record.fields);
1256
+ if (fields.length > 0) {
1257
+ responseText += ` Fields: ${fields.join(', ')}\n`;
1258
+ }
1259
+ });
1260
+ }
1261
+ }
1262
+
1263
+ else if (toolName === 'batch_update_records') {
1264
+ const { table, records } = toolParams;
1265
+
1266
+ if (records.length > 10) {
1267
+ responseText = 'Error: Cannot update more than 10 records at once. Please split into smaller batches.';
1268
+ } else {
1269
+ const body = { records };
1270
+ result = await callAirtableAPI(table, 'PATCH', body);
1271
+
1272
+ responseText = `Successfully updated ${result.records.length} records:\n`;
1273
+ result.records.forEach((record, index) => {
1274
+ responseText += `${index + 1}. ID: ${record.id}\n`;
1275
+ const fields = Object.keys(record.fields);
1276
+ if (fields.length > 0) {
1277
+ responseText += ` Updated fields: ${fields.join(', ')}\n`;
1278
+ }
1279
+ });
1280
+ }
1281
+ }
1282
+
1283
+ else if (toolName === 'batch_delete_records') {
1284
+ const { table, recordIds } = toolParams;
1285
+
1286
+ if (recordIds.length > 10) {
1287
+ responseText = 'Error: Cannot delete more than 10 records at once. Please split into smaller batches.';
1288
+ } else {
1289
+ const queryParams = { records: recordIds };
1290
+ result = await callAirtableAPI(table, 'DELETE', null, queryParams);
1291
+
1292
+ responseText = `Successfully deleted ${result.records.length} records:\n`;
1293
+ result.records.forEach((record, index) => {
1294
+ responseText += `${index + 1}. Deleted ID: ${record.id}\n`;
1295
+ });
1296
+ }
1297
+ }
1298
+
1299
+ else if (toolName === 'batch_upsert_records') {
1300
+ const { table, records, keyFields } = toolParams;
1301
+
1302
+ if (records.length > 10) {
1303
+ responseText = 'Error: Cannot upsert more than 10 records at once. Please split into smaller batches.';
1304
+ } else {
1305
+ // For simplicity, we'll implement this as a batch create with merge fields
1306
+ // Note: Real upsert requires checking existing records first
1307
+ const body = {
1308
+ records,
1309
+ performUpsert: {
1310
+ fieldsToMergeOn: keyFields
1311
+ }
1312
+ };
1313
+
1314
+ result = await callAirtableAPI(table, 'PATCH', body);
1315
+
1316
+ responseText = `Successfully upserted ${result.records.length} records:\n`;
1317
+ result.records.forEach((record, index) => {
1318
+ responseText += `${index + 1}. ID: ${record.id}\n`;
1319
+ const fields = Object.keys(record.fields);
1320
+ if (fields.length > 0) {
1321
+ responseText += ` Fields: ${fields.join(', ')}\n`;
1322
+ }
1323
+ });
1324
+ }
1325
+ }
1326
+
1327
+ // NEW v1.6.0 TOOLS - Advanced View Management
1328
+ else if (toolName === 'create_view') {
1329
+ const { table, name, type, visibleFieldIds, fieldOrder } = toolParams;
1330
+
1331
+ // Get table ID first
1332
+ const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
1333
+ const tableInfo = schemaResult.tables.find(t =>
1334
+ t.name.toLowerCase() === table.toLowerCase() || t.id === table
1335
+ );
1336
+
1337
+ if (!tableInfo) {
1338
+ responseText = `Table "${table}" not found.`;
1339
+ } else {
1340
+ const body = {
1341
+ name,
1342
+ type
1343
+ };
1344
+
1345
+ if (visibleFieldIds) body.visibleFieldIds = visibleFieldIds;
1346
+ if (fieldOrder) body.fieldOrder = fieldOrder;
1347
+
1348
+ result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/views`, 'POST', body);
1349
+
1350
+ responseText = `Successfully created view "${name}" in table "${tableInfo.name}":\n`;
1351
+ responseText += `View ID: ${result.id}\n`;
1352
+ responseText += `Type: ${result.type}\n`;
1353
+ if (result.visibleFieldIds && result.visibleFieldIds.length > 0) {
1354
+ responseText += `Visible fields: ${result.visibleFieldIds.length}\n`;
1355
+ }
1356
+ }
1357
+ }
1358
+
1359
+ else if (toolName === 'get_view_metadata') {
1360
+ const { table, viewId } = toolParams;
1361
+
1362
+ // Get table ID first
1363
+ const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
1364
+ const tableInfo = schemaResult.tables.find(t =>
1365
+ t.name.toLowerCase() === table.toLowerCase() || t.id === table
1366
+ );
1367
+
1368
+ if (!tableInfo) {
1369
+ responseText = `Table "${table}" not found.`;
1370
+ } else {
1371
+ result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/views/${viewId}`, 'GET');
1372
+
1373
+ responseText = `View Metadata: ${result.name}\n`;
1374
+ responseText += `ID: ${result.id}\n`;
1375
+ responseText += `Type: ${result.type}\n`;
1376
+
1377
+ if (result.visibleFieldIds && result.visibleFieldIds.length > 0) {
1378
+ responseText += `\nVisible Fields (${result.visibleFieldIds.length}):\n`;
1379
+ result.visibleFieldIds.forEach((fieldId, index) => {
1380
+ responseText += `${index + 1}. ${fieldId}\n`;
1381
+ });
1382
+ }
1383
+
1384
+ if (result.filterByFormula) {
1385
+ responseText += `\nFilter Formula: ${result.filterByFormula}\n`;
1386
+ }
1387
+
1388
+ if (result.sorts && result.sorts.length > 0) {
1389
+ responseText += `\nSort Configuration:\n`;
1390
+ result.sorts.forEach((sort, index) => {
1391
+ responseText += `${index + 1}. Field: ${sort.field}, Direction: ${sort.direction}\n`;
1392
+ });
1393
+ }
1394
+ }
1395
+ }
1396
+
1397
+ // NEW v1.6.0 TOOLS - Base Management
1398
+ else if (toolName === 'create_base') {
1399
+ const { name, workspaceId, tables } = toolParams;
1400
+
1401
+ const body = {
1402
+ name,
1403
+ tables: tables.map(table => ({
1404
+ name: table.name,
1405
+ description: table.description,
1406
+ fields: table.fields
1407
+ }))
1408
+ };
1409
+
1410
+ if (workspaceId) {
1411
+ body.workspaceId = workspaceId;
1412
+ }
1413
+
1414
+ result = await callAirtableAPI('meta/bases', 'POST', body);
1415
+
1416
+ responseText = `Successfully created base "${name}":\n`;
1417
+ responseText += `Base ID: ${result.id}\n`;
1418
+ if (result.tables && result.tables.length > 0) {
1419
+ responseText += `\nTables created (${result.tables.length}):\n`;
1420
+ result.tables.forEach((table, index) => {
1421
+ responseText += `${index + 1}. ${table.name} (ID: ${table.id})\n`;
1422
+ if (table.fields && table.fields.length > 0) {
1423
+ responseText += ` Fields: ${table.fields.length}\n`;
1424
+ }
1425
+ });
1426
+ }
1427
+ }
1428
+
1429
+ else if (toolName === 'list_collaborators') {
1430
+ const { baseId: targetBaseId } = toolParams;
1431
+ const targetId = targetBaseId || baseId;
1432
+
1433
+ result = await callAirtableAPI(`meta/bases/${targetId}/collaborators`, 'GET');
1434
+
1435
+ if (result.collaborators && result.collaborators.length > 0) {
1436
+ responseText = `Base collaborators (${result.collaborators.length}):\n\n`;
1437
+ result.collaborators.forEach((collaborator, index) => {
1438
+ responseText += `${index + 1}. ${collaborator.email || collaborator.name || 'Unknown'}\n`;
1439
+ responseText += ` Permission: ${collaborator.permissionLevel || 'Unknown'}\n`;
1440
+ responseText += ` Type: ${collaborator.type || 'User'}\n`;
1441
+ if (collaborator.userId) {
1442
+ responseText += ` User ID: ${collaborator.userId}\n`;
1443
+ }
1444
+ responseText += '\n';
1445
+ });
1446
+ } else {
1447
+ responseText = 'No collaborators found for this base.';
1448
+ }
1449
+ }
1450
+
1451
+ else if (toolName === 'list_shares') {
1452
+ const { baseId: targetBaseId } = toolParams;
1453
+ const targetId = targetBaseId || baseId;
1454
+
1455
+ result = await callAirtableAPI(`meta/bases/${targetId}/shares`, 'GET');
1456
+
1457
+ if (result.shares && result.shares.length > 0) {
1458
+ responseText = `Shared views (${result.shares.length}):\n\n`;
1459
+ result.shares.forEach((share, index) => {
1460
+ responseText += `${index + 1}. ${share.name || 'Unnamed Share'}\n`;
1461
+ responseText += ` Share ID: ${share.id}\n`;
1462
+ responseText += ` URL: ${share.url}\n`;
1463
+ responseText += ` Type: ${share.type || 'View'}\n`;
1464
+ if (share.viewId) {
1465
+ responseText += ` View ID: ${share.viewId}\n`;
1466
+ }
1467
+ if (share.tableId) {
1468
+ responseText += ` Table ID: ${share.tableId}\n`;
1469
+ }
1470
+ responseText += ` Effective: ${share.effective ? 'Yes' : 'No'}\n`;
1471
+ responseText += '\n';
1472
+ });
1473
+ } else {
1474
+ responseText = 'No shared views found for this base.';
1475
+ }
1476
+ }
1477
+
1032
1478
  else {
1033
1479
  throw new Error(`Unknown tool: ${toolName}`);
1034
1480
  }