#
tokens: 13822/50000 1/81 files (page 3/4)
lines: off (toggle) GitHub
raw markdown copy
This is page 3 of 4. Use http://codebase.md/rashidazarang/airtable-mcp?page={x} to view the full context.

# Directory Structure

```
├── .eslintrc.js
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   ├── custom.md
│   │   └── feature_request.md
│   └── pull_request_template.md
├── .gitignore
├── .nvmrc
├── .prettierrc
├── bin
│   ├── airtable-crud-cli.js
│   └── airtable-mcp.js
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── docker
│   ├── Dockerfile
│   └── Dockerfile.node
├── docs
│   ├── guides
│   │   ├── CLAUDE_INTEGRATION.md
│   │   ├── ENHANCED_FEATURES.md
│   │   ├── INSTALLATION.md
│   │   └── QUICK_START.md
│   └── releases
│       ├── RELEASE_NOTES_v1.2.2.md
│       ├── RELEASE_NOTES_v1.2.4.md
│       ├── RELEASE_NOTES_v1.4.0.md
│       ├── RELEASE_NOTES_v1.5.0.md
│       └── RELEASE_NOTES_v1.6.0.md
├── examples
│   ├── airtable-crud-example.js
│   ├── building-mcp.md
│   ├── claude_config.json
│   ├── claude_simple_config.json
│   ├── env-demo.js
│   ├── example_usage.md
│   ├── example-tasks-update.json
│   ├── example-tasks.json
│   ├── python_debug_patch.txt
│   ├── sample-transform.js
│   ├── typescript
│   │   ├── advanced-ai-prompts.ts
│   │   ├── basic-usage.ts
│   │   └── claude-desktop-config.json
│   └── windsurf_mcp_config.json
├── index.js
├── ISSUE_RESPONSES.md
├── jest.config.js
├── LICENSE
├── package-lock.json
├── package.json
├── PROJECT_STRUCTURE.md
├── README.md
├── RELEASE_SUMMARY_v3.2.x.md
├── RELEASE_v3.2.1.md
├── RELEASE_v3.2.3.md
├── RELEASE_v3.2.4.md
├── requirements.txt
├── SECURITY_NOTICE.md
├── smithery.yaml
├── src
│   ├── index.js
│   ├── javascript
│   │   ├── airtable_simple_production.js
│   │   └── airtable_simple.js
│   ├── python
│   │   ├── airtable_mcp
│   │   │   ├── __init__.py
│   │   │   └── src
│   │   │       └── server.py
│   │   ├── inspector_server.py
│   │   ├── inspector.py
│   │   ├── setup.py
│   │   ├── simple_airtable_server.py
│   │   └── test_client.py
│   └── typescript
│       ├── ai-prompts.d.ts
│       ├── airtable-mcp-server.d.ts
│       ├── airtable-mcp-server.ts
│       ├── errors.ts
│       ├── index.d.ts
│       ├── prompt-templates.ts
│       ├── test-suite.d.ts
│       ├── test-suite.ts
│       ├── tools-schemas.ts
│       └── tools.d.ts
├── TESTING_REPORT.md
├── tests
│   ├── test_all_features.sh
│   ├── test_mcp_comprehensive.js
│   ├── test_v1.5.0_final.sh
│   └── test_v1.6.0_comprehensive.sh
├── tsconfig.json
└── types
    └── typescript
        ├── airtable-mcp-server.d.ts
        ├── errors.d.ts
        ├── prompt-templates.d.ts
        ├── test-suite.d.ts
        └── tools-schemas.d.ts
```

# Files

--------------------------------------------------------------------------------
/src/javascript/airtable_simple.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node

const http = require('http');
const https = require('https');
const fs = require('fs');
const path = require('path');

// Load environment variables from .env file if it exists
const envPath = path.join(__dirname, '.env');
if (fs.existsSync(envPath)) {
  require('dotenv').config({ path: envPath });
}

// Parse command line arguments with environment variable fallback
const args = process.argv.slice(2);
let tokenIndex = args.indexOf('--token');
let baseIndex = args.indexOf('--base');

// Use environment variables as fallback
const token = tokenIndex !== -1 ? args[tokenIndex + 1] : process.env.AIRTABLE_TOKEN || process.env.AIRTABLE_API_TOKEN;
const baseId = baseIndex !== -1 ? args[baseIndex + 1] : process.env.AIRTABLE_BASE_ID || process.env.AIRTABLE_BASE;

if (!token || !baseId) {
  console.error('Error: Missing Airtable credentials');
  console.error('\nUsage options:');
  console.error('  1. Command line: node airtable_enhanced.js --token YOUR_TOKEN --base YOUR_BASE_ID');
  console.error('  2. Environment variables: AIRTABLE_TOKEN and AIRTABLE_BASE_ID');
  console.error('  3. .env file with AIRTABLE_TOKEN and AIRTABLE_BASE_ID');
  process.exit(1);
}

// Configure logging levels
const LOG_LEVELS = {
  ERROR: 0,
  WARN: 1,
  INFO: 2,
  DEBUG: 3
};

const currentLogLevel = process.env.LOG_LEVEL ? LOG_LEVELS[process.env.LOG_LEVEL.toUpperCase()] || LOG_LEVELS.INFO : LOG_LEVELS.INFO;

function log(level, message, ...args) {
  const levelName = Object.keys(LOG_LEVELS).find(key => LOG_LEVELS[key] === level);
  const timestamp = new Date().toISOString();
  
  if (level <= currentLogLevel) {
    const prefix = `[${timestamp}] [${levelName}]`;
    if (level === LOG_LEVELS.ERROR) {
      console.error(prefix, message, ...args);
    } else if (level === LOG_LEVELS.WARN) {
      console.warn(prefix, message, ...args);
    } else {
      console.log(prefix, message, ...args);
    }
  }
}

log(LOG_LEVELS.INFO, `Starting Enhanced Airtable MCP server v1.6.0`);
log(LOG_LEVELS.INFO, `Authentication configured`);
log(LOG_LEVELS.INFO, `Base connection established`);

// Enhanced Airtable API function with full HTTP method support
function callAirtableAPI(endpoint, method = 'GET', body = null, queryParams = {}) {
  return new Promise((resolve, reject) => {
    const isBaseEndpoint = !endpoint.startsWith('meta/') && !endpoint.startsWith('bases/');
    const baseUrl = isBaseEndpoint ? `${baseId}/${endpoint}` : endpoint;
    
    // Build query string
    const queryString = Object.keys(queryParams).length > 0 
      ? '?' + new URLSearchParams(queryParams).toString() 
      : '';
    
    const url = `https://api.airtable.com/v0/${baseUrl}${queryString}`;
    const urlObj = new URL(url);
    
    log(LOG_LEVELS.DEBUG, `API Request: ${method} ${url}`);
    if (body) {
      log(LOG_LEVELS.DEBUG, `Request body:`, JSON.stringify(body, null, 2));
    }
    
    const options = {
      hostname: urlObj.hostname,
      path: urlObj.pathname + urlObj.search,
      method: method,
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    };
    
    const req = https.request(options, (response) => {
      let data = '';
      
      response.on('data', (chunk) => {
        data += chunk;
      });
      
      response.on('end', () => {
        log(LOG_LEVELS.DEBUG, `Response status: ${response.statusCode}`);
        log(LOG_LEVELS.DEBUG, `Response data:`, data);
        
        try {
          const parsed = data ? JSON.parse(data) : {};
          
          if (response.statusCode >= 200 && response.statusCode < 300) {
            resolve(parsed);
          } else {
            const error = parsed.error || {};
            reject(new Error(`Airtable API error (${response.statusCode}): ${error.message || error.type || 'Unknown error'}`));
          }
        } catch (e) {
          reject(new Error(`Failed to parse Airtable response: ${e.message}`));
        }
      });
    });
    
    req.on('error', (error) => {
      reject(new Error(`Airtable API request failed: ${error.message}`));
    });
    
    if (body) {
      req.write(JSON.stringify(body));
    }
    
    req.end();
  });
}

// Create HTTP server
const server = http.createServer(async (req, res) => {
  // Enable CORS
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  
  // Handle preflight request
  if (req.method === 'OPTIONS') {
    res.writeHead(200);
    res.end();
    return;
  }
  
  // Only handle POST requests to /mcp
  if (req.method !== 'POST' || !req.url.endsWith('/mcp')) {
    res.writeHead(404);
    res.end();
    return;
  }
  
  let body = '';
  req.on('data', chunk => {
    body += chunk.toString();
  });
  
  req.on('end', async () => {
    try {
      const request = JSON.parse(body);
      log(LOG_LEVELS.DEBUG, 'Received request:', JSON.stringify(request, null, 2));
      
      // Handle JSON-RPC methods
      if (request.method === 'tools/list') {
        const response = {
          jsonrpc: '2.0',
          id: request.id,
          result: {
            tools: [
              {
                name: 'list_tables',
                description: 'List all tables in the Airtable base',
                inputSchema: {
                  type: 'object',
                  properties: {}
                }
              },
              {
                name: 'list_records',
                description: 'List records from a specific table',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    maxRecords: { type: 'number', description: 'Maximum number of records to return' },
                    view: { type: 'string', description: 'View name or ID' }
                  },
                  required: ['table']
                }
              },
              {
                name: 'get_record',
                description: 'Get a single record by ID',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    recordId: { type: 'string', description: 'Record ID' }
                  },
                  required: ['table', 'recordId']
                }
              },
              {
                name: 'create_record',
                description: 'Create a new record in a table',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    fields: { type: 'object', description: 'Field values for the new record' }
                  },
                  required: ['table', 'fields']
                }
              },
              {
                name: 'update_record',
                description: 'Update an existing record',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    recordId: { type: 'string', description: 'Record ID to update' },
                    fields: { type: 'object', description: 'Fields to update' }
                  },
                  required: ['table', 'recordId', 'fields']
                }
              },
              {
                name: 'delete_record',
                description: 'Delete a record from a table',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    recordId: { type: 'string', description: 'Record ID to delete' }
                  },
                  required: ['table', 'recordId']
                }
              },
              {
                name: 'search_records',
                description: 'Search records with filtering and sorting',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    filterByFormula: { type: 'string', description: 'Airtable formula to filter records' },
                    sort: { type: 'array', description: 'Sort configuration' },
                    maxRecords: { type: 'number', description: 'Maximum records to return' },
                    fields: { type: 'array', description: 'Fields to return' }
                  },
                  required: ['table']
                }
              },
              {
                name: 'list_webhooks',
                description: 'List all webhooks for the base',
                inputSchema: {
                  type: 'object',
                  properties: {}
                }
              },
              {
                name: 'create_webhook',
                description: 'Create a new webhook for a table',
                inputSchema: {
                  type: 'object',
                  properties: {
                    notificationUrl: { type: 'string', description: 'URL to receive webhook notifications' },
                    specification: {
                      type: 'object',
                      description: 'Webhook specification',
                      properties: {
                        options: {
                          type: 'object',
                          properties: {
                            filters: {
                              type: 'object',
                              properties: {
                                dataTypes: { type: 'array', items: { type: 'string' } },
                                recordChangeScope: { type: 'string' }
                              }
                            }
                          }
                        }
                      }
                    }
                  },
                  required: ['notificationUrl']
                }
              },
              {
                name: 'delete_webhook',
                description: 'Delete a webhook',
                inputSchema: {
                  type: 'object',
                  properties: {
                    webhookId: { type: 'string', description: 'Webhook ID to delete' }
                  },
                  required: ['webhookId']
                }
              },
              {
                name: 'get_webhook_payloads',
                description: 'Get webhook payload history',
                inputSchema: {
                  type: 'object',
                  properties: {
                    webhookId: { type: 'string', description: 'Webhook ID' },
                    cursor: { type: 'number', description: 'Cursor for pagination' }
                  },
                  required: ['webhookId']
                }
              },
              {
                name: 'refresh_webhook',
                description: 'Refresh a webhook to extend its expiration',
                inputSchema: {
                  type: 'object',
                  properties: {
                    webhookId: { type: 'string', description: 'Webhook ID to refresh' }
                  },
                  required: ['webhookId']
                }
              },
              {
                name: 'list_bases',
                description: 'List all accessible Airtable bases',
                inputSchema: {
                  type: 'object',
                  properties: {
                    offset: { type: 'string', description: 'Pagination offset for listing more bases' }
                  }
                }
              },
              {
                name: 'get_base_schema',
                description: 'Get complete schema information for a base',
                inputSchema: {
                  type: 'object',
                  properties: {
                    baseId: { type: 'string', description: 'Base ID to get schema for (optional, defaults to current base)' }
                  }
                }
              },
              {
                name: 'describe_table',
                description: 'Get detailed information about a specific table including all fields',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' }
                  },
                  required: ['table']
                }
              },
              {
                name: 'create_table',
                description: 'Create a new table in the base',
                inputSchema: {
                  type: 'object',
                  properties: {
                    name: { type: 'string', description: 'Name for the new table' },
                    description: { type: 'string', description: 'Optional description for the table' },
                    fields: { 
                      type: 'array', 
                      description: 'Array of field definitions',
                      items: {
                        type: 'object',
                        properties: {
                          name: { type: 'string', description: 'Field name' },
                          type: { type: 'string', description: 'Field type (singleLineText, number, etc.)' },
                          description: { type: 'string', description: 'Field description' },
                          options: { type: 'object', description: 'Field-specific options' }
                        },
                        required: ['name', 'type']
                      }
                    }
                  },
                  required: ['name', 'fields']
                }
              },
              {
                name: 'update_table',
                description: 'Update table name or description',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    name: { type: 'string', description: 'New table name' },
                    description: { type: 'string', description: 'New table description' }
                  },
                  required: ['table']
                }
              },
              {
                name: 'delete_table',
                description: 'Delete a table (WARNING: This will permanently delete all data)',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID to delete' },
                    confirm: { type: 'boolean', description: 'Must be true to confirm deletion' }
                  },
                  required: ['table', 'confirm']
                }
              },
              {
                name: 'create_field',
                description: 'Add a new field to an existing table',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    name: { type: 'string', description: 'Field name' },
                    type: { type: 'string', description: 'Field type (singleLineText, number, multipleSelectionList, etc.)' },
                    description: { type: 'string', description: 'Field description' },
                    options: { type: 'object', description: 'Field-specific options (e.g., choices for select fields)' }
                  },
                  required: ['table', 'name', 'type']
                }
              },
              {
                name: 'update_field',
                description: 'Update field properties',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    fieldId: { type: 'string', description: 'Field ID to update' },
                    name: { type: 'string', description: 'New field name' },
                    description: { type: 'string', description: 'New field description' },
                    options: { type: 'object', description: 'Updated field options' }
                  },
                  required: ['table', 'fieldId']
                }
              },
              {
                name: 'delete_field',
                description: 'Delete a field from a table (WARNING: This will permanently delete all data in this field)',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    fieldId: { type: 'string', description: 'Field ID to delete' },
                    confirm: { type: 'boolean', description: 'Must be true to confirm deletion' }
                  },
                  required: ['table', 'fieldId', 'confirm']
                }
              },
              {
                name: 'list_field_types',
                description: 'Get a reference of all available Airtable field types and their schemas',
                inputSchema: {
                  type: 'object',
                  properties: {}
                }
              },
              {
                name: 'get_table_views',
                description: 'List all views for a specific table',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' }
                  },
                  required: ['table']
                }
              },
              {
                name: 'upload_attachment',
                description: 'Upload/attach a file from URL to a record',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    recordId: { type: 'string', description: 'Record ID to attach file to' },
                    fieldName: { type: 'string', description: 'Name of the attachment field' },
                    url: { type: 'string', description: 'Public URL of the file to attach' },
                    filename: { type: 'string', description: 'Optional filename for the attachment' }
                  },
                  required: ['table', 'recordId', 'fieldName', 'url']
                }
              },
              {
                name: 'batch_create_records',
                description: 'Create multiple records at once (up to 10)',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    records: {
                      type: 'array',
                      description: 'Array of record objects to create (max 10)',
                      items: {
                        type: 'object',
                        properties: {
                          fields: { type: 'object', description: 'Record fields' }
                        },
                        required: ['fields']
                      },
                      maxItems: 10
                    }
                  },
                  required: ['table', 'records']
                }
              },
              {
                name: 'batch_update_records',
                description: 'Update multiple records at once (up to 10)',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    records: {
                      type: 'array',
                      description: 'Array of record objects to update (max 10)',
                      items: {
                        type: 'object',
                        properties: {
                          id: { type: 'string', description: 'Record ID' },
                          fields: { type: 'object', description: 'Fields to update' }
                        },
                        required: ['id', 'fields']
                      },
                      maxItems: 10
                    }
                  },
                  required: ['table', 'records']
                }
              },
              {
                name: 'batch_delete_records',
                description: 'Delete multiple records at once (up to 10)',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    recordIds: {
                      type: 'array',
                      description: 'Array of record IDs to delete (max 10)',
                      items: { type: 'string' },
                      maxItems: 10
                    }
                  },
                  required: ['table', 'recordIds']
                }
              },
              {
                name: 'batch_upsert_records',
                description: 'Update existing records or create new ones based on key fields',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    records: {
                      type: 'array',
                      description: 'Array of record objects (max 10)',
                      items: {
                        type: 'object',
                        properties: {
                          fields: { type: 'object', description: 'Record fields' }
                        },
                        required: ['fields']
                      },
                      maxItems: 10
                    },
                    keyFields: {
                      type: 'array',
                      description: 'Fields to use for matching existing records',
                      items: { type: 'string' }
                    }
                  },
                  required: ['table', 'records', 'keyFields']
                }
              },
              {
                name: 'create_view',
                description: 'Create a new view for a table',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    name: { type: 'string', description: 'Name for the new view' },
                    type: { type: 'string', description: 'View type (grid, form, calendar, etc.)', enum: ['grid', 'form', 'calendar', 'gallery', 'kanban', 'timeline', 'gantt'] },
                    visibleFieldIds: { type: 'array', description: 'Array of field IDs to show in view', items: { type: 'string' } },
                    fieldOrder: { type: 'array', description: 'Order of fields in view', items: { type: 'string' } }
                  },
                  required: ['table', 'name', 'type']
                }
              },
              {
                name: 'get_view_metadata',
                description: 'Get detailed metadata for a specific view',
                inputSchema: {
                  type: 'object',
                  properties: {
                    table: { type: 'string', description: 'Table name or ID' },
                    viewId: { type: 'string', description: 'View ID' }
                  },
                  required: ['table', 'viewId']
                }
              },
              {
                name: 'create_base',
                description: 'Create a new Airtable base',
                inputSchema: {
                  type: 'object',
                  properties: {
                    name: { type: 'string', description: 'Name for the new base' },
                    workspaceId: { type: 'string', description: 'Workspace ID to create the base in' },
                    tables: {
                      type: 'array',
                      description: 'Initial tables to create in the base',
                      items: {
                        type: 'object',
                        properties: {
                          name: { type: 'string', description: 'Table name' },
                          description: { type: 'string', description: 'Table description' },
                          fields: {
                            type: 'array',
                            description: 'Table fields',
                            items: {
                              type: 'object',
                              properties: {
                                name: { type: 'string', description: 'Field name' },
                                type: { type: 'string', description: 'Field type' }
                              },
                              required: ['name', 'type']
                            }
                          }
                        },
                        required: ['name', 'fields']
                      }
                    }
                  },
                  required: ['name', 'tables']
                }
              },
              {
                name: 'list_collaborators',
                description: 'List collaborators and their permissions for the current base',
                inputSchema: {
                  type: 'object',
                  properties: {
                    baseId: { type: 'string', description: 'Base ID (optional, defaults to current base)' }
                  }
                }
              },
              {
                name: 'list_shares',
                description: 'List shared views and their configurations',
                inputSchema: {
                  type: 'object',
                  properties: {
                    baseId: { type: 'string', description: 'Base ID (optional, defaults to current base)' }
                  }
                }
              }
            ]
          }
        };
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(response));
        return;
      }
      
      if (request.method === 'resources/list') {
        const response = {
          jsonrpc: '2.0',
          id: request.id,
          result: {
            resources: [
              {
                id: 'airtable_tables',
                name: 'Airtable Tables',
                description: 'Tables in your Airtable base'
              }
            ]
          }
        };
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(response));
        return;
      }
      
      if (request.method === 'prompts/list') {
        const response = {
          jsonrpc: '2.0',
          id: request.id,
          result: {
            prompts: [
              {
                id: 'tables_prompt',
                name: 'List Tables',
                description: 'List all tables'
              }
            ]
          }
        };
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(response));
        return;
      }
      
      // Handle tool calls
      if (request.method === 'tools/call') {
        const toolName = request.params.name;
        const toolParams = request.params.arguments || {};
        
        let result;
        let responseText;
        
        try {
          // LIST TABLES
          if (toolName === 'list_tables') {
            result = await callAirtableAPI(`meta/bases/${baseId}/tables`);
            const tables = result.tables || [];
            
            responseText = tables.length > 0 
              ? `Found ${tables.length} table(s):\n` + tables.map((table, i) => 
                  `${i+1}. ${table.name} (ID: ${table.id}, Fields: ${table.fields?.length || 0})`
                ).join('\n')
              : 'No tables found in this base.';
          }
          
          // LIST RECORDS
          else if (toolName === 'list_records') {
            const { table, maxRecords, view } = toolParams;
            
            const queryParams = {};
            if (maxRecords) queryParams.maxRecords = maxRecords;
            if (view) queryParams.view = view;
            
            result = await callAirtableAPI(`${table}`, 'GET', null, queryParams);
            const records = result.records || [];
            
            responseText = records.length > 0
              ? `Found ${records.length} record(s) in table "${table}":\n` + 
                records.map((record, i) => 
                  `${i+1}. ID: ${record.id}\n   Fields: ${JSON.stringify(record.fields, null, 2)}`
                ).join('\n\n')
              : `No records found in table "${table}".`;
          }
          
          // GET SINGLE RECORD
          else if (toolName === 'get_record') {
            const { table, recordId } = toolParams;
            
            result = await callAirtableAPI(`${table}/${recordId}`);
            
            responseText = `Record ${recordId} from table "${table}":\n` + 
              JSON.stringify(result.fields, null, 2) +
              `\n\nCreated: ${result.createdTime}`;
          }
          
          // CREATE RECORD
          else if (toolName === 'create_record') {
            const { table, fields } = toolParams;
            
            const body = {
              fields: fields
            };
            
            result = await callAirtableAPI(table, 'POST', body);
            
            responseText = `Successfully created record in table "${table}":\n` +
              `Record ID: ${result.id}\n` +
              `Fields: ${JSON.stringify(result.fields, null, 2)}\n` +
              `Created at: ${result.createdTime}`;
          }
          
          // UPDATE RECORD
          else if (toolName === 'update_record') {
            const { table, recordId, fields } = toolParams;
            
            const body = {
              fields: fields
            };
            
            result = await callAirtableAPI(`${table}/${recordId}`, 'PATCH', body);
            
            responseText = `Successfully updated record ${recordId} in table "${table}":\n` +
              `Updated fields: ${JSON.stringify(result.fields, null, 2)}`;
          }
          
          // DELETE RECORD
          else if (toolName === 'delete_record') {
            const { table, recordId } = toolParams;
            
            result = await callAirtableAPI(`${table}/${recordId}`, 'DELETE');
            
            responseText = `Successfully deleted record ${recordId} from table "${table}".\n` +
              `Deleted record ID: ${result.id}\n` +
              `Deleted: ${result.deleted}`;
          }
          
          // SEARCH RECORDS
          else if (toolName === 'search_records') {
            const { table, filterByFormula, sort, maxRecords, fields } = toolParams;
            
            const queryParams = {};
            if (filterByFormula) queryParams.filterByFormula = filterByFormula;
            if (maxRecords) queryParams.maxRecords = maxRecords;
            if (fields && fields.length > 0) queryParams.fields = fields;
            if (sort && sort.length > 0) {
              sort.forEach((s, i) => {
                queryParams[`sort[${i}][field]`] = s.field;
                queryParams[`sort[${i}][direction]`] = s.direction || 'asc';
              });
            }
            
            result = await callAirtableAPI(table, 'GET', null, queryParams);
            const records = result.records || [];
            
            responseText = records.length > 0
              ? `Found ${records.length} matching record(s) in table "${table}":\n` + 
                records.map((record, i) => 
                  `${i+1}. ID: ${record.id}\n   Fields: ${JSON.stringify(record.fields, null, 2)}`
                ).join('\n\n')
              : `No records found matching the search criteria in table "${table}".`;
          }
          
          // LIST WEBHOOKS
          else if (toolName === 'list_webhooks') {
            result = await callAirtableAPI(`bases/${baseId}/webhooks`, 'GET');
            const webhooks = result.webhooks || [];
            
            responseText = webhooks.length > 0
              ? `Found ${webhooks.length} webhook(s):\n` + 
                webhooks.map((webhook, i) => 
                  `${i+1}. ID: ${webhook.id}\n` +
                  `   URL: ${webhook.notificationUrl}\n` +
                  `   Active: ${webhook.isHookEnabled}\n` +
                  `   Created: ${webhook.createdTime}\n` +
                  `   Expires: ${webhook.expirationTime}`
                ).join('\n\n')
              : 'No webhooks configured for this base.';
          }
          
          // CREATE WEBHOOK
          else if (toolName === 'create_webhook') {
            const { notificationUrl, specification } = toolParams;
            
            const body = {
              notificationUrl: notificationUrl,
              specification: specification || {
                options: {
                  filters: {
                    dataTypes: ['tableData']
                  }
                }
              }
            };
            
            result = await callAirtableAPI(`bases/${baseId}/webhooks`, 'POST', body);
            
            responseText = `Successfully created webhook:\n` +
              `Webhook ID: ${result.id}\n` +
              `URL: ${result.notificationUrl}\n` +
              `MAC Secret: ${result.macSecretBase64}\n` +
              `Expiration: ${result.expirationTime}\n` +
              `Cursor: ${result.cursorForNextPayload}\n\n` +
              `⚠️ IMPORTANT: Save the MAC secret - it won't be shown again!`;
          }
          
          // DELETE WEBHOOK
          else if (toolName === 'delete_webhook') {
            const { webhookId } = toolParams;
            
            await callAirtableAPI(`bases/${baseId}/webhooks/${webhookId}`, 'DELETE');
            
            responseText = `Successfully deleted webhook ${webhookId}`;
          }
          
          // GET WEBHOOK PAYLOADS
          else if (toolName === 'get_webhook_payloads') {
            const { webhookId, cursor } = toolParams;
            
            const queryParams = {};
            if (cursor) queryParams.cursor = cursor;
            
            result = await callAirtableAPI(`bases/${baseId}/webhooks/${webhookId}/payloads`, 'GET', null, queryParams);
            
            const payloads = result.payloads || [];
            responseText = payloads.length > 0
              ? `Found ${payloads.length} webhook payload(s):\n` + 
                payloads.map((payload, i) => 
                  `${i+1}. Timestamp: ${payload.timestamp}\n` +
                  `   Base/Table: ${payload.baseTransactionNumber}\n` +
                  `   Change Types: ${JSON.stringify(payload.changePayload?.changedTablesById || {})}`
                ).join('\n\n') +
                (result.cursor ? `\n\nNext cursor: ${result.cursor}` : '')
              : 'No payloads found for this webhook.';
          }
          
          // REFRESH WEBHOOK
          else if (toolName === 'refresh_webhook') {
            const { webhookId } = toolParams;
            
            result = await callAirtableAPI(`bases/${baseId}/webhooks/${webhookId}/refresh`, 'POST');
            
            responseText = `Successfully refreshed webhook ${webhookId}:\n` +
              `New expiration: ${result.expirationTime}`;
          }
          
          // Schema Management Tools
          else if (toolName === 'list_bases') {
            const { offset } = toolParams;
            const queryParams = offset ? { offset } : {};
            
            result = await callAirtableAPI('meta/bases', 'GET', null, queryParams);
            
            if (result.bases && result.bases.length > 0) {
              responseText = `Found ${result.bases.length} accessible base(s):\n`;
              result.bases.forEach((base, index) => {
                responseText += `${index + 1}. ${base.name} (ID: ${base.id})\n`;
                if (base.permissionLevel) {
                  responseText += `   Permission: ${base.permissionLevel}\n`;
                }
              });
              if (result.offset) {
                responseText += `\nNext page offset: ${result.offset}`;
              }
            } else {
              responseText = 'No accessible bases found.';
            }
          }
          
          else if (toolName === 'get_base_schema') {
            const { baseId: targetBaseId } = toolParams;
            const targetId = targetBaseId || baseId;
            
            result = await callAirtableAPI(`meta/bases/${targetId}/tables`, 'GET');
            
            if (result.tables && result.tables.length > 0) {
              responseText = `Base schema for ${targetId}:\n\n`;
              result.tables.forEach((table, index) => {
                responseText += `${index + 1}. Table: ${table.name} (ID: ${table.id})\n`;
                if (table.description) {
                  responseText += `   Description: ${table.description}\n`;
                }
                responseText += `   Fields (${table.fields.length}):\n`;
                table.fields.forEach((field, fieldIndex) => {
                  responseText += `   ${fieldIndex + 1}. ${field.name} (${field.type})\n`;
                  if (field.description) {
                    responseText += `      Description: ${field.description}\n`;
                  }
                });
                if (table.views && table.views.length > 0) {
                  responseText += `   Views (${table.views.length}): ${table.views.map(v => v.name).join(', ')}\n`;
                }
                responseText += '\n';
              });
            } else {
              responseText = 'No tables found in this base.';
            }
          }
          
          else if (toolName === 'describe_table') {
            const { table } = toolParams;
            
            // Get table schema first
            const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
            const tableInfo = schemaResult.tables.find(t => 
              t.name.toLowerCase() === table.toLowerCase() || t.id === table
            );
            
            if (!tableInfo) {
              responseText = `Table "${table}" not found.`;
            } else {
              responseText = `Table Details: ${tableInfo.name}\n`;
              responseText += `ID: ${tableInfo.id}\n`;
              if (tableInfo.description) {
                responseText += `Description: ${tableInfo.description}\n`;
              }
              responseText += `\nFields (${tableInfo.fields.length}):\n`;
              
              tableInfo.fields.forEach((field, index) => {
                responseText += `${index + 1}. ${field.name}\n`;
                responseText += `   Type: ${field.type}\n`;
                responseText += `   ID: ${field.id}\n`;
                if (field.description) {
                  responseText += `   Description: ${field.description}\n`;
                }
                if (field.options) {
                  responseText += `   Options: ${JSON.stringify(field.options, null, 2)}\n`;
                }
                responseText += '\n';
              });
              
              if (tableInfo.views && tableInfo.views.length > 0) {
                responseText += `Views (${tableInfo.views.length}):\n`;
                tableInfo.views.forEach((view, index) => {
                  responseText += `${index + 1}. ${view.name} (${view.type})\n`;
                });
              }
            }
          }
          
          else if (toolName === 'create_table') {
            const { name, description, fields } = toolParams;
            
            const body = {
              name,
              fields: fields.map(field => ({
                name: field.name,
                type: field.type,
                description: field.description,
                options: field.options
              }))
            };
            
            if (description) {
              body.description = description;
            }
            
            result = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'POST', body);
            
            responseText = `Successfully created table "${name}" (ID: ${result.id})\n`;
            responseText += `Fields created: ${result.fields.length}\n`;
            result.fields.forEach((field, index) => {
              responseText += `${index + 1}. ${field.name} (${field.type})\n`;
            });
          }
          
          else if (toolName === 'update_table') {
            const { table, name, description } = toolParams;
            
            // Get table ID first
            const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
            const tableInfo = schemaResult.tables.find(t => 
              t.name.toLowerCase() === table.toLowerCase() || t.id === table
            );
            
            if (!tableInfo) {
              responseText = `Table "${table}" not found.`;
            } else {
              const body = {};
              if (name) body.name = name;
              if (description !== undefined) body.description = description;
              
              if (Object.keys(body).length === 0) {
                responseText = 'No updates specified. Provide name or description to update.';
              } else {
                result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}`, 'PATCH', body);
                responseText = `Successfully updated table "${tableInfo.name}":\n`;
                if (name) responseText += `New name: ${result.name}\n`;
                if (description !== undefined) responseText += `New description: ${result.description || '(none)'}\n`;
              }
            }
          }
          
          else if (toolName === 'delete_table') {
            const { table, confirm } = toolParams;
            
            if (!confirm) {
              responseText = 'Table deletion requires confirm=true to proceed. This action cannot be undone!';
            } else {
              // Get table ID first
              const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
              const tableInfo = schemaResult.tables.find(t => 
                t.name.toLowerCase() === table.toLowerCase() || t.id === table
              );
              
              if (!tableInfo) {
                responseText = `Table "${table}" not found.`;
              } else {
                result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}`, 'DELETE');
                responseText = `Successfully deleted table "${tableInfo.name}" (ID: ${tableInfo.id})\n`;
                responseText += 'All data in this table has been permanently removed.';
              }
            }
          }
          
          // Field Management Tools
          else if (toolName === 'create_field') {
            const { table, name, type, description, options } = toolParams;
            
            // Get table ID first
            const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
            const tableInfo = schemaResult.tables.find(t => 
              t.name.toLowerCase() === table.toLowerCase() || t.id === table
            );
            
            if (!tableInfo) {
              responseText = `Table "${table}" not found.`;
            } else {
              const body = {
                name,
                type
              };
              
              if (description) body.description = description;
              if (options) body.options = options;
              
              result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/fields`, 'POST', body);
              
              responseText = `Successfully created field "${name}" in table "${tableInfo.name}"\n`;
              responseText += `Field ID: ${result.id}\n`;
              responseText += `Type: ${result.type}\n`;
              if (result.description) {
                responseText += `Description: ${result.description}\n`;
              }
            }
          }
          
          else if (toolName === 'update_field') {
            const { table, fieldId, name, description, options } = toolParams;
            
            // Get table ID first
            const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
            const tableInfo = schemaResult.tables.find(t => 
              t.name.toLowerCase() === table.toLowerCase() || t.id === table
            );
            
            if (!tableInfo) {
              responseText = `Table "${table}" not found.`;
            } else {
              const body = {};
              if (name) body.name = name;
              if (description !== undefined) body.description = description;
              if (options) body.options = options;
              
              if (Object.keys(body).length === 0) {
                responseText = 'No updates specified. Provide name, description, or options to update.';
              } else {
                result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/fields/${fieldId}`, 'PATCH', body);
                responseText = `Successfully updated field in table "${tableInfo.name}":\n`;
                responseText += `Field: ${result.name} (${result.type})\n`;
                responseText += `ID: ${result.id}\n`;
                if (result.description) {
                  responseText += `Description: ${result.description}\n`;
                }
              }
            }
          }
          
          else if (toolName === 'delete_field') {
            const { table, fieldId, confirm } = toolParams;
            
            if (!confirm) {
              responseText = 'Field deletion requires confirm=true to proceed. This action cannot be undone!';
            } else {
              // Get table ID first
              const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
              const tableInfo = schemaResult.tables.find(t => 
                t.name.toLowerCase() === table.toLowerCase() || t.id === table
              );
              
              if (!tableInfo) {
                responseText = `Table "${table}" not found.`;
              } else {
                result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/fields/${fieldId}`, 'DELETE');
                responseText = `Successfully deleted field from table "${tableInfo.name}"\n`;
                responseText += 'All data in this field has been permanently removed.';
              }
            }
          }
          
          else if (toolName === 'list_field_types') {
            responseText = `Available Airtable Field Types:\n\n`;
            responseText += `Basic Fields:\n`;
            responseText += `• singleLineText - Single line text input\n`;
            responseText += `• multilineText - Multi-line text input\n`;
            responseText += `• richText - Rich text with formatting\n`;
            responseText += `• number - Number field with optional formatting\n`;
            responseText += `• percent - Percentage field\n`;
            responseText += `• currency - Currency field\n`;
            responseText += `• singleSelect - Single choice from predefined options\n`;
            responseText += `• multipleSelectionList - Multiple choices from predefined options\n`;
            responseText += `• date - Date field\n`;
            responseText += `• dateTime - Date and time field\n`;
            responseText += `• phoneNumber - Phone number field\n`;
            responseText += `• email - Email address field\n`;
            responseText += `• url - URL field\n`;
            responseText += `• checkbox - Checkbox (true/false)\n`;
            responseText += `• rating - Star rating field\n`;
            responseText += `• duration - Duration/time field\n\n`;
            responseText += `Advanced Fields:\n`;
            responseText += `• multipleAttachment - File attachments\n`;
            responseText += `• linkedRecord - Link to records in another table\n`;
            responseText += `• lookup - Lookup values from linked records\n`;
            responseText += `• rollup - Calculate values from linked records\n`;
            responseText += `• count - Count of linked records\n`;
            responseText += `• formula - Calculated field with formulas\n`;
            responseText += `• createdTime - Auto-timestamp when record created\n`;
            responseText += `• createdBy - Auto-user who created record\n`;
            responseText += `• lastModifiedTime - Auto-timestamp when record modified\n`;
            responseText += `• lastModifiedBy - Auto-user who last modified record\n`;
            responseText += `• autoNumber - Auto-incrementing number\n`;
            responseText += `• barcode - Barcode/QR code field\n`;
            responseText += `• button - Action button field\n`;
          }
          
          else if (toolName === 'get_table_views') {
            const { table } = toolParams;
            
            // Get table schema
            const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
            const tableInfo = schemaResult.tables.find(t => 
              t.name.toLowerCase() === table.toLowerCase() || t.id === table
            );
            
            if (!tableInfo) {
              responseText = `Table "${table}" not found.`;
            } else {
              if (tableInfo.views && tableInfo.views.length > 0) {
                responseText = `Views for table "${tableInfo.name}" (${tableInfo.views.length}):\n\n`;
                tableInfo.views.forEach((view, index) => {
                  responseText += `${index + 1}. ${view.name}\n`;
                  responseText += `   Type: ${view.type}\n`;
                  responseText += `   ID: ${view.id}\n`;
                  if (view.visibleFieldIds && view.visibleFieldIds.length > 0) {
                    responseText += `   Visible fields: ${view.visibleFieldIds.length}\n`;
                  }
                  responseText += '\n';
                });
              } else {
                responseText = `No views found for table "${tableInfo.name}".`;
              }
            }
          }
          
          // NEW v1.6.0 TOOLS - Attachment and Batch Operations
          else if (toolName === 'upload_attachment') {
            const { table, recordId, fieldName, url, filename } = toolParams;
            
            const attachment = { url };
            if (filename) attachment.filename = filename;
            
            const updateBody = {
              fields: {
                [fieldName]: [attachment]
              }
            };
            
            result = await callAirtableAPI(`${table}/${recordId}`, 'PATCH', updateBody);
            
            responseText = `Successfully attached file to record ${recordId}:\n`;
            responseText += `Field: ${fieldName}\n`;
            responseText += `URL: ${url}\n`;
            if (filename) responseText += `Filename: ${filename}\n`;
          }
          
          else if (toolName === 'batch_create_records') {
            const { table, records } = toolParams;
            
            if (records.length > 10) {
              responseText = 'Error: Cannot create more than 10 records at once. Please split into smaller batches.';
            } else {
              const body = { records };
              result = await callAirtableAPI(table, 'POST', body);
              
              responseText = `Successfully created ${result.records.length} records:\n`;
              result.records.forEach((record, index) => {
                responseText += `${index + 1}. ID: ${record.id}\n`;
                const fields = Object.keys(record.fields);
                if (fields.length > 0) {
                  responseText += `   Fields: ${fields.join(', ')}\n`;
                }
              });
            }
          }
          
          else if (toolName === 'batch_update_records') {
            const { table, records } = toolParams;
            
            if (records.length > 10) {
              responseText = 'Error: Cannot update more than 10 records at once. Please split into smaller batches.';
            } else {
              const body = { records };
              result = await callAirtableAPI(table, 'PATCH', body);
              
              responseText = `Successfully updated ${result.records.length} records:\n`;
              result.records.forEach((record, index) => {
                responseText += `${index + 1}. ID: ${record.id}\n`;
                const fields = Object.keys(record.fields);
                if (fields.length > 0) {
                  responseText += `   Updated fields: ${fields.join(', ')}\n`;
                }
              });
            }
          }
          
          else if (toolName === 'batch_delete_records') {
            const { table, recordIds } = toolParams;
            
            if (recordIds.length > 10) {
              responseText = 'Error: Cannot delete more than 10 records at once. Please split into smaller batches.';
            } else {
              const queryParams = { records: recordIds };
              result = await callAirtableAPI(table, 'DELETE', null, queryParams);
              
              responseText = `Successfully deleted ${result.records.length} records:\n`;
              result.records.forEach((record, index) => {
                responseText += `${index + 1}. Deleted ID: ${record.id}\n`;
              });
            }
          }
          
          else if (toolName === 'batch_upsert_records') {
            const { table, records, keyFields } = toolParams;
            
            if (records.length > 10) {
              responseText = 'Error: Cannot upsert more than 10 records at once. Please split into smaller batches.';
            } else {
              // For simplicity, we'll implement this as a batch create with merge fields
              // Note: Real upsert requires checking existing records first
              const body = {
                records,
                performUpsert: {
                  fieldsToMergeOn: keyFields
                }
              };
              
              result = await callAirtableAPI(table, 'PATCH', body);
              
              responseText = `Successfully upserted ${result.records.length} records:\n`;
              result.records.forEach((record, index) => {
                responseText += `${index + 1}. ID: ${record.id}\n`;
                const fields = Object.keys(record.fields);
                if (fields.length > 0) {
                  responseText += `   Fields: ${fields.join(', ')}\n`;
                }
              });
            }
          }
          
          // NEW v1.6.0 TOOLS - Advanced View Management
          else if (toolName === 'create_view') {
            const { table, name, type, visibleFieldIds, fieldOrder } = toolParams;
            
            // Get table ID first
            const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
            const tableInfo = schemaResult.tables.find(t => 
              t.name.toLowerCase() === table.toLowerCase() || t.id === table
            );
            
            if (!tableInfo) {
              responseText = `Table "${table}" not found.`;
            } else {
              const body = {
                name,
                type
              };
              
              if (visibleFieldIds) body.visibleFieldIds = visibleFieldIds;
              if (fieldOrder) body.fieldOrder = fieldOrder;
              
              result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/views`, 'POST', body);
              
              responseText = `Successfully created view "${name}" in table "${tableInfo.name}":\n`;
              responseText += `View ID: ${result.id}\n`;
              responseText += `Type: ${result.type}\n`;
              if (result.visibleFieldIds && result.visibleFieldIds.length > 0) {
                responseText += `Visible fields: ${result.visibleFieldIds.length}\n`;
              }
            }
          }
          
          else if (toolName === 'get_view_metadata') {
            const { table, viewId } = toolParams;
            
            // Get table ID first
            const schemaResult = await callAirtableAPI(`meta/bases/${baseId}/tables`, 'GET');
            const tableInfo = schemaResult.tables.find(t => 
              t.name.toLowerCase() === table.toLowerCase() || t.id === table
            );
            
            if (!tableInfo) {
              responseText = `Table "${table}" not found.`;
            } else {
              result = await callAirtableAPI(`meta/bases/${baseId}/tables/${tableInfo.id}/views/${viewId}`, 'GET');
              
              responseText = `View Metadata: ${result.name}\n`;
              responseText += `ID: ${result.id}\n`;
              responseText += `Type: ${result.type}\n`;
              
              if (result.visibleFieldIds && result.visibleFieldIds.length > 0) {
                responseText += `\nVisible Fields (${result.visibleFieldIds.length}):\n`;
                result.visibleFieldIds.forEach((fieldId, index) => {
                  responseText += `${index + 1}. ${fieldId}\n`;
                });
              }
              
              if (result.filterByFormula) {
                responseText += `\nFilter Formula: ${result.filterByFormula}\n`;
              }
              
              if (result.sorts && result.sorts.length > 0) {
                responseText += `\nSort Configuration:\n`;
                result.sorts.forEach((sort, index) => {
                  responseText += `${index + 1}. Field: ${sort.field}, Direction: ${sort.direction}\n`;
                });
              }
            }
          }
          
          // NEW v1.6.0 TOOLS - Base Management
          else if (toolName === 'create_base') {
            const { name, workspaceId, tables } = toolParams;
            
            const body = {
              name,
              tables: tables.map(table => ({
                name: table.name,
                description: table.description,
                fields: table.fields
              }))
            };
            
            if (workspaceId) {
              body.workspaceId = workspaceId;
            }
            
            result = await callAirtableAPI('meta/bases', 'POST', body);
            
            responseText = `Successfully created base "${name}":\n`;
            responseText += `Base ID: ${result.id}\n`;
            if (result.tables && result.tables.length > 0) {
              responseText += `\nTables created (${result.tables.length}):\n`;
              result.tables.forEach((table, index) => {
                responseText += `${index + 1}. ${table.name} (ID: ${table.id})\n`;
                if (table.fields && table.fields.length > 0) {
                  responseText += `   Fields: ${table.fields.length}\n`;
                }
              });
            }
          }
          
          else if (toolName === 'list_collaborators') {
            const { baseId: targetBaseId } = toolParams;
            const targetId = targetBaseId || baseId;
            
            result = await callAirtableAPI(`meta/bases/${targetId}/collaborators`, 'GET');
            
            if (result.collaborators && result.collaborators.length > 0) {
              responseText = `Base collaborators (${result.collaborators.length}):\n\n`;
              result.collaborators.forEach((collaborator, index) => {
                responseText += `${index + 1}. ${collaborator.email || collaborator.name || 'Unknown'}\n`;
                responseText += `   Permission: ${collaborator.permissionLevel || 'Unknown'}\n`;
                responseText += `   Type: ${collaborator.type || 'User'}\n`;
                if (collaborator.userId) {
                  responseText += `   User ID: ${collaborator.userId}\n`;
                }
                responseText += '\n';
              });
            } else {
              responseText = 'No collaborators found for this base.';
            }
          }
          
          else if (toolName === 'list_shares') {
            const { baseId: targetBaseId } = toolParams;
            const targetId = targetBaseId || baseId;
            
            result = await callAirtableAPI(`meta/bases/${targetId}/shares`, 'GET');
            
            if (result.shares && result.shares.length > 0) {
              responseText = `Shared views (${result.shares.length}):\n\n`;
              result.shares.forEach((share, index) => {
                responseText += `${index + 1}. ${share.name || 'Unnamed Share'}\n`;
                responseText += `   Share ID: ${share.id}\n`;
                responseText += `   URL: ${share.url}\n`;
                responseText += `   Type: ${share.type || 'View'}\n`;
                if (share.viewId) {
                  responseText += `   View ID: ${share.viewId}\n`;
                }
                if (share.tableId) {
                  responseText += `   Table ID: ${share.tableId}\n`;
                }
                responseText += `   Effective: ${share.effective ? 'Yes' : 'No'}\n`;
                responseText += '\n';
              });
            } else {
              responseText = 'No shared views found for this base.';
            }
          }
          
          else {
            throw new Error(`Unknown tool: ${toolName}`);
          }
          
          const response = {
            jsonrpc: '2.0',
            id: request.id,
            result: {
              content: [
                {
                  type: 'text',
                  text: responseText
                }
              ]
            }
          };
          res.writeHead(200, { 'Content-Type': 'application/json' });
          res.end(JSON.stringify(response));
          
        } catch (error) {
          log(LOG_LEVELS.ERROR, `Tool ${toolName} error:`, error.message);
          
          const response = {
            jsonrpc: '2.0',
            id: request.id,
            result: {
              content: [
                {
                  type: 'text',
                  text: `Error executing ${toolName}: ${error.message}`
                }
              ]
            }
          };
          res.writeHead(200, { 'Content-Type': 'application/json' });
          res.end(JSON.stringify(response));
        }
        
        return;
      }
      
      // Method not found
      const response = {
        jsonrpc: '2.0',
        id: request.id,
        error: {
          code: -32601,
          message: `Method ${request.method} not found`
        }
      };
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify(response));
      
    } catch (error) {
      log(LOG_LEVELS.ERROR, 'Error processing request:', error);
      const response = {
        jsonrpc: '2.0',
        id: request.id || null,
        error: {
          code: -32000,
          message: error.message || 'Unknown error'
        }
      };
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify(response));
    }
  });
});

// Start server
const PORT = process.env.PORT || 8010;
server.listen(PORT, () => {
  log(LOG_LEVELS.INFO, `Enhanced Airtable MCP server v1.4.0 running at http://localhost:${PORT}/mcp`);
  console.log(`For Claude, use this URL: http://localhost:${PORT}/mcp`);
});

// Graceful shutdown
process.on('SIGINT', () => {
  log(LOG_LEVELS.INFO, 'Shutting down server...');
  server.close(() => {
    log(LOG_LEVELS.INFO, 'Server stopped');
    process.exit(0);
  });
});
```
Page 3/4FirstPrevNextLast