@rashidazarang/airtable-mcp 1.4.0 → 1.6.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.
- package/DEVELOPMENT.md +1 -0
- package/MCP_REVIEW_SUMMARY.md +1 -0
- package/README.md +142 -15
- package/RELEASE_NOTES_v1.2.3.md +1 -0
- package/RELEASE_NOTES_v1.5.0.md +185 -0
- package/RELEASE_NOTES_v1.6.0.md +248 -0
- package/airtable_simple.js +911 -3
- package/cleanup.sh +1 -0
- package/inspector_server.py +1 -1
- package/package.json +1 -1
- package/quick_test.sh +1 -0
- package/test_mcp_comprehensive.js +1 -0
- package/test_v1.5.0_comprehensive.sh +96 -0
- package/test_v1.5.0_final.sh +224 -0
- package/test_v1.6.0_comprehensive.sh +187 -0
package/airtable_simple.js
CHANGED
|
@@ -55,9 +55,9 @@ function log(level, message, ...args) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
log(LOG_LEVELS.INFO, `Starting Enhanced Airtable MCP server v1.
|
|
59
|
-
log(LOG_LEVELS.INFO, `
|
|
60
|
-
log(LOG_LEVELS.INFO, `Base
|
|
58
|
+
log(LOG_LEVELS.INFO, `Starting Enhanced Airtable MCP server v1.6.0`);
|
|
59
|
+
log(LOG_LEVELS.INFO, `Authentication configured`);
|
|
60
|
+
log(LOG_LEVELS.INFO, `Base connection established`);
|
|
61
61
|
|
|
62
62
|
// Enhanced Airtable API function with full HTTP method support
|
|
63
63
|
function callAirtableAPI(endpoint, method = 'GET', body = null, queryParams = {}) {
|
|
@@ -319,6 +319,340 @@ const server = http.createServer(async (req, res) => {
|
|
|
319
319
|
},
|
|
320
320
|
required: ['webhookId']
|
|
321
321
|
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: 'list_bases',
|
|
325
|
+
description: 'List all accessible Airtable bases',
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: 'object',
|
|
328
|
+
properties: {
|
|
329
|
+
offset: { type: 'string', description: 'Pagination offset for listing more bases' }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: 'get_base_schema',
|
|
335
|
+
description: 'Get complete schema information for a base',
|
|
336
|
+
inputSchema: {
|
|
337
|
+
type: 'object',
|
|
338
|
+
properties: {
|
|
339
|
+
baseId: { type: 'string', description: 'Base ID to get schema for (optional, defaults to current base)' }
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: 'describe_table',
|
|
345
|
+
description: 'Get detailed information about a specific table including all fields',
|
|
346
|
+
inputSchema: {
|
|
347
|
+
type: 'object',
|
|
348
|
+
properties: {
|
|
349
|
+
table: { type: 'string', description: 'Table name or ID' }
|
|
350
|
+
},
|
|
351
|
+
required: ['table']
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: 'create_table',
|
|
356
|
+
description: 'Create a new table in the base',
|
|
357
|
+
inputSchema: {
|
|
358
|
+
type: 'object',
|
|
359
|
+
properties: {
|
|
360
|
+
name: { type: 'string', description: 'Name for the new table' },
|
|
361
|
+
description: { type: 'string', description: 'Optional description for the table' },
|
|
362
|
+
fields: {
|
|
363
|
+
type: 'array',
|
|
364
|
+
description: 'Array of field definitions',
|
|
365
|
+
items: {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
name: { type: 'string', description: 'Field name' },
|
|
369
|
+
type: { type: 'string', description: 'Field type (singleLineText, number, etc.)' },
|
|
370
|
+
description: { type: 'string', description: 'Field description' },
|
|
371
|
+
options: { type: 'object', description: 'Field-specific options' }
|
|
372
|
+
},
|
|
373
|
+
required: ['name', 'type']
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
required: ['name', 'fields']
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: 'update_table',
|
|
382
|
+
description: 'Update table name or description',
|
|
383
|
+
inputSchema: {
|
|
384
|
+
type: 'object',
|
|
385
|
+
properties: {
|
|
386
|
+
table: { type: 'string', description: 'Table name or ID' },
|
|
387
|
+
name: { type: 'string', description: 'New table name' },
|
|
388
|
+
description: { type: 'string', description: 'New table description' }
|
|
389
|
+
},
|
|
390
|
+
required: ['table']
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'delete_table',
|
|
395
|
+
description: 'Delete a table (WARNING: This will permanently delete all data)',
|
|
396
|
+
inputSchema: {
|
|
397
|
+
type: 'object',
|
|
398
|
+
properties: {
|
|
399
|
+
table: { type: 'string', description: 'Table name or ID to delete' },
|
|
400
|
+
confirm: { type: 'boolean', description: 'Must be true to confirm deletion' }
|
|
401
|
+
},
|
|
402
|
+
required: ['table', 'confirm']
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: 'create_field',
|
|
407
|
+
description: 'Add a new field to an existing table',
|
|
408
|
+
inputSchema: {
|
|
409
|
+
type: 'object',
|
|
410
|
+
properties: {
|
|
411
|
+
table: { type: 'string', description: 'Table name or ID' },
|
|
412
|
+
name: { type: 'string', description: 'Field name' },
|
|
413
|
+
type: { type: 'string', description: 'Field type (singleLineText, number, multipleSelectionList, etc.)' },
|
|
414
|
+
description: { type: 'string', description: 'Field description' },
|
|
415
|
+
options: { type: 'object', description: 'Field-specific options (e.g., choices for select fields)' }
|
|
416
|
+
},
|
|
417
|
+
required: ['table', 'name', 'type']
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: 'update_field',
|
|
422
|
+
description: 'Update field properties',
|
|
423
|
+
inputSchema: {
|
|
424
|
+
type: 'object',
|
|
425
|
+
properties: {
|
|
426
|
+
table: { type: 'string', description: 'Table name or ID' },
|
|
427
|
+
fieldId: { type: 'string', description: 'Field ID to update' },
|
|
428
|
+
name: { type: 'string', description: 'New field name' },
|
|
429
|
+
description: { type: 'string', description: 'New field description' },
|
|
430
|
+
options: { type: 'object', description: 'Updated field options' }
|
|
431
|
+
},
|
|
432
|
+
required: ['table', 'fieldId']
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'delete_field',
|
|
437
|
+
description: 'Delete a field from a table (WARNING: This will permanently delete all data in this field)',
|
|
438
|
+
inputSchema: {
|
|
439
|
+
type: 'object',
|
|
440
|
+
properties: {
|
|
441
|
+
table: { type: 'string', description: 'Table name or ID' },
|
|
442
|
+
fieldId: { type: 'string', description: 'Field ID to delete' },
|
|
443
|
+
confirm: { type: 'boolean', description: 'Must be true to confirm deletion' }
|
|
444
|
+
},
|
|
445
|
+
required: ['table', 'fieldId', 'confirm']
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: 'list_field_types',
|
|
450
|
+
description: 'Get a reference of all available Airtable field types and their schemas',
|
|
451
|
+
inputSchema: {
|
|
452
|
+
type: 'object',
|
|
453
|
+
properties: {}
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
name: 'get_table_views',
|
|
458
|
+
description: 'List all views for a specific table',
|
|
459
|
+
inputSchema: {
|
|
460
|
+
type: 'object',
|
|
461
|
+
properties: {
|
|
462
|
+
table: { type: 'string', description: 'Table name or ID' }
|
|
463
|
+
},
|
|
464
|
+
required: ['table']
|
|
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
|
+
}
|
|
322
656
|
}
|
|
323
657
|
]
|
|
324
658
|
}
|
|
@@ -567,6 +901,580 @@ const server = http.createServer(async (req, res) => {
|
|
|
567
901
|
`New expiration: ${result.expirationTime}`;
|
|
568
902
|
}
|
|
569
903
|
|
|
904
|
+
// Schema Management Tools
|
|
905
|
+
else if (toolName === 'list_bases') {
|
|
906
|
+
const { offset } = toolParams;
|
|
907
|
+
const queryParams = offset ? { offset } : {};
|
|
908
|
+
|
|
909
|
+
result = await callAirtableAPI('meta/bases', 'GET', null, queryParams);
|
|
910
|
+
|
|
911
|
+
if (result.bases && result.bases.length > 0) {
|
|
912
|
+
responseText = `Found ${result.bases.length} accessible base(s):\n`;
|
|
913
|
+
result.bases.forEach((base, index) => {
|
|
914
|
+
responseText += `${index + 1}. ${base.name} (ID: ${base.id})\n`;
|
|
915
|
+
if (base.permissionLevel) {
|
|
916
|
+
responseText += ` Permission: ${base.permissionLevel}\n`;
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
if (result.offset) {
|
|
920
|
+
responseText += `\nNext page offset: ${result.offset}`;
|
|
921
|
+
}
|
|
922
|
+
} else {
|
|
923
|
+
responseText = 'No accessible bases found.';
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
else if (toolName === 'get_base_schema') {
|
|
928
|
+
const { baseId: targetBaseId } = toolParams;
|
|
929
|
+
const targetId = targetBaseId || baseId;
|
|
930
|
+
|
|
931
|
+
result = await callAirtableAPI(`meta/bases/${targetId}/tables`, 'GET');
|
|
932
|
+
|
|
933
|
+
if (result.tables && result.tables.length > 0) {
|
|
934
|
+
responseText = `Base schema for ${targetId}:\n\n`;
|
|
935
|
+
result.tables.forEach((table, index) => {
|
|
936
|
+
responseText += `${index + 1}. Table: ${table.name} (ID: ${table.id})\n`;
|
|
937
|
+
if (table.description) {
|
|
938
|
+
responseText += ` Description: ${table.description}\n`;
|
|
939
|
+
}
|
|
940
|
+
responseText += ` Fields (${table.fields.length}):\n`;
|
|
941
|
+
table.fields.forEach((field, fieldIndex) => {
|
|
942
|
+
responseText += ` ${fieldIndex + 1}. ${field.name} (${field.type})\n`;
|
|
943
|
+
if (field.description) {
|
|
944
|
+
responseText += ` Description: ${field.description}\n`;
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
if (table.views && table.views.length > 0) {
|
|
948
|
+
responseText += ` Views (${table.views.length}): ${table.views.map(v => v.name).join(', ')}\n`;
|
|
949
|
+
}
|
|
950
|
+
responseText += '\n';
|
|
951
|
+
});
|
|
952
|
+
} else {
|
|
953
|
+
responseText = 'No tables found in this base.';
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
else if (toolName === 'describe_table') {
|
|
958
|
+
const { table } = toolParams;
|
|
959
|
+
|
|
960
|
+
// Get table schema first
|
|
961
|
+
const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
|
|
962
|
+
const tableInfo = schemaResult.tables.find(t =>
|
|
963
|
+
t.name.toLowerCase() === table.toLowerCase() || t.id === table
|
|
964
|
+
);
|
|
965
|
+
|
|
966
|
+
if (!tableInfo) {
|
|
967
|
+
responseText = `Table "${table}" not found.`;
|
|
968
|
+
} else {
|
|
969
|
+
responseText = `Table Details: ${tableInfo.name}\n`;
|
|
970
|
+
responseText += `ID: ${tableInfo.id}\n`;
|
|
971
|
+
if (tableInfo.description) {
|
|
972
|
+
responseText += `Description: ${tableInfo.description}\n`;
|
|
973
|
+
}
|
|
974
|
+
responseText += `\nFields (${tableInfo.fields.length}):\n`;
|
|
975
|
+
|
|
976
|
+
tableInfo.fields.forEach((field, index) => {
|
|
977
|
+
responseText += `${index + 1}. ${field.name}\n`;
|
|
978
|
+
responseText += ` Type: ${field.type}\n`;
|
|
979
|
+
responseText += ` ID: ${field.id}\n`;
|
|
980
|
+
if (field.description) {
|
|
981
|
+
responseText += ` Description: ${field.description}\n`;
|
|
982
|
+
}
|
|
983
|
+
if (field.options) {
|
|
984
|
+
responseText += ` Options: ${JSON.stringify(field.options, null, 2)}\n`;
|
|
985
|
+
}
|
|
986
|
+
responseText += '\n';
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
if (tableInfo.views && tableInfo.views.length > 0) {
|
|
990
|
+
responseText += `Views (${tableInfo.views.length}):\n`;
|
|
991
|
+
tableInfo.views.forEach((view, index) => {
|
|
992
|
+
responseText += `${index + 1}. ${view.name} (${view.type})\n`;
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
else if (toolName === 'create_table') {
|
|
999
|
+
const { name, description, fields } = toolParams;
|
|
1000
|
+
|
|
1001
|
+
const body = {
|
|
1002
|
+
name,
|
|
1003
|
+
fields: fields.map(field => ({
|
|
1004
|
+
name: field.name,
|
|
1005
|
+
type: field.type,
|
|
1006
|
+
description: field.description,
|
|
1007
|
+
options: field.options
|
|
1008
|
+
}))
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
if (description) {
|
|
1012
|
+
body.description = description;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
result = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'POST', body);
|
|
1016
|
+
|
|
1017
|
+
responseText = `Successfully created table "${name}" (ID: ${result.id})\n`;
|
|
1018
|
+
responseText += `Fields created: ${result.fields.length}\n`;
|
|
1019
|
+
result.fields.forEach((field, index) => {
|
|
1020
|
+
responseText += `${index + 1}. ${field.name} (${field.type})\n`;
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
else if (toolName === 'update_table') {
|
|
1025
|
+
const { table, name, description } = toolParams;
|
|
1026
|
+
|
|
1027
|
+
// Get table ID first
|
|
1028
|
+
const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
|
|
1029
|
+
const tableInfo = schemaResult.tables.find(t =>
|
|
1030
|
+
t.name.toLowerCase() === table.toLowerCase() || t.id === table
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1033
|
+
if (!tableInfo) {
|
|
1034
|
+
responseText = `Table "${table}" not found.`;
|
|
1035
|
+
} else {
|
|
1036
|
+
const body = {};
|
|
1037
|
+
if (name) body.name = name;
|
|
1038
|
+
if (description !== undefined) body.description = description;
|
|
1039
|
+
|
|
1040
|
+
if (Object.keys(body).length === 0) {
|
|
1041
|
+
responseText = 'No updates specified. Provide name or description to update.';
|
|
1042
|
+
} else {
|
|
1043
|
+
result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}`, 'PATCH', body);
|
|
1044
|
+
responseText = `Successfully updated table "${tableInfo.name}":\n`;
|
|
1045
|
+
if (name) responseText += `New name: ${result.name}\n`;
|
|
1046
|
+
if (description !== undefined) responseText += `New description: ${result.description || '(none)'}\n`;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
else if (toolName === 'delete_table') {
|
|
1052
|
+
const { table, confirm } = toolParams;
|
|
1053
|
+
|
|
1054
|
+
if (!confirm) {
|
|
1055
|
+
responseText = 'Table deletion requires confirm=true to proceed. This action cannot be undone!';
|
|
1056
|
+
} else {
|
|
1057
|
+
// Get table ID first
|
|
1058
|
+
const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
|
|
1059
|
+
const tableInfo = schemaResult.tables.find(t =>
|
|
1060
|
+
t.name.toLowerCase() === table.toLowerCase() || t.id === table
|
|
1061
|
+
);
|
|
1062
|
+
|
|
1063
|
+
if (!tableInfo) {
|
|
1064
|
+
responseText = `Table "${table}" not found.`;
|
|
1065
|
+
} else {
|
|
1066
|
+
result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}`, 'DELETE');
|
|
1067
|
+
responseText = `Successfully deleted table "${tableInfo.name}" (ID: ${tableInfo.id})\n`;
|
|
1068
|
+
responseText += 'All data in this table has been permanently removed.';
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// Field Management Tools
|
|
1074
|
+
else if (toolName === 'create_field') {
|
|
1075
|
+
const { table, name, type, description, options } = toolParams;
|
|
1076
|
+
|
|
1077
|
+
// Get table ID first
|
|
1078
|
+
const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
|
|
1079
|
+
const tableInfo = schemaResult.tables.find(t =>
|
|
1080
|
+
t.name.toLowerCase() === table.toLowerCase() || t.id === table
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
if (!tableInfo) {
|
|
1084
|
+
responseText = `Table "${table}" not found.`;
|
|
1085
|
+
} else {
|
|
1086
|
+
const body = {
|
|
1087
|
+
name,
|
|
1088
|
+
type
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
if (description) body.description = description;
|
|
1092
|
+
if (options) body.options = options;
|
|
1093
|
+
|
|
1094
|
+
result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/fields`, 'POST', body);
|
|
1095
|
+
|
|
1096
|
+
responseText = `Successfully created field "${name}" in table "${tableInfo.name}"\n`;
|
|
1097
|
+
responseText += `Field ID: ${result.id}\n`;
|
|
1098
|
+
responseText += `Type: ${result.type}\n`;
|
|
1099
|
+
if (result.description) {
|
|
1100
|
+
responseText += `Description: ${result.description}\n`;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
else if (toolName === 'update_field') {
|
|
1106
|
+
const { table, fieldId, name, description, options } = toolParams;
|
|
1107
|
+
|
|
1108
|
+
// Get table ID first
|
|
1109
|
+
const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
|
|
1110
|
+
const tableInfo = schemaResult.tables.find(t =>
|
|
1111
|
+
t.name.toLowerCase() === table.toLowerCase() || t.id === table
|
|
1112
|
+
);
|
|
1113
|
+
|
|
1114
|
+
if (!tableInfo) {
|
|
1115
|
+
responseText = `Table "${table}" not found.`;
|
|
1116
|
+
} else {
|
|
1117
|
+
const body = {};
|
|
1118
|
+
if (name) body.name = name;
|
|
1119
|
+
if (description !== undefined) body.description = description;
|
|
1120
|
+
if (options) body.options = options;
|
|
1121
|
+
|
|
1122
|
+
if (Object.keys(body).length === 0) {
|
|
1123
|
+
responseText = 'No updates specified. Provide name, description, or options to update.';
|
|
1124
|
+
} else {
|
|
1125
|
+
result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/fields/${fieldId}`, 'PATCH', body);
|
|
1126
|
+
responseText = `Successfully updated field in table "${tableInfo.name}":\n`;
|
|
1127
|
+
responseText += `Field: ${result.name} (${result.type})\n`;
|
|
1128
|
+
responseText += `ID: ${result.id}\n`;
|
|
1129
|
+
if (result.description) {
|
|
1130
|
+
responseText += `Description: ${result.description}\n`;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
else if (toolName === 'delete_field') {
|
|
1137
|
+
const { table, fieldId, confirm } = toolParams;
|
|
1138
|
+
|
|
1139
|
+
if (!confirm) {
|
|
1140
|
+
responseText = 'Field deletion requires confirm=true to proceed. This action cannot be undone!';
|
|
1141
|
+
} else {
|
|
1142
|
+
// Get table ID first
|
|
1143
|
+
const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
|
|
1144
|
+
const tableInfo = schemaResult.tables.find(t =>
|
|
1145
|
+
t.name.toLowerCase() === table.toLowerCase() || t.id === table
|
|
1146
|
+
);
|
|
1147
|
+
|
|
1148
|
+
if (!tableInfo) {
|
|
1149
|
+
responseText = `Table "${table}" not found.`;
|
|
1150
|
+
} else {
|
|
1151
|
+
result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/fields/${fieldId}`, 'DELETE');
|
|
1152
|
+
responseText = `Successfully deleted field from table "${tableInfo.name}"\n`;
|
|
1153
|
+
responseText += 'All data in this field has been permanently removed.';
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
else if (toolName === 'list_field_types') {
|
|
1159
|
+
responseText = `Available Airtable Field Types:\n\n`;
|
|
1160
|
+
responseText += `Basic Fields:\n`;
|
|
1161
|
+
responseText += `• singleLineText - Single line text input\n`;
|
|
1162
|
+
responseText += `• multilineText - Multi-line text input\n`;
|
|
1163
|
+
responseText += `• richText - Rich text with formatting\n`;
|
|
1164
|
+
responseText += `• number - Number field with optional formatting\n`;
|
|
1165
|
+
responseText += `• percent - Percentage field\n`;
|
|
1166
|
+
responseText += `• currency - Currency field\n`;
|
|
1167
|
+
responseText += `• singleSelect - Single choice from predefined options\n`;
|
|
1168
|
+
responseText += `• multipleSelectionList - Multiple choices from predefined options\n`;
|
|
1169
|
+
responseText += `• date - Date field\n`;
|
|
1170
|
+
responseText += `• dateTime - Date and time field\n`;
|
|
1171
|
+
responseText += `• phoneNumber - Phone number field\n`;
|
|
1172
|
+
responseText += `• email - Email address field\n`;
|
|
1173
|
+
responseText += `• url - URL field\n`;
|
|
1174
|
+
responseText += `• checkbox - Checkbox (true/false)\n`;
|
|
1175
|
+
responseText += `• rating - Star rating field\n`;
|
|
1176
|
+
responseText += `• duration - Duration/time field\n\n`;
|
|
1177
|
+
responseText += `Advanced Fields:\n`;
|
|
1178
|
+
responseText += `• multipleAttachment - File attachments\n`;
|
|
1179
|
+
responseText += `• linkedRecord - Link to records in another table\n`;
|
|
1180
|
+
responseText += `• lookup - Lookup values from linked records\n`;
|
|
1181
|
+
responseText += `• rollup - Calculate values from linked records\n`;
|
|
1182
|
+
responseText += `• count - Count of linked records\n`;
|
|
1183
|
+
responseText += `• formula - Calculated field with formulas\n`;
|
|
1184
|
+
responseText += `• createdTime - Auto-timestamp when record created\n`;
|
|
1185
|
+
responseText += `• createdBy - Auto-user who created record\n`;
|
|
1186
|
+
responseText += `• lastModifiedTime - Auto-timestamp when record modified\n`;
|
|
1187
|
+
responseText += `• lastModifiedBy - Auto-user who last modified record\n`;
|
|
1188
|
+
responseText += `• autoNumber - Auto-incrementing number\n`;
|
|
1189
|
+
responseText += `• barcode - Barcode/QR code field\n`;
|
|
1190
|
+
responseText += `• button - Action button field\n`;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
else if (toolName === 'get_table_views') {
|
|
1194
|
+
const { table } = toolParams;
|
|
1195
|
+
|
|
1196
|
+
// Get table schema
|
|
1197
|
+
const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
|
|
1198
|
+
const tableInfo = schemaResult.tables.find(t =>
|
|
1199
|
+
t.name.toLowerCase() === table.toLowerCase() || t.id === table
|
|
1200
|
+
);
|
|
1201
|
+
|
|
1202
|
+
if (!tableInfo) {
|
|
1203
|
+
responseText = `Table "${table}" not found.`;
|
|
1204
|
+
} else {
|
|
1205
|
+
if (tableInfo.views && tableInfo.views.length > 0) {
|
|
1206
|
+
responseText = `Views for table "${tableInfo.name}" (${tableInfo.views.length}):\n\n`;
|
|
1207
|
+
tableInfo.views.forEach((view, index) => {
|
|
1208
|
+
responseText += `${index + 1}. ${view.name}\n`;
|
|
1209
|
+
responseText += ` Type: ${view.type}\n`;
|
|
1210
|
+
responseText += ` ID: ${view.id}\n`;
|
|
1211
|
+
if (view.visibleFieldIds && view.visibleFieldIds.length > 0) {
|
|
1212
|
+
responseText += ` Visible fields: ${view.visibleFieldIds.length}\n`;
|
|
1213
|
+
}
|
|
1214
|
+
responseText += '\n';
|
|
1215
|
+
});
|
|
1216
|
+
} else {
|
|
1217
|
+
responseText = `No views found for table "${tableInfo.name}".`;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
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
|
+
|
|
570
1478
|
else {
|
|
571
1479
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
572
1480
|
}
|